@automatey-org/mcp 0.1.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/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # @automatey-org/mcp
2
+
3
+ Provider-agnostic [Model Context Protocol](https://modelcontextprotocol.io) helpers for the
4
+ [Vercel AI SDK](https://sdk.vercel.ai).
5
+
6
+ - **`McpHttpClient`** (aka `NativeMcpHttpClient`) — a lazily-connecting MCP client over
7
+ Streamable HTTP. Normalizes network failures into friendly "unavailable" errors while
8
+ re-throwing abort errors so callers can tell cancellation apart from an outage.
9
+ - **`buildMcpToolSet()`** — turns MCP tools into an AI SDK `ToolSet`: each tool's JSON Schema
10
+ is wrapped with `jsonSchema()`, destructive tools are gated unless `unattended`, results are
11
+ flattened for the model, and call/result/error callbacks fire around every invocation.
12
+ - **tooling** — `safeToolName`, `compactToolResult`, `isDestructiveToolName`,
13
+ `destructiveActionPattern` (all overridable via `buildMcpToolSet` options).
14
+
15
+ ```ts
16
+ import { McpHttpClient, buildMcpToolSet } from "@automatey-org/mcp";
17
+ import { streamText } from "ai";
18
+
19
+ const mcp = new McpHttpClient("http://127.0.0.1:8123/mcp", abortSignal);
20
+ const tools = buildMcpToolSet({
21
+ tools: await mcp.listTools(),
22
+ mcp,
23
+ unattended: false,
24
+ onToolCall: (name, input) => console.log("call", name, input),
25
+ });
26
+
27
+ const result = streamText({ model, tools, prompt });
28
+ ```
29
+
30
+ Peer deps: `ai` (>=7 <8), `@modelcontextprotocol/sdk` (>=1.29 <2).
@@ -0,0 +1,48 @@
1
+ import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
2
+ import type { McpCallResult, McpToolInfo } from "./types";
3
+ export interface McpHttpClientOptions {
4
+ /** Signals the AI SDK abort down into the transport + closes the session. */
5
+ abortSignal?: AbortSignal;
6
+ /** Client identity advertised during `initialize`. */
7
+ clientName?: string;
8
+ clientVersion?: string;
9
+ /**
10
+ * Supply a custom transport instead of the default Streamable HTTP one.
11
+ * Useful for in-memory testing (e.g. `InMemoryTransport` from the MCP SDK)
12
+ * or non-HTTP transports. When provided, `endpoint` is ignored.
13
+ */
14
+ transportFactory?: () => Transport;
15
+ }
16
+ export interface McpInitializeResult {
17
+ protocolVersion?: string;
18
+ capabilities?: unknown;
19
+ serverInfo?: {
20
+ name?: string;
21
+ version?: string;
22
+ };
23
+ }
24
+ /**
25
+ * A thin, lazily-connecting MCP client over Streamable HTTP. Network failures
26
+ * are normalized into friendly "unavailable" errors while abort errors are
27
+ * re-thrown untouched so the caller can distinguish user cancellation.
28
+ */
29
+ export declare class McpHttpClient {
30
+ private readonly endpoint;
31
+ private client;
32
+ private transport;
33
+ private readonly abortSignal?;
34
+ private readonly clientName;
35
+ private readonly clientVersion;
36
+ private readonly transportFactory?;
37
+ constructor(endpoint: string, optionsOrSignal?: AbortSignal | McpHttpClientOptions);
38
+ getSessionId(): string | undefined;
39
+ private unavailableMessage;
40
+ private ensureConnected;
41
+ initialize(): Promise<McpInitializeResult>;
42
+ listTools(): Promise<McpToolInfo[]>;
43
+ callTool(name: string, args: unknown): Promise<McpCallResult>;
44
+ close(): Promise<void>;
45
+ }
46
+ /** Back-compat alias for the original MadeHuman UX name. */
47
+ export { McpHttpClient as NativeMcpHttpClient };
48
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+CAA+C,CAAC;AAC/E,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE1D,MAAM,WAAW,oBAAoB;IACnC,6EAA6E;IAC7E,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,sDAAsD;IACtD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,SAAS,CAAC;CACpC;AAED,MAAM,WAAW,mBAAmB;IAClC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAClD;AAyBD;;;;GAIG;AACH,qBAAa,aAAa;IActB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAb3B,OAAO,CAAC,MAAM,CAAqB;IAEnC,OAAO,CAAC,SAAS,CAAwB;IAEzC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAc;IAE3C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IAEpC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IAEvC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAkB;gBAGjC,QAAQ,EAAE,MAAM,EACjC,eAAe,CAAC,EAAE,WAAW,GAAG,oBAAoB;IAYtD,YAAY,IAAI,MAAM,GAAG,SAAS;IAIlC,OAAO,CAAC,kBAAkB;YAIZ,eAAe;IA+BvB,UAAU,IAAI,OAAO,CAAC,mBAAmB,CAAC;IAkB1C,SAAS,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAcnC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC;IAgB7D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAwB7B;AAED,4DAA4D;AAC5D,OAAO,EAAE,aAAa,IAAI,mBAAmB,EAAE,CAAC"}
package/dist/client.js ADDED
@@ -0,0 +1,142 @@
1
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
3
+ function isAbortError(error) {
4
+ return error instanceof Error && error.name === "AbortError";
5
+ }
6
+ function isNetworkError(error) {
7
+ return (error instanceof TypeError ||
8
+ (error instanceof Error && /fetch|network|Failed to fetch/i.test(error.message)));
9
+ }
10
+ function normalizeToolInfo(tool) {
11
+ return {
12
+ name: tool.name,
13
+ description: tool.description ?? "",
14
+ inputSchema: tool.inputSchema,
15
+ };
16
+ }
17
+ /**
18
+ * A thin, lazily-connecting MCP client over Streamable HTTP. Network failures
19
+ * are normalized into friendly "unavailable" errors while abort errors are
20
+ * re-thrown untouched so the caller can distinguish user cancellation.
21
+ */
22
+ export class McpHttpClient {
23
+ endpoint;
24
+ client;
25
+ transport;
26
+ abortSignal;
27
+ clientName;
28
+ clientVersion;
29
+ transportFactory;
30
+ constructor(endpoint, optionsOrSignal) {
31
+ this.endpoint = endpoint;
32
+ const options = optionsOrSignal instanceof AbortSignal
33
+ ? { abortSignal: optionsOrSignal }
34
+ : optionsOrSignal ?? {};
35
+ this.abortSignal = options.abortSignal;
36
+ this.clientName = options.clientName ?? "automatey-mcp";
37
+ this.clientVersion = options.clientVersion ?? "0.1.0";
38
+ this.transportFactory = options.transportFactory;
39
+ }
40
+ getSessionId() {
41
+ return this.transport?.sessionId;
42
+ }
43
+ unavailableMessage(target) {
44
+ return `${target} is unavailable because the MCP server at ${this.endpoint} could not be reached.`;
45
+ }
46
+ async ensureConnected() {
47
+ if (this.client && this.transport) {
48
+ return this.client;
49
+ }
50
+ const transport = this.transportFactory?.() ??
51
+ new StreamableHTTPClientTransport(new URL(this.endpoint), {
52
+ requestInit: this.abortSignal ? { signal: this.abortSignal } : undefined,
53
+ });
54
+ const client = new Client({ name: this.clientName, version: this.clientVersion }, { capabilities: {} });
55
+ if (this.abortSignal) {
56
+ this.abortSignal.addEventListener("abort", () => {
57
+ void this.close();
58
+ }, { once: true });
59
+ }
60
+ await client.connect(transport);
61
+ this.transport = transport;
62
+ this.client = client;
63
+ return client;
64
+ }
65
+ async initialize() {
66
+ try {
67
+ const client = await this.ensureConnected();
68
+ return {
69
+ protocolVersion: this.transport
70
+ ?.protocolVersion,
71
+ capabilities: client.getServerCapabilities(),
72
+ serverInfo: client.getServerVersion(),
73
+ };
74
+ }
75
+ catch (error) {
76
+ if (isAbortError(error))
77
+ throw error;
78
+ if (isNetworkError(error)) {
79
+ throw new Error(this.unavailableMessage("MCP server"));
80
+ }
81
+ throw error;
82
+ }
83
+ }
84
+ async listTools() {
85
+ try {
86
+ const client = await this.ensureConnected();
87
+ const result = await client.listTools();
88
+ return (result.tools ?? []).map(normalizeToolInfo);
89
+ }
90
+ catch (error) {
91
+ if (isAbortError(error))
92
+ throw error;
93
+ if (isNetworkError(error)) {
94
+ throw new Error(this.unavailableMessage("MCP tool catalog"));
95
+ }
96
+ throw error;
97
+ }
98
+ }
99
+ async callTool(name, args) {
100
+ try {
101
+ const client = await this.ensureConnected();
102
+ return (await client.callTool({
103
+ name,
104
+ arguments: (args ?? {}),
105
+ }));
106
+ }
107
+ catch (error) {
108
+ if (isAbortError(error))
109
+ throw error;
110
+ if (isNetworkError(error)) {
111
+ throw new Error(this.unavailableMessage(`MCP ${name}`));
112
+ }
113
+ throw error;
114
+ }
115
+ }
116
+ async close() {
117
+ const transport = this.transport;
118
+ const client = this.client;
119
+ this.transport = undefined;
120
+ this.client = undefined;
121
+ if (!transport || !client) {
122
+ return;
123
+ }
124
+ if ("terminateSession" in transport) {
125
+ try {
126
+ await transport.terminateSession();
127
+ }
128
+ catch {
129
+ // Some servers may not support DELETE /mcp; fall through to close.
130
+ }
131
+ }
132
+ try {
133
+ await client.close();
134
+ }
135
+ catch {
136
+ // best-effort shutdown only
137
+ }
138
+ }
139
+ }
140
+ /** Back-compat alias for the original MadeHuman UX name. */
141
+ export { McpHttpClient as NativeMcpHttpClient };
142
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export type { McpToolInfo, McpCallResult } from "./types";
2
+ export { McpHttpClient, NativeMcpHttpClient, type McpHttpClientOptions, type McpInitializeResult, } from "./client";
3
+ export { buildMcpToolSet, type BuildMcpToolSetOptions, type McpToolCaller, } from "./toolset";
4
+ export { compactToolResult, isDestructiveToolName, safeToolName, destructiveActionPattern, } from "./tooling";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,GACzB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,eAAe,EACf,KAAK,sBAAsB,EAC3B,KAAK,aAAa,GACnB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,YAAY,EACZ,wBAAwB,GACzB,MAAM,WAAW,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { McpHttpClient, NativeMcpHttpClient, } from "./client";
2
+ export { buildMcpToolSet, } from "./toolset";
3
+ export { compactToolResult, isDestructiveToolName, safeToolName, destructiveActionPattern, } from "./tooling";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,aAAa,EACb,mBAAmB,GAGpB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,eAAe,GAGhB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,YAAY,EACZ,wBAAwB,GACzB,MAAM,WAAW,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { McpCallResult } from "./types";
2
+ /**
3
+ * Heuristic pattern for tools whose side effects should require explicit
4
+ * approval (or unattended mode) before they run. Callers can override the
5
+ * gate entirely via {@link BuildMcpToolSetOptions.isDestructive}.
6
+ */
7
+ export declare const destructiveActionPattern: RegExp;
8
+ export declare function isDestructiveToolName(name: string): boolean;
9
+ /** Sanitize an MCP tool name into an identifier the AI SDK / model will accept. */
10
+ export declare function safeToolName(name: string): string;
11
+ /**
12
+ * Flatten an MCP call result to a string for the model. Concatenates text
13
+ * parts when present, otherwise pretty-prints the whole result. Truncates to
14
+ * `maxChars` to keep tool output from blowing up the context window.
15
+ */
16
+ export declare function compactToolResult(result: McpCallResult, maxChars?: number): string;
17
+ //# sourceMappingURL=tooling.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tooling.d.ts","sourceRoot":"","sources":["../src/tooling.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,QACiF,CAAC;AAEvH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE3D;AAED,mFAAmF;AACnF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,aAAa,EAAE,QAAQ,SAAO,GAAG,MAAM,CAUhF"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Heuristic pattern for tools whose side effects should require explicit
3
+ * approval (or unattended mode) before they run. Callers can override the
4
+ * gate entirely via {@link BuildMcpToolSetOptions.isDestructive}.
5
+ */
6
+ export const destructiveActionPattern = /(delete|remove|save|write|commit|push|spawn|create|import|export|build|run|execute|python|system|manage_pipeline)/i;
7
+ export function isDestructiveToolName(name) {
8
+ return destructiveActionPattern.test(name);
9
+ }
10
+ /** Sanitize an MCP tool name into an identifier the AI SDK / model will accept. */
11
+ export function safeToolName(name) {
12
+ return name.replace(/[^A-Za-z0-9_]/g, "_");
13
+ }
14
+ /**
15
+ * Flatten an MCP call result to a string for the model. Concatenates text
16
+ * parts when present, otherwise pretty-prints the whole result. Truncates to
17
+ * `maxChars` to keep tool output from blowing up the context window.
18
+ */
19
+ export function compactToolResult(result, maxChars = 8000) {
20
+ if (Array.isArray(result.content)) {
21
+ const text = result.content
22
+ .filter((part) => part.type === "text" && typeof part.text === "string")
23
+ .map((part) => part.text)
24
+ .join("\n");
25
+ return text.length > maxChars ? `${text.slice(0, maxChars)}\n…[truncated]` : text;
26
+ }
27
+ const text = JSON.stringify(result, null, 2);
28
+ return text.length > maxChars ? `${text.slice(0, maxChars)}\n…[truncated]` : text;
29
+ }
30
+ //# sourceMappingURL=tooling.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tooling.js","sourceRoot":"","sources":["../src/tooling.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,CAAC,MAAM,wBAAwB,GACnC,oHAAoH,CAAC;AAEvH,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,OAAO,wBAAwB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC7C,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC;AAC7C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAqB,EAAE,QAAQ,GAAG,IAAI;IACtE,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO;aACxB,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC;aACvE,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;aACxB,IAAI,CAAC,IAAI,CAAC,CAAC;QACd,OAAO,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC;IACpF,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC7C,OAAO,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC;AACpF,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { type ToolSet } from "ai";
2
+ import type { McpCallResult, McpToolInfo } from "./types";
3
+ /** The subset of an MCP client that {@link buildMcpToolSet} needs. */
4
+ export interface McpToolCaller {
5
+ callTool(name: string, args: unknown): Promise<McpCallResult>;
6
+ }
7
+ export interface BuildMcpToolSetOptions {
8
+ tools: McpToolInfo[];
9
+ mcp: McpToolCaller;
10
+ /** When false, tools flagged destructive are blocked before execution. */
11
+ unattended: boolean;
12
+ onToolCall?: (toolName: string, input: unknown) => void;
13
+ onToolResult?: (toolName: string, output: string) => void;
14
+ onToolError?: (toolName: string, error: string) => void;
15
+ /** Override the destructive-tool gate. Defaults to {@link isDestructiveToolName}. */
16
+ isDestructive?: (toolName: string) => boolean;
17
+ /** Override how a raw MCP result is flattened to a string. Defaults to {@link compactToolResult}. */
18
+ formatResult?: (result: McpCallResult) => string;
19
+ /** Description used when an MCP tool advertises none. */
20
+ fallbackDescription?: (toolName: string) => string;
21
+ }
22
+ /**
23
+ * Turn a list of MCP tools into an AI SDK {@link ToolSet}. Each tool's JSON
24
+ * Schema is wrapped with `jsonSchema()`, destructive tools are gated unless
25
+ * `unattended`, and results are flattened for the model. Call/result/error
26
+ * callbacks fire around every invocation.
27
+ */
28
+ export declare function buildMcpToolSet(options: BuildMcpToolSetOptions): ToolSet;
29
+ //# sourceMappingURL=toolset.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolset.d.ts","sourceRoot":"","sources":["../src/toolset.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,OAAO,EAAE,MAAM,IAAI,CAAC;AACpD,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAG1D,sEAAsE;AACtE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;CAC/D;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,GAAG,EAAE,aAAa,CAAC;IACnB,0EAA0E;IAC1E,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACxD,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1D,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxD,qFAAqF;IACrF,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;IAC9C,qGAAqG;IACrG,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,MAAM,CAAC;IACjD,yDAAyD;IACzD,mBAAmB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;CACpD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAgCxE"}
@@ -0,0 +1,39 @@
1
+ import { jsonSchema, tool } from "ai";
2
+ import { compactToolResult, isDestructiveToolName, safeToolName } from "./tooling";
3
+ /**
4
+ * Turn a list of MCP tools into an AI SDK {@link ToolSet}. Each tool's JSON
5
+ * Schema is wrapped with `jsonSchema()`, destructive tools are gated unless
6
+ * `unattended`, and results are flattened for the model. Call/result/error
7
+ * callbacks fire around every invocation.
8
+ */
9
+ export function buildMcpToolSet(options) {
10
+ const isDestructive = options.isDestructive ?? isDestructiveToolName;
11
+ const formatResult = options.formatResult ?? compactToolResult;
12
+ const fallbackDescription = options.fallbackDescription ?? ((name) => `Call MCP tool ${name}`);
13
+ const result = {};
14
+ for (const mcpTool of options.tools) {
15
+ const exposedName = safeToolName(mcpTool.name);
16
+ result[exposedName] = tool({
17
+ description: mcpTool.description || fallbackDescription(mcpTool.name),
18
+ inputSchema: jsonSchema(mcpTool.inputSchema),
19
+ execute: async (input) => {
20
+ options.onToolCall?.(mcpTool.name, input);
21
+ try {
22
+ if (!options.unattended && isDestructive(mcpTool.name)) {
23
+ throw new Error(`Tool ${mcpTool.name} requires unattended mode or explicit host approval.`);
24
+ }
25
+ const output = formatResult(await options.mcp.callTool(mcpTool.name, input));
26
+ options.onToolResult?.(mcpTool.name, output);
27
+ return output;
28
+ }
29
+ catch (error) {
30
+ const message = error instanceof Error ? error.message : String(error);
31
+ options.onToolError?.(mcpTool.name, message);
32
+ throw error;
33
+ }
34
+ },
35
+ });
36
+ }
37
+ return result;
38
+ }
39
+ //# sourceMappingURL=toolset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolset.js","sourceRoot":"","sources":["../src/toolset.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,IAAI,EAAgB,MAAM,IAAI,CAAC;AAEpD,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAuBnF;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,OAA+B;IAC7D,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,qBAAqB,CAAC;IACrE,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,iBAAiB,CAAC;IAC/D,MAAM,mBAAmB,GACvB,OAAO,CAAC,mBAAmB,IAAI,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC;IAE7E,MAAM,MAAM,GAAY,EAAE,CAAC;IAC3B,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QACpC,MAAM,WAAW,GAAG,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC/C,MAAM,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC;YACzB,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,mBAAmB,CAAC,OAAO,CAAC,IAAI,CAAC;YACrE,WAAW,EAAE,UAAU,CAAC,OAAO,CAAC,WAAW,CAAC;YAC5C,OAAO,EAAE,KAAK,EAAE,KAAc,EAAE,EAAE;gBAChC,OAAO,CAAC,UAAU,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;gBAC1C,IAAI,CAAC;oBACH,IAAI,CAAC,OAAO,CAAC,UAAU,IAAI,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;wBACvD,MAAM,IAAI,KAAK,CACb,QAAQ,OAAO,CAAC,IAAI,sDAAsD,CAC3E,CAAC;oBACJ,CAAC;oBACD,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;oBAC7E,OAAO,CAAC,YAAY,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;oBAC7C,OAAO,MAAM,CAAC;gBAChB,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;oBACvE,OAAO,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;oBAC7C,MAAM,KAAK,CAAC;gBACd,CAAC;YACH,CAAC;SACF,CAAC,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Minimal shape of an MCP tool as advertised by `tools/list`.
3
+ * `inputSchema` is a raw JSON Schema object (passed to the AI SDK via `jsonSchema()`).
4
+ */
5
+ export interface McpToolInfo {
6
+ name: string;
7
+ description: string;
8
+ inputSchema: unknown;
9
+ }
10
+ /**
11
+ * Result of an MCP `tools/call`. The AI SDK never sees this directly — it is
12
+ * flattened to a string by {@link compactToolResult} (or a caller-supplied
13
+ * `formatResult`) before being handed back to the model.
14
+ */
15
+ export interface McpCallResult {
16
+ content?: Array<{
17
+ type?: string;
18
+ text?: string;
19
+ }>;
20
+ isError?: boolean;
21
+ [key: string]: unknown;
22
+ }
23
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@automatey-org/mcp",
3
+ "version": "0.1.1",
4
+ "description": "Provider-agnostic MCP helpers for the Vercel AI SDK: a Streamable-HTTP MCP client and a buildMcpToolSet() that exposes MCP tools as AI SDK tools with destructive-tool gating and call/result callbacks.",
5
+ "type": "module",
6
+ "types": "./dist/index.d.ts",
7
+ "module": "./dist/index.js",
8
+ "main": "./dist/index.js",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "default": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": ["dist", "README.md"],
16
+ "sideEffects": false,
17
+ "scripts": {
18
+ "build": "tsc -p tsconfig.json",
19
+ "typecheck": "tsc -p tsconfig.json --noEmit",
20
+ "test": "vitest run"
21
+ },
22
+ "peerDependencies": {
23
+ "@modelcontextprotocol/sdk": ">=1.29 <2",
24
+ "ai": ">=7 <8"
25
+ },
26
+ "devDependencies": {
27
+ "@modelcontextprotocol/sdk": "^1.29.0",
28
+ "ai": "^7.0.11",
29
+ "typescript": "^5.6.0",
30
+ "vitest": "^4.1.2"
31
+ },
32
+ "license": "MIT",
33
+ "publishConfig": {
34
+ "access": "public"
35
+ }
36
+ }