@cyanheads/mcp-ts-core 0.11.0 → 0.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +9 -5
- package/CLAUDE.md +9 -5
- package/README.md +4 -2
- package/biome.json +2 -2
- package/changelog/0.11.x/0.11.0.md +0 -2
- package/changelog/0.11.x/0.11.1.md +74 -0
- package/changelog/0.11.x/0.11.2.md +98 -0
- package/dist/config/index.d.ts +10 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +27 -13
- package/dist/config/index.js.map +1 -1
- package/dist/core/app.d.ts +37 -14
- package/dist/core/app.d.ts.map +1 -1
- package/dist/core/app.js +56 -23
- package/dist/core/app.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/worker.d.ts.map +1 -1
- package/dist/core/worker.js +35 -18
- package/dist/core/worker.js.map +1 -1
- package/dist/mcp-server/elicitation.d.ts +64 -0
- package/dist/mcp-server/elicitation.d.ts.map +1 -0
- package/dist/mcp-server/elicitation.js +81 -0
- package/dist/mcp-server/elicitation.js.map +1 -0
- package/dist/mcp-server/protocolSession.d.ts +34 -0
- package/dist/mcp-server/protocolSession.d.ts.map +1 -0
- package/dist/mcp-server/protocolSession.js +13 -0
- package/dist/mcp-server/protocolSession.js.map +1 -0
- package/dist/mcp-server/resources/resource-registration.d.ts +2 -1
- package/dist/mcp-server/resources/resource-registration.d.ts.map +1 -1
- package/dist/mcp-server/resources/resource-registration.js +6 -2
- package/dist/mcp-server/resources/resource-registration.js.map +1 -1
- package/dist/mcp-server/resources/utils/resourceHandlerFactory.d.ts +7 -12
- package/dist/mcp-server/resources/utils/resourceHandlerFactory.d.ts.map +1 -1
- package/dist/mcp-server/resources/utils/resourceHandlerFactory.js +24 -31
- package/dist/mcp-server/resources/utils/resourceHandlerFactory.js.map +1 -1
- package/dist/mcp-server/server.d.ts +3 -0
- package/dist/mcp-server/server.d.ts.map +1 -1
- package/dist/mcp-server/server.js +12 -2
- package/dist/mcp-server/server.js.map +1 -1
- package/dist/mcp-server/tools/tool-registration.d.ts +2 -1
- package/dist/mcp-server/tools/tool-registration.d.ts.map +1 -1
- package/dist/mcp-server/tools/tool-registration.js +6 -2
- package/dist/mcp-server/tools/tool-registration.js.map +1 -1
- package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts +7 -13
- package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts.map +1 -1
- package/dist/mcp-server/tools/utils/toolHandlerFactory.js +11 -44
- package/dist/mcp-server/tools/utils/toolHandlerFactory.js.map +1 -1
- package/dist/mcp-server/transports/http/httpErrorHandler.js +1 -1
- package/dist/mcp-server/transports/http/httpErrorHandler.js.map +1 -1
- package/dist/mcp-server/transports/http/httpServer.d.ts +2 -2
- package/dist/mcp-server/transports/http/httpServer.d.ts.map +1 -1
- package/dist/mcp-server/transports/http/httpServer.js.map +1 -1
- package/dist/mcp-server/transports/http/httpTransport.d.ts +2 -2
- package/dist/mcp-server/transports/http/httpTransport.d.ts.map +1 -1
- package/dist/mcp-server/transports/http/httpTransport.js +113 -55
- package/dist/mcp-server/transports/http/httpTransport.js.map +1 -1
- package/dist/mcp-server/transports/http/sessionStore.d.ts +17 -0
- package/dist/mcp-server/transports/http/sessionStore.d.ts.map +1 -1
- package/dist/mcp-server/transports/http/sessionStore.js +71 -0
- package/dist/mcp-server/transports/http/sessionStore.js.map +1 -1
- package/dist/mcp-server/transports/manager.d.ts +2 -6
- package/dist/mcp-server/transports/manager.d.ts.map +1 -1
- package/dist/mcp-server/transports/manager.js +4 -0
- package/dist/mcp-server/transports/manager.js.map +1 -1
- package/dist/services/canvas/core/CanvasRegistry.d.ts +2 -2
- package/dist/services/canvas/core/CanvasRegistry.d.ts.map +1 -1
- package/dist/services/canvas/core/CanvasRegistry.js +14 -3
- package/dist/services/canvas/core/CanvasRegistry.js.map +1 -1
- package/dist/services/canvas/core/DataCanvas.d.ts +1 -1
- package/dist/services/canvas/core/DataCanvas.d.ts.map +1 -1
- package/dist/services/canvas/core/DataCanvas.js +2 -2
- package/dist/services/canvas/core/DataCanvas.js.map +1 -1
- package/dist/services/canvas/providers/duckdb/DuckdbProvider.d.ts.map +1 -1
- package/dist/services/canvas/providers/duckdb/DuckdbProvider.js +11 -3
- package/dist/services/canvas/providers/duckdb/DuckdbProvider.js.map +1 -1
- package/dist/services/canvas/providers/duckdb/exportWriter.d.ts.map +1 -1
- package/dist/services/canvas/providers/duckdb/exportWriter.js +23 -2
- package/dist/services/canvas/providers/duckdb/exportWriter.js.map +1 -1
- package/dist/services/mirror/core/defineMirror.d.ts.map +1 -1
- package/dist/services/mirror/core/defineMirror.js +18 -7
- package/dist/services/mirror/core/defineMirror.js.map +1 -1
- package/dist/services/mirror/core/runner.d.ts.map +1 -1
- package/dist/services/mirror/core/runner.js +3 -0
- package/dist/services/mirror/core/runner.js.map +1 -1
- package/dist/services/mirror/index.d.ts +1 -1
- package/dist/services/mirror/index.d.ts.map +1 -1
- package/dist/services/mirror/index.js.map +1 -1
- package/dist/services/mirror/sqlite/sqliteMirrorStore.d.ts +20 -0
- package/dist/services/mirror/sqlite/sqliteMirrorStore.d.ts.map +1 -1
- package/dist/services/mirror/sqlite/sqliteMirrorStore.js +87 -8
- package/dist/services/mirror/sqlite/sqliteMirrorStore.js.map +1 -1
- package/dist/storage/core/StorageService.d.ts.map +1 -1
- package/dist/storage/core/StorageService.js +4 -1
- package/dist/storage/core/StorageService.js.map +1 -1
- package/dist/storage/core/storageValidation.d.ts +8 -0
- package/dist/storage/core/storageValidation.d.ts.map +1 -1
- package/dist/storage/core/storageValidation.js +38 -0
- package/dist/storage/core/storageValidation.js.map +1 -1
- package/dist/storage/providers/inMemory/inMemoryProvider.d.ts +1 -1
- package/dist/storage/providers/inMemory/inMemoryProvider.d.ts.map +1 -1
- package/dist/storage/providers/inMemory/inMemoryProvider.js +67 -25
- package/dist/storage/providers/inMemory/inMemoryProvider.js.map +1 -1
- package/dist/testing/fuzz.d.ts +5 -1
- package/dist/testing/fuzz.d.ts.map +1 -1
- package/dist/testing/fuzz.js +147 -37
- package/dist/testing/fuzz.js.map +1 -1
- package/dist/testing/index.d.ts +111 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +139 -0
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/vitest.d.ts +86 -12
- package/dist/testing/vitest.d.ts.map +1 -1
- package/dist/testing/vitest.js +91 -11
- package/dist/testing/vitest.js.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/network/fetchWithTimeout.d.ts +7 -6
- package/dist/utils/network/fetchWithTimeout.d.ts.map +1 -1
- package/dist/utils/network/fetchWithTimeout.js +141 -51
- package/dist/utils/network/fetchWithTimeout.js.map +1 -1
- package/dist/utils/network/httpError.d.ts.map +1 -1
- package/dist/utils/network/httpError.js +3 -2
- package/dist/utils/network/httpError.js.map +1 -1
- package/dist/utils/network/responseBody.d.ts +15 -0
- package/dist/utils/network/responseBody.d.ts.map +1 -0
- package/dist/utils/network/responseBody.js +63 -0
- package/dist/utils/network/responseBody.js.map +1 -0
- package/dist/utils/parsing/frontmatterParser.d.ts +3 -1
- package/dist/utils/parsing/frontmatterParser.d.ts.map +1 -1
- package/dist/utils/parsing/frontmatterParser.js +6 -7
- package/dist/utils/parsing/frontmatterParser.js.map +1 -1
- package/dist/utils/parsing/htmlExtractor.d.ts +4 -0
- package/dist/utils/parsing/htmlExtractor.d.ts.map +1 -1
- package/dist/utils/parsing/htmlExtractor.js +4 -5
- package/dist/utils/parsing/htmlExtractor.js.map +1 -1
- package/dist/utils/parsing/index.d.ts +1 -0
- package/dist/utils/parsing/index.d.ts.map +1 -1
- package/dist/utils/parsing/index.js +1 -0
- package/dist/utils/parsing/index.js.map +1 -1
- package/dist/utils/parsing/inputBudget.d.ts +28 -0
- package/dist/utils/parsing/inputBudget.d.ts.map +1 -0
- package/dist/utils/parsing/inputBudget.js +76 -0
- package/dist/utils/parsing/inputBudget.js.map +1 -0
- package/dist/utils/parsing/jsonParser.d.ts +3 -1
- package/dist/utils/parsing/jsonParser.d.ts.map +1 -1
- package/dist/utils/parsing/jsonParser.js +5 -6
- package/dist/utils/parsing/jsonParser.js.map +1 -1
- package/dist/utils/parsing/pdfParser.d.ts +11 -3
- package/dist/utils/parsing/pdfParser.d.ts.map +1 -1
- package/dist/utils/parsing/pdfParser.js +65 -50
- package/dist/utils/parsing/pdfParser.js.map +1 -1
- package/dist/utils/parsing/xmlParser.d.ts +3 -1
- package/dist/utils/parsing/xmlParser.d.ts.map +1 -1
- package/dist/utils/parsing/xmlParser.js +8 -6
- package/dist/utils/parsing/xmlParser.js.map +1 -1
- package/dist/utils/parsing/yamlParser.d.ts +3 -1
- package/dist/utils/parsing/yamlParser.d.ts.map +1 -1
- package/dist/utils/parsing/yamlParser.js +5 -6
- package/dist/utils/parsing/yamlParser.js.map +1 -1
- package/package.json +28 -26
- package/skills/api-testing/SKILL.md +65 -4
- package/skills/git-wrapup/SKILL.md +22 -1
- package/skills/security-pass/SKILL.md +2 -2
- package/templates/devcheck.config.json +1 -1
- package/templates/package.json +2 -0
- package/templates/tests/fuzz/echo-tool.fuzz.test.ts +20 -0
- package/templates/tests/integration/echo-contract.int.test.ts +25 -0
- package/templates/tests/smoke/definitions.smoke.test.ts +46 -0
- package/templates/vitest.config.ts +24 -27
- package/vitest.config.base.mjs +6 -0
- package/dist/logs/combined.log +0 -8
- package/dist/logs/error.log +0 -4
- package/dist/logs/interactions.log +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyanheads/mcp-ts-core",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.2",
|
|
4
4
|
"mcpName": "io.github.cyanheads/mcp-ts-core",
|
|
5
5
|
"description": "Agent-native TypeScript framework for building MCP servers. Declarative definitions with auth, multi-backend storage, OpenTelemetry, and first-class support for Bun/Node/Cloudflare Workers.",
|
|
6
6
|
"files": [
|
|
@@ -172,10 +172,12 @@
|
|
|
172
172
|
"tree": "bun run scripts/tree.ts",
|
|
173
173
|
"fetch-spec": "bun run scripts/fetch-openapi-spec.ts",
|
|
174
174
|
"test": "bunx vitest run",
|
|
175
|
-
"test:all": "bun run test:coverage && bun run test:node && bun run test:worker && bun run test:integration",
|
|
175
|
+
"test:all": "bun run rebuild && bun run test:coverage && bun run test:node && bun run test:worker && bun run test:integration",
|
|
176
176
|
"test:integration": "bunx vitest run --config vitest.integration.ts",
|
|
177
177
|
"test:leaks": "bunx vitest run --detect-async-leaks",
|
|
178
178
|
"test:node": "sh -c 'NODE_BIN=$(which -a node | grep -v /bun-node- | head -n 1); \"$NODE_BIN\" ./node_modules/vitest/vitest.mjs run && \"$NODE_BIN\" ./node_modules/vitest/vitest.mjs run --config vitest.integration.ts'",
|
|
179
|
+
"test:order": "sh -c 'NODE_BIN=$(which -a node | grep -v /bun-node- | head -n 1); \"$NODE_BIN\" ./node_modules/vitest/vitest.mjs run --sequence.shuffle --sequence.seed=20260809 --expect.requireAssertions'",
|
|
180
|
+
"test:package": "bun run rebuild && bunx vitest run --config vitest.package.ts",
|
|
179
181
|
"test:unit": "bunx vitest run --project unit",
|
|
180
182
|
"test:smoke": "bunx vitest run --project smoke",
|
|
181
183
|
"test:fuzz": "bunx vitest run --project fuzz",
|
|
@@ -193,57 +195,57 @@
|
|
|
193
195
|
"publish-mcp": "mcp-publisher login github -token \"$(security find-generic-password -a \"$USER\" -s mcp-publisher-github-pat -w)\" && mcp-publisher publish"
|
|
194
196
|
},
|
|
195
197
|
"devDependencies": {
|
|
196
|
-
"@biomejs/biome": "2.5.
|
|
197
|
-
"@cloudflare/vitest-pool-workers": "^0.
|
|
198
|
-
"@cloudflare/workers-types": "5.
|
|
199
|
-
"@duckdb/node-api": "^1.5.5-r.
|
|
198
|
+
"@biomejs/biome": "2.5.6",
|
|
199
|
+
"@cloudflare/vitest-pool-workers": "^0.19.1",
|
|
200
|
+
"@cloudflare/workers-types": "5.20260730.1",
|
|
201
|
+
"@duckdb/node-api": "^1.5.5-r.3",
|
|
200
202
|
"@hono/otel": "^1.1.2",
|
|
201
203
|
"@opentelemetry/exporter-metrics-otlp-http": "^0.221.0",
|
|
202
204
|
"@opentelemetry/exporter-trace-otlp-http": "^0.221.0",
|
|
203
205
|
"@opentelemetry/instrumentation-http": "^0.221.0",
|
|
204
|
-
"@opentelemetry/instrumentation-pino": "^0.
|
|
206
|
+
"@opentelemetry/instrumentation-pino": "^0.67.0",
|
|
205
207
|
"@opentelemetry/resources": "^2.10.0",
|
|
206
208
|
"@opentelemetry/sdk-metrics": "^2.10.0",
|
|
207
209
|
"@opentelemetry/sdk-node": "^0.221.0",
|
|
208
210
|
"@opentelemetry/sdk-trace-node": "^2.10.0",
|
|
209
211
|
"@opentelemetry/semantic-conventions": "^1.43.0",
|
|
210
212
|
"@socketsecurity/bun-security-scanner": "^1.1.2",
|
|
211
|
-
"@supabase/supabase-js": "^2.
|
|
213
|
+
"@supabase/supabase-js": "^2.112.2",
|
|
212
214
|
"@types/bun": "^1.3.14",
|
|
213
|
-
"@types/node": "26.1.
|
|
215
|
+
"@types/node": "26.1.2",
|
|
214
216
|
"@types/papaparse": "^5.5.2",
|
|
215
217
|
"@types/sanitize-html": "^2.16.1",
|
|
216
218
|
"@types/validator": "^13.15.10",
|
|
217
219
|
"@vitest/coverage-istanbul": "4.1.10",
|
|
218
220
|
"@vitest/ui": "4.1.10",
|
|
219
|
-
"better-sqlite3": "^13.0.
|
|
221
|
+
"better-sqlite3": "^13.0.3",
|
|
220
222
|
"bun-types": "^1.3.14",
|
|
221
223
|
"chrono-node": "^2.10.1",
|
|
222
|
-
"clipboardy": "^5.3.
|
|
224
|
+
"clipboardy": "^5.3.2",
|
|
223
225
|
"defuddle": "^0.19.2",
|
|
224
226
|
"depcheck": "^1.4.7",
|
|
225
227
|
"diff": "^9.0.0",
|
|
226
|
-
"execa": "^10.0.
|
|
228
|
+
"execa": "^10.0.1",
|
|
227
229
|
"fast-check": "^4.9.0",
|
|
228
230
|
"fast-xml-parser": "^5.10.1",
|
|
229
231
|
"ignore": "^7.0.6",
|
|
230
|
-
"js-yaml": "^5.2.
|
|
232
|
+
"js-yaml": "^5.2.3",
|
|
231
233
|
"linkedom": "^0.18.13",
|
|
232
234
|
"node-cron": "^4.6.0",
|
|
233
|
-
"openai": "^6.
|
|
235
|
+
"openai": "^6.49.0",
|
|
234
236
|
"papaparse": "^5.5.4",
|
|
235
237
|
"partial-json": "^0.1.7",
|
|
236
238
|
"pdf-lib": "^1.17.1",
|
|
237
239
|
"pino-pretty": "^13.1.3",
|
|
238
|
-
"repomix": "^1.
|
|
240
|
+
"repomix": "^1.18.0",
|
|
239
241
|
"sanitize-html": "^2.17.6",
|
|
240
242
|
"tsc-alias": "^1.9.1",
|
|
241
243
|
"typedoc": "^0.28.20",
|
|
242
244
|
"typescript": "^7.0.2",
|
|
243
245
|
"typescript-v6": "npm:typescript@^6.0.3",
|
|
244
|
-
"unpdf": "^1.
|
|
246
|
+
"unpdf": "^1.8.0",
|
|
245
247
|
"validator": "^13.15.35",
|
|
246
|
-
"vite": "8.
|
|
248
|
+
"vite": "8.2.0",
|
|
247
249
|
"vitest": "^4.1.10"
|
|
248
250
|
},
|
|
249
251
|
"keywords": [
|
|
@@ -297,13 +299,13 @@
|
|
|
297
299
|
},
|
|
298
300
|
"dependencies": {
|
|
299
301
|
"@hono/mcp": "^0.3.1",
|
|
300
|
-
"@hono/node-server": "^2.0
|
|
301
|
-
"@modelcontextprotocol/ext-apps": "^1.7.
|
|
302
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
302
|
+
"@hono/node-server": "^2.1.0",
|
|
303
|
+
"@modelcontextprotocol/ext-apps": "^1.7.5",
|
|
304
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
303
305
|
"@opentelemetry/api": "^1.9.1",
|
|
304
306
|
"dotenv": "^17.4.2",
|
|
305
|
-
"hono": "^4.
|
|
306
|
-
"jose": "^6.2.
|
|
307
|
+
"hono": "^4.13.1",
|
|
308
|
+
"jose": "^6.2.8",
|
|
307
309
|
"pino": "^10.3.1",
|
|
308
310
|
"zod": "^4.4.3"
|
|
309
311
|
},
|
|
@@ -429,11 +431,11 @@
|
|
|
429
431
|
}
|
|
430
432
|
},
|
|
431
433
|
"overrides": {
|
|
432
|
-
"brace-expansion": "^2.1.
|
|
433
|
-
"postcss": "^8.5.
|
|
434
|
+
"brace-expansion": "^2.1.3",
|
|
435
|
+
"postcss": "^8.5.23",
|
|
434
436
|
"protobufjs": "^7.6.5",
|
|
435
437
|
"sharp": "^0.35.3",
|
|
436
|
-
"fast-uri": "^3.1.
|
|
437
|
-
"
|
|
438
|
+
"fast-uri": "^3.1.5",
|
|
439
|
+
"ip-address": "^10.3.1"
|
|
438
440
|
}
|
|
439
441
|
}
|
|
@@ -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.6"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -13,7 +13,7 @@ metadata:
|
|
|
13
13
|
|
|
14
14
|
Tests target handler behavior directly — call `handler(input, ctx)`, assert on the return value or thrown error. The framework's handler factory (try/catch, formatting, telemetry) is not involved. Use `createMockContext` from `@cyanheads/mcp-ts-core/testing` to construct the `ctx` argument.
|
|
15
15
|
|
|
16
|
-
**Additional exports from `/testing`:** `
|
|
16
|
+
**Additional exports from `/testing`:** `createMockSession()` binds a mock handler context to an HTTP session; `createFetchMock()` provides a strict upstream HTTP fake; `runToolContract()` executes a definition through schema, handler, formatting, enrichment/content, and production-shaped error-envelope checks. `createMockLogger()` returns a standalone `MockContextLogger`, and `createInMemoryStorage(options?)` provides a real `StorageService` backed by `InMemoryProvider`.
|
|
17
17
|
|
|
18
18
|
**Philosophy:** Test behavior, not implementation. Refactors should not break tests. Match the repo's existing test layout: fresh scaffolds use `tests/`, while colocated `src/**/*.test.ts` files are also supported. Integration tests at I/O boundaries over unit tests of internals.
|
|
19
19
|
|
|
@@ -21,7 +21,7 @@ Tests target handler behavior directly — call `handler(input, ctx)`, assert on
|
|
|
21
21
|
|
|
22
22
|
## `mcpTest` — fixture-based Vitest test
|
|
23
23
|
|
|
24
|
-
`mcpTest` is a `test.extend`-based Vitest test that provides `ctx` and `storage` as **per-test fixtures** — fresh instances for every test, eliminating
|
|
24
|
+
`mcpTest` is a `test.extend`-based Vitest test that provides `ctx`, `session`, `fetchMock`, and `storage` as **per-test fixtures** — fresh instances for every test, eliminating boilerplate and enforcing isolation automatically. `fetchMock` is installed as `globalThis.fetch` only when requested by a test and restored afterward.
|
|
25
25
|
|
|
26
26
|
```ts
|
|
27
27
|
import { mcpTest } from '@cyanheads/mcp-ts-core/testing/vitest';
|
|
@@ -36,6 +36,14 @@ mcpTest('uses storage fixture', async ({ ctx, storage }) => {
|
|
|
36
36
|
const result = await svc.doWork(ctx);
|
|
37
37
|
expect(result).toBeDefined();
|
|
38
38
|
});
|
|
39
|
+
|
|
40
|
+
mcpTest('stubs an upstream HTTP boundary', async ({ fetchMock }) => {
|
|
41
|
+
fetchMock.route({
|
|
42
|
+
match: 'https://api.example.test/items/42',
|
|
43
|
+
respond: Response.json({ id: '42' }),
|
|
44
|
+
});
|
|
45
|
+
await expect(loadItem('42')).resolves.toMatchObject({ id: '42' });
|
|
46
|
+
});
|
|
39
47
|
```
|
|
40
48
|
|
|
41
49
|
### Fixtures
|
|
@@ -43,6 +51,8 @@ mcpTest('uses storage fixture', async ({ ctx, storage }) => {
|
|
|
43
51
|
| Fixture | Type | Per-test? | Notes |
|
|
44
52
|
|:--------|:-----|:----------|:------|
|
|
45
53
|
| `ctx` | `Context` | Yes | Fresh `createMockContext()` each test |
|
|
54
|
+
| `session` | `MockSession` | Yes | Fresh `{ sessionId, tenantId?, ctx }` from `createMockSession()` |
|
|
55
|
+
| `fetchMock` | `FetchMockHarness` | Yes | Strict fetch fake installed/restored around the requesting test |
|
|
46
56
|
| `storage` | `StorageService` | Yes | Fresh `createInMemoryStorage()` each test |
|
|
47
57
|
|
|
48
58
|
### Extending with the function form
|
|
@@ -61,7 +71,58 @@ const tenantTest = mcpTest.extend({
|
|
|
61
71
|
// const tenantTest = mcpTest.extend({ ctx: createMockContext({ tenantId: 'test-tenant' }) });
|
|
62
72
|
```
|
|
63
73
|
|
|
64
|
-
|
|
74
|
+
The portable `/testing` helpers are re-exported from `@cyanheads/mcp-ts-core/testing/vitest` so fixture overrides don't need a second import.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Upstream HTTP testing with `createFetchMock`
|
|
79
|
+
|
|
80
|
+
Use the fetch harness at real outbound I/O boundaries. Stub the external service, not server-owned services or handlers.
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
import { createFetchMock } from '@cyanheads/mcp-ts-core/testing';
|
|
84
|
+
|
|
85
|
+
const http = createFetchMock([
|
|
86
|
+
{
|
|
87
|
+
method: 'GET',
|
|
88
|
+
match: 'https://api.example.test/items/42',
|
|
89
|
+
respond: Response.json({ id: '42', name: 'Example' }),
|
|
90
|
+
},
|
|
91
|
+
]);
|
|
92
|
+
|
|
93
|
+
http.install();
|
|
94
|
+
try {
|
|
95
|
+
await expect(loadItem('42')).resolves.toEqual({ id: '42', name: 'Example' });
|
|
96
|
+
expect(http.calls[0]?.request.url).toBe('https://api.example.test/items/42');
|
|
97
|
+
} finally {
|
|
98
|
+
http.restore();
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Routes match in registration order. `match` accepts an exact URL, `RegExp`, or request predicate; `respond` accepts a clonable `Response` or response factory. Set `once: true` for one-shot behavior. Unmatched requests throw unless `onUnhandled` is provided.
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Tool conformance with `toolContractSuite`
|
|
107
|
+
|
|
108
|
+
Point the reusable suite at a definition plus representative success and failure inputs. It checks input/output schemas, invokes the real handler, applies formatting/enrichment/content, and validates both public error surfaces.
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
112
|
+
import { toolContractSuite } from '@cyanheads/mcp-ts-core/testing/vitest';
|
|
113
|
+
|
|
114
|
+
toolContractSuite(searchTool, {
|
|
115
|
+
success: [{ name: 'returns matches', input: { query: 'mcp' } }],
|
|
116
|
+
errors: [{
|
|
117
|
+
name: 'reports an empty query',
|
|
118
|
+
input: { query: '' },
|
|
119
|
+
code: JsonRpcErrorCode.InvalidParams,
|
|
120
|
+
reason: 'empty_query',
|
|
121
|
+
}],
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Use `runToolContract(definition, input, { context })` from `/testing` when a custom test runner or an imperative assertion is a better fit. It intentionally skips transport auth and telemetry; those belong in transport/integration tests.
|
|
65
126
|
|
|
66
127
|
---
|
|
67
128
|
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Land working-tree changes as logical commits — the work grouped by concern, topped by a release commit (version bump, changelog, regenerated artifacts) and an annotated tag. Verify, commit, tag. Stops at "committed and tagged locally" — no push, no publish. The release-and-publish skill picks up from here. Distilled from the git_wrapup_instructions protocol.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.9"
|
|
8
8
|
audience: external
|
|
9
9
|
type: workflow
|
|
10
10
|
---
|
|
@@ -95,6 +95,8 @@ security: false # true ONLY for a security fix in this server's own source
|
|
|
95
95
|
---
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
+
**Write `summary:` LAST, derived from the body you just wrote — never independently.** It is the line most readers see, and it propagates unedited to three further surfaces: the `CHANGELOG.md` rollup, the GitHub Release body, and the annotated tag (which cannot be edited once pushed). Written from recollection rather than from the body, it reliably names a mechanism that was never built or a target that was never fixed, while the body beside it stays correct. After writing it, re-read the body and confirm every claim in the summary appears there.
|
|
99
|
+
|
|
98
100
|
**`security:` is a source-code signal — not a dependency-CVE signal.** Set `security: true` only when this release fixes a vulnerability or adds hardening in code *this server ships*. A dependency or transitive CVE bump — even one that clears an advisory (`bun audit` going 1 → 0) — is routine maintenance: record it under `## Dependencies` with the advisory ID and leave the flag `false`. The `🛡️ Security` badge answers "does the server itself have a vuln"; a dep bump must not trip it.
|
|
99
101
|
|
|
100
102
|
**Body:** Section order follows Keep a Changelog — Added / Changed / Deprecated / Removed / Fixed / Security. Include only sections with entries. Delete empty sections.
|
|
@@ -142,6 +144,24 @@ git commit -m "<subject>"
|
|
|
142
144
|
- Work commits (no version): `feat: hosted server endpoint`, `fix: handle empty SPARQL result sets`, `feat(linter): enrichment contract rules`, `docs: document the enrichment block`
|
|
143
145
|
- Release commit (subject leads with the version): `chore(release): 0.2.1 — empty SPARQL result handling`
|
|
144
146
|
|
|
147
|
+
**Body: every commit has one, and it is one or two lines.** Uniform across the stack — no commit ships subject-only, none ships a paragraph. One sentence stating the *why* or the load-bearing constraint, a second only if the first genuinely cannot carry it. Two lines is the hard ceiling.
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
fix: handle empty SPARQL result sets
|
|
151
|
+
|
|
152
|
+
Upstream returns 200 with an empty bindings array rather than 404.
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Never put a closing keyword in a commit body.** `Fixes #N`, `Closes #N`, `Resolves #N` and friends close the issue the moment the commit is pushed — before the close-out comment recording what shipped, so the issue closes with no account of the fix. Reference issues as bare `(#N)` backlinks in the subject or body; closing is a deliberate later step.
|
|
156
|
+
|
|
157
|
+
A body is too long the moment it:
|
|
158
|
+
- enumerates the files, subsystems, or symbols touched — that is `git show --stat`
|
|
159
|
+
- walks through how the implementation works — that is the code
|
|
160
|
+
- narrates a fix's mechanism across multiple sentences — that is the changelog entry
|
|
161
|
+
- runs to a second paragraph, ever
|
|
162
|
+
|
|
163
|
+
The changelog carries the depth, the tag carries the headline, the commit carries one line of why. When the body wants to grow, that pressure is telling you the content belongs in the changelog entry.
|
|
164
|
+
|
|
145
165
|
**Rules:**
|
|
146
166
|
- Plain `-m` flag only — no heredoc, no command substitution
|
|
147
167
|
- No `Co-authored-by` or `Generated with` trailers
|
|
@@ -218,6 +238,7 @@ If the working tree isn't clean or the tag doesn't point at HEAD, something went
|
|
|
218
238
|
- [ ] `bun run devcheck` passes
|
|
219
239
|
- [ ] `bun run test:all` (or `test`) passes
|
|
220
240
|
- [ ] Work grouped into logical commits (large features split by layer); release artifacts (version + changelog + tree) committed separately on top, subject leading with the version
|
|
241
|
+
- [ ] Every commit carries a body, and every body is one or two lines — none subject-only, none a paragraph
|
|
221
242
|
- [ ] Annotated tag `v<version>` with structured markdown message, final line linking this version's changelog file
|
|
222
243
|
- [ ] Working tree clean
|
|
223
244
|
- [ ] Nothing pushed — local only
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
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.6"
|
|
8
8
|
audience: external
|
|
9
9
|
type: audit
|
|
10
10
|
---
|
|
@@ -122,7 +122,7 @@ grep -rn "ctx.elicit" src/mcp-server/tools/definitions/
|
|
|
122
122
|
**Check:**
|
|
123
123
|
|
|
124
124
|
- Each destructive handler calls `ctx.elicit` before the side effect?
|
|
125
|
-
- Fallback when
|
|
125
|
+
- Fallback when elicit is unavailable — **proceeds on the tool annotations, and says so in the tool description?** Do NOT require a refusal here: `ctx.elicit` is `undefined` on *every* Streamable HTTP request (the transport builds a fresh `McpServer` per request, so the SDK never records the client's capabilities — mcp-ts-core#312), so a refusing fallback makes the gated tools permanently unusable on the hosted transport. That is an outage, not a hardening. What IS in scope: a **declined or unparseable** elicit response must never proceed, and any doc claiming the gate protects "clients that support elicitation" must name the HTTP limitation or it overstates the control.
|
|
126
126
|
- Elicit **response** validated against a Zod schema before use? The returned payload is LLM-mediated, not user-direct — "user confirmed" does not mean "user authored these exact fields."
|
|
127
127
|
- Consent is scoped to the specific target (e.g., record ID rendered in the prompt), not a generic "proceed?"
|
|
128
128
|
|
package/templates/package.json
CHANGED
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"changelog:check": "bun run scripts/build-changelog.ts --check",
|
|
37
37
|
"release:github": "bun run scripts/release-github.ts",
|
|
38
38
|
"test": "vitest run",
|
|
39
|
+
"test:coverage": "vitest run --coverage",
|
|
39
40
|
"start": "node dist/index.js",
|
|
40
41
|
"start:stdio": "MCP_TRANSPORT_TYPE=stdio node dist/index.js",
|
|
41
42
|
"start:http": "MCP_TRANSPORT_TYPE=http node dist/index.js"
|
|
@@ -67,6 +68,7 @@
|
|
|
67
68
|
"@biomejs/biome": "2.5.5",
|
|
68
69
|
"@socketsecurity/bun-security-scanner": "^1.1.2",
|
|
69
70
|
"@types/node": "26.1.1",
|
|
71
|
+
"@vitest/coverage-istanbul": "4.1.10",
|
|
70
72
|
"depcheck": "^1.4.7",
|
|
71
73
|
"ignore": "^7.0.6",
|
|
72
74
|
"tsc-alias": "^1.9.1",
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Property-based fuzz coverage for the scaffolded echo tool.
|
|
3
|
+
* @module tests/fuzz/echo-tool.fuzz.test
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { expect, it } from 'vitest';
|
|
7
|
+
import { fuzzTool } from '@cyanheads/mcp-ts-core/testing/fuzz';
|
|
8
|
+
import { echoTool } from '@/mcp-server/tools/definitions/echo.tool.js';
|
|
9
|
+
|
|
10
|
+
it('keeps the echo tool safe across generated and adversarial inputs', async () => {
|
|
11
|
+
const report = await fuzzTool(echoTool, {
|
|
12
|
+
numRuns: 50,
|
|
13
|
+
numAdversarial: 30,
|
|
14
|
+
seed: 20_260_802,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
expect(report.crashes).toHaveLength(0);
|
|
18
|
+
expect(report.leaks).toHaveLength(0);
|
|
19
|
+
expect(report.prototypePollution).toBe(false);
|
|
20
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Tool-contract integration coverage for the scaffolded echo tool.
|
|
3
|
+
* @module tests/integration/echo-contract.int.test
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
7
|
+
import { toolContractSuite } from '@cyanheads/mcp-ts-core/testing/vitest';
|
|
8
|
+
import { echoTool } from '@/mcp-server/tools/definitions/echo.tool.js';
|
|
9
|
+
|
|
10
|
+
toolContractSuite(echoTool, {
|
|
11
|
+
success: [
|
|
12
|
+
{
|
|
13
|
+
name: 'validates, invokes, and formats a successful call',
|
|
14
|
+
input: { message: 'integration' },
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
errors: [
|
|
18
|
+
{
|
|
19
|
+
name: 'returns the declared dual-surface error envelope',
|
|
20
|
+
input: { message: ' ' },
|
|
21
|
+
code: JsonRpcErrorCode.InvalidParams,
|
|
22
|
+
reason: 'empty_message',
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Smoke coverage for every definition shipped by the scaffold.
|
|
3
|
+
* @module tests/smoke/definitions.smoke.test
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, expect, it } from 'vitest';
|
|
7
|
+
import { createMockContext } from '@cyanheads/mcp-ts-core/testing';
|
|
8
|
+
import { echoPrompt } from '@/mcp-server/prompts/definitions/echo.prompt.js';
|
|
9
|
+
import { echoAppUiResource } from '@/mcp-server/resources/definitions/echo-app-ui.app-resource.js';
|
|
10
|
+
import { echoResource } from '@/mcp-server/resources/definitions/echo.resource.js';
|
|
11
|
+
import { echoAppTool } from '@/mcp-server/tools/definitions/echo-app.app-tool.js';
|
|
12
|
+
import { echoTool } from '@/mcp-server/tools/definitions/echo.tool.js';
|
|
13
|
+
|
|
14
|
+
describe('scaffold definition smoke test', () => {
|
|
15
|
+
it('executes the shipped tool, resource, and prompt definitions', async () => {
|
|
16
|
+
const ctx = createMockContext();
|
|
17
|
+
const toolResult = await echoTool.handler(
|
|
18
|
+
echoTool.input.parse({ message: 'smoke' }),
|
|
19
|
+
ctx,
|
|
20
|
+
);
|
|
21
|
+
const resourceResult = await echoResource.handler(
|
|
22
|
+
echoResource.params.parse({ message: 'smoke' }),
|
|
23
|
+
ctx,
|
|
24
|
+
);
|
|
25
|
+
const promptMessages = echoPrompt.generate(echoPrompt.args.parse({ message: 'smoke' }));
|
|
26
|
+
const appResult = await echoAppTool.handler(
|
|
27
|
+
echoAppTool.input.parse({ message: 'smoke app' }),
|
|
28
|
+
ctx,
|
|
29
|
+
);
|
|
30
|
+
const appContent = echoAppTool.format?.(appResult);
|
|
31
|
+
const appHtml = await echoAppUiResource.handler(
|
|
32
|
+
echoAppUiResource.params.parse({}),
|
|
33
|
+
createMockContext({ uri: new URL('ui://template-echo-app/app.html') }),
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
expect(toolResult).toEqual({ message: 'smoke' });
|
|
37
|
+
expect(resourceResult).toEqual({ message: 'smoke' });
|
|
38
|
+
expect(promptMessages).toEqual([
|
|
39
|
+
{ role: 'user', content: { type: 'text', text: 'Echo: smoke' } },
|
|
40
|
+
]);
|
|
41
|
+
expect(appResult).toEqual(expect.schemaMatching(echoAppTool.output));
|
|
42
|
+
expect(appContent?.[0]).toEqual({ type: 'text', text: JSON.stringify(appResult) });
|
|
43
|
+
expect(appHtml).toContain('<title>Echo App</title>');
|
|
44
|
+
expect(appHtml).toContain('app.callServerTool');
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -25,33 +25,30 @@ export default mergeConfig(
|
|
|
25
25
|
exclude: ['tests/smoke/**', 'tests/integration/**', 'tests/fuzz/**'],
|
|
26
26
|
},
|
|
27
27
|
},
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
// testTimeout: 30_000,
|
|
53
|
-
// },
|
|
54
|
-
// },
|
|
28
|
+
{
|
|
29
|
+
extends: true,
|
|
30
|
+
test: {
|
|
31
|
+
name: 'smoke',
|
|
32
|
+
include: ['tests/smoke/**/*.test.ts'],
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
extends: true,
|
|
37
|
+
test: {
|
|
38
|
+
name: 'integration',
|
|
39
|
+
include: ['tests/integration/**/*.test.ts'],
|
|
40
|
+
maxWorkers: 1,
|
|
41
|
+
testTimeout: 30_000,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
extends: true,
|
|
46
|
+
test: {
|
|
47
|
+
name: 'fuzz',
|
|
48
|
+
include: ['tests/fuzz/**/*.test.ts'],
|
|
49
|
+
testTimeout: 15_000,
|
|
50
|
+
},
|
|
51
|
+
},
|
|
55
52
|
],
|
|
56
53
|
},
|
|
57
54
|
}),
|
package/vitest.config.base.mjs
CHANGED
package/dist/logs/combined.log
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
{"level":40,"time":1785063807938,"env":"testing","version":"0.11.0","pid":56700,"transport":"http","requestId":"4S3UM-DS2A2","timestamp":"2026-07-26T11:03:27.937Z","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":1785063809897,"env":"testing","version":"0.11.0","pid":56700,"component":"HttpTransport","requestId":"SRPG5-UDGEF","timestamp":"2026-07-26T11:03:29.897Z","operation":"HttpRpcRequest","sessionId":"not-a-real-session-1785063809896","msg":"Session validation failed - invalid or hijacked session"}
|
|
3
|
-
{"level":50,"time":1785063813548,"env":"testing","version":"0.0.0-test","pid":56900,"requestId":"BZXOG-60V18","timestamp":"2026-07-26T11:03:33.548Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"5c775ae579ccf2a72f4baa13d44903c5f5d4bdbed02f19e8936d4f440e2b50c6","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"errorData":{"sessionId":"5c775ae579ccf2a72f4baa13d44903c5f5d4bdbed02f19e8936d4f440e2b50c6","toolName":"scoped_echo","requestId":"BZXOG-60V18","timestamp":"2026-07-26T11:03:33.548Z","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 (file:///Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:84:54)\n at withRequiredScopes (file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:68:15)\n at file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:287:17\n at McpServer.executeToolHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:233:42)\n at file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43\n at async wrappedHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js:125:32)"},"stack":"McpError: Insufficient permissions.\n at ErrorHandler.handleError (file:///Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:170:19)\n at file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:336:26\n at McpServer.executeToolHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:233:42)\n at file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43\n at async wrappedHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js:125:32)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
|
|
4
|
-
{"level":50,"time":1785063813562,"env":"testing","version":"0.0.0-test","pid":56900,"requestId":"0UAVG-3BJIC","timestamp":"2026-07-26T11:03:33.562Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"675ebae9a36f615bca0e44bc994e3b156d20f51156b91678bc2dd72c36ca2935","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":"675ebae9a36f615bca0e44bc994e3b156d20f51156b91678bc2dd72c36ca2935","toolName":"scoped_echo","requestId":"0UAVG-3BJIC","timestamp":"2026-07-26T11:03:33.562Z","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 (file:///Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:84:54)\n at withRequiredScopes (file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:68:15)\n at file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:287:17\n at McpServer.executeToolHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:233:42)\n at file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43\n at async wrappedHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js:125:32)"},"stack":"McpError: Insufficient permissions.\n at ErrorHandler.handleError (file:///Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:170:19)\n at file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:336:26\n at McpServer.executeToolHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:233:42)\n at file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43\n at async wrappedHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js:125:32)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
|
|
5
|
-
{"level":40,"time":1785063825944,"env":"testing","version":"0.11.0","pid":57179,"transport":"http","requestId":"9P2HG-AOY8N","timestamp":"2026-07-26T11:03:45.943Z","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."}
|
|
6
|
-
{"level":40,"time":1785063827752,"env":"testing","version":"0.11.0","pid":57179,"component":"HttpTransport","requestId":"L3KZL-ZPBLK","timestamp":"2026-07-26T11:03:47.752Z","operation":"HttpRpcRequest","sessionId":"not-a-real-session-1785063827752","msg":"Session validation failed - invalid or hijacked session"}
|
|
7
|
-
{"level":50,"time":1785063830224,"env":"testing","version":"0.0.0-test","pid":57273,"requestId":"REJHT-C3VBN","timestamp":"2026-07-26T11:03:50.223Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"7871d98a58be8d5777097cf18cde920771863c2d9652ddc4853fc9b86bb3b015","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"errorData":{"sessionId":"7871d98a58be8d5777097cf18cde920771863c2d9652ddc4853fc9b86bb3b015","toolName":"scoped_echo","requestId":"REJHT-C3VBN","timestamp":"2026-07-26T11:03:50.223Z","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:287: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:336: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."}
|
|
8
|
-
{"level":50,"time":1785063830234,"env":"testing","version":"0.0.0-test","pid":57273,"requestId":"ROVXJ-AU9NR","timestamp":"2026-07-26T11:03:50.234Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"37ca626d705d9bc28c28dbf904beb81ca5683454f810b6556b11c29b7ad78707","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":"37ca626d705d9bc28c28dbf904beb81ca5683454f810b6556b11c29b7ad78707","toolName":"scoped_echo","requestId":"ROVXJ-AU9NR","timestamp":"2026-07-26T11:03:50.234Z","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:287: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:336: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,4 +0,0 @@
|
|
|
1
|
-
{"level":50,"time":1785063813548,"env":"testing","version":"0.0.0-test","pid":56900,"requestId":"BZXOG-60V18","timestamp":"2026-07-26T11:03:33.548Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"5c775ae579ccf2a72f4baa13d44903c5f5d4bdbed02f19e8936d4f440e2b50c6","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"errorData":{"sessionId":"5c775ae579ccf2a72f4baa13d44903c5f5d4bdbed02f19e8936d4f440e2b50c6","toolName":"scoped_echo","requestId":"BZXOG-60V18","timestamp":"2026-07-26T11:03:33.548Z","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 (file:///Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:84:54)\n at withRequiredScopes (file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:68:15)\n at file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:287:17\n at McpServer.executeToolHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:233:42)\n at file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43\n at async wrappedHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js:125:32)"},"stack":"McpError: Insufficient permissions.\n at ErrorHandler.handleError (file:///Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:170:19)\n at file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:336:26\n at McpServer.executeToolHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:233:42)\n at file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43\n at async wrappedHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js:125:32)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
|
|
2
|
-
{"level":50,"time":1785063813562,"env":"testing","version":"0.0.0-test","pid":56900,"requestId":"0UAVG-3BJIC","timestamp":"2026-07-26T11:03:33.562Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"675ebae9a36f615bca0e44bc994e3b156d20f51156b91678bc2dd72c36ca2935","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":"675ebae9a36f615bca0e44bc994e3b156d20f51156b91678bc2dd72c36ca2935","toolName":"scoped_echo","requestId":"0UAVG-3BJIC","timestamp":"2026-07-26T11:03:33.562Z","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 (file:///Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:84:54)\n at withRequiredScopes (file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:68:15)\n at file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:287:17\n at McpServer.executeToolHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:233:42)\n at file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43\n at async wrappedHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js:125:32)"},"stack":"McpError: Insufficient permissions.\n at ErrorHandler.handleError (file:///Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:170:19)\n at file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:336:26\n at McpServer.executeToolHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:233:42)\n at file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43\n at async wrappedHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js:125:32)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
|
|
3
|
-
{"level":50,"time":1785063830224,"env":"testing","version":"0.0.0-test","pid":57273,"requestId":"REJHT-C3VBN","timestamp":"2026-07-26T11:03:50.223Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"7871d98a58be8d5777097cf18cde920771863c2d9652ddc4853fc9b86bb3b015","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"errorData":{"sessionId":"7871d98a58be8d5777097cf18cde920771863c2d9652ddc4853fc9b86bb3b015","toolName":"scoped_echo","requestId":"REJHT-C3VBN","timestamp":"2026-07-26T11:03:50.223Z","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:287: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:336: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":1785063830234,"env":"testing","version":"0.0.0-test","pid":57273,"requestId":"ROVXJ-AU9NR","timestamp":"2026-07-26T11:03:50.234Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"37ca626d705d9bc28c28dbf904beb81ca5683454f810b6556b11c29b7ad78707","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":"37ca626d705d9bc28c28dbf904beb81ca5683454f810b6556b11c29b7ad78707","toolName":"scoped_echo","requestId":"ROVXJ-AU9NR","timestamp":"2026-07-26T11:03:50.234Z","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:287: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:336: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
|