@toolgrain/mcp 0.0.0-stage → 0.1.0-rc.0
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/LICENSE +21 -0
- package/README.md +86 -2
- package/dist/auth/abort.d.ts +3 -0
- package/dist/auth/abort.d.ts.map +1 -0
- package/dist/auth/abort.js +14 -0
- package/dist/auth/abort.js.map +1 -0
- package/dist/auth/audience.d.ts +3 -0
- package/dist/auth/audience.d.ts.map +1 -0
- package/dist/auth/audience.js +17 -0
- package/dist/auth/audience.js.map +1 -0
- package/dist/auth/authorization.d.ts +7 -0
- package/dist/auth/authorization.d.ts.map +1 -0
- package/dist/auth/authorization.js +58 -0
- package/dist/auth/authorization.js.map +1 -0
- package/dist/auth/context.d.ts +8 -0
- package/dist/auth/context.d.ts.map +1 -0
- package/dist/auth/context.js +37 -0
- package/dist/auth/context.js.map +1 -0
- package/dist/auth/errors.d.ts +14 -0
- package/dist/auth/errors.d.ts.map +1 -0
- package/dist/auth/errors.js +23 -0
- package/dist/auth/errors.js.map +1 -0
- package/dist/auth/types.d.ts +28 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +2 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/auth/url.d.ts +3 -0
- package/dist/auth/url.d.ts.map +1 -0
- package/dist/auth/url.js +18 -0
- package/dist/auth/url.js.map +1 -0
- package/dist/connection.d.ts +12 -0
- package/dist/connection.d.ts.map +1 -0
- package/dist/connection.js +151 -0
- package/dist/connection.js.map +1 -0
- package/dist/context/access.d.ts +7 -0
- package/dist/context/access.d.ts.map +1 -0
- package/dist/context/access.js +22 -0
- package/dist/context/access.js.map +1 -0
- package/dist/context/definition.d.ts +24 -0
- package/dist/context/definition.d.ts.map +1 -0
- package/dist/context/definition.js +57 -0
- package/dist/context/definition.js.map +1 -0
- package/dist/context/registry.d.ts +5 -0
- package/dist/context/registry.d.ts.map +1 -0
- package/dist/context/registry.js +15 -0
- package/dist/context/registry.js.map +1 -0
- package/dist/context/scope.d.ts +5 -0
- package/dist/context/scope.d.ts.map +1 -0
- package/dist/context/scope.js +56 -0
- package/dist/context/scope.js.map +1 -0
- package/dist/diagnostics.d.ts +10 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +13 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/dispatcher.d.ts +19 -0
- package/dist/dispatcher.d.ts.map +1 -0
- package/dist/dispatcher.js +23 -0
- package/dist/dispatcher.js.map +1 -0
- package/dist/execute-request.d.ts +7 -0
- package/dist/execute-request.d.ts.map +1 -0
- package/dist/execute-request.js +20 -0
- package/dist/execute-request.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/legacy/connection.d.ts +26 -0
- package/dist/legacy/connection.d.ts.map +1 -0
- package/dist/legacy/connection.js +73 -0
- package/dist/legacy/connection.js.map +1 -0
- package/dist/legacy/dispatcher.d.ts +10 -0
- package/dist/legacy/dispatcher.d.ts.map +1 -0
- package/dist/legacy/dispatcher.js +41 -0
- package/dist/legacy/dispatcher.js.map +1 -0
- package/dist/legacy/handlers.d.ts +12 -0
- package/dist/legacy/handlers.d.ts.map +1 -0
- package/dist/legacy/handlers.js +44 -0
- package/dist/legacy/handlers.js.map +1 -0
- package/dist/legacy/messages.d.ts +31 -0
- package/dist/legacy/messages.d.ts.map +1 -0
- package/dist/legacy/messages.js +27 -0
- package/dist/legacy/messages.js.map +1 -0
- package/dist/legacy/responses.d.ts +5 -0
- package/dist/legacy/responses.d.ts.map +1 -0
- package/dist/legacy/responses.js +8 -0
- package/dist/legacy/responses.js.map +1 -0
- package/dist/legacy/results.d.ts +5 -0
- package/dist/legacy/results.d.ts.map +1 -0
- package/dist/legacy/results.js +19 -0
- package/dist/legacy/results.js.map +1 -0
- package/dist/modern/dispatcher.d.ts +7 -0
- package/dist/modern/dispatcher.d.ts.map +1 -0
- package/dist/modern/dispatcher.js +37 -0
- package/dist/modern/dispatcher.js.map +1 -0
- package/dist/modern/handlers.d.ts +9 -0
- package/dist/modern/handlers.d.ts.map +1 -0
- package/dist/modern/handlers.js +36 -0
- package/dist/modern/handlers.js.map +1 -0
- package/dist/modern/messages.d.ts +142 -0
- package/dist/modern/messages.d.ts.map +1 -0
- package/dist/modern/messages.js +14 -0
- package/dist/modern/messages.js.map +1 -0
- package/dist/modern/metadata.d.ts +45 -0
- package/dist/modern/metadata.d.ts.map +1 -0
- package/dist/modern/metadata.js +49 -0
- package/dist/modern/metadata.js.map +1 -0
- package/dist/modern/responses.d.ts +3 -0
- package/dist/modern/responses.d.ts.map +1 -0
- package/dist/modern/responses.js +15 -0
- package/dist/modern/responses.js.map +1 -0
- package/dist/modern/results.d.ts +4 -0
- package/dist/modern/results.d.ts.map +1 -0
- package/dist/modern/results.js +4 -0
- package/dist/modern/results.js.map +1 -0
- package/dist/pending-requests.d.ts +12 -0
- package/dist/pending-requests.d.ts.map +1 -0
- package/dist/pending-requests.js +30 -0
- package/dist/pending-requests.js.map +1 -0
- package/dist/protocol/decode.d.ts +21 -0
- package/dist/protocol/decode.d.ts.map +1 -0
- package/dist/protocol/decode.js +40 -0
- package/dist/protocol/decode.js.map +1 -0
- package/dist/protocol/icon.d.ts +11 -0
- package/dist/protocol/icon.d.ts.map +1 -0
- package/dist/protocol/icon.js +8 -0
- package/dist/protocol/icon.js.map +1 -0
- package/dist/protocol/messages.d.ts +19 -0
- package/dist/protocol/messages.d.ts.map +1 -0
- package/dist/protocol/messages.js +21 -0
- package/dist/protocol/messages.js.map +1 -0
- package/dist/protocol/responses.d.ts +9 -0
- package/dist/protocol/responses.d.ts.map +1 -0
- package/dist/protocol/responses.js +20 -0
- package/dist/protocol/responses.js.map +1 -0
- package/dist/protocol/version-metadata.d.ts +7 -0
- package/dist/protocol/version-metadata.d.ts.map +1 -0
- package/dist/protocol/version-metadata.js +21 -0
- package/dist/protocol/version-metadata.js.map +1 -0
- package/dist/server.d.ts +35 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +101 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/adapter.d.ts +12 -0
- package/dist/tools/adapter.d.ts.map +1 -0
- package/dist/tools/adapter.js +45 -0
- package/dist/tools/adapter.js.map +1 -0
- package/dist/tools/call.d.ts +8 -0
- package/dist/tools/call.d.ts.map +1 -0
- package/dist/tools/call.js +9 -0
- package/dist/tools/call.js.map +1 -0
- package/dist/tools/content.d.ts +181 -0
- package/dist/tools/content.d.ts.map +1 -0
- package/dist/tools/content.js +54 -0
- package/dist/tools/content.js.map +1 -0
- package/dist/tools/context.d.ts +36 -0
- package/dist/tools/context.d.ts.map +1 -0
- package/dist/tools/context.js +50 -0
- package/dist/tools/context.js.map +1 -0
- package/dist/tools/definition.d.ts +55 -0
- package/dist/tools/definition.d.ts.map +1 -0
- package/dist/tools/definition.js +44 -0
- package/dist/tools/definition.js.map +1 -0
- package/dist/tools/errors.d.ts +11 -0
- package/dist/tools/errors.d.ts.map +1 -0
- package/dist/tools/errors.js +19 -0
- package/dist/tools/errors.js.map +1 -0
- package/dist/tools/json-value.d.ts +15 -0
- package/dist/tools/json-value.d.ts.map +1 -0
- package/dist/tools/json-value.js +42 -0
- package/dist/tools/json-value.js.map +1 -0
- package/dist/tools/name.d.ts +6 -0
- package/dist/tools/name.d.ts.map +1 -0
- package/dist/tools/name.js +13 -0
- package/dist/tools/name.js.map +1 -0
- package/dist/tools/registry.d.ts +14 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +31 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/results.d.ts +14 -0
- package/dist/tools/results.d.ts.map +1 -0
- package/dist/tools/results.js +71 -0
- package/dist/tools/results.js.map +1 -0
- package/dist/tools/rich-result.d.ts +44 -0
- package/dist/tools/rich-result.d.ts.map +1 -0
- package/dist/tools/rich-result.js +24 -0
- package/dist/tools/rich-result.js.map +1 -0
- package/dist/tools/schema.d.ts +18 -0
- package/dist/tools/schema.d.ts.map +1 -0
- package/dist/tools/schema.js +70 -0
- package/dist/tools/schema.js.map +1 -0
- package/dist/transport-ownership.d.ts +4 -0
- package/dist/transport-ownership.d.ts.map +1 -0
- package/dist/transport-ownership.js +13 -0
- package/dist/transport-ownership.js.map +1 -0
- package/dist/transport.d.ts +24 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +4 -0
- package/dist/transport.js.map +1 -0
- package/package.json +65 -4
- package/src/auth/abort.ts +14 -0
- package/src/auth/audience.ts +22 -0
- package/src/auth/authorization.ts +81 -0
- package/src/auth/context.ts +43 -0
- package/src/auth/errors.ts +35 -0
- package/src/auth/types.ts +34 -0
- package/src/auth/url.ts +22 -0
- package/src/connection.ts +207 -0
- package/src/context/access.ts +36 -0
- package/src/context/definition.ts +100 -0
- package/src/context/registry.ts +25 -0
- package/src/context/scope.ts +82 -0
- package/src/diagnostics.ts +25 -0
- package/src/dispatcher.ts +70 -0
- package/src/execute-request.ts +35 -0
- package/src/index.ts +41 -0
- package/src/legacy/connection.ts +101 -0
- package/src/legacy/dispatcher.ts +72 -0
- package/src/legacy/handlers.ts +76 -0
- package/src/legacy/messages.ts +27 -0
- package/src/legacy/responses.ts +20 -0
- package/src/legacy/results.ts +20 -0
- package/src/modern/dispatcher.ts +77 -0
- package/src/modern/handlers.ts +55 -0
- package/src/modern/messages.ts +14 -0
- package/src/modern/metadata.ts +49 -0
- package/src/modern/responses.ts +25 -0
- package/src/modern/results.ts +6 -0
- package/src/pending-requests.ts +30 -0
- package/src/protocol/decode.ts +69 -0
- package/src/protocol/icon.ts +8 -0
- package/src/protocol/messages.ts +29 -0
- package/src/protocol/responses.ts +27 -0
- package/src/protocol/version-metadata.ts +42 -0
- package/src/server.ts +164 -0
- package/src/tools/adapter.ts +76 -0
- package/src/tools/call.ts +18 -0
- package/src/tools/content.ts +57 -0
- package/src/tools/context.ts +119 -0
- package/src/tools/definition.ts +144 -0
- package/src/tools/errors.ts +21 -0
- package/src/tools/json-value.ts +81 -0
- package/src/tools/name.ts +13 -0
- package/src/tools/registry.ts +52 -0
- package/src/tools/results.ts +100 -0
- package/src/tools/rich-result.ts +70 -0
- package/src/tools/schema.ts +86 -0
- package/src/transport-ownership.ts +17 -0
- package/src/transport.ts +34 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as core from "zod/v4/core";
|
|
2
|
+
import { isRegisteredTool, } from "./definition.js";
|
|
3
|
+
import { ToolInputError, UnknownToolError } from "./errors.js";
|
|
4
|
+
export function createRegistry(tools) {
|
|
5
|
+
const entries = new Map(Object.entries(tools).map(([name, definition]) => {
|
|
6
|
+
if (!isRegisteredTool(definition))
|
|
7
|
+
throw new TypeError(`Tool ${JSON.stringify(name)} must be created with tool()`);
|
|
8
|
+
return [name, Object.freeze({ ...definition })];
|
|
9
|
+
}));
|
|
10
|
+
return {
|
|
11
|
+
list() {
|
|
12
|
+
return Array.from(entries, ([name, definition]) => {
|
|
13
|
+
const { execute: _execute, ...metadata } = definition;
|
|
14
|
+
return { name, ...metadata };
|
|
15
|
+
});
|
|
16
|
+
},
|
|
17
|
+
async execute(name, input, ctx) {
|
|
18
|
+
const definition = entries.get(name);
|
|
19
|
+
if (!definition)
|
|
20
|
+
throw new UnknownToolError(name);
|
|
21
|
+
const parsed = await core.safeParseAsync(definition.input, input);
|
|
22
|
+
if (!parsed.success)
|
|
23
|
+
throw new ToolInputError(name, parsed.error);
|
|
24
|
+
// A request cancelled during validation never starts its handler.
|
|
25
|
+
ctx.signal.throwIfAborted();
|
|
26
|
+
// Parsing with this definition's schema establishes its handler input type.
|
|
27
|
+
return definition.execute(parsed.data, ctx);
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/tools/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AAEpC,OAAO,EACL,gBAAgB,GAGjB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAe/D,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,MAAM,OAAO,GAAG,IAAI,GAAG,CACrB,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,UAAU,CAAC,EAAE,EAAE;QAC/C,IAAI,CAAC,gBAAgB,CAAC,UAAU,CAAC;YAC/B,MAAM,IAAI,SAAS,CACjB,QAAQ,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,8BAA8B,CAC3D,CAAC;QACJ,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,UAAU,EAAE,CAAC,CAAU,CAAC;IAC3D,CAAC,CAAC,CACH,CAAC;IAEF,OAAO;QACL,IAAI;YACF,OAAO,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,UAAU,CAAC,EAAE,EAAE;gBAChD,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,QAAQ,EAAE,GAAG,UAAU,CAAC;gBACtD,OAAO,EAAE,IAAI,EAAE,GAAG,QAAQ,EAAE,CAAC;YAC/B,CAAC,CAAC,CAAC;QACL,CAAC;QACD,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG;YAC5B,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACrC,IAAI,CAAC,UAAU;gBAAE,MAAM,IAAI,gBAAgB,CAAC,IAAI,CAAC,CAAC;YAClD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YAClE,IAAI,CAAC,MAAM,CAAC,OAAO;gBAAE,MAAM,IAAI,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;YAClE,kEAAkE;YAClE,GAAG,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;YAC5B,4EAA4E;YAC5E,OAAO,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,IAAa,EAAE,GAAG,CAAC,CAAC;QACvD,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ContentBlock, MetaObject } from "@toolgrain/protocol";
|
|
2
|
+
import * as core from "zod/v4/core";
|
|
3
|
+
/** Validated output shared by both protocol adapters. */
|
|
4
|
+
export interface ToolExecutionResult {
|
|
5
|
+
content: ContentBlock[];
|
|
6
|
+
structuredContent?: unknown;
|
|
7
|
+
isError?: boolean;
|
|
8
|
+
_meta?: MetaObject;
|
|
9
|
+
}
|
|
10
|
+
export declare function toolResult(value: unknown): ToolExecutionResult;
|
|
11
|
+
/** Validate supplied structured data, including data in explicit error results. */
|
|
12
|
+
export declare function resolveToolResult(name: string, value: unknown, output?: core.$ZodType): Promise<ToolExecutionResult>;
|
|
13
|
+
export declare function toolError(message: string): ToolExecutionResult;
|
|
14
|
+
//# sourceMappingURL=results.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"results.d.ts","sourceRoot":"","sources":["../../src/tools/results.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AAKpC,yDAAyD;AACzD,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,UAAU,CAAC;CACpB;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,CAK9D;AAsCD,mFAAmF;AACnF,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,OAAO,EACd,MAAM,CAAC,EAAE,IAAI,CAAC,QAAQ,GACrB,OAAO,CAAC,mBAAmB,CAAC,CA8B9B;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,mBAAmB,CAK9D"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import * as core from "zod/v4/core";
|
|
2
|
+
import { explicitResult } from "./content.js";
|
|
3
|
+
import { assertJSON } from "./json-value.js";
|
|
4
|
+
import { isMcpResult } from "./rich-result.js";
|
|
5
|
+
export function toolResult(value) {
|
|
6
|
+
if (isMcpResult(value))
|
|
7
|
+
return withStructuredText(readExplicit(value));
|
|
8
|
+
if (typeof value === "string")
|
|
9
|
+
return { content: [{ type: "text", text: value }] };
|
|
10
|
+
return structuredResult(value);
|
|
11
|
+
}
|
|
12
|
+
function structuredResult(value, options) {
|
|
13
|
+
assertJSON(value, options);
|
|
14
|
+
const text = JSON.stringify(value);
|
|
15
|
+
return {
|
|
16
|
+
content: [{ type: "text", text }],
|
|
17
|
+
structuredContent: JSON.parse(text),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function readExplicit(value) {
|
|
21
|
+
// Structured content is application data, so undefined values inside it fail.
|
|
22
|
+
const { structuredContent } = value.value;
|
|
23
|
+
if (structuredContent !== undefined)
|
|
24
|
+
assertJSON(structuredContent);
|
|
25
|
+
// Undefined optional envelope fields, such as `isError: undefined`, are absent.
|
|
26
|
+
assertJSON(value.value, { omitUndefined: true });
|
|
27
|
+
return explicitResult.parse(JSON.parse(JSON.stringify(value.value)));
|
|
28
|
+
}
|
|
29
|
+
function withStructuredText(result) {
|
|
30
|
+
if (Object.hasOwn(result, "structuredContent")) {
|
|
31
|
+
const text = JSON.stringify(result.structuredContent);
|
|
32
|
+
if (!result.content.some((block) => block.type === "text" && block.text === text))
|
|
33
|
+
result.content.push({ type: "text", text });
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
/** Validate supplied structured data, including data in explicit error results. */
|
|
38
|
+
export async function resolveToolResult(name, value, output) {
|
|
39
|
+
if (!output)
|
|
40
|
+
return toolResult(value);
|
|
41
|
+
const explicit = isMcpResult(value) ? readExplicit(value) : undefined;
|
|
42
|
+
if (explicit && !Object.hasOwn(explicit, "structuredContent")) {
|
|
43
|
+
if (explicit.isError)
|
|
44
|
+
return explicit;
|
|
45
|
+
throw new Error(`Output schema requires structuredContent for tool: ${name}`);
|
|
46
|
+
}
|
|
47
|
+
const parsed = await core.safeParseAsync(output, explicit ? explicit.structuredContent : value);
|
|
48
|
+
if (!parsed.success)
|
|
49
|
+
throw new Error(`Invalid output for tool: ${name}`, {
|
|
50
|
+
cause: parsed.error,
|
|
51
|
+
});
|
|
52
|
+
// The schema accepted these values; undefined optional properties are absent.
|
|
53
|
+
const result = structuredResult(parsed.data, { omitUndefined: true });
|
|
54
|
+
if (!explicit)
|
|
55
|
+
return result;
|
|
56
|
+
// Text serializing the raw data describes the parsed data after output parsing.
|
|
57
|
+
const raw = JSON.stringify(explicit.structuredContent);
|
|
58
|
+
const text = JSON.stringify(result.structuredContent);
|
|
59
|
+
return withStructuredText({
|
|
60
|
+
...explicit,
|
|
61
|
+
content: explicit.content.map((block) => block.type === "text" && block.text === raw ? { ...block, text } : block),
|
|
62
|
+
structuredContent: result.structuredContent,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
export function toolError(message) {
|
|
66
|
+
return {
|
|
67
|
+
isError: true,
|
|
68
|
+
content: [{ type: "text", text: message }],
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=results.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"results.js","sourceRoot":"","sources":["../../src/tools/results.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ContentBlock, MetaObject } from "@toolgrain/protocol";
|
|
2
|
+
import type { DeepReadonly, JsonCompatible } from "./json-value.js";
|
|
3
|
+
export interface McpResultOptions<Structured = unknown> {
|
|
4
|
+
content: readonly DeepReadonly<ContentBlock>[];
|
|
5
|
+
structuredContent?: Structured & JsonCompatible<NoInfer<Structured>>;
|
|
6
|
+
isError?: boolean;
|
|
7
|
+
_meta?: MetaObject;
|
|
8
|
+
}
|
|
9
|
+
export declare class McpResult<Structured> {
|
|
10
|
+
readonly value: McpResultOptions<Structured>;
|
|
11
|
+
constructor(value: McpResultOptions<Structured>);
|
|
12
|
+
/** Structural brand shared by package copies; object spreads do not keep it. */
|
|
13
|
+
"~toolgrain"(): "result@1";
|
|
14
|
+
}
|
|
15
|
+
/** Recognize results created by any compatible copy of this package. */
|
|
16
|
+
export declare function isMcpResult(value: unknown): value is McpResult<unknown>;
|
|
17
|
+
export type McpErrorResult<Structured = never> = McpResult<Structured> & {
|
|
18
|
+
readonly value: McpResultOptions<Structured> & {
|
|
19
|
+
isError: true;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
/** A result whose structured data is present, as required by an output schema. */
|
|
23
|
+
export type McpStructuredResult<Structured> = McpResult<Structured> & {
|
|
24
|
+
readonly value: McpResultOptions<Structured> & {
|
|
25
|
+
structuredContent: Structured;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
/** Explicitly return MCP content. Ordinary objects always remain application data. */
|
|
29
|
+
export declare function mcpResult<Structured = never>(value: McpResultOptions<Structured> & {
|
|
30
|
+
isError: true;
|
|
31
|
+
structuredContent: Structured;
|
|
32
|
+
}): McpErrorResult<Structured> & McpStructuredResult<Structured>;
|
|
33
|
+
export declare function mcpResult(value: McpResultOptions<never> & {
|
|
34
|
+
isError: true;
|
|
35
|
+
}): McpErrorResult<never>;
|
|
36
|
+
export declare function mcpResult<Structured>(value: McpResultOptions<Structured> & {
|
|
37
|
+
isError: true;
|
|
38
|
+
}): McpErrorResult<Structured>;
|
|
39
|
+
export declare function mcpResult<Structured>(value: McpResultOptions<Structured> & {
|
|
40
|
+
structuredContent: Structured;
|
|
41
|
+
}): McpStructuredResult<Structured>;
|
|
42
|
+
export declare function mcpResult(value: McpResultOptions<never>): McpResult<never>;
|
|
43
|
+
export declare function mcpResult<Structured = never>(value: McpResultOptions<Structured>): McpResult<Structured>;
|
|
44
|
+
//# sourceMappingURL=rich-result.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rich-result.d.ts","sourceRoot":"","sources":["../../src/tools/rich-result.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEpE,MAAM,WAAW,gBAAgB,CAAC,UAAU,GAAG,OAAO;IACpD,OAAO,EAAE,SAAS,YAAY,CAAC,YAAY,CAAC,EAAE,CAAC;IAC/C,iBAAiB,CAAC,EAAE,UAAU,GAAG,cAAc,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;IACrE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,UAAU,CAAC;CACpB;AAKD,qBAAa,SAAS,CAAC,UAAU;IACnB,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC,UAAU,CAAC;IAAxD,YAAqB,KAAK,EAAE,gBAAgB,CAAC,UAAU,CAAC,EAEvD;IAED,gFAAgF;IAChF,YAAY,IAAI,UAAU,CAEzB;CACF;AAGD,wEAAwE;AACxE,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAAC,OAAO,CAAC,CAMvE;AAED,MAAM,MAAM,cAAc,CAAC,UAAU,GAAG,KAAK,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG;IACvE,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC,UAAU,CAAC,GAAG;QAAE,OAAO,EAAE,IAAI,CAAA;KAAE,CAAC;CAClE,CAAC;AAEF,kFAAkF;AAClF,MAAM,MAAM,mBAAmB,CAAC,UAAU,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG;IACpE,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC,UAAU,CAAC,GAAG;QAC7C,iBAAiB,EAAE,UAAU,CAAC;KAC/B,CAAC;CACH,CAAC;AAEF,sFAAsF;AACtF,wBAAgB,SAAS,CAAC,UAAU,GAAG,KAAK,EAC1C,KAAK,EAAE,gBAAgB,CAAC,UAAU,CAAC,GAAG;IACpC,OAAO,EAAE,IAAI,CAAC;IACd,iBAAiB,EAAE,UAAU,CAAC;CAC/B,GACA,cAAc,CAAC,UAAU,CAAC,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;AAChE,wBAAgB,SAAS,CACvB,KAAK,EAAE,gBAAgB,CAAC,KAAK,CAAC,GAAG;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GACjD,cAAc,CAAC,KAAK,CAAC,CAAC;AACzB,wBAAgB,SAAS,CAAC,UAAU,EAClC,KAAK,EAAE,gBAAgB,CAAC,UAAU,CAAC,GAAG;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GACtD,cAAc,CAAC,UAAU,CAAC,CAAC;AAC9B,wBAAgB,SAAS,CAAC,UAAU,EAClC,KAAK,EAAE,gBAAgB,CAAC,UAAU,CAAC,GAAG;IAAE,iBAAiB,EAAE,UAAU,CAAA;CAAE,GACtE,mBAAmB,CAAC,UAAU,CAAC,CAAC;AACnC,wBAAgB,SAAS,CAAC,KAAK,EAAE,gBAAgB,CAAC,KAAK,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;AAC5E,wBAAgB,SAAS,CAAC,UAAU,GAAG,KAAK,EAC1C,KAAK,EAAE,gBAAgB,CAAC,UAAU,CAAC,GAClC,SAAS,CAAC,UAAU,CAAC,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Registered symbols let duplicate package copies recognize each other's results.
|
|
2
|
+
const RESULT_BRAND = Symbol.for("@toolgrain/mcp/result@1");
|
|
3
|
+
export class McpResult {
|
|
4
|
+
value;
|
|
5
|
+
constructor(value) {
|
|
6
|
+
this.value = value;
|
|
7
|
+
Object.freeze(this);
|
|
8
|
+
}
|
|
9
|
+
/** Structural brand shared by package copies; object spreads do not keep it. */
|
|
10
|
+
"~toolgrain"() {
|
|
11
|
+
return "result@1";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(McpResult.prototype, RESULT_BRAND, { value: true });
|
|
15
|
+
/** Recognize results created by any compatible copy of this package. */
|
|
16
|
+
export function isMcpResult(value) {
|
|
17
|
+
return (typeof value === "object" &&
|
|
18
|
+
value !== null &&
|
|
19
|
+
value[RESULT_BRAND] === true);
|
|
20
|
+
}
|
|
21
|
+
export function mcpResult(value) {
|
|
22
|
+
return new McpResult(value);
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=rich-result.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rich-result.js","sourceRoot":"","sources":["../../src/tools/rich-result.ts"],"names":[],"mappings":"AAUA,kFAAkF;AAClF,MAAM,YAAY,GAAG,MAAM,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;AAE3D,MAAM,OAAO,SAAS;IACC,KAAK;IAA1B,YAAqB,KAAmC;qBAAnC,KAAK;QACxB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACtB,CAAC;IAED,gFAAgF;IAChF,YAAY;QACV,OAAO,UAAU,CAAC;IACpB,CAAC;CACF;AACD,MAAM,CAAC,cAAc,CAAC,SAAS,CAAC,SAAS,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AAE1E,wEAAwE;AACxE,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACb,KAAsC,CAAC,YAAY,CAAC,KAAK,IAAI,CAC/D,CAAC;AACJ,CAAC;AAiCD,MAAM,UAAU,SAAS,CACvB,KAAmC;IAEnC,OAAO,IAAI,SAAS,CAAC,KAAK,CAAC,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Tool as ProtocolTool } from "@toolgrain/protocol";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import * as core from "zod/v4/core";
|
|
4
|
+
import type { ToolMap } from "./registry.js";
|
|
5
|
+
export declare const toolAnnotations: z.ZodObject<{
|
|
6
|
+
title: z.ZodOptional<z.ZodString>;
|
|
7
|
+
readOnlyHint: z.ZodOptional<z.ZodBoolean>;
|
|
8
|
+
destructiveHint: z.ZodOptional<z.ZodBoolean>;
|
|
9
|
+
idempotentHint: z.ZodOptional<z.ZodBoolean>;
|
|
10
|
+
openWorldHint: z.ZodOptional<z.ZodBoolean>;
|
|
11
|
+
}, core.$loose>;
|
|
12
|
+
export declare class ToolSchemaError extends Error {
|
|
13
|
+
readonly toolName: string;
|
|
14
|
+
readonly schemaKind: "input" | "output" | "metadata";
|
|
15
|
+
constructor(toolName: string, cause: unknown, schemaKind?: "input" | "output" | "metadata");
|
|
16
|
+
}
|
|
17
|
+
export declare function describeTool(name: string, definition: Pick<ToolMap[string], "input" | "output" | "title" | "description" | "icons" | "annotations" | "_meta">): ProtocolTool;
|
|
18
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/tools/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,IAAI,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AAMpC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C,eAAO,MAAM,eAAe;;;;;;eAM1B,CAAC;AAUH,qBAAa,eAAgB,SAAQ,KAAK;aAEtB,QAAQ,EAAE,MAAM;aAEhB,UAAU,EAAE,OAAO,GAAG,QAAQ,GAAG,UAAU;IAH7D,YACkB,QAAQ,EAAE,MAAM,EAChC,KAAK,EAAE,OAAO,EACE,UAAU,GAAE,OAAO,GAAG,QAAQ,GAAG,UAAoB,EAMtE;CACF;AAED,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,IAAI,CACd,OAAO,CAAC,MAAM,CAAC,EACb,OAAO,GACP,QAAQ,GACR,OAAO,GACP,aAAa,GACb,OAAO,GACP,aAAa,GACb,OAAO,CACV,GACA,YAAY,CAkCd"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import * as core from "zod/v4/core";
|
|
3
|
+
import { icon } from "../protocol/icon.js";
|
|
4
|
+
import { object } from "../protocol/messages.js";
|
|
5
|
+
import { TOOL_METADATA_KEYS } from "./definition.js";
|
|
6
|
+
import { assertJSON } from "./json-value.js";
|
|
7
|
+
import { validateToolName } from "./name.js";
|
|
8
|
+
export const toolAnnotations = z.looseObject({
|
|
9
|
+
title: z.string().optional(),
|
|
10
|
+
readOnlyHint: z.boolean().optional(),
|
|
11
|
+
destructiveHint: z.boolean().optional(),
|
|
12
|
+
idempotentHint: z.boolean().optional(),
|
|
13
|
+
openWorldHint: z.boolean().optional(),
|
|
14
|
+
});
|
|
15
|
+
const metadata = z.strictObject({
|
|
16
|
+
title: z.string().optional(),
|
|
17
|
+
description: z.string().optional(),
|
|
18
|
+
icons: z.array(icon).optional(),
|
|
19
|
+
annotations: toolAnnotations.optional(),
|
|
20
|
+
_meta: object.optional(),
|
|
21
|
+
});
|
|
22
|
+
export class ToolSchemaError extends Error {
|
|
23
|
+
toolName;
|
|
24
|
+
schemaKind;
|
|
25
|
+
constructor(toolName, cause, schemaKind = "input") {
|
|
26
|
+
super(`Cannot expose ${schemaKind} schema for tool: ${toolName}`, {
|
|
27
|
+
cause,
|
|
28
|
+
});
|
|
29
|
+
this.toolName = toolName;
|
|
30
|
+
this.schemaKind = schemaKind;
|
|
31
|
+
this.name = "ToolSchemaError";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export function describeTool(name, definition) {
|
|
35
|
+
validateToolName(name);
|
|
36
|
+
let schemaKind = "input";
|
|
37
|
+
try {
|
|
38
|
+
const inputSchema = core.toJSONSchema(definition.input, {
|
|
39
|
+
io: "input",
|
|
40
|
+
target: "draft-2020-12",
|
|
41
|
+
});
|
|
42
|
+
if (inputSchema.type !== "object")
|
|
43
|
+
throw new Error("Tool input schema must have an object root");
|
|
44
|
+
schemaKind = "output";
|
|
45
|
+
const outputSchema = definition.output === undefined
|
|
46
|
+
? undefined
|
|
47
|
+
: core.toJSONSchema(definition.output, {
|
|
48
|
+
io: "output",
|
|
49
|
+
target: "draft-2020-12",
|
|
50
|
+
});
|
|
51
|
+
schemaKind = "metadata";
|
|
52
|
+
const fields = {};
|
|
53
|
+
for (const key of TOOL_METADATA_KEYS)
|
|
54
|
+
if (definition[key] !== undefined)
|
|
55
|
+
fields[key] = definition[key];
|
|
56
|
+
assertJSON(fields);
|
|
57
|
+
metadata.parse(fields);
|
|
58
|
+
const result = {
|
|
59
|
+
name,
|
|
60
|
+
inputSchema: { ...inputSchema, type: "object" },
|
|
61
|
+
...(outputSchema === undefined ? {} : { outputSchema }),
|
|
62
|
+
...fields,
|
|
63
|
+
};
|
|
64
|
+
return structuredClone(result);
|
|
65
|
+
}
|
|
66
|
+
catch (cause) {
|
|
67
|
+
throw new ToolSchemaError(name, cause, schemaKind);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/tools/schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RequestTransport, Transport } from "./transport.js";
|
|
2
|
+
/** Claim once before startup. Share weak ownership across compatible package copies. */
|
|
3
|
+
export declare function claimTransport(transport: Transport | RequestTransport): void;
|
|
4
|
+
//# sourceMappingURL=transport-ownership.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport-ownership.d.ts","sourceRoot":"","sources":["../src/transport-ownership.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAIlE,wFAAwF;AACxF,wBAAgB,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,gBAAgB,GAAG,IAAI,CAW5E"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const key = Symbol.for("@toolgrain/mcp/claimed-transports/v1");
|
|
2
|
+
/** Claim once before startup. Share weak ownership across compatible package copies. */
|
|
3
|
+
export function claimTransport(transport) {
|
|
4
|
+
let claimed = Object.getOwnPropertyDescriptor(globalThis, key)?.value;
|
|
5
|
+
if (!claimed) {
|
|
6
|
+
claimed = new WeakSet();
|
|
7
|
+
Object.defineProperty(globalThis, key, { value: claimed });
|
|
8
|
+
}
|
|
9
|
+
if (claimed.has(transport))
|
|
10
|
+
throw new Error("Transport is already attached to a server");
|
|
11
|
+
claimed.add(transport);
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=transport-ownership.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport-ownership.js","sourceRoot":"","sources":["../src/transport-ownership.ts"],"names":[],"mappings":"AAEA,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC;AAE/D,wFAAwF;AACxF,MAAM,UAAU,cAAc,CAAC,SAAuC;IACpE,IAAI,OAAO,GAAG,MAAM,CAAC,wBAAwB,CAAC,UAAU,EAAE,GAAG,CAAC,EAAE,KAEnD,CAAC;IACd,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,GAAG,IAAI,OAAO,EAAU,CAAC;QAChC,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;IAC7D,CAAC;IACD,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC/D,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ProtocolResponse } from "@toolgrain/protocol";
|
|
2
|
+
import type { Server } from "./server.js";
|
|
3
|
+
/** Callbacks are synchronous; message processing runs independently. */
|
|
4
|
+
export interface TransportCallbacks {
|
|
5
|
+
onMessage(message: unknown): void;
|
|
6
|
+
onParseError(error: unknown): void;
|
|
7
|
+
onError(error: unknown): void;
|
|
8
|
+
onClose(): void;
|
|
9
|
+
}
|
|
10
|
+
/** Single-use adapters own UTF-8 framing, JSON decoding, and stream cleanup. */
|
|
11
|
+
export interface Transport {
|
|
12
|
+
readonly mode?: "message";
|
|
13
|
+
start(callbacks: TransportCallbacks): Promise<void>;
|
|
14
|
+
send(message: ProtocolResponse): Promise<void>;
|
|
15
|
+
close(): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
/** Single-use request transports own listeners and use per-request authorization. */
|
|
18
|
+
export interface RequestTransport {
|
|
19
|
+
readonly mode: "request";
|
|
20
|
+
start(server: Server, callbacks: Pick<TransportCallbacks, "onError" | "onClose">): Promise<void>;
|
|
21
|
+
close(): Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
export declare function isRequestTransport(transport: Transport | RequestTransport): transport is RequestTransport;
|
|
24
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,wEAAwE;AACxE,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACnC,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IAC9B,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,gFAAgF;AAChF,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC;IAC1B,KAAK,CAAC,SAAS,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,IAAI,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,qFAAqF;AACrF,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,KAAK,CACH,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,IAAI,CAAC,kBAAkB,EAAE,SAAS,GAAG,SAAS,CAAC,GACzD,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,SAAS,GAAG,gBAAgB,GACtC,SAAS,IAAI,gBAAgB,CAE/B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AA6BA,MAAM,UAAU,kBAAkB,CAChC,SAAuC;IAEvC,OAAO,MAAM,IAAI,SAAS,IAAI,SAAS,CAAC,IAAI,KAAK,SAAS,CAAC;AAC7D,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,67 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toolgrain/mcp",
|
|
3
|
-
"version": "0.0.0
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0-rc.0",
|
|
4
|
+
"description": "Define reusable, typed MCP tools and servers with Zod validation.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/keshavkatwe/toolgrain.git",
|
|
9
|
+
"directory": "packages/mcp"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/keshavkatwe/toolgrain/tree/main/packages/mcp#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/keshavkatwe/toolgrain/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"toolgrain",
|
|
17
|
+
"mcp",
|
|
18
|
+
"model-context-protocol",
|
|
19
|
+
"typescript",
|
|
20
|
+
"zod",
|
|
21
|
+
"tools"
|
|
22
|
+
],
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public",
|
|
25
|
+
"registry": "https://registry.npmjs.org/",
|
|
26
|
+
"tag": "next"
|
|
27
|
+
},
|
|
28
|
+
"type": "module",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"import": "./dist/index.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist",
|
|
37
|
+
"src/**/*.ts"
|
|
38
|
+
],
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
41
|
+
"check-types": "tsc --noEmit",
|
|
42
|
+
"test": "vitest run",
|
|
43
|
+
"test:package": "toolgrain-repo mcp-compat && toolgrain-repo exports",
|
|
44
|
+
"dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput",
|
|
45
|
+
"test:coverage": "vitest run --coverage",
|
|
46
|
+
"test:size": "toolgrain-repo size"
|
|
47
|
+
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"zod": "^4.6.6"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/node": "24.10.0",
|
|
53
|
+
"ajv": "8.20.0",
|
|
54
|
+
"fast-check": "4.10.2",
|
|
55
|
+
"typescript": "7.0.2",
|
|
56
|
+
"vitest": "5.0.4",
|
|
57
|
+
"zod": "4.6.7",
|
|
58
|
+
"@toolgrain/repo-tools": "0.0.0"
|
|
59
|
+
},
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"@toolgrain/protocol": "0.1.0-rc.0"
|
|
62
|
+
},
|
|
63
|
+
"engines": {
|
|
64
|
+
"node": "^24.20.0",
|
|
65
|
+
"bun": ">=1.4.4 <2"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Stop waiting promptly even when an integration cannot cancel its underlying work. */
|
|
2
|
+
export function abortable<T>(
|
|
3
|
+
work: Promise<T>,
|
|
4
|
+
signal: AbortSignal,
|
|
5
|
+
): Promise<T> {
|
|
6
|
+
return new Promise<T>((resolve, reject) => {
|
|
7
|
+
const abort = () => reject(signal.reason);
|
|
8
|
+
if (signal.aborted) abort();
|
|
9
|
+
else signal.addEventListener("abort", abort, { once: true });
|
|
10
|
+
work
|
|
11
|
+
.then(resolve, reject)
|
|
12
|
+
.finally(() => signal.removeEventListener("abort", abort));
|
|
13
|
+
});
|
|
14
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { AuthError } from "./errors.js";
|
|
2
|
+
|
|
3
|
+
/** Validate the audience of an already verified token without disclosing its claims. */
|
|
4
|
+
export function validateTokenAudience(
|
|
5
|
+
audience: unknown,
|
|
6
|
+
resource: string,
|
|
7
|
+
): void {
|
|
8
|
+
if (audience === undefined)
|
|
9
|
+
throw new AuthError("invalid_token", {
|
|
10
|
+
description: `Access token is missing the aud claim. Configure the authorization server to include an audience and request a new token with resource=${resource}.`,
|
|
11
|
+
});
|
|
12
|
+
const audiences = typeof audience === "string" ? [audience] : audience;
|
|
13
|
+
if (
|
|
14
|
+
!Array.isArray(audiences) ||
|
|
15
|
+
audiences.some((value) => typeof value !== "string")
|
|
16
|
+
)
|
|
17
|
+
throw new AuthError("invalid_token");
|
|
18
|
+
if (!audiences.includes(resource))
|
|
19
|
+
throw new AuthError("invalid_token", {
|
|
20
|
+
description: `Access token aud claim does not match the expected resource ${resource}. Request a new token with this resource.`,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { abortable } from "./abort.js";
|
|
2
|
+
import { identitySnapshot } from "./context.js";
|
|
3
|
+
import { AuthError } from "./errors.js";
|
|
4
|
+
import type {
|
|
5
|
+
AuthIdentity,
|
|
6
|
+
AuthMetadata,
|
|
7
|
+
AuthProvider,
|
|
8
|
+
AuthScope,
|
|
9
|
+
} from "./types.js";
|
|
10
|
+
import { authUrl } from "./url.js";
|
|
11
|
+
|
|
12
|
+
interface Entry {
|
|
13
|
+
identity: AuthIdentity;
|
|
14
|
+
consumed: boolean;
|
|
15
|
+
disposed: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function createAuthorization(provider?: AuthProvider) {
|
|
19
|
+
const scopes = new WeakMap<AuthScope, Entry>();
|
|
20
|
+
const metadata: AuthMetadata | undefined = provider && {
|
|
21
|
+
...(provider.resource === undefined
|
|
22
|
+
? {}
|
|
23
|
+
: { resource: authUrl(provider.resource) }),
|
|
24
|
+
authorizationServers: provider.authorizationServers.map((issuer) => {
|
|
25
|
+
authUrl(issuer);
|
|
26
|
+
return issuer;
|
|
27
|
+
}),
|
|
28
|
+
};
|
|
29
|
+
if (
|
|
30
|
+
provider &&
|
|
31
|
+
(typeof provider.type !== "symbol" ||
|
|
32
|
+
!metadata?.authorizationServers.length)
|
|
33
|
+
)
|
|
34
|
+
throw new TypeError(
|
|
35
|
+
"Auth providers require a type symbol and at least one authorization server",
|
|
36
|
+
);
|
|
37
|
+
const verify = provider?.verify.bind(provider);
|
|
38
|
+
const type = provider?.type;
|
|
39
|
+
return {
|
|
40
|
+
metadata: () => metadata && structuredClone(metadata),
|
|
41
|
+
async authorize(
|
|
42
|
+
token: string,
|
|
43
|
+
resource: string,
|
|
44
|
+
signal: AbortSignal,
|
|
45
|
+
finish: () => void,
|
|
46
|
+
): Promise<AuthScope> {
|
|
47
|
+
if (!verify || !type) throw new Error("Server has no auth provider");
|
|
48
|
+
signal.throwIfAborted();
|
|
49
|
+
const resolved = metadata?.resource ?? authUrl(resource);
|
|
50
|
+
const identity = {
|
|
51
|
+
type,
|
|
52
|
+
value: identitySnapshot(
|
|
53
|
+
await abortable(
|
|
54
|
+
verify(token, { resource: resolved, signal }),
|
|
55
|
+
signal,
|
|
56
|
+
),
|
|
57
|
+
),
|
|
58
|
+
};
|
|
59
|
+
signal.throwIfAborted();
|
|
60
|
+
const entry: Entry = { identity, consumed: false, disposed: false };
|
|
61
|
+
const scope = Object.freeze({
|
|
62
|
+
signal,
|
|
63
|
+
dispose() {
|
|
64
|
+
if (entry.disposed) return;
|
|
65
|
+
entry.disposed = true;
|
|
66
|
+
finish();
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
scopes.set(scope, entry);
|
|
70
|
+
return scope;
|
|
71
|
+
},
|
|
72
|
+
consume(scope?: AuthScope): AuthIdentity | undefined {
|
|
73
|
+
if (!metadata && !scope) return;
|
|
74
|
+
const entry = scope && scopes.get(scope);
|
|
75
|
+
if (!entry || entry.consumed || entry.disposed)
|
|
76
|
+
throw new AuthError("invalid_token");
|
|
77
|
+
entry.consumed = true;
|
|
78
|
+
return entry.identity;
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ToolContext } from "../tools/context.js";
|
|
2
|
+
import type { AuthIdentity, AuthReadonly } from "./types.js";
|
|
3
|
+
|
|
4
|
+
const key = Symbol.for("@toolgrain/mcp/auth-context/v1");
|
|
5
|
+
|
|
6
|
+
/** Typed integrations bind their accessor to a stable, registered provider symbol. */
|
|
7
|
+
export function createAuthAccessor<Identity extends object>(type: symbol) {
|
|
8
|
+
return (ctx: ToolContext): AuthReadonly<Identity> => {
|
|
9
|
+
const auth = Object.getOwnPropertyDescriptor(ctx, key)?.value as
|
|
10
|
+
| AuthIdentity
|
|
11
|
+
| undefined;
|
|
12
|
+
if (!auth || auth.type !== type)
|
|
13
|
+
throw new Error("The context has no identity for this auth provider");
|
|
14
|
+
return auth.value as AuthReadonly<Identity>;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function attachAuth(ctx: ToolContext, auth?: AuthIdentity): ToolContext {
|
|
19
|
+
if (auth) Object.defineProperty(ctx, key, { value: auth });
|
|
20
|
+
return Object.freeze(ctx);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Auth identities are plain data: copy them before exposing them to handlers. */
|
|
24
|
+
export function identitySnapshot(value: object): object {
|
|
25
|
+
if (value === null || typeof value !== "object")
|
|
26
|
+
throw new TypeError("Auth identities must be objects");
|
|
27
|
+
const copy = structuredClone(value);
|
|
28
|
+
const freeze = (item: unknown, visited: Set<object>) => {
|
|
29
|
+
if (typeof item !== "object" || item === null || visited.has(item)) return;
|
|
30
|
+
const prototype = Object.getPrototypeOf(item);
|
|
31
|
+
if (
|
|
32
|
+
prototype !== Object.prototype &&
|
|
33
|
+
prototype !== Array.prototype &&
|
|
34
|
+
prototype !== null
|
|
35
|
+
)
|
|
36
|
+
throw new TypeError("Auth identities must contain plain data");
|
|
37
|
+
visited.add(item);
|
|
38
|
+
for (const child of Object.values(item)) freeze(child, visited);
|
|
39
|
+
Object.freeze(item);
|
|
40
|
+
};
|
|
41
|
+
freeze(copy, new Set());
|
|
42
|
+
return copy;
|
|
43
|
+
}
|