@cyanheads/mcp-ts-core 0.10.2 → 0.10.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +20 -10
- package/CLAUDE.md +20 -10
- package/README.md +5 -6
- package/changelog/0.10.x/0.10.2.md +4 -4
- package/changelog/0.10.x/0.10.3.md +46 -0
- package/changelog/0.10.x/0.10.4.md +41 -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 +35 -15
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/context.js +0 -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/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 +20 -4
- package/dist/mcp-server/server.d.ts.map +1 -1
- package/dist/mcp-server/server.js +7 -4
- 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/schemaSniffer.d.ts +6 -4
- package/dist/services/canvas/core/schemaSniffer.d.ts.map +1 -1
- package/dist/services/canvas/core/schemaSniffer.js +7 -10
- package/dist/services/canvas/core/schemaSniffer.js.map +1 -1
- package/dist/services/canvas/core/sqlGate.d.ts +15 -0
- package/dist/services/canvas/core/sqlGate.d.ts.map +1 -1
- package/dist/services/canvas/core/sqlGate.js +40 -0
- package/dist/services/canvas/core/sqlGate.js.map +1 -1
- package/dist/services/canvas/index.d.ts +1 -1
- package/dist/services/canvas/index.d.ts.map +1 -1
- package/dist/services/canvas/index.js +1 -1
- package/dist/services/canvas/index.js.map +1 -1
- package/dist/services/canvas/providers/duckdb/DuckdbProvider.d.ts.map +1 -1
- package/dist/services/canvas/providers/duckdb/DuckdbProvider.js +76 -18
- package/dist/services/canvas/providers/duckdb/DuckdbProvider.js.map +1 -1
- package/dist/services/canvas/types.d.ts +30 -1
- 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 +45 -24
- 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/api-canvas/SKILL.md +12 -6
- package/skills/api-config/SKILL.md +12 -3
- package/skills/api-context/SKILL.md +17 -31
- package/skills/api-testing/SKILL.md +18 -16
- package/skills/code-simplifier/SKILL.md +2 -2
- 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 +3 -0
- package/templates/package.json +5 -5
- package/templates/tests/tools/echo.tool.test.ts +7 -0
- package/tsconfig.base.json +0 -1
- package/dist/logs/combined.log +0 -4
- package/dist/logs/error.log +0 -2
- package/dist/logs/interactions.log +0 -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
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: api-context
|
|
3
3
|
description: >
|
|
4
|
-
Canonical reference for the unified `Context` object passed to every tool and resource handler in `@cyanheads/mcp-ts-core`. Covers the full interface, all sub-APIs (`ctx.log`, `ctx.state`, `ctx.elicit`, `ctx.
|
|
4
|
+
Canonical reference for the unified `Context` object passed to every tool and resource handler in `@cyanheads/mcp-ts-core`. Covers the full interface, all sub-APIs (`ctx.log`, `ctx.state`, `ctx.elicit`, `ctx.progress`, `ctx.enrich`), and when to use each.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.8"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
## Overview
|
|
13
13
|
|
|
14
|
-
Every tool and resource handler receives a single `Context` (`ctx`) argument. It provides request identity, structured logging, tenant-scoped storage, optional protocol capabilities (elicitation
|
|
14
|
+
Every tool and resource handler receives a single `Context` (`ctx`) argument. It provides request identity, structured logging, tenant-scoped storage, optional protocol capabilities (elicitation), cancellation, and task progress — all auto-correlated to the current request.
|
|
15
15
|
|
|
16
16
|
The framework auto-instruments every handler call (OTel span, duration, payload metrics). Use `ctx.log` for domain-specific logging and `ctx.state` for storage inside handlers. Use the global `logger` and `StorageService` directly only in lifecycle/background code (`setup()`, services).
|
|
17
17
|
|
|
@@ -39,8 +39,7 @@ interface Context {
|
|
|
39
39
|
readonly state: ContextState;
|
|
40
40
|
|
|
41
41
|
// Optional protocol capabilities (undefined when client doesn't support them)
|
|
42
|
-
readonly elicit?: (message
|
|
43
|
-
readonly sample?: (messages: SamplingMessage[], opts?: SamplingOpts) => Promise<CreateMessageResult>;
|
|
42
|
+
readonly elicit?: ElicitFn; // callable (message, schema) for form mode + .url(message, url) — see § ctx.elicit
|
|
44
43
|
|
|
45
44
|
// List-changed / resource-updated notifications — wired in every handler ctx;
|
|
46
45
|
// delivery is request-scoped (see § list-changed notifications)
|
|
@@ -254,9 +253,11 @@ await ctx.state.set(sessionKey, value);
|
|
|
254
253
|
|
|
255
254
|
---
|
|
256
255
|
|
|
257
|
-
## `ctx.elicit`
|
|
256
|
+
## `ctx.elicit`
|
|
258
257
|
|
|
259
|
-
|
|
258
|
+
Optional — `undefined` when the connected client doesn't advertise the `elicitation` capability (checked per request, after the initialize handshake). Check for presence before calling. A simple truthiness check is enough; no type guards needed.
|
|
259
|
+
|
|
260
|
+
`ctx.elicit` is an `ElicitFn` (exported from the main entry): directly callable for form-mode elicitation, with a `.url(message, url)` method for URL-mode. On the wire, the Zod schema is converted to the restricted flat JSON Schema the MCP spec requires — handlers never deal with that detail.
|
|
260
261
|
|
|
261
262
|
### `ctx.elicit` — ask the user for structured input
|
|
262
263
|
|
|
@@ -295,37 +296,23 @@ interface ElicitResult {
|
|
|
295
296
|
|
|
296
297
|
> **Note:** `content` is not typed against the Zod schema you pass — it is a `Record` of primitives. Validate `content` against your schema manually (e.g. `MySchema.parse(result.content)`) when `action === 'accept'`.
|
|
297
298
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
### `ctx.sample` — request an LLM completion from the client
|
|
299
|
+
### `ctx.elicit.url` — hand the user an external link
|
|
301
300
|
|
|
302
|
-
|
|
301
|
+
URL-mode elicitation (MCP 2025-11-25): instead of an inline form, the client directs the user to an external URL — authorization flows, hosted forms. The framework generates the protocol-required `elicitationId` internally.
|
|
303
302
|
|
|
304
303
|
```ts
|
|
305
|
-
if (ctx.
|
|
306
|
-
const result = await ctx.
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
],
|
|
310
|
-
{ maxTokens: 500 },
|
|
304
|
+
if (ctx.elicit) {
|
|
305
|
+
const result = await ctx.elicit.url(
|
|
306
|
+
'Authorize access to your account',
|
|
307
|
+
'https://example.com/oauth/authorize?state=...',
|
|
311
308
|
);
|
|
312
|
-
|
|
309
|
+
if (result.action !== 'accept') throw forbidden('Authorization declined');
|
|
313
310
|
}
|
|
314
311
|
```
|
|
315
312
|
|
|
316
|
-
`
|
|
313
|
+
`result.content` is absent in URL mode — the interaction completes out-of-band; only `action` reports the outcome.
|
|
317
314
|
|
|
318
|
-
|
|
319
|
-
interface SamplingOpts {
|
|
320
|
-
includeContext?: 'none' | 'thisServer' | 'allServers';
|
|
321
|
-
maxTokens?: number;
|
|
322
|
-
modelPreferences?: ModelPreferences;
|
|
323
|
-
stopSequences?: string[];
|
|
324
|
-
temperature?: number;
|
|
325
|
-
}
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
**Convention:** Only call `ctx.sample` from tool handlers, not from services.
|
|
315
|
+
**Convention:** Only call `ctx.elicit` from tool handlers, not from services.
|
|
329
316
|
|
|
330
317
|
---
|
|
331
318
|
|
|
@@ -666,7 +653,6 @@ See `add-tool`'s **Tool Response Design** and `skills/api-linter` (`enrichment-*
|
|
|
666
653
|
| `ctx.signal` | `AbortSignal` | Always |
|
|
667
654
|
| `ctx.enrich` | `Enrich` | Always; typed on `HandlerContext<R, E>` when an `enrichment` block is declared |
|
|
668
655
|
| `ctx.elicit` | `function \| undefined` | Client supports elicitation |
|
|
669
|
-
| `ctx.sample` | `function \| undefined` | Client supports sampling |
|
|
670
656
|
| `ctx.notifyResourceListChanged` | `function \| undefined` | Always in handler ctx; delivery request-scoped (see [§ list-changed notifications](#list-changed-notifications-ctxnotify)) |
|
|
671
657
|
| `ctx.notifyResourceUpdated` | `function \| undefined` | Always in handler ctx; delivery request-scoped |
|
|
672
658
|
| `ctx.notifyPromptListChanged` | `function \| undefined` | Always in handler ctx; delivery request-scoped |
|
|
@@ -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.4"
|
|
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();
|
|
@@ -325,6 +311,22 @@ Use `.rejects.toThrow(McpError)` to assert type only. Use `.rejects.toMatchObjec
|
|
|
325
311
|
|
|
326
312
|
---
|
|
327
313
|
|
|
314
|
+
## Output schema assertions
|
|
315
|
+
|
|
316
|
+
`expect.schemaMatching` (Vitest 4, Standard Schema) validates a value against any Zod schema — including the definition's own `output`. Use it to assert schema conformance without duplicating the shape in the test:
|
|
317
|
+
|
|
318
|
+
```ts
|
|
319
|
+
it('output conforms to the declared output schema', async () => {
|
|
320
|
+
const ctx = createMockContext();
|
|
321
|
+
const result = await myTool.handler(myTool.input.parse({ query: 'x' }), ctx);
|
|
322
|
+
expect(result).toEqual(expect.schemaMatching(myTool.output));
|
|
323
|
+
});
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
It composes as an asymmetric matcher anywhere a value is expected — e.g. `toHaveBeenCalledWith(expect.schemaMatching(schema))`. Prefer exact-value assertions when the expected output is fully known; reach for `schemaMatching` when the output is dynamic (timestamps, generated IDs) or the schema itself is the contract under test.
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
328
330
|
## Testing handlers with `errors[]` (typed contract)
|
|
329
331
|
|
|
330
332
|
Tools and resources that declare an `errors[]` contract receive a typed `ctx.fail` helper at runtime. Pass the definition's own `errors` to `createMockContext` and the mock wires `fail` the same way the production handler factory does:
|
|
@@ -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
|
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
|
@@ -114,5 +114,8 @@ ENV MCP_FORCE_CONSOLE_LOGGING="true"
|
|
|
114
114
|
# Expose the port the server listens on
|
|
115
115
|
EXPOSE ${MCP_HTTP_PORT}
|
|
116
116
|
|
|
117
|
+
# Health check using a bun-native fetch (slim image ships no curl/wget)
|
|
118
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 CMD bun -e "fetch('http://localhost:'+(process.env.MCP_HTTP_PORT??'3010')+'/healthz').then((r)=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
|
|
119
|
+
|
|
117
120
|
# The command to start the server
|
|
118
121
|
CMD ["bun", "run", "dist/index.js"]
|
package/templates/package.json
CHANGED
|
@@ -62,12 +62,12 @@
|
|
|
62
62
|
"zod": "{{ZOD_VERSION}}"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
|
-
"@biomejs/biome": "
|
|
66
|
-
"@types/node": "
|
|
65
|
+
"@biomejs/biome": "2.4.16",
|
|
66
|
+
"@types/node": "25.9.3",
|
|
67
67
|
"depcheck": "^1.4.7",
|
|
68
68
|
"ignore": "^7.0.5",
|
|
69
|
-
"tsc-alias": "^1.8.
|
|
70
|
-
"typescript": "^
|
|
71
|
-
"vitest": "^4.1.
|
|
69
|
+
"tsc-alias": "^1.8.17",
|
|
70
|
+
"typescript": "^6.0.3",
|
|
71
|
+
"vitest": "^4.1.8"
|
|
72
72
|
}
|
|
73
73
|
}
|
|
@@ -15,6 +15,13 @@ describe('echoTool', () => {
|
|
|
15
15
|
expect(result).toEqual({ message: 'hello world' });
|
|
16
16
|
});
|
|
17
17
|
|
|
18
|
+
it('output conforms to the declared output schema', async () => {
|
|
19
|
+
const ctx = createMockContext();
|
|
20
|
+
const input = echoTool.input.parse({ message: 'hello world' });
|
|
21
|
+
const result = await echoTool.handler(input, ctx);
|
|
22
|
+
expect(result).toEqual(expect.schemaMatching(echoTool.output));
|
|
23
|
+
});
|
|
24
|
+
|
|
18
25
|
it('formats output as text content', () => {
|
|
19
26
|
const blocks = echoTool.format!({ message: 'hello world' });
|
|
20
27
|
expect(blocks).toEqual([{ type: 'text', text: 'hello world' }]);
|
package/tsconfig.base.json
CHANGED
package/dist/logs/combined.log
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
{"level":40,"time":1781113411294,"env":"testing","version":"0.10.1","pid":33003,"transport":"http","requestId":"PMDCM-4FN1P","timestamp":"2026-06-10T17:43:31.292Z","operation":"TransportManager.start","component":"HttpTransportSetup","msg":"MCP_ALLOWED_ORIGINS is not set — CORS is wildcard for CLI clients; browser Origin headers are restricted to loopback. Set MCP_ALLOWED_ORIGINS for production deployments accepting remote browser origins."}
|
|
2
|
-
{"level":40,"time":1781113413300,"env":"testing","version":"0.10.1","pid":33003,"component":"HttpTransport","requestId":"EK8DV-8JP4K","timestamp":"2026-06-10T17:43:33.299Z","operation":"HttpRpcRequest","sessionId":"not-a-real-session-1781113413299","msg":"Session validation failed - invalid or hijacked session"}
|
|
3
|
-
{"level":50,"time":1781113417289,"env":"testing","version":"0.0.0-test","pid":33149,"requestId":"V1R4T-Z2KJ1","timestamp":"2026-06-10T17:43:37.288Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"0579abd213f9ae7934901e503504a120a4a5e3c311c4ba18b6e33ca85dd0880e","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"errorData":{"sessionId":"0579abd213f9ae7934901e503504a120a4a5e3c311c4ba18b6e33ca85dd0880e","toolName":"scoped_echo","requestId":"V1R4T-Z2KJ1","timestamp":"2026-06-10T17:43:37.288Z","tenantId":"authz-tenant","operation":"HandleToolRequest","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"originalErrorName":"McpError","originalMessage":"Insufficient permissions.","originalStack":"McpError: Insufficient permissions.\n at forbidden (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:84:58)\n at withRequiredScopes (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:68:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:258:17)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Insufficient permissions.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:170:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:300:26)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
|
|
4
|
-
{"level":50,"time":1781113417298,"env":"testing","version":"0.0.0-test","pid":33149,"requestId":"N8G6Q-4WDTQ","timestamp":"2026-06-10T17:43:37.298Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"831fb83a7384a13d4d5efdcc7130a8b08da435d9308b57123a97dc2f15883b20","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["openid","email","profile","offline_access"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"errorData":{"sessionId":"831fb83a7384a13d4d5efdcc7130a8b08da435d9308b57123a97dc2f15883b20","toolName":"scoped_echo","requestId":"N8G6Q-4WDTQ","timestamp":"2026-06-10T17:43:37.298Z","tenantId":"authz-tenant","operation":"HandleToolRequest","auth":{"sub":"authz-user","scopes":["openid","email","profile","offline_access"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"originalErrorName":"McpError","originalMessage":"Insufficient permissions.","originalStack":"McpError: Insufficient permissions.\n at forbidden (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:84:58)\n at withRequiredScopes (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:68:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:258:17)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Insufficient permissions.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:170:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:300:26)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
|
package/dist/logs/error.log
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
{"level":50,"time":1781113417289,"env":"testing","version":"0.0.0-test","pid":33149,"requestId":"V1R4T-Z2KJ1","timestamp":"2026-06-10T17:43:37.288Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"0579abd213f9ae7934901e503504a120a4a5e3c311c4ba18b6e33ca85dd0880e","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"errorData":{"sessionId":"0579abd213f9ae7934901e503504a120a4a5e3c311c4ba18b6e33ca85dd0880e","toolName":"scoped_echo","requestId":"V1R4T-Z2KJ1","timestamp":"2026-06-10T17:43:37.288Z","tenantId":"authz-tenant","operation":"HandleToolRequest","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"originalErrorName":"McpError","originalMessage":"Insufficient permissions.","originalStack":"McpError: Insufficient permissions.\n at forbidden (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:84:58)\n at withRequiredScopes (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:68:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:258:17)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Insufficient permissions.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:170:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:300:26)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
|
|
2
|
-
{"level":50,"time":1781113417298,"env":"testing","version":"0.0.0-test","pid":33149,"requestId":"N8G6Q-4WDTQ","timestamp":"2026-06-10T17:43:37.298Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"831fb83a7384a13d4d5efdcc7130a8b08da435d9308b57123a97dc2f15883b20","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["openid","email","profile","offline_access"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"errorData":{"sessionId":"831fb83a7384a13d4d5efdcc7130a8b08da435d9308b57123a97dc2f15883b20","toolName":"scoped_echo","requestId":"N8G6Q-4WDTQ","timestamp":"2026-06-10T17:43:37.298Z","tenantId":"authz-tenant","operation":"HandleToolRequest","auth":{"sub":"authz-user","scopes":["openid","email","profile","offline_access"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"originalErrorName":"McpError","originalMessage":"Insufficient permissions.","originalStack":"McpError: Insufficient permissions.\n at forbidden (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:84:58)\n at withRequiredScopes (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:68:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:258:17)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Insufficient permissions.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:170:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:300:26)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
|
|
File without changes
|
|
@@ -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"}
|