feat: write an AGENTS.md into generated projects - #33
Merged
Merged
Conversation
Generated projects explained how to run themselves and nothing about how to change themselves. The traps that actually bite - state discarded between requests, stdout corrupting the protocol stream, a token rejection returning 500 instead of 401 - were only comments in the code, where you find them after the bug rather than before. Adds src/templates/common/agents.md.ts, written for whoever edits the project rather than whoever runs it. The README keeps the install and deployment story; this has no overlap with it beyond a command block. The file is deliberately example-agnostic. The shipped notes example exists so nothing starts empty and is meant to be deleted once real tools arrive, but AGENTS.md stays - so naming notes-store or list_notes in the guidance would rot it the moment the example goes. It says the example is meant to be replaced, then uses a neutral illustration (list_invoices) for every rule. A test asserts no example-specific name reaches the output. It branches per variant, and the branching is the substance: - HTTP gets the per-request factory warning. createMcpHandler runs the factory once per request, so state on the server instance vanishes between calls - a write appears to succeed and the next read returns nothing. - stdio gets the stdout rule instead, since the factory warning does not apply there and the protocol channel does. - OAuth adds the OAuthError-not-Error rule, with the missing-JWKS guard called out as the deliberate exception. - FastMCP gets a trimmed version that states plainly it ships without tests, rather than leaving a gap a reader has to infer. SDK variants also carry the tool-design guidance - naming, parameter documentation, signalled truncation, actionable errors, payloads that cannot mean two things - and a section on organizing tools as they grow: flat files while there are few, then src/tools/ one per tool, then feature folders once a server spans unrelated domains. The scaffold ships flat because that suits a handful of tools; the advice on outgrowing it belongs in prose rather than in a directory structure the example has not earned. The naming rule interpolates the real project name, so the double-prefix warning reads "<project>_invoices_list" rather than an abstract example. Also adds vitest.config.ts. generated/ holds scratch projects from testing the CLI by hand; they are gitignored, but since they now ship their own test suites the repo's own run was picking them up and failing on them.
IBJunior
force-pushed
the
feat/generated-agents-md
branch
from
September 19, 2026 16:49
789bffe to
4d968dd
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Every generated project now gets an
AGENTS.md— working notes for whoever changes the project, as distinct from the README, which covers running it.Fifth of the sequence. One PR left after this: installing the
tool-designskill.Why
Generated projects explained how to run themselves and nothing about how to change themselves. The traps that actually bite — state discarded between requests, stdout corrupting the protocol stream, a token rejection returning 500 instead of 401 — existed only as comments in the code, which is where you find them after the bug rather than before.
Example-agnostic on purpose
The shipped notes example exists so nothing starts empty, and it is meant to be deleted once real tools arrive.
AGENTS.mdstays. So any guidance namingnotes-storeorlist_noteswould rot the moment the example goes — the file would describe code that no longer exists.It now says the example is meant to be replaced, then uses a neutral illustration (
list_invoices,inv_4821) for every rule. A test asserts no example-specific name —notes-store,list_notes,get_note,create_note,summarize-notes— reaches any variant's output.src/server.test.tsis still named, since that path survives the example being replaced.No overlap with the README
The README keeps install, endpoints, Docker and OAuth provider tables.
AGENTS.mdhas none of that; a test asserts it. The only deliberate overlap is a short command block, so an agent reading this file doesn't have to open another one to learn the test command.It branches per variant, and the branching is the substance
createMcpHandlerruns the factory once per request, so state on the server instance vanishes between calls: a write appears to succeed and the next read returns nothingOAuthError-not-Errorrule, with the missing-JWKS guard called out as the deliberate exceptionVerified by generating all four and diffing their section lists:
Tool guidance, and organizing tools as they grow
SDK variants carry the tool-design rules — naming, parameter documentation with format and example, signalled truncation, actionable
isErrorpayloads, and not letting two different situations produce the same payload.Then the section you asked for: how to organize tools as they grow.
src/tools.ts, as now.src/tools/, one file per tool, with anindex.tsre-exporting a singleregisterTools.src/notes/{tools,resources,store}.ts.The scaffold ships flat because that suits a handful of tools. The advice on outgrowing it belongs in prose, not in a directory structure the example hasn't earned.
One detail worth noting: the naming rule interpolates the real project name, so the warning reads
my-server_invoices_listrather than an abstract example — the double-prefix problem is much more obvious when you see it with your own server's name.Testing section
Repeats the framing the emitted suite already uses: one test per tool in that spirit is enough, and the nine shipped tests are a worked example, not a quota. Worth keeping consistent — the point of trimming the suite was to avoid implying a bar, and prose that contradicted that would undo it.
Verification
npm test— 456 tests across 18 files (up from 422); new tests cover the template, including that the OAuth and FastMCP sections appear and disappear correctly, and that no example-specific name leaks innpm run lintclean; new files passprettier --checkOne fix that came out of this
Adds
vitest.config.ts.generated/holds ~35 scratch projects from testing the CLI by hand — gitignored, but since PR #32 they ship their own test suites, so the repo's ownnpm testhad started picking them up and failing on them. The config scopes the run tosrc/anddist/.Repo
AGENTS.mdand README updated to record the new template and the file it emits.