stitchkit 0.122.0 → 0.122.1
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/CHANGELOG.md +21 -0
- package/dist/entrypoints/cli.js +2 -1
- package/dist/entrypoints/tools/connections.js +7 -1
- package/dist/entrypoints/tools/mcp.js +2 -1
- package/dist/entrypoints/tools/toolkit.js +3 -2
- package/dist/index-332d0m9g.js +37 -0
- package/dist/{index-v8yqk4w7.js → index-aswegq1r.js} +3 -5
- package/dist/{index-knmnj11j.js → index-y6ye582c.js} +7 -5
- package/dist/tools/connections/mcp.d.ts.map +1 -1
- package/dist/tools/mcp/catalog.d.ts.map +1 -1
- package/dist/tools/mcp/mount.d.ts.map +1 -1
- package/dist/tools/mcp/register.d.ts +1 -2
- package/dist/tools/mcp/register.d.ts.map +1 -1
- package/dist/tools/mcp/runtime-tools.d.ts.map +1 -1
- package/dist/tools/read-operation.d.ts +21 -0
- package/dist/tools/read-operation.d.ts.map +1 -0
- package/llms/cli.txt +12 -3
- package/llms/tools.2.txt +109 -0
- package/llms/tools.txt +30 -109
- package/llms-full.txt +42 -3
- package/llms.txt +2 -2
- package/package.json +1 -1
- package/dist/tools/cli/repeatable-read.d.ts +0 -9
- package/dist/tools/cli/repeatable-read.d.ts.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,27 @@ additive**; the first breaking change landed in 0.10.0. Grep the file for
|
|
|
15
15
|
|
|
16
16
|
## [Unreleased]
|
|
17
17
|
|
|
18
|
+
## [0.122.1] - 2026-11-10
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- `stitchkit/tools/mcp` — the MCP server publishes `readOnlyHint: true` for a `GET` tool that
|
|
23
|
+
declared no `readOnlyHint`; it published only declared annotations, so a read was
|
|
24
|
+
indistinguishable from a write to every MCP client. A declared `readOnlyHint` is published as
|
|
25
|
+
written, and a `GET` declaring `destructiveHint: true` gets none. The catalog digest hashes the
|
|
26
|
+
published annotations and moves once on upgrade. A `GET` endpoint with an effect must declare
|
|
27
|
+
`readOnlyHint: false`: a host may run a read-only tool without asking. → ADR 0287
|
|
28
|
+
**Affects:** `stitchkit/tools/mcp` behaviour
|
|
29
|
+
- `stitchkit/tools/connections` — `defineMcpClientConnection` carries the annotations a server
|
|
30
|
+
published onto the tools it mounts; it dropped them, so a CLI built over an MCP surface printed
|
|
31
|
+
a read's complete result on a refused view (`--table` with an unknown column) while the same
|
|
32
|
+
command over the contract printed nothing. A hint of the wrong type is dropped and the tool
|
|
33
|
+
stays. The mounted tool is as read-only as its server says it is. → ADR 0287
|
|
34
|
+
**Affects:** `stitchkit/tools/connections` behaviour
|
|
35
|
+
- `stitchkit/cli` — a `GET` command declaring `destructiveHint: true` without `readOnlyHint`
|
|
36
|
+
keeps its result on stdout after a refused view; it was treated as a read. → ADR 0287
|
|
37
|
+
**Affects:** `stitchkit/cli` behaviour
|
|
38
|
+
|
|
18
39
|
## [0.122.0] - 2026-11-10
|
|
19
40
|
|
|
20
41
|
### ⚠️ Breaking changes
|
package/dist/entrypoints/cli.js
CHANGED
|
@@ -16,13 +16,14 @@ import {
|
|
|
16
16
|
renderCliView2,
|
|
17
17
|
pollUntilDone2,
|
|
18
18
|
createCli2
|
|
19
|
-
} from "../index-
|
|
19
|
+
} from "../index-aswegq1r.js";
|
|
20
20
|
import"../index-k42ckyzx.js";
|
|
21
21
|
import"../index-ct75c3at.js";
|
|
22
22
|
import"../index-x4p3kw1p.js";
|
|
23
23
|
import"../index-s3yxw6kt.js";
|
|
24
24
|
import"../index-69t14s4a.js";
|
|
25
25
|
import"../index-ycqhyars.js";
|
|
26
|
+
import"../index-332d0m9g.js";
|
|
26
27
|
import"../index-m5m9rmvt.js";
|
|
27
28
|
import {
|
|
28
29
|
CliBuildSignatureSchema2,
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readDiscoveredToolAnnotations
|
|
3
|
+
} from "../../index-332d0m9g.js";
|
|
1
4
|
import"../../index-69t14s4a.js";
|
|
2
5
|
import"../../index-ct75c3at.js";
|
|
3
6
|
import {
|
|
@@ -919,6 +922,7 @@ async function mountMcpConnection(connection, instanceId, onSkipped) {
|
|
|
919
922
|
name,
|
|
920
923
|
description,
|
|
921
924
|
identity: { serviceName: connection.name, action: name, method: "POST" },
|
|
925
|
+
...tool.annotations && { annotations: tool.annotations },
|
|
922
926
|
...connection.transports && { transports: connection.transports },
|
|
923
927
|
input,
|
|
924
928
|
output: z2.unknown(),
|
|
@@ -945,10 +949,12 @@ function readDiscoveredTools(listed, connectionName) {
|
|
|
945
949
|
for (const raw of listed.tools) {
|
|
946
950
|
if (!isRecord(raw) || typeof raw.name !== "string")
|
|
947
951
|
continue;
|
|
952
|
+
const annotations = readDiscoveredToolAnnotations(raw.annotations);
|
|
948
953
|
tools.push({
|
|
949
954
|
name: raw.name,
|
|
950
955
|
...typeof raw.description === "string" && { description: raw.description },
|
|
951
|
-
...isRecord(raw.inputSchema) && { inputSchema: raw.inputSchema }
|
|
956
|
+
...isRecord(raw.inputSchema) && { inputSchema: raw.inputSchema },
|
|
957
|
+
...annotations && { annotations }
|
|
952
958
|
});
|
|
953
959
|
}
|
|
954
960
|
return tools;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import"../../index-ct75c3at.js";
|
|
2
|
+
import"../../index-332d0m9g.js";
|
|
2
3
|
import"../../index-k42ckyzx.js";
|
|
3
4
|
import"../../index-dsa676h8.js";
|
|
4
5
|
import"../../index-rbzq0q16.js";
|
|
@@ -20,7 +21,7 @@ import {
|
|
|
20
21
|
createMcpHandler2,
|
|
21
22
|
createMcpHttpRoute2,
|
|
22
23
|
createStdioMcpServer2
|
|
23
|
-
} from "../../index-
|
|
24
|
+
} from "../../index-y6ye582c.js";
|
|
24
25
|
import {
|
|
25
26
|
DEFAULT_PROCESS_SIGNALS,
|
|
26
27
|
defaultSignalSource,
|
|
@@ -11,14 +11,15 @@ import {
|
|
|
11
11
|
} from "../../index-1fyea34p.js";
|
|
12
12
|
import {
|
|
13
13
|
createCli2
|
|
14
|
-
} from "../../index-
|
|
14
|
+
} from "../../index-aswegq1r.js";
|
|
15
|
+
import"../../index-332d0m9g.js";
|
|
15
16
|
import"../../index-m5m9rmvt.js";
|
|
16
17
|
import {
|
|
17
18
|
mountMcp2,
|
|
18
19
|
buildMcpServer2,
|
|
19
20
|
createMcpHandler2,
|
|
20
21
|
createStdioMcpServer2
|
|
21
|
-
} from "../../index-
|
|
22
|
+
} from "../../index-y6ye582c.js";
|
|
22
23
|
import"../../index-aqmadanx.js";
|
|
23
24
|
import"../../index-eeahxwvg.js";
|
|
24
25
|
import"../../index-ff39f9dr.js";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// src/tools/read-operation.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
function isRepeatableRead(operation) {
|
|
4
|
+
const declared = operation.annotations;
|
|
5
|
+
if (declared?.readOnlyHint !== undefined)
|
|
6
|
+
return declared.readOnlyHint;
|
|
7
|
+
return operation.method === "GET" && declared?.destructiveHint !== true;
|
|
8
|
+
}
|
|
9
|
+
function publishedToolAnnotations(operation) {
|
|
10
|
+
const declared = operation.annotations;
|
|
11
|
+
if (declared?.readOnlyHint !== undefined || !isRepeatableRead(operation))
|
|
12
|
+
return declared;
|
|
13
|
+
return { ...declared, readOnlyHint: true };
|
|
14
|
+
}
|
|
15
|
+
var DiscoveredToolAnnotationsSchema = z.object({
|
|
16
|
+
title: z.string().optional().catch(undefined),
|
|
17
|
+
readOnlyHint: z.boolean().optional().catch(undefined),
|
|
18
|
+
destructiveHint: z.boolean().optional().catch(undefined),
|
|
19
|
+
idempotentHint: z.boolean().optional().catch(undefined),
|
|
20
|
+
openWorldHint: z.boolean().optional().catch(undefined)
|
|
21
|
+
});
|
|
22
|
+
function readDiscoveredToolAnnotations(raw) {
|
|
23
|
+
const parsed = DiscoveredToolAnnotationsSchema.safeParse(raw);
|
|
24
|
+
if (!parsed.success)
|
|
25
|
+
return;
|
|
26
|
+
const { title, readOnlyHint, destructiveHint, idempotentHint, openWorldHint } = parsed.data;
|
|
27
|
+
const annotations = {
|
|
28
|
+
...title !== undefined && { title },
|
|
29
|
+
...readOnlyHint !== undefined && { readOnlyHint },
|
|
30
|
+
...destructiveHint !== undefined && { destructiveHint },
|
|
31
|
+
...idempotentHint !== undefined && { idempotentHint },
|
|
32
|
+
...openWorldHint !== undefined && { openWorldHint }
|
|
33
|
+
};
|
|
34
|
+
return Object.keys(annotations).length > 0 ? annotations : undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export { isRepeatableRead, publishedToolAnnotations, readDiscoveredToolAnnotations };
|
|
@@ -23,6 +23,9 @@ import {
|
|
|
23
23
|
createToolRunner,
|
|
24
24
|
formatToolError
|
|
25
25
|
} from "./index-ycqhyars.js";
|
|
26
|
+
import {
|
|
27
|
+
isRepeatableRead
|
|
28
|
+
} from "./index-332d0m9g.js";
|
|
26
29
|
import {
|
|
27
30
|
writeContainedFile
|
|
28
31
|
} from "./index-aqmadanx.js";
|
|
@@ -1782,11 +1785,6 @@ async function prepareInvocation(command, commandArgv, descriptor, config, readS
|
|
|
1782
1785
|
return { ok: true, toolArgs, options };
|
|
1783
1786
|
}
|
|
1784
1787
|
|
|
1785
|
-
// src/tools/cli/repeatable-read.ts
|
|
1786
|
-
function isRepeatableRead(operation) {
|
|
1787
|
-
return operation.annotations?.readOnlyHint ?? operation.method === "GET";
|
|
1788
|
-
}
|
|
1789
|
-
|
|
1790
1788
|
// src/tools/cli/session.ts
|
|
1791
1789
|
function nativeDescriptor(definition) {
|
|
1792
1790
|
return {
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
toJsonSchema
|
|
3
3
|
} from "./index-ct75c3at.js";
|
|
4
|
+
import {
|
|
5
|
+
publishedToolAnnotations
|
|
6
|
+
} from "./index-332d0m9g.js";
|
|
4
7
|
import {
|
|
5
8
|
toolSurfaceOutputSchema,
|
|
6
9
|
staticInputRounds,
|
|
@@ -86,7 +89,7 @@ function toolFingerprint(descriptor) {
|
|
|
86
89
|
description: mountable.method.desc,
|
|
87
90
|
input: descriptor.inputSchema,
|
|
88
91
|
output: descriptor.outputSchema ? toJsonSchema(descriptor.outputSchema, "output", "any") : null,
|
|
89
|
-
annotations: mountable.method
|
|
92
|
+
annotations: publishedToolAnnotations(mountable.method) ?? null
|
|
90
93
|
};
|
|
91
94
|
}
|
|
92
95
|
function mcpCatalogStamp(surface) {
|
|
@@ -556,8 +559,9 @@ function registerMcpTool(server, presentation, descriptor, config) {
|
|
|
556
559
|
};
|
|
557
560
|
if (descriptor.outputSchema)
|
|
558
561
|
toolConfig.outputSchema = descriptor.outputSchema;
|
|
559
|
-
|
|
560
|
-
|
|
562
|
+
const annotations = publishedToolAnnotations(mountable.method);
|
|
563
|
+
if (annotations)
|
|
564
|
+
toolConfig.annotations = annotations;
|
|
561
565
|
if (presentation.ui) {
|
|
562
566
|
toolConfig._meta = {
|
|
563
567
|
ui: presentation.ui,
|
|
@@ -665,7 +669,6 @@ function mountPreparedRuntimeMcp(server, tools, config) {
|
|
|
665
669
|
registerMcpTool(server, {
|
|
666
670
|
name: definition.name,
|
|
667
671
|
description: definition.description,
|
|
668
|
-
annotations: definition.annotations,
|
|
669
672
|
ui: definition.ui,
|
|
670
673
|
...present && { present }
|
|
671
674
|
}, descriptor, {
|
|
@@ -716,7 +719,6 @@ function mountPreparedMcp(mcpServer, prepared, config = {}) {
|
|
|
716
719
|
registerMcpTool(mcpServer, {
|
|
717
720
|
name,
|
|
718
721
|
description: method.desc,
|
|
719
|
-
annotations: method.annotations,
|
|
720
722
|
ui: method.ui
|
|
721
723
|
}, descriptor, {
|
|
722
724
|
runTool,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../../src/tools/connections/mcp.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../../src/tools/connections/mcp.ts"],"names":[],"mappings":"AAIA,OAAO,EAAqB,KAAK,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAIhF,OAAO,EACL,KAAK,0BAA0B,EAIhC,MAAM,WAAW,CAAC;AAGnB,OAAO,KAAK,EAAE,mBAAmB,EAAiB,MAAM,SAAS,CAAC;AAUlE,gEAAgE;AAChE,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,mBAAmB,EAC/B,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,0BAA0B,GACpC,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAyElC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../../src/tools/mcp/catalog.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../../src/tools/mcp/catalog.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,wBAAwB,EAAmB,MAAM,WAAW,CAAC;AAE3E;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,sBAAsB,CAAC;AAExD;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAyBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,wBAAwB,GAAG,eAAe,CAclF;AAED,6DAA6D;AAC7D,wBAAgB,cAAc,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAE9E;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG,eAAe,GAAG,IAAI,CAQzE;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,SAAS;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,EACvF,MAAM,EAAE,OAAO,EACf,KAAK,EAAE,eAAe,GAAG,SAAS,GACjC,OAAO,CAGT;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,OAAO,SAAS;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,EACjF,MAAM,EAAE,OAAO,EACf,KAAK,EAAE,eAAe,GAAG,SAAS,GACjC,OAAO,CAGT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../../../src/tools/mcp/mount.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,SAAS,EAET,KAAK,aAAa,EACnB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAClF,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAC7D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACzE,OAAO,EAAE,KAAK,cAAc,EAAsB,MAAM,OAAO,CAAC;AAChE,OAAO,EAAE,KAAK,eAAe,EAAmB,MAAM,WAAW,CAAC;AAClE,OAAO,EAEL,KAAK,yBAAyB,EAE9B,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,EAC7B,KAAK,kBAAkB,EAGvB,KAAK,wBAAwB,EAC9B,MAAM,WAAW,CAAC;AAEnB,OAAO,KAAK,EAAE,eAAe,EAAiB,MAAM,SAAS,CAAC;AAG9D;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,qCAAqC;IACrC,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,qEAAqE;IACrE,gBAAgB,CAAC,EAAE,yBAAyB,CAAC;IAC7C,8DAA8D;IAC9D,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,+EAA+E;IAC/E,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,+EAA+E;IAC/E,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,gEAAgE;IAChE,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,mEAAmE;IACnE,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CAC7D;AAED,UAAU,sBAAuB,SAAQ,cAAc;IACrD,iBAAiB,CAAC,EAAE,eAAe,CAAC;IACpC,8EAA8E;IAC9E,OAAO,CAAC,EAAE,eAAe,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,wBAAwB,GAAG,IAAI,CAOzE;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CACtB,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,UAAU,GAAG,UAAU,EAAE,EACnC,MAAM,GAAE,cAAmB,GAC1B,IAAI,CAWN;AAED,2EAA2E;AAC3E,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,kBAAkB,EAC5B,MAAM,GAAE,sBAA2B,GAClC,IAAI,
|
|
1
|
+
{"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../../../src/tools/mcp/mount.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type CallToolResult, type McpServer } from '@modelcontextprotocol/server';
|
|
2
|
-
import type {
|
|
2
|
+
import type { EndpointUiMeta } from '../../contract/tool-options.js';
|
|
3
3
|
import type { ToolExecutionOptions } from '../execute.js';
|
|
4
4
|
import type { ToolCallHooks } from '../execute-hooks.js';
|
|
5
5
|
import { type ToolResult } from '../execute-result.js';
|
|
@@ -11,7 +11,6 @@ import { type McpRoundRuntime } from './round.js';
|
|
|
11
11
|
export interface McpToolPresentation {
|
|
12
12
|
name: string;
|
|
13
13
|
description: string;
|
|
14
|
-
annotations?: EndpointToolAnnotations;
|
|
15
14
|
ui?: EndpointUiMeta;
|
|
16
15
|
/** A runtime tool's own MCP answer, built from its validated output. */
|
|
17
16
|
present?(data: unknown): unknown | Promise<unknown>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../src/tools/mcp/register.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,cAAc,EAEnB,KAAK,SAAS,EACf,MAAM,8BAA8B,CAAC;AAGtC,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../src/tools/mcp/register.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,cAAc,EAEnB,KAAK,SAAS,EACf,MAAM,8BAA8B,CAAC;AAGtC,OAAO,KAAK,EAA2B,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAE3F,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,KAAK,EAAmB,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,KAAK,UAAU,EAAuB,MAAM,mBAAmB,CAAC;AAEzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAG9C,OAAO,EAAE,KAAK,eAAe,EAA0C,MAAM,WAAW,CAAC;AACzF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,KAAK,eAAe,EAAmB,MAAM,SAAS,CAAC;AAIhE,uFAAuF;AACvF,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,EAAE,CAAC,EAAE,cAAc,CAAC;IACpB,wEAAwE;IACxE,OAAO,CAAC,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACrD;AAED,6EAA6E;AAC7E,MAAM,MAAM,aAAa,GAAG,CAC1B,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,OAAO,CAAC,EAAE,IAAI,CAAC,oBAAoB,EAAE,gBAAgB,CAAC,KACnD,OAAO,CAAC,UAAU,CAAC,CAAC;AAEzB,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,aAAa,CAAC;IACvB,YAAY,EAAE,CACZ,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,eAAe,CAAC,YAAY,CAAC,EACnC,QAAQ,EAAE,MAAM,KACb,cAAc,CAAC;IACpB,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,iBAAiB,CAAC,EAAE,eAAe,CAAC;IACpC,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,SAAS,EACjB,YAAY,EAAE,mBAAmB,EACjC,UAAU,EAAE,eAAe,EAC3B,MAAM,EAAE,yBAAyB,GAChC,IAAI,CAiIN"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-tools.d.ts","sourceRoot":"","sources":["../../../src/tools/mcp/runtime-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAE9E,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAExD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,UAAU,sBAAsB;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC5D,iBAAiB,CAAC,EAAE,eAAe,CAAC;IACpC,8EAA8E;IAC9E,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,YAAY,EAAE,CACZ,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,sBAAsB,CAAC,YAAY,CAAC,CAAC,YAAY,CAAC,EACxD,QAAQ,EAAE,MAAM,KACb,cAAc,CAAC;CACrB;AAaD;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,SAAS,sBAAsB,EAAE,EACxC,MAAM,EAAE,sBAAsB,GAC7B,IAAI,
|
|
1
|
+
{"version":3,"file":"runtime-tools.d.ts","sourceRoot":"","sources":["../../../src/tools/mcp/runtime-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAE9E,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAExD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,UAAU,sBAAsB;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC5D,iBAAiB,CAAC,EAAE,eAAe,CAAC;IACpC,8EAA8E;IAC9E,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,YAAY,EAAE,CACZ,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,sBAAsB,CAAC,YAAY,CAAC,CAAC,YAAY,CAAC,EACxD,QAAQ,EAAE,MAAM,KACb,cAAc,CAAC;CACrB;AAaD;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,SAAS,sBAAsB,EAAE,EACxC,MAAM,EAAE,sBAAsB,GAC7B,IAAI,CAiCN"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { EndpointToolAnnotations } from '../contract/tool-options.js';
|
|
2
|
+
import type { OperationIdentity } from '../server/types.js';
|
|
3
|
+
type Operation = Pick<OperationIdentity, 'method' | 'annotations'>;
|
|
4
|
+
/**
|
|
5
|
+
* Whether calling the operation again returns its answer without a new effect.
|
|
6
|
+
*
|
|
7
|
+
* The declared `readOnlyHint` decides in either direction. Without one, `GET`
|
|
8
|
+
* is the contract's own statement that the endpoint reads — unless it declares
|
|
9
|
+
* `destructiveHint: true`, which says the opposite in the same breath.
|
|
10
|
+
*/
|
|
11
|
+
export declare function isRepeatableRead(operation: Operation): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* The annotations a tool surface publishes for the operation: the declared
|
|
14
|
+
* ones, with `readOnlyHint: true` stated for a read that declared none. A
|
|
15
|
+
* declared `readOnlyHint`, either way, is published as written.
|
|
16
|
+
*/
|
|
17
|
+
export declare function publishedToolAnnotations(operation: Operation): EndpointToolAnnotations | undefined;
|
|
18
|
+
/** The hints another MCP server published for one of its tools, or `undefined` when it gave none. */
|
|
19
|
+
export declare function readDiscoveredToolAnnotations(raw: unknown): EndpointToolAnnotations | undefined;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=read-operation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-operation.d.ts","sourceRoot":"","sources":["../../src/tools/read-operation.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,KAAK,SAAS,GAAG,IAAI,CAAC,iBAAiB,EAAE,QAAQ,GAAG,aAAa,CAAC,CAAC;AAEnE;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAI9D;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,SAAS,GACnB,uBAAuB,GAAG,SAAS,CAIrC;AAeD,qGAAqG;AACrG,wBAAgB,6BAA6B,CAC3C,GAAG,EAAE,OAAO,GACX,uBAAuB,GAAG,SAAS,CAYrC"}
|
package/llms/cli.txt
CHANGED
|
@@ -718,9 +718,18 @@ and `details.resultOnStdout` states which case occurred:
|
|
|
718
718
|
| only reads | nothing | repeating the command with a corrected view |
|
|
719
719
|
|
|
720
720
|
A managed command only reads when its tool declares `annotations.readOnlyHint: true`,
|
|
721
|
-
or declares no `readOnlyHint` and its method is `GET`. `readOnlyHint: false
|
|
722
|
-
endpoint keeps the result on stdout. A native
|
|
723
|
-
so a failed `present` always keeps the validated
|
|
721
|
+
or declares no `readOnlyHint` and its method is `GET`. `readOnlyHint: false`, or
|
|
722
|
+
`destructiveHint: true` without a `readOnlyHint`, on a `GET` endpoint keeps the result on stdout. A native
|
|
723
|
+
`defineCliCommand` declares neither fact, so a failed `present` always keeps the validated
|
|
724
|
+
result on stdout.
|
|
725
|
+
|
|
726
|
+
A CLI that mounts a server's tools through `defineMcpClientConnection` never sees the
|
|
727
|
+
method — every MCP call is a `POST` — so it reads the `readOnlyHint` the server
|
|
728
|
+
publishes. A Stitchkit server publishes it for every `GET` by the rule above
|
|
729
|
+
([MCP → `readOnlyHint`](./mcp-and-agents.md#saying-which-tools-only-read--readonlyhint)),
|
|
730
|
+
so the same command behaves the same over the contract and over MCP. The hint is the
|
|
731
|
+
server's word: a server that labels a write as read-only makes the CLI withhold that
|
|
732
|
+
write's result on a refused view, and repeating the command repeats the effect.
|
|
724
733
|
|
|
725
734
|
CLI argument errors detected before execution still exit with `2`
|
|
726
735
|
and have no successful result to preserve.
|
package/llms/tools.2.txt
CHANGED
|
@@ -13,6 +13,115 @@ Parts of this slice:
|
|
|
13
13
|
# Guide: MCP & agents (docs/guide/mcp-and-agents.md) (continued)
|
|
14
14
|
==============================================================================
|
|
15
15
|
|
|
16
|
+
## OAuth 2.1 — a native remote connector
|
|
17
|
+
|
|
18
|
+
A remote MCP server is connectable from Claude (Desktop / web "custom
|
|
19
|
+
connector") only through the MCP authorization spec: OAuth 2.1 with PKCE, plus
|
|
20
|
+
the discovery documents (RFC 9728 / 8414), Client ID Metadata Documents (CIMD)
|
|
21
|
+
and resource indicators (RFC 8707). A Bearer-only server returns a
|
|
22
|
+
bare `401` and the connector never establishes.
|
|
23
|
+
|
|
24
|
+
stitchkit ships the OAuth **protocol mechanics**; the app supplies only
|
|
25
|
+
**identity and storage**. Three pieces wire it together:
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { mountOAuthProvider, oauthProtectedResourceRoute } from 'stitchkit/tools'
|
|
29
|
+
import { createMcpHandler, createMcpHttpRoute } from 'stitchkit/tools/mcp'
|
|
30
|
+
import { createServer } from 'stitchkit/server'
|
|
31
|
+
|
|
32
|
+
const resource = 'https://api.example.com/mcp'
|
|
33
|
+
const issuer = 'https://api.example.com'
|
|
34
|
+
|
|
35
|
+
// 1. Resource server — the 401 now points at the metadata.
|
|
36
|
+
const mcp = createMcpHandler({
|
|
37
|
+
serverInfo: { name: 'my-app', version: '1.0.0' },
|
|
38
|
+
auth: resolveOAuthToken, // validate the Bearer JWT (verifyJwt + audience)
|
|
39
|
+
services,
|
|
40
|
+
protectedResource: { resource, authorizationServers: [issuer] },
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
// 2. Authorization server — CIMD, /authorize (PKCE), /token.
|
|
44
|
+
const oauthRoutes = mountOAuthProvider({
|
|
45
|
+
issuer,
|
|
46
|
+
resource,
|
|
47
|
+
signingSecret: env.OAUTH_SECRET,
|
|
48
|
+
clientRegistration: {
|
|
49
|
+
preRegistered: { get: getFirstPartyClient },
|
|
50
|
+
// CIMD is enabled with secure defaults; DCR remains disabled.
|
|
51
|
+
},
|
|
52
|
+
codes, refreshTokens, // your stores (DB or in-memory)
|
|
53
|
+
authorizeUser, // → { userId, approvedScopes } | Response
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
createServer({
|
|
57
|
+
services,
|
|
58
|
+
rawRoutes: [
|
|
59
|
+
createMcpHttpRoute({ path: '/mcp', handler: mcp }),
|
|
60
|
+
oauthProtectedResourceRoute({ resource, authorizationServers: [issuer] }),
|
|
61
|
+
...oauthRoutes,
|
|
62
|
+
],
|
|
63
|
+
})
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Access tokens are signed HS256 JWTs (`signJwt`) whose `aud` is the resource and
|
|
67
|
+
whose `iss` is the issuer — validate both in `auth` with
|
|
68
|
+
`verifyJwt(token, secret, { audience: resource, issuer })`. `authorizeUser` is
|
|
69
|
+
where the app authenticates the user (reuse an existing session) and records
|
|
70
|
+
consent; return `{ userId, approvedScopes }`, where approved scopes are a subset
|
|
71
|
+
of the requested/supported values, or a `Response` to redirect the browser to a
|
|
72
|
+
login page first. The AS and resource server can co-locate or live
|
|
73
|
+
on separate origins. See
|
|
74
|
+
[ADR 0015](../decisions/0015-oauth-resource-server.md).
|
|
75
|
+
|
|
76
|
+
Client resolution is deterministic: exact pre-registered client, then an HTTPS
|
|
77
|
+
URL client id resolved as CIMD, then explicitly enabled DCR. CIMD is on by
|
|
78
|
+
default; DCR is off unless `clientRegistration.dcr` supplies both `register` and
|
|
79
|
+
`get`. A CIMD-only deployment therefore exposes neither `/register` nor a
|
|
80
|
+
`registration_endpoint`. The metadata document must declare a `client_id` that
|
|
81
|
+
exactly equals its URL, explicit redirect URIs and
|
|
82
|
+
`token_endpoint_auth_method: 'none'`.
|
|
83
|
+
|
|
84
|
+
The token endpoint and `/register` read at most 64 KiB of a request body, or
|
|
85
|
+
`maxRequestBodyBytes` when you set it; a larger one is answered `413` with
|
|
86
|
+
`invalid_request` and the rest is not read.
|
|
87
|
+
|
|
88
|
+
The production fetcher rejects credentials, fragments, non-HTTPS URLs and
|
|
89
|
+
private/reserved targets. Every redirect is DNS-resolved and IP-pinned again;
|
|
90
|
+
timeouts, redirect count and body size are bounded. Its bounded cache respects
|
|
91
|
+
`Cache-Control` (including `no-store`/`no-cache`), `Age`, `Expires`, `ETag` and
|
|
92
|
+
`Last-Modified`, coalesces concurrent misses and reports sanitized cache events;
|
|
93
|
+
invalid or unavailable identity receives only a short fail-closed cache entry.
|
|
94
|
+
|
|
95
|
+
Resolution itself is rate-limited on two levels (`CimdCachePolicy`):
|
|
96
|
+
`maxResolutionsPerClient` (default 10) bounds ONE `client_id` per
|
|
97
|
+
`resolutionWindowMs` window (default 60 s) — a client whose document disables
|
|
98
|
+
caching burns its own budget, not the server's — while `maxResolutions`
|
|
99
|
+
(default 120) caps the server-wide outbound rate as the flood backstop.
|
|
100
|
+
Positive and negative cache entries keep separate pools of `maxEntries` each,
|
|
101
|
+
so a flood of unresolvable ids cannot evict warmed clients and a burst of
|
|
102
|
+
successes cannot reset a failing client's backoff.
|
|
103
|
+
|
|
104
|
+
### Authorization hardening (MCP 2026-07-28)
|
|
105
|
+
|
|
106
|
+
- **`iss` on every authorization response (RFC 9207, SEP-2468).** Success *and*
|
|
107
|
+
error redirects carry `iss`, and the AS metadata advertises
|
|
108
|
+
`authorization_response_iss_parameter_supported: true`. A client that talks to
|
|
109
|
+
several authorization servers validates `iss` before redeeming the code, which
|
|
110
|
+
closes the **mix-up attack** — an attacker's server cannot pass its response
|
|
111
|
+
off as this issuer's. Additive on the wire: a client that ignores `iss` is
|
|
112
|
+
unaffected.
|
|
113
|
+
- **`application_type` on registration (SEP-837).** A client may declare
|
|
114
|
+
`"native"` (desktop / CLI) or `"web"` in its DCR body. A **native** client may
|
|
115
|
+
register an `http` loopback redirect (`http://127.0.0.1:…`, RFC 8252 §7.3); a
|
|
116
|
+
**web** client is held to `https` only — that mismatch is the usual cause of
|
|
117
|
+
the `redirect_uri` rejection CLI clients hit. Omit the field and registration
|
|
118
|
+
behaves exactly as before (loopback allowed); an unknown value is rejected
|
|
119
|
+
rather than silently defaulted.
|
|
120
|
+
|
|
121
|
+
> Dynamic Client Registration is an explicit interoperability mode, not the
|
|
122
|
+
> default. Enabling it changes public discovery and adds `/register`; keep it off
|
|
123
|
+
> unless a client that cannot publish CIMD requires it.
|
|
124
|
+
|
|
16
125
|
## Proxying a remote API — `implementRemote`
|
|
17
126
|
|
|
18
127
|
`implement` binds a contract to local handlers. `implementRemote` binds it to a
|
package/llms/tools.txt
CHANGED
|
@@ -317,6 +317,36 @@ schema and annotations, and nothing else — it moves when the contract moves an
|
|
|
317
317
|
not when a handler does. Raw SDK registrations stay consumer-owned and are
|
|
318
318
|
outside it.
|
|
319
319
|
|
|
320
|
+
### Saying which tools only read — `readOnlyHint`
|
|
321
|
+
|
|
322
|
+
A host reads a tool's `annotations` to group tools and to choose permission
|
|
323
|
+
defaults, and a CLI mounted over this surface reads them to decide what a
|
|
324
|
+
refused view may withhold ([CLI → views](./cli.md#aggregate-views)). The server publishes the
|
|
325
|
+
annotations the tool declared, and states `readOnlyHint: true` for a read that
|
|
326
|
+
declared none:
|
|
327
|
+
|
|
328
|
+
| The tool | Published `readOnlyHint` |
|
|
329
|
+
|---|---|
|
|
330
|
+
| declares `readOnlyHint`, either way | as declared |
|
|
331
|
+
| `GET` with no `readOnlyHint` | `true` |
|
|
332
|
+
| `GET` declaring `destructiveHint: true` and no `readOnlyHint` | none |
|
|
333
|
+
| any other method with no `readOnlyHint` | none |
|
|
334
|
+
|
|
335
|
+
A runtime tool follows the same rule by its `identity.method`. A raw SDK
|
|
336
|
+
registration (`mountWait`, `mountViewFile` and the like) publishes only what it
|
|
337
|
+
passes. The catalog digest hashes the published annotations, so it moves once
|
|
338
|
+
when a server starts publishing the derived hint.
|
|
339
|
+
|
|
340
|
+
> **A `GET` with an effect must say so.** Hosts may run a tool that publishes
|
|
341
|
+
> `readOnlyHint: true` without asking. An endpoint that changes something on
|
|
342
|
+
> `GET` — marks as seen, rotates a token — declares
|
|
343
|
+
> `tool: { annotations: { readOnlyHint: false } }`.
|
|
344
|
+
|
|
345
|
+
A client connection (`defineMcpClientConnection`) carries the hints a server
|
|
346
|
+
published onto the tools it mounts, field by field: a hint of the wrong type is
|
|
347
|
+
dropped and the tool stays. The mounted tool is as read-only as its server says
|
|
348
|
+
it is, and a surface that re-publishes mounted tools passes those hints on.
|
|
349
|
+
|
|
320
350
|
### Shaping arguments before validation — `beforeToolCall`
|
|
321
351
|
|
|
322
352
|
`beforeToolCall` sees the arguments as they arrived and may **replace** them:
|
|
@@ -865,112 +895,3 @@ OS default disposition instead of reporting a fictional forced result.
|
|
|
865
895
|
Both transports build the server through the shared `buildMcpServer` — same
|
|
866
896
|
contract/runtime pipeline, same surface selection, context, hooks, raw escape
|
|
867
897
|
hatch and instructions.
|
|
868
|
-
|
|
869
|
-
## OAuth 2.1 — a native remote connector
|
|
870
|
-
|
|
871
|
-
A remote MCP server is connectable from Claude (Desktop / web "custom
|
|
872
|
-
connector") only through the MCP authorization spec: OAuth 2.1 with PKCE, plus
|
|
873
|
-
the discovery documents (RFC 9728 / 8414), Client ID Metadata Documents (CIMD)
|
|
874
|
-
and resource indicators (RFC 8707). A Bearer-only server returns a
|
|
875
|
-
bare `401` and the connector never establishes.
|
|
876
|
-
|
|
877
|
-
stitchkit ships the OAuth **protocol mechanics**; the app supplies only
|
|
878
|
-
**identity and storage**. Three pieces wire it together:
|
|
879
|
-
|
|
880
|
-
```ts
|
|
881
|
-
import { mountOAuthProvider, oauthProtectedResourceRoute } from 'stitchkit/tools'
|
|
882
|
-
import { createMcpHandler, createMcpHttpRoute } from 'stitchkit/tools/mcp'
|
|
883
|
-
import { createServer } from 'stitchkit/server'
|
|
884
|
-
|
|
885
|
-
const resource = 'https://api.example.com/mcp'
|
|
886
|
-
const issuer = 'https://api.example.com'
|
|
887
|
-
|
|
888
|
-
// 1. Resource server — the 401 now points at the metadata.
|
|
889
|
-
const mcp = createMcpHandler({
|
|
890
|
-
serverInfo: { name: 'my-app', version: '1.0.0' },
|
|
891
|
-
auth: resolveOAuthToken, // validate the Bearer JWT (verifyJwt + audience)
|
|
892
|
-
services,
|
|
893
|
-
protectedResource: { resource, authorizationServers: [issuer] },
|
|
894
|
-
})
|
|
895
|
-
|
|
896
|
-
// 2. Authorization server — CIMD, /authorize (PKCE), /token.
|
|
897
|
-
const oauthRoutes = mountOAuthProvider({
|
|
898
|
-
issuer,
|
|
899
|
-
resource,
|
|
900
|
-
signingSecret: env.OAUTH_SECRET,
|
|
901
|
-
clientRegistration: {
|
|
902
|
-
preRegistered: { get: getFirstPartyClient },
|
|
903
|
-
// CIMD is enabled with secure defaults; DCR remains disabled.
|
|
904
|
-
},
|
|
905
|
-
codes, refreshTokens, // your stores (DB or in-memory)
|
|
906
|
-
authorizeUser, // → { userId, approvedScopes } | Response
|
|
907
|
-
})
|
|
908
|
-
|
|
909
|
-
createServer({
|
|
910
|
-
services,
|
|
911
|
-
rawRoutes: [
|
|
912
|
-
createMcpHttpRoute({ path: '/mcp', handler: mcp }),
|
|
913
|
-
oauthProtectedResourceRoute({ resource, authorizationServers: [issuer] }),
|
|
914
|
-
...oauthRoutes,
|
|
915
|
-
],
|
|
916
|
-
})
|
|
917
|
-
```
|
|
918
|
-
|
|
919
|
-
Access tokens are signed HS256 JWTs (`signJwt`) whose `aud` is the resource and
|
|
920
|
-
whose `iss` is the issuer — validate both in `auth` with
|
|
921
|
-
`verifyJwt(token, secret, { audience: resource, issuer })`. `authorizeUser` is
|
|
922
|
-
where the app authenticates the user (reuse an existing session) and records
|
|
923
|
-
consent; return `{ userId, approvedScopes }`, where approved scopes are a subset
|
|
924
|
-
of the requested/supported values, or a `Response` to redirect the browser to a
|
|
925
|
-
login page first. The AS and resource server can co-locate or live
|
|
926
|
-
on separate origins. See
|
|
927
|
-
[ADR 0015](../decisions/0015-oauth-resource-server.md).
|
|
928
|
-
|
|
929
|
-
Client resolution is deterministic: exact pre-registered client, then an HTTPS
|
|
930
|
-
URL client id resolved as CIMD, then explicitly enabled DCR. CIMD is on by
|
|
931
|
-
default; DCR is off unless `clientRegistration.dcr` supplies both `register` and
|
|
932
|
-
`get`. A CIMD-only deployment therefore exposes neither `/register` nor a
|
|
933
|
-
`registration_endpoint`. The metadata document must declare a `client_id` that
|
|
934
|
-
exactly equals its URL, explicit redirect URIs and
|
|
935
|
-
`token_endpoint_auth_method: 'none'`.
|
|
936
|
-
|
|
937
|
-
The token endpoint and `/register` read at most 64 KiB of a request body, or
|
|
938
|
-
`maxRequestBodyBytes` when you set it; a larger one is answered `413` with
|
|
939
|
-
`invalid_request` and the rest is not read.
|
|
940
|
-
|
|
941
|
-
The production fetcher rejects credentials, fragments, non-HTTPS URLs and
|
|
942
|
-
private/reserved targets. Every redirect is DNS-resolved and IP-pinned again;
|
|
943
|
-
timeouts, redirect count and body size are bounded. Its bounded cache respects
|
|
944
|
-
`Cache-Control` (including `no-store`/`no-cache`), `Age`, `Expires`, `ETag` and
|
|
945
|
-
`Last-Modified`, coalesces concurrent misses and reports sanitized cache events;
|
|
946
|
-
invalid or unavailable identity receives only a short fail-closed cache entry.
|
|
947
|
-
|
|
948
|
-
Resolution itself is rate-limited on two levels (`CimdCachePolicy`):
|
|
949
|
-
`maxResolutionsPerClient` (default 10) bounds ONE `client_id` per
|
|
950
|
-
`resolutionWindowMs` window (default 60 s) — a client whose document disables
|
|
951
|
-
caching burns its own budget, not the server's — while `maxResolutions`
|
|
952
|
-
(default 120) caps the server-wide outbound rate as the flood backstop.
|
|
953
|
-
Positive and negative cache entries keep separate pools of `maxEntries` each,
|
|
954
|
-
so a flood of unresolvable ids cannot evict warmed clients and a burst of
|
|
955
|
-
successes cannot reset a failing client's backoff.
|
|
956
|
-
|
|
957
|
-
### Authorization hardening (MCP 2026-07-28)
|
|
958
|
-
|
|
959
|
-
- **`iss` on every authorization response (RFC 9207, SEP-2468).** Success *and*
|
|
960
|
-
error redirects carry `iss`, and the AS metadata advertises
|
|
961
|
-
`authorization_response_iss_parameter_supported: true`. A client that talks to
|
|
962
|
-
several authorization servers validates `iss` before redeeming the code, which
|
|
963
|
-
closes the **mix-up attack** — an attacker's server cannot pass its response
|
|
964
|
-
off as this issuer's. Additive on the wire: a client that ignores `iss` is
|
|
965
|
-
unaffected.
|
|
966
|
-
- **`application_type` on registration (SEP-837).** A client may declare
|
|
967
|
-
`"native"` (desktop / CLI) or `"web"` in its DCR body. A **native** client may
|
|
968
|
-
register an `http` loopback redirect (`http://127.0.0.1:…`, RFC 8252 §7.3); a
|
|
969
|
-
**web** client is held to `https` only — that mismatch is the usual cause of
|
|
970
|
-
the `redirect_uri` rejection CLI clients hit. Omit the field and registration
|
|
971
|
-
behaves exactly as before (loopback allowed); an unknown value is rejected
|
|
972
|
-
rather than silently defaulted.
|
|
973
|
-
|
|
974
|
-
> Dynamic Client Registration is an explicit interoperability mode, not the
|
|
975
|
-
> default. Enabling it changes public discovery and adds `/register`; keep it off
|
|
976
|
-
> unless a client that cannot publish CIMD requires it.
|
package/llms-full.txt
CHANGED
|
@@ -4182,6 +4182,36 @@ schema and annotations, and nothing else — it moves when the contract moves an
|
|
|
4182
4182
|
not when a handler does. Raw SDK registrations stay consumer-owned and are
|
|
4183
4183
|
outside it.
|
|
4184
4184
|
|
|
4185
|
+
### Saying which tools only read — `readOnlyHint`
|
|
4186
|
+
|
|
4187
|
+
A host reads a tool's `annotations` to group tools and to choose permission
|
|
4188
|
+
defaults, and a CLI mounted over this surface reads them to decide what a
|
|
4189
|
+
refused view may withhold ([CLI → views](./cli.md#aggregate-views)). The server publishes the
|
|
4190
|
+
annotations the tool declared, and states `readOnlyHint: true` for a read that
|
|
4191
|
+
declared none:
|
|
4192
|
+
|
|
4193
|
+
| The tool | Published `readOnlyHint` |
|
|
4194
|
+
|---|---|
|
|
4195
|
+
| declares `readOnlyHint`, either way | as declared |
|
|
4196
|
+
| `GET` with no `readOnlyHint` | `true` |
|
|
4197
|
+
| `GET` declaring `destructiveHint: true` and no `readOnlyHint` | none |
|
|
4198
|
+
| any other method with no `readOnlyHint` | none |
|
|
4199
|
+
|
|
4200
|
+
A runtime tool follows the same rule by its `identity.method`. A raw SDK
|
|
4201
|
+
registration (`mountWait`, `mountViewFile` and the like) publishes only what it
|
|
4202
|
+
passes. The catalog digest hashes the published annotations, so it moves once
|
|
4203
|
+
when a server starts publishing the derived hint.
|
|
4204
|
+
|
|
4205
|
+
> **A `GET` with an effect must say so.** Hosts may run a tool that publishes
|
|
4206
|
+
> `readOnlyHint: true` without asking. An endpoint that changes something on
|
|
4207
|
+
> `GET` — marks as seen, rotates a token — declares
|
|
4208
|
+
> `tool: { annotations: { readOnlyHint: false } }`.
|
|
4209
|
+
|
|
4210
|
+
A client connection (`defineMcpClientConnection`) carries the hints a server
|
|
4211
|
+
published onto the tools it mounts, field by field: a hint of the wrong type is
|
|
4212
|
+
dropped and the tool stays. The mounted tool is as read-only as its server says
|
|
4213
|
+
it is, and a surface that re-publishes mounted tools passes those hints on.
|
|
4214
|
+
|
|
4185
4215
|
### Shaping arguments before validation — `beforeToolCall`
|
|
4186
4216
|
|
|
4187
4217
|
`beforeToolCall` sees the arguments as they arrived and may **replace** them:
|
|
@@ -10847,9 +10877,18 @@ and `details.resultOnStdout` states which case occurred:
|
|
|
10847
10877
|
| only reads | nothing | repeating the command with a corrected view |
|
|
10848
10878
|
|
|
10849
10879
|
A managed command only reads when its tool declares `annotations.readOnlyHint: true`,
|
|
10850
|
-
or declares no `readOnlyHint` and its method is `GET`. `readOnlyHint: false
|
|
10851
|
-
endpoint keeps the result on stdout. A native
|
|
10852
|
-
so a failed `present` always keeps the validated
|
|
10880
|
+
or declares no `readOnlyHint` and its method is `GET`. `readOnlyHint: false`, or
|
|
10881
|
+
`destructiveHint: true` without a `readOnlyHint`, on a `GET` endpoint keeps the result on stdout. A native
|
|
10882
|
+
`defineCliCommand` declares neither fact, so a failed `present` always keeps the validated
|
|
10883
|
+
result on stdout.
|
|
10884
|
+
|
|
10885
|
+
A CLI that mounts a server's tools through `defineMcpClientConnection` never sees the
|
|
10886
|
+
method — every MCP call is a `POST` — so it reads the `readOnlyHint` the server
|
|
10887
|
+
publishes. A Stitchkit server publishes it for every `GET` by the rule above
|
|
10888
|
+
([MCP → `readOnlyHint`](./mcp-and-agents.md#saying-which-tools-only-read--readonlyhint)),
|
|
10889
|
+
so the same command behaves the same over the contract and over MCP. The hint is the
|
|
10890
|
+
server's word: a server that labels a write as read-only makes the CLI withhold that
|
|
10891
|
+
write's result on a refused view, and repeating the command repeats the effect.
|
|
10853
10892
|
|
|
10854
10893
|
CLI argument errors detected before execution still exit with `2`
|
|
10855
10894
|
and have no successful result to preserve.
|
package/llms.txt
CHANGED
|
@@ -14,14 +14,14 @@ Load the slice for each entrypoint your code imports — `import … from 'stitc
|
|
|
14
14
|
- **stitchkit/react**: [llms/react.txt](llms/react.txt) 8.7 KB
|
|
15
15
|
- **stitchkit/react/keyboard**: [llms/react-keyboard.txt](llms/react-keyboard.txt) 22.7 KB
|
|
16
16
|
- **stitchkit/react/keyboard/testing**: [llms/react-keyboard-testing.txt](llms/react-keyboard-testing.txt) 1.9 KB
|
|
17
|
-
- **stitchkit/tools**: [llms/tools.txt](llms/tools.txt)
|
|
17
|
+
- **stitchkit/tools**: [llms/tools.txt](llms/tools.txt) 43.2 KB → [llms/tools.2.txt](llms/tools.2.txt) 35.7 KB → [llms/tools.3.txt](llms/tools.3.txt) 21.0 KB
|
|
18
18
|
- **stitchkit/tools/mcp**: [llms/tools-mcp.txt](llms/tools-mcp.txt) 6.6 KB
|
|
19
19
|
- **stitchkit/tools/agent**: [llms/tools-agent.txt](llms/tools-agent.txt) 1.1 KB
|
|
20
20
|
- **stitchkit/tools/toolkit**: [llms/tools-toolkit.txt](llms/tools-toolkit.txt) 0.9 KB
|
|
21
21
|
- **stitchkit/tools/contract**: [llms/tools-contract.txt](llms/tools-contract.txt) 0.2 KB
|
|
22
22
|
- **stitchkit/tools/invoker**: [llms/tools-invoker.txt](llms/tools-invoker.txt) 0.2 KB
|
|
23
23
|
- **stitchkit/tools/connections**: [llms/tools-connections.txt](llms/tools-connections.txt) 3.9 KB
|
|
24
|
-
- **stitchkit/cli**: [llms/cli.txt](llms/cli.txt) 37.
|
|
24
|
+
- **stitchkit/cli**: [llms/cli.txt](llms/cli.txt) 37.8 KB → [llms/cli.2.txt](llms/cli.2.txt) 36.4 KB
|
|
25
25
|
- **stitchkit/cli/publish**: [llms/cli-publish.txt](llms/cli-publish.txt) 1.5 KB
|
|
26
26
|
- **stitchkit/remote**: [llms/remote.txt](llms/remote.txt) 1.0 KB
|
|
27
27
|
- **stitchkit/contract**: [llms/contract.txt](llms/contract.txt) 33.8 KB
|
package/package.json
CHANGED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { OperationIdentity } from '../../server/types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Whether calling the operation again returns its answer without a new effect.
|
|
4
|
-
*
|
|
5
|
-
* The declared `readOnlyHint` decides in either direction; without one, `GET` is
|
|
6
|
-
* the contract's own statement that the endpoint reads.
|
|
7
|
-
*/
|
|
8
|
-
export declare function isRepeatableRead(operation: Pick<OperationIdentity, 'method' | 'annotations'>): boolean;
|
|
9
|
-
//# sourceMappingURL=repeatable-read.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"repeatable-read.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/repeatable-read.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,IAAI,CAAC,iBAAiB,EAAE,QAAQ,GAAG,aAAa,CAAC,GAC3D,OAAO,CAET"}
|