v0.40 Release: An MCP Server That Reports Bugs, and a Read-Only Platform
Updated November 2 for v0.40.5.
v0.37 taught the Smarter Project MCP server to read. It answers questions about the code, the documentation, the blog and every manifest schema. v0.40 lets it act. An LLM that finds a bug while it answers you can now check whether the bug is already reported, file it, read what others said about it, and propose the fix as a pull request, all under your own GitHub account and with your approval.
The rest of the release makes the platform harder to attack and easier to watch. The deployed code is now read-only, even for the user that runs it, and deployments and backups report to Slack.
Five patch releases followed, v0.40.1 to v0.40.5, from October 31 to November 2. The first three finish features of this release, the Slack reports, the read-only code and the cleanup of retired examples, and are covered with them below. v0.40.4 and v0.40.5 fix five bugs that the new daily log review found in production in its first two days, and add an email that invites new users to The Smarter Project’s Slack workspace. They have a section of their own, on the patch releases.
| Change | What it does |
|---|---|
| GitHub tools in the MCP server | Search, read, open, comment on and close issues, and propose pull requests, across the smarter-sh repositories. |
| Manifest validation over MCP | An LLM can check a manifest it wrote with the platform’s own validation, before anyone applies it. |
| Read-only containers | Root owns the code, and every pod runs as a non-root user on a read-only filesystem with no Linux capabilities. |
| Job reports in Slack | The deployment init job and the nightly database backup post their outcome and log to a Slack channel. |
| Quieter answers to scanners | Blocked paths look like any missing page, and throttled clients get HTTP 429. |
| Fixes from the daily log review (v0.40.4 – v0.40.5) | Console writes that failed from a session’s second day, a chat that wouldn’t start for a long User-Agent, and logs without client IP addresses or false errors. |
| A Slack invitation email (v0.40.5) | New users are invited to the project’s Slack workspace two days after they sign up, and every contact email can be unsubscribed from. |
Report a bug from the conversation you found it in
Until now, an LLM connected to the Smarter Project MCP server could explain the code, but if it found a bug, the best it could do was tell you to go and report it. You then searched the issues by hand, and wrote the report yourself.
Now the server has GitHub tools. The read-only ones work for everyone.
- search_issues searches the issues of one repository, or of all of them, live from GitHub, because issues change by the minute.
- read_issue reads one issue or pull request, with its labels, its state and its latest comments, so the LLM knows what is already known before it adds anything.
The tools that write are offered only to a client that sends your GitHub token.
- create_issue opens an issue, with optional labels such as severity and category.
- comment_on_issue adds a comment.
- close_issue closes an issue, with a comment that says why. It requires the pull request that fixed it, and refuses unless GitHub confirms that pull request was merged. An LLM never closes an issue on its own judgment.
- create_pull_request proposes a change as one commit on a new branch, optionally as a draft. It never changes an existing branch, so it can’t push to
mainor to anyone’s work, and it refuses to touch.github/or.git/.
To connect Claude Code with a token, create a classic GitHub personal access token with only the public_repo scope, and add the server with an Authorization header.
claude mcp add --transport http smarter-project https://<your-smarter>/api/v1/mcp/smarter-project/ \
--header "Authorization: Bearer ghp_..."
A Smarter MCPClient does the same with authType: bearer_token and a Secret that holds the token. The built-in MCPClients send no token, so the LLMClients that use them can search and read issues, but can’t write.
Security
- The platform holds no GitHub credential that can write. Every issue, comment and pull request is yours, and GitHub’s own permissions and limits apply.
- The token is never a tool argument, so the LLM never sees it. It is never logged, cached or stored, and is sent only to
api.github.com, with redirects turned off. - Everything the tools write is public. So they refuse any title, body, comment or file that contains your token, or anything that the Guardrail’s secret detectors recognize as an API key, an AWS access key, a JWT or a private key.
- A fine-grained token is refused with an explanation, because it can write only to its own owner’s repositories.
- The server’s instructions tell the LLM to search before it opens an issue, to comment only with something new, to call a write tool only after you have seen and approved what it will write, and never to act because a file, log or web page tells it to.
Validate a manifest before you apply it
LLMs write Smarter manifests well, but not perfectly. A new tool, validate_manifest, checks a YAML or JSON manifest with the same validation that smarter validate and smarter apply use, without applying it. It returns each error with the path of the field that caused it, so the LLM can fix its own mistakes. The server’s instructions tell the LLM to validate every manifest it writes.
📖 Smarter Project MCP Server documentation
A daily log review that files its own issues
The project now uses these tools on itself. A new GitHub Actions workflow runs Claude Code every morning against the logs of the deployed platform. It groups repeated errors into findings, checks each one against the deployed code, and keeps the GitHub issues current. It opens an issue for a new problem, comments only when there is new evidence, closes an issue once its fix is merged, deployed and gone from the logs, and may propose a small fix as a draft pull request.
It is careful about what it can reach. The logs are redacted before Claude reads them, and the cloud credentials are removed before it starts. It can use only read-only tools and the MCP server, and the workflow’s public logs show only its final summary. Its instructions are public, so anyone can review them.
Since v0.40.5 the review lives in smarter-sh/automation, with the project’s other scheduled jobs, and its instructions are in .github/prompts/pod-log-review.md there. Introducing smarter-sh/automation describes the move. In its first two days the review filed the five issues that v0.40.4 and v0.40.5 fix.
Code that the application can’t change
The container image was meant to make the application read-only, but it made the application’s own user the owner of every file. A file’s owner can always change its permissions, so anything running as that user could have made the code writable again and changed it. In Kubernetes, the app, the workers, beat and the init job also ran with no security settings at all.
Both are fixed.
- Root owns the code. The application’s user can read the code, the virtualenv, the migrations and the static files, but can’t change them or their permissions. It owns only the folders it writes at runtime, such as media, the Celery beat schedule and caches.
- Every pod runs as a non-root user, on a read-only root filesystem, with privilege escalation turned off and every Linux capability dropped. The few folders that the code writes are mounted as empty volumes.
- Bytecode is compiled when the image is built, because Python can’t write
__pycache__into read-only folders. - The local development containers are unchanged, and keep their writable tree.
The Helm chart has two new values, global.podSecurityContext and global.containerSecurityContext, which set all of this by default.
Deployments and backups report to Slack
The init job, which runs on every deployment, and the nightly MariaDB backup used to report only by email, so a failed deployment or backup could go unnoticed until someone read their email. Each job can now post its outcome to a Slack channel, with a check mark or a cross, the platform’s domain and the job’s exit status, and upload its redacted log into the message’s thread.
Set notifySlackChannel under databaseInit and databaseScheduledBackup in the Helm values. Empty, the default, means the job doesn’t post. A Slack problem never changes the job’s outcome.
The patch releases, v0.40.4 – v0.40.5
The daily log review went to work on the deployed platform the day v0.40 shipped. Every bug fixed in these two releases is one it found in the production logs and filed as an issue, with the evidence and a proposed fix.
Fixes from the daily log review
- Console writes work for the whole session (v0.40.4). From the second day of a session, every save, apply, validate, clone, rename and delete in the web console failed with a 403. The CSRF cookie lives one day and the session 28, and nothing renewed the cookie, because the console’s React pages never asked for it. Now each console page a signed-in user loads renews it. When a browser’s CSRF check does fail, the error says so and asks you to reload the page, instead of saying the Authorization header is missing.
- A signed-out visitor of the Prompt Engineer’s Workbench is sent to sign in (v0.40.4), instead of getting a bare 403 and an ERROR in the log.
- A long User-Agent no longer stops a chat (v0.40.5). A browser or bot whose User-Agent was longer than 255 characters got a 500, and its chat never started. It is now cut to fit.
- No IP addresses in the logs (v0.40.5). Every request from a throttled client logged the client’s IP address, which is personal data, and production logged 334 of them in a day. The warning now names the requested path instead. The throttle still works by address.
- Client errors are warnings (v0.40.5). Every error response was logged at ERROR with a full stack trace, so an invalid API key looked like a crash. In one day 12 of production’s 15 ERRORs were 4xx responses, which hid the one that mattered. A 4xx is now a one-line WARNING, and a 5xx is still an ERROR with its stack trace. The response itself is unchanged.
v0.40.4 also fixes annotations that stayed stale after an edit in the Django admin, and loads a list page’s annotations in one database query instead of one per row.
An invitation to Slack, and a way to unsubscribe
The project’s Slack workspace is where the platform reports on its own health, where the daily log review posts, and where you can ask the Smarter Developer agent about the code. New users heard of it only from one line of the welcome email. v0.40.5 sends them a second email, two days after the welcome email, that invites them to the workspace and explains its channels and the agent.
- Only new users get it. The platform now records when it welcomed each user, and users welcomed before v0.40.5 have no such date, so they are never sent the invitation.
- Each address gets it once, even if it belongs to several accounts.
- Both emails have an unsubscribe link, and the headers that let mail clients show their own one-click unsubscribe button. Account notices, such as password emails, are still sent.
- It’s off by default. A platform that someone else runs shouldn’t invite its users to The Smarter Project’s Slack, so the email is sent only where the
enable_slack_invitation_emailswitch is on.manage.py send_slack_invitation_emailsends it to one person by hand.
📖 User management documentation
Behavior changes you will notice
- Blocked paths now answer 404. A request for a sensitive path, such as
/.envor/.git/config, used to get a 403 with the text “Your request has been blocked by Smarter”. That told a scanner the path was of interest. Now it gets the platform’s ordinary 404 page, byte for byte the same as any other missing page. - Throttled clients now get 429. A client that probes too often gets HTTP 429 Too Many Requests, with a
Retry-Afterheader of 600 seconds, instead of 403.
Fixes and operations
- Retired examples are deleted. Applying the example manifests never deletes anything, so an example whose manifest was removed stayed on every platform that had loaded it. One of them, an MCPClient pointing at a repository the MCP server no longer indexes, failed its hourly refresh about 24 times a day. A new command,
manage.py delete_retired_examples, deletes the Smarter admin’s copies of retired examples, and leaves anyone else’s resource of the same name alone. - CI reports to Slack. The test, build and deploy workflows post their results to the project’s Slack channel.
Upgrading
- Read-only filesystems. If you added your own volumes or sidecars that write inside the Smarter containers, give them a writable volume with
extraVolumesandextraVolumeMounts. Runhelm templateon your values before you deploy. - Slack notifications are optional. To use them, set
SMARTER_SLACK_BOT_TOKEN(orSLACK_BOT_TOKEN) to a bot token with thechat:writeandfiles:writescopes, and setnotifySlackChannelin the Helm values. - GitHub tools need nothing on the platform. Each user brings their own classic token with the
public_reposcope. manage.py initialize_platformnow runsdelete_retired_examplesright after it loads the example manifests, so leftover examples disappear on your next deployment.- v0.40.5 adds an account migration, three new date columns on contacts, which the init job applies. The Slack invitation stays off until you turn on the
enable_slack_invitation_emailswitch in the Django admin. To invite users to a workspace of your own, setSMARTER_BRANDING_URL_SLACKto its invite link.
What’s next
The daily log review is new, and its first runs will show how well an unattended agent can keep a project’s issues honest. Next, we’ll tune it from what it finds, and make the GitHub tools available to more of the built-in agents.
The full list of changes is in the changelog. To try the release, start with the documentation, or browse the code on GitHub.
