@cyanheads/mcp-ts-core 0.10.7 → 0.10.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -1
- package/CLAUDE.md +1 -1
- package/README.md +1 -1
- package/changelog/0.10.x/0.10.8.md +19 -0
- package/dist/core/app.d.ts +6 -1
- package/dist/core/app.d.ts.map +1 -1
- package/dist/core/app.js.map +1 -1
- package/dist/core/context.d.ts +63 -1
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/context.js +56 -0
- package/dist/core/context.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/logs/combined.log +8 -8
- package/dist/logs/error.log +4 -4
- package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts.map +1 -1
- package/dist/mcp-server/tools/utils/toolHandlerFactory.js +9 -1
- package/dist/mcp-server/tools/utils/toolHandlerFactory.js.map +1 -1
- package/dist/services/canvas/core/sqlGate.d.ts +2 -0
- package/dist/services/canvas/core/sqlGate.d.ts.map +1 -1
- package/dist/services/canvas/core/sqlGate.js +2 -0
- package/dist/services/canvas/core/sqlGate.js.map +1 -1
- package/dist/services/canvas/providers/duckdb/DuckdbProvider.d.ts.map +1 -1
- package/dist/services/canvas/providers/duckdb/DuckdbProvider.js +39 -0
- package/dist/services/canvas/providers/duckdb/DuckdbProvider.js.map +1 -1
- package/dist/testing/index.d.ts +15 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +22 -1
- package/dist/testing/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/add-tool/SKILL.md +15 -1
- package/skills/api-canvas/SKILL.md +3 -1
- package/skills/api-config/SKILL.md +2 -2
- package/skills/api-context/SKILL.md +55 -2
- package/skills/api-telemetry/SKILL.md +2 -2
- package/skills/polish-docs-meta/SKILL.md +2 -2
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.15"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -252,6 +252,20 @@ enrichmentTrailer: {
|
|
|
252
252
|
|
|
253
253
|
`structuredContent` always keeps the full structured value; `enrichmentTrailer` only controls the human-facing `content[]` line.
|
|
254
254
|
|
|
255
|
+
### Image / audio output belongs in `ctx.content`
|
|
256
|
+
|
|
257
|
+
When a tool produces image or audio bytes for the calling model to *see or hear* — a rendered chart, a generated frame, synthesized speech — emit them via `ctx.content`, not an `output` field. `ctx.content.image(data, mimeType)` / `.audio(data, mimeType)` prepend a content block to `content[]` after `format()` runs and **never** write to `structuredContent`, so the base64 is carried once instead of duplicating into the typed output. Like `ctx.enrich`, it lives on the base `Context` and is callable from the service layer.
|
|
258
|
+
|
|
259
|
+
```ts
|
|
260
|
+
async handler(input, ctx) {
|
|
261
|
+
const png = await render(input.spec); // base64 PNG
|
|
262
|
+
ctx.content.image(png, 'image/png'); // → content[] block, not structuredContent
|
|
263
|
+
return { width: input.spec.w, height: input.spec.h }; // typed result stays small
|
|
264
|
+
},
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
The alternative — declaring `previewData: z.string()` in `output` and emitting the block from `format()` — ships the bytes twice (once in `structuredContent`, once in the block). Reserve `output` for data the agent reasons over; route raw media through `ctx.content`. Test with `getContentBlocks(ctx)`. Full reference: `skills/api-context` § `ctx.content`.
|
|
268
|
+
|
|
255
269
|
### Capped lists must disclose truncation
|
|
256
270
|
|
|
257
271
|
When a tool accepts a cap-like input (`limit`, `per_page`, `page_size`, `max_results`, `max_items`) and returns an array, disclose when the cap was hit — the agent otherwise treats a partial set as complete.
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
DataCanvas primitive reference — a Tier 3 SQL/analytical workspace for tabular MCP servers, backed by DuckDB. Use when registering tables from upstream APIs, running ad-hoc SQL across them, and exporting results. Covers the acquire → register → query → export flow, per-table TTL, the token-sharing pattern for multi-agent collaboration, env config, and Cloudflare Workers fail-closed behavior.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.7"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -150,6 +150,8 @@ Run SQL across registered tables. Returns at most `rowLimit` rows (default 10 00
|
|
|
150
150
|
|
|
151
151
|
Querying a table that does not exist throws `NotFound` (`data.reason: 'missing_table'`) with a recovery hint to re-stage the table or call `describe()`. This happens when a table has expired (per-table TTL), been dropped, or the name is mistyped. The error is `NotFound`, not `ValidationError` — agents should re-stage, not fix the SQL shape.
|
|
152
152
|
|
|
153
|
+
A `SELECT` that parses but fails to prepare for any other reason — a mistyped column, an unknown function, an invalid expression — throws `ValidationError` (`data.reason: 'invalid_sql'`) and preserves the DuckDB binder detail in `data.binderMessage` (e.g. `Referenced column "x" not found...`, often with a candidate suggestion). This is distinct from `non_select_statement`, reserved for statements that genuinely aren't `SELECT`s — here the shape is fine, so the agent should fix the named column or function.
|
|
154
|
+
|
|
153
155
|
```ts
|
|
154
156
|
const result = await instance.query(`
|
|
155
157
|
SELECT germplasmName, COUNT(*) AS n
|
|
@@ -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.8"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -167,7 +167,7 @@ Activated when both `SUPABASE_URL` and `SUPABASE_ANON_KEY` are set.
|
|
|
167
167
|
| Env Var | `AppConfig` field | Default | Notes |
|
|
168
168
|
|:--------|:-----------------|:--------|:------|
|
|
169
169
|
| `OTEL_ENABLED` | `openTelemetry.enabled` | `false` | Enable OpenTelemetry export |
|
|
170
|
-
| `OTEL_SERVICE_NAME` | `openTelemetry.serviceName` | `package.json` `name` | |
|
|
170
|
+
| `OTEL_SERVICE_NAME` | `openTelemetry.serviceName` | `createApp` `name` → `package.json` `name` | Seeded from `createApp({ name })` when unset; an env value wins |
|
|
171
171
|
| `OTEL_SERVICE_VERSION` | `openTelemetry.serviceVersion` | `package.json` `version` | |
|
|
172
172
|
| `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | `openTelemetry.tracesEndpoint` | — | OTLP traces endpoint URL |
|
|
173
173
|
| `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` | `openTelemetry.metricsEndpoint` | — | OTLP metrics endpoint URL |
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: api-context
|
|
3
3
|
description: >
|
|
4
|
-
Canonical reference for the unified `Context` object passed to every tool and resource handler in `@cyanheads/mcp-ts-core`. Covers the full interface, all sub-APIs (`ctx.log`, `ctx.state`, `ctx.elicit`, `ctx.progress`, `ctx.enrich`), and when to use each.
|
|
4
|
+
Canonical reference for the unified `Context` object passed to every tool and resource handler in `@cyanheads/mcp-ts-core`. Covers the full interface, all sub-APIs (`ctx.log`, `ctx.state`, `ctx.elicit`, `ctx.progress`, `ctx.enrich`, `ctx.content`), and when to use each.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.9"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -63,6 +63,12 @@ interface Context {
|
|
|
63
63
|
// declared fields. Kind-tagged helpers: enrich.notice / .total / .echo.
|
|
64
64
|
readonly enrich: Enrich;
|
|
65
65
|
|
|
66
|
+
// Non-text content blocks (image/audio bytes) for the calling model — prepended
|
|
67
|
+
// to content[] after format() runs, never placed in structuredContent. Always
|
|
68
|
+
// present (no-op when never called). Helpers: content.image / .audio; content(block)
|
|
69
|
+
// pushes a raw ContentBlock.
|
|
70
|
+
readonly content: ContentCollect;
|
|
71
|
+
|
|
66
72
|
// Opt-in contract resolver — always present (returns {} when no contract is attached
|
|
67
73
|
// or the reason is unknown), strictly typed on HandlerContext<R> against declared reasons.
|
|
68
74
|
recoveryFor(reason: string): { recovery: { hint: string } } | {};
|
|
@@ -637,6 +643,52 @@ See `add-tool`'s **Tool Response Design** and `skills/api-linter` (`enrichment-*
|
|
|
637
643
|
|
|
638
644
|
---
|
|
639
645
|
|
|
646
|
+
## `ctx.content`
|
|
647
|
+
|
|
648
|
+
Always present on `Context`. Collects **non-text content blocks** — image or audio bytes the calling model should see or hear — and prepends them to the tool's `content[]` after `format()` runs. Collected blocks **never** enter `structuredContent`, so the base64 payload is carried once (in `content[]`) instead of duplicating into the typed output field. The media counterpart to `ctx.enrich`: both ride alongside the domain result without bloating it.
|
|
649
|
+
|
|
650
|
+
```ts
|
|
651
|
+
export const renderChart = tool('render_chart', {
|
|
652
|
+
description: 'Render a chart from a series and return its summary.',
|
|
653
|
+
input: z.object({ series: z.array(z.number()).describe('Data points') }),
|
|
654
|
+
output: z.object({ points: z.number().describe('Number of points plotted') }),
|
|
655
|
+
async handler(input, ctx) {
|
|
656
|
+
const png = await draw(input.series); // base64 PNG
|
|
657
|
+
ctx.content.image(png, 'image/png'); // → content[] block, NOT structuredContent
|
|
658
|
+
return { points: input.series.length }; // the typed result stays small
|
|
659
|
+
},
|
|
660
|
+
});
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
Without `ctx.content`, the only way to surface bytes to the model is to declare them in `output` and emit an image block from `format()` — which ships the base64 twice (once in `structuredContent`, once in the block). `ctx.content` removes the duplication.
|
|
664
|
+
|
|
665
|
+
### Signature
|
|
666
|
+
|
|
667
|
+
```ts
|
|
668
|
+
// Callable — push a raw ContentBlock (escape hatch for embedded resources, resource links):
|
|
669
|
+
ctx.content(block: ContentBlock): void
|
|
670
|
+
|
|
671
|
+
// Typed helpers for the two base64 media blocks:
|
|
672
|
+
ctx.content.image(data: string, mimeType: string): void // → { type: 'image', data, mimeType }
|
|
673
|
+
ctx.content.audio(data: string, mimeType: string): void // → { type: 'audio', data, mimeType }
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
### Behavior
|
|
677
|
+
|
|
678
|
+
| Aspect | Detail |
|
|
679
|
+
|:-------|:-------|
|
|
680
|
+
| `content[]` only | Blocks are prepended to `content[]` and never written to `structuredContent`. Data meant for the typed result stays on the handler's return value. |
|
|
681
|
+
| Order | `content[]` is `[...collected blocks, ...format()/JSON output, ...enrichment trailer]` — media first, domain content next, enrichment trailer last. |
|
|
682
|
+
| Accumulation | Each call appends; blocks render in call order. |
|
|
683
|
+
| No-op | A handler that never calls `ctx.content` produces a `content[]` / `structuredContent` byte-identical to before — the feature is purely additive and opt-in. |
|
|
684
|
+
| Error path | If the handler throws, collected blocks are dropped — a failed call returns the error result only, never a partial image. |
|
|
685
|
+
| Service usage | Services accepting `ctx: Context` can call `ctx.content(...)`; the blocks reach `content[]` exactly as if the handler had. |
|
|
686
|
+
| No schema involvement | Blocks bypass `output` entirely, so no linter rule requires them in `format()` and they never appear in the advertised `outputSchema`. |
|
|
687
|
+
|
|
688
|
+
Test content blocks with `getContentBlocks(ctx)` from `@cyanheads/mcp-ts-core/testing`.
|
|
689
|
+
|
|
690
|
+
---
|
|
691
|
+
|
|
640
692
|
## Quick reference
|
|
641
693
|
|
|
642
694
|
| Property | Type | Present when |
|
|
@@ -652,6 +704,7 @@ See `add-tool`'s **Tool Response Design** and `skills/api-linter` (`enrichment-*
|
|
|
652
704
|
| `ctx.state` | `ContextState` | Always (throws if `tenantId` missing) |
|
|
653
705
|
| `ctx.signal` | `AbortSignal` | Always |
|
|
654
706
|
| `ctx.enrich` | `Enrich` | Always; typed on `HandlerContext<R, E>` when an `enrichment` block is declared |
|
|
707
|
+
| `ctx.content` | `ContentCollect` | Always — prepends image/audio blocks to `content[]`, never `structuredContent` |
|
|
655
708
|
| `ctx.elicit` | `function \| undefined` | Client supports elicitation |
|
|
656
709
|
| `ctx.notifyResourceListChanged` | `function \| undefined` | Always in handler ctx; delivery request-scoped (see [§ list-changed notifications](#list-changed-notifications-ctxnotify)) |
|
|
657
710
|
| `ctx.notifyResourceUpdated` | `function \| undefined` | Always in handler ctx; delivery request-scoped |
|
|
@@ -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.2"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -28,7 +28,7 @@ OTel is **off by default**. `OTEL_ENABLED=true` alone does nothing — you also
|
|
|
28
28
|
| `OTEL_ENABLED` | `false` | Master switch. Must be `true` to start the SDK. |
|
|
29
29
|
| `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | — | OTLP/HTTP traces endpoint (e.g. `http://localhost:4318/v1/traces`). |
|
|
30
30
|
| `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` | — | OTLP/HTTP metrics endpoint (e.g. `http://localhost:4318/v1/metrics`). |
|
|
31
|
-
| `OTEL_SERVICE_NAME` | `package.json` `name` | `service.name` resource attribute. |
|
|
31
|
+
| `OTEL_SERVICE_NAME` | `createApp` `name` → `package.json` `name` | `service.name` resource attribute. Seeded from `createApp({ name })` when unset; an env value wins. |
|
|
32
32
|
| `OTEL_SERVICE_VERSION` | `package.json` `version` | `service.version` resource attribute. |
|
|
33
33
|
| `OTEL_TRACES_SAMPLER_ARG` | `1.0` | Trace sampling ratio (0–1) for `TraceIdRatioBasedSampler`. |
|
|
34
34
|
| `OTEL_LOG_LEVEL` | `INFO` | OTel diagnostic logger level (`NONE`/`ERROR`/`WARN`/`INFO`/`DEBUG`/`VERBOSE`/`ALL`). |
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Finalize documentation and project metadata for a ship-ready MCP server. Use after implementation is complete, tests pass, and devcheck is clean. Safe to run at any stage — each step checks current state and only acts on what still needs work.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "2.
|
|
7
|
+
version: "2.8"
|
|
8
8
|
audience: external
|
|
9
9
|
type: workflow
|
|
10
10
|
---
|
|
@@ -76,7 +76,7 @@ Key fields: `name`, `description`, `repository`, `author`, `homepage`, `bugs`, `
|
|
|
76
76
|
|
|
77
77
|
**`name` must communicate the server's domain at a glance.** See `references/package-meta.md` for the naming convention — ambiguous abbreviations and acronym-only names fail the scannability test for humans and agents alike.
|
|
78
78
|
|
|
79
|
-
**`name` and `title` in `createApp()` / `createWorkerHandler()` must match the unscoped `package.json` `name`** — display identity is the machine name on every surface; `lint:packaging` (run by `devcheck`) enforces the match and warns when the pair is partial. `description` is never duplicated into the entrypoint — `package.json` is the canonical source (the framework derives the served description from it).
|
|
79
|
+
**`name` and `title` in `createApp()` / `createWorkerHandler()` must match the unscoped `package.json` `name`** — display identity is the machine name on every surface; `lint:packaging` (run by `devcheck`) enforces the match and warns when the pair is partial. `description` is never duplicated into the entrypoint — `package.json` is the canonical source (the framework derives the served description from it). Adopting the pair also seeds `OTEL_SERVICE_NAME` when unset, so telemetry's `service.name` switches to the machine name on first boot — expect a one-time series split in backends keyed on the old scoped label.
|
|
80
80
|
|
|
81
81
|
**`description` is the canonical source.** Every other surface (README header, `server.json`, Dockerfile OCI label, GitHub repo description) derives from it. Write it here first, then propagate.
|
|
82
82
|
|