stitchkit 0.41.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.
Files changed (51) hide show
  1. package/dist/cli.js +2 -2
  2. package/dist/contract/errors-factory.d.ts +88 -15
  3. package/dist/contract/errors-factory.d.ts.map +1 -1
  4. package/dist/contract/errors.d.ts +4 -4
  5. package/dist/contract/errors.d.ts.map +1 -1
  6. package/dist/contract/index.d.ts +1 -1
  7. package/dist/contract/index.d.ts.map +1 -1
  8. package/dist/contract/index.js +2 -2
  9. package/dist/{index-xax049k6.js → index-11x5dts2.js} +44 -13
  10. package/dist/{index-4e1c0hsw.js → index-181aebw8.js} +1 -1
  11. package/dist/{index-dnkefke9.js → index-310bfer5.js} +1 -1
  12. package/dist/{index-sc67e454.js → index-gex6gxhe.js} +7 -4
  13. package/dist/{index-ncqqn1bc.js → index-p9vkwns1.js} +2 -2
  14. package/dist/{index-809wc1tt.js → index-s4qsmgwe.js} +4 -1
  15. package/dist/index.js +2 -2
  16. package/dist/internal/typed.d.ts +8 -0
  17. package/dist/internal/typed.d.ts.map +1 -1
  18. package/dist/node.js +2 -2
  19. package/dist/observability/index.js +1 -1
  20. package/dist/react.js +1 -1
  21. package/dist/server/error-hook.d.ts +8 -4
  22. package/dist/server/error-hook.d.ts.map +1 -1
  23. package/dist/server/index.js +7 -6
  24. package/dist/tools/agent.d.ts +1 -1
  25. package/dist/tools/agent.d.ts.map +1 -1
  26. package/dist/tools/list-names.d.ts +8 -6
  27. package/dist/tools/list-names.d.ts.map +1 -1
  28. package/dist/tools/manifest.d.ts +13 -5
  29. package/dist/tools/manifest.d.ts.map +1 -1
  30. package/dist/tools/mcp-handler.d.ts +5 -4
  31. package/dist/tools/mcp-handler.d.ts.map +1 -1
  32. package/dist/tools/mcp-stdio.d.ts +3 -2
  33. package/dist/tools/mcp-stdio.d.ts.map +1 -1
  34. package/dist/tools/mcp.d.ts +49 -9
  35. package/dist/tools/mcp.d.ts.map +1 -1
  36. package/dist/tools/native-mcp.d.ts +4 -18
  37. package/dist/tools/native-mcp.d.ts.map +1 -1
  38. package/dist/tools/runtime-tool.d.ts +1 -1
  39. package/dist/tools/runtime-tool.d.ts.map +1 -1
  40. package/dist/tools/surface.d.ts +37 -0
  41. package/dist/tools/surface.d.ts.map +1 -0
  42. package/dist/tools/toolkit.d.ts +4 -4
  43. package/dist/tools/toolkit.d.ts.map +1 -1
  44. package/dist/tools/transports.d.ts +11 -9
  45. package/dist/tools/transports.d.ts.map +1 -1
  46. package/dist/tools/view-file.d.ts +3 -3
  47. package/dist/tools.d.ts +5 -5
  48. package/dist/tools.d.ts.map +1 -1
  49. package/dist/tools.js +292 -155
  50. package/llms-full.txt +264 -47
  51. 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-dnkefke9.js";
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-ncqqn1bc.js";
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-sc67e454.js";
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
- assertToolName(definition.name, definition.identity.serviceName, definition.identity.action);
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 service of serviceList) {
108
- for (const mountable of collectTools(service, "AGENT", {
109
- extend: config.extend,
110
- flattenUnionInput: config.flattenUnionInput
111
- })) {
112
- assertUniqueToolName(mountable.name, Object.hasOwn(tools, mountable.name), "agent tool name");
113
- tools[mountable.name] = tool({
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
- if (definition.present?.agent) {
152
- const presenter = definition.present.agent;
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
- tools[definition.name] = tool({
169
- description: definition.description,
200
+ const output = entry.kind === "runtime" ? entry.definition.output : undefined;
201
+ tools[mountable.name] = tool({
202
+ description: mountable.method.desc,
170
203
  inputSchema,
171
- ...definition.output && { outputSchema: definition.output },
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(services) {
260
+ function listToolNames(surface) {
228
261
  const entries = new Map;
229
- for (const service of services) {
230
- for (const transport of TOOL_TRANSPORTS) {
231
- for (const tool2 of collectTools(service, transport, { assertNames: false })) {
232
- const id = `${service.name}\x00${tool2.method.key}`;
233
- const existing = entries.get(id);
234
- if (existing) {
235
- existing.transports.push(transport);
236
- } else {
237
- entries.set(id, {
238
- name: tool2.name,
239
- service: service.name,
240
- method: tool2.method.key,
241
- transports: [transport]
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(tools) {
251
- return tools.map((tool2) => ({
252
- name: tool2.name,
253
- description: tool2.method.desc,
254
- inputSchema: tool2.presentationSchema
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 createNativeMcpRegistrar(server, config) {
289
- return {
290
- rawServer: server,
291
- registerTool: (definition) => {
292
- if (!runtimeToolSupports(definition, "MCP"))
293
- return;
294
- assertUniqueToolName(definition.name, config.takenNames.has(definition.name), "MCP tool name");
295
- config.takenNames.add(definition.name);
296
- const mountable = runtimeToolMountable(definition);
297
- const [prepared] = config.prepare(mountable);
298
- if (!prepared)
299
- return;
300
- const toolConfig = {
301
- description: definition.description,
302
- inputSchema: z.looseObject({}).meta(presentationMetadata(prepared.inputSchema))
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
- if (prepared.outputSchema)
305
- toolConfig.outputSchema = prepared.outputSchema;
306
- if (definition.annotations)
307
- toolConfig.annotations = definition.annotations;
308
- if (definition.ui) {
309
- toolConfig._meta = {
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
- server.registerTool(definition.name, toolConfig, async (rawArgs) => {
315
- const args = isRecord(rawArgs) ? rawArgs : {};
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: { message: "Runtime MCP presenter returned an invalid CallToolResult" }
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
- const services = typeof config.services === "function" ? config.services(auth) : config.services;
690
- const prepared = prepareMcpSurface(services, {
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
- if (config.nativeTools) {
714
- const takenNames = new Set(prepared.map((tool2) => tool2.mountable.name));
715
- const registrar = createNativeMcpRegistrar(server, {
716
- context,
717
- hooks: config.hooks,
718
- lifecycle: config.lifecycle,
719
- coerceJsonArgs: config.coerceJsonArgs,
720
- onOutputStrip: config.onOutputStrip,
721
- takenNames,
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
- if (Array.isArray(config.services)) {
842
- const prepared = prepareMcpSurface(config.services, {
843
- logger: config.logger,
844
- extend: config.extend,
845
- flattenUnionInput: config.flattenUnionInput,
846
- schemaValidation: config.schemaValidation
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);
@@ -1727,27 +1820,71 @@ function createToolkit() {
1727
1820
  };
1728
1821
  }
1729
1822
  // src/tools/transports.ts
1823
+ var ALL_TRANSPORTS = ["HTTP", "MCP", "AGENT", "CLI"];
1730
1824
  var TOOL_TRANSPORTS2 = ["MCP", "AGENT", "CLI"];
1731
1825
  function emptyCounts() {
1732
1826
  return { HTTP: 0, MCP: 0, AGENT: 0, CLI: 0 };
1733
1827
  }
1734
- function summarizeTransports(services) {
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) {
1735
1861
  const totals = emptyCounts();
1736
- const perService = services.map((service) => {
1737
- const counts = emptyCounts();
1738
- for (const method of Object.values(service.methods)) {
1739
- if (!method.expose || method.expose.includes("HTTP"))
1740
- counts.HTTP += 1;
1741
- }
1742
- for (const transport of TOOL_TRANSPORTS2) {
1743
- counts[transport] = collectTools(service, transport, { assertNames: false }).length;
1744
- }
1745
- for (const transport of Object.keys(counts)) {
1746
- totals[transport] += counts[transport];
1747
- }
1748
- return { service: service.name, counts };
1749
- });
1750
- return { services: services.length, totals, perService };
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
+ };
1751
1888
  }
1752
1889
  // src/tools/view-file.ts
1753
1890
  import { readFile, realpath, stat } from "node:fs/promises";