@cyanheads/mcp-ts-core 0.5.0 → 0.5.2
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 +2 -4
- package/README.md +2 -2
- package/dist/linter/rules/format-parity-rules.d.ts +34 -0
- package/dist/linter/rules/format-parity-rules.d.ts.map +1 -0
- package/dist/linter/rules/format-parity-rules.js +335 -0
- package/dist/linter/rules/format-parity-rules.js.map +1 -0
- package/dist/linter/rules/tool-rules.d.ts.map +1 -1
- package/dist/linter/rules/tool-rules.js +5 -0
- package/dist/linter/rules/tool-rules.js.map +1 -1
- package/package.json +1 -1
- package/skills/add-tool/SKILL.md +3 -2
- package/skills/api-config/SKILL.md +1 -1
- package/skills/field-test/SKILL.md +2 -2
- package/skills/polish-docs-meta/SKILL.md +2 -2
- package/skills/polish-docs-meta/references/readme.md +217 -79
- package/skills/setup/SKILL.md +1 -1
- package/templates/AGENTS.md +1 -0
- package/templates/CLAUDE.md +1 -0
- package/skills/devcheck/SKILL.md +0 -54
- package/skills/walkthrough-init/SKILL.md +0 -50
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.2
|
|
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.
|
|
@@ -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[]` — the only field most LLM clients (Claude Code, VS Code Copilot, Cursor, Windsurf) forward to the model. `structuredContent` (from `output`) is for programmatic use and is not reliably shown to the LLM. **Make `format()` content-complete** — render all data the model needs to reason about the result, not just a count or title. Omit for JSON stringify fallback. Additional formatters: `markdown()` (builder), `diffFormatter` (async), `tableFormatter`, `treeFormatter` from `/utils`.
|
|
202
|
+
**`format`**: Maps output to MCP `content[]` — the only field most LLM clients (Claude Code, VS Code Copilot, Cursor, Windsurf) forward to the model. `structuredContent` (from `output`) is for programmatic use and is not reliably shown to the LLM. **Make `format()` content-complete** — render all data the model needs to reason about the result, not just a count or title. 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. If a field shouldn't be shown to the model, remove it from the output schema. Omit `format` 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
|
|
|
@@ -464,14 +464,12 @@ Detailed method signatures, options, and examples live in skill files. Read the
|
|
|
464
464
|
| `add-export` | `skills/add-export/SKILL.md` | Add a new subpath export |
|
|
465
465
|
| `design-mcp-server` | `skills/design-mcp-server/SKILL.md` | Design tool surface, resources, and service layer for a new server |
|
|
466
466
|
| `setup` | `skills/setup/SKILL.md` | Initialize a new consumer server from the template |
|
|
467
|
-
| `devcheck` | `skills/devcheck/SKILL.md` | Run lint, format, typecheck, security checks |
|
|
468
467
|
| `polish-docs-meta` | `skills/polish-docs-meta/SKILL.md` | Finalize docs, README, metadata, and agent protocol for shipping |
|
|
469
468
|
| `report-issue-framework` | `skills/report-issue-framework/SKILL.md` | File a bug or feature request against `@cyanheads/mcp-ts-core` via `gh` CLI |
|
|
470
469
|
| `report-issue-local` | `skills/report-issue-local/SKILL.md` | File a bug or feature request against this server's own repo via `gh` CLI |
|
|
471
470
|
| `release` | `skills/release/SKILL.md` | Version bump, changelog, publish workflow |
|
|
472
471
|
| `maintenance` | `skills/maintenance/SKILL.md` | Dependency updates, housekeeping tasks |
|
|
473
472
|
| `migrate-mcp-ts-template` | `skills/migrate-mcp-ts-template/SKILL.md` | Migrate legacy template fork to package dependency |
|
|
474
|
-
| `walkthrough-init` | `skills/walkthrough-init/SKILL.md` | Trace and audit the agent onboarding instruction chain |
|
|
475
473
|
|
|
476
474
|
---
|
|
477
475
|
|
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,
|
|
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 most LLM clients only forward `content[]` to the model). 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.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Format-parity lint rule. Verifies that every field in a tool's
|
|
3
|
+
* `output` schema is actually rendered by its `format()` function.
|
|
4
|
+
*
|
|
5
|
+
* Most LLM clients (Claude Code, VS Code Copilot, Cursor, Windsurf) only forward
|
|
6
|
+
* `content[]` to the model — not `structuredContent`. A field that exists in
|
|
7
|
+
* `output` but is never rendered is invisible to the model, even though it
|
|
8
|
+
* contributes to the tool's apparent surface area.
|
|
9
|
+
*
|
|
10
|
+
* Approach: sentinel injection.
|
|
11
|
+
* 1. Walk the output schema, build a synthetic value where every leaf is a
|
|
12
|
+
* uniquely identifiable sentinel (distinctive string, large number, or
|
|
13
|
+
* boolean/enum/literal with key-name fallback).
|
|
14
|
+
* 2. Invoke `def.format(synthetic)` once and concatenate `content[].text`.
|
|
15
|
+
* 3. For each leaf path, verify either the sentinel value or the field's key
|
|
16
|
+
* name appears in the rendered text.
|
|
17
|
+
* 4. Emit one error per missing path.
|
|
18
|
+
*
|
|
19
|
+
* Deterministic, dependency-free. Runs inside `validateDefinitions()` alongside
|
|
20
|
+
* every other lint rule — picked up automatically by `bun run lint:mcp`,
|
|
21
|
+
* `bun run devcheck`, and `createApp()` startup validation.
|
|
22
|
+
*
|
|
23
|
+
* @module src/linter/rules/format-parity-rules
|
|
24
|
+
*/
|
|
25
|
+
import type { LintDiagnostic } from '../types.js';
|
|
26
|
+
/**
|
|
27
|
+
* Verifies that `def.format()` renders every field present in `def.output`.
|
|
28
|
+
*
|
|
29
|
+
* Preconditions (caller must check): `def.output` is a valid ZodObject and
|
|
30
|
+
* `def.format` is a function. Skipped entirely otherwise — the default
|
|
31
|
+
* JSON-stringify fallback renders everything by construction.
|
|
32
|
+
*/
|
|
33
|
+
export declare function lintFormatParity(def: unknown, displayName: string): LintDiagnostic[];
|
|
34
|
+
//# sourceMappingURL=format-parity-rules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format-parity-rules.d.ts","sourceRoot":"","sources":["../../../src/linter/rules/format-parity-rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;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,CAqEpF"}
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Format-parity lint rule. Verifies that every field in a tool's
|
|
3
|
+
* `output` schema is actually rendered by its `format()` function.
|
|
4
|
+
*
|
|
5
|
+
* Most LLM clients (Claude Code, VS Code Copilot, Cursor, Windsurf) only forward
|
|
6
|
+
* `content[]` to the model — not `structuredContent`. A field that exists in
|
|
7
|
+
* `output` but is never rendered is invisible to the model, even though it
|
|
8
|
+
* contributes to the tool's apparent surface area.
|
|
9
|
+
*
|
|
10
|
+
* Approach: sentinel injection.
|
|
11
|
+
* 1. Walk the output schema, build a synthetic value where every leaf is a
|
|
12
|
+
* uniquely identifiable sentinel (distinctive string, large number, or
|
|
13
|
+
* boolean/enum/literal with key-name fallback).
|
|
14
|
+
* 2. Invoke `def.format(synthetic)` once and concatenate `content[].text`.
|
|
15
|
+
* 3. For each leaf path, verify either the sentinel value or the field's key
|
|
16
|
+
* name appears in the rendered text.
|
|
17
|
+
* 4. Emit one error per missing path.
|
|
18
|
+
*
|
|
19
|
+
* Deterministic, dependency-free. Runs inside `validateDefinitions()` alongside
|
|
20
|
+
* every other lint rule — picked up automatically by `bun run lint:mcp`,
|
|
21
|
+
* `bun run devcheck`, and `createApp()` startup validation.
|
|
22
|
+
*
|
|
23
|
+
* @module src/linter/rules/format-parity-rules
|
|
24
|
+
*/
|
|
25
|
+
/** Zod 4 stores the type discriminator at `_zod.def.type`. Falls back to `_def.type`. */
|
|
26
|
+
function zodTypeOf(schema) {
|
|
27
|
+
if (!schema || typeof schema !== 'object')
|
|
28
|
+
return '';
|
|
29
|
+
const s = schema;
|
|
30
|
+
return s._zod?.def?.type ?? s._def?.type ?? '';
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Strips Optional/Nullable/Default wrappers so we can always populate the field.
|
|
34
|
+
* Parity cares about "does format render this when present", so wrappers are transparent.
|
|
35
|
+
*/
|
|
36
|
+
function unwrapSchema(schema) {
|
|
37
|
+
let current = schema;
|
|
38
|
+
for (let i = 0; i < 10; i++) {
|
|
39
|
+
const type = zodTypeOf(current);
|
|
40
|
+
if (type !== 'optional' && type !== 'nullable' && type !== 'default')
|
|
41
|
+
return current;
|
|
42
|
+
const c = current;
|
|
43
|
+
const inner = c._zod?.def?.innerType ?? c._def?.innerType;
|
|
44
|
+
if (!inner)
|
|
45
|
+
return current;
|
|
46
|
+
current = inner;
|
|
47
|
+
}
|
|
48
|
+
return current;
|
|
49
|
+
}
|
|
50
|
+
function stringSentinel(path) {
|
|
51
|
+
return `__MCP_PARITY_${path.replace(/[.[\]]/g, '_')}__`;
|
|
52
|
+
}
|
|
53
|
+
/** Builds a synthetic value and collects every leaf path simultaneously. */
|
|
54
|
+
function walk(schema, path, keyName, state) {
|
|
55
|
+
if (state.depth > 8)
|
|
56
|
+
return null;
|
|
57
|
+
state.depth++;
|
|
58
|
+
try {
|
|
59
|
+
const node = unwrapSchema(schema);
|
|
60
|
+
const type = zodTypeOf(node);
|
|
61
|
+
const n = node;
|
|
62
|
+
switch (type) {
|
|
63
|
+
case 'string': {
|
|
64
|
+
const sentinel = stringSentinel(path || keyName || 'root');
|
|
65
|
+
state.leaves.push({ path, keyName, sentinel, matchStrategy: 'strict' });
|
|
66
|
+
return sentinel;
|
|
67
|
+
}
|
|
68
|
+
case 'number':
|
|
69
|
+
case 'int':
|
|
70
|
+
case 'bigint': {
|
|
71
|
+
const sentinel = 900_000_001 + state.numberIndex++;
|
|
72
|
+
state.leaves.push({ path, keyName, sentinel, matchStrategy: 'strict' });
|
|
73
|
+
return sentinel;
|
|
74
|
+
}
|
|
75
|
+
case 'boolean': {
|
|
76
|
+
state.leaves.push({ path, keyName, sentinel: true, matchStrategy: 'permissive' });
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
case 'enum': {
|
|
80
|
+
// Zod 4 exposes options as array on the schema itself.
|
|
81
|
+
const options = n.options ?? getDefOptions(n);
|
|
82
|
+
const value = Array.isArray(options) && options.length > 0 ? options[0] : '';
|
|
83
|
+
state.leaves.push({ path, keyName, sentinel: value, matchStrategy: 'permissive' });
|
|
84
|
+
return value;
|
|
85
|
+
}
|
|
86
|
+
case 'literal': {
|
|
87
|
+
const value = n.value ?? getDefValue(n);
|
|
88
|
+
state.leaves.push({ path, keyName, sentinel: value, matchStrategy: 'permissive' });
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
case 'array': {
|
|
92
|
+
const element = n.element ?? getDefElement(n);
|
|
93
|
+
return [walk(element, `${path}[]`, keyName, state)];
|
|
94
|
+
}
|
|
95
|
+
case 'object': {
|
|
96
|
+
const shape = n.shape ?? {};
|
|
97
|
+
const out = {};
|
|
98
|
+
for (const [key, childSchema] of Object.entries(shape)) {
|
|
99
|
+
const childPath = path ? `${path}.${key}` : key;
|
|
100
|
+
out[key] = walk(childSchema, childPath, key, state);
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
case 'union':
|
|
105
|
+
case 'discriminated_union': {
|
|
106
|
+
const options = getDefOptions(n);
|
|
107
|
+
if (Array.isArray(options) && options.length > 0) {
|
|
108
|
+
return walk(options[0], path, keyName, state);
|
|
109
|
+
}
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
case 'record': {
|
|
113
|
+
const valueSchema = getDefValueType(n);
|
|
114
|
+
if (valueSchema) {
|
|
115
|
+
return { parity_key: walk(valueSchema, `${path}.<key>`, keyName, state) };
|
|
116
|
+
}
|
|
117
|
+
return {};
|
|
118
|
+
}
|
|
119
|
+
case 'tuple': {
|
|
120
|
+
const items = getDefItems(n);
|
|
121
|
+
if (Array.isArray(items)) {
|
|
122
|
+
return items.map((item, i) => walk(item, `${path}[${i}]`, keyName, state));
|
|
123
|
+
}
|
|
124
|
+
return [];
|
|
125
|
+
}
|
|
126
|
+
default: {
|
|
127
|
+
// Unknown/unsupported type — emit leaf with permissive fallback so the
|
|
128
|
+
// rule still asks "did format render this field's key somehow?"
|
|
129
|
+
state.leaves.push({ path, keyName, sentinel: null, matchStrategy: 'permissive' });
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
finally {
|
|
135
|
+
state.depth--;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function getDefOptions(node) {
|
|
139
|
+
const zod = node._zod;
|
|
140
|
+
const legacy = node._def;
|
|
141
|
+
if (Array.isArray(zod?.def?.options))
|
|
142
|
+
return zod.def.options;
|
|
143
|
+
if (Array.isArray(legacy?.options))
|
|
144
|
+
return legacy.options;
|
|
145
|
+
if (Array.isArray(legacy?.values))
|
|
146
|
+
return legacy.values;
|
|
147
|
+
// Zod 4 enum stores values in `entries` as { label: value }
|
|
148
|
+
const entries = zod?.def?.entries ?? legacy?.entries;
|
|
149
|
+
if (entries && typeof entries === 'object')
|
|
150
|
+
return Object.values(entries);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
function getDefValue(node) {
|
|
154
|
+
const zod = node._zod;
|
|
155
|
+
const legacy = node._def;
|
|
156
|
+
if (zod?.def?.value !== undefined)
|
|
157
|
+
return zod.def.value;
|
|
158
|
+
if (legacy?.value !== undefined)
|
|
159
|
+
return legacy.value;
|
|
160
|
+
if (Array.isArray(zod?.def?.values) && zod.def.values.length > 0)
|
|
161
|
+
return zod.def.values[0];
|
|
162
|
+
if (Array.isArray(legacy?.values) && legacy.values.length > 0)
|
|
163
|
+
return legacy.values[0];
|
|
164
|
+
return '';
|
|
165
|
+
}
|
|
166
|
+
function getDefElement(node) {
|
|
167
|
+
const zod = node._zod;
|
|
168
|
+
const legacy = node._def;
|
|
169
|
+
return zod?.def?.element ?? legacy?.element ?? legacy?.type;
|
|
170
|
+
}
|
|
171
|
+
function getDefValueType(node) {
|
|
172
|
+
const zod = node._zod;
|
|
173
|
+
const legacy = node._def;
|
|
174
|
+
return node.valueType ?? zod?.def?.valueType ?? legacy?.valueType;
|
|
175
|
+
}
|
|
176
|
+
function getDefItems(node) {
|
|
177
|
+
const zod = node._zod;
|
|
178
|
+
const legacy = node._def;
|
|
179
|
+
return zod?.def?.items ?? legacy?.items;
|
|
180
|
+
}
|
|
181
|
+
// ---------------------------------------------------------------------------
|
|
182
|
+
// Rendering + matching
|
|
183
|
+
// ---------------------------------------------------------------------------
|
|
184
|
+
/**
|
|
185
|
+
* Collects every string/number/boolean value reachable inside `content[]` so
|
|
186
|
+
* the sentinel check works for any ContentBlock variant — text, image, audio,
|
|
187
|
+
* resource — not just text. Image/audio blocks render sentinels inside
|
|
188
|
+
* `data`/`mimeType`; resource blocks inside `uri`/`text`/`blob`.
|
|
189
|
+
*/
|
|
190
|
+
function extractText(content) {
|
|
191
|
+
if (!Array.isArray(content))
|
|
192
|
+
return '';
|
|
193
|
+
const parts = [];
|
|
194
|
+
for (const block of content)
|
|
195
|
+
collectPrimitives(block, parts);
|
|
196
|
+
return parts.join('\n');
|
|
197
|
+
}
|
|
198
|
+
function collectPrimitives(value, out) {
|
|
199
|
+
if (value === null || value === undefined)
|
|
200
|
+
return;
|
|
201
|
+
const t = typeof value;
|
|
202
|
+
if (t === 'string' || t === 'number' || t === 'boolean') {
|
|
203
|
+
out.push(String(value));
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (Array.isArray(value)) {
|
|
207
|
+
for (const v of value)
|
|
208
|
+
collectPrimitives(v, out);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (t === 'object') {
|
|
212
|
+
for (const v of Object.values(value))
|
|
213
|
+
collectPrimitives(v, out);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function sentinelAppears(sentinel, text) {
|
|
217
|
+
if (sentinel === null || sentinel === undefined)
|
|
218
|
+
return false;
|
|
219
|
+
const asString = typeof sentinel === 'string' ? sentinel : String(sentinel);
|
|
220
|
+
return asString.length > 0 && text.includes(asString);
|
|
221
|
+
}
|
|
222
|
+
function escapeRegex(str) {
|
|
223
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
224
|
+
}
|
|
225
|
+
/** Case-insensitive whole-word match. Returns false for keys shorter than 3. */
|
|
226
|
+
function wholeWordMatch(word, text) {
|
|
227
|
+
if (word.length < 3)
|
|
228
|
+
return false;
|
|
229
|
+
return new RegExp(`\\b${escapeRegex(word)}\\b`, 'i').test(text);
|
|
230
|
+
}
|
|
231
|
+
/** Splits camelCase/snake_case into lowercase segments. */
|
|
232
|
+
function splitKey(key) {
|
|
233
|
+
return key
|
|
234
|
+
.replace(/([a-z])([A-Z])/g, '$1_$2')
|
|
235
|
+
.split(/[_-]/)
|
|
236
|
+
.map((s) => s.toLowerCase())
|
|
237
|
+
.filter(Boolean);
|
|
238
|
+
}
|
|
239
|
+
function keyNameAppears(keyName, text) {
|
|
240
|
+
if (!keyName)
|
|
241
|
+
return false;
|
|
242
|
+
if (wholeWordMatch(keyName, text))
|
|
243
|
+
return true;
|
|
244
|
+
for (const segment of splitKey(keyName)) {
|
|
245
|
+
if (wholeWordMatch(segment, text))
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
function leafIsRendered(leaf, text) {
|
|
251
|
+
if (sentinelAppears(leaf.sentinel, text))
|
|
252
|
+
return true;
|
|
253
|
+
if (leaf.matchStrategy === 'permissive') {
|
|
254
|
+
return keyNameAppears(leaf.keyName, text);
|
|
255
|
+
}
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
// ---------------------------------------------------------------------------
|
|
259
|
+
// Public rule
|
|
260
|
+
// ---------------------------------------------------------------------------
|
|
261
|
+
/**
|
|
262
|
+
* Verifies that `def.format()` renders every field present in `def.output`.
|
|
263
|
+
*
|
|
264
|
+
* Preconditions (caller must check): `def.output` is a valid ZodObject and
|
|
265
|
+
* `def.format` is a function. Skipped entirely otherwise — the default
|
|
266
|
+
* JSON-stringify fallback renders everything by construction.
|
|
267
|
+
*/
|
|
268
|
+
export function lintFormatParity(def, displayName) {
|
|
269
|
+
const d = def;
|
|
270
|
+
const output = d.output;
|
|
271
|
+
const format = d.format;
|
|
272
|
+
if (zodTypeOf(output) !== 'object')
|
|
273
|
+
return [];
|
|
274
|
+
if (typeof format !== 'function')
|
|
275
|
+
return [];
|
|
276
|
+
// Build synthetic sample.
|
|
277
|
+
const state = { leaves: [], numberIndex: 0, depth: 0 };
|
|
278
|
+
let synthetic;
|
|
279
|
+
try {
|
|
280
|
+
synthetic = walk(output, '', '', state);
|
|
281
|
+
}
|
|
282
|
+
catch (err) {
|
|
283
|
+
return [
|
|
284
|
+
{
|
|
285
|
+
rule: 'format-parity-walk-failed',
|
|
286
|
+
severity: 'warning',
|
|
287
|
+
message: `Tool '${displayName}' output schema could not be walked to build a synthetic ` +
|
|
288
|
+
`sample (${err instanceof Error ? err.message : String(err)}). ` +
|
|
289
|
+
'Format parity could not be verified.',
|
|
290
|
+
definitionType: 'tool',
|
|
291
|
+
definitionName: displayName,
|
|
292
|
+
},
|
|
293
|
+
];
|
|
294
|
+
}
|
|
295
|
+
if (state.leaves.length === 0)
|
|
296
|
+
return [];
|
|
297
|
+
// Run format().
|
|
298
|
+
let rendered;
|
|
299
|
+
try {
|
|
300
|
+
const result = format(synthetic);
|
|
301
|
+
rendered = extractText(result);
|
|
302
|
+
}
|
|
303
|
+
catch (err) {
|
|
304
|
+
return [
|
|
305
|
+
{
|
|
306
|
+
rule: 'format-parity-threw',
|
|
307
|
+
severity: 'warning',
|
|
308
|
+
message: `Tool '${displayName}' format() threw on a synthetic sample ` +
|
|
309
|
+
`(${err instanceof Error ? err.message : String(err)}). ` +
|
|
310
|
+
'format() should be total — render any valid value of the output schema.',
|
|
311
|
+
definitionType: 'tool',
|
|
312
|
+
definitionName: displayName,
|
|
313
|
+
},
|
|
314
|
+
];
|
|
315
|
+
}
|
|
316
|
+
// Verify each leaf.
|
|
317
|
+
const diagnostics = [];
|
|
318
|
+
for (const leaf of state.leaves) {
|
|
319
|
+
if (!leafIsRendered(leaf, rendered)) {
|
|
320
|
+
const displayPath = leaf.path || leaf.keyName || '<root>';
|
|
321
|
+
diagnostics.push({
|
|
322
|
+
rule: 'format-parity',
|
|
323
|
+
severity: 'error',
|
|
324
|
+
message: `Tool '${displayName}' format() does not render output field '${displayPath}'. ` +
|
|
325
|
+
'Every field in the output schema must appear in format() — most LLM clients only ' +
|
|
326
|
+
'forward content[] to the model, not structuredContent. Either render it in format() ' +
|
|
327
|
+
'or remove it from the output schema.',
|
|
328
|
+
definitionType: 'tool',
|
|
329
|
+
definitionName: displayName,
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return diagnostics;
|
|
334
|
+
}
|
|
335
|
+
//# sourceMappingURL=format-parity-rules.js.map
|
|
@@ -0,0 +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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-rules.d.ts","sourceRoot":"","sources":["../../../src/linter/rules/tool-rules.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"tool-rules.d.ts","sourceRoot":"","sources":["../../../src/linter/rules/tool-rules.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AASlD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,cAAc,EAAE,CA6EjE;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAC5B,IAAI,EAAE,OAAO,EACb,cAAc,EAAE,cAAc,CAAC,gBAAgB,CAAC,EAChD,cAAc,EAAE,MAAM,GACrB,cAAc,EAAE,CA8BlB;AAgDD;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,OAAO,EAAE,EAChB,SAAS,EAAE,OAAO,EAAE,GACnB,cAAc,EAAE,CA6ClB"}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Validates tool definitions against MCP spec and framework conventions.
|
|
4
4
|
* @module src/linter/rules/tool-rules
|
|
5
5
|
*/
|
|
6
|
+
import { lintFormatParity } from './format-parity-rules.js';
|
|
6
7
|
import { checkNameRequired, checkToolNameFormat } from './name-rules.js';
|
|
7
8
|
import { checkFieldDescriptions, checkIsZodObject, checkSchemaSerializable, } from './schema-rules.js';
|
|
8
9
|
/**
|
|
@@ -64,6 +65,10 @@ export function lintToolDefinition(def) {
|
|
|
64
65
|
const outputSerial = checkSchemaSerializable(d?.output, 'output', 'tool', displayName);
|
|
65
66
|
if (outputSerial)
|
|
66
67
|
diagnostics.push(outputSerial);
|
|
68
|
+
// Format parity: skip when output isn't serializable (synthetic sample may misbehave).
|
|
69
|
+
if (!outputSerial && typeof d?.format === 'function') {
|
|
70
|
+
diagnostics.push(...lintFormatParity(d, displayName));
|
|
71
|
+
}
|
|
67
72
|
}
|
|
68
73
|
// Auth scopes validation
|
|
69
74
|
if (d?.auth !== undefined) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-rules.js","sourceRoot":"","sources":["../../../src/linter/rules/tool-rules.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"tool-rules.js","sourceRoot":"","sources":["../../../src/linter/rules/tool-rules.ts"],"names":[],"mappings":"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"}
|
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.2",
|
|
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",
|
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.5"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -60,6 +60,7 @@ export const {{TOOL_EXPORT}} = tool('{{tool_name}}', {
|
|
|
60
60
|
// format() populates MCP content[] — the only field most LLM clients forward
|
|
61
61
|
// to the model. structuredContent (from output) is for programmatic use only.
|
|
62
62
|
// Render ALL data the LLM needs to reason about the result.
|
|
63
|
+
// Enforced at lint time: every field in `output` must appear in the rendered text.
|
|
63
64
|
format: (result) => {
|
|
64
65
|
const lines: string[] = [];
|
|
65
66
|
// Render each item with all relevant fields — not just a count or title.
|
|
@@ -312,7 +313,7 @@ Large payloads burn the agent's context window. Default to curated summaries; of
|
|
|
312
313
|
- [ ] JSDoc `@fileoverview` and `@module` header present
|
|
313
314
|
- [ ] Optional nested objects guarded for empty inner values from form-based clients (check `?.field` truthiness, not just object presence)
|
|
314
315
|
- [ ] `handler(input, ctx)` is pure — throws on failure, no try/catch
|
|
315
|
-
- [ ] `format()` renders
|
|
316
|
+
- [ ] `format()` renders every field in the output schema — enforced at lint time via sentinel injection, startup fails with `format-parity` errors otherwise. `content[]` is the only field most clients forward to the model; if a field shouldn't be shown, remove it from the output schema rather than skipping it in `format()`
|
|
316
317
|
- [ ] If wrapping external API: output schema and `format()` preserve uncertainty from sparse upstream payloads instead of inventing concrete values
|
|
317
318
|
- [ ] `auth` scopes declared if the tool needs authorization
|
|
318
319
|
- [ ] `task: true` added if the tool is long-running
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Reference for core and server configuration in `@cyanheads/mcp-ts-core`. Covers env var tables with defaults, priority order, server-specific Zod schema pattern, and Workers lazy-parsing requirement.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.2"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Exercise tools, resources, and prompts with real-world inputs to verify behavior end-to-end. Use after adding or modifying definitions, or when the user asks to test, try out, or verify their MCP surface. Calls each definition with realistic and adversarial inputs and produces a report of issues, pain points, and recommendations.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.2"
|
|
8
8
|
audience: external
|
|
9
9
|
type: debug
|
|
10
10
|
---
|
|
@@ -36,7 +36,7 @@ For every tool, resource, and prompt, run through these categories:
|
|
|
36
36
|
| Category | What to test |
|
|
37
37
|
|:---------|:-------------|
|
|
38
38
|
| **Happy path** | Realistic input that should succeed. Verify output shape matches the output schema. Verify format function produces sensible content blocks. |
|
|
39
|
-
| **`structuredContent` parity** |
|
|
39
|
+
| **`structuredContent` parity** | The `format-parity` lint rule already asserts every terminal field in the output schema appears in `format()`'s rendered text (via sentinel injection at startup). Field testing layers real-data checks on top: are values rendered accurately (not just their labels)? Do conditional-render branches in `format()` still render every field when specific values are present? Does the content look right to a human reading the LLM's view? |
|
|
40
40
|
| **Variations** | Different valid input combinations — optional fields omitted, optional fields included, different enum values, min/max boundaries. |
|
|
41
41
|
| **Field selection / projection** | For tools with `fields`, `include`, `expand`, `view`, or similar parameters, call the tool with non-default selections. Verify the handler returns the requested fields and `format()` renders each requested field rather than a hardcoded summary subset. |
|
|
42
42
|
| **Edge cases** | Empty strings, zero values, very long inputs, special characters, Unicode. |
|
|
@@ -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: "1.
|
|
7
|
+
version: "1.4"
|
|
8
8
|
audience: external
|
|
9
9
|
type: workflow
|
|
10
10
|
---
|
|
@@ -48,7 +48,7 @@ Capture: tool count, resource count, prompt count, service count, required env v
|
|
|
48
48
|
|
|
49
49
|
Read `references/readme.md` for structure and conventions. If `README.md` doesn't exist, create it from scratch. If it exists, diff the current content against the audit — update tool/resource/prompt tables, env var lists, and descriptions to match the actual surface area. Don't rewrite sections that are already accurate.
|
|
50
50
|
|
|
51
|
-
The header tagline (`<
|
|
51
|
+
The bold header tagline (the `<b>` text inside the first `<p>`) must match the `package.json` `description`. The surface count is a nested `<div>` inside the same `<p>`, separated by `•`.
|
|
52
52
|
|
|
53
53
|
### 3. Agent Protocol (CLAUDE.md / AGENTS.md)
|
|
54
54
|
|
|
@@ -7,42 +7,47 @@ Structure and content guide for creating or updating a README for an MCP server
|
|
|
7
7
|
Use this section order. Omit sections that don't apply (e.g., skip Docker/Workers if the server doesn't deploy there).
|
|
8
8
|
|
|
9
9
|
```text
|
|
10
|
-
# {Server Name}
|
|
11
|
-
|
|
10
|
+
# {Server Name} ← centered HTML block
|
|
11
|
+
[Public hosted callout if present] ← centered HTML block, directly under badges
|
|
12
|
+
Badges row ← npm, Docker, Version, Framework, MCP SDK, License, TS, Bun, Coverage
|
|
12
13
|
---
|
|
13
|
-
## Tools
|
|
14
|
-
## Resources (if any)
|
|
15
|
-
##
|
|
16
|
-
##
|
|
17
|
-
##
|
|
18
|
-
##
|
|
19
|
-
##
|
|
20
|
-
##
|
|
21
|
-
##
|
|
22
|
-
##
|
|
23
|
-
## License ← one line
|
|
14
|
+
## Tools ← grouping sentence → summary table → per-tool subsections
|
|
15
|
+
## Resources and prompts (if any) ← single combined table (Type / Name / Description)
|
|
16
|
+
## Features ← framework bullets + domain-specific bullets
|
|
17
|
+
## Getting started ← hosted (if any), bunx/npx/docker configs, HTTP one-liner, prerequisites, install
|
|
18
|
+
## Configuration ← env var table + `.env.example` pointer
|
|
19
|
+
## Running the server ← dev, production, Workers/Docker
|
|
20
|
+
## Project structure ← directory/purpose table
|
|
21
|
+
## Development guide ← link to CLAUDE.md, key rules
|
|
22
|
+
## Contributing ← brief
|
|
23
|
+
## License ← one line
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
## Section Guide
|
|
27
27
|
|
|
28
28
|
### Title Block
|
|
29
29
|
|
|
30
|
-
Centered HTML. The `<h1>` is the server name — use the scoped package name if published under a scope (e.g., `@cyanheads/my-mcp-server`). The `<p>` is a bold one-liner: what the server wraps, key capabilities, transport/deployment options.
|
|
30
|
+
Centered HTML. The `<h1>` is the server name — use the scoped package name if published under a scope (e.g., `@cyanheads/my-mcp-server`). The `<p>` is a bold one-liner: what the server wraps, key capabilities, transport/deployment options. **Nest the surface count as a `<div>` inside the same `<p>`**, separated by `•` (U+2022 bullet) — not as a second `<p>`. This matches the shipping convention across `@cyanheads/*` servers.
|
|
31
31
|
|
|
32
32
|
```html
|
|
33
33
|
<div align="center">
|
|
34
34
|
<h1>@cyanheads/my-mcp-server</h1>
|
|
35
|
-
<p><b>MCP server for the Acme API
|
|
36
|
-
<
|
|
35
|
+
<p><b>MCP server for the Acme API — search projects, manage tasks, track teams. STDIO or Streamable HTTP.</b>
|
|
36
|
+
<div>7 Tools • 2 Resources • 1 Prompt</div>
|
|
37
|
+
</p>
|
|
37
38
|
</div>
|
|
38
39
|
|
|
39
40
|
<div align="center">
|
|
40
41
|
|
|
41
|
-
[](https://www.npmjs.com/package/my-mcp-server) [](./CHANGELOG.md) [](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) [](https://www.npmjs.com/package/@cyanheads/my-mcp-server) [](./CHANGELOG.md) [](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) [](https://modelcontextprotocol.io/)
|
|
43
|
+
|
|
44
|
+
[](./LICENSE) [](https://www.typescriptlang.org/) [](https://bun.sh/)
|
|
42
45
|
|
|
43
46
|
</div>
|
|
44
47
|
```
|
|
45
48
|
|
|
49
|
+
The header tagline must match the `package.json` `description`.
|
|
50
|
+
|
|
46
51
|
**Badge selection:** All badges use `style=flat-square`. Include what applies — don't add badges for things the server doesn't have:
|
|
47
52
|
|
|
48
53
|
| Badge | When to include |
|
|
@@ -51,19 +56,41 @@ Centered HTML. The `<h1>` is the server name — use the scoped package name if
|
|
|
51
56
|
| Docker | Published to ghcr.io or Docker Hub |
|
|
52
57
|
| Version | Always — link to CHANGELOG.md |
|
|
53
58
|
| Framework | Always — links to `@cyanheads/mcp-ts-core` on npm |
|
|
54
|
-
| MCP Spec | Always — link to the spec version implemented |
|
|
55
59
|
| MCP SDK | Always — show the `@modelcontextprotocol/sdk` version |
|
|
56
60
|
| License | Always |
|
|
57
|
-
| Status | Optional — Stable, Beta, etc. |
|
|
58
61
|
| TypeScript | Always |
|
|
59
|
-
| Bun | If using Bun |
|
|
62
|
+
| Bun | If using Bun (standard for this framework) |
|
|
63
|
+
| MCP Spec | Optional — rarely included; the SDK badge usually suffices |
|
|
64
|
+
| Status | Optional — Stable, Beta, etc. |
|
|
60
65
|
| Code Coverage | If coverage is tracked |
|
|
61
66
|
|
|
62
67
|
Add a `---` horizontal rule after the badge block.
|
|
63
68
|
|
|
69
|
+
### Public Hosted Callout (if present)
|
|
70
|
+
|
|
71
|
+
If a public hosted instance is available, **promote it to a top-level callout** immediately below the badge block — don't bury it inside Getting Started. This is the highest-value piece of information for a visitor who wants to try the server with zero install.
|
|
72
|
+
|
|
73
|
+
```html
|
|
74
|
+
<div align="center">
|
|
75
|
+
|
|
76
|
+
**Public Hosted Server:** [https://my-server.example.com/mcp](https://my-server.example.com/mcp)
|
|
77
|
+
|
|
78
|
+
</div>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Keep the full connection-config JSON block inside a `### Public Hosted Instance` subsection under Getting Started (covered below). This callout is just the visibility pointer.
|
|
82
|
+
|
|
64
83
|
### Tools
|
|
65
84
|
|
|
66
|
-
This is the most important section — it tells humans and LLMs exactly what the server exposes.
|
|
85
|
+
This is the most important section — it tells humans and LLMs exactly what the server exposes. Three layers: a **grouping framing sentence**, a summary table, then per-tool subsections for tools with non-trivial behavior.
|
|
86
|
+
|
|
87
|
+
**Grouping framing sentence:** Lead with one sentence that explains how the tool surface is organized. Richer than a bare count — tells the reader what mental model to apply. Examples:
|
|
88
|
+
|
|
89
|
+
- "Seventeen tools grouped by shape — workflow helpers orchestrate common flows end-to-end, primitive tools expose fine-grained CRUD, and the instruction tool returns procedural guidance merged with live account state."
|
|
90
|
+
- "Nine tools for working with PubMed and NCBI data:"
|
|
91
|
+
- "Five tools covering project lifecycle — discovery, task CRUD, and team analytics."
|
|
92
|
+
|
|
93
|
+
If the tools aren't meaningfully grouped, a single sentence count ("Seven tools for working with Acme data:") is acceptable.
|
|
67
94
|
|
|
68
95
|
**Summary table:**
|
|
69
96
|
|
|
@@ -79,8 +106,6 @@ Seven tools for working with Acme data:
|
|
|
79
106
|
| `get_task` | Fetch one or more tasks by ID, with full or summary data. |
|
|
80
107
|
```
|
|
81
108
|
|
|
82
|
-
Lead with a one-line count: "Seven tools for working with X data:" (or "Three tools", etc.).
|
|
83
|
-
|
|
84
109
|
**Per-tool subsections:**
|
|
85
110
|
|
|
86
111
|
Below the table, add a `### tool_name` subsection for each tool that has meaningful detail beyond its one-line description. Include:
|
|
@@ -110,34 +135,35 @@ Fetch one or more tasks by ID, with full data or concise summaries.
|
|
|
110
135
|
- Batch fetch up to 5 tasks at once
|
|
111
136
|
- Full data includes subtasks, comments, attachments, and history
|
|
112
137
|
- Partial success reporting when some tasks in a batch fail
|
|
113
|
-
|
|
114
|
-
[View detailed examples](./examples/get_task.md)
|
|
115
138
|
```
|
|
116
139
|
|
|
117
140
|
Skip the per-tool subsection for simple tools where the table description says everything (e.g., a `get_field_values` lookup tool).
|
|
118
141
|
|
|
119
|
-
### Resources (
|
|
142
|
+
### Resources and Prompts (combined)
|
|
143
|
+
|
|
144
|
+
**Use a single combined table with a `Type` column** rather than separate `## Resources` and `## Prompts` sections. This is the shipping convention — it scales better when a server has only 1 or 2 of each, and co-locates related content.
|
|
120
145
|
|
|
121
146
|
```markdown
|
|
122
|
-
## Resources
|
|
147
|
+
## Resources and prompts
|
|
123
148
|
|
|
124
|
-
|
|
|
125
|
-
|
|
126
|
-
| `acme://projects/{projectId}` | Project details by ID |
|
|
127
|
-
| `acme://tasks/{taskId}` | Task details by ID |
|
|
149
|
+
| Type | Name | Description |
|
|
150
|
+
|:---|:---|:---|
|
|
151
|
+
| Resource | `acme://projects/{projectId}` | Project details by ID |
|
|
152
|
+
| Resource | `acme://tasks/{taskId}` | Task details by ID |
|
|
153
|
+
| Prompt | `project_summary` | Summarize a project's status and open tasks |
|
|
128
154
|
```
|
|
129
155
|
|
|
130
|
-
|
|
156
|
+
Use singular ("Resource and prompt") if there's only one of each.
|
|
131
157
|
|
|
132
|
-
|
|
133
|
-
## Prompts
|
|
158
|
+
**Always include the tool-coverage note** directly under the table. Many MCP clients are tool-only and don't surface resources — this tells both the reader and downstream agents that the data is still reachable:
|
|
134
159
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
| `project_summary` | Summarize a project's status and open tasks |
|
|
160
|
+
```markdown
|
|
161
|
+
All resource data is also reachable via tools. Large collections (`projects`, `tasks`) are not exposed as resources — use the `list` operation on the corresponding tool instead.
|
|
138
162
|
```
|
|
139
163
|
|
|
140
|
-
|
|
164
|
+
If a prompt has an associated design doc or reference, link it in the same paragraph: `Design reference for the prompt: [\`docs/email-design-playbook.md\`](./docs/email-design-playbook.md).`
|
|
165
|
+
|
|
166
|
+
Derive all tool/resource/prompt rows directly from the actual definitions. Use the real names and descriptions from the Zod schemas.
|
|
141
167
|
|
|
142
168
|
### Features
|
|
143
169
|
|
|
@@ -146,90 +172,166 @@ Two subsection groups: framework capabilities, then domain-specific capabilities
|
|
|
146
172
|
```markdown
|
|
147
173
|
## Features
|
|
148
174
|
|
|
149
|
-
Built on [`@cyanheads/mcp-ts-core`](https://
|
|
175
|
+
Built on [`@cyanheads/mcp-ts-core`](https://www.npmjs.com/package/@cyanheads/mcp-ts-core):
|
|
150
176
|
|
|
151
|
-
- Declarative tool definitions — single file per
|
|
152
|
-
- Unified error handling
|
|
153
|
-
- Pluggable auth
|
|
177
|
+
- Declarative tool, resource, and prompt definitions — single file per primitive, framework handles registration and validation
|
|
178
|
+
- Unified error handling — handlers throw, framework catches, classifies, and formats
|
|
179
|
+
- Pluggable auth: `none`, `jwt`, `oauth`
|
|
154
180
|
- Swappable storage backends: `in-memory`, `filesystem`, `Supabase`, `Cloudflare KV/R2/D1`
|
|
155
181
|
- Structured logging with optional OpenTelemetry tracing
|
|
156
|
-
-
|
|
182
|
+
- STDIO and Streamable HTTP transports
|
|
157
183
|
|
|
158
184
|
Acme-specific:
|
|
159
185
|
|
|
160
186
|
- Type-safe client for the Acme v2 API
|
|
161
187
|
- Automatic cleaning and simplification of API responses for agent consumption
|
|
188
|
+
- Workflow tools parallelize related sub-requests under a configurable concurrency limit
|
|
162
189
|
```
|
|
163
190
|
|
|
164
191
|
### Getting Started
|
|
165
192
|
|
|
166
|
-
Lead with the lowest-friction option. If a public hosted instance exists, show that first. Then
|
|
193
|
+
Lead with the lowest-friction option. If a public hosted instance exists, show that first. Then the **three standard install configs in order — `bunx`, `npx`, `docker run`** — followed by the HTTP one-liner quickstart, then prerequisites and install steps.
|
|
167
194
|
|
|
168
|
-
|
|
169
|
-
## Getting Started
|
|
195
|
+
**Standard three-block pattern** (the house style across shipping `@cyanheads/*` servers):
|
|
170
196
|
|
|
171
|
-
|
|
197
|
+
```markdown
|
|
198
|
+
## Getting started
|
|
172
199
|
|
|
173
|
-
|
|
200
|
+
Add the following to your MCP client configuration file. See [`docs/api-key.md`](./docs/api-key.md) for how to generate an API key.
|
|
174
201
|
|
|
175
202
|
\`\`\`json
|
|
176
203
|
{
|
|
177
204
|
"mcpServers": {
|
|
178
205
|
"my-server": {
|
|
179
|
-
"type": "
|
|
180
|
-
"
|
|
206
|
+
"type": "stdio",
|
|
207
|
+
"command": "bunx",
|
|
208
|
+
"args": ["@cyanheads/my-mcp-server@latest"],
|
|
209
|
+
"env": {
|
|
210
|
+
"MCP_TRANSPORT_TYPE": "stdio",
|
|
211
|
+
"MCP_LOG_LEVEL": "info",
|
|
212
|
+
"ACME_API_KEY": "your-api-key"
|
|
213
|
+
}
|
|
181
214
|
}
|
|
182
215
|
}
|
|
183
216
|
}
|
|
184
217
|
\`\`\`
|
|
185
218
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
Add to your MCP client config (e.g., `claude_desktop_config.json`):
|
|
219
|
+
Or with npx (no Bun required):
|
|
189
220
|
|
|
190
221
|
\`\`\`json
|
|
191
222
|
{
|
|
192
223
|
"mcpServers": {
|
|
193
|
-
"my-
|
|
224
|
+
"my-server": {
|
|
194
225
|
"type": "stdio",
|
|
195
|
-
"command": "
|
|
196
|
-
"args": ["my-mcp-server@latest"],
|
|
226
|
+
"command": "npx",
|
|
227
|
+
"args": ["-y", "@cyanheads/my-mcp-server@latest"],
|
|
197
228
|
"env": {
|
|
198
|
-
"
|
|
199
|
-
"
|
|
229
|
+
"MCP_TRANSPORT_TYPE": "stdio",
|
|
230
|
+
"MCP_LOG_LEVEL": "info",
|
|
231
|
+
"ACME_API_KEY": "your-api-key"
|
|
200
232
|
}
|
|
201
233
|
}
|
|
202
234
|
}
|
|
203
235
|
}
|
|
204
236
|
\`\`\`
|
|
205
237
|
|
|
238
|
+
Or with Docker:
|
|
239
|
+
|
|
240
|
+
\`\`\`json
|
|
241
|
+
{
|
|
242
|
+
"mcpServers": {
|
|
243
|
+
"my-server": {
|
|
244
|
+
"type": "stdio",
|
|
245
|
+
"command": "docker",
|
|
246
|
+
"args": [
|
|
247
|
+
"run", "-i", "--rm",
|
|
248
|
+
"-e", "MCP_TRANSPORT_TYPE=stdio",
|
|
249
|
+
"-e", "ACME_API_KEY=your-api-key",
|
|
250
|
+
"ghcr.io/cyanheads/my-mcp-server:latest"
|
|
251
|
+
]
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
\`\`\`
|
|
256
|
+
|
|
257
|
+
For Streamable HTTP, set the transport and start the server:
|
|
258
|
+
|
|
259
|
+
\`\`\`sh
|
|
260
|
+
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 ACME_API_KEY=... bun run start:http
|
|
261
|
+
# Server listens at http://localhost:3010/mcp
|
|
262
|
+
\`\`\`
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Refer to "your MCP client configuration file" generically — don't prescribe `claude_desktop_config.json` by name. Different clients use different config paths and the server isn't client-specific.
|
|
266
|
+
|
|
267
|
+
**If a public hosted instance exists**, precede the three-block pattern with a `### Public Hosted Instance` subsection and wrap the local configs in a `### Self-Hosted / Local` subsection:
|
|
268
|
+
|
|
269
|
+
```markdown
|
|
270
|
+
### Public Hosted Instance
|
|
271
|
+
|
|
272
|
+
A public instance is available at `https://my-server.example.com/mcp` — no installation required. Point any MCP client at it via Streamable HTTP:
|
|
273
|
+
|
|
274
|
+
\`\`\`json
|
|
275
|
+
{
|
|
276
|
+
"mcpServers": {
|
|
277
|
+
"my-server": {
|
|
278
|
+
"type": "streamable-http",
|
|
279
|
+
"url": "https://my-server.example.com/mcp"
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
\`\`\`
|
|
284
|
+
|
|
285
|
+
### Self-Hosted / Local
|
|
286
|
+
|
|
287
|
+
[bunx / npx / docker blocks here]
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
**Prerequisites:** Include a Bun version line and any domain-specific setup (API key format, rate-limit tiers, required accounts). Don't just list Bun — readers need to know what else to prepare.
|
|
291
|
+
|
|
292
|
+
```markdown
|
|
206
293
|
### Prerequisites
|
|
207
294
|
|
|
208
|
-
- [Bun v1.2
|
|
295
|
+
- [Bun v1.3.2](https://bun.sh/) or higher (or Node.js v22+).
|
|
296
|
+
- An Acme API key — see [`docs/api-key.md`](./docs/api-key.md) for how to generate one.
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
**Installation:** Standard four steps (clone, cd, install, configure env):
|
|
209
300
|
|
|
301
|
+
```markdown
|
|
210
302
|
### Installation
|
|
211
303
|
|
|
212
304
|
1. **Clone the repository:**
|
|
305
|
+
|
|
213
306
|
\`\`\`sh
|
|
214
307
|
git clone https://github.com/cyanheads/my-mcp-server.git
|
|
215
308
|
\`\`\`
|
|
216
309
|
|
|
217
310
|
2. **Navigate into the directory:**
|
|
311
|
+
|
|
218
312
|
\`\`\`sh
|
|
219
313
|
cd my-mcp-server
|
|
220
314
|
\`\`\`
|
|
221
315
|
|
|
222
316
|
3. **Install dependencies:**
|
|
317
|
+
|
|
223
318
|
\`\`\`sh
|
|
224
319
|
bun install
|
|
225
320
|
\`\`\`
|
|
321
|
+
|
|
322
|
+
4. **Configure environment:**
|
|
323
|
+
|
|
324
|
+
\`\`\`sh
|
|
325
|
+
cp .env.example .env
|
|
326
|
+
# edit .env and set required vars
|
|
327
|
+
\`\`\`
|
|
226
328
|
```
|
|
227
329
|
|
|
228
|
-
Omit the
|
|
330
|
+
Omit the clone/install steps if the server is npm-only (not meant to be cloned).
|
|
229
331
|
|
|
230
332
|
### Configuration
|
|
231
333
|
|
|
232
|
-
Table of environment variables. Include framework vars only if the server uses non-default values.
|
|
334
|
+
Table of environment variables. Include framework vars only if the server uses non-default values. Mark required vars with bold **Required.** in the description rather than a separate column. **Close with a pointer to `.env.example`** for the full list of optional overrides.
|
|
233
335
|
|
|
234
336
|
```markdown
|
|
235
337
|
## Configuration
|
|
@@ -238,6 +340,7 @@ Table of environment variables. Include framework vars only if the server uses n
|
|
|
238
340
|
|:---------|:------------|:--------|
|
|
239
341
|
| `ACME_API_KEY` | **Required.** API key for the Acme service. | — |
|
|
240
342
|
| `ACME_BASE_URL` | API base URL. | `https://api.acme.com` |
|
|
343
|
+
| `ACME_TIMEOUT_MS` | Per-request timeout in milliseconds. | `60000` |
|
|
241
344
|
| `MCP_TRANSPORT_TYPE` | Transport: `stdio` or `http`. | `stdio` |
|
|
242
345
|
| `MCP_HTTP_PORT` | Port for HTTP server. | `3010` |
|
|
243
346
|
| `MCP_AUTH_MODE` | Auth mode: `none`, `jwt`, or `oauth`. | `none` |
|
|
@@ -245,74 +348,105 @@ Table of environment variables. Include framework vars only if the server uses n
|
|
|
245
348
|
| `LOGS_DIR` | Directory for log files (Node.js only). | `<project-root>/logs` |
|
|
246
349
|
| `STORAGE_PROVIDER_TYPE` | Storage backend. | `in-memory` |
|
|
247
350
|
| `OTEL_ENABLED` | Enable OpenTelemetry. | `false` |
|
|
351
|
+
|
|
352
|
+
See [`.env.example`](./.env.example) for the full list of optional overrides.
|
|
248
353
|
```
|
|
249
354
|
|
|
250
|
-
Source from the server config Zod schema and `.env.example`.
|
|
355
|
+
Source from the server config Zod schema and `.env.example`.
|
|
251
356
|
|
|
252
357
|
### Running the Server
|
|
253
358
|
|
|
254
|
-
Separate from Getting Started. Show build + run
|
|
359
|
+
Separate from Getting Started. Show dev, build + run, and Workers/Docker deployment if applicable.
|
|
255
360
|
|
|
256
361
|
```markdown
|
|
257
|
-
## Running the
|
|
362
|
+
## Running the server
|
|
363
|
+
|
|
364
|
+
### Local development
|
|
258
365
|
|
|
259
|
-
|
|
366
|
+
- **Hot-reload dev mode:**
|
|
367
|
+
|
|
368
|
+
\`\`\`sh
|
|
369
|
+
bun run dev:stdio
|
|
370
|
+
bun run dev:http
|
|
371
|
+
\`\`\`
|
|
260
372
|
|
|
261
373
|
- **Build and run the production version:**
|
|
374
|
+
|
|
262
375
|
\`\`\`sh
|
|
263
|
-
|
|
264
|
-
bun run
|
|
376
|
+
# One-time build
|
|
377
|
+
bun run rebuild
|
|
378
|
+
|
|
379
|
+
# Run the built server
|
|
380
|
+
bun run start:stdio
|
|
381
|
+
# or
|
|
382
|
+
bun run start:http
|
|
265
383
|
\`\`\`
|
|
266
384
|
|
|
267
385
|
- **Run checks and tests:**
|
|
386
|
+
|
|
268
387
|
\`\`\`sh
|
|
269
|
-
bun run devcheck
|
|
270
|
-
bun run test
|
|
388
|
+
bun run devcheck # Lint, format, typecheck, security
|
|
389
|
+
bun run test # Vitest test suite
|
|
390
|
+
bun run lint:mcp # Validate MCP definitions against spec
|
|
271
391
|
\`\`\`
|
|
272
392
|
|
|
393
|
+
### Docker
|
|
394
|
+
|
|
395
|
+
\`\`\`sh
|
|
396
|
+
docker build -t my-mcp-server .
|
|
397
|
+
docker run --rm -e ACME_API_KEY=your-key -p 3010:3010 my-mcp-server
|
|
398
|
+
\`\`\`
|
|
399
|
+
|
|
400
|
+
The Dockerfile defaults to HTTP transport, stateless session mode, and logs to `/var/log/my-mcp-server`. OpenTelemetry peer dependencies are installed by default — build with `--build-arg OTEL_ENABLED=false` to omit them.
|
|
401
|
+
|
|
273
402
|
### Cloudflare Workers
|
|
274
403
|
|
|
275
404
|
1. **Build the Worker bundle:**
|
|
405
|
+
|
|
276
406
|
\`\`\`sh
|
|
277
407
|
bun run build:worker
|
|
278
408
|
\`\`\`
|
|
279
409
|
|
|
280
410
|
2. **Deploy:**
|
|
411
|
+
|
|
281
412
|
\`\`\`sh
|
|
282
413
|
bun run deploy:prod
|
|
283
414
|
\`\`\`
|
|
284
415
|
```
|
|
285
416
|
|
|
286
|
-
Include the Docker or Workers subsection only if the server supports it.
|
|
417
|
+
Include the Docker or Workers subsection only if the server supports it. The Docker trailing paragraph (log directory, OTEL build arg) is important — it documents Dockerfile behavior that isn't obvious from the build command.
|
|
287
418
|
|
|
288
419
|
### Project Structure
|
|
289
420
|
|
|
290
421
|
Directory/purpose table orienting contributors to the codebase.
|
|
291
422
|
|
|
292
423
|
```markdown
|
|
293
|
-
## Project
|
|
424
|
+
## Project structure
|
|
294
425
|
|
|
295
426
|
| Directory | Purpose |
|
|
296
427
|
|:----------|:--------|
|
|
428
|
+
| `src/index.ts` | `createApp()` entry point — registers tools/resources/prompts and inits services. |
|
|
429
|
+
| `src/config` | Server-specific environment variable parsing and validation with Zod. |
|
|
297
430
|
| `src/mcp-server/tools` | Tool definitions (`*.tool.ts`). |
|
|
298
431
|
| `src/mcp-server/resources` | Resource definitions (`*.resource.ts`). |
|
|
432
|
+
| `src/mcp-server/prompts` | Prompt definitions (`*.prompt.ts`). |
|
|
299
433
|
| `src/services` | Domain service integrations. |
|
|
300
|
-
| `src/config` | Environment variable parsing and validation with Zod. |
|
|
301
434
|
| `tests/` | Unit and integration tests mirroring `src/`. |
|
|
302
435
|
```
|
|
303
436
|
|
|
304
437
|
### Development Guide
|
|
305
438
|
|
|
306
|
-
Brief — link to CLAUDE.md for full details. State
|
|
439
|
+
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.
|
|
307
440
|
|
|
308
441
|
```markdown
|
|
309
|
-
## Development
|
|
442
|
+
## Development guide
|
|
310
443
|
|
|
311
444
|
See [`CLAUDE.md`](./CLAUDE.md) for development guidelines and architectural rules. The short version:
|
|
312
445
|
|
|
313
446
|
- Handlers throw, framework catches — no `try/catch` in tool logic
|
|
314
|
-
- Use `ctx.log` for
|
|
315
|
-
- Register new tools and resources
|
|
447
|
+
- Use `ctx.log` for request-scoped logging, `ctx.state` for tenant-scoped storage
|
|
448
|
+
- Register new tools and resources via the barrels in `src/mcp-server/*/definitions/index.ts`
|
|
449
|
+
- Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields
|
|
316
450
|
```
|
|
317
451
|
|
|
318
452
|
### Contributing
|
|
@@ -320,7 +454,7 @@ See [`CLAUDE.md`](./CLAUDE.md) for development guidelines and architectural rule
|
|
|
320
454
|
```markdown
|
|
321
455
|
## Contributing
|
|
322
456
|
|
|
323
|
-
Issues and pull requests are welcome. Run checks before submitting:
|
|
457
|
+
Issues and pull requests are welcome. Run checks and tests before submitting:
|
|
324
458
|
|
|
325
459
|
\`\`\`sh
|
|
326
460
|
bun run devcheck
|
|
@@ -343,8 +477,12 @@ Apache-2.0 — see [LICENSE](LICENSE) for details.
|
|
|
343
477
|
- **Accuracy over aspiration.** Only document what exists. Don't describe planned features as if they're implemented.
|
|
344
478
|
- **Tools first.** The tool surface is the most important content. Lead with it.
|
|
345
479
|
- **Tables over prose** for structured data (tools, config, directories). Scannable and diff-friendly.
|
|
346
|
-
- **Two-layer tool docs.**
|
|
480
|
+
- **Two-layer tool docs.** Grouping sentence + summary table for quick scanning, per-tool subsections for detail. Skip subsections for trivial tools.
|
|
481
|
+
- **Combined resources + prompts.** Single table with a `Type` column, not separate sections.
|
|
482
|
+
- **Promote hosted instances.** If there's a public URL, put it in a top-level callout under the badges — not buried in Getting Started.
|
|
483
|
+
- **Three install configs.** `bunx`, `npx`, `docker run` in that order. Each as a complete MCP-client JSON block.
|
|
347
484
|
- **Real names from code.** Tool names, env vars, and URIs must match the source exactly. Copy from the definitions, don't paraphrase.
|
|
348
|
-
- **Lowest friction first.**
|
|
485
|
+
- **Lowest friction first.** Hosted instance > bunx > npx > docker > clone.
|
|
349
486
|
- **No badges unless publishing.** Badges for unpublished packages are noise.
|
|
487
|
+
- **Client-agnostic framing.** Say "your MCP client configuration file", not `claude_desktop_config.json`.
|
|
350
488
|
- **Keep it current.** Update the README whenever tools are added or removed.
|
package/skills/setup/SKILL.md
CHANGED
|
@@ -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
|
+
version: "1.3"
|
|
8
8
|
audience: external
|
|
9
9
|
type: workflow
|
|
10
10
|
---
|
package/templates/AGENTS.md
CHANGED
|
@@ -76,6 +76,7 @@ export const searchItems = tool('search_items', {
|
|
|
76
76
|
|
|
77
77
|
// format() populates content[] — the only field most LLM clients forward to
|
|
78
78
|
// the model. Render all data the LLM needs, not just a count or title.
|
|
79
|
+
// Enforced at lint time: every field in `output` must appear in the rendered text.
|
|
79
80
|
format: (result) => [{
|
|
80
81
|
type: 'text',
|
|
81
82
|
text: result.items.map(i => `**${i.id}**: ${i.name}`).join('\n'),
|
package/templates/CLAUDE.md
CHANGED
|
@@ -76,6 +76,7 @@ export const searchItems = tool('search_items', {
|
|
|
76
76
|
|
|
77
77
|
// format() populates content[] — the only field most LLM clients forward to
|
|
78
78
|
// the model. Render all data the LLM needs, not just a count or title.
|
|
79
|
+
// Enforced at lint time: every field in `output` must appear in the rendered text.
|
|
79
80
|
format: (result) => [{
|
|
80
81
|
type: 'text',
|
|
81
82
|
text: result.items.map(i => `**${i.id}**: ${i.name}`).join('\n'),
|
package/skills/devcheck/SKILL.md
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: devcheck
|
|
3
|
-
description: >
|
|
4
|
-
Lint, format, typecheck, and verify the project is clean. Use after making changes, before committing, or when the user asks to verify quality.
|
|
5
|
-
metadata:
|
|
6
|
-
author: cyanheads
|
|
7
|
-
version: "1.3"
|
|
8
|
-
audience: external
|
|
9
|
-
type: workflow
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## What It Runs
|
|
13
|
-
|
|
14
|
-
`bun run devcheck` runs a broader check suite than just lint + types. By default it includes local hygiene checks, MCP definition linting, Biome, TypeScript, and slow dependency/security checks unless `--fast` is passed. Tests are opt-in via `--test`.
|
|
15
|
-
|
|
16
|
-
| Check | Tool | Notes |
|
|
17
|
-
|:------|:-----|:------|
|
|
18
|
-
| TODOs / FIXMEs | `git grep` | Fails on tracked TODO/FIXME markers outside excluded files |
|
|
19
|
-
| Tracked secrets | `git ls-files` | Flags tracked `.env`, keys, credentials, and similar sensitive files |
|
|
20
|
-
| MCP definitions | `bun run scripts/lint-mcp.ts` | Validates tool/resource/prompt definitions against framework rules |
|
|
21
|
-
| Biome | `biome check` | Unified lint + format — read-only by default |
|
|
22
|
-
| TypeScript | `tsc --noEmit` | Full project type check |
|
|
23
|
-
| Unused dependencies | `depcheck` | Runs by default; network-free but slower on large repos |
|
|
24
|
-
| Security audit | `bun audit` | Runs by default unless `--fast` or `--no-audit` |
|
|
25
|
-
| Outdated dependencies | `bun outdated` | Runs by default unless `--fast` or `--no-deps` |
|
|
26
|
-
| Tests | `vitest run` | Off by default; enable with `bun run devcheck --test` |
|
|
27
|
-
|
|
28
|
-
To auto-fix lint/format issues, run `bun run format`.
|
|
29
|
-
|
|
30
|
-
## Steps
|
|
31
|
-
|
|
32
|
-
1. Run `bun run devcheck`
|
|
33
|
-
2. Read the failing checks in the summary and per-check output
|
|
34
|
-
3. Fix the reported issues
|
|
35
|
-
4. Re-run `bun run devcheck` until clean
|
|
36
|
-
5. If the change touches runtime behavior, also run `bun run devcheck --test` or `bun run test`
|
|
37
|
-
6. Do not consider this skill complete until the required commands exit successfully with no errors
|
|
38
|
-
|
|
39
|
-
## Common Issues
|
|
40
|
-
|
|
41
|
-
| Check | Error Type | Typical Fix |
|
|
42
|
-
|:------|:-----------|:------------|
|
|
43
|
-
| TODOs / FIXMEs | Tracked work markers | Resolve or remove the marker before committing |
|
|
44
|
-
| Tracked secrets | Sensitive files in git | Add to `.gitignore` and remove from the index |
|
|
45
|
-
| MCP definitions | Definition lint errors | Fix schema/name/annotation issues reported by `lint-mcp` |
|
|
46
|
-
| Biome | Lint/format errors | Run `bun run format` to auto-fix, or address the flagged rule manually |
|
|
47
|
-
| TypeScript | Type errors | Fix type mismatches, missing properties, incorrect generics |
|
|
48
|
-
| Security audit | Vulnerabilities in direct deps | Update or replace the affected dependency |
|
|
49
|
-
| Outdated deps | Stale package versions | Run `bun update` or allowlist intentionally pinned packages |
|
|
50
|
-
|
|
51
|
-
## Checklist
|
|
52
|
-
|
|
53
|
-
- [ ] `bun run devcheck` exits with no errors
|
|
54
|
-
- [ ] Tests run when needed (`bun run devcheck --test` or `bun run test`)
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: walkthrough-init
|
|
3
|
-
description: >
|
|
4
|
-
Trace the agent onboarding flow after `init` scaffolds a new project. Starts from the agent reading CLAUDE.md for the first time and follows every instruction chain through skills and framework docs. Use to audit the onboarding path, find dead ends, or catch missing instructions.
|
|
5
|
-
metadata:
|
|
6
|
-
author: cyanheads
|
|
7
|
-
version: "1.0"
|
|
8
|
-
audience: internal
|
|
9
|
-
type: debug
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## What this skill does
|
|
13
|
-
|
|
14
|
-
A developer has run `npx @cyanheads/mcp-ts-core init banking-mcp-server` and `bun install`. They open the project and run `claude`. You are that agent, seeing this project for the first time.
|
|
15
|
-
|
|
16
|
-
Trace the onboarding path through the actual files as they exist right now. Follow every instruction chain — read what you're told to read, do what you're told to do, and report what happens.
|
|
17
|
-
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
## Instructions
|
|
21
|
-
|
|
22
|
-
### Step 1: Read the project's CLAUDE.md
|
|
23
|
-
|
|
24
|
-
This is the first file the agent sees. Read `CLAUDE.md` at the project root. Report:
|
|
25
|
-
|
|
26
|
-
- What does it tell you to do first?
|
|
27
|
-
- What files does it point you to?
|
|
28
|
-
- Does it mention the `setup` skill? How?
|
|
29
|
-
|
|
30
|
-
### Step 2: Follow the first instruction
|
|
31
|
-
|
|
32
|
-
Whatever CLAUDE.md says to do first — do it. If it says to read a file, read it. If it says to run a skill, read that skill's SKILL.md. Report what you find and what it tells you to do next.
|
|
33
|
-
|
|
34
|
-
### Step 3: Keep following the chain
|
|
35
|
-
|
|
36
|
-
Continue following instructions until you've completed the full onboarding loop. At each step:
|
|
37
|
-
|
|
38
|
-
- What file are you reading?
|
|
39
|
-
- What does it tell you to do?
|
|
40
|
-
- Can you actually do it? (Do the referenced files/paths exist?)
|
|
41
|
-
- What's the next step it points you to?
|
|
42
|
-
|
|
43
|
-
### Step 4: Report
|
|
44
|
-
|
|
45
|
-
After the chain ends (or breaks), produce:
|
|
46
|
-
|
|
47
|
-
1. **The path you followed** — ordered list of files read and actions taken
|
|
48
|
-
2. **Broken links** — instructions that point to files that don't exist, skills that aren't written, or actions that can't be completed
|
|
49
|
-
3. **Dead ends** — places where the instructions stop and you don't know what to do next
|
|
50
|
-
4. **The complete onboarding flow** — a summary of what a new agent actually experiences, step by step
|