@cyanheads/mcp-ts-core 0.5.2 → 0.5.4
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.
- package/CLAUDE.md +5 -4
- package/README.md +2 -2
- package/dist/cli/init.js +8 -1
- package/dist/cli/init.js.map +1 -1
- package/dist/linter/rules/format-parity-rules.d.ts +7 -4
- package/dist/linter/rules/format-parity-rules.d.ts.map +1 -1
- package/dist/linter/rules/format-parity-rules.js +14 -8
- package/dist/linter/rules/format-parity-rules.js.map +1 -1
- package/dist/linter/validate.d.ts.map +1 -1
- package/dist/linter/validate.js +21 -2
- package/dist/linter/validate.js.map +1 -1
- package/dist/mcp-server/tools/utils/toolDefinition.d.ts +10 -9
- package/dist/mcp-server/tools/utils/toolDefinition.d.ts.map +1 -1
- package/dist/mcp-server/tools/utils/toolDefinition.js +2 -2
- package/dist/mcp-server/tools/utils/toolDefinition.js.map +1 -1
- package/package.json +3 -2
- package/scripts/check-docs-sync.ts +85 -0
- package/scripts/devcheck.ts +11 -3
- package/skills/add-tool/SKILL.md +5 -5
- package/skills/api-linter/SKILL.md +391 -0
- package/skills/design-mcp-server/SKILL.md +3 -3
- package/templates/AGENTS.md +4 -3
- package/templates/CLAUDE.md +4 -3
- package/templates/src/mcp-server/tools/definitions/echo.tool.ts +3 -2
package/CLAUDE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Agent Protocol
|
|
2
2
|
|
|
3
|
-
**Package:** `@cyanheads/mcp-ts-core` · **Version:** 0.5.
|
|
3
|
+
**Package:** `@cyanheads/mcp-ts-core` · **Version:** 0.5.3
|
|
4
4
|
**npm:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) · **Docker:** [ghcr.io/cyanheads/mcp-ts-core](https://ghcr.io/cyanheads/mcp-ts-core)
|
|
5
5
|
|
|
6
6
|
> **Developer note:** Never assume. Read related files and docs before making changes. Read full file content for context. Never edit a file before reading it.
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
- **Unified Context.** Handlers receive `ctx` with logging (`ctx.log`), tenant-scoped storage (`ctx.state`), optional protocol capabilities (`ctx.elicit`, `ctx.sample`), and cancellation (`ctx.signal`).
|
|
15
15
|
- **Decoupled storage.** `ctx.state` for tenant-scoped KV. Never access persistence backends directly.
|
|
16
16
|
- **Runtime parity.** All features work with `stdio`/`http` and Worker bundle. Guard non-portable deps via `runtimeCaps` from `@cyanheads/mcp-ts-core/utils` — a frozen capability object (`isNode`, `isBun`, `isWorkerLike`, `hasBuffer`, `hasProcess`, etc.) computed once at module load. Prefer runtime-agnostic abstractions (Hono + `@hono/mcp`, Fetch APIs).
|
|
17
|
-
- **Startup validation.** `createApp()` runs the definition linter before proceeding — errors (spec violations) throw `ConfigurationError` and block startup; warnings are logged. Also available standalone via `bun run lint:mcp` and as a devcheck step.
|
|
17
|
+
- **Startup validation.** `createApp()` runs the definition linter before proceeding — errors (spec violations) throw `ConfigurationError` and block startup; warnings are logged. Also available standalone via `bun run lint:mcp` and as a devcheck step. Every diagnostic links to the rule reference in `api-linter` skill; see that skill for the full rule catalog.
|
|
18
18
|
- **Elicitation for missing input.** Use `ctx.elicit` when the client supports it.
|
|
19
19
|
|
|
20
20
|
---
|
|
@@ -199,7 +199,7 @@ export const myTool = tool('my_tool', {
|
|
|
199
199
|
|
|
200
200
|
**Form-client safety:** Form-based MCP clients (MCP Inspector, web UIs) send optional object fields with empty-string inner values instead of `undefined`. Don't reject with `.min(1)` on optional fields — guard for meaningful values in the handler (`if (input.dateRange?.minDate && input.dateRange?.maxDate)`). Test with both omitted and empty-value payloads.
|
|
201
201
|
|
|
202
|
-
**`format`**: Maps output to MCP `content[]
|
|
202
|
+
**`format`**: Maps output to MCP `content[]`. 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 be content-complete** so every client sees the same data — `format()` is the markdown-rendered twin of `structuredContent`, not a separate payload. A thin `format()` (count or title only) leaves `content[]`-only clients blind to data that `structuredContent` clients can see. Enforced at lint time: every terminal field in `output` must appear in `format()`'s rendered text (via sentinel injection), or startup fails with a `format-parity` error. Primary fix: render the missing field in `format()` (use `z.discriminatedUnion` for list/detail variants — each branch is validated separately). Escape hatch: if the output schema was over-typed for a genuinely dynamic upstream API, relax it (`z.object({}).passthrough()`) rather than maintaining aspirational typing — passthrough still flows data to `structuredContent`. Omit `format` entirely for JSON stringify fallback. Additional formatters: `markdown()` (builder), `diffFormatter` (async), `tableFormatter`, `treeFormatter` from `/utils`.
|
|
203
203
|
|
|
204
204
|
**Task tools:** Add `task: true` for long-running async operations. Framework manages lifecycle: creates task → returns ID immediately → runs handler in background with `ctx.progress` → stores result/error → `ctx.signal` for cancellation. See `add-tool` skill for full example.
|
|
205
205
|
|
|
@@ -453,6 +453,7 @@ Detailed method signatures, options, and examples live in skill files. Read the
|
|
|
453
453
|
| `api-config` | `skills/api-config/SKILL.md` | AppConfig, parseConfig, env vars |
|
|
454
454
|
| `api-testing` | `skills/api-testing/SKILL.md` | createMockContext, test patterns, MockContextOptions |
|
|
455
455
|
| `api-workers` | `skills/api-workers/SKILL.md` | createWorkerHandler, CloudflareBindings, Worker runtime |
|
|
456
|
+
| `api-linter` | `skills/api-linter/SKILL.md` | Definition lint rules (`format-parity`, `schema-*`, `name-*`, `server-json-*`, …) — look here when devcheck reports a lint diagnostic |
|
|
456
457
|
| `add-tool` | `skills/add-tool/SKILL.md` | Scaffold a new MCP tool definition |
|
|
457
458
|
| `add-app-tool` | `skills/add-app-tool/SKILL.md` | Scaffold an MCP App tool + UI resource pair |
|
|
458
459
|
| `add-resource` | `skills/add-resource/SKILL.md` | Scaffold a new MCP resource definition |
|
|
@@ -483,7 +484,7 @@ Detailed method signatures, options, and examples live in skill files. Read the
|
|
|
483
484
|
- **JSDoc:** `@fileoverview` + `@module` required on every file
|
|
484
485
|
- **No fabricated signal:** Don't invent synthetic scores or arbitrary "confidence percentages." Surface real signal.
|
|
485
486
|
- **Builders:** `tool()`/`resource()`/`prompt()` with correct fields (`handler`, `input`, `output`, `format`, `auth`, `args`)
|
|
486
|
-
- **`format()` completeness:**
|
|
487
|
+
- **`format()` completeness:** different clients forward different surfaces (Claude Code reads `structuredContent`, Claude Desktop reads `content[]`) — both must carry the same data; `format()` is the markdown twin of `structuredContent`, not a reduced summary
|
|
487
488
|
- **Auth:** via `auth: ['scope']` on definitions (not HOF wrapper)
|
|
488
489
|
- **Presence checks:** `ctx.elicit`/`ctx.sample` checked before use
|
|
489
490
|
- **Task tools:** use `task: true` flag
|
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
<div align="center">
|
|
7
7
|
|
|
8
|
-
[](./CHANGELOG.md) [](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-11-25/changelog.mdx) [](https://modelcontextprotocol.io/) [](./LICENSE)
|
|
9
9
|
|
|
10
10
|
[](https://www.typescriptlang.org/) [](https://bun.sh/)
|
|
11
11
|
|
|
@@ -39,7 +39,7 @@ That's a complete MCP server. Every tool call is automatically logged with durat
|
|
|
39
39
|
- **Unified Context** — handlers receive a single `ctx` object with `ctx.log` (request-scoped logging), `ctx.state` (tenant-scoped storage), `ctx.elicit` (user prompting), `ctx.sample` (LLM completion), and `ctx.signal` (cancellation).
|
|
40
40
|
- **Inline auth** — `auth: ['scope']` on definitions. No wrapper functions. Framework checks scopes before calling your handler.
|
|
41
41
|
- **Task tools** — `task: true` flag for long-running operations. Framework manages the full lifecycle (create, poll, progress, complete/fail/cancel).
|
|
42
|
-
- **Definition linter** — `validateDefinitions()` checks tools, resources, and prompts against MCP spec at startup. Name format, schema structure, `.describe()` presence, JSON Schema serializability, auth scope validity, annotation coherence, URI template–params alignment, and **format-parity** (every field in a tool's `output` must be rendered by `format()` — verified via sentinel injection, since
|
|
42
|
+
- **Definition linter** — `validateDefinitions()` checks tools, resources, and prompts against MCP spec at startup. Name format, schema structure, `.describe()` presence, JSON Schema serializability, auth scope validity, annotation coherence, URI template–params alignment, and **format-parity** (every field in a tool's `output` must be rendered by `format()` — verified via sentinel injection, since different MCP clients forward different surfaces to the model and both `structuredContent` and `content[]` must carry the same data). Also available as a standalone CLI (`lint:mcp`) and devcheck step.
|
|
43
43
|
- **Structured error handling** — Handlers throw freely; the framework catches, classifies, and formats. Error factories (`notFound()`, `validationError()`, `serviceUnavailable()`, etc.) for precise control when the code matters. Auto-classification from plain `Error` messages when it doesn't.
|
|
44
44
|
- **Multi-backend storage** — `in-memory`, `filesystem`, `Supabase`, `Cloudflare D1/KV/R2`. Swap providers via env var without changing tool logic. Cursor pagination, batch ops, TTL, tenant isolation.
|
|
45
45
|
- **Pluggable auth** — `none`, `jwt`, or `oauth` modes. JWT with local secret or OAuth with JWKS verification.
|
package/dist/cli/init.js
CHANGED
|
@@ -12,7 +12,14 @@ const TEMPLATES_DIR = join(PACKAGE_ROOT, 'templates');
|
|
|
12
12
|
const SKILLS_DIR = join(PACKAGE_ROOT, 'skills');
|
|
13
13
|
const SCRIPTS_DIR = join(PACKAGE_ROOT, 'scripts');
|
|
14
14
|
// Keep in sync with package.json `files` — entries here must also appear there to ship in the npm package.
|
|
15
|
-
const SCAFFOLD_SCRIPTS = [
|
|
15
|
+
const SCAFFOLD_SCRIPTS = [
|
|
16
|
+
'build.ts',
|
|
17
|
+
'check-docs-sync.ts',
|
|
18
|
+
'clean.ts',
|
|
19
|
+
'devcheck.ts',
|
|
20
|
+
'lint-mcp.ts',
|
|
21
|
+
'tree.ts',
|
|
22
|
+
];
|
|
16
23
|
const TEXT_EXTENSIONS = new Set([
|
|
17
24
|
'.md',
|
|
18
25
|
'.ts',
|
package/dist/cli/init.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":";AACA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAClG,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAC/E,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;AACtD,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;AAChD,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;AAClD,2GAA2G;AAC3G,MAAM,gBAAgB,GAAG,
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
* @fileoverview Format-parity lint rule. Verifies that every field in a tool's
|
|
3
3
|
* `output` schema is actually rendered by its `format()` function.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* `
|
|
8
|
-
*
|
|
5
|
+
* Different MCP clients forward different surfaces to the model: some (e.g.,
|
|
6
|
+
* Claude Code) read `structuredContent` from `output`, others (e.g., Claude
|
|
7
|
+
* Desktop) read `content[]` from `format()`. For every client to see the same
|
|
8
|
+
* picture, both surfaces must be content-complete — `format()` is the
|
|
9
|
+
* markdown-rendered twin of `structuredContent`, not a separate payload.
|
|
10
|
+
* A field that exists in `output` but is never rendered by `format()` is
|
|
11
|
+
* invisible to `content[]`-only clients, silently diverging the two surfaces.
|
|
9
12
|
*
|
|
10
13
|
* Approach: sentinel injection.
|
|
11
14
|
* 1. Walk the output schema, build a synthetic value where every leaf is a
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format-parity-rules.d.ts","sourceRoot":"","sources":["../../../src/linter/rules/format-parity-rules.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"format-parity-rules.d.ts","sourceRoot":"","sources":["../../../src/linter/rules/format-parity-rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAwQlD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,GAAG,cAAc,EAAE,CAwEpF"}
|
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
* @fileoverview Format-parity lint rule. Verifies that every field in a tool's
|
|
3
3
|
* `output` schema is actually rendered by its `format()` function.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* `
|
|
8
|
-
*
|
|
5
|
+
* Different MCP clients forward different surfaces to the model: some (e.g.,
|
|
6
|
+
* Claude Code) read `structuredContent` from `output`, others (e.g., Claude
|
|
7
|
+
* Desktop) read `content[]` from `format()`. For every client to see the same
|
|
8
|
+
* picture, both surfaces must be content-complete — `format()` is the
|
|
9
|
+
* markdown-rendered twin of `structuredContent`, not a separate payload.
|
|
10
|
+
* A field that exists in `output` but is never rendered by `format()` is
|
|
11
|
+
* invisible to `content[]`-only clients, silently diverging the two surfaces.
|
|
9
12
|
*
|
|
10
13
|
* Approach: sentinel injection.
|
|
11
14
|
* 1. Walk the output schema, build a synthetic value where every leaf is a
|
|
@@ -321,10 +324,13 @@ export function lintFormatParity(def, displayName) {
|
|
|
321
324
|
diagnostics.push({
|
|
322
325
|
rule: 'format-parity',
|
|
323
326
|
severity: 'error',
|
|
324
|
-
message: `Tool '${displayName}' format() does not render output field '${displayPath}'
|
|
325
|
-
'
|
|
326
|
-
'
|
|
327
|
-
'
|
|
327
|
+
message: `Tool '${displayName}' format() does not render output field '${displayPath}'.\n` +
|
|
328
|
+
'Different MCP clients forward different surfaces to the model — both must be content-complete:\n' +
|
|
329
|
+
' • structuredContent (from `output`) — forwarded by clients like Claude Code\n' +
|
|
330
|
+
' • content[] (from `format()`) — forwarded by clients like Claude Desktop\n' +
|
|
331
|
+
'format() is the markdown-rendered twin of structuredContent, not a separate payload. Parity failure means one set of clients sees less than another.\n' +
|
|
332
|
+
'Primary fix: render the field in format(). For list/detail variants, use z.discriminatedUnion (the linter walks each branch separately).\n' +
|
|
333
|
+
'Escape hatch: if the output schema was over-typed for a genuinely dynamic upstream API, relax it (z.object({}).passthrough()) rather than maintaining aspirational typing — passthrough still flows data to structuredContent without declaring each field.',
|
|
328
334
|
definitionType: 'tool',
|
|
329
335
|
definitionName: displayName,
|
|
330
336
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format-parity-rules.js","sourceRoot":"","sources":["../../../src/linter/rules/format-parity-rules.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"format-parity-rules.js","sourceRoot":"","sources":["../../../src/linter/rules/format-parity-rules.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/linter/validate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,OAAO,KAAK,EAAkB,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/linter/validate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,OAAO,KAAK,EAAkB,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAuBxE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,SAAS,GAAG,UAAU,CA8DhE"}
|
package/dist/linter/validate.js
CHANGED
|
@@ -9,6 +9,24 @@ import { lintPromptDefinition } from './rules/prompt-rules.js';
|
|
|
9
9
|
import { lintResourceDefinition } from './rules/resource-rules.js';
|
|
10
10
|
import { lintServerJson } from './rules/server-json-rules.js';
|
|
11
11
|
import { lintAppToolResourcePairing, lintToolDefinition } from './rules/tool-rules.js';
|
|
12
|
+
/** Where the rule reference lives. Appended to every diagnostic message. */
|
|
13
|
+
const SKILL_REFERENCE_PATH = 'skills/api-linter/SKILL.md';
|
|
14
|
+
/**
|
|
15
|
+
* Maps a rule ID to its anchor in the api-linter skill doc. Most rules have
|
|
16
|
+
* a per-rule sub-header whose auto-generated anchor matches the rule ID. The
|
|
17
|
+
* server.json family (~40 rules) is documented in a single tabular section,
|
|
18
|
+
* so every `server-json-*` rule points to that section.
|
|
19
|
+
*/
|
|
20
|
+
function ruleAnchor(rule) {
|
|
21
|
+
return rule.startsWith('server-json-') ? 'server-json-rules' : rule;
|
|
22
|
+
}
|
|
23
|
+
/** Appends a "See: skills/api-linter/SKILL.md#<rule>" breadcrumb to the message. */
|
|
24
|
+
function withBreadcrumb(diagnostic) {
|
|
25
|
+
return {
|
|
26
|
+
...diagnostic,
|
|
27
|
+
message: `${diagnostic.message}\nSee: ${SKILL_REFERENCE_PATH}#${ruleAnchor(diagnostic.rule)}`,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
12
30
|
/**
|
|
13
31
|
* Validates MCP tool, resource, and prompt definitions against the MCP spec
|
|
14
32
|
* and framework conventions. Returns a structured report with errors and warnings.
|
|
@@ -71,8 +89,9 @@ export function validateDefinitions(input) {
|
|
|
71
89
|
diagnostics.push(...checkDuplicateNames(extractNames(prompts), 'prompt'));
|
|
72
90
|
// Cross-definition: app tool ↔ app resource pairing
|
|
73
91
|
diagnostics.push(...lintAppToolResourcePairing(tools, resources));
|
|
74
|
-
const
|
|
75
|
-
const
|
|
92
|
+
const annotated = diagnostics.map(withBreadcrumb);
|
|
93
|
+
const errors = annotated.filter((d) => d.severity === 'error');
|
|
94
|
+
const warnings = annotated.filter((d) => d.severity === 'warning');
|
|
76
95
|
return {
|
|
77
96
|
errors,
|
|
78
97
|
warnings,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/linter/validate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,0BAA0B,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAGvF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAgB;IAClD,MAAM,WAAW,GAAqB,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC;IAChC,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,EAAE,CAAC;IACxC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC;IAEpC,4BAA4B;IAC5B,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,WAAW,CAAC,IAAI,CAAC,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/C,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;QAC5B,WAAW,CAAC,IAAI,CAAC,GAAG,sBAAsB,CAAC,GAAG,CAAC,CAAC,CAAC;IACnD,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,WAAW,CAAC,IAAI,CAAC,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC;IACjD,CAAC;IAED,kCAAkC;IAClC,IAAI,KAAK,CAAC,UAAU,IAAI,IAAI,EAAE,CAAC;QAC7B,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,EAAE,OAAO,CAAC;QAC9C,WAAW,CAAC,IAAI,CACd,GAAG,cAAc,CACf,KAAK,CAAC,UAAU,EAChB,UAAU,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,SAAS,CAC5D,CACF,CAAC;IACJ,CAAC;IAED,oCAAoC;IACpC,MAAM,YAAY,GAAG,CAAC,IAAe,EAAE,EAAE,CACvC,IAAI;SACD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAE,CAA6B,EAAE,IAAI,CAAC;SAChD,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAEvE,WAAW,CAAC,IAAI,CAAC,GAAG,mBAAmB,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IAEtE,MAAM,aAAa,GAAG,SAAS;SAC5B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,CAAC,GAAG,CAA4B,CAAC;QACvC,OAAO,OAAO,CAAC,EAAE,IAAI,KAAK,QAAQ;YAChC,CAAC,CAAC,CAAC,CAAC,IAAI;YACR,CAAC,CAAC,OAAO,CAAC,EAAE,WAAW,KAAK,QAAQ;gBAClC,CAAC,CAAC,CAAC,CAAC,WAAW;gBACf,CAAC,CAAC,EAAE,CAAC;IACX,CAAC,CAAC;SACD,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC/B,WAAW,CAAC,IAAI,CAAC,GAAG,mBAAmB,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC,CAAC;IAEpE,WAAW,CAAC,IAAI,CAAC,GAAG,mBAAmB,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;IAE1E,oDAAoD;IACpD,WAAW,CAAC,IAAI,CAAC,GAAG,0BAA0B,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC;IAElE,MAAM,
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/linter/validate.ts"],"names":[],"mappings":"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"}
|
|
@@ -55,14 +55,15 @@ export interface ToolDefinition<TInput extends ZodObject<ZodRawShape> = ZodObjec
|
|
|
55
55
|
/** LLM-facing description. */
|
|
56
56
|
description: string;
|
|
57
57
|
/**
|
|
58
|
-
* Optional formatter mapping output to MCP `content[]
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
58
|
+
* Optional formatter mapping output to MCP `content[]`. Different MCP clients
|
|
59
|
+
* forward different surfaces to the model: some (e.g., Claude Code) read
|
|
60
|
+
* `structuredContent` from `output`, others (e.g., Claude Desktop) read
|
|
61
|
+
* `content[]` from `format()`. **Both must be content-complete** — `format()`
|
|
62
|
+
* is the markdown-rendered twin of `structuredContent`, not a separate payload.
|
|
62
63
|
*
|
|
63
|
-
* **Make `format()` content-complete.**
|
|
64
|
-
*
|
|
65
|
-
*
|
|
64
|
+
* **Make `format()` content-complete.** A thin one-liner (e.g., a count or
|
|
65
|
+
* title) leaves `content[]`-only clients blind to data that `structuredContent`
|
|
66
|
+
* clients can see. The `format-parity` lint rule enforces this at startup.
|
|
66
67
|
*
|
|
67
68
|
* If omitted, the handler factory JSON-stringifies the output as a fallback.
|
|
68
69
|
*/
|
|
@@ -106,8 +107,8 @@ export type AnyToolDefinition = ToolDefinition<ZodObject<ZodRawShape>, ZodObject
|
|
|
106
107
|
* ctx.log.info('Processing', { query: input.query });
|
|
107
108
|
* return { items: await search(input.query) };
|
|
108
109
|
* },
|
|
109
|
-
* // format() populates content[] — the
|
|
110
|
-
* //
|
|
110
|
+
* // format() populates content[] — the markdown twin of structuredContent.
|
|
111
|
+
* // Different clients read different surfaces; both must be content-complete.
|
|
111
112
|
* format: (result) => [{
|
|
112
113
|
* type: 'text',
|
|
113
114
|
* text: result.items.map(i => `**${i.id}**: ${i.name} (${i.status})`).join('\n'),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toolDefinition.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/utils/toolDefinition.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAErD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEjD;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc,CAC7B,MAAM,SAAS,SAAS,CAAC,WAAW,CAAC,GAAG,SAAS,CAAC,WAAW,CAAC,EAC9D,OAAO,SAAS,SAAS,CAAC,WAAW,CAAC,GAAG,SAAS,CAAC,WAAW,CAAC;IAE/D,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,qCAAqC;IACrC,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,+EAA+E;IAC/E,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,8BAA8B;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB
|
|
1
|
+
{"version":3,"file":"toolDefinition.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/utils/toolDefinition.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAErD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEjD;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc,CAC7B,MAAM,SAAS,SAAS,CAAC,WAAW,CAAC,GAAG,SAAS,CAAC,WAAW,CAAC,EAC9D,OAAO,SAAS,SAAS,CAAC,WAAW,CAAC,GAAG,SAAS,CAAC,WAAW,CAAC;IAE/D,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,qCAAqC;IACrC,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,+EAA+E;IAC/E,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,8BAA8B;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,YAAY,EAAE,CAAC;IACtD;;;OAGG;IACH,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAChG,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,MAAM,EAAE,OAAO,CAAC;IAChB,qEAAqE;IACrE,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,gEAAgE;AAChE,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC;AAM/F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,IAAI,CAAC,MAAM,SAAS,SAAS,CAAC,WAAW,CAAC,EAAE,OAAO,SAAS,SAAS,CAAC,WAAW,CAAC,EAChG,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,GACrD,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,CAEjC"}
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
* ctx.log.info('Processing', { query: input.query });
|
|
29
29
|
* return { items: await search(input.query) };
|
|
30
30
|
* },
|
|
31
|
-
* // format() populates content[] — the
|
|
32
|
-
* //
|
|
31
|
+
* // format() populates content[] — the markdown twin of structuredContent.
|
|
32
|
+
* // Different clients read different surfaces; both must be content-complete.
|
|
33
33
|
* format: (result) => [{
|
|
34
34
|
* type: 'text',
|
|
35
35
|
* text: result.items.map(i => `**${i.id}**: ${i.name} (${i.status})`).join('\n'),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toolDefinition.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/utils/toolDefinition.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"toolDefinition.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/utils/toolDefinition.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA4FH,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,UAAU,IAAI,CAClB,IAAY,EACZ,OAAsD;IAEtD,OAAO,EAAE,IAAI,EAAE,GAAG,OAAO,EAAE,CAAC;AAC9B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyanheads/mcp-ts-core",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.4",
|
|
4
4
|
"mcpName": "io.github.cyanheads/mcp-ts-core",
|
|
5
5
|
"description": "Agent-native TypeScript framework for building MCP servers. Build tools, not infrastructure. Declarative definitions with auth, multi-backend storage, OpenTelemetry, and first-class support for Node.js and Cloudflare Workers.",
|
|
6
6
|
"main": "dist/core/index.js",
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"files": [
|
|
9
9
|
"dist/",
|
|
10
10
|
"scripts/build.ts",
|
|
11
|
+
"scripts/check-docs-sync.ts",
|
|
11
12
|
"scripts/clean.ts",
|
|
12
13
|
"scripts/devcheck.ts",
|
|
13
14
|
"scripts/lint-mcp.ts",
|
|
@@ -250,7 +251,7 @@
|
|
|
250
251
|
},
|
|
251
252
|
"dependencies": {
|
|
252
253
|
"@hono/mcp": "^0.2.5",
|
|
253
|
-
"@hono/node-server": "^
|
|
254
|
+
"@hono/node-server": "^2.0.0",
|
|
254
255
|
"@modelcontextprotocol/ext-apps": "^1.6.0",
|
|
255
256
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
256
257
|
"@opentelemetry/api": "^1.9.1",
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Verifies that CLAUDE.md and AGENTS.md stay in sync. The init CLI
|
|
4
|
+
* ships both files byte-identical and each agent tool reads the file named for
|
|
5
|
+
* it — silent drift after edits leaves one agent on a stale protocol.
|
|
6
|
+
*
|
|
7
|
+
* Behavior:
|
|
8
|
+
* • Both exist, identical → pass
|
|
9
|
+
* • Both exist, drift → fail, print first divergent lines + fix hint
|
|
10
|
+
* • Only one exists → pass (report which file is present)
|
|
11
|
+
* • Neither exists → skip (not an mcp-ts-core project)
|
|
12
|
+
*
|
|
13
|
+
* Runs as a devcheck step and standalone: `bun run scripts/check-docs-sync.ts`.
|
|
14
|
+
*
|
|
15
|
+
* @module scripts/check-docs-sync
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
19
|
+
import { resolve } from 'node:path';
|
|
20
|
+
import process from 'node:process';
|
|
21
|
+
|
|
22
|
+
const CLAUDE_PATH = resolve('CLAUDE.md');
|
|
23
|
+
const AGENTS_PATH = resolve('AGENTS.md');
|
|
24
|
+
const MAX_DIFF_LINES = 20;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Line-by-line drift summary. Not a true unified diff — tools that move lines
|
|
28
|
+
* will show every shifted line as divergent, which is fine for the enforcement
|
|
29
|
+
* use case (the fix is always "reconcile both files" regardless).
|
|
30
|
+
*/
|
|
31
|
+
function summarizeDrift(a: string, b: string): string {
|
|
32
|
+
const aLines = a.split('\n');
|
|
33
|
+
const bLines = b.split('\n');
|
|
34
|
+
const max = Math.max(aLines.length, bLines.length);
|
|
35
|
+
const lines: string[] = [];
|
|
36
|
+
let drifts = 0;
|
|
37
|
+
|
|
38
|
+
for (let i = 0; i < max; i++) {
|
|
39
|
+
if (aLines[i] !== bLines[i]) {
|
|
40
|
+
drifts++;
|
|
41
|
+
if (drifts <= MAX_DIFF_LINES) {
|
|
42
|
+
const lineNo = String(i + 1).padStart(4);
|
|
43
|
+
if (aLines[i] !== undefined) lines.push(`${lineNo} - CLAUDE.md: ${aLines[i]}`);
|
|
44
|
+
if (bLines[i] !== undefined) lines.push(`${lineNo} + AGENTS.md: ${bLines[i]}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (drifts > MAX_DIFF_LINES) {
|
|
50
|
+
lines.push(` ... and ${drifts - MAX_DIFF_LINES} more diverging line(s)`);
|
|
51
|
+
}
|
|
52
|
+
return lines.join('\n');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const hasClaude = existsSync(CLAUDE_PATH);
|
|
56
|
+
const hasAgents = existsSync(AGENTS_PATH);
|
|
57
|
+
|
|
58
|
+
if (!hasClaude && !hasAgents) {
|
|
59
|
+
console.log('Skipped: neither CLAUDE.md nor AGENTS.md exists.');
|
|
60
|
+
process.exit(0);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (hasClaude !== hasAgents) {
|
|
64
|
+
const present = hasClaude ? 'CLAUDE.md' : 'AGENTS.md';
|
|
65
|
+
const absent = hasClaude ? 'AGENTS.md' : 'CLAUDE.md';
|
|
66
|
+
console.log(`${present} found. No ${absent} found — nothing to sync.`);
|
|
67
|
+
process.exit(0);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const claude = readFileSync(CLAUDE_PATH, 'utf-8');
|
|
71
|
+
const agents = readFileSync(AGENTS_PATH, 'utf-8');
|
|
72
|
+
|
|
73
|
+
if (claude === agents) {
|
|
74
|
+
console.log('CLAUDE.md and AGENTS.md are in sync.');
|
|
75
|
+
process.exit(0);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
console.error('CLAUDE.md and AGENTS.md have drifted:');
|
|
79
|
+
console.error('');
|
|
80
|
+
console.error(summarizeDrift(claude, agents));
|
|
81
|
+
console.error('');
|
|
82
|
+
console.error(
|
|
83
|
+
'Fix: edit both files together, or `cp CLAUDE.md AGENTS.md` (or reverse) if one is canonical.',
|
|
84
|
+
);
|
|
85
|
+
process.exit(1);
|
package/scripts/devcheck.ts
CHANGED
|
@@ -67,7 +67,7 @@ const createColor = (open: string, close: string, closeRe: RegExp) => (str: stri
|
|
|
67
67
|
return open + `${str}`.replace(closeRe, close + open) + close;
|
|
68
68
|
};
|
|
69
69
|
|
|
70
|
-
const esc = (code: string) => new RegExp(code.replace(
|
|
70
|
+
const esc = (code: string) => new RegExp(code.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g');
|
|
71
71
|
const c = {
|
|
72
72
|
bold: createColor('\x1b[1m', '\x1b[22m', esc('\x1b[22m')),
|
|
73
73
|
dim: createColor('\x1b[2m', '\x1b[22m', esc('\x1b[22m')),
|
|
@@ -411,7 +411,15 @@ const ALL_CHECKS: Check[] = [
|
|
|
411
411
|
canFix: false,
|
|
412
412
|
getCommand: () => ['bun', 'run', 'scripts/lint-mcp.ts'],
|
|
413
413
|
tip: (c) =>
|
|
414
|
-
`Fix definition errors
|
|
414
|
+
`Fix definition errors above — each diagnostic links to its rule in ${c.bold('skills/api-linter/SKILL.md')}.`,
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
name: 'Docs Sync',
|
|
418
|
+
flag: '--no-docs-sync',
|
|
419
|
+
canFix: false,
|
|
420
|
+
getCommand: () => ['bun', 'run', 'scripts/check-docs-sync.ts'],
|
|
421
|
+
tip: (c) =>
|
|
422
|
+
`Edit both files together, or run ${c.bold('cp CLAUDE.md AGENTS.md')} (or reverse) to resync.`,
|
|
415
423
|
},
|
|
416
424
|
{
|
|
417
425
|
name: 'Biome',
|
|
@@ -554,7 +562,7 @@ const ALL_CHECKS: Check[] = [
|
|
|
554
562
|
return unexpected.length === 0;
|
|
555
563
|
},
|
|
556
564
|
tip: (c) =>
|
|
557
|
-
`Run ${c.bold(`${PM_CMD} update`)} to upgrade
|
|
565
|
+
`Run ${c.bold(`${PM_CMD} update`)} to upgrade; the ${c.bold('maintenance')} skill then investigates changelogs and adopts upstream changes. Configure allowlist in ${c.bold('devcheck.config.json')}.`,
|
|
558
566
|
},
|
|
559
567
|
];
|
|
560
568
|
|
package/skills/add-tool/SKILL.md
CHANGED
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Scaffold a new MCP tool definition. Use when the user asks to add a tool, create a new tool, or implement a new capability for the server.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.6"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -57,9 +57,9 @@ export const {{TOOL_EXPORT}} = tool('{{tool_name}}', {
|
|
|
57
57
|
return { /* output */ };
|
|
58
58
|
},
|
|
59
59
|
|
|
60
|
-
// format() populates MCP content[] — the
|
|
61
|
-
//
|
|
62
|
-
//
|
|
60
|
+
// format() populates MCP content[] — the markdown twin of structuredContent.
|
|
61
|
+
// Different clients read different surfaces (Claude Code → structuredContent,
|
|
62
|
+
// Claude Desktop → content[]), so both must carry the same data.
|
|
63
63
|
// Enforced at lint time: every field in `output` must appear in the rendered text.
|
|
64
64
|
format: (result) => {
|
|
65
65
|
const lines: string[] = [];
|
|
@@ -313,7 +313,7 @@ Large payloads burn the agent's context window. Default to curated summaries; of
|
|
|
313
313
|
- [ ] JSDoc `@fileoverview` and `@module` header present
|
|
314
314
|
- [ ] Optional nested objects guarded for empty inner values from form-based clients (check `?.field` truthiness, not just object presence)
|
|
315
315
|
- [ ] `handler(input, ctx)` is pure — throws on failure, no try/catch
|
|
316
|
-
- [ ] `format()` renders every field in the output schema — enforced at lint time via sentinel injection, startup fails with `format-parity` errors otherwise. `content[]`
|
|
316
|
+
- [ ] `format()` renders every field in the output schema — enforced at lint time via sentinel injection, startup fails with `format-parity` errors otherwise. Different clients forward different surfaces (Claude Code → `structuredContent`, Claude Desktop → `content[]`); both must carry the same data. Primary fix: render the missing field in `format()` (use `z.discriminatedUnion` for list/detail variants). Escape hatch: if the output schema was over-typed for a genuinely dynamic upstream API, relax it (`z.object({}).passthrough()`) rather than maintaining aspirational typing
|
|
317
317
|
- [ ] If wrapping external API: output schema and `format()` preserve uncertainty from sparse upstream payloads instead of inventing concrete values
|
|
318
318
|
- [ ] `auth` scopes declared if the tool needs authorization
|
|
319
319
|
- [ ] `task: true` added if the tool is long-running
|
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: api-linter
|
|
3
|
+
description: >
|
|
4
|
+
MCP definition linter rules reference. Use when `bun run lint:mcp`, `bun run devcheck`, or `createApp()` startup 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
|
+
metadata:
|
|
6
|
+
author: cyanheads
|
|
7
|
+
version: "1.0"
|
|
8
|
+
audience: external
|
|
9
|
+
type: reference
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
The linter validates tool, resource, and prompt definitions against the MCP spec and framework conventions. It runs in three places:
|
|
15
|
+
|
|
16
|
+
| Entry point | When | On failure |
|
|
17
|
+
|:------------|:-----|:-----------|
|
|
18
|
+
| `createApp()` / `createWorkerHandler()` | Every startup | Throws `ConfigurationError`; process exits with a formatted banner. Warnings are logged and startup continues. |
|
|
19
|
+
| `bun run lint:mcp` | Manual or CI | Prints errors + warnings, exits non-zero on errors. |
|
|
20
|
+
| `bun run devcheck` | Pre-commit workflow | Wraps `lint:mcp` alongside typecheck, format, `bun audit`, `bun outdated`. |
|
|
21
|
+
|
|
22
|
+
All three surface the same `LintReport` from `validateDefinitions()` (exported from `@cyanheads/mcp-ts-core/linter`). Each diagnostic has a stable `rule` ID — that's the anchor you land on via the `See: skills/api-linter/SKILL.md#<rule>` breadcrumb appended to every message.
|
|
23
|
+
|
|
24
|
+
**Severity:**
|
|
25
|
+
- **error** — MUST-level spec violation; blocks startup.
|
|
26
|
+
- **warning** — SHOULD-level or quality issue; logged but startup continues.
|
|
27
|
+
|
|
28
|
+
**Imports (if you need to run the linter programmatically):**
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { validateDefinitions } from '@cyanheads/mcp-ts-core/linter';
|
|
32
|
+
import type { LintReport, LintDiagnostic } from '@cyanheads/mcp-ts-core/linter';
|
|
33
|
+
|
|
34
|
+
const report = validateDefinitions({ tools, resources, prompts, serverJson, packageJson });
|
|
35
|
+
if (!report.passed) process.exit(1);
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Rule index
|
|
41
|
+
|
|
42
|
+
Grouped by family. Jump to any rule ID via its anchor.
|
|
43
|
+
|
|
44
|
+
| Family | Rules | Section |
|
|
45
|
+
|:-------|:------|:--------|
|
|
46
|
+
| Format parity | `format-parity`, `format-parity-threw`, `format-parity-walk-failed` | [Format parity](#format-parity) |
|
|
47
|
+
| Schema | `schema-is-object`, `describe-on-fields`, `schema-serializable` | [Schema rules](#schema-rules) |
|
|
48
|
+
| Names | `name-required`, `name-format`, `name-unique` | [Name rules](#name-rules) |
|
|
49
|
+
| Tools | `description-required`, `handler-required`, `auth-type`, `auth-scope-format`, `annotation-type`, `annotation-coherence`, `meta-ui-type`, `meta-ui-resource-uri-required`, `meta-ui-resource-uri-scheme`, `app-tool-resource-pairing` | [Tool rules](#tool-rules) |
|
|
50
|
+
| Resources | `uri-template-required`, `uri-template-valid`, `resource-name-not-uri`, `template-params-align` | [Resource rules](#resource-rules) |
|
|
51
|
+
| Prompts | `generate-required` | [Prompt rules](#prompt-rules) |
|
|
52
|
+
| server.json | ~40 rules prefixed `server-json-*` | [server.json rules](#server-json-rules) |
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Format parity
|
|
57
|
+
|
|
58
|
+
Why this family exists: different MCP clients forward different surfaces of a tool response to the model. Claude Code reads `structuredContent` (from your handler's return value, typed by `output`). Claude Desktop reads `content[]` (from your `format()` function). Every field must be visible on both surfaces or one class of client sees less than another. The linter enforces this by synthesizing a sample value where every leaf is a uniquely identifiable sentinel, calling `format()` once, then verifying each sentinel (or its key name, for permissive types like booleans) appears in the rendered text.
|
|
59
|
+
|
|
60
|
+
### format-parity
|
|
61
|
+
|
|
62
|
+
**Severity:** error
|
|
63
|
+
|
|
64
|
+
Fires when `format()` does not render a field present in `output`. Emitted once per missing field; large schemas can produce many `format-parity` diagnostics from a single tool.
|
|
65
|
+
|
|
66
|
+
**Primary fix:** render the missing field in `format()`. For tools that return either a summary list or a detail view, use `z.discriminatedUnion` so each branch is walked separately:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
output: z.discriminatedUnion('mode', [
|
|
70
|
+
z.object({ mode: z.literal('list'), items: z.array(ItemSchema) }),
|
|
71
|
+
z.object({ mode: z.literal('detail'), item: ItemSchema, history: z.array(HistoryEntry) }),
|
|
72
|
+
]),
|
|
73
|
+
|
|
74
|
+
format: (result) => {
|
|
75
|
+
if (result.mode === 'list') return renderList(result.items);
|
|
76
|
+
return renderDetail(result.item, result.history);
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Escape hatch:** if the output schema was over-typed for a genuinely dynamic upstream API (e.g., a third-party JSON blob whose shape you can't nail down), relax it:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
output: z.object({}).passthrough()
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`passthrough()` still flows the full payload to `structuredContent` without declaring each field, so the linter has nothing to check against and you're not maintaining aspirational typing.
|
|
87
|
+
|
|
88
|
+
**Anti-pattern:** summary-only `format()` like `return [{ type: 'text', text: \`Found ${n} items\` }]`. The sentinel walk will flag every field in the items array. Don't "fix" this by removing fields from `output` — that makes `structuredContent` clients blind too.
|
|
89
|
+
|
|
90
|
+
### format-parity-threw
|
|
91
|
+
|
|
92
|
+
**Severity:** warning
|
|
93
|
+
|
|
94
|
+
Fires when `format()` throws while being called with a synthetic sample. The linter cannot verify parity because your formatter crashed before producing output.
|
|
95
|
+
|
|
96
|
+
**Fix:** `format()` must be **total** — render any valid value of the output schema without throwing. Common causes:
|
|
97
|
+
|
|
98
|
+
- Assuming an optional array is always present (`result.items.map(...)` when `items` could be `undefined`)
|
|
99
|
+
- Dereferencing a discriminated-union branch without checking the discriminator
|
|
100
|
+
- Calling `toFixed()` or `toISOString()` on a value that could legitimately be any number/string
|
|
101
|
+
|
|
102
|
+
Add narrow guards. The linter feeds a synthetic but schema-valid value; if your formatter can't handle it, real inputs will eventually hit the same path.
|
|
103
|
+
|
|
104
|
+
### format-parity-walk-failed
|
|
105
|
+
|
|
106
|
+
**Severity:** warning
|
|
107
|
+
|
|
108
|
+
Fires when the linter cannot walk the output schema to build a synthetic sample (usually because the schema uses an unusual composition the walker doesn't recognize). Parity is not verified for that tool — nothing is broken at runtime, but the check is silently disabled.
|
|
109
|
+
|
|
110
|
+
**Fix:** inspect the walker error message in the diagnostic. Usually caused by very deep recursion, custom Zod extensions, or mixing Zod 3 and 4 schema internals. File an issue against `@cyanheads/mcp-ts-core` with the schema shape — this is a linter gap, not user error.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Schema rules
|
|
115
|
+
|
|
116
|
+
### schema-is-object
|
|
117
|
+
|
|
118
|
+
**Severity:** error
|
|
119
|
+
|
|
120
|
+
Tool `input`/`output` and prompt `args` must be `z.object({...})` at the top level (not `z.string()`, `z.array(...)`, etc.). The MCP spec requires a keyed structure at the schema root.
|
|
121
|
+
|
|
122
|
+
**Fix:** wrap whatever you had in a single-key object:
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
// Wrong
|
|
126
|
+
input: z.array(z.string())
|
|
127
|
+
// Right
|
|
128
|
+
input: z.object({ items: z.array(z.string()).describe('List of items') })
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### describe-on-fields
|
|
132
|
+
|
|
133
|
+
**Severity:** warning
|
|
134
|
+
|
|
135
|
+
Every field in `input`, `output`, `params`, or `args` needs a `.describe('...')` call. Descriptions ship to the client and the LLM — missing ones make tools harder to use correctly.
|
|
136
|
+
|
|
137
|
+
**Fix:** add `.describe('...')` to every leaf. The linter reports which path is missing a description (e.g., `input.filters.status`), so it's a mechanical fix.
|
|
138
|
+
|
|
139
|
+
### schema-serializable
|
|
140
|
+
|
|
141
|
+
**Severity:** error
|
|
142
|
+
|
|
143
|
+
Input/output schemas must use JSON-Schema-serializable Zod types only. The MCP SDK converts schemas to JSON Schema for `tools/list`; non-serializable types cause a hard runtime failure.
|
|
144
|
+
|
|
145
|
+
**Disallowed:** `z.custom()`, `z.date()`, `z.transform()`, `z.bigint()`, `z.symbol()`, `z.void()`, `z.map()`, `z.set()`, `z.function()`, `z.nan()`.
|
|
146
|
+
|
|
147
|
+
**Fix:** use structural equivalents. Most common swap:
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
// Wrong
|
|
151
|
+
z.date()
|
|
152
|
+
// Right
|
|
153
|
+
z.string().describe('ISO 8601 timestamp, e.g., 2026-04-20T12:00:00Z')
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Parse the string to a `Date` inside the handler if you need one.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Name rules
|
|
161
|
+
|
|
162
|
+
### name-required
|
|
163
|
+
|
|
164
|
+
**Severity:** error
|
|
165
|
+
|
|
166
|
+
Every tool, resource, and prompt definition needs a non-empty `name` string. For resources, an empty `name` also falls back to the URI template (see `resource-name-not-uri`).
|
|
167
|
+
|
|
168
|
+
### name-format
|
|
169
|
+
|
|
170
|
+
**Severity:** error
|
|
171
|
+
|
|
172
|
+
Names must match `^[a-zA-Z0-9._-]+$` (alphanumerics, dots, hyphens, underscores). Tools conventionally use `snake_case`, resources and prompts use `kebab-case` or `snake_case`.
|
|
173
|
+
|
|
174
|
+
**Fix:** rename to a valid identifier. If the legacy name is user-facing, keep `title` as the display string and use a valid `name` internally.
|
|
175
|
+
|
|
176
|
+
### name-unique
|
|
177
|
+
|
|
178
|
+
**Severity:** error
|
|
179
|
+
|
|
180
|
+
Tool names, resource names, and prompt names must each be unique within their type. Duplicates would cause the client to see only one.
|
|
181
|
+
|
|
182
|
+
**Fix:** rename one, or consolidate into a single definition if they're actually the same tool.
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Tool rules
|
|
187
|
+
|
|
188
|
+
### description-required
|
|
189
|
+
|
|
190
|
+
**Severity:** warning
|
|
191
|
+
|
|
192
|
+
Every tool, resource, and prompt needs a non-empty `description`. This is what the client shows the LLM to decide whether to call the definition. A missing description dramatically hurts selection accuracy.
|
|
193
|
+
|
|
194
|
+
Also applies to resources and prompts (same rule ID, different `definitionType`).
|
|
195
|
+
|
|
196
|
+
**Fix:** write a single cohesive paragraph. Prose, not bullet lists. Descriptions render inline in most clients.
|
|
197
|
+
|
|
198
|
+
### handler-required
|
|
199
|
+
|
|
200
|
+
**Severity:** error
|
|
201
|
+
|
|
202
|
+
Every tool must have a `handler` function (or `taskHandlers` object for task tools). Every resource must have a `handler`. Definitions without handlers can't do anything at runtime.
|
|
203
|
+
|
|
204
|
+
Also applies to resources (same rule ID, different `definitionType`).
|
|
205
|
+
|
|
206
|
+
### auth-type
|
|
207
|
+
|
|
208
|
+
**Severity:** error
|
|
209
|
+
|
|
210
|
+
`auth` must be an array of strings. A single string or other shape is rejected.
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
// Wrong
|
|
214
|
+
auth: 'tool:my_tool:read'
|
|
215
|
+
// Right
|
|
216
|
+
auth: ['tool:my_tool:read']
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### auth-scope-format
|
|
220
|
+
|
|
221
|
+
**Severity:** error
|
|
222
|
+
|
|
223
|
+
Every element in `auth` must be a non-empty string. Empty strings in the array are rejected — they'd match anything.
|
|
224
|
+
|
|
225
|
+
### annotation-type
|
|
226
|
+
|
|
227
|
+
**Severity:** warning
|
|
228
|
+
|
|
229
|
+
`annotations` hints (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`) must be booleans. Strings like `'yes'` or numbers are rejected — the MCP spec defines these as booleans and clients may type-check.
|
|
230
|
+
|
|
231
|
+
### annotation-coherence
|
|
232
|
+
|
|
233
|
+
**Severity:** warning
|
|
234
|
+
|
|
235
|
+
Contradictory annotation combinations. The canonical case: `readOnlyHint: true` with `destructiveHint: true` — a read-only tool cannot be destructive. `idempotentHint: true` alongside `readOnlyHint: true` is fine (explicit redundancy is allowed).
|
|
236
|
+
|
|
237
|
+
### meta-ui-type
|
|
238
|
+
|
|
239
|
+
**Severity:** error (MCP Apps tools only)
|
|
240
|
+
|
|
241
|
+
When a tool declares `_meta.ui`, that field must be an object. `null`, arrays, or primitives are rejected.
|
|
242
|
+
|
|
243
|
+
### meta-ui-resource-uri-required
|
|
244
|
+
|
|
245
|
+
**Severity:** error (MCP Apps tools only)
|
|
246
|
+
|
|
247
|
+
`_meta.ui.resourceUri` must be a non-empty string. This is the URI the client resolves to load the app UI.
|
|
248
|
+
|
|
249
|
+
### meta-ui-resource-uri-scheme
|
|
250
|
+
|
|
251
|
+
**Severity:** warning (MCP Apps tools only)
|
|
252
|
+
|
|
253
|
+
`_meta.ui.resourceUri` should use the `ui://` scheme. Other schemes (like `https://`) work but are discouraged — the `ui://` convention signals the resource is meant to be hosted by the MCP server, not fetched externally.
|
|
254
|
+
|
|
255
|
+
### app-tool-resource-pairing
|
|
256
|
+
|
|
257
|
+
**Severity:** warning (MCP Apps tools only)
|
|
258
|
+
|
|
259
|
+
An app tool's `_meta.ui.resourceUri` must match the `uriTemplate` of a registered resource. This catches the common mistake of renaming one side of the pair and forgetting the other.
|
|
260
|
+
|
|
261
|
+
**Fix:** either correct the `resourceUri` to match an existing resource, or register the resource it references. Use the `add-app-tool` skill's paired scaffold to avoid this.
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Resource rules
|
|
266
|
+
|
|
267
|
+
### uri-template-required
|
|
268
|
+
|
|
269
|
+
**Severity:** error
|
|
270
|
+
|
|
271
|
+
Every resource needs a non-empty `uriTemplate` string. The URI template is the resource's primary identifier.
|
|
272
|
+
|
|
273
|
+
### uri-template-valid
|
|
274
|
+
|
|
275
|
+
**Severity:** error
|
|
276
|
+
|
|
277
|
+
`uriTemplate` must be syntactically valid per RFC 6570: balanced braces, non-empty variable names. `test://{id/data` (unbalanced) and `test://{}/data` (empty variable) are rejected.
|
|
278
|
+
|
|
279
|
+
### resource-name-not-uri
|
|
280
|
+
|
|
281
|
+
**Severity:** warning
|
|
282
|
+
|
|
283
|
+
Warns when the resource's `name` defaults to the URI template because no explicit name was provided. URIs make poor display names — clients often show them verbatim.
|
|
284
|
+
|
|
285
|
+
**Fix:** add a short `name` field:
|
|
286
|
+
|
|
287
|
+
```ts
|
|
288
|
+
resource('myscheme://{id}/data', {
|
|
289
|
+
name: 'Item data', // <-- add this
|
|
290
|
+
// ...
|
|
291
|
+
})
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### template-params-align
|
|
295
|
+
|
|
296
|
+
**Severity:** error
|
|
297
|
+
|
|
298
|
+
Every variable in the URI template must appear as a key in the `params` schema, and vice versa. `test://{itemId}/data` with `params: z.object({ item_id: ... })` is rejected — casing mismatches count.
|
|
299
|
+
|
|
300
|
+
**Fix:** rename one side so they match exactly. The error message names which variables are on which side.
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## Prompt rules
|
|
305
|
+
|
|
306
|
+
### generate-required
|
|
307
|
+
|
|
308
|
+
**Severity:** error
|
|
309
|
+
|
|
310
|
+
Every prompt needs a `generate` function that returns the message array. Prompts without `generate` have nothing to produce.
|
|
311
|
+
|
|
312
|
+
(Prompts also share `name-*` and `description-required` rules from their respective families.)
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## server.json rules
|
|
317
|
+
|
|
318
|
+
Validates the `server.json` manifest at project root against the [MCP server manifest spec](https://modelcontextprotocol.io/specification). Every rule below fires only when a `server.json` is present.
|
|
319
|
+
|
|
320
|
+
| Rule ID | Severity | What it checks |
|
|
321
|
+
|:--------|:---------|:---------------|
|
|
322
|
+
| `server-json-type` | error | `server.json` must be a JSON object, not an array or primitive |
|
|
323
|
+
| `server-json-name-required` | error | `name` must be present and non-empty |
|
|
324
|
+
| `server-json-name-length` | error | `name` length 3–200 characters |
|
|
325
|
+
| `server-json-name-format` | error | `name` must match reverse-DNS pattern `owner/project` |
|
|
326
|
+
| `server-json-description-required` | error | `description` must be present and non-empty |
|
|
327
|
+
| `server-json-description-length` | warning | `description` > 100 chars — some registries truncate |
|
|
328
|
+
| `server-json-version-required` | error | `version` must be present |
|
|
329
|
+
| `server-json-version-length` | error | `version` length ≤ 255 |
|
|
330
|
+
| `server-json-version-no-range` | error | `version` must be a specific version, not a range (`^`, `~`, `>=`, etc.) |
|
|
331
|
+
| `server-json-version-semver` | warning | `version` should be valid semver (`major.minor.patch`) |
|
|
332
|
+
| `server-json-version-sync` | warning | `server.json` `version` should match `package.json` `version` |
|
|
333
|
+
| `server-json-repository-type` | error | `repository` must be an object |
|
|
334
|
+
| `server-json-repository-url` | error | `repository.url` is required when `repository` is present |
|
|
335
|
+
| `server-json-repository-source` | error | `repository.source` is required when `repository` is present |
|
|
336
|
+
| `server-json-packages-type` | error | `packages` must be an array |
|
|
337
|
+
| `server-json-package-type` | error | Each `packages[i]` must be an object |
|
|
338
|
+
| `server-json-package-registry` | error | `packages[i].registryType` is required |
|
|
339
|
+
| `server-json-package-identifier` | error | `packages[i].identifier` is required |
|
|
340
|
+
| `server-json-package-transport` | error | `packages[i].transport` is required |
|
|
341
|
+
| `server-json-package-no-latest` | error | `packages[i].version` must not be `"latest"` — pin a specific version |
|
|
342
|
+
| `server-json-package-version-sync` | warning | `packages[i].version` should match root `version` |
|
|
343
|
+
| `server-json-package-args-type` | error | `packages[i].packageArguments` must be an array |
|
|
344
|
+
| `server-json-runtime-args-type` | error | `packages[i].runtimeArguments` must be an array |
|
|
345
|
+
| `server-json-env-vars-type` | error | `packages[i].environmentVariables` must be an array |
|
|
346
|
+
| `server-json-remotes-type` | error | `remotes` must be an array |
|
|
347
|
+
| `server-json-remote-type` | error | Each `remotes[i]` must be an object |
|
|
348
|
+
| `server-json-remote-transport-type` | error | `remotes[i].type` is required |
|
|
349
|
+
| `server-json-remote-no-stdio` | error | `remotes[i].type` must be `streamable-http` or `sse` — `stdio` is not valid for remotes |
|
|
350
|
+
| `server-json-transport-type` | error | `transport` must be an object |
|
|
351
|
+
| `server-json-transport-type-value` | error | `transport.type` must be one of `stdio`, `streamable-http`, `sse` |
|
|
352
|
+
| `server-json-transport-url-required` | error | `transport.url` required for `streamable-http` and `sse` |
|
|
353
|
+
| `server-json-transport-url-format` | warning | `transport.url` should be `http://` or `https://` |
|
|
354
|
+
| `server-json-argument-type` | error | Each argument must be an object |
|
|
355
|
+
| `server-json-argument-type-value` | error | `argument.type` must be `positional` or `named` |
|
|
356
|
+
| `server-json-argument-name` | error | Named arguments require `name` |
|
|
357
|
+
| `server-json-argument-value` | error | Positional arguments require `value` or `valueHint` |
|
|
358
|
+
| `server-json-input-format` | warning | `format` should be `string`, `number`, `boolean`, or `filepath` |
|
|
359
|
+
| `server-json-env-var-type` | error | Each environment variable must be an object |
|
|
360
|
+
| `server-json-env-var-name` | error | Environment variable `name` is required |
|
|
361
|
+
| `server-json-env-var-description` | warning | Environment variables should have a `description` |
|
|
362
|
+
|
|
363
|
+
Most of these are mechanical — fix the manifest field named in the diagnostic's `message`. The registry spec is the source of truth; this linter just surfaces violations before you submit.
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
## Escape hatches
|
|
368
|
+
|
|
369
|
+
### Dynamic upstream data
|
|
370
|
+
|
|
371
|
+
If `output` wraps a third-party API whose shape you can't pin down, prefer `z.object({}).passthrough()` over aspirational typing. The linter skips `format-parity` for passthrough schemas, and `structuredContent` still receives the full payload.
|
|
372
|
+
|
|
373
|
+
### Temporarily suppress a warning
|
|
374
|
+
|
|
375
|
+
Warnings don't block startup, so you can ship with them logged. If one is genuinely wrong (rather than the rule being wrong for your case), file an issue against `@cyanheads/mcp-ts-core` with the repro — the linter rules are still maturing.
|
|
376
|
+
|
|
377
|
+
### Escape isn't "make it pass"
|
|
378
|
+
|
|
379
|
+
Don't remove fields from `output` to silence `format-parity` — that makes the data invisible to `structuredContent` clients too. Don't rename `description` to something else to silence `describe-on-fields`. The right fix is either to render the field (format-parity) or accept the warning (description-required).
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
## Adding a new rule
|
|
384
|
+
|
|
385
|
+
If you're extending `@cyanheads/mcp-ts-core` with a new lint rule:
|
|
386
|
+
|
|
387
|
+
1. Add the rule to `src/linter/rules/<family>-rules.ts`. Return `LintDiagnostic` objects with a stable `rule` ID.
|
|
388
|
+
2. Wire it into `validateDefinitions()` in `src/linter/validate.ts` if it's a new family.
|
|
389
|
+
3. Add tests in `tests/unit/linter/`.
|
|
390
|
+
4. **Document the rule in this file.** Add it to the rule index, write a section under the matching family, and bump `metadata.version` in the frontmatter.
|
|
391
|
+
5. The breadcrumb mapping in `validateDefinitions()` is family-prefix-based (`server-json-*` → `#server-json-rules`, etc.), so rules in existing families pick up the right anchor automatically.
|
|
@@ -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.
|
|
7
|
+
version: "2.4"
|
|
8
8
|
audience: external
|
|
9
9
|
type: workflow
|
|
10
10
|
---
|
|
@@ -201,7 +201,7 @@ output: z.object({
|
|
|
201
201
|
```
|
|
202
202
|
|
|
203
203
|
- **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.
|
|
204
|
-
- **`format()` is the
|
|
204
|
+
- **`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.
|
|
205
205
|
|
|
206
206
|
#### Batch input design
|
|
207
207
|
|
|
@@ -428,7 +428,7 @@ Execute the plan using the scaffolding skills:
|
|
|
428
428
|
- [ ] Parameter `.describe()` text explains what the value is, what it affects, and tradeoffs
|
|
429
429
|
- [ ] Input schemas use constrained types (enums, literals, regex) over free strings
|
|
430
430
|
- [ ] Output schemas designed for LLM's next action — chaining IDs, post-write state, filtering communicated
|
|
431
|
-
- [ ] `format()` renders all data the LLM needs — `content[]`
|
|
431
|
+
- [ ] `format()` renders all data the LLM needs — different clients forward different surfaces (Claude Code → `structuredContent`, Claude Desktop → `content[]`); both must carry the same data, not just a count or title
|
|
432
432
|
- [ ] Error messages guide recovery — name what went wrong and what to do next
|
|
433
433
|
- [ ] Annotations set correctly (`readOnlyHint`, `destructiveHint`, etc.)
|
|
434
434
|
- [ ] Tool surface is self-sufficient — a tool-only agent can accomplish everything the server is for
|
package/templates/AGENTS.md
CHANGED
|
@@ -74,8 +74,9 @@ export const searchItems = tool('search_items', {
|
|
|
74
74
|
return { items };
|
|
75
75
|
},
|
|
76
76
|
|
|
77
|
-
// format() populates content[] — the
|
|
78
|
-
//
|
|
77
|
+
// format() populates content[] — the markdown twin of structuredContent.
|
|
78
|
+
// Different clients read different surfaces (Claude Code → structuredContent,
|
|
79
|
+
// Claude Desktop → content[]); both must carry the same data.
|
|
79
80
|
// Enforced at lint time: every field in `output` must appear in the rendered text.
|
|
80
81
|
format: (result) => [{
|
|
81
82
|
type: 'text',
|
|
@@ -294,7 +295,7 @@ import { getMyService } from '@/services/my-domain/my-service.js';
|
|
|
294
295
|
- [ ] JSDoc `@fileoverview` + `@module` on every file
|
|
295
296
|
- [ ] `ctx.log` for logging, `ctx.state` for storage
|
|
296
297
|
- [ ] Handlers throw on failure — error factories or plain `Error`, no try/catch
|
|
297
|
-
- [ ] `format()` renders all data the LLM needs — `content[]`
|
|
298
|
+
- [ ] `format()` renders all data the LLM needs — different clients forward different surfaces (Claude Code → `structuredContent`, Claude Desktop → `content[]`); both must carry the same data
|
|
298
299
|
- [ ] If wrapping external API: raw/domain/output schemas reviewed against real upstream sparsity/nullability before finalizing required vs optional fields
|
|
299
300
|
- [ ] If wrapping external API: normalization and `format()` preserve uncertainty; do not fabricate facts from missing upstream data
|
|
300
301
|
- [ ] If wrapping external API: tests include at least one sparse payload case with omitted upstream fields
|
package/templates/CLAUDE.md
CHANGED
|
@@ -74,8 +74,9 @@ export const searchItems = tool('search_items', {
|
|
|
74
74
|
return { items };
|
|
75
75
|
},
|
|
76
76
|
|
|
77
|
-
// format() populates content[] — the
|
|
78
|
-
//
|
|
77
|
+
// format() populates content[] — the markdown twin of structuredContent.
|
|
78
|
+
// Different clients read different surfaces (Claude Code → structuredContent,
|
|
79
|
+
// Claude Desktop → content[]); both must carry the same data.
|
|
79
80
|
// Enforced at lint time: every field in `output` must appear in the rendered text.
|
|
80
81
|
format: (result) => [{
|
|
81
82
|
type: 'text',
|
|
@@ -294,7 +295,7 @@ import { getMyService } from '@/services/my-domain/my-service.js';
|
|
|
294
295
|
- [ ] JSDoc `@fileoverview` + `@module` on every file
|
|
295
296
|
- [ ] `ctx.log` for logging, `ctx.state` for storage
|
|
296
297
|
- [ ] Handlers throw on failure — error factories or plain `Error`, no try/catch
|
|
297
|
-
- [ ] `format()` renders all data the LLM needs — `content[]`
|
|
298
|
+
- [ ] `format()` renders all data the LLM needs — different clients forward different surfaces (Claude Code → `structuredContent`, Claude Desktop → `content[]`); both must carry the same data
|
|
298
299
|
- [ ] If wrapping external API: raw/domain/output schemas reviewed against real upstream sparsity/nullability before finalizing required vs optional fields
|
|
299
300
|
- [ ] If wrapping external API: normalization and `format()` preserve uncertainty; do not fabricate facts from missing upstream data
|
|
300
301
|
- [ ] If wrapping external API: tests include at least one sparse payload case with omitted upstream fields
|
|
@@ -21,8 +21,9 @@ export const echoTool = tool('template_echo_message', {
|
|
|
21
21
|
return { message: input.message };
|
|
22
22
|
},
|
|
23
23
|
|
|
24
|
-
// format() populates MCP content[] — the
|
|
25
|
-
//
|
|
24
|
+
// format() populates MCP content[] — the markdown twin of structuredContent.
|
|
25
|
+
// Different clients read different surfaces (Claude Code → structuredContent,
|
|
26
|
+
// Claude Desktop → content[]); both must carry the same data.
|
|
26
27
|
// This echo tool is trivial; real tools should render every relevant field.
|
|
27
28
|
format: (result) => [{ type: 'text', text: result.message }],
|
|
28
29
|
});
|