@cyanheads/mcp-ts-core 0.12.2 → 0.12.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 +2 -2
- package/CLAUDE.md +2 -2
- package/README.md +1 -1
- package/changelog/0.12.x/0.12.3.md +26 -0
- package/changelog/0.12.x/0.12.4.md +54 -0
- package/dist/config/index.d.ts +17 -17
- package/dist/core/app.d.ts.map +1 -1
- package/dist/core/app.js +34 -1
- package/dist/core/app.js.map +1 -1
- package/dist/linter/rules/error-contract-rules.d.ts +2 -2
- package/dist/linter/rules/error-contract-rules.d.ts.map +1 -1
- package/dist/linter/rules/error-contract-rules.js +6 -3
- package/dist/linter/rules/error-contract-rules.js.map +1 -1
- package/dist/mcp-server/resources/utils/resourceHandlerFactory.d.ts.map +1 -1
- package/dist/mcp-server/resources/utils/resourceHandlerFactory.js +19 -12
- package/dist/mcp-server/resources/utils/resourceHandlerFactory.js.map +1 -1
- package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts.map +1 -1
- package/dist/mcp-server/tools/utils/toolHandlerFactory.js +43 -35
- package/dist/mcp-server/tools/utils/toolHandlerFactory.js.map +1 -1
- package/dist/mcp-server/transports/http/httpErrorHandler.d.ts.map +1 -1
- package/dist/mcp-server/transports/http/httpErrorHandler.js +11 -0
- package/dist/mcp-server/transports/http/httpErrorHandler.js.map +1 -1
- package/dist/mcp-server/transports/http/httpServer.d.ts.map +1 -1
- package/dist/mcp-server/transports/http/httpServer.js +97 -14
- package/dist/mcp-server/transports/http/httpServer.js.map +1 -1
- package/dist/mcp-server/transports/stdio/stdioTransport.d.ts +29 -0
- package/dist/mcp-server/transports/stdio/stdioTransport.d.ts.map +1 -1
- package/dist/mcp-server/transports/stdio/stdioTransport.js +33 -0
- package/dist/mcp-server/transports/stdio/stdioTransport.js.map +1 -1
- package/dist/types-global/errors.d.ts +7 -0
- package/dist/types-global/errors.d.ts.map +1 -1
- package/dist/types-global/errors.js +7 -0
- package/dist/types-global/errors.js.map +1 -1
- package/dist/utils/internal/error-handler/errorHandler.d.ts +10 -7
- package/dist/utils/internal/error-handler/errorHandler.d.ts.map +1 -1
- package/dist/utils/internal/error-handler/errorHandler.js +45 -28
- package/dist/utils/internal/error-handler/errorHandler.js.map +1 -1
- package/dist/utils/internal/logger.d.ts +12 -0
- package/dist/utils/internal/logger.d.ts.map +1 -1
- package/dist/utils/internal/logger.js +39 -6
- package/dist/utils/internal/logger.js.map +1 -1
- package/dist/utils/internal/performance.d.ts +18 -9
- package/dist/utils/internal/performance.d.ts.map +1 -1
- package/dist/utils/internal/performance.js +66 -14
- package/dist/utils/internal/performance.js.map +1 -1
- package/dist/utils/network/fetchWithTimeout.d.ts +14 -7
- package/dist/utils/network/fetchWithTimeout.d.ts.map +1 -1
- package/dist/utils/network/fetchWithTimeout.js +59 -34
- package/dist/utils/network/fetchWithTimeout.js.map +1 -1
- package/dist/utils/network/httpError.d.ts +25 -3
- package/dist/utils/network/httpError.d.ts.map +1 -1
- package/dist/utils/network/httpError.js +23 -8
- package/dist/utils/network/httpError.js.map +1 -1
- package/package.json +23 -23
- package/skills/add-tool/SKILL.md +2 -2
- package/skills/api-config/SKILL.md +11 -3
- package/skills/api-context/SKILL.md +3 -1
- package/skills/api-errors/SKILL.md +11 -8
- package/skills/api-linter/SKILL.md +2 -2
- package/skills/api-telemetry/SKILL.md +35 -3
- package/skills/api-utils/SKILL.md +4 -4
- package/skills/design-mcp-server/SKILL.md +2 -2
- package/skills/git-wrapup/SKILL.md +4 -2
- package/skills/maintenance/SKILL.md +2 -1
- package/skills/polish-docs-meta/references/package-meta.md +1 -1
- package/skills/release-and-publish/SKILL.md +12 -2
- package/templates/Dockerfile +12 -2
- package/templates/package.json +1 -1
- package/dist/logs/combined.log +0 -10
- package/dist/logs/error.log +0 -6
- package/dist/logs/interactions.log +0 -0
package/skills/add-tool/SKILL.md
CHANGED
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Scaffold a new MCP tool definition. Use when the user asks to add a tool, create a new tool, or implement a new capability for the server.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "2.
|
|
7
|
+
version: "2.21"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -131,7 +131,7 @@ export const {{TOOL_EXPORT}} = tool('{{tool_name}}', {
|
|
|
131
131
|
|
|
132
132
|
### Multi-round-trip variant
|
|
133
133
|
|
|
134
|
-
A handler that needs something the caller didn't supply returns `ctx.requestInput(...)` and is re-entered with the answers on `ctx.inputs`. There is no mid-handler `await` for user input, and no capability check — the surface is always present, on every transport and both protocol eras.
|
|
134
|
+
A handler that needs something the caller didn't supply returns `ctx.requestInput(...)` and is re-entered with the answers on `ctx.inputs`. There is no mid-handler `await` for user input, and no capability check — the surface is always present, on every transport and both protocol eras. Whether the caller can *answer* is a separate question — a 2025-era HTTP client cannot when the server runs `MCP_SESSION_MODE=stateless` (`api-context` § `ctx.requestInput`). Treat an unanswered round as terminal, never as consent.
|
|
135
135
|
|
|
136
136
|
```typescript
|
|
137
137
|
import { inputRequired, tool, z } from '@cyanheads/mcp-ts-core';
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Reference for core and server configuration in `@cyanheads/mcp-ts-core`. Covers env var tables with defaults, priority order, server-specific Zod schema pattern, and Workers lazy-parsing requirement.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.14"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -86,9 +86,9 @@ await createApp({
|
|
|
86
86
|
| `MCP_HTTP_HOST` | `mcpHttpHost` | `127.0.0.1` | Bind address |
|
|
87
87
|
| `MCP_HTTP_ENDPOINT_PATH` | `mcpHttpEndpointPath` | `/mcp` | HTTP endpoint path |
|
|
88
88
|
| `MCP_HTTP_MAX_BODY_BYTES` | `mcpHttpMaxBodyBytes` | `1048576` (1 MiB) | Max **inbound** JSON-RPC request body; oversized requests get `413` before per-request allocation. Does **not** cap upstream data staged into a canvas or response sizes. `0` disables (defer to runtime/proxy). |
|
|
89
|
-
| `MCP_HTTP_MAX_PORT_RETRIES` | `mcpHttpMaxPortRetries` | `15` |
|
|
89
|
+
| `MCP_HTTP_MAX_PORT_RETRIES` | `mcpHttpMaxPortRetries` | `15` | Rungs of the port ladder walked when a bind collides; each rung tries `port + 1`. See [Port binding](#port-binding) |
|
|
90
90
|
| `MCP_HTTP_PORT_RETRY_DELAY_MS` | `mcpHttpPortRetryDelayMs` | `50` | Delay between port retries (ms) |
|
|
91
|
-
| `MCP_SESSION_MODE` | `mcpSessionMode` | `auto` | `stateless` \| `stateful` \| `auto` |
|
|
91
|
+
| `MCP_SESSION_MODE` | `mcpSessionMode` | `auto` | `stateless` \| `stateful` \| `auto`; `auto` resolves to `stateful`. `stateless` also disables the 2025-era multi-round-trip shim, so v1 HTTP clients cannot answer a `ctx.requestInput` round — 2026-07-28 clients and stdio are unaffected |
|
|
92
92
|
| `MCP_STATEFUL_SESSION_STALE_TIMEOUT_MS` | `mcpStatefulSessionStaleTimeoutMs` | `1800000` | 30 min; stale session eviction |
|
|
93
93
|
| `MCP_HTTP_RESUMABILITY` | `mcpHttpResumability` | `true` | SSE stream replay under stateful HTTP. On by default — selecting a session mode is the opt-in. Kill switch only; no effect on stateless serving or the session-less 2026-07-28 era |
|
|
94
94
|
| `MCP_HTTP_RESUMABILITY_MAX_EVENTS` | `mcpHttpResumabilityMaxEvents` | `512` | Events retained per session for replay; oldest evicted first. Lower it on a server whose tools return large results |
|
|
@@ -101,6 +101,14 @@ await createApp({
|
|
|
101
101
|
| `MCP_HEARTBEAT_MISS_THRESHOLD` | `mcpHeartbeatMissThreshold` | `3` | Missed heartbeats before session is considered stale |
|
|
102
102
|
| `MCP_GC_PRESSURE_INTERVAL_MS` | `mcpGcPressureIntervalMs` | `0` (disabled) | Bun-only opt-in forced GC loop for HTTP deployments with heap growth |
|
|
103
103
|
|
|
104
|
+
#### Port binding
|
|
105
|
+
|
|
106
|
+
`MCP_HTTP_PORT` is where the HTTP transport starts, not necessarily where it ends up. Startup walks a ladder: bind `MCP_HTTP_PORT`, and on a collision wait `MCP_HTTP_PORT_RETRY_DELAY_MS` and try the next port, up to `MCP_HTTP_MAX_PORT_RETRIES` times. Read the bound port off the `HTTP transport listening at …` log line or the startup banner — with the defaults the server may be anywhere in `3010`–`3025`. Pin the port by setting `MCP_HTTP_MAX_PORT_RETRIES=0`, which makes a collision a startup failure instead of a silent move.
|
|
107
|
+
|
|
108
|
+
- **Startup resolves only once the server reports `'listening'`.** A bind failure arriving after the listen call — a collision the pre-bind probe could not see, because another process took the port in between — is routed to the ladder like any other, not reported as a successful start.
|
|
109
|
+
- **A failure the ladder cannot clear rejects immediately.** Each rung only changes the port, so `EACCES` (privileged port, typically `<1024` as a non-root user) and `EADDRNOTAVAIL` (the `MCP_HTTP_HOST` address is not local to this machine) fail startup on the first attempt with the OS error as the rejection's `cause`, rather than burning every rung. Ladder exhaustion carries the last bind error as `cause` too, when a real bind attempt produced one.
|
|
110
|
+
- **Runtime caveat:** Bun reports a permission-denied bind as `EADDRINUSE` where Node reports `EACCES`. On Bun a privileged port therefore reads as an ordinary collision and walks the whole ladder before failing with `Failed to bind to any port after N retries.` — on Node the same port fails on the first attempt, naming `EACCES`.
|
|
111
|
+
|
|
104
112
|
---
|
|
105
113
|
|
|
106
114
|
### Auth
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Canonical reference for the unified `Context` object passed to every tool and resource handler in `@cyanheads/mcp-ts-core`. Covers the full interface, its `RequestContext` base, all sub-APIs (`ctx.log`, `ctx.state`, `ctx.requestInput`, `ctx.inputs`, `ctx.enrich`, `ctx.content`), and when to use each.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "2.
|
|
7
|
+
version: "2.2"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -326,6 +326,8 @@ Always present, on every transport and both protocol eras. A handler that needs
|
|
|
326
326
|
|
|
327
327
|
One code path serves both eras. A 2026-07-28 client fulfils the embedded requests and retries the call; for a 2025-era session the SDK's legacy shim fulfils the same returns by issuing real `elicitation/create` / `sampling/createMessage` / `roots/list` round trips and re-entering the handler itself.
|
|
328
328
|
|
|
329
|
+
**`MCP_SESSION_MODE` decides whether that second leg exists.** Under `stateful` / `auto` the shim has the session it needs. Under `stateless` each 2025-era request is served by a fresh instance that never saw `initialize`, so its client-capability view is empty and the round trip is refused rather than attempted — fail-closed, but the handler never gets its answer. Ship `stateless` on a server whose destructive tools gate on `ctx.requestInput` and those tools become unusable for v1 HTTP clients. 2026-07-28 clients are unaffected in either mode: that revision has no server→client request channel at all, which is precisely why `input_required` exists. stdio is unaffected in either mode.
|
|
330
|
+
|
|
329
331
|
### The shape of a multi-round-trip handler
|
|
330
332
|
|
|
331
333
|
Read `ctx.inputs` first, request only what is still missing, and write the call in return position so TypeScript narrows the line below it.
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
McpError constructor, JsonRpcErrorCode reference, and error handling patterns for `@cyanheads/mcp-ts-core`. Use when looking up error codes, understanding where errors should be thrown vs. caught, or using ErrorHandler.tryCatch in services.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.8"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -198,6 +198,7 @@ throw serviceUnavailable('API call failed', { url }, { cause: error });
|
|
|
198
198
|
| `internalError(msg, data?, options?)` | InternalError (-32603) |
|
|
199
199
|
| `serializationError(msg, data?, options?)` | SerializationError (-32070) — JSON/XML/parser failures |
|
|
200
200
|
| `databaseError(msg, data?, options?)` | DatabaseError (-32010) |
|
|
201
|
+
| `requestCancelled(msg, data?, options?)` | RequestCancelled (-32011) — caller went away |
|
|
201
202
|
|
|
202
203
|
`options` is `{ cause?: unknown }` — the standard ES2022 `ErrorOptions` type.
|
|
203
204
|
|
|
@@ -255,6 +256,7 @@ throw new McpError(JsonRpcErrorCode.DatabaseError, 'Connection pool exhausted',
|
|
|
255
256
|
| `ConfigurationError` | -32008 | Missing env var, invalid config |
|
|
256
257
|
| `InitializationFailed` | -32009 | Server/component startup failure |
|
|
257
258
|
| `DatabaseError` | -32010 | Storage/persistence layer failure |
|
|
259
|
+
| `RequestCancelled` | -32011 | Caller abandoned the request — client disconnect, external abort signal. Framework-raised; never retried, logged at `info` |
|
|
258
260
|
| `SerializationError` | -32070 | Data serialization/deserialization failed |
|
|
259
261
|
| `UnknownError` | -32099 | Generic fallback when no other code fits |
|
|
260
262
|
|
|
@@ -271,11 +273,12 @@ Use factories or `McpError` directly when the code must be exact — auto-classi
|
|
|
271
273
|
The framework applies these steps in order — first match wins:
|
|
272
274
|
|
|
273
275
|
1. **`McpError` instance** — `error.code` is preserved as-is; no classification needed.
|
|
274
|
-
2. **
|
|
275
|
-
3. **
|
|
276
|
-
4. **
|
|
277
|
-
5.
|
|
278
|
-
6. **
|
|
276
|
+
2. **SDK transport-closed rejection** — an `SdkError` carrying `SdkErrorCode.ConnectionClosed` → `RequestCancelled`. The SDK rejects every in-flight request when the transport closes, which is what a client disconnect looks like from inside a handler. Matched on the code, not the message: one of its wordings says "aborted" and would otherwise be caught by the generic abort pattern in step 5 and read as a `Timeout`.
|
|
277
|
+
3. **JS constructor name** — matched against a fixed table (e.g. `ZodError` → `ValidationError`, `SyntaxError` → `ValidationError`). Note: `TypeError` is intentionally excluded — runtime TypeErrors are programmer errors, not validation failures.
|
|
278
|
+
4. **Provider-specific patterns** — HTTP status codes, AWS exception names, Supabase, OpenRouter. Checked before common patterns because they are more specific (e.g. `status code 429` beats the generic `rate limit` pattern).
|
|
279
|
+
5. **Common message/name patterns** — broad keyword patterns covering auth, not-found, validation, etc. First match wins; order matters.
|
|
280
|
+
6. **`AbortError` name** — `error.name === 'AbortError'` → `Timeout`.
|
|
281
|
+
7. **Fallback** — `InternalError`.
|
|
279
282
|
|
|
280
283
|
### JS Constructor Name Mappings
|
|
281
284
|
|
|
@@ -455,8 +458,7 @@ Full status table:
|
|
|
455
458
|
| 422 | `ValidationError` |
|
|
456
459
|
| 429 | `RateLimited` |
|
|
457
460
|
| 405, 406, 410, 412, 415, 416, 417, 428, 431, 451, 4xx (other) | `InvalidRequest` |
|
|
458
|
-
| 500, 501 | `
|
|
459
|
-
| 502, 503, 5xx (other) | `ServiceUnavailable` |
|
|
461
|
+
| 500, 501, 502, 503, 5xx (other) | `ServiceUnavailable` |
|
|
460
462
|
|
|
461
463
|
Also exports `httpStatusToErrorCode(status)` for sync mapping when you don't have a Response object.
|
|
462
464
|
|
|
@@ -514,6 +516,7 @@ These codes bubble up from anywhere — services, framework utilities, the auto-
|
|
|
514
516
|
- `Timeout` — request deadline exceeded, abort
|
|
515
517
|
- `ValidationError` — schema violations, malformed input
|
|
516
518
|
- `SerializationError` — JSON/XML parse failures
|
|
519
|
+
- `RequestCancelled` — the caller disconnected or aborted mid-call
|
|
517
520
|
|
|
518
521
|
If you *want* to declare one of these as a domain-specific failure (e.g., a tool that intentionally times out under defined conditions), put it in `errors[]` anyway — the contract still binds `ctx.fail(reason)` and the conformance lint will catch undeclared throws. The lint just doesn't *require* you to enumerate baselines.
|
|
519
522
|
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
MCP definition linter rules reference. Use when `bun run lint:mcp` or `bun run devcheck` reports a lint error or warning (`format-parity`, `schema-is-object`, `name-format`, `server-json-*`, etc.) and you need to understand the rule, its severity, and how to fix it. Every rule ID the linter emits has an entry in this doc.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.13"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -798,7 +798,7 @@ Fires when `recovery` has fewer than 5 words. Short recoveries like "Try again."
|
|
|
798
798
|
|
|
799
799
|
Cross-check rule. Fires when a handler throws a non-baseline code (via `new McpError(JsonRpcErrorCode.X, …)` or a factory like `notFound()`) that isn't declared in `errors[]`.
|
|
800
800
|
|
|
801
|
-
Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`) are auto-allowed because they bubble from anywhere — services, framework utilities, the auto-classifier — and are implicitly always-possible on any tool. Only domain-specific codes need declaring.
|
|
801
|
+
Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`, `RequestCancelled`) are auto-allowed because they bubble from anywhere — services, framework utilities, the auto-classifier — and are implicitly always-possible on any tool. Only domain-specific codes need declaring.
|
|
802
802
|
|
|
803
803
|
**Fix:** add the missing code to `errors[]` with a stable reason, or route through `ctx.fail(reason, …)` if it maps to an existing entry.
|
|
804
804
|
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Catalog of OpenTelemetry instrumentation built into framework `@cyanheads/mcp-ts-core` — spans, metrics, completion logs, env config, runtime caveats, custom instrumentation patterns, and cardinality rules. Use when enabling OTel export, adding custom spans or metrics in services, debugging missing telemetry, looking up attribute names, or deciding what's safe to put on a metric attribute vs. a span.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.7"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -57,6 +57,25 @@ Cloud platform detection auto-populates resource attributes:
|
|
|
57
57
|
|
|
58
58
|
---
|
|
59
59
|
|
|
60
|
+
## Flush at exit
|
|
61
|
+
|
|
62
|
+
Spans batch and metrics push on a 15-second cycle, so a process that exits between cycles takes its telemetry with it. `ServerHandle.shutdown()` is the drain: it stops the transport, then force-flushes traces and metrics through the OTLP exporters and closes the logger.
|
|
63
|
+
|
|
64
|
+
| Trigger | Path |
|
|
65
|
+
|:--------|:-----|
|
|
66
|
+
| `SIGTERM` / `SIGINT` | `shutdown(signal)` |
|
|
67
|
+
| `uncaughtException` / `unhandledRejection` | `shutdown(signal)`, then `process.exit(1)` |
|
|
68
|
+
| stdin EOF, stdio transport | `shutdown('STDIN_EOF')`, then `process.exit(0)` |
|
|
69
|
+
| `ServerHandle.shutdown()` called directly | the same drain, no exit |
|
|
70
|
+
|
|
71
|
+
**Stdin EOF is a disconnect.** A stdio host closing the pipe runs the cleanup a signal runs, exactly once — the shutdown detaches the signal handlers and the EOF watcher as it starts, so neither can re-enter it — and the process then exits explicitly instead of waiting to run out of handles. Two things follow: the OTLP export leaves the process, and a `setInterval` a service registered without `unref()` can no longer keep the server resident after its client is gone. The path writes nothing to stdout.
|
|
72
|
+
|
|
73
|
+
**The drain is bounded.** Shutdown-on-exit races a 10-second backstop, so a cleanup step that never settles still terminates the process. The logger bounds its own flush separately, per pino instance: a completing callback is awaited in full, and a runtime whose callback never arrives releases shutdown rather than hanging it.
|
|
74
|
+
|
|
75
|
+
Workers has no `ServerHandle` and no `NodeSDK` — flush whatever exporter you wired there yourself, via `ctx.waitUntil()`.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
60
79
|
## Spans
|
|
61
80
|
|
|
62
81
|
Every handler call gets a span. Nested operations (storage, graph, LLM) become child spans on the same trace. All spans carry `code.function.name` and `code.namespace` for code-attribution. Errors are recorded via `span.recordException()` and `SpanStatusCode.ERROR`; `McpError` codes surface as the `*.error_code` attribute.
|
|
@@ -74,6 +93,19 @@ Every handler call gets a span. Nested operations (storage, graph, LLM) become c
|
|
|
74
93
|
|
|
75
94
|
A handler that ends its round with `ctx.requestInput(...)` closes its span `OK` with `mcp.*.input_required` set — no recorded exception, no error-counter increment. Multi-round-trip input is protocol control flow, so it never inflates error rates; split on that attribute to tell an incomplete round from a completed call.
|
|
76
95
|
|
|
96
|
+
### The measured region
|
|
97
|
+
|
|
98
|
+
A tool or resource call is measured from the start of the handler through the response pipeline that follows it: output-schema validation, `format()`, the enrichment merge, and the trailer render for tools; output-schema validation and `format()` for resources. Telemetry therefore records the outcome the client sees — a failure in any of those is an ERROR span, `success=false` counters, an error-counter increment, and `isSuccess: false` in the completion log, matching the `isError: true` the caller receives. Prompt generation has no post-handler pipeline, so its region is the generate function alone.
|
|
99
|
+
|
|
100
|
+
Two consequences worth knowing when reading a dashboard:
|
|
101
|
+
|
|
102
|
+
| Signal | What it covers |
|
|
103
|
+
|:-------|:---------------|
|
|
104
|
+
| `mcp.tool.duration` / `mcp.resource.duration` | The handler **plus** validation, formatting, and the enrichment merge — time to produce the result, not time spent in handler code. An expensive `format()` shows up here. |
|
|
105
|
+
| `mcp.tool.output_bytes` / `mcp.resource.output_bytes` | The handler's returned domain value, not the assembled result. `content[]` re-renders the data the structured payload already carries, so measuring the assembly would double-count it. Nothing is recorded for a call that fails after the handler. |
|
|
106
|
+
|
|
107
|
+
`mcp.tool.partial_success` and the `mcp.tool.batch.*` counts read the same domain value, so a batch envelope (`{ succeeded, failed }`) is still detected once the result has been assembled around it.
|
|
108
|
+
|
|
77
109
|
Trace context propagates across boundaries via W3C `traceparent` headers. See `api-utils` → `telemetry/trace` for `withSpan`, `buildTraceparent`, `extractTraceparent`, `createContextWithParentTrace`, `injectCurrentContextInto`, `runInContext` signatures.
|
|
78
110
|
|
|
79
111
|
---
|
|
@@ -90,12 +122,12 @@ All custom metrics are namespaced `mcp.*` (or `process.*` / `http.client.*` wher
|
|
|
90
122
|
| `mcp.tool.duration` | histogram | `ms` | `mcp.tool.name`, `mcp.tool.success` |
|
|
91
123
|
| `mcp.tool.errors` | counter | `{errors}` | `mcp.tool.name`, `mcp.tool.error_category` (`upstream`/`server`/`client`) |
|
|
92
124
|
| `mcp.tool.input_bytes` | histogram | `bytes` | `mcp.tool.name` |
|
|
93
|
-
| `mcp.tool.output_bytes` | histogram | `bytes` | `mcp.tool.name` (success only) |
|
|
125
|
+
| `mcp.tool.output_bytes` | histogram | `bytes` | `mcp.tool.name` (success only; the handler's returned value) |
|
|
94
126
|
| `mcp.tool.param.usage` | counter | `{uses}` | `mcp.tool.name`, `mcp.tool.param` (top-level keys supplied by caller) |
|
|
95
127
|
| `mcp.resource.reads` | counter | `{reads}` | `mcp.resource.name`, `mcp.resource.success` |
|
|
96
128
|
| `mcp.resource.duration` | histogram | `ms` | `mcp.resource.name`, `mcp.resource.success` |
|
|
97
129
|
| `mcp.resource.errors` | counter | `{errors}` | `mcp.resource.name` |
|
|
98
|
-
| `mcp.resource.output_bytes` | histogram | `bytes` | `mcp.resource.name` (success only) |
|
|
130
|
+
| `mcp.resource.output_bytes` | histogram | `bytes` | `mcp.resource.name` (success only; the handler's returned value) |
|
|
99
131
|
| `mcp.prompt.generations` | counter | `{generations}` | `mcp.prompt.name`, `mcp.prompt.success` |
|
|
100
132
|
| `mcp.prompt.duration` | histogram | `ms` | `mcp.prompt.name`, `mcp.prompt.success` |
|
|
101
133
|
| `mcp.prompt.errors` | counter | `{errors}` | `mcp.prompt.name`, `mcp.prompt.error_category` |
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
API reference for all utilities exported from `@cyanheads/mcp-ts-core/utils`. Use when looking up utility method signatures, options, peer dependencies, or usage patterns.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "2.
|
|
7
|
+
version: "2.7"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -31,10 +31,10 @@ Utility exports from `@cyanheads/mcp-ts-core/utils`. Utilities with complex APIs
|
|
|
31
31
|
|
|
32
32
|
| Export | API | Notes |
|
|
33
33
|
|:-------|:----|:------|
|
|
34
|
-
| `fetchWithTimeout` | `(url, timeoutMs, context, options?: FetchWithTimeoutOptions) -> Promise<Response>` | Wraps `fetch` with `AbortController` timeout. `timeoutMs` bounds the **whole exchange**: on a 2xx carrying a body the returned `Response` is a passthrough wrapper that keeps the deadline armed until the body closes, errors, or is cancelled, so a stalled stream rejects the caller's `.text()`/`.json()` with the same `Timeout` error the header phase raises. `status`, `statusText`, `headers`, `url`, `redirected`, and `type` carry across the wrapper; the original body is locked by it, and bodyless/null-body responses (HEAD, 204/205/304) come back untouched. `FetchWithTimeoutOptions` extends `RequestInit` (minus `signal`) and adds `rejectPrivateIPs?: boolean`, `expectedStatuses?: number[]` (listed non-2xx statuses logged at `debug` not `error`, still thrown), `errorBodyLimit?: number` (bytes of a non-2xx body kept, default `500`), and `signal?: AbortSignal` (external cancellation). On a non-2xx, `error.data` carries `status`/`body` plus the legacy `statusCode`/`responseBody` aliases (identical values; consolidating in a future major); a body over `errorBodyLimit` is captured from both ends — 40% head, 60% tail, joined by `…[N bytes elided]…` — so a diagnostic behind a boilerplate preamble survives the cap, while a body still streaming at the 16 KiB scan ceiling stays head-only with a trailing `…`. SSRF guard (best-effort, not hard isolation): blocks RFC 1918, loopback, link-local, CGNAT, cloud metadata. DNS validation on Node, Bun, and Cloudflare Workers under `nodejs_compat`; hostname-only fallback otherwise. Manual redirect following (max 5) with per-hop SSRF check. **DNS rebinding / TOCTOU gap** — the validation lookup and `fetch`'s own resolution are independent; pair with egress controls or a DNS-pinning fetch proxy for strong isolation. **Error/log redaction:** URLs written into thrown errors and log lines are reduced to `origin + pathname` — the query string (where API keys commonly ride: `?api-key=…`, `?api_key=…`) never reaches the client or the logs. The actual request still uses the full URL. |
|
|
34
|
+
| `fetchWithTimeout` | `(url, timeoutMs, context, options?: FetchWithTimeoutOptions) -> Promise<Response>` | Wraps `fetch` with `AbortController` timeout. `timeoutMs` bounds the **whole exchange**: on a 2xx carrying a body the returned `Response` is a passthrough wrapper that keeps the deadline armed until the body closes, errors, or is cancelled, so a stalled stream rejects the caller's `.text()`/`.json()` with the same `Timeout` error the header phase raises. `status`, `statusText`, `headers`, `url`, `redirected`, and `type` carry across the wrapper; the original body is locked by it, and bodyless/null-body responses (HEAD, 204/205/304) come back untouched. `FetchWithTimeoutOptions` extends `RequestInit` (minus `signal`) and adds `rejectPrivateIPs?: boolean`, `expectedStatuses?: number[]` (listed non-2xx statuses logged at `debug` not `error`, still thrown), `errorBodyLimit?: number` (bytes of a non-2xx body kept, default `500`), and `signal?: AbortSignal` (external cancellation — an abort on it throws `RequestCancelled` (-32011), logged at `info` and outside `withRetry`'s transient set, since the caller is gone and no retry can reach them). On a non-2xx, `error.data` carries `status`/`body` plus the legacy `statusCode`/`responseBody` aliases (identical values; consolidating in a future major); a body over `errorBodyLimit` is captured from both ends — 40% head, 60% tail, joined by `…[N bytes elided]…` — so a diagnostic behind a boilerplate preamble survives the cap, while a body still streaming at the 16 KiB scan ceiling stays head-only with a trailing `…`. SSRF guard (best-effort, not hard isolation): blocks RFC 1918, loopback, link-local, CGNAT, cloud metadata. DNS validation on Node, Bun, and Cloudflare Workers under `nodejs_compat`; hostname-only fallback otherwise. **Both resolvers are queried** — `resolve4`/`resolve6` (c-ares) and `lookup` (the system resolver, which is what reads `/etc/hosts`, split DNS, and NSS modules) — and a non-global answer from either rejects. Runtimes differ in which resolver the connection uses (Bun 1.4 moved `net.connect()` on Linux to `getaddrinfo` while leaving `dns.resolve*()` on c-ares), so checking one alone leaves a name the other can see unguarded; each probe settles independently, so a resolver absent from the runtime is skipped rather than fatal. Manual redirect following (max 5) with per-hop SSRF check. **DNS rebinding / TOCTOU gap** — the validation lookup and `fetch`'s own resolution are independent; pair with egress controls or a DNS-pinning fetch proxy for strong isolation. **Error/log redaction:** URLs written into thrown errors and log lines are reduced to `origin + pathname` — the query string (where API keys commonly ride: `?api-key=…`, `?api_key=…`) never reaches the client or the logs. The actual request still uses the full URL. |
|
|
35
35
|
| `withRetry` | `<T>(fn: () => Promise<T>, options?: RetryOptions) -> Promise<T>` | Executes `fn` with exponential backoff. Retries on transient errors (`ServiceUnavailable`, `Timeout`, `RateLimited`); non-transient errors fail immediately. Honors an upstream `Retry-After` on `data.retryAfter` (delta-seconds or HTTP-date) over exponential backoff, capped at `maxDelayMs`; a requested wait beyond the cap fails fast rather than sleeping. On exhaustion, enriches the final error with attempt count in message and `data.retryAttempts`. **Place the retry boundary around the full pipeline** (fetch + parse), not just the network call. `RetryOptions`: `maxRetries` (default `3`), `baseDelayMs` (default `1000`), `maxDelayMs` (default `30000`), `jitter` (default `0.25`), `operation` (log label), `context` (RequestContext), `signal` (AbortSignal), `isTransient` (custom predicate). |
|
|
36
|
-
| `httpErrorFromResponse` | `(response: Response, options?: HttpErrorFromResponseOptions) -> Promise<McpError>` | Maps an HTTP `Response` to a properly classified `McpError` — full status table including 401/403/408/422/429/5xx, body capture (truncated), `retry-after` header, optional `cause`. `error.data` carries `status`/`body` plus the legacy `statusCode`/`responseBody` aliases (identical values), so a consumer can classify either helper's error without knowing which raised it. Use this instead of hand-rolling `if (status === 429) ...` ladders. Reads the response body — `clone()` first if you need it elsewhere. `HttpErrorFromResponseOptions`: `service?` (logical name in message, e.g. `'NCBI'`), `captureBody?` (default `true`), `bodyLimit?` (default `500`), `data?` (extra fields merged into `error.data`), `cause?`, `codeOverride?` (per-status mapping override). Pairs naturally with `withRetry` — both classify codes the same way. |
|
|
37
|
-
| `httpStatusToErrorCode` | `(status: number) -> JsonRpcErrorCode \| undefined` | Sync status → code lookup. Returns `undefined` for 1xx/2xx/3xx. Use when you need just the code without a `Response` object handy. |
|
|
36
|
+
| `httpErrorFromResponse` | `(response: Response, options?: HttpErrorFromResponseOptions) -> Promise<McpError>` | Maps an HTTP `Response` to a properly classified `McpError` — full status table including 401/403/408/422/429/5xx, body capture (truncated), `retry-after` header, optional `cause`. `error.data` carries `status`/`body` plus the legacy `statusCode`/`responseBody` aliases (identical values), so a consumer can classify either helper's error without knowing which raised it. Use this instead of hand-rolling `if (status === 429) ...` ladders. Reads the response body — `clone()` first if you need it elsewhere. `HttpErrorFromResponseOptions`: `service?` (logical name in message, e.g. `'NCBI'`), `captureBody?` (default `true`), `bodyLimit?` (default `500`), `data?` (extra fields merged into `error.data`), `cause?`, `codeOverride?` (per-status mapping override). Pairs naturally with `withRetry` — both classify codes the same way. A 501 also carries `data.retryable: false`, so retry fails it fast instead of re-asking for a method the upstream does not implement. |
|
|
37
|
+
| `httpStatusToErrorCode` | `(status: number) -> JsonRpcErrorCode \| undefined` | Sync status → code lookup. Returns `undefined` for 1xx/2xx/3xx. Use when you need just the code without a `Response` object handy. No status maps to `InternalError` — that code means *this* server failed, which a remote status cannot establish; every 5xx is `ServiceUnavailable` (or `Timeout` for 504) and so picks up `withRetry`'s default transient policy. |
|
|
38
38
|
|
|
39
39
|
---
|
|
40
40
|
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Design the tool surface, resources, and service layer for a new MCP server. Use when starting a new server, planning a major feature expansion, or when the user describes a domain/API they want to expose via MCP. Produces a design doc at docs/design.md that drives implementation.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "2.
|
|
7
|
+
version: "2.23"
|
|
8
8
|
audience: external
|
|
9
9
|
type: workflow
|
|
10
10
|
---
|
|
@@ -271,7 +271,7 @@ Tools that perform multi-step mutations (the Workflow shape) have two safety con
|
|
|
271
271
|
|
|
272
272
|
**Confirmation-gated destructive modes, with an annotation fallback.** When a workflow's `mode` parameter switches between safe and destructive arms (`draft` vs `send`, `plan` vs `apply`), gate the destructive arm on a confirmation the handler asks for via `ctx.requestInput(...)`, so a human approves before the irreversible step fires. The handler is re-entered with the answer on `ctx.inputs`; it does not `await` mid-call.
|
|
273
273
|
|
|
274
|
-
The gate is always *reachable* — `ctx.requestInput` is present on every transport and both protocol eras — but it is not always *answerable*: a client that never fulfils the `input_required` result simply doesn't retry, and the destructive step never runs. Keep `destructiveHint: true` in annotations so those clients' own approval flows still surface the risk.
|
|
274
|
+
The gate is always *reachable* — `ctx.requestInput` is present on every transport and both protocol eras — but it is not always *answerable*: a client that never fulfils the `input_required` result simply doesn't retry, and the destructive step never runs. The same holds for a 2025-era HTTP client when the server runs `MCP_SESSION_MODE=stateless`, which disables the legacy round-trip shim — the gate refuses and the destructive step never fires. That is the safe outcome, but it makes the tool unusable for those clients, so weigh it before defaulting such a server to `stateless` (`api-context` § `ctx.requestInput`). Keep `destructiveHint: true` in annotations so those clients' own approval flows still surface the risk.
|
|
275
275
|
|
|
276
276
|
```ts
|
|
277
277
|
annotations: { destructiveHint: true }, // client-side approval flows still see the risk
|
|
@@ -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.11"
|
|
8
8
|
audience: external
|
|
9
9
|
type: workflow
|
|
10
10
|
---
|
|
@@ -27,7 +27,7 @@ Every item must be true before starting wrapup. Committing means releasing — a
|
|
|
27
27
|
- [ ] **Code simplified** — if the diff spans more than ~50 changed lines or touches 3+ source files, the `code-simplifier` skill has been run across the changes
|
|
28
28
|
- [ ] **`bun run devcheck` passes** — typecheck + lint clean
|
|
29
29
|
- [ ] **`bun run rebuild` succeeds** — full clean build from scratch
|
|
30
|
-
- [ ] **All tests pass** — `bun run test:all` (or `bun run test`)
|
|
30
|
+
- [ ] **All tests pass** — `bun run test:all` (or `bun run test`), plus `bun run test:package` where the project defines one: it guards the public-export manifest and is not part of `test:all`. New tests and regression tests added as needed for the changes being shipped.
|
|
31
31
|
- [ ] **Fixes verified** — bug fixes validated, generally via `bun run rebuild` and field-testing. Not just written — confirmed to resolve the described behavior.
|
|
32
32
|
- [ ] **No known regressions** — the changes don't break existing functionality
|
|
33
33
|
- [ ] **GH issues updated** — issues addressed by this work commented with what landed and any follow-ups needed. Concise. Backlinked as needed.
|
|
@@ -119,6 +119,7 @@ The tree being committed must pass verification. Both must succeed:
|
|
|
119
119
|
```bash
|
|
120
120
|
bun run devcheck
|
|
121
121
|
bun run test:all # or `bun run test` if no test:all script exists
|
|
122
|
+
bun run test:package # only if the script exists — NOT part of test:all
|
|
122
123
|
```
|
|
123
124
|
|
|
124
125
|
**If either fails, halt.** Do not bypass verification to land the commit. Fix the issue first, then re-run from step 6.
|
|
@@ -237,6 +238,7 @@ If the working tree isn't clean or the tag doesn't point at HEAD, something went
|
|
|
237
238
|
- [ ] `docs/tree.md` regenerated if structure changed (`bun run tree`)
|
|
238
239
|
- [ ] `bun run devcheck` passes
|
|
239
240
|
- [ ] `bun run test:all` (or `test`) passes
|
|
241
|
+
- [ ] `bun run test:package` passes, when the project defines it — it guards the public-export manifest and `test:all` does not run it
|
|
240
242
|
- [ ] 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
243
|
- [ ] Every commit carries a body, and every body is one or two lines — none subject-only, none a paragraph
|
|
242
244
|
- [ ] Annotated tag `v<version>` with structured markdown message, final line linking this version's changelog file
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Investigate, adopt, and verify dependency updates — with special handling for `@cyanheads/mcp-ts-core`. Captures what changed, understands why, cross-references against the codebase, adopts framework improvements, syncs project skills, and runs final checks. Supports two entry modes: run the full flow end-to-end, or review updates you already applied.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "2.
|
|
7
|
+
version: "2.6"
|
|
8
8
|
audience: external
|
|
9
9
|
type: workflow
|
|
10
10
|
---
|
|
@@ -103,6 +103,7 @@ Procedure:
|
|
|
103
103
|
- If missing in project `skills/`, copy the full directory
|
|
104
104
|
- If present, compare `metadata.version` — replace if the package version is newer
|
|
105
105
|
- If the local version is equal or newer, skip (local override)
|
|
106
|
+
- **Report every skip.** List each skipped skill with both versions in the pass output. The rule trusts a downstream stamp it cannot verify, so a stamp that ever moves backwards upstream makes the skip permanent and silent — the local copy outranks the package copy forever and no future edit reaches it. A skip you can see is a skip you can question; compare the two bodies whenever one looks unexpected.
|
|
106
107
|
3. Leave skills in `skills/` that lack `metadata.audience: external` untouched — they're server-specific or sourced elsewhere, not framework-managed.
|
|
107
108
|
4. **Prune framework skills deleted upstream.** A skill in `skills/` that *carries* `metadata.audience: external` but is **absent** from the package was removed upstream (e.g. `migrate-mcp-ts-template`, removed in 0.9.12) and lingers because sync was previously add/update-only. Delete it from `skills/` (and from the agent mirrors in Phase B). The `audience: external` marker is the provenance: it scopes the prune to framework-managed skills, so a server's own skills — which never carry it — are never touched. Before deleting, scan the skill for local edits worth keeping; if any exist, reconcile or surface them rather than discarding silently.
|
|
108
109
|
|
|
@@ -28,7 +28,7 @@ These are set by `init` and generally don't need changes. Verify they're present
|
|
|
28
28
|
| `types` | `"dist/index.d.ts"` | TypeScript declarations |
|
|
29
29
|
| `files` | `["dist/"]` | What npm publishes |
|
|
30
30
|
| `engines` | `{ "node": ">=24.0.0", "bun": ">=1.3.0" }` | Node runs the built `dist/`; Bun is the dev floor |
|
|
31
|
-
| `packageManager` | `"bun@1.
|
|
31
|
+
| `packageManager` | `"bun@1.4.0"` | Pins the dev package manager; keep current with the framework's Bun version |
|
|
32
32
|
| `scripts` | _(various)_ | Build, dev, test scripts |
|
|
33
33
|
| `dependencies` | `@cyanheads/mcp-ts-core` | Core framework |
|
|
34
34
|
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Ship a release end-to-end across every registry the project targets (npm, MCP Registry, GitHub Releases for `.mcpb` bundles, GHCR). Runs the final verification gate, pushes commits and tags, then publishes to each applicable destination. Assumes git wrapup (version bumps, changelog, commit, annotated tag) is already complete — this skill is the post-wrapup publish workflow. Retries transient network failures on publish steps; halts with a partial-state report when retries are exhausted or the failure is terminal.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "2.
|
|
7
|
+
version: "2.13"
|
|
8
8
|
audience: external
|
|
9
9
|
type: workflow
|
|
10
10
|
---
|
|
@@ -73,14 +73,20 @@ If working tree is dirty or HEAD isn't on `v<version>`, halt.
|
|
|
73
73
|
|
|
74
74
|
### 2. Run the verification gate
|
|
75
75
|
|
|
76
|
-
All
|
|
76
|
+
All must succeed. Check `package.json` `scripts` for `test:all`; if absent, fall back to `test`:
|
|
77
77
|
|
|
78
78
|
```bash
|
|
79
79
|
bun run devcheck
|
|
80
80
|
bun run rebuild
|
|
81
81
|
bun run test:all # or `bun run test` if no test:all
|
|
82
|
+
bun run test:package # only if the script exists — NOT part of test:all
|
|
82
83
|
```
|
|
83
84
|
|
|
85
|
+
`test:package` is a separate gate wherever a project defines one: it verifies the public-export
|
|
86
|
+
manifest against what the built subpaths actually export. A release that adds, removes, or renames
|
|
87
|
+
an export passes `test:all` and fails here. Regenerate the manifest with the command the failure
|
|
88
|
+
names rather than editing it by hand.
|
|
89
|
+
|
|
84
90
|
Any non-zero exit → halt with the failing command's output.
|
|
85
91
|
|
|
86
92
|
### 3. Push to origin
|
|
@@ -182,6 +188,8 @@ docker buildx build --platform linux/amd64,linux/arm64 \
|
|
|
182
188
|
--push .
|
|
183
189
|
```
|
|
184
190
|
|
|
191
|
+
The build stage in `Dockerfile` must carry `FROM --platform=$BUILDPLATFORM` (the templates ship it). Without it the non-native leg of the multi-arch build runs under QEMU, where bun >= 1.4 aborts inside `bun run build` with a JavaScriptCore allocator assertion (`qemu: uncaught target signal 6`, exit 134) and no image publishes for either architecture. npm, the MCP Registry, and the GitHub Release have all published by this step, so the recovery is a follow-up patch release rather than a retry — check the flag before building, not after.
|
|
192
|
+
|
|
185
193
|
If the project uses a non-GHCR registry or a custom image name, respect the project's convention. If push fails with a 401/403, prompt the user to authenticate (`echo $GITHUB_TOKEN | docker login ghcr.io -u <OWNER> --password-stdin`) and retry. Halt on build failure or non-auth push failure.
|
|
186
194
|
|
|
187
195
|
### 8. Report the deployed artifacts
|
|
@@ -205,6 +213,7 @@ Confirm each published artifact is actually live — don't rely on a successful
|
|
|
205
213
|
- **MCP Registry**: `curl -s "https://registry.modelcontextprotocol.io/v0.1/servers/<mcpName>/versions/<version>"` — must return HTTP 200 with `server.version` matching `<version>` (`mcpName` is the `name` field from `server.json`; URL-encode `/` as `%2F`). The search endpoint (`/v0.1/servers?search=`) paginates and may not include the latest version for packages with many releases — always use the direct version lookup.
|
|
206
214
|
- **GitHub Release**: `gh release view v<VERSION> -R <OWNER>/<REPO> --json assets --jq '.assets[].name'` — must list the `.mcpb` file
|
|
207
215
|
- **GHCR**: `docker manifest inspect ghcr.io/<OWNER>/<REPO>:<VERSION>` — must exit 0 (resolves multi-arch OCI indexes directly with the correct media types; exits non-zero when the tag is genuinely absent)
|
|
216
|
+
- The manifest check is the whole verification available on a single-arch host. Running the published image for a foreign architecture (`docker run --platform linux/amd64` on an arm64 host) is emulation and hits the same bun/QEMU assertion the build stage avoids, so a failure there says nothing about the image. Verifying a foreign-arch image by running it requires a native host of that architecture.
|
|
208
217
|
|
|
209
218
|
If any check fails, halt and report which destination is unreachable. A successful `docker push` or `bun publish` exit code does not guarantee the artifact is queryable — registry propagation delays, auth scoping, and partial failures all exist.
|
|
210
219
|
|
|
@@ -214,6 +223,7 @@ If any check fails, halt and report which destination is unreachable. A successf
|
|
|
214
223
|
- [ ] `bun run devcheck` passes
|
|
215
224
|
- [ ] `bun run rebuild` succeeds
|
|
216
225
|
- [ ] `bun run test:all` (or `test`) passes
|
|
226
|
+
- [ ] `bun run test:package` passes, when the project defines it
|
|
217
227
|
- [ ] Commits pushed to origin
|
|
218
228
|
- [ ] Tags pushed to origin
|
|
219
229
|
- [ ] `bun publish --access public` succeeds
|
package/templates/Dockerfile
CHANGED
|
@@ -3,8 +3,18 @@
|
|
|
3
3
|
#
|
|
4
4
|
# This stage installs all dependencies (including dev), builds the TypeScript
|
|
5
5
|
# source code into JavaScript, and prepares the production assets.
|
|
6
|
+
#
|
|
7
|
+
# Pinned to $BUILDPLATFORM rather than the target platform: `bun run build` emits
|
|
8
|
+
# JavaScript, and only `dist/` crosses into the production stage, which runs its
|
|
9
|
+
# own target-arch install. Built for the target instead, the non-native leg of a
|
|
10
|
+
# `--platform linux/amd64,linux/arm64` build runs under QEMU, where bun >= 1.4
|
|
11
|
+
# aborts with a JavaScriptCore allocator assertion and fails the multi-arch push.
|
|
12
|
+
#
|
|
13
|
+
# The constraint this assumes: the build stage produces platform-independent
|
|
14
|
+
# output. A stage that compiles a native addon needs the target-arch toolchain
|
|
15
|
+
# and cannot cross-compile this way — drop the flag there.
|
|
6
16
|
# ==============================================================================
|
|
7
|
-
FROM oven/bun:1.
|
|
17
|
+
FROM --platform=$BUILDPLATFORM oven/bun:1.4.0 AS build
|
|
8
18
|
|
|
9
19
|
WORKDIR /usr/src/app
|
|
10
20
|
|
|
@@ -30,7 +40,7 @@ RUN bun run build
|
|
|
30
40
|
# application. It uses a slim base image and only includes production
|
|
31
41
|
# dependencies and build artifacts.
|
|
32
42
|
# ==============================================================================
|
|
33
|
-
FROM oven/bun:1.
|
|
43
|
+
FROM oven/bun:1.4.0-slim AS production
|
|
34
44
|
|
|
35
45
|
WORKDIR /usr/src/app
|
|
36
46
|
|
package/templates/package.json
CHANGED
package/dist/logs/combined.log
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
{"level":40,"time":1787257451141,"env":"testing","version":"0.12.2","pid":2077,"operation":"TransportManager.start","requestId":"43OM2-VIOAT","timestamp":"2026-08-20T20:24:11.140Z","transport":"http","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":1787257453310,"env":"testing","version":"0.12.2","pid":2077,"operation":"HttpRpcRequest","requestId":"HQIB2-ZJ6Z9","sessionId":"not-a-real-session-1787257453308","timestamp":"2026-08-20T20:24:13.310Z","component":"HttpTransport","msg":"Session validation failed - invalid or hijacked session"}
|
|
3
|
-
{"level":50,"time":1787257456744,"env":"testing","version":"0.0.0-test","pid":2100,"auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"operation":"HandleToolRequest","requestId":"ZC6PT-US941","sessionId":"150a3b106bcd4ba471ddf23e8ea5fab8e71307c5d51656146dfdfbd513728f14","tenantId":"authz-tenant","timestamp":"2026-08-20T20:24:16.744Z","toolName":"scoped_echo","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","errorData":{"operation":"HandleToolRequest","requestId":"ZC6PT-US941","sessionId":"150a3b106bcd4ba471ddf23e8ea5fab8e71307c5d51656146dfdfbd513728f14","tenantId":"authz-tenant","timestamp":"2026-08-20T20:24:16.744Z","toolName":"scoped_echo","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:91:54)\n at withRequiredScopes (file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:60:15)\n at file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:383:17\n at Object.callback [as executor] (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1861:31)\n at McpServer.executeToolHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1450:15)\n at file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1400:31\n at async Server._invokeInputRequiredCapableHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:887:13)\n at async file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:838:19"},"stack":"McpError: Insufficient permissions.\n at ErrorHandler.handleError (file:///Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:178:19)\n at file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:450:26\n at Object.callback [as executor] (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1861:31)\n at McpServer.executeToolHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1450:15)\n at file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1400:31\n at async Server._invokeInputRequiredCapableHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:887:13)\n at async file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:838:19","msg":"Error in tool:scoped_echo: Insufficient permissions."}
|
|
4
|
-
{"level":50,"time":1787257456756,"env":"testing","version":"0.0.0-test","pid":2100,"auth":{"sub":"authz-user","scopes":["openid","email","profile","offline_access"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"operation":"HandleToolRequest","requestId":"SCLK4-INO9D","sessionId":"22becb78f201a05364e2fdd3017a2338924a22ff02bcc77a7a102082f8de990a","tenantId":"authz-tenant","timestamp":"2026-08-20T20:24:16.756Z","toolName":"scoped_echo","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","errorData":{"operation":"HandleToolRequest","requestId":"SCLK4-INO9D","sessionId":"22becb78f201a05364e2fdd3017a2338924a22ff02bcc77a7a102082f8de990a","tenantId":"authz-tenant","timestamp":"2026-08-20T20:24:16.756Z","toolName":"scoped_echo","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:91:54)\n at withRequiredScopes (file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:60:15)\n at file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:383:17\n at Object.callback [as executor] (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1861:31)\n at McpServer.executeToolHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1450:15)\n at file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1400:31\n at async Server._invokeInputRequiredCapableHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:887:13)\n at async file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:838:19"},"stack":"McpError: Insufficient permissions.\n at ErrorHandler.handleError (file:///Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:178:19)\n at file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:450:26\n at Object.callback [as executor] (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1861:31)\n at McpServer.executeToolHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1450:15)\n at file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1400:31\n at async Server._invokeInputRequiredCapableHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:887:13)\n at async file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:838:19","msg":"Error in tool:scoped_echo: Insufficient permissions."}
|
|
5
|
-
{"level":50,"time":1787257457780,"env":"testing","version":"0.0.0-test","pid":2112,"operation":"HandleToolRequest","requestId":"0VL0G-EUGXV","sessionId":"73208d8b75f52313dc79c52a60167eea89c365751827c90b84dad53015dbe340","timestamp":"2026-08-20T20:24:17.774Z","toolName":"session_cancellable_tool","critical":false,"errorCode":-32004,"originalErrorType":"stringEncountered","finalErrorType":"McpError","errorData":{"operation":"HandleToolRequest","requestId":"0VL0G-EUGXV","sessionId":"73208d8b75f52313dc79c52a60167eea89c365751827c90b84dad53015dbe340","timestamp":"2026-08-20T20:24:17.774Z","toolName":"session_cancellable_tool","originalErrorName":"stringEncountered","originalMessage":"AbortError: cancel tool request"},"stack":"McpError: AbortError: cancel tool request\n at ErrorHandler.handleError (file:///Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:186:19)\n at file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:450:26\n at process.processTicksAndRejections (node:internal/process/task_queues:104:5)\n at async file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1400:20\n at async Server._invokeInputRequiredCapableHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:887:13)\n at async file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:838:19","msg":"Error in tool:session_cancellable_tool: AbortError: cancel tool request"}
|
|
6
|
-
{"level":40,"time":1787257469530,"env":"testing","version":"0.12.2","pid":2213,"operation":"TransportManager.start","requestId":"1UYP2-LCSAT","timestamp":"2026-08-20T20:24:29.529Z","transport":"http","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."}
|
|
7
|
-
{"level":40,"time":1787257471475,"env":"testing","version":"0.12.2","pid":2213,"operation":"HttpRpcRequest","requestId":"9J8HH-QGBPX","sessionId":"not-a-real-session-1787257471474","timestamp":"2026-08-20T20:24:31.475Z","component":"HttpTransport","msg":"Session validation failed - invalid or hijacked session"}
|
|
8
|
-
{"level":50,"time":1787257474800,"env":"testing","version":"0.0.0-test","pid":2235,"auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"operation":"HandleToolRequest","requestId":"6R39F-UVSOT","sessionId":"65a11974803af77e5115ccaad32660eb2ab875016ed0d4332490522fae79e98c","tenantId":"authz-tenant","timestamp":"2026-08-20T20:24:34.799Z","toolName":"scoped_echo","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","errorData":{"operation":"HandleToolRequest","requestId":"6R39F-UVSOT","sessionId":"65a11974803af77e5115ccaad32660eb2ab875016ed0d4332490522fae79e98c","tenantId":"authz-tenant","timestamp":"2026-08-20T20:24:34.799Z","toolName":"scoped_echo","originalErrorName":"McpError","originalMessage":"Insufficient permissions.","originalStack":"McpError: Insufficient permissions.\n at forbidden (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:91:58)\n at withRequiredScopes (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:60:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:383:17)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1861:31)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1450:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1400:31)\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:178:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:450:26)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1861:31)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1450:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1400:31)\n at processTicksAndRejections (native:7:39)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
|
|
9
|
-
{"level":50,"time":1787257474807,"env":"testing","version":"0.0.0-test","pid":2235,"auth":{"sub":"authz-user","scopes":["openid","email","profile","offline_access"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"operation":"HandleToolRequest","requestId":"WYG05-52QRJ","sessionId":"50b46576df7ff245b4ad221cb01746a81b8ffb5ce7395ceef3aeb0b8dc15476d","tenantId":"authz-tenant","timestamp":"2026-08-20T20:24:34.807Z","toolName":"scoped_echo","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","errorData":{"operation":"HandleToolRequest","requestId":"WYG05-52QRJ","sessionId":"50b46576df7ff245b4ad221cb01746a81b8ffb5ce7395ceef3aeb0b8dc15476d","tenantId":"authz-tenant","timestamp":"2026-08-20T20:24:34.807Z","toolName":"scoped_echo","originalErrorName":"McpError","originalMessage":"Insufficient permissions.","originalStack":"McpError: Insufficient permissions.\n at forbidden (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:91:58)\n at withRequiredScopes (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:60:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:383:17)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1861:31)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1450:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1400:31)\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:178:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:450:26)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1861:31)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1450:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1400:31)\n at processTicksAndRejections (native:7:39)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
|
|
10
|
-
{"level":50,"time":1787257475576,"env":"testing","version":"0.0.0-test","pid":2246,"operation":"HandleToolRequest","requestId":"7QR9T-RGZ5H","sessionId":"b7b50d5e87c58a4da184f5a37d899fab22a88979870de391bb25021ed2a60f2c","timestamp":"2026-08-20T20:24:35.572Z","toolName":"session_cancellable_tool","critical":false,"errorCode":-32004,"originalErrorType":"stringEncountered","finalErrorType":"McpError","errorData":{"operation":"HandleToolRequest","requestId":"7QR9T-RGZ5H","sessionId":"b7b50d5e87c58a4da184f5a37d899fab22a88979870de391bb25021ed2a60f2c","timestamp":"2026-08-20T20:24:35.572Z","toolName":"session_cancellable_tool","originalErrorName":"stringEncountered","originalMessage":"AbortError: cancel tool request"},"stack":"McpError: AbortError: cancel tool request\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:186:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:450:26)\n at processTicksAndRejections (native:7:39)","msg":"Error in tool:session_cancellable_tool: AbortError: cancel tool request"}
|
package/dist/logs/error.log
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
{"level":50,"time":1787257456744,"env":"testing","version":"0.0.0-test","pid":2100,"auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"operation":"HandleToolRequest","requestId":"ZC6PT-US941","sessionId":"150a3b106bcd4ba471ddf23e8ea5fab8e71307c5d51656146dfdfbd513728f14","tenantId":"authz-tenant","timestamp":"2026-08-20T20:24:16.744Z","toolName":"scoped_echo","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","errorData":{"operation":"HandleToolRequest","requestId":"ZC6PT-US941","sessionId":"150a3b106bcd4ba471ddf23e8ea5fab8e71307c5d51656146dfdfbd513728f14","tenantId":"authz-tenant","timestamp":"2026-08-20T20:24:16.744Z","toolName":"scoped_echo","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:91:54)\n at withRequiredScopes (file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:60:15)\n at file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:383:17\n at Object.callback [as executor] (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1861:31)\n at McpServer.executeToolHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1450:15)\n at file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1400:31\n at async Server._invokeInputRequiredCapableHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:887:13)\n at async file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:838:19"},"stack":"McpError: Insufficient permissions.\n at ErrorHandler.handleError (file:///Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:178:19)\n at file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:450:26\n at Object.callback [as executor] (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1861:31)\n at McpServer.executeToolHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1450:15)\n at file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1400:31\n at async Server._invokeInputRequiredCapableHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:887:13)\n at async file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:838:19","msg":"Error in tool:scoped_echo: Insufficient permissions."}
|
|
2
|
-
{"level":50,"time":1787257456756,"env":"testing","version":"0.0.0-test","pid":2100,"auth":{"sub":"authz-user","scopes":["openid","email","profile","offline_access"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"operation":"HandleToolRequest","requestId":"SCLK4-INO9D","sessionId":"22becb78f201a05364e2fdd3017a2338924a22ff02bcc77a7a102082f8de990a","tenantId":"authz-tenant","timestamp":"2026-08-20T20:24:16.756Z","toolName":"scoped_echo","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","errorData":{"operation":"HandleToolRequest","requestId":"SCLK4-INO9D","sessionId":"22becb78f201a05364e2fdd3017a2338924a22ff02bcc77a7a102082f8de990a","tenantId":"authz-tenant","timestamp":"2026-08-20T20:24:16.756Z","toolName":"scoped_echo","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:91:54)\n at withRequiredScopes (file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:60:15)\n at file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:383:17\n at Object.callback [as executor] (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1861:31)\n at McpServer.executeToolHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1450:15)\n at file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1400:31\n at async Server._invokeInputRequiredCapableHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:887:13)\n at async file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:838:19"},"stack":"McpError: Insufficient permissions.\n at ErrorHandler.handleError (file:///Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:178:19)\n at file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:450:26\n at Object.callback [as executor] (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1861:31)\n at McpServer.executeToolHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1450:15)\n at file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1400:31\n at async Server._invokeInputRequiredCapableHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:887:13)\n at async file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:838:19","msg":"Error in tool:scoped_echo: Insufficient permissions."}
|
|
3
|
-
{"level":50,"time":1787257457780,"env":"testing","version":"0.0.0-test","pid":2112,"operation":"HandleToolRequest","requestId":"0VL0G-EUGXV","sessionId":"73208d8b75f52313dc79c52a60167eea89c365751827c90b84dad53015dbe340","timestamp":"2026-08-20T20:24:17.774Z","toolName":"session_cancellable_tool","critical":false,"errorCode":-32004,"originalErrorType":"stringEncountered","finalErrorType":"McpError","errorData":{"operation":"HandleToolRequest","requestId":"0VL0G-EUGXV","sessionId":"73208d8b75f52313dc79c52a60167eea89c365751827c90b84dad53015dbe340","timestamp":"2026-08-20T20:24:17.774Z","toolName":"session_cancellable_tool","originalErrorName":"stringEncountered","originalMessage":"AbortError: cancel tool request"},"stack":"McpError: AbortError: cancel tool request\n at ErrorHandler.handleError (file:///Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:186:19)\n at file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:450:26\n at process.processTicksAndRejections (node:internal/process/task_queues:104:5)\n at async file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1400:20\n at async Server._invokeInputRequiredCapableHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:887:13)\n at async file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:838:19","msg":"Error in tool:session_cancellable_tool: AbortError: cancel tool request"}
|
|
4
|
-
{"level":50,"time":1787257474800,"env":"testing","version":"0.0.0-test","pid":2235,"auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"operation":"HandleToolRequest","requestId":"6R39F-UVSOT","sessionId":"65a11974803af77e5115ccaad32660eb2ab875016ed0d4332490522fae79e98c","tenantId":"authz-tenant","timestamp":"2026-08-20T20:24:34.799Z","toolName":"scoped_echo","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","errorData":{"operation":"HandleToolRequest","requestId":"6R39F-UVSOT","sessionId":"65a11974803af77e5115ccaad32660eb2ab875016ed0d4332490522fae79e98c","tenantId":"authz-tenant","timestamp":"2026-08-20T20:24:34.799Z","toolName":"scoped_echo","originalErrorName":"McpError","originalMessage":"Insufficient permissions.","originalStack":"McpError: Insufficient permissions.\n at forbidden (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:91:58)\n at withRequiredScopes (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:60:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:383:17)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1861:31)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1450:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1400:31)\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:178:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:450:26)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1861:31)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1450:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1400:31)\n at processTicksAndRejections (native:7:39)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
|
|
5
|
-
{"level":50,"time":1787257474807,"env":"testing","version":"0.0.0-test","pid":2235,"auth":{"sub":"authz-user","scopes":["openid","email","profile","offline_access"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"operation":"HandleToolRequest","requestId":"WYG05-52QRJ","sessionId":"50b46576df7ff245b4ad221cb01746a81b8ffb5ce7395ceef3aeb0b8dc15476d","tenantId":"authz-tenant","timestamp":"2026-08-20T20:24:34.807Z","toolName":"scoped_echo","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","errorData":{"operation":"HandleToolRequest","requestId":"WYG05-52QRJ","sessionId":"50b46576df7ff245b4ad221cb01746a81b8ffb5ce7395ceef3aeb0b8dc15476d","tenantId":"authz-tenant","timestamp":"2026-08-20T20:24:34.807Z","toolName":"scoped_echo","originalErrorName":"McpError","originalMessage":"Insufficient permissions.","originalStack":"McpError: Insufficient permissions.\n at forbidden (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:91:58)\n at withRequiredScopes (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:60:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:383:17)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1861:31)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1450:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1400:31)\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:178:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:450:26)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1861:31)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1450:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs:1400:31)\n at processTicksAndRejections (native:7:39)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
|
|
6
|
-
{"level":50,"time":1787257475576,"env":"testing","version":"0.0.0-test","pid":2246,"operation":"HandleToolRequest","requestId":"7QR9T-RGZ5H","sessionId":"b7b50d5e87c58a4da184f5a37d899fab22a88979870de391bb25021ed2a60f2c","timestamp":"2026-08-20T20:24:35.572Z","toolName":"session_cancellable_tool","critical":false,"errorCode":-32004,"originalErrorType":"stringEncountered","finalErrorType":"McpError","errorData":{"operation":"HandleToolRequest","requestId":"7QR9T-RGZ5H","sessionId":"b7b50d5e87c58a4da184f5a37d899fab22a88979870de391bb25021ed2a60f2c","timestamp":"2026-08-20T20:24:35.572Z","toolName":"session_cancellable_tool","originalErrorName":"stringEncountered","originalMessage":"AbortError: cancel tool request"},"stack":"McpError: AbortError: cancel tool request\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:186:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:450:26)\n at processTicksAndRejections (native:7:39)","msg":"Error in tool:session_cancellable_tool: AbortError: cancel tool request"}
|
|
File without changes
|