@cyanheads/mcp-ts-core 0.10.13 → 0.10.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -1
- package/CLAUDE.md +1 -1
- package/README.md +10 -6
- package/biome.json +1 -1
- package/changelog/0.10.x/0.10.13.md +2 -2
- package/changelog/0.10.x/0.10.14.md +22 -0
- package/changelog/0.10.x/0.10.15.md +57 -0
- package/dist/linter/rules/definition-rules.d.ts +23 -0
- package/dist/linter/rules/definition-rules.d.ts.map +1 -0
- package/dist/linter/rules/definition-rules.js +35 -0
- package/dist/linter/rules/definition-rules.js.map +1 -0
- package/dist/linter/rules/enrichment-rules.d.ts +2 -2
- package/dist/linter/rules/enrichment-rules.d.ts.map +1 -1
- package/dist/linter/rules/enrichment-rules.js +5 -0
- package/dist/linter/rules/enrichment-rules.js.map +1 -1
- package/dist/linter/rules/handler-body-rules.d.ts +1 -1
- package/dist/linter/rules/handler-body-rules.d.ts.map +1 -1
- package/dist/linter/rules/handler-body-rules.js +3 -0
- package/dist/linter/rules/handler-body-rules.js.map +1 -1
- package/dist/linter/rules/prompt-rules.d.ts.map +1 -1
- package/dist/linter/rules/prompt-rules.js +3 -0
- package/dist/linter/rules/prompt-rules.js.map +1 -1
- package/dist/linter/rules/resource-rules.d.ts.map +1 -1
- package/dist/linter/rules/resource-rules.js +3 -0
- package/dist/linter/rules/resource-rules.js.map +1 -1
- package/dist/linter/rules/tool-rules.d.ts.map +1 -1
- package/dist/linter/rules/tool-rules.js +3 -0
- package/dist/linter/rules/tool-rules.js.map +1 -1
- package/dist/linter/validate.d.ts.map +1 -1
- package/dist/linter/validate.js +16 -1
- package/dist/linter/validate.js.map +1 -1
- package/dist/logs/combined.log +8 -0
- package/dist/logs/error.log +4 -0
- package/dist/logs/interactions.log +0 -0
- package/dist/services/canvas/providers/duckdb/DuckdbProvider.d.ts.map +1 -1
- package/dist/services/canvas/providers/duckdb/DuckdbProvider.js +17 -2
- package/dist/services/canvas/providers/duckdb/DuckdbProvider.js.map +1 -1
- package/dist/utils/network/fetchWithTimeout.d.ts +14 -1
- package/dist/utils/network/fetchWithTimeout.d.ts.map +1 -1
- package/dist/utils/network/fetchWithTimeout.js +28 -5
- package/dist/utils/network/fetchWithTimeout.js.map +1 -1
- package/dist/utils/network/httpError.d.ts +4 -1
- package/dist/utils/network/httpError.d.ts.map +1 -1
- package/dist/utils/network/httpError.js +6 -0
- package/dist/utils/network/httpError.js.map +1 -1
- package/dist/utils/network/retry.d.ts +12 -0
- package/dist/utils/network/retry.d.ts.map +1 -1
- package/dist/utils/network/retry.js +58 -3
- package/dist/utils/network/retry.js.map +1 -1
- package/dist/utils/overflow/outlineOnOverflow.d.ts +24 -11
- package/dist/utils/overflow/outlineOnOverflow.d.ts.map +1 -1
- package/dist/utils/overflow/outlineOnOverflow.js +24 -11
- package/dist/utils/overflow/outlineOnOverflow.js.map +1 -1
- package/package.json +25 -25
- package/skills/add-tool/SKILL.md +2 -2
- package/skills/api-canvas/SKILL.md +2 -2
- package/skills/api-linter/SKILL.md +14 -1
- package/skills/api-utils/SKILL.md +4 -4
- package/skills/design-mcp-server/SKILL.md +2 -2
- package/skills/field-test/SKILL.md +5 -4
- package/skills/git-wrapup/SKILL.md +12 -24
- package/skills/orchestrations/SKILL.md +2 -2
- package/skills/orchestrations/workflows/field-test-fix.md +1 -1
- package/skills/orchestrations/workflows/fix-wrapup-release.md +2 -2
- package/skills/orchestrations/workflows/maintenance-release.md +2 -2
- package/skills/release-and-publish/SKILL.md +2 -2
- package/skills/techniques/SKILL.md +2 -2
- package/skills/techniques/references/outline-on-overflow.md +15 -9
- package/skills/tool-defs-analysis/SKILL.md +63 -22
- package/templates/Dockerfile +1 -1
- package/templates/package.json +2 -2
|
@@ -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.7"
|
|
8
8
|
audience: external
|
|
9
9
|
type: workflow
|
|
10
10
|
---
|
|
@@ -189,7 +189,7 @@ Sub-agent self-reports describe intent, not always reality. After every phase th
|
|
|
189
189
|
- **GitHub** — `gh repo view --json visibility`, `gh release view v<VERSION>`, `gh issue list`, `gh issue view <N> --comments` to confirm the fix comment landed
|
|
190
190
|
- **npm / registries** — `npm view <pkg>@<version>`, registry-specific checks
|
|
191
191
|
- **Build state** — re-run `bun run devcheck` if the previous phase was supposed to land green
|
|
192
|
-
- **Quality** — tag annotation
|
|
192
|
+
- **Quality** — tag annotation is a headline digest covering every change (flat bullets — notable ones named, minor ones in one grouped bullet; no changelog section headers, deps ≤1 line, no gates line), subject omits the version number, no marketing adjectives, issue backlinks where applicable, changelog link as final line
|
|
193
193
|
|
|
194
194
|
If verification disagrees with the sub-agent's report, that's the signal to re-spawn with the actual state and the unmet goal in the prompt — not to trust the report. The goal hasn't changed; only the path needs to.
|
|
195
195
|
|
|
@@ -137,7 +137,7 @@ Each sub-agent reads both `skills/git-wrapup/SKILL.md` and `skills/release-and-p
|
|
|
137
137
|
4. Release commit on top — version bump + changelog + regenerated artifacts as `chore(release): v<version>`
|
|
138
138
|
5. Tag the release commit
|
|
139
139
|
|
|
140
|
-
The tag annotation
|
|
140
|
+
The changelog carries the depth; the tag annotation covers every change at headline granularity — notable ones named, minor ones in one grouped bullet (per git-wrapup step 8). The commit split is about git history, not release notes.
|
|
141
141
|
|
|
142
142
|
**Version bump.** Default **patch** for field-test fix releases. **Minor** when enhancements are bundled in.
|
|
143
143
|
|
|
@@ -123,7 +123,7 @@ Each sub-agent reads BOTH `skills/git-wrapup/SKILL.md` AND `skills/release-and-p
|
|
|
123
123
|
4. Release commit on top: `chore(release): v<version>` — version bump + changelog + regenerated artifacts
|
|
124
124
|
5. Tag the release commit
|
|
125
125
|
|
|
126
|
-
The tag annotation
|
|
126
|
+
The changelog carries the depth; the tag annotation covers every change at headline granularity — notable ones named, minor ones in one grouped bullet (per git-wrapup step 8). The commit split is about git history, not release notes.
|
|
127
127
|
|
|
128
128
|
**Version bump.** Default **patch** for bug-fix releases. **Minor** when enhancements are included.
|
|
129
129
|
|
|
@@ -134,7 +134,7 @@ The tag annotation and changelog cover ALL fixes — the commit split is about g
|
|
|
134
134
|
| Private / in-development | Version bump → changelog → commit → tag → mcpb bundle → push → `gh release create`. Skip `bun publish`, Docker, MCP Registry. |
|
|
135
135
|
| Public / launched | Full `release-and-publish`: push + `bun publish` + `publish-mcp` + bundle + GH release + Docker (if Dockerfile). |
|
|
136
136
|
|
|
137
|
-
**Tag annotations** are for end users — internal
|
|
137
|
+
**Tag annotations** are for end users — every changelog-worthy change stays visible in the tag, with minor/internal items (build config, repo hygiene, metadata) grouped into ONE compact bullet; only non-changelog churn (lockfile refreshes, lint fixes) stays in commit bodies alone.
|
|
138
138
|
|
|
139
139
|
### Phase 4: Issue cleanup
|
|
140
140
|
Close issues that shipped — only those. Skipped issues stay open.
|
|
@@ -119,7 +119,7 @@ If a target's diff suggests minor-or-above, **pause that target and surface to t
|
|
|
119
119
|
### Phase 4: Wrap-up + release
|
|
120
120
|
Each sub-agent reads BOTH `skills/git-wrapup/SKILL.md` AND `skills/release-and-publish/SKILL.md`. Runs wrap-up (version bump, changelog authoring, commit, annotated tag), then release (push, npm publish, MCP Registry, GH release, Docker).
|
|
121
121
|
|
|
122
|
-
**Framework changelog reading.** When `mcp-ts-core` was updated, the sub-agent must read the framework's changelog files for the version delta (e.g. `node_modules/@cyanheads/mcp-ts-core/changelog/0.9.x/0.9.2.md` through `0.9.6.md`) and distill user-facing changes relevant to this server into the changelog entry
|
|
122
|
+
**Framework changelog reading.** When `mcp-ts-core` was updated, the sub-agent must read the framework's changelog files for the version delta (e.g. `node_modules/@cyanheads/mcp-ts-core/changelog/0.9.x/0.9.2.md` through `0.9.6.md`) and distill user-facing changes relevant to this server into the changelog entry; the tag annotation carries at most a one-line framework mention with the version arrow. "Picks up upstream fixes" is not acceptable in the changelog — name what changed.
|
|
123
123
|
|
|
124
124
|
**Wrap-up scope.** Determined by repo visibility (`gh repo view --json visibility`):
|
|
125
125
|
|
|
@@ -135,7 +135,7 @@ Each sub-agent reads BOTH `skills/git-wrapup/SKILL.md` AND `skills/release-and-p
|
|
|
135
135
|
|
|
136
136
|
**Commit structure.** Group the work by concern, then land the release artifacts (version bumps + changelog + regenerated `docs/tree.md`/`server.json`/`manifest.json`) as a `chore(release): <version> — <theme>` commit on top — same model as `git-wrapup` Step 7. A single-concern pass (just dep updates, or one framework adoption) is one work commit plus the release commit; a pass spanning multiple distinct concerns splits into per-concern work commits with the release commit last. Regenerated meta-drift is release-artifact-shaped — it rides in the release commit, never carved out as its own.
|
|
137
137
|
|
|
138
|
-
**Tag annotations are for end users.**
|
|
138
|
+
**Tag annotations are for end users.** Every changelog-worthy change stays visible in the tag, with minor/internal items (build config, repo hygiene, metadata) grouped into ONE compact bullet; only non-changelog churn (lockfile refreshes, lint fixes) stays in commit bodies alone.
|
|
139
139
|
|
|
140
140
|
**Tag-moving protocol.** If post-version doc changes land after the version commit, move the tag to HEAD: delete remote release, delete remote + local tag, recreate tag at new HEAD with same annotation, re-push, recreate release with `.mcpb`. Authorized within the workflow — same-day forward move.
|
|
141
141
|
|
|
@@ -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.11"
|
|
8
8
|
audience: external
|
|
9
9
|
type: workflow
|
|
10
10
|
---
|
|
@@ -202,7 +202,7 @@ Confirm each published artifact is actually live — don't rely on a successful
|
|
|
202
202
|
- **npm**: `npm view <package.json#name>@<version> version` — must return the version string
|
|
203
203
|
- **MCP Registry**: `curl -s "https://registry.modelcontextprotocol.io/v0.1/servers/<mcpName>/versions/<version>"` — must return HTTP 200 with `server.version` matching `<version>` (`mcpName` is the `name` field from `server.json`; URL-encode `/` as `%2F`). The search endpoint (`/v0.1/servers?search=`) paginates and may not include the latest version for packages with many releases — always use the direct version lookup.
|
|
204
204
|
- **GitHub Release**: `gh release view v<VERSION> -R <OWNER>/<REPO> --json assets --jq '.assets[].name'` — must list the `.mcpb` file
|
|
205
|
-
- **GHCR**:
|
|
205
|
+
- **GHCR**: `docker manifest inspect ghcr.io/<OWNER>/<REPO>:<VERSION>` — must exit 0 (resolves multi-arch OCI indexes directly with the correct media types; exits non-zero when the tag is genuinely absent)
|
|
206
206
|
|
|
207
207
|
If any check fails, halt and report which destination is unreachable. A successful `docker push` or `bun publish` exit code does not guarantee the artifact is queryable — registry propagation delays, auth scoping, and partial failures all exist.
|
|
208
208
|
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Catalog of reusable response- and data-shaping techniques for MCP servers built on `@cyanheads/mcp-ts-core` — overflow handling, payload shaping, retrieval patterns. Use when a tool's payload is too large, awkwardly shaped, or expensive to retrieve and you want a proven pattern instead of inventing one. Each technique has a self-contained reference under `references/`.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "0.
|
|
7
|
+
version: "0.2"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -13,7 +13,7 @@ metadata:
|
|
|
13
13
|
|
|
14
14
|
A directory of cross-cutting techniques for shaping what a handler returns and how a client retrieves it — patterns that don't belong to a single API surface. Each entry is a self-contained reference under `references/`: the problem it solves, when to reach for it (and when not to), and how to apply it with current framework primitives.
|
|
15
15
|
|
|
16
|
-
These are **patterns, not new primitives** — they compose `tool()`,
|
|
16
|
+
These are **patterns, not new primitives** — they compose `tool()`, `output`/`format()` shaping, `ctx.state`, and the existing helpers. Where a technique has (or will have) a dedicated helper, its reference says so and links the tracking issue.
|
|
17
17
|
|
|
18
18
|
## Techniques
|
|
19
19
|
|
|
@@ -20,9 +20,9 @@ This is distinct from the other two overflow shapes:
|
|
|
20
20
|
|
|
21
21
|
**Never truncate to fit a budget.** When a payload is too big, return a complete, honest outline of what's available plus how to retrieve it — identically on `content[]` and `structuredContent`.
|
|
22
22
|
|
|
23
|
-
## The shape — a
|
|
23
|
+
## The shape — a flat `output` with a `kind` discriminator
|
|
24
24
|
|
|
25
|
-
The outline is the payload the agent acts on, so it lands in the **main body** (`structuredContent` + `content[]`), as a variant of the tool's own `output`. Not the enrichment block — enrichment is *additive* (`output.extend(...)` merged after `output.parse(result)`), so it can add fields to the fat document but never replace it. Not a post-hoc framework swap either — that would emit a `structuredContent` shape the advertised `outputSchema` (`tools/list`) doesn't describe. A
|
|
25
|
+
The outline is the payload the agent acts on, so it lands in the **main body** (`structuredContent` + `content[]`), as a variant of the tool's own `output`. Not the enrichment block — enrichment is *additive* (`output.extend(...)` merged after `output.parse(result)`), so it can add fields to the fat document but never replace it. Not a post-hoc framework swap either — that would emit a `structuredContent` shape the advertised `outputSchema` (`tools/list`) doesn't describe. A single `output` object carrying both modes — a `kind` discriminator plus presence-based optional arms — is the placement that replaces the payload, is advertised honestly, and holds `format()`-parity. (`tool()` requires `output` to be a `ZodObject`: the `schema-is-object` lint rule and the enrichment `.extend()` are both object-only, so a `z.discriminatedUnion` output is rejected — model the two modes as optional arms of one object, not union branches.)
|
|
26
26
|
|
|
27
27
|
```ts
|
|
28
28
|
import { tool, z } from '@cyanheads/mcp-ts-core';
|
|
@@ -44,11 +44,17 @@ export const getLabel = tool('get_label', {
|
|
|
44
44
|
.optional()
|
|
45
45
|
.describe('Sections to return. Omit for the full label (or an outline if it overflows).'),
|
|
46
46
|
}),
|
|
47
|
-
output: z.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
output: z.object({
|
|
48
|
+
kind: z.enum(['full', 'outline']),
|
|
49
|
+
...FullLabel.partial().shape, // full arm — every field optional
|
|
50
|
+
sections: OUTLINE_VARIANT.shape.sections.optional(), // outline arm
|
|
51
|
+
notice: OUTLINE_VARIANT.shape.notice.optional(),
|
|
52
|
+
}),
|
|
53
|
+
// Render each arm on field presence, independently — never branch on `kind` (see below).
|
|
54
|
+
format: (r) => [
|
|
55
|
+
...(r.id ? renderLabel(r) : []), // full arm — key on a full-only field
|
|
56
|
+
...(r.sections ? formatOutline({ kind: 'outline', sections: r.sections, notice: r.notice ?? '' }) : []),
|
|
57
|
+
],
|
|
52
58
|
async handler(input) {
|
|
53
59
|
const doc = await fetchLabel(input.query); // deterministic from query
|
|
54
60
|
if (input.sections?.length) {
|
|
@@ -60,7 +66,7 @@ export const getLabel = tool('get_label', {
|
|
|
60
66
|
});
|
|
61
67
|
```
|
|
62
68
|
|
|
63
|
-
`format()`-parity
|
|
69
|
+
`format()`-parity holds because every terminal field in `output` must appear in the rendered text. With a flat object the linter builds **one** synthetic sample with every optional field populated at once, so render each arm on field presence, independently — a mutually-exclusive `if (kind === 'outline') … else …` renders only one arm against that all-fields sample and fails parity for the other. `formatOutline` is the shipped renderer for the `outline` arm; you supply the `full` renderer. That keeps the two client surfaces in lockstep.
|
|
64
70
|
|
|
65
71
|
## The helper
|
|
66
72
|
|
|
@@ -69,7 +75,7 @@ export const getLabel = tool('get_label', {
|
|
|
69
75
|
| Export | Purpose |
|
|
70
76
|
|:--|:--|
|
|
71
77
|
| `outlineOnOverflow(doc, options?)` | Returns `{ kind: 'full', ...doc }` under budget (or with `< 2` sections), else `{ kind: 'outline', sections, notice }`. |
|
|
72
|
-
| `OUTLINE_VARIANT` | The reusable `outline`-arm Zod schema
|
|
78
|
+
| `OUTLINE_VARIANT` | The reusable `outline`-arm Zod schema; fold `.shape.sections` / `.shape.notice` into your flat `output` object as optional arms. |
|
|
73
79
|
| `selectSections(doc, want, { alwaysKeep })` | Projects the document to requested keys plus always-kept metadata. The selection-path counterpart. |
|
|
74
80
|
| `formatOutline(outline)` | Renders the outline to `content[]` for `format()`. |
|
|
75
81
|
| `DEFAULT_OUTLINE_BUDGET_BYTES` | The default budget (`24_000`) when `options.budget` is omitted. |
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: tool-defs-analysis
|
|
3
3
|
description: >
|
|
4
|
-
Read-only audit of MCP definition language across an existing surface — tools, resources, prompts. Walks every definition file and checks
|
|
4
|
+
Read-only audit of MCP definition language across an existing surface — tools, resources, prompts, server instructions. Walks every definition file and checks 15 categories the LLM reads to decide whether and how to call: voice & tense, internal leaks, audience leaks, defaults, recovery hints, field descriptions, cross-references, sparsity, examples, structure, mutator observability, unit-bearing numeric names, validator-enforced constraints, annotations truthfulness, single-line strings — then a cross-surface pass: naming taxonomy, parameter vocabulary, tool overlap, instructions drift, length outliers. Produces grouped findings with file:line citations and a numbered options list. Use during polish, after a refactor, or before a release. Complements `field-test` (behavior testing) and `security-pass` (security audit).
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.4"
|
|
8
8
|
audience: external
|
|
9
9
|
type: audit
|
|
10
10
|
---
|
|
@@ -22,7 +22,7 @@ This skill is the **review-time pass** for that drift. Read each definition the
|
|
|
22
22
|
| `security-pass` | Injection, scopes, input sinks |
|
|
23
23
|
| `tool-defs-analysis` (this) | LLM-facing language across the existing surface |
|
|
24
24
|
|
|
25
|
-
`field-test` already audits descriptions for implementation leaks, meta-coaching, and consumer-aware phrasing during its catalog step — that's a fast shallow pass alongside live tool calls. This skill is the deeper review:
|
|
25
|
+
`field-test` already audits descriptions for implementation leaks, meta-coaching, and consumer-aware phrasing during its catalog step — that's a fast shallow pass alongside live tool calls. This skill is the deeper review: 15 categories, every field, every recovery hint, every default value, with file:line citations — plus a cross-surface pass for the drift no single file shows.
|
|
26
26
|
|
|
27
27
|
**Read-only.** This skill produces a report; the maintainer applies fixes. While running it, do not run git, do not stage or commit, do not update the changelog, do not run `devcheck`, do not invoke wrapup or release workflows. Fixes flow through the normal authoring path (edit the definition, then re-run this skill if you want to verify).
|
|
28
28
|
|
|
@@ -53,11 +53,11 @@ find src/mcp-server/resources/definitions -type f -name "*resource.ts" 2>/dev/nu
|
|
|
53
53
|
find src/mcp-server/prompts/definitions -type f -name "*.prompt.ts" 2>/dev/null | sort
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
The `*tool.ts` / `*resource.ts` patterns also catch `*.app-tool.ts` / `*.app-resource.ts`. If the server's definitions live elsewhere (`examples/`, a packages workspace, …), audit those paths too.
|
|
56
|
+
The `*tool.ts` / `*resource.ts` patterns also catch `*.app-tool.ts` / `*.app-resource.ts`. If the server's definitions live elsewhere (`examples/`, a packages workspace, …), audit those paths too. Also locate the server-level `instructions` string if the server sets one (the `createApp` option — `grep -rn "instructions" src/ --include="*.ts"`); it's audited in the cross-surface pass.
|
|
57
57
|
|
|
58
58
|
Use `TaskCreate` — one task per file. Mark each complete after its findings are captured.
|
|
59
59
|
|
|
60
|
-
### 2. Walk the
|
|
60
|
+
### 2. Walk the 15 categories per file
|
|
61
61
|
|
|
62
62
|
Read each definition file in full. Apply every category — most files trip more than one. Capture each hit with `file:line`, the offending excerpt, and a one-line fix.
|
|
63
63
|
|
|
@@ -79,8 +79,6 @@ Read each definition file in full. Apply every category — most files trip more
|
|
|
79
79
|
|
|
80
80
|
**Smell:** "/api/v2/by-state", "Adds a second API call", "API requires `two_year_period`", "(deprecated; use bar_v2)", "TODO: support batch mode", "Used internally by FooService".
|
|
81
81
|
|
|
82
|
-
Prior art: #25.
|
|
83
|
-
|
|
84
82
|
#### 3. Audience leaks
|
|
85
83
|
|
|
86
84
|
**Look in:** every `description` and `.describe()`.
|
|
@@ -89,7 +87,7 @@ Prior art: #25.
|
|
|
89
87
|
|
|
90
88
|
**Smell:** "suitable for LLM consumption", "Treat the returned ID as the canonical Y", "Agents should…", "Callers should…", "When you call this tool…", any reference to "LLM", "agent", "Claude", "the model".
|
|
91
89
|
|
|
92
|
-
|
|
90
|
+
Field-test catches this in its leak audit; this skill is the more thorough pass.
|
|
93
91
|
|
|
94
92
|
#### 4. Defaults
|
|
95
93
|
|
|
@@ -107,19 +105,21 @@ Prior art: #74. Field-test catches this in its leak audit; this skill is the mor
|
|
|
107
105
|
|
|
108
106
|
**Smell:** "Check the logs", "See documentation", "Contact admin", "Try again later" (with no condition), generic non-actionable text, hints that name internal classes or files.
|
|
109
107
|
|
|
110
|
-
#### 6.
|
|
108
|
+
#### 6. Field descriptions
|
|
111
109
|
|
|
112
|
-
**Look in:** every field
|
|
110
|
+
**Look in:** every field in `input` and `output` schemas; resource URI template variables.
|
|
113
111
|
|
|
114
|
-
**Check:**
|
|
112
|
+
**Check:** every field carries a `.describe()`, and it tells the agent what the *value* is — not just the field name restated, not silent on dynamic shapes. Enum variants — especially operation discriminators — are explained.
|
|
115
113
|
|
|
116
114
|
**Smell:**
|
|
117
115
|
|
|
116
|
+
- An input field with no `.describe()` at all
|
|
118
117
|
- `name: z.string().describe('Name')` — tautology
|
|
119
|
-
- `
|
|
118
|
+
- `operation: z.enum([...])` whose variants are never explained
|
|
120
119
|
- `metadata: z.record(z.string(), z.unknown()).describe('Metadata')` — opaque dynamic shape with no hint about keys/values
|
|
121
120
|
- Optional fields with no note on when they're absent
|
|
122
|
-
-
|
|
121
|
+
- Paging fields (`total`, `hasMore`, `nextCursor`) with semantics unstated — or a `limit` param that doesn't say whether it caps the page or the whole result
|
|
122
|
+
- A URI template variable (`{cid}`) never described anywhere
|
|
123
123
|
|
|
124
124
|
#### 7. Cross-references
|
|
125
125
|
|
|
@@ -157,8 +157,6 @@ Prior art: #74. Field-test catches this in its leak audit; this skill is the mor
|
|
|
157
157
|
|
|
158
158
|
**Smell:** blank lines (`\n\n`) inside a description string, `- bullet` lines, `## Header` lines, "Operations:\n- foo: …" duplicating an enum's `.describe()` text.
|
|
159
159
|
|
|
160
|
-
Prior art: #33.
|
|
161
|
-
|
|
162
160
|
#### 11. Mutator observability
|
|
163
161
|
|
|
164
162
|
**Look in:** mutator tools — any tool that writes, updates, deletes, appends, or patches (i.e., definitions without `annotations.readOnlyHint: true`).
|
|
@@ -171,11 +169,51 @@ Prior art: #33.
|
|
|
171
169
|
|
|
172
170
|
**Look in:** every `z.number()` field in `output` schemas.
|
|
173
171
|
|
|
174
|
-
**Check:** the field name carries a unit when not pinned by context — `sizeInBytes`, `durationInMs`, `priceInCents`, `latencyInMs`. The `.describe()` drops in summarization or gets truncated; the field name persists into the JSON the agent reads.
|
|
172
|
+
**Check:** the field name carries a unit when not pinned by context — `sizeInBytes`, `durationInMs`, `priceInCents`, `latencyInMs`. The `.describe()` drops in summarization or gets truncated; the field name persists into the JSON the agent reads. Scores, ratios, and percentages carry their range the same way — in the name or as the first thing in the describe (`0–1`).
|
|
173
|
+
|
|
174
|
+
**Smell:** `size`, `duration`, `price`, `latency` — bare names that force the agent to guess units; `score`/`confidence` with no stated range (0.87 and 87 both pass the schema). Exempt: `index`, `position`, `page`, `offset`, `limit`, `totalCount`, `itemCount` (dimensionless).
|
|
175
|
+
|
|
176
|
+
#### 13. Constraints in validators
|
|
177
|
+
|
|
178
|
+
**Look in:** input schemas — every field whose `.describe()` states a format, range, length, or pattern.
|
|
179
|
+
|
|
180
|
+
**Check:** stated constraints are machine-enforced in the schema (`.regex()`, `.min()`/`.max()`, `.int()`, `.length()`, an enum) so they emit into the JSON Schema the client renders — a constraint living only in prose reaches a weaker model unreliably and burns retries on malformed input. Opaque-ID params also say how to *obtain* the value (which sibling tool returns it), not just its shape.
|
|
181
|
+
|
|
182
|
+
**Smell:** `.describe('Date in YYYY-MM-DD format')` on a bare `z.string()`; "max 100" in prose with no `.max(100)`; an ID param whose describe gives the format but never the tool that produces it.
|
|
183
|
+
|
|
184
|
+
#### 14. Annotations truthfulness
|
|
185
|
+
|
|
186
|
+
**Look in:** the `annotations` block on every tool.
|
|
187
|
+
|
|
188
|
+
**Check:** hints match what the handler actually does — clients gate confirmation prompts and retry policy on them. A purely-read tool carries `readOnlyHint: true`; deletes and overwrites aren't marked `destructiveHint: false`; retry-safe mutators carry `idempotentHint: true`; tools calling external services carry `openWorldHint: true`. If `annotations.title` is set, it still matches the tool's current name and behavior.
|
|
189
|
+
|
|
190
|
+
**Smell:** `readOnlyHint: true` on anything that writes; a read-only tool with no `readOnlyHint` (clients assume it can mutate); `destructiveHint: false` on a delete; a stale `title` surviving a rename.
|
|
175
191
|
|
|
176
|
-
|
|
192
|
+
#### 15. Single-line strings
|
|
177
193
|
|
|
178
|
-
|
|
194
|
+
**Look in:** every `description`, `.describe()`, and error `recovery` / `when` string in a definition file.
|
|
195
|
+
|
|
196
|
+
**Check:** each is a single-line string literal. NEVER split one across lines with `+` concatenation (`'part one ' + 'part two'`), and never line-wrap a description into a `\n`-bearing template literal. The formatter does not break string literals, so a long single-line string passes formatting untouched — the line-width limit is not a reason to concatenate.
|
|
197
|
+
|
|
198
|
+
**Why it's not cosmetic:** `+`-concatenation forces every fragment to hand-carry its boundary whitespace, and a dropped trailing space silently fuses two words in the rendered schema the model reads (`'…table_name. ' + 'Columns…'` renders as `table_name.Columns`). Correct output is byte-identical to the single-line form, so the concatenation buys nothing and adds a class of silent contract corruption.
|
|
199
|
+
|
|
200
|
+
**Smell:** a string literal ending in `' +` or `" +` at end of line; a `description:` value spanning multiple quoted fragments; a multi-line template literal inside a description (also a Structure finding, #10).
|
|
201
|
+
|
|
202
|
+
**Fix:** collapse to one single-line string literal.
|
|
203
|
+
|
|
204
|
+
### 3. Cross-surface pass
|
|
205
|
+
|
|
206
|
+
The per-file walk misses drift that only shows between files. After it, sweep the whole surface:
|
|
207
|
+
|
|
208
|
+
- **Naming taxonomy** — verb prefixes mean one thing each across the surface (`search_` / `find_` / `get_` / `list_` / `lookup_`); the same verb carrying different semantics on different tools is a finding.
|
|
209
|
+
- **Parameter vocabulary** — one name per concept everywhere: `query` vs `q`, `limit` vs `maxResults`, `nctId` vs `nct_id` on sibling tools is a finding.
|
|
210
|
+
- **Tool overlap** — for any pair with adjacent scope, the two descriptions alone must answer "when X vs Y." If an agent can't pick, that's material.
|
|
211
|
+
- **Instructions drift** — if the server sets `instructions`: every tool it names exists, workflow guidance reflects the current surface (new tools that belong in it, renamed or removed ones purged), and nothing contradicts a per-tool description.
|
|
212
|
+
- **Length outliers** — a description several times longer than its siblings (attention drag), or a one-liner that underspecifies (selection risk).
|
|
213
|
+
|
|
214
|
+
Cross-surface findings use the same finding format, cited at the file:line you'd change (the `instructions` string is a citable location).
|
|
215
|
+
|
|
216
|
+
### 4. Report
|
|
179
217
|
|
|
180
218
|
Three sections.
|
|
181
219
|
|
|
@@ -194,6 +232,8 @@ Issue: <one line: what's wrong>
|
|
|
194
232
|
Fix: <one line: what to change to>
|
|
195
233
|
```
|
|
196
234
|
|
|
235
|
+
Excerpts are verbatim copy-paste from the file as read, line numbers from that read — re-verify any finding written from memory before it enters the report.
|
|
236
|
+
|
|
197
237
|
Two-level severity:
|
|
198
238
|
|
|
199
239
|
- **material** — affects agent decisions (will mis-select tool, mis-fill input, mis-handle output, swallow an irrecoverable error)
|
|
@@ -219,10 +259,11 @@ End with:
|
|
|
219
259
|
|
|
220
260
|
- [ ] Scope confirmed (whole server / module / specific files)
|
|
221
261
|
- [ ] Severity floor applied — nits suppressed if user requested
|
|
222
|
-
- [ ] Inventory built — every `*.tool.ts`, `*.app-tool.ts`, `*.resource.ts`, `*.app-resource.ts`, `*.prompt.ts` listed
|
|
223
|
-
- [ ] Each file walked through all
|
|
262
|
+
- [ ] Inventory built — every `*.tool.ts`, `*.app-tool.ts`, `*.resource.ts`, `*.app-resource.ts`, `*.prompt.ts` listed; server `instructions` located if set
|
|
263
|
+
- [ ] Each file walked through all 15 categories (per-file, not 15 separate passes)
|
|
264
|
+
- [ ] Cross-surface pass run — naming taxonomy, parameter vocabulary, tool overlap, instructions drift, length outliers
|
|
224
265
|
- [ ] **Read-only:** no git, no commits, no changelog edits, no `devcheck`, no wrapup invoked during the audit
|
|
225
|
-
- [ ] Findings carry file:line citation, excerpt, issue, fix
|
|
266
|
+
- [ ] Findings carry file:line citation, excerpt, issue, fix — excerpts verbatim, line numbers verified
|
|
226
267
|
- [ ] Report: summary → grouped-by-category findings → numbered options
|
|
227
|
-
- [ ] Options section produced — numbered,
|
|
268
|
+
- [ ] Options section produced — numbered, each single-file-scoped, severity tagged, cherry-pickable
|
|
228
269
|
- [ ] If no findings: summary states "no findings"; Findings and Options sections omitted
|
package/templates/Dockerfile
CHANGED
|
@@ -14,7 +14,7 @@ COPY package.json bun.lock ./
|
|
|
14
14
|
# Install all dependencies (including dev dependencies for building).
|
|
15
15
|
# The BuildKit cache mount persists Bun's global package cache across builds.
|
|
16
16
|
RUN --mount=type=cache,target=/root/.bun/install/cache \
|
|
17
|
-
bun install --frozen-lockfile
|
|
17
|
+
bun install --frozen-lockfile --ignore-scripts
|
|
18
18
|
|
|
19
19
|
# Copy the rest of the source code
|
|
20
20
|
COPY . .
|
package/templates/package.json
CHANGED
|
@@ -64,9 +64,9 @@
|
|
|
64
64
|
"zod": "{{ZOD_VERSION}}"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
|
-
"@biomejs/biome": "2.5.
|
|
67
|
+
"@biomejs/biome": "2.5.4",
|
|
68
68
|
"@socketsecurity/bun-security-scanner": "^1.1.2",
|
|
69
|
-
"@types/node": "26.1.
|
|
69
|
+
"@types/node": "26.1.1",
|
|
70
70
|
"depcheck": "^1.4.7",
|
|
71
71
|
"ignore": "^7.0.5",
|
|
72
72
|
"tsc-alias": "^1.9.0",
|