@cyanheads/mcp-ts-core 0.10.9 → 0.10.11
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/AGENTS.md +22 -10
- package/CLAUDE.md +22 -10
- package/README.md +1 -10
- package/biome.json +1 -1
- package/changelog/0.10.x/0.10.10.md +39 -0
- package/changelog/0.10.x/0.10.11.md +39 -0
- package/changelog/template.md +5 -3
- package/dist/core/app.js +3 -3
- package/dist/core/app.js.map +1 -1
- package/dist/logs/combined.log +8 -19
- package/dist/logs/error.log +4 -10
- package/dist/mcp-server/tools/utils/toolDefinition.d.ts +4 -3
- package/dist/mcp-server/tools/utils/toolDefinition.d.ts.map +1 -1
- package/dist/mcp-server/tools/utils/toolDefinition.js.map +1 -1
- package/dist/utils/parsing/yamlParser.d.ts +1 -1
- package/dist/utils/parsing/yamlParser.d.ts.map +1 -1
- package/dist/utils/parsing/yamlParser.js +5 -2
- package/dist/utils/parsing/yamlParser.js.map +1 -1
- package/package.json +24 -28
- package/scripts/build-changelog.ts +1 -1
- package/skills/api-utils/SKILL.md +1 -1
- package/skills/api-utils/references/parsing.md +1 -1
- package/skills/code-simplifier/SKILL.md +6 -6
- package/skills/design-mcp-server/SKILL.md +51 -15
- package/skills/git-wrapup/SKILL.md +12 -5
- package/skills/orchestrations/SKILL.md +2 -2
- package/skills/polish-docs-meta/SKILL.md +1 -1
- package/skills/polish-docs-meta/references/readme.md +0 -14
- package/skills/release-and-publish/SKILL.md +3 -1
- package/templates/AGENTS.md +27 -21
- package/templates/CLAUDE.md +27 -21
- package/templates/changelog/template.md +5 -3
- package/templates/package.json +2 -2
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Post-session code review and cleanup against a working tree of changes. Analyzes `git diff` to simplify, consolidate, and align changed code with the existing codebase — modernize syntax, remove unnecessary complexity, consolidate duplicated logic, catch efficiency issues. Use after a substantive working session, or when asked to clean up, simplify, reduce slop, consolidate, modernize, tighten up, or de-slop code. For `@cyanheads/mcp-ts-core` projects, includes specific transformations for tool/resource/prompt definitions, the ctx pattern, error factories, and framework idioms.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.2"
|
|
8
8
|
audience: external
|
|
9
9
|
type: workflow
|
|
10
10
|
---
|
|
@@ -21,7 +21,7 @@ Post-session cleanup pass. Reviews what changed, understands how it fits the exi
|
|
|
21
21
|
|
|
22
22
|
### Phase 1: Identify changes
|
|
23
23
|
|
|
24
|
-
Run `git
|
|
24
|
+
Run `git status` to see the shape of the working tree, then `git diff HEAD` for all uncommitted changes (staged and unstaged). Untracked files never appear in the diff — read new files directly. If the tree is clean, review the most recently modified files from the current session.
|
|
25
25
|
|
|
26
26
|
### Phase 2: Understand the surrounding codebase
|
|
27
27
|
|
|
@@ -72,7 +72,7 @@ Evaluate the changes across these dimensions. Not every dimension applies to eve
|
|
|
72
72
|
|
|
73
73
|
1. **Filter findings ruthlessly.** If a finding is a false positive or not worth the churn, skip it. Don't argue with yourself about borderline cases — move on.
|
|
74
74
|
2. **Transform incrementally** — one category of change at a time (modernize syntax, then reduce nesting, then consolidate).
|
|
75
|
-
3. **Verify equivalence** — all functionality, types, and public interfaces must remain unchanged.
|
|
75
|
+
3. **Verify equivalence** — all functionality, types, and public interfaces must remain unchanged. Run the project's gates after transforming (`bun run devcheck` and the test suite in mcp-ts-core projects; typecheck/lint/tests elsewhere); a simplification that breaks the build is worse than the verbosity it removed.
|
|
76
76
|
4. **Keep the diff minimal.** Only touch lines that have a real reason to change. Don't reformat untouched code, add comments to code you didn't modify, or "improve" things that are already fine.
|
|
77
77
|
|
|
78
78
|
When done, briefly summarize what was fixed or confirm the code was already clean.
|
|
@@ -88,13 +88,13 @@ The tables below cover TypeScript and Python. For other languages, apply analogo
|
|
|
88
88
|
| `const x: Foo = { ... } as Foo` | `const x = { ... } satisfies Foo` | Type-checked without assertion |
|
|
89
89
|
| `let resource = acquire(); try { ... } finally { release(resource) }` | `using resource = acquire()` | Explicit resource disposal (TS 5.2+) |
|
|
90
90
|
| `if (x !== null && x !== undefined)` | `if (x != null)` | Idiomatic null/undefined check |
|
|
91
|
-
| `arr.filter(x => x !== null) as T[]` | `arr.filter(
|
|
91
|
+
| `arr.filter(x => x !== null) as T[]` | `arr.filter(x => x != null)` | TS 5.5+ infers the type predicate — no cast; on older TS use an explicit `(x): x is T` predicate |
|
|
92
92
|
| `export { foo } from './foo/index.js'` | Direct imports at call sites | Avoid barrel re-exports inside the package; barrel exports are for public APIs only |
|
|
93
93
|
| `async function f() { const a = await x(); const b = await y(); }` | `const [a, b] = await Promise.all([x(), y()])` | Parallel when independent |
|
|
94
94
|
| `obj.x !== undefined ? obj.x : fallback` | `obj.x ?? fallback` | Nullish coalescing |
|
|
95
95
|
| `if (a) { if (b) { if (c) { ... } } }` | Guard clauses with early returns | Reduce nesting |
|
|
96
96
|
| `try { risky() } catch (e: any) { ... }` | `try { risky() } catch (e: unknown) { ... }` | Type-safe error handling |
|
|
97
|
-
| `enum Status { A, B, C }` | `const Status = { A: 'A', B: 'B', C: 'C' } as const` | Prefer const objects
|
|
97
|
+
| `enum Status { A, B, C }` | `const Status = { A: 'A', B: 'B', C: 'C' } as const` | Prefer const objects over enums — but switching numeric values to strings changes serialized output; keep values stable if they're persisted (string enums are acceptable) |
|
|
98
98
|
| `function f(a: string, b: string, c: string, d?: string)` | `function f(opts: FnOptions)` | Options object when >3 params |
|
|
99
99
|
| `throw new Error('Bad input')` (in a tool handler) | `throw validationError('Bad input', { field: 'x' })` | Use framework error factories so the framework can classify and instrument |
|
|
100
100
|
| `const ATTR_KEY = 'mcp.tool.name'` | `import { ATTR_MCP_TOOL_NAME } from '@cyanheads/mcp-ts-core/utils'` | Use framework attribute constants |
|
|
@@ -109,7 +109,7 @@ The tables below cover TypeScript and Python. For other languages, apply analogo
|
|
|
109
109
|
| `class Config: def __init__(self, a, b, c): self.a = a ...` | `@dataclass class Config: a: str; b: int; c: float` | Less boilerplate, built-in eq/repr |
|
|
110
110
|
| `results = []; for item in items: results.append(transform(item))` | `results = [transform(item) for item in items]` | Idiomatic comprehension |
|
|
111
111
|
| `f = open('x'); try: ... finally: f.close()` | `with open('x') as f: ...` | Context manager for resources |
|
|
112
|
-
| `
|
|
112
|
+
| `m = pattern.match(s)` then `if m: use(m)` | `if (m := pattern.match(s)): use(m)` | Walrus operator where it removes a throwaway assignment |
|
|
113
113
|
| `"Hello " + name + "!"` | `f"Hello {name}!"` | f-string over concatenation |
|
|
114
114
|
| `except Exception as e: pass` | `except SpecificError as e: log(e)` | Catch specific, never bare except/pass |
|
|
115
115
|
| `from module import *` | `from module import specific_name` | Explicit imports only |
|
|
@@ -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.19"
|
|
8
8
|
audience: external
|
|
9
9
|
type: workflow
|
|
10
10
|
---
|
|
@@ -77,8 +77,10 @@ When research is genuinely parallelizable (multiple independent APIs, several SD
|
|
|
77
77
|
- **Field selection** — check if the API supports `fields` or `select` parameters to request only the data you need. This reduces payload size dramatically for large objects.
|
|
78
78
|
- **Pagination behavior** — verify token format, page size limits, and what happens when results exceed one page.
|
|
79
79
|
- **Error shapes** — trigger real 400/404/429 responses to see the actual error format, not just what docs claim.
|
|
80
|
+
- **Unknown-param behavior** — send one deliberately misspelled parameter. If the API silently ignores it (plausible but unfiltered results instead of an error), every typo'd or unverified param name becomes a silent-wrongness bug — the service layer then needs a strict allowlist of confirmed spellings, and new filters require a probe before they ship.
|
|
81
|
+
- **Omission semantics** — for each major optional parameter, check what omitting it actually returns. Some APIs default to the intuitive scope; others silently widen (all historical versions, all statuses, global instead of regional). A default that changes result *meaning* becomes a server-side default plus an echoed output field, not something left to the agent.
|
|
80
82
|
|
|
81
|
-
**Stopping condition:** at minimum, probe one list/search endpoint, one single-item GET,
|
|
83
|
+
**Stopping condition:** at minimum, probe one list/search endpoint, one single-item GET, one error case (force a 404 or 400), and one unknown-param request. For large APIs with many resource types, add one probe per major noun. Stop when the response shapes and error envelope are confirmed.
|
|
82
84
|
|
|
83
85
|
This step prevents building a service layer against assumed response shapes that don't match reality.
|
|
84
86
|
|
|
@@ -139,8 +141,9 @@ Most tools follow the `{server}_{verb}_{noun}` default — one focused responsib
|
|
|
139
141
|
|:------|:--------|:-------------|:---------|
|
|
140
142
|
| **Workflow** | Multi-step orchestration that replaces a common agent chain | N upstream calls (often parallelized); may elicit confirmation; may need mid-flow cleanup | `clinicaltrials_find_studies` (search → filter → rank) |
|
|
141
143
|
| **Instruction** | State-aware procedural guidance — advice, not action | Static markdown + a few live-state fetches, `readOnlyHint: true`, outputs `nextToolSuggestions` pre-filling the recommended follow-up. No writes. | `git_wrapup_instructions` |
|
|
144
|
+
| **Reference** | Decode opaque domain vocabulary — codes, enums, identifier formats, coverage windows — so agents can build valid inputs for the rest of the surface | Static tables or one cached fetch (often zero upstream calls); consolidate N lists under one `topic` enum; `readOnlyHint: true`, `openWorldHint: false` when offline | `medcode_list_systems`, `osv_list_ecosystems` |
|
|
142
145
|
|
|
143
|
-
These aren't boxes every tool must fit into — some blend shapes — but the design pressures differ enough that naming them helps avoid re-discovering the patterns per server. The subsections below cover considerations specific to each — workflow framing applies broadly
|
|
146
|
+
These aren't boxes every tool must fit into — some blend shapes — but the design pressures differ enough that naming them helps avoid re-discovering the patterns per server. The subsections below cover considerations specific to each — workflow framing applies broadly; instruction tools, reference tools, and workflow safety are their own subsections.
|
|
144
147
|
|
|
145
148
|
#### Think in workflows, not endpoints
|
|
146
149
|
|
|
@@ -252,6 +255,12 @@ const wrapupInstructions = tool('git_wrapup_instructions', {
|
|
|
252
255
|
|
|
253
256
|
Prior art: [`git_wrapup_instructions`](https://github.com/cyanheads/git-mcp-server) walks through staging, commit, and push with repo state inspected. If a server has recurring "how do I do X well given my state" questions, an instruction tool typically beats N topic-specific tools and duplicating guidance in tool descriptions.
|
|
254
257
|
|
|
258
|
+
#### Reference tools
|
|
259
|
+
|
|
260
|
+
**Applies when:** the domain speaks in opaque vocabulary — enum codes, classification systems, identifier formats, per-source coverage windows — that agents must supply as inputs elsewhere. Skip when inputs are self-evident (free text, ISO dates, well-known formats).
|
|
261
|
+
|
|
262
|
+
A reference tool is the surface's decoder ring: which codes exist, what they mean, what format each identifier takes, what each source covers. Consolidate what could be N per-list tools under one `topic` enum, and serve it from static tables or a cached fetch so it stays cheap to call. Its second job is structural: it is the standing **target of recovery routing** — error `recovery` strings, zero-hit notices, and resolver guidance across the surface end with "consult the reference tool, topic X" (see Error design), which only works when the tool exists. Implement it first — it usually has no service dependency, and it grounds field-testing for every other tool.
|
|
263
|
+
|
|
255
264
|
#### Workflow tool safety
|
|
256
265
|
|
|
257
266
|
**Applies when:** a tool performs multi-step mutations with destructive modes (`send`/`apply`/`promote`) that benefit from human confirmation before the irreversible step fires. Skip for read-only or idempotent workflows.
|
|
@@ -348,6 +357,7 @@ output: z.object({
|
|
|
348
357
|
}),
|
|
349
358
|
```
|
|
350
359
|
|
|
360
|
+
- **Empty results are a designed surface, not a fallthrough.** For every search/list tool, spec the zero-hit behavior at design time: zero hits are success + an `enrichment` notice, never an error, and the notice is composed from condition → fragment pairs (too-narrow filter, a defaulted date window, a syntax trap), each routing to a concrete next call — relax a named filter, switch to the named sibling tool, or consult the reference tool. Relatedly, when the server applies a default that changes result *semantics* (an as-of date, an implicit status filter), echo the applied value in the output — an agent can't reason about a filter it can't see.
|
|
351
361
|
- **Capped lists disclose truncation.** When a tool accepts a cap-like input (`limit`, `per_page`, `page_size`, `max_results`, `max_items`) and returns an array, the handler must disclose when the cap was hit. Standard fields: `truncated: true`, `shown`, `cap` in the `enrichment` block via `ctx.enrich.truncated({ shown, cap })`. `ctx.enrich.total(n)` (writes `totalCount`) is also recognized. Silent caps leave the agent treating a partial set as complete. The `capped-list-no-truncation` lint rule enforces this; see `api-linter` and `api-context`'s `ctx.enrich.truncated()` section.
|
|
352
362
|
- **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.
|
|
353
363
|
- **Spill big *analytical* results to a queryable surface.** When a tool's row set is something an agent would run SQL over (aggregate, group, join) *and* can exceed any reasonable context budget — paginated APIs, streamed exports, big query results — pair an inline preview with a `DataCanvas` table holding the full set. **Two rules gate this:** (1) it must earn its keep on *shape, not size* — a discovery/search surface of categorical metadata (titles, IDs) is not analytical and doesn't get a canvas regardless of row count; for name→ID resolution over a bounded list use [MCP-side list filtering](#mcp-side-list-filtering); (2) the `canvas_id` is reachable only if the same server **also exposes a `dataframe_query` tool** — emit one without the other and the handle is dead output. Compute distributions or refinement hints across the full result, not the preview, so aggregate signal stays honest. See `api-canvas` for the `spillover()` helper and both rules in full.
|
|
@@ -421,21 +431,25 @@ Two params, two behaviors — keep them named distinctly:
|
|
|
421
431
|
|
|
422
432
|
Errors are part of the tool's interface — design them during the design phase, not as an afterthought. Three aspects: **the contract** (which failures are public), **classification** (what error code), and **messaging** (what the LLM reads).
|
|
423
433
|
|
|
424
|
-
**Declare a typed contract for domain failures.** When a tool has known failure modes the agent should plan around (`no_match`, `queue_full`, `vendor_down`), enumerate them as `errors: [{ reason, code, when, retryable? }]` on the definition. The framework types `ctx.fail(reason, …)` against the declared reason union (typos become TS errors) and auto-populates `data.reason` on the thrown error for stable observability. The error reaches clients with parity across both surfaces — `structuredContent.error` (Claude Code) and `content[]` text (Claude Desktop). Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`) bubble from anywhere and don't need to be enumerated. See `api-errors` skill for the full pattern.
|
|
434
|
+
**Declare a typed contract for domain failures.** When a tool has known failure modes the agent should plan around (`no_match`, `queue_full`, `vendor_down`), enumerate them as `errors: [{ reason, code, when, recovery, retryable? }]` on the definition. `recovery` is required metadata — the agent's next move when this failure fires (≥ 5 words, lint-validated; spread `ctx.recoveryFor('reason')` into the throw-site `data` to send it on the wire as `data.recovery.hint`). The framework types `ctx.fail(reason, …)` against the declared reason union (typos become TS errors) and auto-populates `data.reason` on the thrown error for stable observability. The error reaches clients with parity across both surfaces — `structuredContent.error` (Claude Code) and `content[]` text (Claude Desktop). Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`) bubble from anywhere and don't need to be enumerated. See `api-errors` skill for the full pattern.
|
|
425
435
|
|
|
426
436
|
**Classify errors by origin.** Different error sources need different codes and different recovery guidance. Map the failure modes for each tool during design:
|
|
427
437
|
|
|
428
438
|
| Origin | Examples | Error code | Agent can recover? |
|
|
429
439
|
|:-------|:---------|:-----------|:-------------------|
|
|
430
|
-
| **Client input** | Bad ID format, invalid params, missing required field, out-of-range value | `
|
|
440
|
+
| **Client input** | Bad ID format, invalid params, missing required field, out-of-range value | `ValidationError` | Yes — fix the input and retry |
|
|
431
441
|
| **Upstream API** | 5xx, rate limit (429), timeout, network error | `ServiceUnavailable` | Maybe — retry later, or the upstream is down |
|
|
432
|
-
| **Not found** | Valid ID format but entity doesn't exist | `NotFound` (or `
|
|
442
|
+
| **Not found** | Valid ID format but entity doesn't exist | `NotFound` (or `ValidationError` if ambiguous) | Yes — check the ID, try a search |
|
|
433
443
|
| **Auth/permissions** | Insufficient scopes, expired token | `Forbidden` / `Unauthorized` | Maybe — escalate or re-auth |
|
|
434
444
|
| **Server internal** | Parse failure, missing config, unexpected state | `InternalError` | No — server-side issue |
|
|
435
445
|
|
|
446
|
+
(`InvalidParams` also exists — the SDK emits it when input fails Zod schema validation before the handler runs. Anything the handler itself throws about inputs uses `ValidationError`.)
|
|
447
|
+
|
|
436
448
|
The framework auto-classifies many of these at runtime (HTTP status codes, JS error types, common patterns), but explicit classification in the handler gives better error messages. For declared contract failures, throw via `ctx.fail('reason', …)`. For ad-hoc throws outside the contract, use error factories (`notFound()`, `validationError()`, etc.) when the code matters; plain `throw new Error()` when the framework's auto-classification is good enough.
|
|
437
449
|
|
|
438
|
-
**
|
|
450
|
+
**Expected misses are results, not errors.** When a tool's whole job is resolving one identifier — a citation, a code, a name → ID — a no-match is an expected outcome the agent must reason about, not a failure. Return `{ found: false, guidance }` instead of throwing, and treat `guidance` as a first-class recovery surface: per miss outcome, say what didn't parse or resolve and route to the named tool that can recover (the broader search tool, the reference tool). Agents self-correct better from a structured miss than from a throw — a throw reads as "something broke," a miss result reads as "adjust and retry."
|
|
451
|
+
|
|
452
|
+
**Write error messages as recovery instructions.** The message is the agent's only signal for what to do next — and the strongest recovery instruction ends in a named tool call, never a bare "check your input."
|
|
439
453
|
|
|
440
454
|
```ts
|
|
441
455
|
// Bad — dead end, no recovery path
|
|
@@ -461,7 +475,7 @@ throw forbidden(
|
|
|
461
475
|
throw notFound(`Paper '${id}' not found on arXiv. Verify the ID format (e.g., '2401.12345' or '2401.12345v2').`);
|
|
462
476
|
```
|
|
463
477
|
|
|
464
|
-
**During design,
|
|
478
|
+
**During design, settle the full contract for each tool** — reason, code, when-clause, *and the verbatim `recovery` string* — in the tool's section of the design doc; they become the literal `errors: [...]` entries during scaffolding. Hold every recovery string (and zero-hit notice, and resolver `guidance`) to the **no-dead-ends rule: it names the concrete next tool call**, with the reference tool as the most common routing target. Settled at design time these stay sharp; left to implementation they degrade into "check your input." Not every failure needs a contract entry; baseline infrastructure errors (5xx, timeouts, validation) are fine to let bubble.
|
|
465
479
|
|
|
466
480
|
#### Design table
|
|
467
481
|
|
|
@@ -474,7 +488,7 @@ Summarize each tool:
|
|
|
474
488
|
| **Description** | Concrete capability statement. Add operational guidance (prerequisites, constraints, gotchas) when non-obvious. |
|
|
475
489
|
| **Input schema** | `.describe()` on every field. Constrained types (enums, literals, regex). Explain costs/tradeoffs of parameter choices. |
|
|
476
490
|
| **Output schema** | Designed for the LLM's next action. Include chaining IDs. Communicate filtering. Post-write state where useful. |
|
|
477
|
-
| **Errors** | Declare domain failure modes as a typed contract (`errors: [{ reason, code, when, retryable? }]`) so `ctx.fail` is type-checked and capable clients can preview failures via `tools/list`.
|
|
491
|
+
| **Errors** | Declare domain failure modes as a typed contract (`errors: [{ reason, code, when, recovery, retryable? }]`) so `ctx.fail` is type-checked and capable clients can preview failures via `tools/list`. Every `recovery` string follows the no-dead-ends rule — it names the next tool call. |
|
|
478
492
|
| **Annotations** | `readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`. Helps clients auto-approve safely. |
|
|
479
493
|
| **Auth scopes** | `tool:<snake_tool_name>:<verb>` or `resource:<kebab-resource-name>:<verb>` (e.g., `tool:inventory_search:read`, `resource:echo-app-ui:read`). Domain-led `<domain>:<verb>` (e.g., `inventory:read`) is an acceptable alternative — pick one convention per server and stay consistent. Skip for read-only or stdio-only servers. |
|
|
480
494
|
|
|
@@ -559,6 +573,17 @@ What this server does, what system it wraps, who it's for.
|
|
|
559
573
|
- Bullet list of capabilities and constraints
|
|
560
574
|
- Auth requirements, rate limits, data access scope
|
|
561
575
|
|
|
576
|
+
## User Goals
|
|
577
|
+
|
|
578
|
+
1. Numbered outcomes agents will accomplish (from Step 2) — every tool in the surface traces back to one
|
|
579
|
+
|
|
580
|
+
## Tools — detail
|
|
581
|
+
|
|
582
|
+
One subsection per tool: a param table (param | type | maps-to | notes), the output field
|
|
583
|
+
list, the error contract table (reason | code | when | recovery — verbatim strings), and
|
|
584
|
+
zero-hit notice fragments for search tools. This is the section implementation reads
|
|
585
|
+
tool-by-tool.
|
|
586
|
+
|
|
562
587
|
## Services
|
|
563
588
|
| Service | Wraps | Used By |
|
|
564
589
|
|:--------|:------|:--------|
|
|
@@ -567,14 +592,21 @@ What this server does, what system it wraps, who it's for.
|
|
|
567
592
|
| Env Var | Required | Description |
|
|
568
593
|
|:--------|:---------|:------------|
|
|
569
594
|
|
|
595
|
+
## Server Instructions
|
|
596
|
+
|
|
597
|
+
Draft `instructions` string for `createApp()` — the orientation every client sees at
|
|
598
|
+
initialize: canonical workflow chain, identifier semantics, rate-limit posture. A short
|
|
599
|
+
paragraph; tool descriptions carry the rest.
|
|
600
|
+
|
|
570
601
|
## Implementation Order
|
|
571
602
|
|
|
572
603
|
1. Config and server setup
|
|
573
604
|
2. Services (external API clients)
|
|
574
|
-
3.
|
|
575
|
-
4.
|
|
576
|
-
5.
|
|
577
|
-
6.
|
|
605
|
+
3. Reference tool (static, no service dependency — grounds field-testing for everything else)
|
|
606
|
+
4. Read-only tools
|
|
607
|
+
5. Write tools
|
|
608
|
+
6. Resources
|
|
609
|
+
7. Prompts
|
|
578
610
|
|
|
579
611
|
Each step is independently testable.
|
|
580
612
|
|
|
@@ -640,9 +672,13 @@ Items without an `If …:` prefix apply to every design. Conditional items only
|
|
|
640
672
|
- [ ] Input schemas use constrained types (enums, literals, regex) over free strings
|
|
641
673
|
- [ ] Output schemas designed for LLM's next action — chaining IDs, post-write state, filtering communicated
|
|
642
674
|
- [ ] `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
|
|
643
|
-
- [ ] Error messages guide recovery — name what went wrong and
|
|
644
|
-
- [ ] **If a tool has known domain failure modes:** typed error contract declared (`errors: [{ reason, code, when, retryable? }]`)
|
|
675
|
+
- [ ] Error messages guide recovery — name what went wrong and the next tool call (no dead ends)
|
|
676
|
+
- [ ] **If a tool has known domain failure modes:** typed error contract declared (`errors: [{ reason, code, when, recovery, retryable? }]`) with verbatim `recovery` strings settled in the design doc, each naming the next tool call
|
|
677
|
+
- [ ] **If the server has search/list tools:** zero-hit notices specced (condition → fragment, each routing to a named next call); server-applied defaults that change result semantics echoed in output
|
|
678
|
+
- [ ] **If a tool resolves a single identifier:** no-match returns `{ found: false, guidance }` — a result, not a throw — with guidance routing per miss outcome
|
|
679
|
+
- [ ] **If the domain has opaque vocabulary (codes, identifier formats, coverage windows):** reference tool designed (`topic` enum), implemented first, and used as the routing target in recovery strings and notices
|
|
645
680
|
- [ ] Annotations set correctly (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`)
|
|
681
|
+
- [ ] Server-level `instructions` string drafted — workflow chain, identifier semantics, rate-limit posture (ships via `createApp()` on every initialize)
|
|
646
682
|
- [ ] Design doc written to `docs/design.md`
|
|
647
683
|
- [ ] Design confirmed with user (or user pre-authorized implementation)
|
|
648
684
|
- [ ] **If ops share a noun:** related operations consolidated under one tool with `mode`/`operation` enum
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Land working-tree changes as logical commits — the work grouped by concern, topped by a release commit (version bump, changelog, regenerated artifacts) and an annotated tag. Verify, commit, tag. Stops at "committed and tagged locally" — no push, no publish. The release-and-publish skill picks up from here. Distilled from the git_wrapup_instructions protocol.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.6"
|
|
8
8
|
audience: external
|
|
9
9
|
type: workflow
|
|
10
10
|
---
|
|
@@ -91,10 +91,12 @@ Create `changelog/<major.minor>.x/<version>.md`. Use `changelog/template.md` as
|
|
|
91
91
|
---
|
|
92
92
|
summary: "<one-line headline, ≤350 chars, no markdown>"
|
|
93
93
|
breaking: false # true if consumers must change code to upgrade
|
|
94
|
-
security: false # true
|
|
94
|
+
security: false # true ONLY for a security fix in this server's own source — NOT a dependency/transitive CVE bump (those go under ## Dependencies)
|
|
95
95
|
---
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
+
**`security:` is a source-code signal — not a dependency-CVE signal.** Set `security: true` only when this release fixes a vulnerability or adds hardening in code *this server ships*. A dependency or transitive CVE bump — even one that clears an advisory (`bun audit` going 1 → 0) — is routine maintenance: record it under `## Dependencies` with the advisory ID and leave the flag `false`. The `🛡️ Security` badge answers "does the server itself have a vuln"; a dep bump must not trip it.
|
|
99
|
+
|
|
98
100
|
**Body:** Section order follows Keep a Changelog — Added / Changed / Deprecated / Removed / Fixed / Security. Include only sections with entries. Delete empty sections.
|
|
99
101
|
|
|
100
102
|
**Tone:** Terse, fact-dense. Lead each bullet with the symbol or concept in **bold**. One sentence per bullet by default. See the authoring guide in `changelog/template.md` for full conventions.
|
|
@@ -151,10 +153,14 @@ git commit -m "<subject>"
|
|
|
151
153
|
### 8. Create an annotated tag
|
|
152
154
|
|
|
153
155
|
```bash
|
|
154
|
-
git tag -a v<version> -m "<tag message with embedded newlines>"
|
|
156
|
+
git tag -a v<version> --cleanup=whitespace -m "<tag message with embedded newlines>"
|
|
155
157
|
```
|
|
156
158
|
|
|
157
|
-
Use `-m` with embedded newlines in the string (the commit `-m`-only constraint applies here too — no heredoc). The tag message renders as the GitHub Release body via `--notes-from-tag`. It must be structured markdown, not a flat string.
|
|
159
|
+
Use `-m` with embedded newlines in the string (the commit `-m`-only constraint applies here too — no heredoc). The tag message renders as the GitHub Release body via `--notes-from-tag`. It must be structured markdown, not a flat string.
|
|
160
|
+
|
|
161
|
+
`--cleanup=whitespace` is load-bearing. The default cleanup (`strip`) deletes `#`-leading lines as comments, so markdown headers silently vanish from the tag body. `--cleanup=verbatim` is worse: it skips end-of-message normalization, so with tag signing enabled the signature is appended flush against the message's last character — git then can't parse its own signature (the tag reads as unsigned) and the whole `-----BEGIN SSH SIGNATURE-----` block publishes verbatim into the GitHub Release body.
|
|
162
|
+
|
|
163
|
+
Format:
|
|
158
164
|
|
|
159
165
|
```
|
|
160
166
|
<theme — omit version number, GitHub prepends v<VERSION>:>
|
|
@@ -194,9 +200,10 @@ Dependency bumps:
|
|
|
194
200
|
git log --oneline -8 # confirm the commit stack: work commits + release commit on top
|
|
195
201
|
git show v<version> --stat | head -20 # confirm tag points at HEAD (the release commit)
|
|
196
202
|
git status # must be clean
|
|
203
|
+
git tag -l v<version> --format='%(if)%(contents:signature)%(then)signed%(else)unsigned%(end)' # with tag signing enabled, must print "signed"
|
|
197
204
|
```
|
|
198
205
|
|
|
199
|
-
If the working tree isn't clean or the tag doesn't point at HEAD, something went wrong — investigate before proceeding.
|
|
206
|
+
If the working tree isn't clean or the tag doesn't point at HEAD, something went wrong — investigate before proceeding. `unsigned` under enabled tag signing means the signature didn't parse (see step 8's cleanup note) — delete and recreate the tag before it leaks the signature block into the GitHub Release body.
|
|
200
207
|
|
|
201
208
|
**Do NOT push.** This skill stops here. Use the `release-and-publish` skill for the push + publish workflow.
|
|
202
209
|
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Pick and run a multi-phase workflow that chains foundational task skills (`git-wrapup`, `release-and-publish`, `maintenance`, `field-test`, `setup`, etc.) end-to-end. Routes user intent to a workflow file under `workflows/` — greenfield builds, maintenance + release, field-test + fix, or known-work + release. Single source for the universal rules (no commits without authorization, no destructive git, no marketing language), the orchestrator posture (own the goal, ground sub-agents in primary sources, verify against the goal), and the sub-agent strategy (orient block, parallel fanout, isolation, normalization) that apply across every workflow. Sub-agents are an optional capability — workflows run linearly when fanout isn't available.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.5"
|
|
8
8
|
audience: external
|
|
9
9
|
type: workflow
|
|
10
10
|
---
|
|
@@ -58,7 +58,7 @@ These apply to every workflow. Workflow files don't restate them; the orchestrat
|
|
|
58
58
|
5. **No marketing adjectives** in commits, tags, READMEs, or changelog entries — no "comprehensive", "robust", "enhanced", "seamless", "improved". State the change, not its quality.
|
|
59
59
|
6. **One workflow per orchestration run.** Don't interleave two workflows in the same session. If a target needs both (e.g., maintenance surfaces a bug fix that needs field-testing first), sequence them as two workflow runs with a clean handoff in between.
|
|
60
60
|
7. **`gh release create --notes-from-tag` is incompatible with `--repo`.** Always `cd` into the target repo directory for `gh release` commands.
|
|
61
|
-
8. **Annotated tags only** (`git tag -a`), never lightweight. Tag annotation subject omits the version number — GitHub prepends `v<VERSION>:` to release titles when using `--notes-from-tag`, so including the version in the subject creates stutter.
|
|
61
|
+
8. **Annotated tags only** (`git tag -a`), never lightweight, created with `--cleanup=whitespace` — the default (`strip`) deletes `#`-leading lines as comments; `--cleanup=verbatim` glues the SSH signature into the message (unparseable-as-signed tag, signature block leaks into the release body). Tag annotation subject omits the version number — GitHub prepends `v<VERSION>:` to release titles when using `--notes-from-tag`, so including the version in the subject creates stutter.
|
|
62
62
|
9. **Conventional Commits subjects** (`feat|fix|refactor|chore|docs|test|build(scope): message`). One logical concern per commit. The release commit (version bump + changelog + regenerated artifacts) lands on top of a stack of feature/fix commits, never collapsed alongside them.
|
|
63
63
|
10. **Email on any artifact is the user's domain email**, never a personal address that might appear in git config.
|
|
64
64
|
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Finalize documentation and project metadata for a ship-ready MCP server. Use after implementation is complete, tests pass, and devcheck is clean. Safe to run at any stage — each step checks current state and only acts on what still needs work.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "2.
|
|
7
|
+
version: "2.10"
|
|
8
8
|
audience: external
|
|
9
9
|
type: workflow
|
|
10
10
|
---
|
|
@@ -21,7 +21,6 @@ Framework badge ← solo spotlight row — `Built on @cy
|
|
|
21
21
|
## Running the server ← dev, production, Workers/Docker
|
|
22
22
|
## Project structure ← directory/purpose table
|
|
23
23
|
## Development guide ← link to CLAUDE.md/AGENTS.md, key rules
|
|
24
|
-
## Contributing ← brief
|
|
25
24
|
## License ← one line
|
|
26
25
|
```
|
|
27
26
|
|
|
@@ -484,19 +483,6 @@ See [`CLAUDE.md`/`AGENTS.md`](./CLAUDE.md) for development guidelines and archit
|
|
|
484
483
|
- Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields
|
|
485
484
|
```
|
|
486
485
|
|
|
487
|
-
### Contributing
|
|
488
|
-
|
|
489
|
-
```markdown
|
|
490
|
-
## Contributing
|
|
491
|
-
|
|
492
|
-
Issues and pull requests are welcome. Run checks and tests before submitting:
|
|
493
|
-
|
|
494
|
-
\`\`\`sh
|
|
495
|
-
bun run devcheck
|
|
496
|
-
bun run test
|
|
497
|
-
\`\`\`
|
|
498
|
-
```
|
|
499
|
-
|
|
500
486
|
### License
|
|
501
487
|
|
|
502
488
|
One line referencing the LICENSE file.
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Ship a release end-to-end across every registry the project targets (npm, MCP Registry, GitHub Releases for `.mcpb` bundles, GHCR). Runs the final verification gate, pushes commits and tags, then publishes to each applicable destination. Assumes git wrapup (version bumps, changelog, commit, annotated tag) is already complete — this skill is the post-wrapup publish workflow. Retries transient network failures on publish steps; halts with a partial-state report when retries are exhausted or the failure is terminal.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "2.
|
|
7
|
+
version: "2.10"
|
|
8
8
|
audience: external
|
|
9
9
|
type: workflow
|
|
10
10
|
---
|
|
@@ -129,6 +129,8 @@ Halt on any publisher error other than "cannot publish duplicate version".
|
|
|
129
129
|
|
|
130
130
|
### 6. Create GitHub Release
|
|
131
131
|
|
|
132
|
+
Pre-flight: `--notes-from-tag` publishes the tag message as-is. With tag signing enabled, confirm the tag's signature parses — `git tag -l v<version> --format='%(contents:signature)'` must be non-empty. Empty on a signing-enabled repo (e.g. a tag created with `--cleanup=verbatim`) means git is treating the signature as message text, and the `-----BEGIN SSH SIGNATURE-----` block will land in the public release body — recreate the tag per git-wrapup step 8 first.
|
|
133
|
+
|
|
132
134
|
For all projects (including those without `manifest.json`):
|
|
133
135
|
|
|
134
136
|
```bash
|
package/templates/AGENTS.md
CHANGED
|
@@ -182,12 +182,14 @@ Handlers receive a unified `ctx` object. Key properties:
|
|
|
182
182
|
| Property | Description |
|
|
183
183
|
|:---------|:------------|
|
|
184
184
|
| `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. |
|
|
185
|
-
| `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.list(prefix, { cursor, limit })`. Accepts any serializable value. |
|
|
185
|
+
| `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.getMany(keys)`, `.list(prefix, { cursor, limit })`. Accepts any serializable value. |
|
|
186
186
|
| `ctx.elicit` | Ask user for structured input — form call `(message, schema)` or `.url(message, url)` for an external link. **Check for presence first:** `if (ctx.elicit) { ... }` |
|
|
187
|
+
| `ctx.enrich` | Success-path agent context (empty-result notices, query echo, pagination totals) — `ctx.enrich(...)` or `.notice()` / `.total()` / `.echo()` / `.truncated()`. Reaches `structuredContent` and `content[]`; lands only when the definition declares an `enrichment` block (no-op otherwise). |
|
|
188
|
+
| `ctx.content` | Non-text content blocks — `.image(data, mimeType)`, `.audio(data, mimeType)`, or `ctx.content(block)` for a raw block. Prepended to `content[]` after `format()`; never enters `structuredContent`. |
|
|
187
189
|
| `ctx.signal` | `AbortSignal` for cancellation. |
|
|
188
190
|
| `ctx.progress` | Task progress (present when `task: true`) — `.setTotal(n)`, `.increment()`, `.update(message)`. |
|
|
189
191
|
| `ctx.requestId` | Unique request ID. |
|
|
190
|
-
| `ctx.tenantId` | Tenant ID from JWT
|
|
192
|
+
| `ctx.tenantId` | Tenant ID from JWT; `'default'` for stdio or HTTP with auth off. |
|
|
191
193
|
|
|
192
194
|
---
|
|
193
195
|
|
|
@@ -195,7 +197,7 @@ Handlers receive a unified `ctx` object. Key properties:
|
|
|
195
197
|
|
|
196
198
|
Handlers throw — the framework catches, classifies, and formats.
|
|
197
199
|
|
|
198
|
-
**Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` / `resource()` to receive `ctx.fail(reason, …)` typed against the reason union. TypeScript catches typos at compile time, `data.reason` is auto-populated for observability, linter enforces conformance against the handler body. `recovery` is required
|
|
200
|
+
**Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` / `resource()` to receive `ctx.fail(reason, …)` typed against the reason union. TypeScript catches typos at compile time, `data.reason` is auto-populated for observability, linter enforces conformance against the handler body. `recovery` is required (≥ 5 words, lint-validated) — the single source of truth for the agent's next move. Pass `ctx.recoveryFor('reason')` as the throw's data to put it on the wire (`data.recovery.hint`, mirrored into `content[]` text); override with an explicit `{ recovery: { hint: '...' } }` when dynamic runtime context matters. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`) bubble freely and don't need declaring.
|
|
199
201
|
|
|
200
202
|
```ts
|
|
201
203
|
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
@@ -207,7 +209,7 @@ errors: [
|
|
|
207
209
|
],
|
|
208
210
|
async handler(input, ctx) {
|
|
209
211
|
const item = await db.find(input.id);
|
|
210
|
-
if (!item) throw ctx.fail('no_match', `No item ${input.id}
|
|
212
|
+
if (!item) throw ctx.fail('no_match', `No item ${input.id}`, ctx.recoveryFor('no_match'));
|
|
211
213
|
return item;
|
|
212
214
|
}
|
|
213
215
|
```
|
|
@@ -270,7 +272,7 @@ src/
|
|
|
270
272
|
|
|
271
273
|
## Skills
|
|
272
274
|
|
|
273
|
-
Skills are modular instructions in `skills/` at the project root. Read them directly when a task matches — e.g., `skills/add-tool/SKILL.md` when adding a tool.
|
|
275
|
+
Skills are modular instructions in `skills/` at the project root. Read them directly when a task matches — e.g., `skills/add-tool/SKILL.md` when adding a tool. `bun run list-skills` prints the full registry.
|
|
274
276
|
|
|
275
277
|
**Agent skill directory:** Copy skills into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent). Skills then load as context without referencing `skills/` paths. After framework updates, run the `maintenance` skill — Phase B re-syncs the agent directory.
|
|
276
278
|
|
|
@@ -290,7 +292,6 @@ Available skills:
|
|
|
290
292
|
| `tool-defs-analysis` | Read-only audit of MCP definition language across the surface — voice, leaks, defaults, recovery hints, output descriptions |
|
|
291
293
|
| `security-pass` | Audit server for MCP-flavored security gaps: output injection, scope blast radius, input sinks, tenant isolation |
|
|
292
294
|
| `code-simplifier` | Post-session cleanup against `git diff` — modernize syntax, consolidate duplication, align with the codebase |
|
|
293
|
-
| `devcheck` | Lint, format, typecheck, audit |
|
|
294
295
|
| `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
|
|
295
296
|
| `git-wrapup` | Land working-tree changes as a versioned commit + annotated tag — version bump, changelog, verify, tag. Local only. |
|
|
296
297
|
| `release-and-publish` | Push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |
|
|
@@ -298,12 +299,14 @@ Available skills:
|
|
|
298
299
|
| `orchestrations` | Chain task skills into a gated multi-phase pipeline — build-out, QA-fix, update-ship — when you can spawn sub-agents |
|
|
299
300
|
| `report-issue-framework` | File a bug or feature request against `@cyanheads/mcp-ts-core` via `gh` CLI |
|
|
300
301
|
| `report-issue-local` | File a bug or feature request against this server's own repo via `gh` CLI |
|
|
302
|
+
| `techniques` | Catalog of response/data-shaping techniques — overflow handling, payload shaping, retrieval patterns |
|
|
301
303
|
| `api-auth` | Auth modes, scopes, JWT/OAuth |
|
|
302
304
|
| `api-canvas` | DataCanvas: register tabular data, run SQL, export, plus the `spillover()` helper for big result sets — Tier 3 opt-in |
|
|
303
305
|
| `api-config` | AppConfig, parseConfig, env vars |
|
|
304
306
|
| `api-context` | Context interface, logger, state, progress |
|
|
305
307
|
| `api-errors` | McpError, JsonRpcErrorCode, error patterns |
|
|
306
308
|
| `api-linter` | Definition linter rule catalog — invoked by `bun run lint:mcp` and `devcheck` |
|
|
309
|
+
| `api-mirror` | MirrorService: persistent self-refreshing local mirror (embedded SQLite + FTS5) of a bulk upstream dataset — Tier 3 opt-in |
|
|
307
310
|
| `api-services` | LLM, Speech, Graph services |
|
|
308
311
|
| `api-testing` | createMockContext, test patterns |
|
|
309
312
|
| `api-utils` | Formatting, parsing, security, pagination, scheduling, telemetry helpers |
|
|
@@ -322,20 +325,23 @@ When you complete a skill's checklist, check the boxes and add a completion time
|
|
|
322
325
|
|
|
323
326
|
| Command | Purpose |
|
|
324
327
|
|:--------|:--------|
|
|
325
|
-
| `
|
|
326
|
-
| `
|
|
327
|
-
| `
|
|
328
|
-
| `
|
|
328
|
+
| `bun run build` | Compile TypeScript |
|
|
329
|
+
| `bun run rebuild` | Clean + build |
|
|
330
|
+
| `bun run clean` | Remove build artifacts |
|
|
331
|
+
| `bun run devcheck` | Lint + format + typecheck + security + changelog sync |
|
|
329
332
|
| `bun run audit:refresh` | Delete `bun.lock`, reinstall, and re-run `bun audit`. Use when `devcheck` flags a transitive advisory — Bun's `update` is sticky on transitive resolutions, so the advisory may be a stale-lockfile false positive. If it survives the refresh, it's real. |
|
|
330
|
-
| `
|
|
331
|
-
| `
|
|
332
|
-
| `
|
|
333
|
-
| `
|
|
334
|
-
| `
|
|
335
|
-
| `
|
|
336
|
-
| `
|
|
337
|
-
| `
|
|
338
|
-
| `
|
|
333
|
+
| `bun run lint:mcp` | Run the MCP definition linter standalone (rule catalog: `api-linter` skill) |
|
|
334
|
+
| `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity (run by devcheck) |
|
|
335
|
+
| `bun run list-skills` | Print the skill registry |
|
|
336
|
+
| `bun run tree` | Generate directory structure doc |
|
|
337
|
+
| `bun run format` | Auto-fix formatting (safe fixes only) |
|
|
338
|
+
| `bun run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior |
|
|
339
|
+
| `bun run test` | Run tests (Vitest — use `bun run test`, not `bun test`) |
|
|
340
|
+
| `bun run start:stdio` | Production mode (stdio) |
|
|
341
|
+
| `bun run start:http` | Production mode (HTTP) |
|
|
342
|
+
| `bun run changelog:build` | Regenerate `CHANGELOG.md` from `changelog/*.md` |
|
|
343
|
+
| `bun run changelog:check` | Verify `CHANGELOG.md` is in sync (used by devcheck) |
|
|
344
|
+
| `bun run bundle` | Build, pack, and clean a `.mcpb` for one-click Claude Desktop install |
|
|
339
345
|
|
|
340
346
|
---
|
|
341
347
|
|
|
@@ -359,14 +365,14 @@ Each per-version file opens with YAML frontmatter:
|
|
|
359
365
|
---
|
|
360
366
|
summary: "One-line headline, ≤350 chars" # required — powers the rollup index
|
|
361
367
|
breaking: false # optional — true flags breaking changes
|
|
362
|
-
security: false # optional — true
|
|
368
|
+
security: false # optional — true ONLY for a source-code security fix, never a dependency CVE bump
|
|
363
369
|
---
|
|
364
370
|
|
|
365
371
|
# 0.1.0 — YYYY-MM-DD
|
|
366
372
|
...
|
|
367
373
|
```
|
|
368
374
|
|
|
369
|
-
`breaking: true` renders a `· ⚠️ Breaking` badge — use it when consumers must update code on upgrade (signature changes, removed APIs, config renames). `security: true` renders a `· 🛡️ Security` badge and pairs with a `## Security` body section. When both are set, badges render `· ⚠️ Breaking · 🛡️ Security`.
|
|
375
|
+
`breaking: true` renders a `· ⚠️ Breaking` badge — use it when consumers must update code on upgrade (signature changes, removed APIs, config renames). `security: true` renders a `· 🛡️ Security` badge and pairs with a `## Security` body section — set it only for a security fix in this server's *own source code*, never for a routine dependency or transitive CVE bump (record those under `## Dependencies`). When both are set, badges render `· ⚠️ Breaking · 🛡️ Security`.
|
|
370
376
|
|
|
371
377
|
`agent-notes` is an optional free-form field for maintenance agents processing the release downstream. Content here won't appear in the rendered CHANGELOG — it's consumed by agents running the `maintenance` skill. Use it for adoption instructions that don't fit the human-facing sections: new files to create, fields to populate, one-time migration steps. Omit entirely when there's nothing to say.
|
|
372
378
|
|
package/templates/CLAUDE.md
CHANGED
|
@@ -182,12 +182,14 @@ Handlers receive a unified `ctx` object. Key properties:
|
|
|
182
182
|
| Property | Description |
|
|
183
183
|
|:---------|:------------|
|
|
184
184
|
| `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. |
|
|
185
|
-
| `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.list(prefix, { cursor, limit })`. Accepts any serializable value. |
|
|
185
|
+
| `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.getMany(keys)`, `.list(prefix, { cursor, limit })`. Accepts any serializable value. |
|
|
186
186
|
| `ctx.elicit` | Ask user for structured input — form call `(message, schema)` or `.url(message, url)` for an external link. **Check for presence first:** `if (ctx.elicit) { ... }` |
|
|
187
|
+
| `ctx.enrich` | Success-path agent context (empty-result notices, query echo, pagination totals) — `ctx.enrich(...)` or `.notice()` / `.total()` / `.echo()` / `.truncated()`. Reaches `structuredContent` and `content[]`; lands only when the definition declares an `enrichment` block (no-op otherwise). |
|
|
188
|
+
| `ctx.content` | Non-text content blocks — `.image(data, mimeType)`, `.audio(data, mimeType)`, or `ctx.content(block)` for a raw block. Prepended to `content[]` after `format()`; never enters `structuredContent`. |
|
|
187
189
|
| `ctx.signal` | `AbortSignal` for cancellation. |
|
|
188
190
|
| `ctx.progress` | Task progress (present when `task: true`) — `.setTotal(n)`, `.increment()`, `.update(message)`. |
|
|
189
191
|
| `ctx.requestId` | Unique request ID. |
|
|
190
|
-
| `ctx.tenantId` | Tenant ID from JWT
|
|
192
|
+
| `ctx.tenantId` | Tenant ID from JWT; `'default'` for stdio or HTTP with auth off. |
|
|
191
193
|
|
|
192
194
|
---
|
|
193
195
|
|
|
@@ -195,7 +197,7 @@ Handlers receive a unified `ctx` object. Key properties:
|
|
|
195
197
|
|
|
196
198
|
Handlers throw — the framework catches, classifies, and formats.
|
|
197
199
|
|
|
198
|
-
**Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` / `resource()` to receive `ctx.fail(reason, …)` typed against the reason union. TypeScript catches typos at compile time, `data.reason` is auto-populated for observability, linter enforces conformance against the handler body. `recovery` is required
|
|
200
|
+
**Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` / `resource()` to receive `ctx.fail(reason, …)` typed against the reason union. TypeScript catches typos at compile time, `data.reason` is auto-populated for observability, linter enforces conformance against the handler body. `recovery` is required (≥ 5 words, lint-validated) — the single source of truth for the agent's next move. Pass `ctx.recoveryFor('reason')` as the throw's data to put it on the wire (`data.recovery.hint`, mirrored into `content[]` text); override with an explicit `{ recovery: { hint: '...' } }` when dynamic runtime context matters. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`) bubble freely and don't need declaring.
|
|
199
201
|
|
|
200
202
|
```ts
|
|
201
203
|
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
@@ -207,7 +209,7 @@ errors: [
|
|
|
207
209
|
],
|
|
208
210
|
async handler(input, ctx) {
|
|
209
211
|
const item = await db.find(input.id);
|
|
210
|
-
if (!item) throw ctx.fail('no_match', `No item ${input.id}
|
|
212
|
+
if (!item) throw ctx.fail('no_match', `No item ${input.id}`, ctx.recoveryFor('no_match'));
|
|
211
213
|
return item;
|
|
212
214
|
}
|
|
213
215
|
```
|
|
@@ -270,7 +272,7 @@ src/
|
|
|
270
272
|
|
|
271
273
|
## Skills
|
|
272
274
|
|
|
273
|
-
Skills are modular instructions in `skills/` at the project root. Read them directly when a task matches — e.g., `skills/add-tool/SKILL.md` when adding a tool.
|
|
275
|
+
Skills are modular instructions in `skills/` at the project root. Read them directly when a task matches — e.g., `skills/add-tool/SKILL.md` when adding a tool. `bun run list-skills` prints the full registry.
|
|
274
276
|
|
|
275
277
|
**Agent skill directory:** Copy skills into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent). Skills then load as context without referencing `skills/` paths. After framework updates, run the `maintenance` skill — Phase B re-syncs the agent directory.
|
|
276
278
|
|
|
@@ -290,7 +292,6 @@ Available skills:
|
|
|
290
292
|
| `tool-defs-analysis` | Read-only audit of MCP definition language across the surface — voice, leaks, defaults, recovery hints, output descriptions |
|
|
291
293
|
| `security-pass` | Audit server for MCP-flavored security gaps: output injection, scope blast radius, input sinks, tenant isolation |
|
|
292
294
|
| `code-simplifier` | Post-session cleanup against `git diff` — modernize syntax, consolidate duplication, align with the codebase |
|
|
293
|
-
| `devcheck` | Lint, format, typecheck, audit |
|
|
294
295
|
| `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
|
|
295
296
|
| `git-wrapup` | Land working-tree changes as a versioned commit + annotated tag — version bump, changelog, verify, tag. Local only. |
|
|
296
297
|
| `release-and-publish` | Push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |
|
|
@@ -298,12 +299,14 @@ Available skills:
|
|
|
298
299
|
| `orchestrations` | Chain task skills into a gated multi-phase pipeline — build-out, QA-fix, update-ship — when you can spawn sub-agents |
|
|
299
300
|
| `report-issue-framework` | File a bug or feature request against `@cyanheads/mcp-ts-core` via `gh` CLI |
|
|
300
301
|
| `report-issue-local` | File a bug or feature request against this server's own repo via `gh` CLI |
|
|
302
|
+
| `techniques` | Catalog of response/data-shaping techniques — overflow handling, payload shaping, retrieval patterns |
|
|
301
303
|
| `api-auth` | Auth modes, scopes, JWT/OAuth |
|
|
302
304
|
| `api-canvas` | DataCanvas: register tabular data, run SQL, export, plus the `spillover()` helper for big result sets — Tier 3 opt-in |
|
|
303
305
|
| `api-config` | AppConfig, parseConfig, env vars |
|
|
304
306
|
| `api-context` | Context interface, logger, state, progress |
|
|
305
307
|
| `api-errors` | McpError, JsonRpcErrorCode, error patterns |
|
|
306
308
|
| `api-linter` | Definition linter rule catalog — invoked by `bun run lint:mcp` and `devcheck` |
|
|
309
|
+
| `api-mirror` | MirrorService: persistent self-refreshing local mirror (embedded SQLite + FTS5) of a bulk upstream dataset — Tier 3 opt-in |
|
|
307
310
|
| `api-services` | LLM, Speech, Graph services |
|
|
308
311
|
| `api-testing` | createMockContext, test patterns |
|
|
309
312
|
| `api-utils` | Formatting, parsing, security, pagination, scheduling, telemetry helpers |
|
|
@@ -322,20 +325,23 @@ When you complete a skill's checklist, check the boxes and add a completion time
|
|
|
322
325
|
|
|
323
326
|
| Command | Purpose |
|
|
324
327
|
|:--------|:--------|
|
|
325
|
-
| `
|
|
326
|
-
| `
|
|
327
|
-
| `
|
|
328
|
-
| `
|
|
328
|
+
| `bun run build` | Compile TypeScript |
|
|
329
|
+
| `bun run rebuild` | Clean + build |
|
|
330
|
+
| `bun run clean` | Remove build artifacts |
|
|
331
|
+
| `bun run devcheck` | Lint + format + typecheck + security + changelog sync |
|
|
329
332
|
| `bun run audit:refresh` | Delete `bun.lock`, reinstall, and re-run `bun audit`. Use when `devcheck` flags a transitive advisory — Bun's `update` is sticky on transitive resolutions, so the advisory may be a stale-lockfile false positive. If it survives the refresh, it's real. |
|
|
330
|
-
| `
|
|
331
|
-
| `
|
|
332
|
-
| `
|
|
333
|
-
| `
|
|
334
|
-
| `
|
|
335
|
-
| `
|
|
336
|
-
| `
|
|
337
|
-
| `
|
|
338
|
-
| `
|
|
333
|
+
| `bun run lint:mcp` | Run the MCP definition linter standalone (rule catalog: `api-linter` skill) |
|
|
334
|
+
| `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity (run by devcheck) |
|
|
335
|
+
| `bun run list-skills` | Print the skill registry |
|
|
336
|
+
| `bun run tree` | Generate directory structure doc |
|
|
337
|
+
| `bun run format` | Auto-fix formatting (safe fixes only) |
|
|
338
|
+
| `bun run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior |
|
|
339
|
+
| `bun run test` | Run tests (Vitest — use `bun run test`, not `bun test`) |
|
|
340
|
+
| `bun run start:stdio` | Production mode (stdio) |
|
|
341
|
+
| `bun run start:http` | Production mode (HTTP) |
|
|
342
|
+
| `bun run changelog:build` | Regenerate `CHANGELOG.md` from `changelog/*.md` |
|
|
343
|
+
| `bun run changelog:check` | Verify `CHANGELOG.md` is in sync (used by devcheck) |
|
|
344
|
+
| `bun run bundle` | Build, pack, and clean a `.mcpb` for one-click Claude Desktop install |
|
|
339
345
|
|
|
340
346
|
---
|
|
341
347
|
|
|
@@ -359,14 +365,14 @@ Each per-version file opens with YAML frontmatter:
|
|
|
359
365
|
---
|
|
360
366
|
summary: "One-line headline, ≤350 chars" # required — powers the rollup index
|
|
361
367
|
breaking: false # optional — true flags breaking changes
|
|
362
|
-
security: false # optional — true
|
|
368
|
+
security: false # optional — true ONLY for a source-code security fix, never a dependency CVE bump
|
|
363
369
|
---
|
|
364
370
|
|
|
365
371
|
# 0.1.0 — YYYY-MM-DD
|
|
366
372
|
...
|
|
367
373
|
```
|
|
368
374
|
|
|
369
|
-
`breaking: true` renders a `· ⚠️ Breaking` badge — use it when consumers must update code on upgrade (signature changes, removed APIs, config renames). `security: true` renders a `· 🛡️ Security` badge and pairs with a `## Security` body section. When both are set, badges render `· ⚠️ Breaking · 🛡️ Security`.
|
|
375
|
+
`breaking: true` renders a `· ⚠️ Breaking` badge — use it when consumers must update code on upgrade (signature changes, removed APIs, config renames). `security: true` renders a `· 🛡️ Security` badge and pairs with a `## Security` body section — set it only for a security fix in this server's *own source code*, never for a routine dependency or transitive CVE bump (record those under `## Dependencies`). When both are set, badges render `· ⚠️ Breaking · 🛡️ Security`.
|
|
370
376
|
|
|
371
377
|
`agent-notes` is an optional free-form field for maintenance agents processing the release downstream. Content here won't appear in the rendered CHANGELOG — it's consumed by agents running the `maintenance` skill. Use it for adoption instructions that don't fit the human-facing sections: new files to create, fields to populate, one-time migration steps. Omit entirely when there's nothing to say.
|
|
372
378
|
|