Closes #2163 `AGENTS.md` was one 1,223-line contract loaded in full on every turn, whether the task was a one-line CSS fix or a release cut. This splits it: rules stay resident, procedures move to `.claude/skills/`. - Ten skills under `.claude/skills/`, seven `disable-model-invocation: true` (invoked by name only, so they cost no listing context and have no trigger question) and three model-invoked with committed eval cases. - `AGENTS.md` 1,223 → 380 lines: rules, a skills index, and a project tree reduced to one line per entry. The removed recipes are not duplicated. - `README.md` 811 → 105 lines, split into `docs/{architecture,quality-gate, test-servers,publishing,docker}.md`, and dropped from `CLAUDE.md`'s auto-load. - `.github/copilot-instructions.md` and its mirroring rule removed — Copilot reads `AGENTS.md`. - `npm run verify:skills` (in `validate`, so in `local:gate` and CI) parses each SKILL.md the way Claude Code does, requires an explicit invocation mode and eval cases for model-invoked skills, and budgets the listing. - `npm run skills:eval` runs the committed trigger cases headless. Signed-off-by: cliffhall <cliff@futurescale.com>
3.5 KiB
| name | description | disable-model-invocation |
|---|---|---|
| test-servers | Run a composable MCP test server by hand — pick the showcase config that reproduces a given feature or bug, build test-servers, and connect with the right protocol era. Covers the stale-build hazard. | true |
Running a test server
test-servers/ provides composable MCP servers so tests and manual checks
exercise a real server over a real transport instead of mocks. A server is
assembled from presets (fixture factories in
test-servers/src/preset-registry.ts) and configured declaratively with a JSON
file under test-servers/configs/.
The full catalogue of showcase configs — one per feature, each with what to click
and what the broken build did — is
docs/test-servers.md. This skill is how to
run one.
Build first
The servers are spawned as real subprocesses, so the build output must exist:
cd clients/web && npm run test-servers:build # tsc -p test-servers → test-servers/build/
Scripts reach this through scripts/ensure-test-servers.mjs, which builds
unconditionally (once per process per repo root).
⚠️ Unconditional emit is not a clean. A deleted source file leaves its
stale .js behind, existence checks pass against it, and anything still
importing that module silently runs the old code — reported not as staleness but
as a product failure in whatever was being tested. So after deleting or renaming
a source file:
rm -rf test-servers/build
The .tsbuildinfo is pinned inside build/ so that clean actually invalidates
the cache.
Run one
# From the repo root, with a built launcher:
node clients/launcher/build/index.js --web
# then in the Inspector, add the server the config announces
The server prints its URL on stderr, and the bound port is not
necessarily the config's — findAvailablePort() walks upward when the
configured port is taken. Parse the announced URL rather than assuming.
Pick the right protocol era
Each config in docs/test-servers.md says
which era to connect with. The default is legacy; configs setting
transport.modern need Protocol Era = Modern. Connecting with the wrong era
usually looks like a missing capability rather than an error.
Common starting points
| Want to see | Config |
|---|---|
| An MCP App in the Apps tab | mcp-app-http.json (legacy) |
| An App-rendered elicitation | app-elicitation-http.json (legacy) |
Mcp-* headers + the modern error taxonomy |
modern-network-http.json |
A tool result's structuredContent section |
structured-output-http.json (legacy) |
| RFC 6570 resource-template expansion | rfc6570-templates-http.json |
| OAuth token revocation on clear | oauth-revocation-http.json (legacy) |
| Cancelling a call mid-flight | cancellation-modern-http.json (modern) |
Adding a config or preset
- Presets live in
test-servers/src/preset-registry.ts; configs intest-servers/configs/*.json. - A new showcase config gets a row in
docs/test-servers.mdsaying what to do and what the broken build did — the "what it looked like broken" half is what makes the fixture reproducible later. - ⚠️ An
outputSchemaoverride must ride a tool that returns structured content. A conforming client validates the result against the advertised schema, so an override on a preset returning none makes every call fail with "declares an output schema but returned no structured content".