@cyanheads/mcp-ts-core 0.9.13 → 0.9.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/AGENTS.md +559 -0
  2. package/CLAUDE.md +8 -4
  3. package/README.md +33 -44
  4. package/changelog/0.9.x/0.9.14.md +31 -0
  5. package/changelog/0.9.x/0.9.15.md +52 -0
  6. package/changelog/template.md +1 -1
  7. package/dist/core/context.d.ts +119 -14
  8. package/dist/core/context.d.ts.map +1 -1
  9. package/dist/core/context.js +70 -1
  10. package/dist/core/context.js.map +1 -1
  11. package/dist/core/index.d.ts +1 -1
  12. package/dist/core/index.d.ts.map +1 -1
  13. package/dist/core/index.js.map +1 -1
  14. package/dist/linter/rules/enrichment-rules.d.ts +41 -0
  15. package/dist/linter/rules/enrichment-rules.d.ts.map +1 -0
  16. package/dist/linter/rules/enrichment-rules.js +204 -0
  17. package/dist/linter/rules/enrichment-rules.js.map +1 -0
  18. package/dist/linter/rules/index.d.ts +1 -0
  19. package/dist/linter/rules/index.d.ts.map +1 -1
  20. package/dist/linter/rules/index.js +1 -0
  21. package/dist/linter/rules/index.js.map +1 -1
  22. package/dist/linter/rules/schema-rules.d.ts +4 -0
  23. package/dist/linter/rules/schema-rules.d.ts.map +1 -1
  24. package/dist/linter/rules/schema-rules.js +2 -2
  25. package/dist/linter/rules/schema-rules.js.map +1 -1
  26. package/dist/linter/rules/tool-rules.d.ts.map +1 -1
  27. package/dist/linter/rules/tool-rules.js +4 -0
  28. package/dist/linter/rules/tool-rules.js.map +1 -1
  29. package/dist/mcp-server/tools/tool-registration.d.ts.map +1 -1
  30. package/dist/mcp-server/tools/tool-registration.js +7 -7
  31. package/dist/mcp-server/tools/tool-registration.js.map +1 -1
  32. package/dist/mcp-server/tools/utils/toolDefinition.d.ts +81 -7
  33. package/dist/mcp-server/tools/utils/toolDefinition.d.ts.map +1 -1
  34. package/dist/mcp-server/tools/utils/toolDefinition.js.map +1 -1
  35. package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts +23 -1
  36. package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts.map +1 -1
  37. package/dist/mcp-server/tools/utils/toolHandlerFactory.js +118 -9
  38. package/dist/mcp-server/tools/utils/toolHandlerFactory.js.map +1 -1
  39. package/dist/testing/index.d.ts +13 -0
  40. package/dist/testing/index.d.ts.map +1 -1
  41. package/dist/testing/index.js +21 -1
  42. package/dist/testing/index.js.map +1 -1
  43. package/dist/utils/internal/performance.d.ts +5 -1
  44. package/dist/utils/internal/performance.d.ts.map +1 -1
  45. package/dist/utils/internal/performance.js +10 -1
  46. package/dist/utils/internal/performance.js.map +1 -1
  47. package/dist/utils/telemetry/attributes.d.ts +2 -0
  48. package/dist/utils/telemetry/attributes.d.ts.map +1 -1
  49. package/dist/utils/telemetry/attributes.js +2 -0
  50. package/dist/utils/telemetry/attributes.js.map +1 -1
  51. package/package.json +5 -3
  52. package/scripts/build-changelog.ts +3 -1
  53. package/scripts/check-skills-sync.ts +42 -8
  54. package/skills/add-app-tool/SKILL.md +2 -2
  55. package/skills/add-export/SKILL.md +2 -2
  56. package/skills/add-service/SKILL.md +2 -2
  57. package/skills/add-tool/SKILL.md +85 -32
  58. package/skills/api-context/SKILL.md +68 -3
  59. package/skills/api-linter/SKILL.md +73 -2
  60. package/skills/design-mcp-server/SKILL.md +2 -1
  61. package/skills/git-wrapup/SKILL.md +22 -15
  62. package/skills/maintenance/SKILL.md +8 -7
  63. package/skills/orchestrations/SKILL.md +9 -5
  64. package/skills/orchestrations/workflows/maintenance-release.md +1 -1
  65. package/skills/polish-docs-meta/SKILL.md +1 -1
  66. package/skills/polish-docs-meta/references/agent-protocol.md +2 -2
  67. package/skills/polish-docs-meta/references/readme.md +3 -3
  68. package/skills/report-issue-framework/SKILL.md +8 -3
  69. package/skills/report-issue-local/SKILL.md +8 -3
  70. package/skills/setup/SKILL.md +5 -10
  71. package/templates/AGENTS.md +2 -1
  72. package/templates/CLAUDE.md +2 -1
  73. package/templates/_.mcpbignore +2 -0
  74. package/templates/changelog/template.md +1 -1
  75. package/templates/package.json +2 -1
  76. package/templates/src/mcp-server/tools/definitions/echo.tool.ts +10 -0
  77. package/dist/logs/combined.log +0 -4
  78. package/dist/logs/error.log +0 -2
  79. package/dist/logs/interactions.log +0 -0
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  name: api-context
3
3
  description: >
4
- Canonical reference for the unified `Context` object passed to every tool and resource handler in `@cyanheads/mcp-ts-core`. Covers the full interface, all sub-APIs (`ctx.log`, `ctx.state`, `ctx.elicit`, `ctx.sample`, `ctx.progress`), and when to use each.
4
+ Canonical reference for the unified `Context` object passed to every tool and resource handler in `@cyanheads/mcp-ts-core`. Covers the full interface, all sub-APIs (`ctx.log`, `ctx.state`, `ctx.elicit`, `ctx.sample`, `ctx.progress`, `ctx.enrich`), and when to use each.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.3"
7
+ version: "1.5"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
@@ -57,6 +57,12 @@ interface Context {
57
57
  // Raw URI — present only for resource handlers
58
58
  readonly uri?: URL;
59
59
 
60
+ // Agent-facing success-path enrichment — accumulates notices, query echo, totals
61
+ // onto the request; reaches structuredContent + content[]. Always present (no-op
62
+ // when no `enrichment` block), strictly typed on HandlerContext<R, E> against the
63
+ // declared fields. Kind-tagged helpers: enrich.notice / .total / .echo.
64
+ readonly enrich: Enrich;
65
+
60
66
  // Opt-in contract resolver — always present (returns {} when no contract is attached
61
67
  // or the reason is unknown), strictly typed on HandlerContext<R> against declared reasons.
62
68
  recoveryFor(reason: string): { recovery: { hint: string } } | {};
@@ -208,7 +214,7 @@ Use this only when downstream code is structured around `ctx.sessionId` and acce
208
214
 
209
215
  ### Capability-token model
210
216
 
211
- Surfacing `sessionId` does not change the framework's capability-as-token rule (possession of an opaque ID grants access — see CLAUDE.md `# Core Rules`). It is an opt-in *discovery-scoping* axis, not an access boundary.
217
+ Surfacing `sessionId` does not change the framework's capability-as-token rule (possession of an opaque ID grants access — see CLAUDE.md/AGENTS.md `# Core Rules`). It is an opt-in *discovery-scoping* axis, not an access boundary.
212
218
 
213
219
  - Tokens shared across sessions (e.g. `df_<uuid>` handed from Agent A to Agent B) still resolve on the receiving side. The lookup key is the token, not the session.
214
220
  - Session-scoped *enumeration* (e.g. `dataframe_describe` returning only items registered by the current session) is a per-server pattern: maintain a session-keyed lookup of known names, gate list-all on it, but route direct lookups against the shared backing store.
@@ -520,6 +526,64 @@ The `≥5 words` lint rule on contract `recovery` (validated at lint time) makes
520
526
 
521
527
  ---
522
528
 
529
+ ## `ctx.enrich`
530
+
531
+ Always present on `Context`. Accumulates agent-facing **success-path** context — empty-result notices, the query/filter as the server parsed it, pagination totals — onto the request. The framework merges it into `structuredContent`, advertises `output.extend(enrichment)` as the tool's `outputSchema`, and mirrors it into a `content[]` trailer. The success-path counterpart to `ctx.fail` / `ctx.recoveryFor`.
532
+
533
+ ```ts
534
+ export const search = tool('search', {
535
+ description: 'Search the catalog.',
536
+ input: z.object({ query: z.string().describe('Search terms') }),
537
+ output: z.object({ items: z.array(z.string()).describe('Matching items') }),
538
+ enrichment: {
539
+ effectiveQuery: z.string().describe('Query as the server parsed it'),
540
+ totalCount: z.number().describe('Total matches before the limit'),
541
+ notice: z.string().optional().describe('Guidance when nothing matched'),
542
+ },
543
+ async handler(input, ctx) {
544
+ const res = await runSearch(input.query);
545
+ ctx.enrich.echo(res.parsed); // → effectiveQuery + "Query: …" trailer
546
+ ctx.enrich.total(res.total); // → totalCount + "N total" trailer
547
+ if (res.items.length === 0) ctx.enrich.notice(`No matches for "${input.query}".`);
548
+ return { items: res.items }; // enrichment never rides in the domain return
549
+ },
550
+ });
551
+ ```
552
+
553
+ ### Signature
554
+
555
+ ```ts
556
+ // Loose (always present on Context — works without a block; service-callable):
557
+ ctx.enrich(fields: Record<string, unknown>): void
558
+
559
+ // Strict (HandlerContext<R, E> when the definition declares an enrichment block):
560
+ ctx.enrich(fields: Partial<z.infer<ZodObject<E>>>): void
561
+
562
+ // Kind-tagged field-helpers (always present) — write a conventional key and tag
563
+ // the content[] trailer rendering:
564
+ ctx.enrich.notice(text: string): void // writes `notice` → blockquote
565
+ ctx.enrich.total(count: number): void // writes `totalCount` → "N total"
566
+ ctx.enrich.echo(query: string): void // writes `effectiveQuery` → "Query: …"
567
+ ctx.enrich.delta({ field, before, after }): void // writes `{before, after}` → "field: before → after"
568
+ ```
569
+
570
+ ### Behavior
571
+
572
+ | Aspect | Detail |
573
+ |:-------|:-------|
574
+ | Accumulation | Each call merges its fields onto the request; later calls override earlier keys. |
575
+ | Both surfaces | Merged into `structuredContent` (validated against `output.extend(enrichment)`) and appended to `content[]` as a trailer — even when the tool defines no `format()`. |
576
+ | Domain payload untouched | `content[]` renders the handler's return via `format()` (or the JSON default); enrichment is a separate trailer, never double-rendered. The handler return must NOT carry enrichment fields. |
577
+ | Required-field guard | A required enrichment field never populated fails the effective-output parse — the bug surfaces loudly rather than dropping silently. |
578
+ | No block | Calling `ctx.enrich` on a tool that declared no `enrichment` is a silent no-op (values are stripped by the parse) — the price of service-layer callability. |
579
+ | Service usage | Services accepting `ctx: Context` can call `ctx.enrich(...)`; the value reaches `structuredContent` exactly as if the handler had. |
580
+ | `format-parity` | Enrichment lives outside `output`, so the `format-parity` lint never requires it in `format()`. |
581
+ | Trailer rendering | Per field: kind-tag if set (notice/total/echo/delta), else the definition's `enrichmentTrailer.render`/`label`, else `**key:** value` (objects/arrays `JSON.stringify`'d). A structured field with no `render` errors under `enrichment-trailer-render` — supply one so it renders as markdown; `structuredContent` keeps the full value regardless. |
582
+
583
+ See `add-tool`'s **Tool Response Design** and `skills/api-linter` (`enrichment-*` rules) for the full pattern. Test enrichment with `getEnrichment(ctx)` from `@cyanheads/mcp-ts-core/testing`.
584
+
585
+ ---
586
+
523
587
  ## Quick reference
524
588
 
525
589
  | Property | Type | Present when |
@@ -534,6 +598,7 @@ The `≥5 words` lint rule on contract `recovery` (validated at lint time) makes
534
598
  | `ctx.log` | `ContextLogger` | Always |
535
599
  | `ctx.state` | `ContextState` | Always (throws if `tenantId` missing) |
536
600
  | `ctx.signal` | `AbortSignal` | Always |
601
+ | `ctx.enrich` | `Enrich` | Always; typed on `HandlerContext<R, E>` when an `enrichment` block is declared |
537
602
  | `ctx.elicit` | `function \| undefined` | Client supports elicitation |
538
603
  | `ctx.sample` | `function \| undefined` | Client supports sampling |
539
604
  | `ctx.notifyResourceListChanged` | `function \| undefined` | Transport supports resource notifications |
@@ -4,7 +4,7 @@ description: >
4
4
  MCP definition linter rules reference. Use when `bun run lint:mcp` or `bun run devcheck` reports a lint error or warning (`format-parity`, `schema-is-object`, `name-format`, `server-json-*`, etc.) and you need to understand the rule, its severity, and how to fix it. Every rule ID the linter emits has an entry in this doc.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.3"
7
+ version: "1.5"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
@@ -53,6 +53,7 @@ Grouped by family. Jump to any rule ID via its anchor.
53
53
  | Handler body | `prefer-mcp-error-in-handler`, `prefer-error-factory`, `preserve-cause-on-rethrow`, `no-stringify-upstream-error` | [Handler body rules](#handler-body-rules) |
54
54
  | Error contract (structural) | `error-contract-type`, `error-contract-empty`, `error-contract-entry-type`, `error-contract-code-type`, `error-contract-code-unknown`, `error-contract-code-unknown-error`, `error-contract-reason-required`, `error-contract-reason-format`, `error-contract-reason-unique`, `error-contract-when-required`, `error-contract-retryable-type`, `error-contract-recovery-required`, `error-contract-recovery-empty`, `error-contract-recovery-min-words` | [Error contract rules](#error-contract-rules) |
55
55
  | Error contract (conformance) | `error-contract-conformance`, `error-contract-prefer-fail` | [Error contract rules](#error-contract-rules) |
56
+ | Enrichment | `enrichment-type`, `enrichment-empty`, `enrichment-field-type`, `enrichment-output-collision`, `enrichment-prefer-block`, `enrichment-trailer-render`, `enrichment-trailer-orphan`, `enrichment-trailer-unknown-field` | [Enrichment rules](#enrichment-rules) |
56
57
  | server.json | ~40 rules prefixed `server-json-*` | [server.json rules](#server-json-rules) |
57
58
 
58
59
  ---
@@ -514,7 +515,7 @@ Heuristic source-text checks that scan `handler.toString()` for common error-han
514
515
 
515
516
  Fires when a handler contains `throw new Error(...)`. Plain `Error` doesn't carry a JSON-RPC code — the framework's auto-classifier degrades to `InternalError`, hiding the actual failure mode.
516
517
 
517
- Plain `Error` is acceptable for "don't care" cases where the specific code doesn't matter (per CLAUDE.md: "plain `Error` for don't-care cases"). This rule targets domain-specific failures that deserve a concrete code — upgrade those to factories or `ctx.fail`, and accept the warning for the rest.
518
+ Plain `Error` is acceptable for "don't care" cases where the specific code doesn't matter (per CLAUDE.md/AGENTS.md: "plain `Error` for don't-care cases"). This rule targets domain-specific failures that deserve a concrete code — upgrade those to factories or `ctx.fail`, and accept the warning for the rest.
518
519
 
519
520
  **Fix:** use `McpError` or a factory for domain-specific failures:
520
521
 
@@ -708,6 +709,76 @@ The diagnostic message includes the declared reason(s) for the code so you can c
708
709
 
709
710
  ---
710
711
 
712
+ ## Enrichment rules
713
+
714
+ Validate the `enrichment` block — the success-path counterpart to `errors[]`. Enrichment fields are merged into `structuredContent` and advertised as `output.extend(enrichment)`, so the linter guards the block's shape and its disjointness from `output`. See `api-context`'s `ctx.enrich` and `add-tool`'s **Tool Response Design**.
715
+
716
+ ### enrichment-type
717
+
718
+ **Severity:** error
719
+
720
+ Fires when `enrichment` is present but isn't a plain object mapping field names to Zod schemas (a `ZodRawShape`) — e.g. an array or a primitive.
721
+
722
+ **Fix:** declare `enrichment: { <name>: <ZodType>, … }`.
723
+
724
+ ### enrichment-empty
725
+
726
+ **Severity:** warning
727
+
728
+ Fires when `enrichment: {}` is declared with no fields — a no-op.
729
+
730
+ **Fix:** drop the field, or declare the agent-facing fields `ctx.enrich(...)` will populate.
731
+
732
+ ### enrichment-field-type
733
+
734
+ **Severity:** error
735
+
736
+ Fires when an enrichment field's value isn't a Zod schema.
737
+
738
+ **Fix:** use a Zod type (`z.string().describe(…)`, `z.number().describe(…)`, …) for every enrichment field.
739
+
740
+ ### enrichment-output-collision
741
+
742
+ **Severity:** error
743
+
744
+ Fires when an enrichment key matches an `output` key. The effective output schema is `output.extend(enrichment)`, so a collision silently overrides the `output` field.
745
+
746
+ **Fix:** rename one side so enrichment keys are disjoint from output keys.
747
+
748
+ ### enrichment-prefer-block
749
+
750
+ **Severity:** warning
751
+
752
+ Advisory. Fires when a tool has **no** `enrichment` block but an `output` field whose name strongly signals agent-facing context (`notice`, `effectiveQuery`, `queryEcho`) rather than domain payload.
753
+
754
+ **Fix:** move the field into an `enrichment` block and populate it via `ctx.enrich(...)` — it reaches both client surfaces without a `format()` entry. Ignore if the field is genuinely domain data. Deliberately conservative — common domain fields like `totalCount` are not flagged.
755
+
756
+ ### enrichment-trailer-render
757
+
758
+ **Severity:** error
759
+
760
+ Fires when a non-scalar (object/array) enrichment field has no `enrichmentTrailer.render`. It would `JSON.stringify` into a one-line blob in the `content[]` trailer (`structuredContent` keeps the full value either way). The `delta` shape (`z.object({ before, after })`, populated by `ctx.enrich.delta()`) is exempt — it renders natively as `field: before → after`.
761
+
762
+ **Fix:** add a renderer — `enrichmentTrailer: { <field>: { render: (v) => … } }` — use `ctx.enrich.delta()` for before/after state, or opt into the JSON blob explicitly with `render: (v) => JSON.stringify(v)`.
763
+
764
+ ### enrichment-trailer-orphan
765
+
766
+ **Severity:** error
767
+
768
+ Fires when `enrichmentTrailer` is declared without an `enrichment` block — trailer config only renders enrichment fields.
769
+
770
+ **Fix:** add the `enrichment` block, or drop the `enrichmentTrailer`.
771
+
772
+ ### enrichment-trailer-unknown-field
773
+
774
+ **Severity:** error
775
+
776
+ Fires when an `enrichmentTrailer` key doesn't match any declared `enrichment` field (a typo or drift the `keyof`-typed config already catches for TS authors).
777
+
778
+ **Fix:** rename the trailer key to a declared enrichment field, or remove it.
779
+
780
+ ---
781
+
711
782
  ## Escape hatches
712
783
 
713
784
  ### Dynamic upstream data
@@ -4,7 +4,7 @@ description: >
4
4
  Design the tool surface, resources, and service layer for a new MCP server. Use when starting a new server, planning a major feature expansion, or when the user describes a domain/API they want to expose via MCP. Produces a design doc at docs/design.md that drives implementation.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "2.12"
7
+ version: "2.14"
8
8
  audience: external
9
9
  type: workflow
10
10
  ---
@@ -351,6 +351,7 @@ output: z.object({
351
351
  - **Truncate large output with counts.** When a list exceeds a reasonable display size, show the top N and append "...and X more". Don't silently drop results.
352
352
  - **Spill big tabular results to a queryable surface.** When a tool's row set can exceed any reasonable context budget — paginated APIs, streamed exports, big query results — pair an inline preview with a `DataCanvas` table holding the full set, returned as a token the agent can SQL. Compute distributions or refinement hints across the full result, not the preview, so aggregate signal stays honest. See `api-canvas` for the `spillover()` helper.
353
353
  - **`format()` is the markdown twin of `structuredContent` — make both content-complete.** Different MCP clients forward different surfaces to the model: some (e.g., Claude Code) read `structuredContent` from `output`, others (e.g., Claude Desktop) read `content[]` from `format()`. Both must carry the same data so every client sees the same picture — `format()` just dresses it up with markdown. A thin `format()` that returns only a count or title leaves `content[]`-only clients blind to data that `structuredContent` clients can see. Render all fields the LLM needs, with structured markdown (headers, bold labels, lists) for readability.
354
+ - **Agent-facing context must reach both client surfaces — put it in `enrichment`.** `structuredContent` (from `output`) and `content[]` (from `format()`) are read by different clients. Empty-result notices, the query/filter as the server parsed it, and pagination totals — the context the agent *reasons with*, distinct from the domain payload — reach only `content[]` if hand-authored into `format()` text alone, leaving `structuredContent`-only clients (Claude Code) blind. (The reverse can't happen: `format-parity` drags every `output` field into `format()`, so `output`-authored context already reaches both.) An `enrichment` block — the success-path counterpart to `errors[]`, populated via `ctx.enrich(...)` — reaches both automatically: merged into `structuredContent`, advertised as `output.extend(enrichment)`, mirrored into a `content[]` trailer, no `format()` entry needed. How each field renders in that trailer is a per-tool call — a kind-tag (`notice`/`total`/`echo`/`delta`) when a canonical form fits, a domain key like `totalFound` otherwise, and an `enrichmentTrailer.render` for any structured (object/array) field so it doesn't ship as a JSON blob. See `add-tool`'s **Tool Response Design**.
354
355
 
355
356
  #### Batch input design
356
357
 
@@ -1,17 +1,17 @@
1
1
  ---
2
2
  name: git-wrapup
3
3
  description: >
4
- Land working-tree changes as a versioned release commit with an annotated tag version bump, changelog, regenerate derived artifacts, verify, commit, tag. Stops at "committed and tagged locally" — no push, no publish. The release-and-publish skill picks up from here. Distilled from the git_wrapup_instructions protocol.
4
+ Land working-tree changes as logical commits the work grouped by concern, topped by a release commit (version bump, changelog, regenerated artifacts) and an annotated tag. Verify, commit, tag. Stops at "committed and tagged locally" — no push, no publish. The release-and-publish skill picks up from here. Distilled from the git_wrapup_instructions protocol.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.1"
7
+ version: "1.2"
8
8
  audience: external
9
9
  type: workflow
10
10
  ---
11
11
 
12
12
  ## When to use
13
13
 
14
- Working-tree or staged changes are ready to ship as a new version. This skill lands them as a single atomic commit with an annotated tag. It does NOT push or publish — that's a separate step (`release-and-publish`).
14
+ Working-tree or staged changes are ready to ship as a new version. This skill lands them as a stack of logical commits — the work grouped by concern, topped by a release commit (version + changelog + tree) — with an annotated tag. It does NOT push or publish — that's a separate step (`release-and-publish`).
15
15
 
16
16
  Common triggers:
17
17
  - Feature work, bug fixes, or dependency updates are done and tested
@@ -119,27 +119,34 @@ bun run test:all # or `bun run test` if no test:all script exists
119
119
 
120
120
  **If either fails, halt.** Do not bypass verification to land the commit. Fix the issue first, then re-run from step 6.
121
121
 
122
- ### 7. Commit
122
+ ### 7. Commit — group by concern, release artifacts on top
123
123
 
124
- Stage everything and create ONE atomic commit. Version bumps ride with the change that warrants them the version bump is not a separate commit.
124
+ Do NOT `git add -A` into one commit. Group the working tree into a handful of logical commits never one blob:
125
+
126
+ 1. **The work — one commit per concern.** A feature spanning multiple layers splits by layer: runtime/logic, linter/tooling, docs/skills. Unrelated changes (two separate fixes, an incidental doc tweak) are their own commits. Work commits do not carry the version.
127
+ 2. **The release commit — last, on top.** Version bumps (`package.json`, `server.json`, README badge, `CLAUDE.md`/`AGENTS.md`), the changelog entry, `CHANGELOG.md`, and `docs/tree.md` go in a single final commit that sits on top of the work stack — never mixed into a feature commit.
128
+
129
+ Stage each group explicitly, commit it, then move to the next — the release commit goes last:
125
130
 
126
131
  ```bash
127
- git add -A
132
+ git add <paths-for-this-concern>
128
133
  git commit -m "<subject>"
134
+ # repeat per concern; version + changelog + tree are the final commit
129
135
  ```
130
136
 
131
- **Subject format:** Conventional Commits. Examples:
132
- - `feat: 0.1.5 — hosted server endpoint`
133
- - `fix: 0.2.1 — handle empty SPARQL result sets`
134
- - `chore(deps): 0.3.2 mcp-ts-core ^0.9.1 ^0.9.6`
137
+ **The file is the atomic boundary:** NEVER split a single file's changes across commits. When one file serves two concerns, it ships whole in the commit of its dominant concern.
138
+
139
+ **Subject format:** Conventional Commits.
140
+ - Work commits (no version): `feat: hosted server endpoint`, `fix: handle empty SPARQL result sets`, `feat(linter): enrichment contract rules`, `docs: document the enrichment block`
141
+ - Release commit (subject leads with the version): `chore(release): 0.2.1 — empty SPARQL result handling`
135
142
 
136
143
  **Rules:**
137
144
  - Plain `-m` flag only — no heredoc, no command substitution
138
145
  - No `Co-authored-by` or `Generated with` trailers
139
146
  - No marketing adjectives ("comprehensive", "robust", "enhanced", "seamless", "improved")
140
- - The commit message must stand alone for someone reading `git log` — no references to chat context, option numbers, or "as discussed"
147
+ - Each commit message stands alone for someone reading `git log` — no chat context, option numbers, or "as discussed"
141
148
 
142
- **When to split into multiple commits:** Only when the working tree contains genuinely independent concerns (e.g., two unrelated bug fixes in unrelated files). NEVER split a single file's changes across commits.
149
+ **Right-size it.** "Group by concern" is not "always split." A genuinely single-concern change — one fix, a dependency bump, a small doc edit — is one work commit plus the release commit; when the change and its version bump are inseparable for a tiny patch, a single commit whose subject leads with the version is fine. The failure mode to prevent is the inverse: a large, multi-layer feature crammed into one commit alongside the release artifacts.
143
150
 
144
151
  ### 8. Create an annotated tag
145
152
 
@@ -183,8 +190,8 @@ Dependency bumps:
183
190
  ### 9. Verify end state
184
191
 
185
192
  ```bash
186
- git log --oneline -1 # confirm commit subject
187
- git show v<version> --stat | head -20 # confirm tag points at HEAD
193
+ git log --oneline -8 # confirm the commit stack: work commits + release commit on top
194
+ git show v<version> --stat | head -20 # confirm tag points at HEAD (the release commit)
188
195
  git status # must be clean
189
196
  ```
190
197
 
@@ -211,7 +218,7 @@ If the working tree isn't clean or the tag doesn't point at HEAD, something went
211
218
  - [ ] `docs/tree.md` regenerated if structure changed (`bun run tree`)
212
219
  - [ ] `bun run devcheck` passes
213
220
  - [ ] `bun run test:all` (or `test`) passes
214
- - [ ] One atomic commit in Conventional Commits format
221
+ - [ ] Work grouped into logical commits (large features split by layer); release artifacts (version + changelog + tree) committed separately on top, subject leading with the version
215
222
  - [ ] Annotated tag `v<version>` with structured markdown message
216
223
  - [ ] Working tree clean
217
224
  - [ ] Nothing pushed — local only
@@ -4,7 +4,7 @@ description: >
4
4
  Investigate, adopt, and verify dependency updates — with special handling for `@cyanheads/mcp-ts-core`. Captures what changed, understands why, cross-references against the codebase, adopts framework improvements, syncs project skills, and runs final checks. Supports two entry modes: run the full flow end-to-end, or review updates you already applied.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "2.4"
7
+ version: "2.5"
8
8
  audience: external
9
9
  type: workflow
10
10
  ---
@@ -78,7 +78,7 @@ Scan specifically for:
78
78
  | Config changes | New env vars, renamed keys, changed defaults |
79
79
  | Linter rules | New definition-lint rules that may now flag existing tools/resources |
80
80
  | New or materially-changed skills | Note new skills or workflow changes (renamed steps, new checklist items) worth surfacing at end-of-run. Don't auto-invoke — some skills (e.g. `security-pass`) are user-triggered. The per-version changelog entries (e.g. 0.6.14 calling out `skills/security-pass/ (v1.0)`) name what changed. |
81
- | New template-scaffolded files | Compare `templates/` in the package against the project root. Files that `init` would create for a new project but don't exist in this project are adoption candidates — create them with project-specific values (version, name, description, env vars from `server.json`). Examples: `manifest.json`, `.mcpbignore`, `.codex-plugin/`, `.claude-plugin/`. Skip files the project has intentionally opted out of (documented in CLAUDE.md or a code comment). |
81
+ | New template-scaffolded files | Compare `templates/` in the package against the project root. Files that `init` would create for a new project but don't exist in this project are adoption candidates — create them with project-specific values (version, name, description, env vars from `server.json`). Examples: `manifest.json`, `.mcpbignore`, `.codex-plugin/`, `.claude-plugin/`. Skip files the project has intentionally opted out of (documented in CLAUDE.md/AGENTS.md or a code comment). |
82
82
  | Changelog `agent-notes` | Read `agent-notes` frontmatter from each new per-version changelog file — these carry release-specific adoption instructions for downstream consumers (new files to create, fields to populate, one-time migration steps). Apply them alongside other adoption work in Step 6. |
83
83
 
84
84
  Cross-reference each finding against the server's code. Collect adoption opportunities for Step 6.
@@ -103,7 +103,8 @@ Procedure:
103
103
  - If missing in project `skills/`, copy the full directory
104
104
  - If present, compare `metadata.version` — replace if the package version is newer
105
105
  - If the local version is equal or newer, skip (local override)
106
- 3. Do not touch skills in `skills/` that don't exist in the package (server-specific)
106
+ 3. Leave skills in `skills/` that lack `metadata.audience: external` untouched they're server-specific or sourced elsewhere, not framework-managed.
107
+ 4. **Prune framework skills deleted upstream.** A skill in `skills/` that *carries* `metadata.audience: external` but is **absent** from the package was removed upstream (e.g. `migrate-mcp-ts-template`, removed in 0.9.12) and lingers because sync was previously add/update-only. Delete it from `skills/` (and from the agent mirrors in Phase B). The `audience: external` marker is the provenance: it scopes the prune to framework-managed skills, so a server's own skills — which never carry it — are never touched. Before deleting, scan the skill for local edits worth keeping; if any exist, reconcile or surface them rather than discarding silently.
107
108
 
108
109
  **Skill diffs are adoption signal, not just sync output.** After replacing files in `skills/`, run `git diff skills/` to read what changed. Updated skill bodies describe new patterns, refined workflows, or new conventions — apply them to the codebase in Step 6 the same way you'd apply a framework API addition. The file copy is the *trigger*, not the work. The work is what the updated skill now says to do.
109
110
 
@@ -122,7 +123,7 @@ The `setup` skill instructs consumers to copy `skills/*` into their agent's skil
122
123
  For each agent directory that exists:
123
124
 
124
125
  1. For every directory in project `skills/`, copy it into the agent dir (overwrite on match, add if missing)
125
- 2. Do **not** delete skills in the agent dir that aren't in project `skills/` — they may be general-purpose skills sourced elsewhere (e.g., `code-security`, `cloudflare`, `changelog`)
126
+ 2. Do **not** delete skills in the agent dir that aren't in project `skills/` — they may be general-purpose skills sourced elsewhere (e.g., `code-security`, `cloudflare`, `changelog`). **Exception:** a framework skill pruned in Phase A step 4 — delete that same-named directory from each agent dir too. Match by the specific name you just removed, never by a blanket "absent from `skills/`" sweep (which would catch the externally-sourced skills above).
126
127
 
127
128
  If no agent directory exists, skip Phase B — the project hasn't opted in to per-agent skill copies.
128
129
 
@@ -154,7 +155,7 @@ Scripts in `scripts/` that aren't present in the package directory are project-s
154
155
  |:--|:--|
155
156
  | `templates/changelog/template.md` | `changelog/template.md` |
156
157
 
157
- Apply the same compare-and-overwrite logic. Add new entries here only when a template is explicitly documented as pristine in the framework's CLAUDE.md or its own header.
158
+ Apply the same compare-and-overwrite logic. Add new entries here only when a template is explicitly documented as pristine in the framework's CLAUDE.md/AGENTS.md or its own header.
158
159
 
159
160
  If the consumer customized a framework script or pristine reference (against guidance), the overwrite discards those changes. After the sync runs, diff `scripts/` and the affected template paths to surface replacements — review before committing. If a specific local customization needs to be preserved, revert that file using your git tools.
160
161
 
@@ -181,7 +182,7 @@ The consumer opted into the framework; its templates, skills, scripts, linter ru
181
182
 
182
183
  | ❌ Not a valid reason to defer | ✅ Valid reason to defer |
183
184
  |:-------------------------------|:-------------------------|
184
- | "Larger change than fits this pass" | Code-commented or `CLAUDE.md`-documented local override that intentionally diverges from the framework convention |
185
+ | "Larger change than fits this pass" | Code-commented or `CLAUDE.md`/`AGENTS.md`-documented local override that intentionally diverges from the framework convention |
185
186
  | "Marginal benefit / leaving as-is" | Breaking change with multiple migration paths that need user input |
186
187
  | "Per-tool refactor — worth doing as a focused follow-up" | Feature genuinely doesn't apply (the surface doesn't exist in this server) |
187
188
  | "Existing helper has rich domain messages we'd lose" | — (port the messages onto the framework path) |
@@ -221,7 +222,7 @@ Present a concise numbered summary to the user:
221
222
  3. **Features adopted** — new framework APIs now in use
222
223
  4. **Skills synced** — added/updated with versions (Phase A) and agent directories refreshed (Phase B)
223
224
  5. **New/changed skills available** — skills that appeared in Phase A for the first time or had materially-changed step sequences. Frame as "consider running when the time is right" rather than immediate actions; the user decides when to invoke them.
224
- 6. **Open decisions** — genuinely ambiguous items only. Valid: breaking changes with multiple migration paths needing user input, framework changes that conflict with a code-commented or `CLAUDE.md`-documented local override, third-party adoptions where cost/benefit is close. **Not valid:** framework adoptions deferred for scope, effort, or marginal-benefit reasoning — those were already adopted in Step 6 and belong under "Features adopted." If this section is empty, that's the expected outcome of a clean framework upgrade.
225
+ 6. **Open decisions** — genuinely ambiguous items only. Valid: breaking changes with multiple migration paths needing user input, framework changes that conflict with a code-commented or `CLAUDE.md`/`AGENTS.md`-documented local override, third-party adoptions where cost/benefit is close. **Not valid:** framework adoptions deferred for scope, effort, or marginal-benefit reasoning — those were already adopted in Step 6 and belong under "Features adopted." If this section is empty, that's the expected outcome of a clean framework upgrade.
225
226
  7. **Status** — rebuild / devcheck / test results
226
227
 
227
228
  ## Checklist
@@ -69,7 +69,7 @@ The orchestrator owns the goals. Workflow phases are not "run skill X" — they
69
69
  Before running a phase (or spawning a sub-agent for it), write down four things:
70
70
 
71
71
  1. **Goal** — the verifiable end state this phase must produce. Concrete and testable: "v0.5.2 tag exists at HEAD with structured-markdown annotation; `bun run devcheck` green; `npm view <pkg>@0.5.2` resolves." Not fuzzy: "ran the release-and-publish skill."
72
- 2. **Primary sources** — the specific files, GH issues, and reference docs the sub-agent must read directly. Inlining content into the prompt is a paraphrase that loses nuance; agents grounded in the source catch details the orchestrator's summary missed. For GH issues, instruct `gh issue view N --comments` — body alone misses thread clarifications. The orchestrator reads these sources too (to construct the prompt), but that's prompt construction, not a substitute for the sub-agent reading them.
72
+ 2. **Primary sources** — the specific files, GH issues, and reference docs the sub-agent must read directly. Inlining content into the prompt is a paraphrase that loses nuance; agents grounded in the source catch details the orchestrator's summary missed. For GH issues, instruct both `gh issue view N --comments` (the comment thread) and the timeline cross-reference query in the Orient block (what references the issue, including cross-repo) the body alone misses both. The orchestrator reads these sources too (to construct the prompt), but that's prompt construction, not a substitute for the sub-agent reading them.
73
73
  3. **Path** — the Tier 1 skill(s) and steps that get to the goal. This is what gets handed to the sub-agent.
74
74
  4. **Verification** — the read-only checks that confirm the goal was hit. Defined upfront, not as an afterthought.
75
75
 
@@ -91,6 +91,8 @@ Sub-agents are optional. Match the mechanism to your platform's capability — t
91
91
 
92
92
  Phases, gates, goals, and constraints are identical across all three tiers — only the fanout mechanism changes. Use the most capable tier available, and don't hand-roll what the platform does natively (e.g., rolling concurrency). Choose by scope and capability, not by default.
93
93
 
94
+ **Model tier ≠ orchestration tier.** A higher orchestration tier is not automatically the right choice. On some platforms, programmatically-orchestrated or *nested* sub-agents (an agent spawning agents) silently run on a cheaper/downgraded model, while the strongest model is reachable only by sub-agents the **main loop spawns directly** (tier 2). When a phase needs the top model (heavy generation, design, framework adoption), prefer direct main-loop fanout even when a more "capable" orchestration primitive exists — the primitive can cost you the model. Verify the model your platform actually assigned via its UI/telemetry; never infer it from a sub-agent's transcript, which interleaves auxiliary calls (titles, summaries) on cheaper models and will mislead you.
95
+
94
96
  The decision tree below is orthogonal to tier — it governs *whether* a given phase fans out, by target count and conflict risk:
95
97
 
96
98
  | Situation | Strategy |
@@ -119,10 +121,12 @@ order. If any file does not exist, note it and continue.
119
121
  available skills with descriptions and locations.
120
122
  5. Read the skill file(s) for this task: `[Tier 1 skill paths]`.
121
123
  6. Read the primary sources for this task directly — design docs (`docs/design.md`),
122
- GH issues (use `gh issue view <N> --comments` to capture the full thread, not
123
- just the body), handoff documents, reference/gold-standard files. List each
124
- one explicitly: `[primary source paths and gh commands]`. Skip this step only
125
- if no primary source applies (rare).
124
+ GH issues, handoff documents, reference/gold-standard files. For a GH issue, read
125
+ both the comment thread and its cross-references the body alone misses both:
126
+ - `gh issue view <N> --comments` description + comment thread
127
+ - `gh api 'repos/{owner}/{repo}/issues/<N>/timeline' --paginate --jq '.[] | select(.event=="cross-referenced") | .source.issue | "\(.repository.full_name)#\(.number) — \(.title)"'` — issues/PRs that reference this one, including from other repos
128
+ List each source explicitly: `[primary source paths and gh commands]`. Skip this
129
+ step only if no primary source applies (rare).
126
130
 
127
131
  Only after that, begin the task below.
128
132
 
@@ -133,7 +133,7 @@ Each sub-agent reads BOTH `skills/git-wrapup/SKILL.md` AND `skills/release-and-p
133
133
  - No bypass → Phase 4 runs serially, orchestrator-driven, one target at a time
134
134
  - No npm publish involved (private only) → non-issue
135
135
 
136
- **Commit structure.** Version bump rides with the change that warrants it. For a maintenance pass with a single cohesive change shape (e.g. just dep updates, or just one framework adoption), that's **one commit** subject can lead with the version (e.g. `feat: 0.5.2 — adopt new error factories, framework ^0.9.6`). For a maintenance pass containing multiple distinct concerns, split into per-concern commits with the version bump folded into the headline-change commit, not a separate ceremonial release commit.
136
+ **Commit structure.** Group the work by concern, then land the release artifacts (version bumps + changelog + regenerated `docs/tree.md`/`server.json`/`manifest.json`) as a `chore(release): <version> <theme>` commit on top same model as `git-wrapup` Step 7. A single-concern pass (just dep updates, or one framework adoption) is one work commit plus the release commit; a pass spanning multiple distinct concerns splits into per-concern work commits with the release commit last. Regenerated meta-drift is release-artifact-shaped — it rides in the release commit, never carved out as its own.
137
137
 
138
138
  **Tag annotations are for end users.** Internal dev cleanup (lockfile refreshes, linter fixes, build config) belongs in the commit body, not the tag annotation.
139
139
 
@@ -4,7 +4,7 @@ description: >
4
4
  Finalize documentation and project metadata for a ship-ready MCP server. Use after implementation is complete, tests pass, and devcheck is clean. Safe to run at any stage — each step checks current state and only acts on what still needs work.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "2.4"
7
+ version: "2.5"
8
8
  audience: external
9
9
  type: workflow
10
10
  ---
@@ -71,11 +71,11 @@ These sections should remain intact unless you have a specific reason to change
71
71
  - **Core Rules** — universal to all servers built on the framework
72
72
  - **Errors section** — "handlers throw, framework catches" is universal
73
73
  - **Imports section** — keep unless the alias convention was changed
74
- - **Framework reference pointer** — the line directing agents to `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md`
74
+ - **Framework reference pointer** — the line directing agents to `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` (or `AGENTS.md`)
75
75
 
76
76
  ## Pitfalls
77
77
 
78
- - Don't duplicate the full framework CLAUDE.md into the project file. The project file covers server-specific conventions; the framework file covers the API. The pointer at the top connects them.
78
+ - Don't duplicate the full framework CLAUDE.md/AGENTS.md into the project file. The project file covers server-specific conventions; the framework file covers the API. The pointer at the top connects them.
79
79
  - Don't remove `## Core Rules` even if it seems obvious — agents read this fresh each session.
80
80
  - Don't add implementation details that change frequently. The agent protocol file should be stable — update it when the server's shape changes, not on every commit.
81
81
  - Don't assume this is a one-time pass. The protocol file should be revisited whenever the server's surface area changes (tools added/removed, new services, config changes).
@@ -20,7 +20,7 @@ Framework badge ← solo spotlight row — `Built on @cy
20
20
  ## Configuration ← env var table + `.env.example` pointer
21
21
  ## Running the server ← dev, production, Workers/Docker
22
22
  ## Project structure ← directory/purpose table
23
- ## Development guide ← link to CLAUDE.md, key rules
23
+ ## Development guide ← link to CLAUDE.md/AGENTS.md, key rules
24
24
  ## Contributing ← brief
25
25
  ## License ← one line
26
26
  ```
@@ -471,12 +471,12 @@ Directory/purpose table orienting contributors to the codebase.
471
471
 
472
472
  ### Development Guide
473
473
 
474
- Brief — link to CLAUDE.md for full details. State 3-4 key rules. **Include the "validate → normalize → never fabricate" bullet** — it's the canonical anti-hallucination convention for external API wrappers and reinforces the framework's `no fabricated signal` principle.
474
+ Brief — link to CLAUDE.md/AGENTS.md for full details. State 3-4 key rules. **Include the "validate → normalize → never fabricate" bullet** — it's the canonical anti-hallucination convention for external API wrappers and reinforces the framework's `no fabricated signal` principle.
475
475
 
476
476
  ```markdown
477
477
  ## Development guide
478
478
 
479
- See [`CLAUDE.md`](./CLAUDE.md) for development guidelines and architectural rules. The short version:
479
+ See [`CLAUDE.md`/`AGENTS.md`](./CLAUDE.md) for development guidelines and architectural rules. The short version:
480
480
 
481
481
  - Handlers throw, framework catches — no `try/catch` in tool logic
482
482
  - Use `ctx.log` for request-scoped logging, `ctx.state` for tenant-scoped storage
@@ -30,7 +30,12 @@ For general `gh` CLI workflows outside issue filing (PRs, workflows, API access)
30
30
  4. **Search existing issues** — don't file duplicates:
31
31
 
32
32
  ```bash
33
- gh issue list -R cyanheads/mcp-ts-core --search "your error message or keyword"
33
+ gh issue list -R cyanheads/mcp-ts-core --search "your error message or keyword" --state all
34
+
35
+ # Assess a close match before commenting — is it already linked to a fix or referenced elsewhere?
36
+ gh issue view <number> -R cyanheads/mcp-ts-core --comments
37
+ gh api 'repos/cyanheads/mcp-ts-core/issues/<number>/timeline' --paginate \
38
+ --jq '.[] | select(.event=="cross-referenced") | .source.issue | "\(.repository.full_name)#\(.number) — \(.title)"'
34
39
  ```
35
40
 
36
41
  5. **For documentation- or contract-shaped requests, audit all three doc layers first** — proposals to add reference docs, public-API conventions, attribute/event catalogs, or stability commitments often duplicate surface that already exists. Check `src/` for behavior, `docs/` for human-facing reference, and `skills/` for agent-facing reference. Skill files marked `audience: external` are the framework's public contract — treat them as authoritative when evaluating whether a documentation gap exists. Also verify the constants or types you'd reference aren't already exported from `@cyanheads/mcp-ts-core` or one of its subpaths.
@@ -273,8 +278,8 @@ ISSUE
273
278
  ## Following Up
274
279
 
275
280
  ```bash
276
- # Check issue status
277
- gh issue view <number> -R cyanheads/mcp-ts-core
281
+ # Check issue status (with comment thread)
282
+ gh issue view <number> -R cyanheads/mcp-ts-core --comments
278
283
 
279
284
  # Add context or respond to maintainer questions
280
285
  gh issue comment <number> -R cyanheads/mcp-ts-core --body "Additional context..."
@@ -35,7 +35,12 @@ gh repo view --json nameWithOwner -q '.nameWithOwner'
35
35
  2. **Search existing issues** — if a close match exists (same symptom, different tool; same tool, different symptom; closed issue that might cover the new case), add a comment on that issue instead of filing a new one — unless the symptom or scope is distinct enough to warrant separate tracking:
36
36
 
37
37
  ```bash
38
- gh issue list --search "your error message or keyword"
38
+ gh issue list --search "your error message or keyword" --state all
39
+
40
+ # Assess a close match before commenting — is it already linked to a fix or referenced elsewhere?
41
+ gh issue view <number> --comments
42
+ gh api 'repos/{owner}/{repo}/issues/<number>/timeline' --paginate \
43
+ --jq '.[] | select(.event=="cross-referenced") | .source.issue | "\(.repository.full_name)#\(.number) — \(.title)"'
39
44
  ```
40
45
 
41
46
  3. **Reproduce the issue** — confirm it's reproducible. Note the exact input, transport mode, and any relevant env vars.
@@ -280,8 +285,8 @@ When genuinely ambiguous, file against this server's repo and note that it might
280
285
  ## Following Up
281
286
 
282
287
  ```bash
283
- # View issue details
284
- gh issue view <number>
288
+ # View issue details (with comment thread)
289
+ gh issue view <number> --comments
285
290
 
286
291
  # Add context
287
292
  gh issue comment <number> --body "Additional findings..."
@@ -4,7 +4,7 @@ description: >
4
4
  Post-init orientation for an MCP server built on @cyanheads/mcp-ts-core. Use after running `@cyanheads/mcp-ts-core init` to understand the project structure, conventions, and skill sync model. Also use when onboarding to an existing project for the first time.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.7"
7
+ version: "1.8"
8
8
  audience: external
9
9
  type: workflow
10
10
  ---
@@ -15,14 +15,9 @@ This skill assumes `bunx @cyanheads/mcp-ts-core init [name]` has already run. Th
15
15
 
16
16
  ## Agent Protocol File
17
17
 
18
- The init CLI generates both `CLAUDE.md` and `AGENTS.md` with the same purpose. Keep one authoritative file for the agent you actually use:
18
+ The init CLI generates both `CLAUDE.md` and `AGENTS.md` with identical content — `CLAUDE.md` is read by Claude Code, `AGENTS.md` by Codex, Cursor, Windsurf, and other agents. **Keep both.** Shipping both keeps the project agent-agnostic, and they're cheap to hold in sync: edit one, then `cp CLAUDE.md AGENTS.md` (the framework keeps its own pair byte-identical the same way, enforced by `check-docs-sync`). Only delete one if you're certain the project will never be opened by the other family of agents.
19
19
 
20
- - **Claude Code** keep `CLAUDE.md`, delete `AGENTS.md`
21
- - **All other agents** (Codex, Cursor, Windsurf, etc.) — keep `AGENTS.md`, delete `CLAUDE.md`
22
-
23
- Both files serve the same purpose: project-specific agent instructions. Prefer committing one authoritative copy rather than trying to keep both in sync by hand.
24
-
25
- For the full framework docs, read `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` once per session. It contains the exports catalog, tool/resource/prompt contracts, error codes, context API, and common import patterns.
20
+ For the full framework docs, read `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` (or its identical twin `AGENTS.md`) once per session. It contains the exports catalog, tool/resource/prompt contracts, error codes, context API, and common import patterns.
26
21
 
27
22
  ## Project Structure
28
23
 
@@ -155,12 +150,12 @@ Skip or reorder as the project calls for it. The agent protocol's "What's Next?"
155
150
 
156
151
  ## Checklist
157
152
 
158
- - [ ] Agent protocol file selectedone authoritative file kept (`CLAUDE.md` or `AGENTS.md`), the other deleted
153
+ - [ ] Agent protocol files keptboth `CLAUDE.md` and `AGENTS.md` present and in sync (or the unused one deliberately deleted)
159
154
  - [ ] `bun install` run
160
155
  - [ ] Dependencies updated (`bun update --latest`)
161
156
  - [ ] Git repo initialized and initial commit made (`chore: scaffold from @cyanheads/mcp-ts-core`)
162
157
  - [ ] Substituted server name verified in `package.json`, agent protocol file, and `server.json`
163
- - [ ] Framework docs read (`node_modules/@cyanheads/mcp-ts-core/CLAUDE.md`)
158
+ - [ ] Framework docs read (`node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` or `AGENTS.md`)
164
159
  - [ ] Unused echo definitions cleaned up (and unregistered from `src/index.ts`)
165
160
  - [ ] Skills copied to agent directory (`cp -R skills/* .claude/skills/` or equivalent)
166
161
  - [ ] Project structure understood (definitions directories, entry point)
@@ -309,7 +309,8 @@ When you complete a skill's checklist, check the boxes and add a completion time
309
309
  | `npm run devcheck` | Lint + format + typecheck + security + changelog sync |
310
310
  | `bun run audit:refresh` | Delete `bun.lock`, reinstall, and re-run `bun audit`. Use when `devcheck` flags a transitive advisory — Bun's `update` is sticky on transitive resolutions, so the advisory may be a stale-lockfile false positive. If it survives the refresh, it's real. |
311
311
  | `npm run tree` | Generate directory structure doc |
312
- | `npm run format` | Auto-fix formatting |
312
+ | `npm run format` | Auto-fix formatting (safe fixes only) |
313
+ | `npm run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior |
313
314
  | `npm test` | Run tests |
314
315
  | `npm run start:stdio` | Production mode (stdio) |
315
316
  | `npm run start:http` | Production mode (HTTP) |
@@ -309,7 +309,8 @@ When you complete a skill's checklist, check the boxes and add a completion time
309
309
  | `npm run devcheck` | Lint + format + typecheck + security + changelog sync |
310
310
  | `bun run audit:refresh` | Delete `bun.lock`, reinstall, and re-run `bun audit`. Use when `devcheck` flags a transitive advisory — Bun's `update` is sticky on transitive resolutions, so the advisory may be a stale-lockfile false positive. If it survives the refresh, it's real. |
311
311
  | `npm run tree` | Generate directory structure doc |
312
- | `npm run format` | Auto-fix formatting |
312
+ | `npm run format` | Auto-fix formatting (safe fixes only) |
313
+ | `npm run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior |
313
314
  | `npm test` | Run tests |
314
315
  | `npm run start:stdio` | Production mode (stdio) |
315
316
  | `npm run start:http` | Production mode (HTTP) |
@@ -1,6 +1,8 @@
1
1
  .env*
2
2
  .mcpregistry_*
3
3
  .claude/
4
+ .agents/
5
+ skills/
4
6
  Dockerfile
5
7
  bun.lock
6
8
  bunfig.toml