@cyanheads/mcp-ts-core 0.10.1 → 0.10.3
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 +15 -9
- package/CLAUDE.md +15 -9
- package/README.md +5 -6
- package/changelog/0.10.x/0.10.2.md +35 -0
- package/changelog/0.10.x/0.10.3.md +46 -0
- package/dist/core/app.d.ts +36 -0
- package/dist/core/app.d.ts.map +1 -1
- package/dist/core/app.js +9 -4
- package/dist/core/app.js.map +1 -1
- package/dist/core/context.d.ts +57 -15
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/context.js +12 -1
- package/dist/core/context.js.map +1 -1
- package/dist/core/index.d.ts +5 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/serverManifest.d.ts +18 -0
- package/dist/core/serverManifest.d.ts.map +1 -1
- package/dist/core/serverManifest.js +10 -2
- package/dist/core/serverManifest.js.map +1 -1
- package/dist/linter/rules/enrichment-rules.d.ts +20 -0
- package/dist/linter/rules/enrichment-rules.d.ts.map +1 -1
- package/dist/linter/rules/enrichment-rules.js +74 -9
- package/dist/linter/rules/enrichment-rules.js.map +1 -1
- package/dist/linter/rules/index.d.ts +2 -2
- package/dist/linter/rules/index.d.ts.map +1 -1
- package/dist/linter/rules/index.js +2 -2
- package/dist/linter/rules/index.js.map +1 -1
- package/dist/linter/rules/schema-rules.d.ts +4 -0
- package/dist/linter/rules/schema-rules.d.ts.map +1 -1
- package/dist/linter/rules/schema-rules.js +13 -0
- package/dist/linter/rules/schema-rules.js.map +1 -1
- package/dist/linter/rules/tool-rules.d.ts +12 -0
- package/dist/linter/rules/tool-rules.d.ts.map +1 -1
- package/dist/linter/rules/tool-rules.js +48 -1
- package/dist/linter/rules/tool-rules.js.map +1 -1
- package/dist/linter/types.d.ts +17 -0
- package/dist/linter/types.d.ts.map +1 -1
- package/dist/linter/validate.d.ts.map +1 -1
- package/dist/linter/validate.js +55 -1
- package/dist/linter/validate.js.map +1 -1
- package/dist/logs/combined.log +4 -0
- package/dist/logs/error.log +2 -0
- package/dist/logs/interactions.log +0 -0
- package/dist/mcp-server/notifications.d.ts +4 -4
- package/dist/mcp-server/prompts/prompt-registration.d.ts.map +1 -1
- package/dist/mcp-server/prompts/prompt-registration.js +1 -0
- package/dist/mcp-server/prompts/prompt-registration.js.map +1 -1
- package/dist/mcp-server/prompts/utils/promptDefinition.d.ts +6 -0
- package/dist/mcp-server/prompts/utils/promptDefinition.d.ts.map +1 -1
- package/dist/mcp-server/prompts/utils/promptDefinition.js.map +1 -1
- package/dist/mcp-server/resources/resource-registration.d.ts.map +1 -1
- package/dist/mcp-server/resources/resource-registration.js +3 -0
- package/dist/mcp-server/resources/resource-registration.js.map +1 -1
- package/dist/mcp-server/resources/utils/resourceDefinition.d.ts +9 -0
- package/dist/mcp-server/resources/utils/resourceDefinition.d.ts.map +1 -1
- package/dist/mcp-server/resources/utils/resourceDefinition.js.map +1 -1
- package/dist/mcp-server/resources/utils/resourceHandlerFactory.d.ts +13 -2
- package/dist/mcp-server/resources/utils/resourceHandlerFactory.d.ts.map +1 -1
- package/dist/mcp-server/resources/utils/resourceHandlerFactory.js +23 -12
- package/dist/mcp-server/resources/utils/resourceHandlerFactory.js.map +1 -1
- package/dist/mcp-server/server.d.ts +18 -2
- package/dist/mcp-server/server.d.ts.map +1 -1
- package/dist/mcp-server/server.js +4 -1
- package/dist/mcp-server/server.js.map +1 -1
- package/dist/mcp-server/tools/tool-registration.d.ts.map +1 -1
- package/dist/mcp-server/tools/tool-registration.js +2 -0
- package/dist/mcp-server/tools/tool-registration.js.map +1 -1
- package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts +14 -2
- package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts.map +1 -1
- package/dist/mcp-server/tools/utils/toolHandlerFactory.js +37 -13
- package/dist/mcp-server/tools/utils/toolHandlerFactory.js.map +1 -1
- package/dist/services/canvas/core/CanvasInstance.d.ts +1 -1
- package/dist/services/canvas/core/CanvasInstance.d.ts.map +1 -1
- package/dist/services/canvas/core/CanvasInstance.js +36 -11
- package/dist/services/canvas/core/CanvasInstance.js.map +1 -1
- package/dist/services/canvas/core/CanvasRegistry.d.ts +50 -3
- package/dist/services/canvas/core/CanvasRegistry.d.ts.map +1 -1
- package/dist/services/canvas/core/CanvasRegistry.js +163 -6
- package/dist/services/canvas/core/CanvasRegistry.js.map +1 -1
- package/dist/services/canvas/spillover.d.ts +6 -0
- package/dist/services/canvas/spillover.d.ts.map +1 -1
- package/dist/services/canvas/spillover.js +1 -0
- package/dist/services/canvas/spillover.js.map +1 -1
- package/dist/services/canvas/types.d.ts +21 -0
- package/dist/services/canvas/types.d.ts.map +1 -1
- package/dist/testing/index.d.ts +8 -14
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +10 -11
- package/dist/testing/index.js.map +1 -1
- package/package.json +7 -7
- package/skills/add-prompt/SKILL.md +33 -2
- package/skills/add-resource/SKILL.md +25 -1
- package/skills/add-service/SKILL.md +2 -2
- package/skills/add-test/SKILL.md +3 -3
- package/skills/add-tool/SKILL.md +38 -1
- package/skills/api-canvas/SKILL.md +23 -3
- package/skills/api-config/SKILL.md +12 -3
- package/skills/api-context/SKILL.md +57 -31
- package/skills/api-linter/SKILL.md +77 -3
- package/skills/api-mirror/SKILL.md +30 -1
- package/skills/api-testing/SKILL.md +2 -16
- package/skills/code-simplifier/SKILL.md +2 -2
- package/skills/design-mcp-server/SKILL.md +2 -1
- package/skills/polish-docs-meta/SKILL.md +1 -1
- package/skills/polish-docs-meta/references/agent-protocol.md +1 -1
- package/skills/report-issue-framework/SKILL.md +2 -2
- package/skills/security-pass/SKILL.md +6 -11
- package/templates/AGENTS.md +17 -5
- package/templates/CLAUDE.md +17 -5
- package/templates/Dockerfile +17 -0
- package/dist/mcp-server/roots/roots-registration.d.ts +0 -22
- package/dist/mcp-server/roots/roots-registration.d.ts.map +0 -1
- package/dist/mcp-server/roots/roots-registration.js +0 -25
- package/dist/mcp-server/roots/roots-registration.js.map +0 -1
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
MCP definition linter rules reference. Use when `bun run lint:mcp` or `bun run devcheck` reports a lint error or warning (`format-parity`, `schema-is-object`, `name-format`, `server-json-*`, etc.) and you need to understand the rule, its severity, and how to fix it. Every rule ID the linter emits has an entry in this doc.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.7"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -46,14 +46,14 @@ Grouped by family. Jump to any rule ID via its anchor.
|
|
|
46
46
|
| Schema | `schema-is-object`, `describe-on-fields`, `schema-serializable` | [Schema rules](#schema-rules) |
|
|
47
47
|
| Portability | `schema-format-portability`, `schema-anyof-needs-type`, `schema-no-discriminator-keyword`, `schema-no-defs`, `schema-dialect-tag` | [Portability rules](#portability-rules) |
|
|
48
48
|
| Names | `name-required`, `name-format`, `name-unique` | [Name rules](#name-rules) |
|
|
49
|
-
| Tools | `description-required`, `handler-required`, `auth-type`, `auth-scope-format`, `annotation-type`, `annotation-coherence`, `meta-ui-type`, `meta-ui-resource-uri-required`, `meta-ui-resource-uri-scheme`, `app-tool-resource-pairing` | [Tool rules](#tool-rules) |
|
|
49
|
+
| Tools | `description-required`, `handler-required`, `auth-type`, `auth-scope-format`, `annotation-type`, `annotation-coherence`, `meta-ui-type`, `meta-ui-resource-uri-required`, `meta-ui-resource-uri-scheme`, `app-tool-resource-pairing`, `canvas-consumer-missing` | [Tool rules](#tool-rules) |
|
|
50
50
|
| Resources | `uri-template-required`, `uri-template-valid`, `resource-name-not-uri`, `template-params-align` | [Resource rules](#resource-rules) |
|
|
51
51
|
| Landing | `landing-*` (23 rules — shape, tagline, logo, links, repo, envExample, connectSnippets, theme) | [Landing config rules](#landing-config-rules) |
|
|
52
52
|
| Prompts | `generate-required` | [Prompt rules](#prompt-rules) |
|
|
53
53
|
| Handler body | `prefer-mcp-error-in-handler`, `prefer-error-factory`, `preserve-cause-on-rethrow`, `no-stringify-upstream-error` | [Handler body rules](#handler-body-rules) |
|
|
54
54
|
| Error contract (structural) | `error-contract-type`, `error-contract-empty`, `error-contract-entry-type`, `error-contract-code-type`, `error-contract-code-unknown`, `error-contract-code-unknown-error`, `error-contract-reason-required`, `error-contract-reason-format`, `error-contract-reason-unique`, `error-contract-when-required`, `error-contract-retryable-type`, `error-contract-recovery-required`, `error-contract-recovery-empty`, `error-contract-recovery-min-words` | [Error contract rules](#error-contract-rules) |
|
|
55
55
|
| Error contract (conformance) | `error-contract-conformance`, `error-contract-prefer-fail` | [Error contract rules](#error-contract-rules) |
|
|
56
|
-
| Enrichment | `enrichment-type`, `enrichment-empty`, `enrichment-field-type`, `enrichment-output-collision`, `enrichment-prefer-block`, `enrichment-trailer-render`, `enrichment-trailer-orphan`, `enrichment-trailer-unknown-field` | [Enrichment rules](#enrichment-rules) |
|
|
56
|
+
| Enrichment | `enrichment-type`, `enrichment-empty`, `enrichment-field-type`, `enrichment-output-collision`, `enrichment-prefer-block`, `enrichment-trailer-render`, `enrichment-trailer-orphan`, `enrichment-trailer-unknown-field`, `capped-list-no-truncation` | [Enrichment rules](#enrichment-rules) |
|
|
57
57
|
| server.json | ~40 rules prefixed `server-json-*` | [server.json rules](#server-json-rules) |
|
|
58
58
|
|
|
59
59
|
---
|
|
@@ -367,6 +367,29 @@ An app tool's `_meta.ui.resourceUri` must match the `uriTemplate` of a registere
|
|
|
367
367
|
|
|
368
368
|
**Fix:** either correct the `resourceUri` to match an existing resource, or register the resource it references. Use the `add-app-tool` skill's paired scaffold to avoid this.
|
|
369
369
|
|
|
370
|
+
### canvas-consumer-missing
|
|
371
|
+
|
|
372
|
+
**Severity:** warning
|
|
373
|
+
|
|
374
|
+
Fires when the registered tool set contains at least one tool whose output schema has a depth-0 field named `canvas_id` or `canvasId`, but no consumer tool is registered — that is, no tool name ends with `_dataframe_query` and no extra names are listed in `canvasConsumers`.
|
|
375
|
+
|
|
376
|
+
A canvas token with no query path is dead output: the agent receives the token but has no tool to send it to. The fix runs in either direction:
|
|
377
|
+
|
|
378
|
+
- **Complete the integration** — add the standard `<prefix>_dataframe_query` and `<prefix>_dataframe_describe` consumers (see `api-canvas`).
|
|
379
|
+
- **Remove the staging** — when the data isn't row-shaped (nested, heterogeneous, single-record payloads), SQL access adds nothing. Drop the DataCanvas integration rather than adding tools to justify it.
|
|
380
|
+
|
|
381
|
+
**Knob:** suppress via `LintInput.canvasConsumers`:
|
|
382
|
+
|
|
383
|
+
```ts
|
|
384
|
+
// Accept a non-standard query tool name:
|
|
385
|
+
validateDefinitions({ tools, canvasConsumers: ['my_sql_query'] });
|
|
386
|
+
|
|
387
|
+
// Disable the rule entirely:
|
|
388
|
+
validateDefinitions({ tools, canvasConsumers: false });
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
**Env var:** `MCP_LINT_CANVAS_CONSUMERS` — comma-separated tool names; the literal `false` disables. A programmatic `LintInput.canvasConsumers` takes precedence over the env var. Servers that need the knob set `MCP_LINT_CANVAS_CONSUMERS=my_query_tool` in their `.env` or CI environment.
|
|
392
|
+
|
|
370
393
|
---
|
|
371
394
|
|
|
372
395
|
## Resource rules
|
|
@@ -777,6 +800,57 @@ Fires when an `enrichmentTrailer` key doesn't match any declared `enrichment` fi
|
|
|
777
800
|
|
|
778
801
|
**Fix:** rename the trailer key to a declared enrichment field, or remove it.
|
|
779
802
|
|
|
803
|
+
### capped-list-no-truncation
|
|
804
|
+
|
|
805
|
+
**Severity:** warning
|
|
806
|
+
|
|
807
|
+
Fires when a tool:
|
|
808
|
+
1. has a depth-0 input field named `limit`, `per_page`, `page_size`, `max_results`, or `max_items` (case-insensitive; camelCase twins like `perPage`, `maxResults` match too), AND
|
|
809
|
+
2. has at least one depth-0 array-typed `output` field, AND
|
|
810
|
+
3. declares no truncation disclosure.
|
|
811
|
+
|
|
812
|
+
**Disclosure-present (rule silent) when** any of the following is true:
|
|
813
|
+
- The declared `enrichment` shape has a `truncated` or `totalCount` key (`ctx.enrich.truncated()` and `ctx.enrich.total()` satisfy this).
|
|
814
|
+
- The `output` schema has a depth-0 `truncated` or `totalCount` field.
|
|
815
|
+
|
|
816
|
+
A silently capped list leaves the agent unaware that results were cut off — it may treat a partial set as complete. Use `ctx.enrich.truncated({ shown, cap })` for the one-liner:
|
|
817
|
+
|
|
818
|
+
```ts
|
|
819
|
+
// In the enrichment block:
|
|
820
|
+
enrichment: {
|
|
821
|
+
truncated: z.boolean().describe('True when the list was capped at the limit.'),
|
|
822
|
+
shown: z.number().describe('Number of items returned.'),
|
|
823
|
+
cap: z.number().describe('The limit applied.'),
|
|
824
|
+
},
|
|
825
|
+
|
|
826
|
+
// In the handler:
|
|
827
|
+
if (items.length >= input.limit) {
|
|
828
|
+
ctx.enrich.truncated({ shown: items.length, cap: input.limit });
|
|
829
|
+
}
|
|
830
|
+
```
|
|
831
|
+
|
|
832
|
+
Or use `ctx.enrich.total(n)` when the upstream total is known — that writes `totalCount`, which is also recognized as honest disclosure.
|
|
833
|
+
|
|
834
|
+
**Threshold bound:** when the list is sorted by the cap key and the upstream total is unknowable (e.g. an API returning only the page), the smallest shown value upper-bounds all omitted items. Pass it as `ceiling`:
|
|
835
|
+
|
|
836
|
+
```ts
|
|
837
|
+
ctx.enrich.truncated({ shown: items.length, cap: input.limit, ceiling: items.at(-1)?.count });
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
Declare `truncationCeiling: z.number().optional()` in the `enrichment` block to surface it.
|
|
841
|
+
|
|
842
|
+
**Knob:** suppress via `LintInput.truncationAllowlist`:
|
|
843
|
+
|
|
844
|
+
```ts
|
|
845
|
+
// Exempt a specific tool:
|
|
846
|
+
validateDefinitions({ tools, truncationAllowlist: ['my_search_tool'] });
|
|
847
|
+
|
|
848
|
+
// Disable the rule entirely:
|
|
849
|
+
validateDefinitions({ tools, truncationAllowlist: false });
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
**Env var:** `MCP_LINT_TRUNCATION_ALLOWLIST` — comma-separated tool names; the literal `false` disables. A programmatic `LintInput.truncationAllowlist` takes precedence.
|
|
853
|
+
|
|
780
854
|
---
|
|
781
855
|
|
|
782
856
|
## Escape hatches
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Stand up a persistent, self-refreshing local mirror of a bulk upstream dataset with the MirrorService (@cyanheads/mcp-ts-core/mirror). Use when a server wraps a large or slow API and should query a synced local index (embedded SQLite + FTS5) instead of paginating the live API per request.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.1"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -92,6 +92,35 @@ The service owns `runSync` + state; it does not schedule. Wire "self-refreshing"
|
|
|
92
92
|
- **Refresh** — register `runSync({ mode: 'refresh' })` on a cron via `schedulerService` from `@cyanheads/mcp-ts-core/utils`, inside `setup()`. Gate on transport (HTTP) when stdio operators run it out-of-band.
|
|
93
93
|
- **Init** — run out-of-band (a CLI script / one-shot), never on startup: a full init can take hours and must not block the server. It is idempotent and resumable — re-running after an interrupt continues from the persisted cursor.
|
|
94
94
|
|
|
95
|
+
### Shipping the mirror CLI in a production Docker image
|
|
96
|
+
|
|
97
|
+
The scaffold `Dockerfile` copies only `dist/` to the runtime stage. A mirror lifecycle script (`mirror:init`, `mirror:refresh`, `mirror:verify`) that imports through the `@/` path alias fails under `docker exec` — `@/` resolves to `src/` via the source `tsconfig.json`, and `src/` never reaches the image.
|
|
98
|
+
|
|
99
|
+
On the Bun runtime image (`oven/bun`), two stanzas fix it — no build change, no `rootDir` surgery, and the `mirror:*` package scripts stay identical between a dev checkout and the image.
|
|
100
|
+
|
|
101
|
+
Add the following to the runtime stage of `Dockerfile`, after the `COPY --from=build .../dist ./dist` line:
|
|
102
|
+
|
|
103
|
+
```dockerfile
|
|
104
|
+
# Copy mirror lifecycle scripts. The shared context shim (_mirror-context.ts)
|
|
105
|
+
# is imported by the three named scripts, so it must travel with them.
|
|
106
|
+
COPY --from=build /usr/src/app/scripts/<your>-mirror-init.ts \
|
|
107
|
+
/usr/src/app/scripts/<your>-mirror-refresh.ts \
|
|
108
|
+
/usr/src/app/scripts/<your>-mirror-verify.ts \
|
|
109
|
+
/usr/src/app/scripts/_mirror-context.ts \
|
|
110
|
+
./scripts/
|
|
111
|
+
|
|
112
|
+
# Bun honors tsconfig `paths` at runtime — map `@/` to the compiled `./dist/`
|
|
113
|
+
# so the .ts scripts resolve their alias imports against the build output.
|
|
114
|
+
# In a dev checkout the source tsconfig.json maps @/* → ./src/*; in the image
|
|
115
|
+
# this emitted one maps @/* → ./dist/*. Same `bun run mirror:*` command, both
|
|
116
|
+
# environments — the only lever is which tsconfig.json is on disk.
|
|
117
|
+
RUN echo '{"compilerOptions":{"baseUrl":".","paths":{"@/*":["./dist/*"]}}}' > tsconfig.json
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Caveat:** this relies on Bun's runtime `paths` resolution. A Node runtime image (no native `.ts` execution) needs the scripts compiled into `dist/` instead — a separate tsconfig pass with a different `rootDir` is required in that case.
|
|
121
|
+
|
|
122
|
+
**`package.json` `files[]`:** add `scripts/_mirror-context.ts` and the three named lifecycle scripts so the npm tarball and `.mcpb` bundle carry them. Consumers installing from npm need them for `docker exec` access.
|
|
123
|
+
|
|
95
124
|
## Checklist
|
|
96
125
|
|
|
97
126
|
- [ ] `defineMirror({ name, store, sync })`; the server holds the instance (one per mirror)
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Testing patterns for MCP tool/resource handlers using `createMockContext` and Vitest. Covers mock context options, handler testing, McpError assertions, format testing, Vitest config setup, and test isolation conventions.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.3"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -27,7 +27,6 @@ import { createMockContext } from '@cyanheads/mcp-ts-core/testing';
|
|
|
27
27
|
createMockContext() // minimal — ctx.state operations throw without tenantId
|
|
28
28
|
createMockContext({ tenantId: 'test-tenant' }) // enables ctx.state (tenant-scoped in-memory storage)
|
|
29
29
|
createMockContext({ errors: myTool.errors }) // attaches typed ctx.fail keyed by the contract reasons
|
|
30
|
-
createMockContext({ sample: vi.fn().mockResolvedValue(...) }) // with MCP sampling
|
|
31
30
|
createMockContext({ elicit: vi.fn().mockResolvedValue(...) }) // with elicitation
|
|
32
31
|
createMockContext({ progress: true }) // with task progress (ctx.progress populated)
|
|
33
32
|
createMockContext({ requestId: 'my-id' }) // override request ID (default: 'test-request-id')
|
|
@@ -52,7 +51,6 @@ interface MockContextOptions {
|
|
|
52
51
|
progress?: boolean;
|
|
53
52
|
sessionId?: string;
|
|
54
53
|
requestId?: string;
|
|
55
|
-
sample?: (messages: SamplingMessage[], opts?: SamplingOpts) => Promise<CreateMessageResult>;
|
|
56
54
|
signal?: AbortSignal;
|
|
57
55
|
tenantId?: string;
|
|
58
56
|
uri?: URL;
|
|
@@ -61,7 +59,7 @@ interface MockContextOptions {
|
|
|
61
59
|
|
|
62
60
|
| Option | Effect |
|
|
63
61
|
|:-------|:-------|
|
|
64
|
-
| _(none)_ | Minimal context — `ctx.state` operations throw without `tenantId`; `ctx.elicit`/`ctx.
|
|
62
|
+
| _(none)_ | Minimal context — `ctx.state` operations throw without `tenantId`; `ctx.elicit`/`ctx.progress` are `undefined` |
|
|
65
63
|
| `auth` | Sets `ctx.auth` for scope-checking tests |
|
|
66
64
|
| `elicit` | Assigns a function to `ctx.elicit` for testing elicitation calls |
|
|
67
65
|
| `errors` | Attaches a typed `ctx.fail` against the contract — same wiring the production handler factory uses. Pass `myTool.errors` directly. |
|
|
@@ -72,7 +70,6 @@ interface MockContextOptions {
|
|
|
72
70
|
| `sessionId` | Sets `ctx.sessionId` for handlers that branch on session ID |
|
|
73
71
|
| `progress` | Populates `ctx.progress` with real state-tracking implementation (see below) |
|
|
74
72
|
| `requestId` | Overrides `ctx.requestId` (default: `'test-request-id'`) |
|
|
75
|
-
| `sample` | Assigns a function to `ctx.sample` for testing sampling calls |
|
|
76
73
|
| `signal` | Overrides `ctx.signal` — useful for cancellation testing |
|
|
77
74
|
| `tenantId` | Sets `ctx.tenantId` and enables `ctx.state` operations with in-memory storage |
|
|
78
75
|
| `uri` | Sets `ctx.uri` for resource handler testing |
|
|
@@ -164,17 +161,6 @@ it('uses elicitation when available', async () => {
|
|
|
164
161
|
expect(elicit).toHaveBeenCalledOnce();
|
|
165
162
|
});
|
|
166
163
|
|
|
167
|
-
it('uses sampling when available', async () => {
|
|
168
|
-
const sample = vi.fn().mockResolvedValue({
|
|
169
|
-
role: 'assistant',
|
|
170
|
-
content: { type: 'text', text: 'Summary text' },
|
|
171
|
-
});
|
|
172
|
-
const ctx = createMockContext({ sample });
|
|
173
|
-
const input = myTool.input.parse({ query: 'summarize this' });
|
|
174
|
-
const result = await myTool.handler(input, ctx);
|
|
175
|
-
expect(result.summary).toBeDefined();
|
|
176
|
-
});
|
|
177
|
-
|
|
178
164
|
it('handles missing elicitation gracefully', async () => {
|
|
179
165
|
// ctx.elicit is undefined — handler must check before calling
|
|
180
166
|
const ctx = createMockContext();
|
|
@@ -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.1"
|
|
8
8
|
audience: external
|
|
9
9
|
type: workflow
|
|
10
10
|
---
|
|
@@ -62,7 +62,7 @@ Evaluate the changes across these dimensions. Not every dimension applies to eve
|
|
|
62
62
|
|
|
63
63
|
- **Error throwing patterns** — Prefer framework error factories (`McpError`, `validationError`, `notFound`, `httpErrorFromResponse`) over raw `throw new Error()`. Tool handlers should throw — the framework catches, classifies, and instruments.
|
|
64
64
|
- **Error codes** — `InvalidParams` only for malformed JSON-RPC params shape. `ValidationError` for domain validation. `NotFound` for missing entities. Don't conflate them.
|
|
65
|
-
- **Ctx usage** — Use `ctx.log`, `ctx.state`, `ctx.elicit
|
|
65
|
+
- **Ctx usage** — Use `ctx.log`, `ctx.state`, `ctx.elicit` — don't reach for global loggers or request-scoped storage directly. The `ctx` pattern carries tenant scope and OTel context.
|
|
66
66
|
- **Zod schemas** — Every tool input/output field needs `.describe()`. Zod 4 requires `z.record(z.string(), z.string())` not `z.record(z.string())`. Use `.optional()` rather than `.nullish()` unless null is semantically distinct from absent.
|
|
67
67
|
- **Tool annotations** — `readOnlyHint`, `idempotentHint`, `openWorldHint` should reflect reality. A read-only tool with `readOnlyHint: false` gives clients the wrong picture.
|
|
68
68
|
- **`exactOptionalPropertyTypes` boundaries** — If a downstream type insists on the field being present-or-not-present (not present-as-undefined), use a mapped widening type at the boundary. The pattern is documented in the framework.
|
|
@@ -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.18"
|
|
8
8
|
audience: external
|
|
9
9
|
type: workflow
|
|
10
10
|
---
|
|
@@ -348,6 +348,7 @@ output: z.object({
|
|
|
348
348
|
}),
|
|
349
349
|
```
|
|
350
350
|
|
|
351
|
+
- **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.
|
|
351
352
|
- **Truncate large output with counts.** When a list exceeds a reasonable display size, show the top N and append "...and X more". Don't silently drop results.
|
|
352
353
|
- **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.
|
|
353
354
|
- **Outline one large *document* into sections.** When a single tool call returns one document-shaped record (not many rows) that can exceed context — a ~130KB FDA drug label, a big API entity dominated by a few fat fields — return a section *outline* (top-level keys + per-section byte size) instead of truncating, and let the agent re-call with `sections: [...]` to pull only what it needs. The `outlineOnOverflow()` helper (`@cyanheads/mcp-ts-core/utils`) measures the payload and returns a `full | outline` discriminated union; declare its `OUTLINE_VARIANT` as a branch of the tool's `output` so `format()`-parity is enforced per branch. Pure measure + key-slice — Workers-portable, unlike canvas-bound `spillover()`. Distinct from spillover on *shape*: spillover splits a row collection, this outlines one fat record. See the `techniques` skill's `outline-on-overflow` reference.
|
|
@@ -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.6"
|
|
8
8
|
audience: external
|
|
9
9
|
type: workflow
|
|
10
10
|
---
|
|
@@ -46,7 +46,7 @@ Compare the structure diagram against the actual directory layout. If it still r
|
|
|
46
46
|
|
|
47
47
|
### 4. Update the Context Table
|
|
48
48
|
|
|
49
|
-
Review the `ctx` feature table. Remove rows for features the server doesn't use (e.g., `ctx.elicit
|
|
49
|
+
Review the `ctx` feature table. Remove rows for features the server doesn't use (e.g., `ctx.elicit` if no tools call it). Add any custom context usage that's become important. The table should reflect what this server actually uses, not the full framework surface.
|
|
50
50
|
|
|
51
51
|
### 5. Update Server Config Example
|
|
52
52
|
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
File a bug or feature request against @cyanheads/mcp-ts-core when you hit a framework issue. Use when a builder, utility, context method, or config behaves contrary to the documented API — not for server-specific application bugs.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.8"
|
|
8
8
|
audience: external
|
|
9
9
|
type: workflow
|
|
10
10
|
---
|
|
@@ -148,7 +148,7 @@ Format: `bug(<scope>): concise description`
|
|
|
148
148
|
| `tool` | Tool builder, handler, format, annotations |
|
|
149
149
|
| `resource` | Resource builder, handler, list, params |
|
|
150
150
|
| `prompt` | Prompt builder, generate, args |
|
|
151
|
-
| `context` | Context, logger, state, progress, elicit
|
|
151
|
+
| `context` | Context, logger, state, progress, elicit |
|
|
152
152
|
| `config` | AppConfig, parseConfig, env parsing |
|
|
153
153
|
| `errors` | McpError, error factories, typed contracts (`errors[]` / `ctx.fail`), conformance lint, `httpErrorFromResponse`, auto-classification |
|
|
154
154
|
| `auth` | Auth modes, scope checking, JWT/OAuth |
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: security-pass
|
|
3
3
|
description: >
|
|
4
|
-
Review an MCP server for common security gaps: LLM-facing surfaces as injection vector (tools, resources, prompts, descriptions), scope blast radius, destructive ops without consent, upstream auth shape, input sinks (URL / path / roots / shell /
|
|
4
|
+
Review an MCP server for common security gaps: LLM-facing surfaces as injection vector (tools, resources, prompts, descriptions), scope blast radius, destructive ops without consent, upstream auth shape, input sinks (URL / path / roots / shell / schema strictness / ReDoS), tenant isolation, leakage through errors and telemetry, unbounded resources, and HTTP-mode deployment surface. Use before a release, after a batch of handler changes, or when the user asks for a security review, audit, or hardening pass. Produces grouped findings and a numbered options list.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.5"
|
|
8
8
|
audience: external
|
|
9
9
|
type: audit
|
|
10
10
|
---
|
|
@@ -44,7 +44,7 @@ find src/mcp-server/prompts/definitions -name "*.prompt.ts" 2>/dev/null | sort
|
|
|
44
44
|
find src/services -maxdepth 1 -mindepth 1 -type d | sort
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
Note: tool / resource / prompt counts, auth mode, storage provider, upstream APIs, which tools have `destructiveHint`, which handlers use `ctx.
|
|
47
|
+
Note: tool / resource / prompt counts, auth mode, storage provider, upstream APIs, which tools have `destructiveHint`, which handlers use `ctx.elicit`, which services hold module-scope state, whether the server reads `roots`.
|
|
48
48
|
|
|
49
49
|
**If transport is streamable HTTP or SSE**, also capture:
|
|
50
50
|
|
|
@@ -162,9 +162,6 @@ grep -rnE "\b(exec|spawn|execSync|spawnSync)\b" src/
|
|
|
162
162
|
# Merges — prototype pollution
|
|
163
163
|
grep -rn "Object.assign\b\|structuredClone" src/
|
|
164
164
|
|
|
165
|
-
# Sampling — LLM-generated content flowing back into server logic
|
|
166
|
-
grep -rn "ctx.sample\|sampling/createMessage" src/
|
|
167
|
-
|
|
168
165
|
# Roots — client-shared filesystem
|
|
169
166
|
grep -rn "roots/list\|ctx.roots" src/
|
|
170
167
|
|
|
@@ -182,9 +179,7 @@ grep -rn "\.passthrough()\|\.catchall(" src/mcp-server/
|
|
|
182
179
|
- User-JSON merges reject `__proto__`, `constructor`, `prototype` keys?
|
|
183
180
|
- **Input schemas `.strict()`** — unknown fields rejected, not silently passed to downstream code that destructures with `...rest`?
|
|
184
181
|
- **Output schemas without `.passthrough()` / `.catchall()`** — no accidental exfiltration of fields your schema didn't declare?
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
**Smell:** `z.string().url()` with no allowlist; `readFile(input.path)` with no canonicalization; `await ctx.sample(...)` result interpolated into a shell, SQL, or URL.
|
|
182
|
+
**Smell:** `z.string().url()` with no allowlist; `readFile(input.path)` with no canonicalization.
|
|
188
183
|
|
|
189
184
|
#### Axis 6 — Tenant isolation
|
|
190
185
|
|
|
@@ -333,14 +328,14 @@ End with:
|
|
|
333
328
|
## Checklist
|
|
334
329
|
|
|
335
330
|
- [ ] Scope confirmed (whole server / module / diff)
|
|
336
|
-
- [ ] Map built: tools / resources / prompts, services, upstream APIs, auth mode,
|
|
331
|
+
- [ ] Map built: tools / resources / prompts, services, upstream APIs, auth mode, elicit / roots usage
|
|
337
332
|
- [ ] Deployment surface reviewed (if HTTP): bind address, Origin allowlist, session ID, unauth routes, auth-spec compliance
|
|
338
333
|
- [ ] `fuzzTool` started in parallel
|
|
339
334
|
- [ ] Axis 1 — LLM-facing surfaces (tool / resource / prompt output + descriptions) framed and static
|
|
340
335
|
- [ ] Axis 2 — scope granularity audited
|
|
341
336
|
- [ ] Axis 3 — destructive ops verified to elicit, elicit response schema-validated
|
|
342
337
|
- [ ] Axis 4 — upstream auth + token passthrough reviewed
|
|
343
|
-
- [ ] Axis 5 — input sinks (URL / path / roots / shell / proto /
|
|
338
|
+
- [ ] Axis 5 — input sinks (URL / path / roots / shell / proto / schema strictness / ReDoS) checked
|
|
344
339
|
- [ ] Axis 6 — tenant isolation: module-scope state swept
|
|
345
340
|
- [ ] Axis 7 — leakage back: errors / outputs / `ctx.log` / `console.*` / telemetry / constant-time comparisons
|
|
346
341
|
- [ ] Axis 8 — resource bounds on loops / retries / pagination / parse size+depth / per-tenant rate
|
package/templates/AGENTS.md
CHANGED
|
@@ -48,7 +48,7 @@ Tailor suggestions to what's actually missing or stale — don't recite the full
|
|
|
48
48
|
- **Logic throws, framework catches.** Tool/resource handlers are pure — throw on failure, no `try/catch`. Plain `Error` is fine; the framework catches, classifies, and formats. Use error factories (`notFound()`, `validationError()`, etc.) when the error code matters.
|
|
49
49
|
- **Use `ctx.log`** for request-scoped logging. No `console` calls.
|
|
50
50
|
- **Use `ctx.state`** for tenant-scoped storage. Never access persistence directly.
|
|
51
|
-
- **Check `ctx.elicit
|
|
51
|
+
- **Check `ctx.elicit`** for presence before calling.
|
|
52
52
|
- **Secrets in env vars only** — never hardcoded.
|
|
53
53
|
- **Close the loop on issues.** When implementing work tracked by a GitHub issue, comment on the issue with what landed and close it. Do both — a comment without a close leaves stale issues open; a close without a comment leaves no record of what shipped. The comment is for future readers — state the concrete changes, not the conversation that produced them.
|
|
54
54
|
|
|
@@ -156,9 +156,22 @@ export function getServerConfig() {
|
|
|
156
156
|
|
|
157
157
|
For env booleans use `z.stringbool()`, never `z.coerce.boolean()` — `Boolean("false")` is `true`, so a coerced flag can't be disabled through the environment. `z.stringbool()` parses `true/false/1/0/yes/no/on/off` and rejects anything else, so `=false` actually disables.
|
|
158
158
|
|
|
159
|
-
### Server instructions
|
|
159
|
+
### Server identity and instructions
|
|
160
160
|
|
|
161
|
-
`createApp(
|
|
161
|
+
`createApp()` accepts optional identity fields forwarded to the SDK's `initialize` response and the server manifest (`/.well-known/mcp.json`):
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
await createApp({
|
|
165
|
+
name: 'my-mcp-server',
|
|
166
|
+
title: 'My Server', // human-readable display name
|
|
167
|
+
websiteUrl: 'https://github.com/owner/repo', // canonical homepage URL
|
|
168
|
+
description: 'One-line description.', // wins over MCP_SERVER_DESCRIPTION
|
|
169
|
+
icons: [{ src: 'https://example.com/icon.png', sizes: ['48x48'], mimeType: 'image/png' }],
|
|
170
|
+
instructions: 'Use shortcut alpha for the most common case.', // session-level context
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
`instructions` is optional server-level orientation, sent on every `initialize` as session-level context. Use it for deployment guidance (connection aliases, regional notes, scope hints) instead of repeating the same context across tool descriptions. Client adoption is uneven, but there's no downside when set.
|
|
162
175
|
|
|
163
176
|
---
|
|
164
177
|
|
|
@@ -170,8 +183,7 @@ Handlers receive a unified `ctx` object. Key properties:
|
|
|
170
183
|
|:---------|:------------|
|
|
171
184
|
| `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. |
|
|
172
185
|
| `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.list(prefix, { cursor, limit })`. Accepts any serializable value. |
|
|
173
|
-
| `ctx.elicit` | Ask user for structured input. **Check for presence first:** `if (ctx.elicit) { ... }` |
|
|
174
|
-
| `ctx.sample` | Request LLM completion from the client. **Check for presence first:** `if (ctx.sample) { ... }` |
|
|
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) { ... }` |
|
|
175
187
|
| `ctx.signal` | `AbortSignal` for cancellation. |
|
|
176
188
|
| `ctx.progress` | Task progress (present when `task: true`) — `.setTotal(n)`, `.increment()`, `.update(message)`. |
|
|
177
189
|
| `ctx.requestId` | Unique request ID. |
|
package/templates/CLAUDE.md
CHANGED
|
@@ -48,7 +48,7 @@ Tailor suggestions to what's actually missing or stale — don't recite the full
|
|
|
48
48
|
- **Logic throws, framework catches.** Tool/resource handlers are pure — throw on failure, no `try/catch`. Plain `Error` is fine; the framework catches, classifies, and formats. Use error factories (`notFound()`, `validationError()`, etc.) when the error code matters.
|
|
49
49
|
- **Use `ctx.log`** for request-scoped logging. No `console` calls.
|
|
50
50
|
- **Use `ctx.state`** for tenant-scoped storage. Never access persistence directly.
|
|
51
|
-
- **Check `ctx.elicit
|
|
51
|
+
- **Check `ctx.elicit`** for presence before calling.
|
|
52
52
|
- **Secrets in env vars only** — never hardcoded.
|
|
53
53
|
- **Close the loop on issues.** When implementing work tracked by a GitHub issue, comment on the issue with what landed and close it. Do both — a comment without a close leaves stale issues open; a close without a comment leaves no record of what shipped. The comment is for future readers — state the concrete changes, not the conversation that produced them.
|
|
54
54
|
|
|
@@ -156,9 +156,22 @@ export function getServerConfig() {
|
|
|
156
156
|
|
|
157
157
|
For env booleans use `z.stringbool()`, never `z.coerce.boolean()` — `Boolean("false")` is `true`, so a coerced flag can't be disabled through the environment. `z.stringbool()` parses `true/false/1/0/yes/no/on/off` and rejects anything else, so `=false` actually disables.
|
|
158
158
|
|
|
159
|
-
### Server instructions
|
|
159
|
+
### Server identity and instructions
|
|
160
160
|
|
|
161
|
-
`createApp(
|
|
161
|
+
`createApp()` accepts optional identity fields forwarded to the SDK's `initialize` response and the server manifest (`/.well-known/mcp.json`):
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
await createApp({
|
|
165
|
+
name: 'my-mcp-server',
|
|
166
|
+
title: 'My Server', // human-readable display name
|
|
167
|
+
websiteUrl: 'https://github.com/owner/repo', // canonical homepage URL
|
|
168
|
+
description: 'One-line description.', // wins over MCP_SERVER_DESCRIPTION
|
|
169
|
+
icons: [{ src: 'https://example.com/icon.png', sizes: ['48x48'], mimeType: 'image/png' }],
|
|
170
|
+
instructions: 'Use shortcut alpha for the most common case.', // session-level context
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
`instructions` is optional server-level orientation, sent on every `initialize` as session-level context. Use it for deployment guidance (connection aliases, regional notes, scope hints) instead of repeating the same context across tool descriptions. Client adoption is uneven, but there's no downside when set.
|
|
162
175
|
|
|
163
176
|
---
|
|
164
177
|
|
|
@@ -170,8 +183,7 @@ Handlers receive a unified `ctx` object. Key properties:
|
|
|
170
183
|
|:---------|:------------|
|
|
171
184
|
| `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. |
|
|
172
185
|
| `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.list(prefix, { cursor, limit })`. Accepts any serializable value. |
|
|
173
|
-
| `ctx.elicit` | Ask user for structured input. **Check for presence first:** `if (ctx.elicit) { ... }` |
|
|
174
|
-
| `ctx.sample` | Request LLM completion from the client. **Check for presence first:** `if (ctx.sample) { ... }` |
|
|
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) { ... }` |
|
|
175
187
|
| `ctx.signal` | `AbortSignal` for cancellation. |
|
|
176
188
|
| `ctx.progress` | Task progress (present when `task: true`) — `.setTotal(n)`, `.increment()`, `.update(message)`. |
|
|
177
189
|
| `ctx.requestId` | Unique request ID. |
|
package/templates/Dockerfile
CHANGED
|
@@ -71,12 +71,29 @@ RUN if [ "$OTEL_ENABLED" = "true" ]; then \
|
|
|
71
71
|
# Copy the compiled application code from the build stage
|
|
72
72
|
COPY --from=build /usr/src/app/dist ./dist
|
|
73
73
|
|
|
74
|
+
# Mirror CLI (MirrorService adopters only — Tier 3, opt-in):
|
|
75
|
+
# Copy your mirror lifecycle scripts and emit a runtime tsconfig so Bun resolves
|
|
76
|
+
# the @/ path alias against ./dist/ rather than ./src/.
|
|
77
|
+
# See the api-mirror skill for the full recipe.
|
|
78
|
+
#
|
|
79
|
+
# COPY --from=build /usr/src/app/scripts/<your>-mirror-init.ts \
|
|
80
|
+
# /usr/src/app/scripts/<your>-mirror-refresh.ts \
|
|
81
|
+
# /usr/src/app/scripts/<your>-mirror-verify.ts \
|
|
82
|
+
# /usr/src/app/scripts/_mirror-context.ts \
|
|
83
|
+
# ./scripts/
|
|
84
|
+
# RUN echo '{"compilerOptions":{"baseUrl":".","paths":{"@/*":["./dist/*"]}}}' > tsconfig.json
|
|
85
|
+
|
|
74
86
|
# The 'oven/bun' image already provides a non-root user named 'bun'.
|
|
75
87
|
# We will use this existing user for enhanced security.
|
|
76
88
|
|
|
77
89
|
# Create and set permissions for the log directory, assigning ownership to the 'bun' user.
|
|
78
90
|
RUN mkdir -p /var/log/{{PACKAGE_NAME}} && chown -R bun:bun /var/log/{{PACKAGE_NAME}}
|
|
79
91
|
|
|
92
|
+
# Writable data dirs for on-disk SQLite stores (catalog index / observations
|
|
93
|
+
# mirror), owned by the runtime user. Mount a volume over either in production.
|
|
94
|
+
RUN mkdir -p /usr/src/app/.cache /usr/src/app/.mirror \
|
|
95
|
+
&& chown -R bun:bun /usr/src/app/.cache /usr/src/app/.mirror
|
|
96
|
+
|
|
80
97
|
# Switch to the non-root user
|
|
81
98
|
USER bun
|
|
82
99
|
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Service for implementing MCP roots capability.
|
|
3
|
-
* Roots provide filesystem/workspace context awareness for the server.
|
|
4
|
-
*
|
|
5
|
-
* MCP Roots Specification:
|
|
6
|
-
* @see {@link https://modelcontextprotocol.io/specification/2025-06-18/basic/roots | MCP Roots}
|
|
7
|
-
* @module src/mcp-server/roots/roots-registration
|
|
8
|
-
*/
|
|
9
|
-
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
10
|
-
import type { logger as defaultLogger } from '../../utils/internal/logger.js';
|
|
11
|
-
export declare class RootsRegistry {
|
|
12
|
-
private logger;
|
|
13
|
-
constructor(logger: typeof defaultLogger);
|
|
14
|
-
/**
|
|
15
|
-
* Registers roots handlers on the given MCP server.
|
|
16
|
-
* Note: In MCP, roots are typically provided BY THE CLIENT to the server.
|
|
17
|
-
* This implementation provides a placeholder for demonstration.
|
|
18
|
-
* In production, roots would be received from the client via client.listRoots().
|
|
19
|
-
*/
|
|
20
|
-
registerAll(_server: McpServer): void;
|
|
21
|
-
}
|
|
22
|
-
//# sourceMappingURL=roots-registration.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"roots-registration.d.ts","sourceRoot":"","sources":["../../../src/mcp-server/roots/roots-registration.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,MAAM,IAAI,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAG1E,qBAAa,aAAa;IACZ,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,OAAO,aAAa;IAEhD;;;;;OAKG;IACH,WAAW,CAAC,OAAO,EAAE,SAAS,GAAG,IAAI;CActC"}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { requestContextService } from '../../utils/internal/requestContext.js';
|
|
2
|
-
export class RootsRegistry {
|
|
3
|
-
logger;
|
|
4
|
-
constructor(logger) {
|
|
5
|
-
this.logger = logger;
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* Registers roots handlers on the given MCP server.
|
|
9
|
-
* Note: In MCP, roots are typically provided BY THE CLIENT to the server.
|
|
10
|
-
* This implementation provides a placeholder for demonstration.
|
|
11
|
-
* In production, roots would be received from the client via client.listRoots().
|
|
12
|
-
*/
|
|
13
|
-
registerAll(_server) {
|
|
14
|
-
const context = requestContextService.createRequestContext({
|
|
15
|
-
operation: 'RootsRegistry.registerAll',
|
|
16
|
-
});
|
|
17
|
-
this.logger.debug('Roots capability enabled (client-provided roots)', context);
|
|
18
|
-
// Note: The MCP SDK handles roots automatically via the client-server protocol.
|
|
19
|
-
// Servers receive roots from clients, not the other way around.
|
|
20
|
-
// This is just a placeholder to demonstrate the capability is enabled.
|
|
21
|
-
// To access roots in your tools, use sdkContext to query the client.
|
|
22
|
-
this.logger.debug('Roots capability registered successfully', context);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
//# sourceMappingURL=roots-registration.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"roots-registration.js","sourceRoot":"","sources":["../../../src/mcp-server/roots/roots-registration.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAE3E,MAAM,OAAO,aAAa;IACJ;IAApB,YAAoB,MAA4B;QAA5B,WAAM,GAAN,MAAM,CAAsB;IAAG,CAAC;IAEpD;;;;;OAKG;IACH,WAAW,CAAC,OAAkB;QAC5B,MAAM,OAAO,GAAG,qBAAqB,CAAC,oBAAoB,CAAC;YACzD,SAAS,EAAE,2BAA2B;SACvC,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,kDAAkD,EAAE,OAAO,CAAC,CAAC;QAE/E,gFAAgF;QAChF,gEAAgE;QAChE,uEAAuE;QACvE,qEAAqE;QAErE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,0CAA0C,EAAE,OAAO,CAAC,CAAC;IACzE,CAAC;CACF"}
|