@ai-sdk/harness-acp 1.0.71 → 1.0.73

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 CHANGED
@@ -1,5 +1,24 @@
1
1
  # @ai-sdk/harness-acp
2
2
 
3
+ ## 1.0.73
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [8c65988]
8
+ - Updated dependencies [8182916]
9
+ - Updated dependencies [527a163]
10
+ - @ai-sdk/provider-utils@5.0.53
11
+ - @ai-sdk/harness@1.0.135
12
+
13
+ ## 1.0.72
14
+
15
+ ### Patch Changes
16
+
17
+ - ede5b89: chore: migrate package builds from tsup to tsdown
18
+ - Updated dependencies [ede5b89]
19
+ - @ai-sdk/harness@1.0.134
20
+ - @ai-sdk/provider-utils@5.0.52
21
+
3
22
  ## 1.0.71
4
23
 
5
24
  ### Patch Changes
@@ -1,302 +1,224 @@
1
- // src/v1/bridge/host-tool-mcp.ts
2
- import { randomUUID } from "crypto";
3
- import { readFile } from "fs/promises";
4
- import { env } from "process";
1
+ import { randomUUID } from "node:crypto";
2
+ import { readFile } from "node:fs/promises";
3
+ import { env } from "node:process";
5
4
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
-
7
- // src/v1/bridge/host-tool-mcp-server.ts
8
5
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
9
- import {
10
- CallToolRequestSchema,
11
- ErrorCode,
12
- ListToolsRequestSchema,
13
- McpError
14
- } from "@modelcontextprotocol/sdk/types.js";
15
- var VERSION = true ? "1.0.71" : "0.0.0-test";
16
- function createHostToolMCPServer({
17
- tools: tools2,
18
- revision = 1,
19
- invoke,
20
- onListTools
21
- }) {
22
- let catalog = createCatalog({ revision, tools: tools2 });
23
- let catalogAcknowledgmentError;
24
- const server = new Server(
25
- {
26
- name: "@ai-sdk/harness-acp-host-tools",
27
- version: VERSION
28
- },
29
- { capabilities: { tools: { listChanged: true } } }
30
- );
31
- server.setRequestHandler(ListToolsRequestSchema, async () => {
32
- if (catalogAcknowledgmentError != null) {
33
- throw catalogAcknowledgmentError;
34
- }
35
- const current = catalog;
36
- if (onListTools != null) {
37
- setImmediate(() => {
38
- void onListTools({ revision: current.revision }).catch((error) => {
39
- catalogAcknowledgmentError = error;
40
- });
41
- });
42
- }
43
- return { tools: current.tools.map(toMCPTool) };
44
- });
45
- server.setRequestHandler(CallToolRequestSchema, async (request2) => {
46
- const current = catalog;
47
- const tool = current.byName.get(request2.params.name);
48
- if (tool == null) {
49
- throw new McpError(
50
- ErrorCode.InvalidParams,
51
- `Unknown host tool: ${request2.params.name}`
52
- );
53
- }
54
- const input = request2.params.arguments ?? {};
55
- const result = await invoke({
56
- toolName: tool.name,
57
- input,
58
- catalogRevision: current.revision
59
- });
60
- return toCallToolResult({ result });
61
- });
62
- return {
63
- server,
64
- updateCatalog: async ({ revision: nextRevision, tools: nextTools }) => {
65
- catalog = createCatalog({
66
- revision: nextRevision,
67
- tools: nextTools
68
- });
69
- await server.sendToolListChanged();
70
- }
71
- };
72
- }
73
- function createCatalog({
74
- revision,
75
- tools: tools2
76
- }) {
77
- return {
78
- revision,
79
- tools: [...tools2],
80
- byName: new Map(tools2.map((tool) => [tool.name, tool]))
81
- };
6
+ import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError } from "@modelcontextprotocol/sdk/types.js";
7
+ import { Agent, request } from "node:http";
8
+ //#region src/v1/bridge/host-tool-mcp-server.ts
9
+ const VERSION = "1.0.73";
10
+ function createHostToolMCPServer({ tools, revision = 1, invoke, onListTools }) {
11
+ let catalog = createCatalog({
12
+ revision,
13
+ tools
14
+ });
15
+ let catalogAcknowledgmentError;
16
+ const server = new Server({
17
+ name: "@ai-sdk/harness-acp-host-tools",
18
+ version: VERSION
19
+ }, { capabilities: { tools: { listChanged: true } } });
20
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
21
+ if (catalogAcknowledgmentError != null) throw catalogAcknowledgmentError;
22
+ const current = catalog;
23
+ if (onListTools != null) setImmediate(() => {
24
+ onListTools({ revision: current.revision }).catch((error) => {
25
+ catalogAcknowledgmentError = error;
26
+ });
27
+ });
28
+ return { tools: current.tools.map(toMCPTool) };
29
+ });
30
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
31
+ const current = catalog;
32
+ const tool = current.byName.get(request.params.name);
33
+ if (tool == null) throw new McpError(ErrorCode.InvalidParams, `Unknown host tool: ${request.params.name}`);
34
+ const input = request.params.arguments ?? {};
35
+ return toCallToolResult({ result: await invoke({
36
+ toolName: tool.name,
37
+ input,
38
+ catalogRevision: current.revision
39
+ }) });
40
+ });
41
+ return {
42
+ server,
43
+ updateCatalog: async ({ revision: nextRevision, tools: nextTools }) => {
44
+ catalog = createCatalog({
45
+ revision: nextRevision,
46
+ tools: nextTools
47
+ });
48
+ await server.sendToolListChanged();
49
+ }
50
+ };
51
+ }
52
+ function createCatalog({ revision, tools }) {
53
+ return {
54
+ revision,
55
+ tools: [...tools],
56
+ byName: new Map(tools.map((tool) => [tool.name, tool]))
57
+ };
82
58
  }
83
59
  function toMCPTool(tool) {
84
- return {
85
- name: tool.name,
86
- ...tool.description == null ? {} : { description: tool.description },
87
- inputSchema: requireObjectSchema({
88
- toolName: tool.name,
89
- schema: tool.inputSchema ?? { type: "object" }
90
- })
91
- };
92
- }
93
- function requireObjectSchema({
94
- toolName,
95
- schema
96
- }) {
97
- if (schema == null || typeof schema !== "object" || Array.isArray(schema) || Reflect.get(schema, "type") !== void 0 && Reflect.get(schema, "type") !== "object") {
98
- throw new Error(
99
- `Host tool ${toolName} must use an object JSON Schema for MCP.`
100
- );
101
- }
102
- return schema;
103
- }
104
- function toCallToolResult({
105
- result
106
- }) {
107
- return {
108
- content: [
109
- {
110
- type: "text",
111
- text: stringifyOutput({ output: result.output })
112
- }
113
- ],
114
- ...result.isError ? { isError: true } : {},
115
- _meta: {
116
- "ai-sdk-harness-acp-correlation": result.correlationToken
117
- }
118
- };
60
+ return {
61
+ name: tool.name,
62
+ ...tool.description == null ? {} : { description: tool.description },
63
+ inputSchema: requireObjectSchema({
64
+ toolName: tool.name,
65
+ schema: tool.inputSchema ?? { type: "object" }
66
+ })
67
+ };
68
+ }
69
+ function requireObjectSchema({ toolName, schema }) {
70
+ if (schema == null || typeof schema !== "object" || Array.isArray(schema) || Reflect.get(schema, "type") !== void 0 && Reflect.get(schema, "type") !== "object") throw new Error(`Host tool ${toolName} must use an object JSON Schema for MCP.`);
71
+ return schema;
72
+ }
73
+ function toCallToolResult({ result }) {
74
+ return {
75
+ content: [{
76
+ type: "text",
77
+ text: stringifyOutput({ output: result.output })
78
+ }],
79
+ ...result.isError ? { isError: true } : {},
80
+ _meta: { "ai-sdk-harness-acp-correlation": result.correlationToken }
81
+ };
119
82
  }
120
83
  function stringifyOutput({ output }) {
121
- if (output === void 0) return "null";
122
- try {
123
- return JSON.stringify(output);
124
- } catch {
125
- return JSON.stringify({
126
- error: "Host tool output could not be serialized as JSON."
127
- });
128
- }
129
- }
130
-
131
- // src/v1/bridge/host-tool-relay-client.ts
132
- import { Agent, request } from "http";
133
- var relayAgent = new Agent({ keepAlive: true, timeout: 0 });
134
- async function postHostToolRelay({
135
- relayUrl: relayUrl2,
136
- relayCredential: relayCredential2,
137
- path,
138
- body
139
- }) {
140
- const serializedBody = JSON.stringify(body);
141
- return new Promise((resolve, reject) => {
142
- const relayRequest = request(
143
- new URL(path, relayUrl2),
144
- {
145
- agent: relayAgent,
146
- method: "POST",
147
- headers: {
148
- authorization: `Bearer ${relayCredential2}`,
149
- "content-type": "application/json",
150
- "content-length": Buffer.byteLength(serializedBody)
151
- }
152
- },
153
- (response) => {
154
- const chunks = [];
155
- response.on("data", (chunk) => chunks.push(chunk));
156
- response.on("error", reject);
157
- response.on("end", () => {
158
- void new Response(Buffer.concat(chunks)).json().then((value) => {
159
- const status = response.statusCode ?? 0;
160
- resolve({
161
- status,
162
- ok: status >= 200 && status < 300,
163
- value
164
- });
165
- }, reject);
166
- });
167
- }
168
- );
169
- relayRequest.on("error", reject);
170
- relayRequest.end(serializedBody);
171
- });
172
- }
173
-
174
- // src/v1/bridge/host-tool-mcp.ts
175
- var catalogPath = requireEnvironmentVariable({
176
- name: "AI_SDK_ACP_HOST_TOOLS_FILE"
177
- });
178
- var relayUrl = requireEnvironmentVariable({
179
- name: "AI_SDK_ACP_HOST_TOOL_RELAY_URL"
84
+ if (output === void 0) return "null";
85
+ try {
86
+ return JSON.stringify(output);
87
+ } catch {
88
+ return JSON.stringify({ error: "Host tool output could not be serialized as JSON." });
89
+ }
90
+ }
91
+ //#endregion
92
+ //#region src/v1/bridge/host-tool-relay-client.ts
93
+ const relayAgent = new Agent({
94
+ keepAlive: true,
95
+ timeout: 0
180
96
  });
181
- var relayCredential = requireEnvironmentVariable({
182
- name: "AI_SDK_ACP_HOST_TOOL_RELAY_CREDENTIAL"
183
- });
184
- var tools = await readToolCatalog({ path: catalogPath });
185
- var hostToolServer = createHostToolMCPServer({
186
- tools,
187
- invoke: async ({ toolName, input, catalogRevision }) => validateInvocationResult({
188
- value: await postRelay({
189
- path: "/invoke",
190
- body: {
191
- requestId: randomUUID(),
192
- toolName,
193
- input,
194
- catalogRevision
195
- }
196
- })
197
- }),
198
- onListTools: async ({ revision }) => {
199
- await postRelay({
200
- path: "/catalog/seen",
201
- body: { revision }
202
- });
203
- }
97
+ async function postHostToolRelay({ relayUrl, relayCredential, path, body }) {
98
+ const serializedBody = JSON.stringify(body);
99
+ return new Promise((resolve, reject) => {
100
+ const relayRequest = request(new URL(path, relayUrl), {
101
+ agent: relayAgent,
102
+ method: "POST",
103
+ headers: {
104
+ authorization: `Bearer ${relayCredential}`,
105
+ "content-type": "application/json",
106
+ "content-length": Buffer.byteLength(serializedBody)
107
+ }
108
+ }, (response) => {
109
+ const chunks = [];
110
+ response.on("data", (chunk) => chunks.push(chunk));
111
+ response.on("error", reject);
112
+ response.on("end", () => {
113
+ new Response(Buffer.concat(chunks)).json().then((value) => {
114
+ const status = response.statusCode ?? 0;
115
+ resolve({
116
+ status,
117
+ ok: status >= 200 && status < 300,
118
+ value
119
+ });
120
+ }, reject);
121
+ });
122
+ });
123
+ relayRequest.on("error", reject);
124
+ relayRequest.end(serializedBody);
125
+ });
126
+ }
127
+ //#endregion
128
+ //#region src/v1/bridge/host-tool-mcp.ts
129
+ const catalogPath = requireEnvironmentVariable({ name: "AI_SDK_ACP_HOST_TOOLS_FILE" });
130
+ const relayUrl = requireEnvironmentVariable({ name: "AI_SDK_ACP_HOST_TOOL_RELAY_URL" });
131
+ const relayCredential = requireEnvironmentVariable({ name: "AI_SDK_ACP_HOST_TOOL_RELAY_CREDENTIAL" });
132
+ const hostToolServer = createHostToolMCPServer({
133
+ tools: await readToolCatalog({ path: catalogPath }),
134
+ invoke: async ({ toolName, input, catalogRevision }) => validateInvocationResult({ value: await postRelay({
135
+ path: "/invoke",
136
+ body: {
137
+ requestId: randomUUID(),
138
+ toolName,
139
+ input,
140
+ catalogRevision
141
+ }
142
+ }) }),
143
+ onListTools: async ({ revision }) => {
144
+ await postRelay({
145
+ path: "/catalog/seen",
146
+ body: { revision }
147
+ });
148
+ }
204
149
  });
205
150
  await hostToolServer.server.connect(new StdioServerTransport());
206
- void watchCatalog({
207
- initialRevision: 1,
208
- updateCatalog: hostToolServer.updateCatalog
151
+ watchCatalog({
152
+ initialRevision: 1,
153
+ updateCatalog: hostToolServer.updateCatalog
209
154
  }).catch((error) => {
210
- process.stderr.write(
211
- `Host tool catalog synchronization failed: ${error instanceof Error ? error.message : String(error)}
212
- `
213
- );
214
- void hostToolServer.server.close();
215
- process.exitCode = 1;
155
+ process.stderr.write(`Host tool catalog synchronization failed: ${error instanceof Error ? error.message : String(error)}\n`);
156
+ hostToolServer.server.close();
157
+ process.exitCode = 1;
216
158
  });
217
- async function watchCatalog({
218
- initialRevision,
219
- updateCatalog
220
- }) {
221
- let revision = initialRevision;
222
- for (; ; ) {
223
- const value = await postRelay({
224
- path: "/catalog/next",
225
- body: { afterRevision: revision }
226
- });
227
- if (isRecord(value) && value.closed === true) return;
228
- if (!isRecord(value) || !Number.isSafeInteger(value.revision) || value.revision < revision) {
229
- throw new Error("Invalid host tool catalog poll response.");
230
- }
231
- const nextRevision = value.revision;
232
- if (nextRevision === revision) continue;
233
- const nextTools = validateToolCatalog({ value: value.tools });
234
- await updateCatalog({ revision: nextRevision, tools: nextTools });
235
- revision = nextRevision;
236
- }
237
- }
238
- async function postRelay({
239
- path,
240
- body
241
- }) {
242
- const response = await postHostToolRelay({
243
- relayUrl,
244
- relayCredential,
245
- path,
246
- body
247
- });
248
- const { value } = response;
249
- if (!response.ok) {
250
- throw new Error(readErrorMessage({ value, status: response.status }));
251
- }
252
- return value;
253
- }
254
- function validateToolCatalog({
255
- value
256
- }) {
257
- if (!Array.isArray(value) || !value.every(isTool)) {
258
- throw new Error("Invalid host tool catalog.");
259
- }
260
- return value;
261
- }
262
- async function readToolCatalog({
263
- path
264
- }) {
265
- const text = await readFile(path, "utf8");
266
- const value = await new Response(text, {
267
- headers: { "content-type": "application/json" }
268
- }).json();
269
- return validateToolCatalog({ value });
159
+ async function watchCatalog({ initialRevision, updateCatalog }) {
160
+ let revision = initialRevision;
161
+ for (;;) {
162
+ const value = await postRelay({
163
+ path: "/catalog/next",
164
+ body: { afterRevision: revision }
165
+ });
166
+ if (isRecord(value) && value.closed === true) return;
167
+ if (!isRecord(value) || !Number.isSafeInteger(value.revision) || value.revision < revision) throw new Error("Invalid host tool catalog poll response.");
168
+ const nextRevision = value.revision;
169
+ if (nextRevision === revision) continue;
170
+ await updateCatalog({
171
+ revision: nextRevision,
172
+ tools: validateToolCatalog({ value: value.tools })
173
+ });
174
+ revision = nextRevision;
175
+ }
176
+ }
177
+ async function postRelay({ path, body }) {
178
+ const response = await postHostToolRelay({
179
+ relayUrl,
180
+ relayCredential,
181
+ path,
182
+ body
183
+ });
184
+ const { value } = response;
185
+ if (!response.ok) throw new Error(readErrorMessage({
186
+ value,
187
+ status: response.status
188
+ }));
189
+ return value;
190
+ }
191
+ function validateToolCatalog({ value }) {
192
+ if (!Array.isArray(value) || !value.every(isTool)) throw new Error("Invalid host tool catalog.");
193
+ return value;
194
+ }
195
+ async function readToolCatalog({ path }) {
196
+ const text = await readFile(path, "utf8");
197
+ return validateToolCatalog({ value: await new Response(text, { headers: { "content-type": "application/json" } }).json() });
270
198
  }
271
199
  function isTool(value) {
272
- return isRecord(value) && typeof value.name === "string" && (value.description === void 0 || typeof value.description === "string") && (value.inputSchema === void 0 || isRecord(value.inputSchema) && !Array.isArray(value.inputSchema));
200
+ return isRecord(value) && typeof value.name === "string" && (value.description === void 0 || typeof value.description === "string") && (value.inputSchema === void 0 || isRecord(value.inputSchema) && !Array.isArray(value.inputSchema));
273
201
  }
274
- function validateInvocationResult({
275
- value
276
- }) {
277
- if (!isRecord(value) || typeof value.correlationToken !== "string" || value.isError !== void 0 && typeof value.isError !== "boolean") {
278
- throw new Error("Invalid host tool relay response.");
279
- }
280
- return {
281
- output: value.output,
282
- ...value.isError ? { isError: true } : {},
283
- correlationToken: value.correlationToken
284
- };
202
+ function validateInvocationResult({ value }) {
203
+ if (!isRecord(value) || typeof value.correlationToken !== "string" || value.isError !== void 0 && typeof value.isError !== "boolean") throw new Error("Invalid host tool relay response.");
204
+ return {
205
+ output: value.output,
206
+ ...value.isError ? { isError: true } : {},
207
+ correlationToken: value.correlationToken
208
+ };
285
209
  }
286
- function readErrorMessage({
287
- value,
288
- status
289
- }) {
290
- return isRecord(value) && typeof value.error === "string" ? value.error : `Host tool relay returned HTTP ${status}.`;
210
+ function readErrorMessage({ value, status }) {
211
+ return isRecord(value) && typeof value.error === "string" ? value.error : `Host tool relay returned HTTP ${status}.`;
291
212
  }
292
213
  function requireEnvironmentVariable({ name }) {
293
- const value = env[name];
294
- if (value == null || value.length === 0) {
295
- throw new Error(`Missing ${name}.`);
296
- }
297
- return value;
214
+ const value = env[name];
215
+ if (value == null || value.length === 0) throw new Error(`Missing ${name}.`);
216
+ return value;
298
217
  }
299
218
  function isRecord(value) {
300
- return value != null && typeof value === "object" && !Array.isArray(value);
219
+ return value != null && typeof value === "object" && !Array.isArray(value);
301
220
  }
221
+ //#endregion
222
+ export {};
223
+
302
224
  //# sourceMappingURL=host-tool-mcp.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/v1/bridge/host-tool-mcp.ts","../../src/v1/bridge/host-tool-mcp-server.ts","../../src/v1/bridge/host-tool-relay-client.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { readFile } from 'node:fs/promises';\nimport { env } from 'node:process';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport type { HarnessV1BridgeToolWire } from '@ai-sdk/harness';\nimport {\n createHostToolMCPServer,\n type HostToolMCPInvocationResult,\n} from './host-tool-mcp-server';\nimport { postHostToolRelay } from './host-tool-relay-client';\n\nconst catalogPath = requireEnvironmentVariable({\n name: 'AI_SDK_ACP_HOST_TOOLS_FILE',\n});\nconst relayUrl = requireEnvironmentVariable({\n name: 'AI_SDK_ACP_HOST_TOOL_RELAY_URL',\n});\nconst relayCredential = requireEnvironmentVariable({\n name: 'AI_SDK_ACP_HOST_TOOL_RELAY_CREDENTIAL',\n});\nconst tools = await readToolCatalog({ path: catalogPath });\nconst hostToolServer = createHostToolMCPServer({\n tools,\n invoke: async ({ toolName, input, catalogRevision }) =>\n validateInvocationResult({\n value: await postRelay({\n path: '/invoke',\n body: {\n requestId: randomUUID(),\n toolName,\n input,\n catalogRevision,\n },\n }),\n }),\n onListTools: async ({ revision }) => {\n await postRelay({\n path: '/catalog/seen',\n body: { revision },\n });\n },\n});\n\nawait hostToolServer.server.connect(new StdioServerTransport());\nvoid watchCatalog({\n initialRevision: 1,\n updateCatalog: hostToolServer.updateCatalog,\n}).catch(error => {\n process.stderr.write(\n `Host tool catalog synchronization failed: ${\n error instanceof Error ? error.message : String(error)\n }\\n`,\n );\n void hostToolServer.server.close();\n process.exitCode = 1;\n});\n\nasync function watchCatalog({\n initialRevision,\n updateCatalog,\n}: {\n initialRevision: number;\n updateCatalog: (options: {\n revision: number;\n tools: ReadonlyArray<HarnessV1BridgeToolWire>;\n }) => Promise<void>;\n}): Promise<void> {\n let revision = initialRevision;\n for (;;) {\n const value = await postRelay({\n path: '/catalog/next',\n body: { afterRevision: revision },\n });\n if (isRecord(value) && value.closed === true) return;\n if (\n !isRecord(value) ||\n !Number.isSafeInteger(value.revision) ||\n (value.revision as number) < revision\n ) {\n throw new Error('Invalid host tool catalog poll response.');\n }\n const nextRevision = value.revision as number;\n if (nextRevision === revision) continue;\n const nextTools = validateToolCatalog({ value: value.tools });\n await updateCatalog({ revision: nextRevision, tools: nextTools });\n revision = nextRevision;\n }\n}\n\nasync function postRelay({\n path,\n body,\n}: {\n path: string;\n body: Readonly<Record<string, unknown>>;\n}): Promise<unknown> {\n const response = await postHostToolRelay({\n relayUrl,\n relayCredential,\n path,\n body,\n });\n const { value } = response;\n if (!response.ok) {\n throw new Error(readErrorMessage({ value, status: response.status }));\n }\n return value;\n}\n\nfunction validateToolCatalog({\n value,\n}: {\n value: unknown;\n}): ReadonlyArray<HarnessV1BridgeToolWire> {\n if (!Array.isArray(value) || !value.every(isTool)) {\n throw new Error('Invalid host tool catalog.');\n }\n return value;\n}\n\nasync function readToolCatalog({\n path,\n}: {\n path: string;\n}): Promise<ReadonlyArray<HarnessV1BridgeToolWire>> {\n const text = await readFile(path, 'utf8');\n const value = await new Response(text, {\n headers: { 'content-type': 'application/json' },\n }).json();\n return validateToolCatalog({ value });\n}\n\nfunction isTool(value: unknown): value is HarnessV1BridgeToolWire {\n return (\n isRecord(value) &&\n typeof value.name === 'string' &&\n (value.description === undefined ||\n typeof value.description === 'string') &&\n (value.inputSchema === undefined ||\n (isRecord(value.inputSchema) && !Array.isArray(value.inputSchema)))\n );\n}\n\nfunction validateInvocationResult({\n value,\n}: {\n value: unknown;\n}): HostToolMCPInvocationResult {\n if (\n !isRecord(value) ||\n typeof value.correlationToken !== 'string' ||\n (value.isError !== undefined && typeof value.isError !== 'boolean')\n ) {\n throw new Error('Invalid host tool relay response.');\n }\n return {\n output: value.output,\n ...(value.isError ? { isError: true } : {}),\n correlationToken: value.correlationToken,\n };\n}\n\nfunction readErrorMessage({\n value,\n status,\n}: {\n value: unknown;\n status: number;\n}): string {\n return isRecord(value) && typeof value.error === 'string'\n ? value.error\n : `Host tool relay returned HTTP ${status}.`;\n}\n\nfunction requireEnvironmentVariable({ name }: { name: string }): string {\n const value = env[name];\n if (value == null || value.length === 0) {\n throw new Error(`Missing ${name}.`);\n }\n return value;\n}\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n return value != null && typeof value === 'object' && !Array.isArray(value);\n}\n","import { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport {\n CallToolRequestSchema,\n ErrorCode,\n ListToolsRequestSchema,\n McpError,\n type CallToolResult,\n type Tool,\n} from '@modelcontextprotocol/sdk/types.js';\nimport type { HarnessV1BridgeToolWire } from '@ai-sdk/harness';\n\ndeclare const __PACKAGE_VERSION__: string | undefined;\n\nconst VERSION: string =\n typeof __PACKAGE_VERSION__ !== 'undefined'\n ? __PACKAGE_VERSION__\n : '0.0.0-test';\n\nexport type HostToolMCPInvocationResult = {\n readonly output: unknown;\n readonly isError?: boolean;\n readonly correlationToken: string;\n};\n\nexport type HostToolMCPServer = {\n readonly server: Server;\n updateCatalog(options: {\n revision: number;\n tools: ReadonlyArray<HarnessV1BridgeToolWire>;\n }): Promise<void>;\n};\n\nexport function createHostToolMCPServer({\n tools,\n revision = 1,\n invoke,\n onListTools,\n}: {\n tools: ReadonlyArray<HarnessV1BridgeToolWire>;\n revision?: number;\n invoke: (options: {\n toolName: string;\n input: Readonly<Record<string, unknown>>;\n catalogRevision: number;\n }) => Promise<HostToolMCPInvocationResult>;\n onListTools?: (options: { revision: number }) => Promise<void>;\n}): HostToolMCPServer {\n let catalog = createCatalog({ revision, tools });\n let catalogAcknowledgmentError: unknown;\n const server = new Server(\n {\n name: '@ai-sdk/harness-acp-host-tools',\n version: VERSION,\n },\n { capabilities: { tools: { listChanged: true } } },\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n if (catalogAcknowledgmentError != null) {\n throw catalogAcknowledgmentError;\n }\n const current = catalog;\n if (onListTools != null) {\n setImmediate(() => {\n void onListTools({ revision: current.revision }).catch(error => {\n catalogAcknowledgmentError = error;\n });\n });\n }\n return { tools: current.tools.map(toMCPTool) };\n });\n server.setRequestHandler(CallToolRequestSchema, async request => {\n const current = catalog;\n const tool = current.byName.get(request.params.name);\n if (tool == null) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `Unknown host tool: ${request.params.name}`,\n );\n }\n const input = request.params.arguments ?? {};\n const result = await invoke({\n toolName: tool.name,\n input,\n catalogRevision: current.revision,\n });\n return toCallToolResult({ result });\n });\n\n return {\n server,\n updateCatalog: async ({ revision: nextRevision, tools: nextTools }) => {\n catalog = createCatalog({\n revision: nextRevision,\n tools: nextTools,\n });\n await server.sendToolListChanged();\n },\n };\n}\n\nfunction createCatalog({\n revision,\n tools,\n}: {\n revision: number;\n tools: ReadonlyArray<HarnessV1BridgeToolWire>;\n}): {\n readonly revision: number;\n readonly tools: ReadonlyArray<HarnessV1BridgeToolWire>;\n readonly byName: ReadonlyMap<string, HarnessV1BridgeToolWire>;\n} {\n return {\n revision,\n tools: [...tools],\n byName: new Map(tools.map(tool => [tool.name, tool])),\n };\n}\n\nfunction toMCPTool(tool: HarnessV1BridgeToolWire): Tool {\n return {\n name: tool.name,\n ...(tool.description == null ? {} : { description: tool.description }),\n inputSchema: requireObjectSchema({\n toolName: tool.name,\n schema: tool.inputSchema ?? { type: 'object' },\n }),\n };\n}\n\nfunction requireObjectSchema({\n toolName,\n schema,\n}: {\n toolName: string;\n schema: unknown;\n}): Tool['inputSchema'] {\n if (\n schema == null ||\n typeof schema !== 'object' ||\n Array.isArray(schema) ||\n (Reflect.get(schema, 'type') !== undefined &&\n Reflect.get(schema, 'type') !== 'object')\n ) {\n throw new Error(\n `Host tool ${toolName} must use an object JSON Schema for MCP.`,\n );\n }\n return schema as Tool['inputSchema'];\n}\n\nfunction toCallToolResult({\n result,\n}: {\n result: HostToolMCPInvocationResult;\n}): CallToolResult {\n return {\n content: [\n {\n type: 'text',\n text: stringifyOutput({ output: result.output }),\n },\n ],\n ...(result.isError ? { isError: true } : {}),\n _meta: {\n 'ai-sdk-harness-acp-correlation': result.correlationToken,\n },\n };\n}\n\nfunction stringifyOutput({ output }: { output: unknown }): string {\n if (output === undefined) return 'null';\n try {\n return JSON.stringify(output);\n } catch {\n return JSON.stringify({\n error: 'Host tool output could not be serialized as JSON.',\n });\n }\n}\n","import { Agent, request } from 'node:http';\n\n/*\n * A host-tool call can remain pending while a person reviews an approval.\n * The loopback connection therefore must not impose an inactivity deadline.\n */\nconst relayAgent = new Agent({ keepAlive: true, timeout: 0 });\n\nexport async function postHostToolRelay({\n relayUrl,\n relayCredential,\n path,\n body,\n}: {\n relayUrl: string;\n relayCredential: string;\n path: string;\n body: Readonly<Record<string, unknown>>;\n}): Promise<{ status: number; ok: boolean; value: unknown }> {\n const serializedBody = JSON.stringify(body);\n\n return new Promise((resolve, reject) => {\n const relayRequest = request(\n new URL(path, relayUrl),\n {\n agent: relayAgent,\n method: 'POST',\n headers: {\n authorization: `Bearer ${relayCredential}`,\n 'content-type': 'application/json',\n 'content-length': Buffer.byteLength(serializedBody),\n },\n },\n response => {\n const chunks: Buffer[] = [];\n response.on('data', (chunk: Buffer) => chunks.push(chunk));\n response.on('error', reject);\n response.on('end', () => {\n void new Response(Buffer.concat(chunks)).json().then(value => {\n const status = response.statusCode ?? 0;\n resolve({\n status,\n ok: status >= 200 && status < 300,\n value,\n });\n }, reject);\n });\n },\n );\n relayRequest.on('error', reject);\n relayRequest.end(serializedBody);\n });\n}\n"],"mappings":"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","names":["tools","request","relayUrl","relayCredential"]}
1
+ {"version":3,"file":"host-tool-mcp.mjs","names":[],"sources":["../../src/v1/bridge/host-tool-mcp-server.ts","../../src/v1/bridge/host-tool-relay-client.ts","../../src/v1/bridge/host-tool-mcp.ts"],"sourcesContent":["import { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport {\n CallToolRequestSchema,\n ErrorCode,\n ListToolsRequestSchema,\n McpError,\n type CallToolResult,\n type Tool,\n} from '@modelcontextprotocol/sdk/types.js';\nimport type { HarnessV1BridgeToolWire } from '@ai-sdk/harness';\n\ndeclare const __PACKAGE_VERSION__: string | undefined;\n\nconst VERSION: string =\n typeof __PACKAGE_VERSION__ !== 'undefined'\n ? __PACKAGE_VERSION__\n : '0.0.0-test';\n\nexport type HostToolMCPInvocationResult = {\n readonly output: unknown;\n readonly isError?: boolean;\n readonly correlationToken: string;\n};\n\nexport type HostToolMCPServer = {\n readonly server: Server;\n updateCatalog(options: {\n revision: number;\n tools: ReadonlyArray<HarnessV1BridgeToolWire>;\n }): Promise<void>;\n};\n\nexport function createHostToolMCPServer({\n tools,\n revision = 1,\n invoke,\n onListTools,\n}: {\n tools: ReadonlyArray<HarnessV1BridgeToolWire>;\n revision?: number;\n invoke: (options: {\n toolName: string;\n input: Readonly<Record<string, unknown>>;\n catalogRevision: number;\n }) => Promise<HostToolMCPInvocationResult>;\n onListTools?: (options: { revision: number }) => Promise<void>;\n}): HostToolMCPServer {\n let catalog = createCatalog({ revision, tools });\n let catalogAcknowledgmentError: unknown;\n const server = new Server(\n {\n name: '@ai-sdk/harness-acp-host-tools',\n version: VERSION,\n },\n { capabilities: { tools: { listChanged: true } } },\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n if (catalogAcknowledgmentError != null) {\n throw catalogAcknowledgmentError;\n }\n const current = catalog;\n if (onListTools != null) {\n setImmediate(() => {\n void onListTools({ revision: current.revision }).catch(error => {\n catalogAcknowledgmentError = error;\n });\n });\n }\n return { tools: current.tools.map(toMCPTool) };\n });\n server.setRequestHandler(CallToolRequestSchema, async request => {\n const current = catalog;\n const tool = current.byName.get(request.params.name);\n if (tool == null) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `Unknown host tool: ${request.params.name}`,\n );\n }\n const input = request.params.arguments ?? {};\n const result = await invoke({\n toolName: tool.name,\n input,\n catalogRevision: current.revision,\n });\n return toCallToolResult({ result });\n });\n\n return {\n server,\n updateCatalog: async ({ revision: nextRevision, tools: nextTools }) => {\n catalog = createCatalog({\n revision: nextRevision,\n tools: nextTools,\n });\n await server.sendToolListChanged();\n },\n };\n}\n\nfunction createCatalog({\n revision,\n tools,\n}: {\n revision: number;\n tools: ReadonlyArray<HarnessV1BridgeToolWire>;\n}): {\n readonly revision: number;\n readonly tools: ReadonlyArray<HarnessV1BridgeToolWire>;\n readonly byName: ReadonlyMap<string, HarnessV1BridgeToolWire>;\n} {\n return {\n revision,\n tools: [...tools],\n byName: new Map(tools.map(tool => [tool.name, tool])),\n };\n}\n\nfunction toMCPTool(tool: HarnessV1BridgeToolWire): Tool {\n return {\n name: tool.name,\n ...(tool.description == null ? {} : { description: tool.description }),\n inputSchema: requireObjectSchema({\n toolName: tool.name,\n schema: tool.inputSchema ?? { type: 'object' },\n }),\n };\n}\n\nfunction requireObjectSchema({\n toolName,\n schema,\n}: {\n toolName: string;\n schema: unknown;\n}): Tool['inputSchema'] {\n if (\n schema == null ||\n typeof schema !== 'object' ||\n Array.isArray(schema) ||\n (Reflect.get(schema, 'type') !== undefined &&\n Reflect.get(schema, 'type') !== 'object')\n ) {\n throw new Error(\n `Host tool ${toolName} must use an object JSON Schema for MCP.`,\n );\n }\n return schema as Tool['inputSchema'];\n}\n\nfunction toCallToolResult({\n result,\n}: {\n result: HostToolMCPInvocationResult;\n}): CallToolResult {\n return {\n content: [\n {\n type: 'text',\n text: stringifyOutput({ output: result.output }),\n },\n ],\n ...(result.isError ? { isError: true } : {}),\n _meta: {\n 'ai-sdk-harness-acp-correlation': result.correlationToken,\n },\n };\n}\n\nfunction stringifyOutput({ output }: { output: unknown }): string {\n if (output === undefined) return 'null';\n try {\n return JSON.stringify(output);\n } catch {\n return JSON.stringify({\n error: 'Host tool output could not be serialized as JSON.',\n });\n }\n}\n","import { Agent, request } from 'node:http';\n\n/*\n * A host-tool call can remain pending while a person reviews an approval.\n * The loopback connection therefore must not impose an inactivity deadline.\n */\nconst relayAgent = new Agent({ keepAlive: true, timeout: 0 });\n\nexport async function postHostToolRelay({\n relayUrl,\n relayCredential,\n path,\n body,\n}: {\n relayUrl: string;\n relayCredential: string;\n path: string;\n body: Readonly<Record<string, unknown>>;\n}): Promise<{ status: number; ok: boolean; value: unknown }> {\n const serializedBody = JSON.stringify(body);\n\n return new Promise((resolve, reject) => {\n const relayRequest = request(\n new URL(path, relayUrl),\n {\n agent: relayAgent,\n method: 'POST',\n headers: {\n authorization: `Bearer ${relayCredential}`,\n 'content-type': 'application/json',\n 'content-length': Buffer.byteLength(serializedBody),\n },\n },\n response => {\n const chunks: Buffer[] = [];\n response.on('data', (chunk: Buffer) => chunks.push(chunk));\n response.on('error', reject);\n response.on('end', () => {\n void new Response(Buffer.concat(chunks)).json().then(value => {\n const status = response.statusCode ?? 0;\n resolve({\n status,\n ok: status >= 200 && status < 300,\n value,\n });\n }, reject);\n });\n },\n );\n relayRequest.on('error', reject);\n relayRequest.end(serializedBody);\n });\n}\n","import { randomUUID } from 'node:crypto';\nimport { readFile } from 'node:fs/promises';\nimport { env } from 'node:process';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport type { HarnessV1BridgeToolWire } from '@ai-sdk/harness';\nimport {\n createHostToolMCPServer,\n type HostToolMCPInvocationResult,\n} from './host-tool-mcp-server';\nimport { postHostToolRelay } from './host-tool-relay-client';\n\nconst catalogPath = requireEnvironmentVariable({\n name: 'AI_SDK_ACP_HOST_TOOLS_FILE',\n});\nconst relayUrl = requireEnvironmentVariable({\n name: 'AI_SDK_ACP_HOST_TOOL_RELAY_URL',\n});\nconst relayCredential = requireEnvironmentVariable({\n name: 'AI_SDK_ACP_HOST_TOOL_RELAY_CREDENTIAL',\n});\nconst tools = await readToolCatalog({ path: catalogPath });\nconst hostToolServer = createHostToolMCPServer({\n tools,\n invoke: async ({ toolName, input, catalogRevision }) =>\n validateInvocationResult({\n value: await postRelay({\n path: '/invoke',\n body: {\n requestId: randomUUID(),\n toolName,\n input,\n catalogRevision,\n },\n }),\n }),\n onListTools: async ({ revision }) => {\n await postRelay({\n path: '/catalog/seen',\n body: { revision },\n });\n },\n});\n\nawait hostToolServer.server.connect(new StdioServerTransport());\nvoid watchCatalog({\n initialRevision: 1,\n updateCatalog: hostToolServer.updateCatalog,\n}).catch(error => {\n process.stderr.write(\n `Host tool catalog synchronization failed: ${\n error instanceof Error ? error.message : String(error)\n }\\n`,\n );\n void hostToolServer.server.close();\n process.exitCode = 1;\n});\n\nasync function watchCatalog({\n initialRevision,\n updateCatalog,\n}: {\n initialRevision: number;\n updateCatalog: (options: {\n revision: number;\n tools: ReadonlyArray<HarnessV1BridgeToolWire>;\n }) => Promise<void>;\n}): Promise<void> {\n let revision = initialRevision;\n for (;;) {\n const value = await postRelay({\n path: '/catalog/next',\n body: { afterRevision: revision },\n });\n if (isRecord(value) && value.closed === true) return;\n if (\n !isRecord(value) ||\n !Number.isSafeInteger(value.revision) ||\n (value.revision as number) < revision\n ) {\n throw new Error('Invalid host tool catalog poll response.');\n }\n const nextRevision = value.revision as number;\n if (nextRevision === revision) continue;\n const nextTools = validateToolCatalog({ value: value.tools });\n await updateCatalog({ revision: nextRevision, tools: nextTools });\n revision = nextRevision;\n }\n}\n\nasync function postRelay({\n path,\n body,\n}: {\n path: string;\n body: Readonly<Record<string, unknown>>;\n}): Promise<unknown> {\n const response = await postHostToolRelay({\n relayUrl,\n relayCredential,\n path,\n body,\n });\n const { value } = response;\n if (!response.ok) {\n throw new Error(readErrorMessage({ value, status: response.status }));\n }\n return value;\n}\n\nfunction validateToolCatalog({\n value,\n}: {\n value: unknown;\n}): ReadonlyArray<HarnessV1BridgeToolWire> {\n if (!Array.isArray(value) || !value.every(isTool)) {\n throw new Error('Invalid host tool catalog.');\n }\n return value;\n}\n\nasync function readToolCatalog({\n path,\n}: {\n path: string;\n}): Promise<ReadonlyArray<HarnessV1BridgeToolWire>> {\n const text = await readFile(path, 'utf8');\n const value = await new Response(text, {\n headers: { 'content-type': 'application/json' },\n }).json();\n return validateToolCatalog({ value });\n}\n\nfunction isTool(value: unknown): value is HarnessV1BridgeToolWire {\n return (\n isRecord(value) &&\n typeof value.name === 'string' &&\n (value.description === undefined ||\n typeof value.description === 'string') &&\n (value.inputSchema === undefined ||\n (isRecord(value.inputSchema) && !Array.isArray(value.inputSchema)))\n );\n}\n\nfunction validateInvocationResult({\n value,\n}: {\n value: unknown;\n}): HostToolMCPInvocationResult {\n if (\n !isRecord(value) ||\n typeof value.correlationToken !== 'string' ||\n (value.isError !== undefined && typeof value.isError !== 'boolean')\n ) {\n throw new Error('Invalid host tool relay response.');\n }\n return {\n output: value.output,\n ...(value.isError ? { isError: true } : {}),\n correlationToken: value.correlationToken,\n };\n}\n\nfunction readErrorMessage({\n value,\n status,\n}: {\n value: unknown;\n status: number;\n}): string {\n return isRecord(value) && typeof value.error === 'string'\n ? value.error\n : `Host tool relay returned HTTP ${status}.`;\n}\n\nfunction requireEnvironmentVariable({ name }: { name: string }): string {\n const value = env[name];\n if (value == null || value.length === 0) {\n throw new Error(`Missing ${name}.`);\n }\n return value;\n}\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n return value != null && typeof value === 'object' && !Array.isArray(value);\n}\n"],"mappings":"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"}