stitchkit 0.40.0 → 0.42.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/dist/browser/client.d.ts.map +1 -1
- package/dist/browser/http.d.ts +1 -1
- package/dist/browser/http.d.ts.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/contract/errors-factory.d.ts +88 -15
- package/dist/contract/errors-factory.d.ts.map +1 -1
- package/dist/contract/errors.d.ts +4 -4
- package/dist/contract/errors.d.ts.map +1 -1
- package/dist/contract/index.d.ts +1 -1
- package/dist/contract/index.d.ts.map +1 -1
- package/dist/contract/index.js +2 -2
- package/dist/{index-xax049k6.js → index-11x5dts2.js} +44 -13
- package/dist/{index-7rbzbnnf.js → index-181aebw8.js} +10 -15
- package/dist/{index-92gs1m5b.js → index-310bfer5.js} +1 -1
- package/dist/{index-fyfk537k.js → index-gex6gxhe.js} +25 -5
- package/dist/{index-p1x65gk7.js → index-p9vkwns1.js} +11 -13
- package/dist/{index-809wc1tt.js → index-s4qsmgwe.js} +4 -1
- package/dist/index.js +37 -13
- package/dist/internal/errors.d.ts +13 -0
- package/dist/internal/errors.d.ts.map +1 -1
- package/dist/internal/typed.d.ts +8 -0
- package/dist/internal/typed.d.ts.map +1 -1
- package/dist/node.js +2 -2
- package/dist/observability/index.js +1 -1
- package/dist/react.js +1 -1
- package/dist/server/create.d.ts.map +1 -1
- package/dist/server/error-hook.d.ts +8 -4
- package/dist/server/error-hook.d.ts.map +1 -1
- package/dist/server/index.js +7 -6
- package/dist/tools/agent.d.ts +1 -1
- package/dist/tools/agent.d.ts.map +1 -1
- package/dist/tools/list-names.d.ts +8 -6
- package/dist/tools/list-names.d.ts.map +1 -1
- package/dist/tools/manifest.d.ts +13 -5
- package/dist/tools/manifest.d.ts.map +1 -1
- package/dist/tools/mcp-handler.d.ts +5 -4
- package/dist/tools/mcp-handler.d.ts.map +1 -1
- package/dist/tools/mcp-stdio.d.ts +3 -2
- package/dist/tools/mcp-stdio.d.ts.map +1 -1
- package/dist/tools/mcp.d.ts +49 -9
- package/dist/tools/mcp.d.ts.map +1 -1
- package/dist/tools/native-mcp.d.ts +4 -18
- package/dist/tools/native-mcp.d.ts.map +1 -1
- package/dist/tools/runtime-tool.d.ts +3 -3
- package/dist/tools/runtime-tool.d.ts.map +1 -1
- package/dist/tools/surface.d.ts +37 -0
- package/dist/tools/surface.d.ts.map +1 -0
- package/dist/tools/toolkit.d.ts +4 -4
- package/dist/tools/toolkit.d.ts.map +1 -1
- package/dist/tools/transports.d.ts +11 -9
- package/dist/tools/transports.d.ts.map +1 -1
- package/dist/tools/view-file.d.ts +3 -3
- package/dist/tools.d.ts +5 -5
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +320 -165
- package/llms-full.txt +310 -53
- package/package.json +1 -1
package/dist/tools.js
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
inputIsQuery,
|
|
3
3
|
signJwt,
|
|
4
4
|
verifyPkce
|
|
5
|
-
} from "./index-
|
|
5
|
+
} from "./index-310bfer5.js";
|
|
6
6
|
import {
|
|
7
7
|
DEFAULT_CORS_ALLOW_HEADERS
|
|
8
8
|
} from "./index-6jypn22c.js";
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
toolErrorFromResult,
|
|
26
26
|
toolResultFromError,
|
|
27
27
|
writeDownload
|
|
28
|
-
} from "./index-
|
|
28
|
+
} from "./index-p9vkwns1.js";
|
|
29
29
|
import {
|
|
30
30
|
toJsonSchema
|
|
31
31
|
} from "./index-frfyw9fa.js";
|
|
@@ -39,7 +39,7 @@ import {
|
|
|
39
39
|
mergeMeta,
|
|
40
40
|
parseTrailingWildcard,
|
|
41
41
|
typedEntries
|
|
42
|
-
} from "./index-
|
|
42
|
+
} from "./index-gex6gxhe.js";
|
|
43
43
|
|
|
44
44
|
// src/tools/agent.ts
|
|
45
45
|
import { jsonSchema, tool } from "ai";
|
|
@@ -70,8 +70,10 @@ function runtimeToolIdentity(definition) {
|
|
|
70
70
|
ui: definition.ui
|
|
71
71
|
};
|
|
72
72
|
}
|
|
73
|
-
function runtimeToolMountable(definition) {
|
|
74
|
-
|
|
73
|
+
function runtimeToolMountable(definition, assertName = true) {
|
|
74
|
+
if (assertName) {
|
|
75
|
+
assertToolName(definition.name, definition.identity.serviceName, definition.identity.action);
|
|
76
|
+
}
|
|
75
77
|
const method = {
|
|
76
78
|
...runtimeToolIdentity(definition),
|
|
77
79
|
inputSchema: definition.input,
|
|
@@ -90,6 +92,55 @@ function runtimeToolMountable(definition) {
|
|
|
90
92
|
};
|
|
91
93
|
}
|
|
92
94
|
|
|
95
|
+
// src/tools/surface.ts
|
|
96
|
+
function duplicateLabel(transport) {
|
|
97
|
+
if (transport === "MCP")
|
|
98
|
+
return "MCP tool name";
|
|
99
|
+
if (transport === "AGENT")
|
|
100
|
+
return "agent tool name";
|
|
101
|
+
return "CLI command";
|
|
102
|
+
}
|
|
103
|
+
function collectToolSurface({
|
|
104
|
+
surface,
|
|
105
|
+
transport,
|
|
106
|
+
assertUniqueNames = true,
|
|
107
|
+
...collectConfig
|
|
108
|
+
}) {
|
|
109
|
+
const entries = [];
|
|
110
|
+
const names = new Set;
|
|
111
|
+
const append = (entry) => {
|
|
112
|
+
if (assertUniqueNames) {
|
|
113
|
+
assertUniqueToolName(entry.mountable.name, names.has(entry.mountable.name), duplicateLabel(transport));
|
|
114
|
+
}
|
|
115
|
+
names.add(entry.mountable.name);
|
|
116
|
+
entries.push(entry);
|
|
117
|
+
};
|
|
118
|
+
for (const service of surface.services ?? []) {
|
|
119
|
+
for (const mountable of collectTools(service, transport, collectConfig)) {
|
|
120
|
+
append({
|
|
121
|
+
kind: "contract",
|
|
122
|
+
service: service.name,
|
|
123
|
+
action: mountable.method.key,
|
|
124
|
+
mountable
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (transport === "CLI")
|
|
129
|
+
return entries;
|
|
130
|
+
for (const definition of surface.runtimeTools ?? []) {
|
|
131
|
+
if (!runtimeToolSupports(definition, transport))
|
|
132
|
+
continue;
|
|
133
|
+
append({
|
|
134
|
+
kind: "runtime",
|
|
135
|
+
service: definition.identity.serviceName,
|
|
136
|
+
action: definition.identity.action,
|
|
137
|
+
mountable: runtimeToolMountable(definition, collectConfig.assertNames),
|
|
138
|
+
definition
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
return entries;
|
|
142
|
+
}
|
|
143
|
+
|
|
93
144
|
// src/tools/agent.ts
|
|
94
145
|
function mountAgent(services, config = {}) {
|
|
95
146
|
const serviceList = Array.isArray(services) ? services : [services];
|
|
@@ -104,36 +155,13 @@ function mountAgent(services, config = {}) {
|
|
|
104
155
|
coerceJsonArgs: config.coerceJsonArgs,
|
|
105
156
|
onOutputStrip: config.onOutputStrip
|
|
106
157
|
});
|
|
107
|
-
for (const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
description: mountable.method.desc,
|
|
115
|
-
inputSchema: jsonSchema(mountable.presentationSchema, {
|
|
116
|
-
validate: async (value) => isRecord(value) ? { success: true, value } : { success: false, error: new Error("Tool arguments must be an object") }
|
|
117
|
-
}),
|
|
118
|
-
execute: async (rawArgs) => {
|
|
119
|
-
const args = isRecord(rawArgs) ? rawArgs : {};
|
|
120
|
-
try {
|
|
121
|
-
const result = await runTool(mountable, args);
|
|
122
|
-
if (result.ok)
|
|
123
|
-
return result.data;
|
|
124
|
-
return formatToolError(result, mountable.name, config.errorHint);
|
|
125
|
-
} catch (err) {
|
|
126
|
-
return formatToolError(toolResultFromError(err), mountable.name, config.errorHint);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
for (const definition of config.runtimeTools ?? []) {
|
|
133
|
-
if (!runtimeToolSupports(definition, "AGENT"))
|
|
134
|
-
continue;
|
|
135
|
-
const mountable = runtimeToolMountable(definition);
|
|
136
|
-
assertUniqueToolName(definition.name, Object.hasOwn(tools, definition.name), "agent tool name");
|
|
158
|
+
for (const entry of collectToolSurface({
|
|
159
|
+
surface: { services: serviceList, runtimeTools: config.runtimeTools },
|
|
160
|
+
transport: "AGENT",
|
|
161
|
+
extend: config.extend,
|
|
162
|
+
flattenUnionInput: config.flattenUnionInput
|
|
163
|
+
})) {
|
|
164
|
+
const { mountable } = entry;
|
|
137
165
|
const inputSchema = jsonSchema(mountable.presentationSchema, {
|
|
138
166
|
validate: async (value) => isRecord(value) ? { success: true, value } : { success: false, error: new Error("Tool arguments must be an object") }
|
|
139
167
|
});
|
|
@@ -148,9 +176,13 @@ function mountAgent(services, config = {}) {
|
|
|
148
176
|
return formatToolError(toolResultFromError(err), mountable.name, config.errorHint);
|
|
149
177
|
}
|
|
150
178
|
};
|
|
151
|
-
|
|
152
|
-
|
|
179
|
+
const presenter = entry.kind === "runtime" ? entry.definition.present?.agent : undefined;
|
|
180
|
+
if (entry.kind === "runtime" && presenter) {
|
|
181
|
+
const { definition } = entry;
|
|
153
182
|
const output = definition.output;
|
|
183
|
+
if (!output) {
|
|
184
|
+
throw new Error(`Runtime tool "${definition.name}" presenter requires an output schema`);
|
|
185
|
+
}
|
|
154
186
|
tools[definition.name] = tool({
|
|
155
187
|
description: definition.description,
|
|
156
188
|
inputSchema,
|
|
@@ -165,10 +197,11 @@ function mountAgent(services, config = {}) {
|
|
|
165
197
|
}
|
|
166
198
|
});
|
|
167
199
|
} else {
|
|
168
|
-
|
|
169
|
-
|
|
200
|
+
const output = entry.kind === "runtime" ? entry.definition.output : undefined;
|
|
201
|
+
tools[mountable.name] = tool({
|
|
202
|
+
description: mountable.method.desc,
|
|
170
203
|
inputSchema,
|
|
171
|
-
...
|
|
204
|
+
...output && { outputSchema: output },
|
|
172
205
|
execute
|
|
173
206
|
});
|
|
174
207
|
}
|
|
@@ -224,34 +257,43 @@ function createToolInvoker(services, config) {
|
|
|
224
257
|
}
|
|
225
258
|
// src/tools/list-names.ts
|
|
226
259
|
var TOOL_TRANSPORTS = ["MCP", "AGENT", "CLI"];
|
|
227
|
-
function listToolNames(
|
|
260
|
+
function listToolNames(surface) {
|
|
228
261
|
const entries = new Map;
|
|
229
|
-
for (const
|
|
230
|
-
for (const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
262
|
+
for (const transport of TOOL_TRANSPORTS) {
|
|
263
|
+
for (const entry of collectToolSurface({
|
|
264
|
+
surface,
|
|
265
|
+
transport,
|
|
266
|
+
assertNames: false,
|
|
267
|
+
assertUniqueNames: false
|
|
268
|
+
})) {
|
|
269
|
+
const id = `${entry.kind}\x00${entry.service}\x00${entry.action}`;
|
|
270
|
+
const existing = entries.get(id);
|
|
271
|
+
if (existing) {
|
|
272
|
+
existing.transports.push(transport);
|
|
273
|
+
} else {
|
|
274
|
+
entries.set(id, {
|
|
275
|
+
kind: entry.kind,
|
|
276
|
+
name: entry.mountable.name,
|
|
277
|
+
service: entry.service,
|
|
278
|
+
method: entry.action,
|
|
279
|
+
transports: [transport]
|
|
280
|
+
});
|
|
244
281
|
}
|
|
245
282
|
}
|
|
246
283
|
}
|
|
247
284
|
return [...entries.values()].sort((a, b) => a.name.localeCompare(b.name) || a.service.localeCompare(b.service));
|
|
248
285
|
}
|
|
249
286
|
// src/tools/manifest.ts
|
|
250
|
-
function buildToolManifest(
|
|
251
|
-
return
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
287
|
+
function buildToolManifest(config) {
|
|
288
|
+
return collectToolSurface({
|
|
289
|
+
surface: config,
|
|
290
|
+
transport: config.transport,
|
|
291
|
+
extend: config.extend,
|
|
292
|
+
flattenUnionInput: config.flattenUnionInput
|
|
293
|
+
}).map(({ mountable }) => ({
|
|
294
|
+
name: mountable.name,
|
|
295
|
+
description: mountable.method.desc,
|
|
296
|
+
inputSchema: mountable.presentationSchema
|
|
255
297
|
}));
|
|
256
298
|
}
|
|
257
299
|
// src/tools/mcp.ts
|
|
@@ -285,63 +327,53 @@ import { z } from "zod";
|
|
|
285
327
|
function isCallToolResult(value) {
|
|
286
328
|
return CallToolResultSchema.safeParse(value).success;
|
|
287
329
|
}
|
|
288
|
-
function
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
330
|
+
function mountPreparedRuntimeMcp(server, tools, config) {
|
|
331
|
+
for (const { definition, descriptor } of tools) {
|
|
332
|
+
const { mountable } = descriptor;
|
|
333
|
+
const toolConfig = {
|
|
334
|
+
description: definition.description,
|
|
335
|
+
inputSchema: z.looseObject({}).meta(presentationMetadata(descriptor.inputSchema))
|
|
336
|
+
};
|
|
337
|
+
if (descriptor.outputSchema)
|
|
338
|
+
toolConfig.outputSchema = descriptor.outputSchema;
|
|
339
|
+
if (definition.annotations)
|
|
340
|
+
toolConfig.annotations = definition.annotations;
|
|
341
|
+
if (definition.ui) {
|
|
342
|
+
toolConfig._meta = {
|
|
343
|
+
ui: definition.ui,
|
|
344
|
+
"ui/resourceUri": definition.ui.resourceUri
|
|
303
345
|
};
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
if (definition.
|
|
309
|
-
|
|
310
|
-
ui: definition.ui,
|
|
311
|
-
"ui/resourceUri": definition.ui.resourceUri
|
|
312
|
-
};
|
|
346
|
+
}
|
|
347
|
+
server.registerTool(definition.name, toolConfig, async (rawArgs) => {
|
|
348
|
+
const args = isRecord(rawArgs) ? rawArgs : {};
|
|
349
|
+
const result = await executeToolMethod(mountable.method, definition.name, args, { ...config.context, source: "mcp" }, config.hooks, config.lifecycle, config.coerceJsonArgs ?? true, config.onOutputStrip ? (paths) => config.onOutputStrip?.(definition.name, paths) : undefined);
|
|
350
|
+
if (!result.ok || !definition.present?.mcp) {
|
|
351
|
+
return config.formatResult(result, descriptor.outputMode, definition.name);
|
|
313
352
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
const result = await executeToolMethod(mountable.method, definition.name, args, { ...config.context, source: "mcp" }, config.hooks, config.lifecycle, config.coerceJsonArgs ?? true, config.onOutputStrip ? (paths) => config.onOutputStrip?.(definition.name, paths) : undefined);
|
|
317
|
-
if (!result.ok || !definition.present?.mcp) {
|
|
318
|
-
return config.formatResult(result, prepared.outputMode, definition.name);
|
|
319
|
-
}
|
|
320
|
-
const presented = await definition.present.mcp(result.data);
|
|
321
|
-
if ("structuredContent" in presented || "isError" in presented) {
|
|
322
|
-
return config.formatResult({
|
|
323
|
-
ok: false,
|
|
324
|
-
code: "INTERNAL_SERVER_ERROR",
|
|
325
|
-
details: {
|
|
326
|
-
message: "Runtime MCP presenter cannot set framework-owned structuredContent or isError"
|
|
327
|
-
}
|
|
328
|
-
}, "none", definition.name);
|
|
329
|
-
}
|
|
330
|
-
const structured = config.formatResult(result, prepared.outputMode, definition.name).structuredContent;
|
|
331
|
-
const response = {
|
|
332
|
-
...presented,
|
|
333
|
-
...structured && { structuredContent: structured }
|
|
334
|
-
};
|
|
335
|
-
if (isCallToolResult(response))
|
|
336
|
-
return response;
|
|
353
|
+
const presented = await definition.present.mcp(result.data);
|
|
354
|
+
if ("structuredContent" in presented || "isError" in presented) {
|
|
337
355
|
return config.formatResult({
|
|
338
356
|
ok: false,
|
|
339
357
|
code: "INTERNAL_SERVER_ERROR",
|
|
340
|
-
details: {
|
|
358
|
+
details: {
|
|
359
|
+
message: "Runtime MCP presenter cannot set framework-owned structuredContent or isError"
|
|
360
|
+
}
|
|
341
361
|
}, "none", definition.name);
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
|
|
362
|
+
}
|
|
363
|
+
const structured = config.formatResult(result, descriptor.outputMode, definition.name).structuredContent;
|
|
364
|
+
const response = {
|
|
365
|
+
...presented,
|
|
366
|
+
...structured && { structuredContent: structured }
|
|
367
|
+
};
|
|
368
|
+
if (isCallToolResult(response))
|
|
369
|
+
return response;
|
|
370
|
+
return config.formatResult({
|
|
371
|
+
ok: false,
|
|
372
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
373
|
+
details: { message: "Runtime MCP presenter returned an invalid CallToolResult" }
|
|
374
|
+
}, "none", definition.name);
|
|
375
|
+
});
|
|
376
|
+
}
|
|
345
377
|
}
|
|
346
378
|
|
|
347
379
|
// src/tools/portable-formats.ts
|
|
@@ -629,6 +661,37 @@ function prepareMcpTools(tools, config = {}) {
|
|
|
629
661
|
throwIfFailures(failures);
|
|
630
662
|
return Object.freeze(prepared);
|
|
631
663
|
}
|
|
664
|
+
function prepareMcpServerSurface(surface, config = {}) {
|
|
665
|
+
const contractMountables = [];
|
|
666
|
+
const definitions = [];
|
|
667
|
+
const runtimeMountables = [];
|
|
668
|
+
for (const entry of collectToolSurface({
|
|
669
|
+
surface,
|
|
670
|
+
transport: "MCP",
|
|
671
|
+
extend: config.extend,
|
|
672
|
+
flattenUnionInput: config.flattenUnionInput
|
|
673
|
+
})) {
|
|
674
|
+
if (entry.kind === "contract") {
|
|
675
|
+
contractMountables.push(entry.mountable);
|
|
676
|
+
} else {
|
|
677
|
+
definitions.push(entry.definition);
|
|
678
|
+
runtimeMountables.push(entry.mountable);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
const contractTools = prepareMcpTools(contractMountables, config);
|
|
682
|
+
const runtimeDescriptors = prepareMcpTools(runtimeMountables, {
|
|
683
|
+
schemaValidation: config.schemaValidation,
|
|
684
|
+
logger: config.logger
|
|
685
|
+
});
|
|
686
|
+
const descriptorsByName = new Map(runtimeDescriptors.map((descriptor) => [descriptor.mountable.name, descriptor]));
|
|
687
|
+
const runtimeTools = [];
|
|
688
|
+
for (const definition of definitions) {
|
|
689
|
+
const descriptor = descriptorsByName.get(definition.name);
|
|
690
|
+
if (descriptor)
|
|
691
|
+
runtimeTools.push(Object.freeze({ definition, descriptor }));
|
|
692
|
+
}
|
|
693
|
+
return Object.freeze({ contractTools, runtimeTools: Object.freeze(runtimeTools) });
|
|
694
|
+
}
|
|
632
695
|
function validateMcpSchemas(config) {
|
|
633
696
|
prepareMcpSurface(config.services, {
|
|
634
697
|
extend: config.extend,
|
|
@@ -686,8 +749,22 @@ function mountPreparedMcp(mcpServer, prepared, config = {}) {
|
|
|
686
749
|
}
|
|
687
750
|
}
|
|
688
751
|
function buildMcpServer(config, auth) {
|
|
689
|
-
|
|
690
|
-
|
|
752
|
+
let surface;
|
|
753
|
+
if (config.surfaces) {
|
|
754
|
+
const key = config.selectSurface(auth);
|
|
755
|
+
if (!Object.hasOwn(config.surfaces, key)) {
|
|
756
|
+
throw new Error(`[stitchkit] Unknown MCP surface "${key}"`);
|
|
757
|
+
}
|
|
758
|
+
const selected = config.surfaces[key];
|
|
759
|
+
if (!selected)
|
|
760
|
+
throw new Error(`[stitchkit] Unknown MCP surface "${key}"`);
|
|
761
|
+
surface = selected;
|
|
762
|
+
} else {
|
|
763
|
+
const services = typeof config.services === "function" ? config.services(auth) : config.services;
|
|
764
|
+
const runtimeTools = typeof config.runtimeTools === "function" ? config.runtimeTools(auth) : config.runtimeTools;
|
|
765
|
+
surface = { services, runtimeTools };
|
|
766
|
+
}
|
|
767
|
+
const prepared = prepareMcpServerSurface(surface, {
|
|
691
768
|
extend: config.extend,
|
|
692
769
|
flattenUnionInput: config.flattenUnionInput,
|
|
693
770
|
schemaValidation: config.schemaValidation,
|
|
@@ -698,7 +775,7 @@ function buildMcpServer(config, auth) {
|
|
|
698
775
|
function buildMcpServerFromPrepared(config, auth, prepared) {
|
|
699
776
|
const server = new McpServer(config.serverInfo, config.instructions ? { instructions: config.instructions } : undefined);
|
|
700
777
|
const context = config.context?.(auth);
|
|
701
|
-
mountPreparedMcp(server, prepared, {
|
|
778
|
+
mountPreparedMcp(server, prepared.contractTools, {
|
|
702
779
|
context,
|
|
703
780
|
hooks: config.hooks,
|
|
704
781
|
lifecycle: config.lifecycle,
|
|
@@ -710,23 +787,15 @@ function buildMcpServerFromPrepared(config, auth, prepared) {
|
|
|
710
787
|
errorHint: config.errorHint,
|
|
711
788
|
onOutputStrip: config.onOutputStrip
|
|
712
789
|
});
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
prepare: (tool2) => prepareMcpTools([tool2], {
|
|
723
|
-
schemaValidation: config.schemaValidation,
|
|
724
|
-
logger: config.logger
|
|
725
|
-
}),
|
|
726
|
-
formatResult: (result, mode, toolName) => formatMcpResult(result, mode, toolName, config.errorHint)
|
|
727
|
-
});
|
|
728
|
-
config.nativeTools(registrar, auth);
|
|
729
|
-
}
|
|
790
|
+
mountPreparedRuntimeMcp(server, prepared.runtimeTools, {
|
|
791
|
+
context,
|
|
792
|
+
hooks: config.hooks,
|
|
793
|
+
lifecycle: config.lifecycle,
|
|
794
|
+
coerceJsonArgs: config.coerceJsonArgs,
|
|
795
|
+
onOutputStrip: config.onOutputStrip,
|
|
796
|
+
formatResult: (result, mode, toolName) => formatMcpResult(result, mode, toolName, config.errorHint)
|
|
797
|
+
});
|
|
798
|
+
config.rawTools?.(server, auth);
|
|
730
799
|
for (const resource of config.resources ?? []) {
|
|
731
800
|
mountMcpResource(server, resource);
|
|
732
801
|
}
|
|
@@ -838,13 +907,37 @@ function jsonRpcError(code, message, status, headers) {
|
|
|
838
907
|
}
|
|
839
908
|
function createMcpHandler(config) {
|
|
840
909
|
let buildServer;
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
910
|
+
const preparation = {
|
|
911
|
+
logger: config.logger,
|
|
912
|
+
extend: config.extend,
|
|
913
|
+
flattenUnionInput: config.flattenUnionInput,
|
|
914
|
+
schemaValidation: config.schemaValidation
|
|
915
|
+
};
|
|
916
|
+
if (config.surfaces && config.selectSurface) {
|
|
917
|
+
const selectSurface = config.selectSurface;
|
|
918
|
+
const preparedByKey = new Map;
|
|
919
|
+
const preparedBySurface = new WeakMap;
|
|
920
|
+
for (const key in config.surfaces) {
|
|
921
|
+
if (!Object.hasOwn(config.surfaces, key))
|
|
922
|
+
continue;
|
|
923
|
+
const surface = config.surfaces[key];
|
|
924
|
+
if (!surface)
|
|
925
|
+
continue;
|
|
926
|
+
const existing = preparedBySurface.get(surface);
|
|
927
|
+
const prepared = existing ?? prepareMcpServerSurface(surface, preparation);
|
|
928
|
+
if (!existing)
|
|
929
|
+
preparedBySurface.set(surface, prepared);
|
|
930
|
+
preparedByKey.set(key, prepared);
|
|
931
|
+
}
|
|
932
|
+
buildServer = (auth) => {
|
|
933
|
+
const key = selectSurface(auth);
|
|
934
|
+
const prepared = preparedByKey.get(key);
|
|
935
|
+
if (!prepared)
|
|
936
|
+
throw new Error(`[stitchkit] Unknown MCP surface "${key}"`);
|
|
937
|
+
return buildMcpServerFromPrepared(config, auth, prepared);
|
|
938
|
+
};
|
|
939
|
+
} else if (config.services && typeof config.services !== "function" && typeof config.runtimeTools !== "function") {
|
|
940
|
+
const prepared = prepareMcpServerSurface({ services: config.services, runtimeTools: config.runtimeTools }, preparation);
|
|
848
941
|
buildServer = (auth) => buildMcpServerFromPrepared(config, auth, prepared);
|
|
849
942
|
} else {
|
|
850
943
|
buildServer = (auth) => buildMcpServer(config, auth);
|
|
@@ -1490,7 +1583,7 @@ function parseApiErrorBody(body) {
|
|
|
1490
1583
|
|
|
1491
1584
|
// src/browser/client.ts
|
|
1492
1585
|
function withTimeout(options, endpoint) {
|
|
1493
|
-
const responseType = endpoint.rawResponse ? "response" : undefined;
|
|
1586
|
+
const responseType = endpoint.rawResponse ? "response" : endpoint.output ? undefined : "void";
|
|
1494
1587
|
if (endpoint.timeout === undefined && responseType === undefined)
|
|
1495
1588
|
return options;
|
|
1496
1589
|
return {
|
|
@@ -1500,10 +1593,20 @@ function withTimeout(options, endpoint) {
|
|
|
1500
1593
|
};
|
|
1501
1594
|
}
|
|
1502
1595
|
function withOutput(endpoint, result) {
|
|
1503
|
-
|
|
1504
|
-
if (!schema)
|
|
1596
|
+
if (endpoint.rawResponse)
|
|
1505
1597
|
return result;
|
|
1506
|
-
|
|
1598
|
+
const schema = endpoint.output;
|
|
1599
|
+
return result.then((value) => {
|
|
1600
|
+
if (!schema) {
|
|
1601
|
+
if (value === undefined || value === null)
|
|
1602
|
+
return;
|
|
1603
|
+
throw new Error("Server returned data for an endpoint with no output contract");
|
|
1604
|
+
}
|
|
1605
|
+
if (value === undefined) {
|
|
1606
|
+
throw new Error("Server returned no body for an endpoint with an output contract");
|
|
1607
|
+
}
|
|
1608
|
+
return schema.parse(value);
|
|
1609
|
+
});
|
|
1507
1610
|
}
|
|
1508
1611
|
function createClient(contract, configOrClient, contractConfig) {
|
|
1509
1612
|
const client = {};
|
|
@@ -1580,10 +1683,14 @@ function createFetchMethod(endpoint, prefix, config, contractConfig) {
|
|
|
1580
1683
|
}
|
|
1581
1684
|
if (endpoint.rawResponse)
|
|
1582
1685
|
return res2;
|
|
1583
|
-
if (
|
|
1686
|
+
if (!endpoint.output) {
|
|
1687
|
+
const text = await res2.text();
|
|
1688
|
+
if (text.length > 0) {
|
|
1689
|
+
throw new Error("Server returned data for an endpoint with no output contract");
|
|
1690
|
+
}
|
|
1584
1691
|
return;
|
|
1585
|
-
|
|
1586
|
-
return endpoint.output
|
|
1692
|
+
}
|
|
1693
|
+
return endpoint.output.parse(await res2.json());
|
|
1587
1694
|
}
|
|
1588
1695
|
const res = await fetch(url, {
|
|
1589
1696
|
method: endpoint.method,
|
|
@@ -1599,10 +1706,14 @@ function createFetchMethod(endpoint, prefix, config, contractConfig) {
|
|
|
1599
1706
|
}
|
|
1600
1707
|
if (endpoint.rawResponse)
|
|
1601
1708
|
return res;
|
|
1602
|
-
if (
|
|
1709
|
+
if (!endpoint.output) {
|
|
1710
|
+
const text = await res.text();
|
|
1711
|
+
if (text.length > 0) {
|
|
1712
|
+
throw new Error("Server returned data for an endpoint with no output contract");
|
|
1713
|
+
}
|
|
1603
1714
|
return;
|
|
1604
|
-
|
|
1605
|
-
return endpoint.output
|
|
1715
|
+
}
|
|
1716
|
+
return endpoint.output.parse(await res.json());
|
|
1606
1717
|
};
|
|
1607
1718
|
}
|
|
1608
1719
|
async function throwForErrorResponse(res, config, fallbackBody) {
|
|
@@ -1709,27 +1820,71 @@ function createToolkit() {
|
|
|
1709
1820
|
};
|
|
1710
1821
|
}
|
|
1711
1822
|
// src/tools/transports.ts
|
|
1823
|
+
var ALL_TRANSPORTS = ["HTTP", "MCP", "AGENT", "CLI"];
|
|
1712
1824
|
var TOOL_TRANSPORTS2 = ["MCP", "AGENT", "CLI"];
|
|
1713
1825
|
function emptyCounts() {
|
|
1714
1826
|
return { HTTP: 0, MCP: 0, AGENT: 0, CLI: 0 };
|
|
1715
1827
|
}
|
|
1716
|
-
function
|
|
1828
|
+
function contractCounts(service) {
|
|
1829
|
+
const counts = emptyCounts();
|
|
1830
|
+
for (const method of Object.values(service.methods)) {
|
|
1831
|
+
if (!method.expose || method.expose.includes("HTTP"))
|
|
1832
|
+
counts.HTTP += 1;
|
|
1833
|
+
}
|
|
1834
|
+
for (const transport of TOOL_TRANSPORTS2) {
|
|
1835
|
+
counts[transport] = collectToolSurface({
|
|
1836
|
+
surface: { services: [service] },
|
|
1837
|
+
transport,
|
|
1838
|
+
assertNames: false,
|
|
1839
|
+
assertUniqueNames: false
|
|
1840
|
+
}).length;
|
|
1841
|
+
}
|
|
1842
|
+
return counts;
|
|
1843
|
+
}
|
|
1844
|
+
function runtimeCounts(runtimeTools) {
|
|
1845
|
+
const counts = emptyCounts();
|
|
1846
|
+
for (const transport of TOOL_TRANSPORTS2) {
|
|
1847
|
+
counts[transport] = collectToolSurface({
|
|
1848
|
+
surface: { runtimeTools },
|
|
1849
|
+
transport,
|
|
1850
|
+
assertNames: false,
|
|
1851
|
+
assertUniqueNames: false
|
|
1852
|
+
}).length;
|
|
1853
|
+
}
|
|
1854
|
+
return counts;
|
|
1855
|
+
}
|
|
1856
|
+
function addCounts(target, source) {
|
|
1857
|
+
for (const transport of ALL_TRANSPORTS)
|
|
1858
|
+
target[transport] += source[transport];
|
|
1859
|
+
}
|
|
1860
|
+
function summarizeTransports(surface) {
|
|
1717
1861
|
const totals = emptyCounts();
|
|
1718
|
-
const
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1862
|
+
const sources = [];
|
|
1863
|
+
for (const service of surface.services ?? []) {
|
|
1864
|
+
const counts = contractCounts(service);
|
|
1865
|
+
addCounts(totals, counts);
|
|
1866
|
+
sources.push({ kind: "contract", service: service.name, counts });
|
|
1867
|
+
}
|
|
1868
|
+
const runtimeGroups = new Map;
|
|
1869
|
+
for (const definition of surface.runtimeTools ?? []) {
|
|
1870
|
+
const service = definition.identity.serviceName;
|
|
1871
|
+
const group = runtimeGroups.get(service);
|
|
1872
|
+
if (group)
|
|
1873
|
+
group.push(definition);
|
|
1874
|
+
else
|
|
1875
|
+
runtimeGroups.set(service, [definition]);
|
|
1876
|
+
}
|
|
1877
|
+
for (const [service, runtimeTools] of runtimeGroups) {
|
|
1878
|
+
const counts = runtimeCounts(runtimeTools);
|
|
1879
|
+
addCounts(totals, counts);
|
|
1880
|
+
sources.push({ kind: "runtime", service, counts });
|
|
1881
|
+
}
|
|
1882
|
+
return {
|
|
1883
|
+
contractServices: surface.services?.length ?? 0,
|
|
1884
|
+
runtimeTools: surface.runtimeTools?.length ?? 0,
|
|
1885
|
+
totals,
|
|
1886
|
+
sources
|
|
1887
|
+
};
|
|
1733
1888
|
}
|
|
1734
1889
|
// src/tools/view-file.ts
|
|
1735
1890
|
import { readFile, realpath, stat } from "node:fs/promises";
|