stitchkit 0.103.12 → 0.104.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 (225) hide show
  1. package/CHANGELOG.md +112 -0
  2. package/darwin-native.cjs +12 -0
  3. package/darwin-native.d.cts +2 -0
  4. package/dist/agent-runtime/store-conformance-lifecycle.d.ts +10 -10
  5. package/dist/application/directory-inbox-accept.d.ts +13 -0
  6. package/dist/application/directory-inbox-accept.d.ts.map +1 -0
  7. package/dist/application/directory-inbox-contract.d.ts +42 -5
  8. package/dist/application/directory-inbox-contract.d.ts.map +1 -1
  9. package/dist/application/directory-inbox-delivery.d.ts +14 -0
  10. package/dist/application/directory-inbox-delivery.d.ts.map +1 -0
  11. package/dist/application/directory-inbox-files.d.ts +28 -0
  12. package/dist/application/directory-inbox-files.d.ts.map +1 -0
  13. package/dist/application/directory-inbox-state.d.ts +14 -0
  14. package/dist/application/directory-inbox-state.d.ts.map +1 -0
  15. package/dist/application/directory-inbox.d.ts +2 -1
  16. package/dist/application/directory-inbox.d.ts.map +1 -1
  17. package/dist/application/file-state-store.d.ts +2 -8
  18. package/dist/application/file-state-store.d.ts.map +1 -1
  19. package/dist/application/state-store.d.ts +6 -1
  20. package/dist/application/state-store.d.ts.map +1 -1
  21. package/dist/browser/client-arguments.d.ts +4 -0
  22. package/dist/browser/client-arguments.d.ts.map +1 -0
  23. package/dist/browser/client-executors.d.ts.map +1 -1
  24. package/dist/browser/client.d.ts.map +1 -1
  25. package/dist/browser/contract-stream.d.ts.map +1 -1
  26. package/dist/browser/http.d.ts +2 -1
  27. package/dist/browser/http.d.ts.map +1 -1
  28. package/dist/browser/native-fetch.d.ts +8 -0
  29. package/dist/browser/native-fetch.d.ts.map +1 -0
  30. package/dist/contract/errors.d.ts +16 -12
  31. package/dist/contract/errors.d.ts.map +1 -1
  32. package/dist/contract/normalize.d.ts +5 -1
  33. package/dist/contract/normalize.d.ts.map +1 -1
  34. package/dist/contract/stream.d.ts +1 -0
  35. package/dist/contract/stream.d.ts.map +1 -1
  36. package/dist/entrypoints/agent-runtime/browser.js +3 -3
  37. package/dist/entrypoints/agent-runtime/coding-tools.js +8 -6
  38. package/dist/entrypoints/agent-runtime/harness-tools.js +8 -8
  39. package/dist/entrypoints/agent-runtime/harness.js +12 -11
  40. package/dist/entrypoints/agent-runtime/realtime.js +3 -3
  41. package/dist/entrypoints/agent-runtime/sandbox.js +8 -6
  42. package/dist/entrypoints/agent-runtime.js +13 -13
  43. package/dist/entrypoints/application/diagnostic-journal.js +3 -2
  44. package/dist/entrypoints/application/directory-inbox.js +424 -119
  45. package/dist/entrypoints/application/grammy.js +1 -1
  46. package/dist/entrypoints/application.d.ts +2 -2
  47. package/dist/entrypoints/application.d.ts.map +1 -1
  48. package/dist/entrypoints/application.js +7 -3
  49. package/dist/entrypoints/cli.d.ts +2 -0
  50. package/dist/entrypoints/cli.d.ts.map +1 -1
  51. package/dist/entrypoints/cli.js +757 -136
  52. package/dist/entrypoints/contract.js +3 -3
  53. package/dist/entrypoints/files.js +11 -107
  54. package/dist/entrypoints/index.js +7 -7
  55. package/dist/entrypoints/node.js +8 -8
  56. package/dist/entrypoints/observability.js +2 -2
  57. package/dist/entrypoints/process.js +2 -1
  58. package/dist/entrypoints/react.js +1 -1
  59. package/dist/entrypoints/remote.js +19 -6
  60. package/dist/entrypoints/server.d.ts +1 -1
  61. package/dist/entrypoints/server.d.ts.map +1 -1
  62. package/dist/entrypoints/server.js +20 -14
  63. package/dist/entrypoints/telegram.d.ts +1 -0
  64. package/dist/entrypoints/telegram.d.ts.map +1 -1
  65. package/dist/entrypoints/telegram.js +214 -98
  66. package/dist/entrypoints/testing.js +7 -7
  67. package/dist/entrypoints/tools/connections.d.ts +2 -2
  68. package/dist/entrypoints/tools/connections.d.ts.map +1 -1
  69. package/dist/entrypoints/tools/connections.js +427 -294
  70. package/dist/entrypoints/tools/invoker.js +6 -6
  71. package/dist/entrypoints/tools/mcp.d.ts +12 -0
  72. package/dist/entrypoints/tools/mcp.d.ts.map +1 -0
  73. package/dist/entrypoints/tools/mcp.js +45 -0
  74. package/dist/entrypoints/tools.d.ts +1 -0
  75. package/dist/entrypoints/tools.d.ts.map +1 -1
  76. package/dist/entrypoints/tools.js +82 -1147
  77. package/dist/entrypoints/tracking.js +1 -1
  78. package/dist/files/boundary.d.ts +2 -8
  79. package/dist/files/boundary.d.ts.map +1 -1
  80. package/dist/files/file-io.d.ts +0 -5
  81. package/dist/files/file-io.d.ts.map +1 -1
  82. package/dist/files/read.d.ts +2 -2
  83. package/dist/files/read.d.ts.map +1 -1
  84. package/dist/index-1a6jtnd9.js +88 -0
  85. package/dist/{index-fyphpknh.js → index-22rhg58f.js} +1 -1
  86. package/dist/index-269zqj8m.js +77 -0
  87. package/dist/{index-r34mvse1.js → index-27a2pj09.js} +1 -1
  88. package/dist/{index-r956rndn.js → index-2hyavjjj.js} +3 -3
  89. package/dist/{index-m2xqkv55.js → index-2vnh05w4.js} +83 -14
  90. package/dist/{index-pw99k9h5.js → index-43p08g7t.js} +1 -1
  91. package/dist/{index-xwq9j1sf.js → index-4e0ah203.js} +9 -7
  92. package/dist/{index-nybn2err.js → index-5csmzesf.js} +1 -1
  93. package/dist/{index-qapzv4p5.js → index-5z6jn897.js} +1 -1
  94. package/dist/{index-ahvsksw5.js → index-6d9xaxw1.js} +1 -1
  95. package/dist/{index-ahtnte39.js → index-6drst9sg.js} +46 -35
  96. package/dist/{index-x87j7csh.js → index-77nxk6ay.js} +2 -2
  97. package/dist/{index-dnn9z96q.js → index-a0zzh8ak.js} +3 -3
  98. package/dist/{index-kmpzj1cq.js → index-a3wma1hk.js} +1 -1
  99. package/dist/{index-j8aw2c0e.js → index-bakgedh8.js} +35 -7
  100. package/dist/index-bhb950qs.js +1145 -0
  101. package/dist/{index-tgzhq07n.js → index-cvda079a.js} +1 -1
  102. package/dist/{index-n2gre124.js → index-fa4wg312.js} +4 -4
  103. package/dist/{index-zp4jpkp0.js → index-fk18k56k.js} +2 -2
  104. package/dist/{index-afjvjyck.js → index-g2j7pnak.js} +2 -134
  105. package/dist/index-g95fk660.js +116 -0
  106. package/dist/{index-ej331hah.js → index-gxm9rc97.js} +1 -1
  107. package/dist/{index-r5c4fedv.js → index-h2fhjqy7.js} +2 -1
  108. package/dist/{index-sybedc8b.js → index-hbd32c6a.js} +23 -94
  109. package/dist/index-jd470k6q.js +109 -0
  110. package/dist/{index-b2bz0kdm.js → index-kb8styz3.js} +4 -3
  111. package/dist/{index-0c419yx7.js → index-kemm0ej5.js} +12 -9
  112. package/dist/{index-806d5phm.js → index-ptn0g0gj.js} +7 -7
  113. package/dist/{index-w575evhq.js → index-r4r70zmh.js} +1 -1
  114. package/dist/{index-12m2v2a9.js → index-s4evrmp5.js} +1 -1
  115. package/dist/{index-1khy8y7n.js → index-t1sr4aks.js} +3 -3
  116. package/dist/index-v5ee8ath.js +88 -0
  117. package/dist/{index-45tdvqa3.js → index-wh0ecvyk.js} +1 -1
  118. package/dist/{index-n53mswt3.js → index-x928baz2.js} +8 -1
  119. package/dist/index-yk4g8805.js +133 -0
  120. package/dist/{index-ygkka6zn.js → index-ywppsaby.js} +28 -5
  121. package/dist/{index-xvdd2rbb.js → index-za6j7nwy.js} +19 -5
  122. package/dist/internal/bounded-file-read.d.ts +41 -0
  123. package/dist/internal/bounded-file-read.d.ts.map +1 -0
  124. package/dist/internal/darwin-binding-error.d.ts +24 -0
  125. package/dist/internal/darwin-binding-error.d.ts.map +1 -0
  126. package/dist/internal/darwin-binding.d.ts.map +1 -1
  127. package/dist/internal/exclusive-lock.d.ts +2 -0
  128. package/dist/internal/exclusive-lock.d.ts.map +1 -1
  129. package/dist/internal/with-exclusive-lock.d.ts +4 -2
  130. package/dist/internal/with-exclusive-lock.d.ts.map +1 -1
  131. package/dist/telegram/broadcast-delivery.d.ts +23 -0
  132. package/dist/telegram/broadcast-delivery.d.ts.map +1 -0
  133. package/dist/telegram/broadcast-failure.d.ts +22 -0
  134. package/dist/telegram/broadcast-failure.d.ts.map +1 -0
  135. package/dist/telegram/broadcast-state.d.ts +6 -3
  136. package/dist/telegram/broadcast-state.d.ts.map +1 -1
  137. package/dist/telegram/broadcast.d.ts +10 -5
  138. package/dist/telegram/broadcast.d.ts.map +1 -1
  139. package/dist/tools/cli/args-fields.d.ts +2 -0
  140. package/dist/tools/cli/args-fields.d.ts.map +1 -1
  141. package/dist/tools/cli/args.d.ts.map +1 -1
  142. package/dist/tools/cli/format.d.ts.map +1 -1
  143. package/dist/tools/cli/invoke.d.ts +2 -2
  144. package/dist/tools/cli/invoke.d.ts.map +1 -1
  145. package/dist/tools/cli/publication-assets.d.ts +10 -0
  146. package/dist/tools/cli/publication-assets.d.ts.map +1 -0
  147. package/dist/tools/cli/publication-contract.d.ts +75 -0
  148. package/dist/tools/cli/publication-contract.d.ts.map +1 -0
  149. package/dist/tools/cli/publication-control.d.ts +4 -0
  150. package/dist/tools/cli/publication-control.d.ts.map +1 -0
  151. package/dist/tools/cli/publication-storage.d.ts +32 -0
  152. package/dist/tools/cli/publication-storage.d.ts.map +1 -0
  153. package/dist/tools/cli/publication.d.ts +35 -0
  154. package/dist/tools/cli/publication.d.ts.map +1 -0
  155. package/dist/tools/cli/update.d.ts.map +1 -1
  156. package/dist/tools/connections/define.d.ts.map +1 -1
  157. package/dist/tools/connections/error-projection.d.ts +4 -0
  158. package/dist/tools/connections/error-projection.d.ts.map +1 -0
  159. package/dist/tools/connections/errors.d.ts +24 -8
  160. package/dist/tools/connections/errors.d.ts.map +1 -1
  161. package/dist/tools/connections/limits.d.ts +11 -31
  162. package/dist/tools/connections/limits.d.ts.map +1 -1
  163. package/dist/tools/connections/mcp-client.d.ts +6 -4
  164. package/dist/tools/connections/mcp-client.d.ts.map +1 -1
  165. package/dist/tools/connections/mcp.d.ts.map +1 -1
  166. package/dist/tools/connections/openapi.d.ts.map +1 -1
  167. package/dist/tools/connections/operation-limits.d.ts +25 -0
  168. package/dist/tools/connections/operation-limits.d.ts.map +1 -0
  169. package/dist/tools/connections/sse-frames.d.ts +11 -2
  170. package/dist/tools/connections/sse-frames.d.ts.map +1 -1
  171. package/dist/tools/connections/sse-session.d.ts +15 -2
  172. package/dist/tools/connections/sse-session.d.ts.map +1 -1
  173. package/dist/tools/connections/types.d.ts +19 -1
  174. package/dist/tools/connections/types.d.ts.map +1 -1
  175. package/dist/tools/execute-result.d.ts.map +1 -1
  176. package/dist/tools/execute.d.ts.map +1 -1
  177. package/dist/tools/internal/observe-hook.d.ts +3 -0
  178. package/dist/tools/internal/observe-hook.d.ts.map +1 -0
  179. package/dist/tools/internal/surface-projector.d.ts +2 -1
  180. package/dist/tools/internal/surface-projector.d.ts.map +1 -1
  181. package/dist/tools/mcp/mount.d.ts +2 -2
  182. package/dist/tools/mcp/mount.d.ts.map +1 -1
  183. package/dist/tools/mcp/prepare.d.ts +4 -5
  184. package/dist/tools/mcp/prepare.d.ts.map +1 -1
  185. package/dist/tools/mcp/register.d.ts +4 -1
  186. package/dist/tools/mcp/register.d.ts.map +1 -1
  187. package/dist/tools/mcp/runtime-tools.d.ts.map +1 -1
  188. package/dist/tools/remote.d.ts.map +1 -1
  189. package/dist/tools/runtime-tool-execution.d.ts +65 -0
  190. package/dist/tools/runtime-tool-execution.d.ts.map +1 -0
  191. package/dist/tools/runtime-tool-mcp.d.ts +27 -0
  192. package/dist/tools/runtime-tool-mcp.d.ts.map +1 -0
  193. package/dist/tools/runtime-tool.d.ts +9 -60
  194. package/dist/tools/runtime-tool.d.ts.map +1 -1
  195. package/dist/tools/surface.d.ts +12 -14
  196. package/dist/tools/surface.d.ts.map +1 -1
  197. package/llms/application.2.txt +221 -44
  198. package/llms/application.3.txt +3 -1
  199. package/llms/application.txt +1 -1
  200. package/llms/cli.2.txt +144 -1
  201. package/llms/cli.txt +133 -104
  202. package/llms/contract.txt +1 -1
  203. package/llms/process.txt +34 -5
  204. package/llms/server.4.txt +4 -1
  205. package/llms/server.5.txt +1 -0
  206. package/llms/stitchkit.2.txt +14 -0
  207. package/llms/stitchkit.txt +5 -4
  208. package/llms/telegram.txt +64 -2
  209. package/llms/tools-connections.txt +3 -0
  210. package/llms/tools-mcp.txt +29 -0
  211. package/llms/tools.2.txt +59 -0
  212. package/llms/tools.3.txt +1 -0
  213. package/llms/tools.txt +11 -52
  214. package/llms/upgrading-0.37.0-0.44.0.txt +444 -0
  215. package/llms/{upgrading-0.44.0-0.60.0.txt → upgrading-0.44.0-0.61.0.txt} +140 -271
  216. package/llms/{upgrading-0.61.0-0.74.0.txt → upgrading-0.62.0-0.79.0.txt} +171 -140
  217. package/llms/{upgrading-0.75.0-0.103.0.txt → upgrading-0.80.0-0.104.0.txt} +120 -171
  218. package/llms/upgrading.txt +4 -4
  219. package/llms-full.txt +677 -64
  220. package/llms.txt +13 -12
  221. package/package.json +10 -1
  222. package/dist/index-d4pa2bgs.js +0 -42
  223. package/dist/index-wykebpn9.js +0 -221
  224. package/llms/upgrading-0.37.0.txt +0 -176
  225. package/dist/{index-69nvze1q.js → index-pptyv13k.js} +3 -3
@@ -0,0 +1,1145 @@
1
+ import {
2
+ toJsonSchema
3
+ } from "./index-ct75c3at.js";
4
+ import {
5
+ AppError2
6
+ } from "./index-x928baz2.js";
7
+ import {
8
+ resolvePropagationContext2
9
+ } from "./index-yex7eg1v.js";
10
+ import {
11
+ toolSurfaceOutputSchema,
12
+ staticInputRounds,
13
+ validateMcpRoundPolicy,
14
+ validateResolvedInputRounds,
15
+ presentationMetadata,
16
+ prepareProjectedMcpTools
17
+ } from "./index-77nxk6ay.js";
18
+ import {
19
+ parseToolCallArguments,
20
+ toolResultFromError,
21
+ observed
22
+ } from "./index-2vnh05w4.js";
23
+ import {
24
+ collectTools2,
25
+ createToolRunner,
26
+ formatToolError
27
+ } from "./index-2hyavjjj.js";
28
+ import {
29
+ redact2
30
+ } from "./index-e6c2spsd.js";
31
+ import {
32
+ DEFAULT_CORS_ALLOW_HEADERS2,
33
+ DEFAULT_PROCESS_SIGNALS,
34
+ defaultSignalSource,
35
+ reportSignalError,
36
+ guardSignalCallback
37
+ } from "./index-yk4g8805.js";
38
+ import {
39
+ collectToolSurface
40
+ } from "./index-a0zzh8ak.js";
41
+ import {
42
+ argumentsDigest
43
+ } from "./index-r7a7r7ha.js";
44
+ import {
45
+ runWithRequestContext2,
46
+ getRequestContext2
47
+ } from "./index-7n68nmc6.js";
48
+ import {
49
+ compareCodeUnits,
50
+ serializeCanonicalJson
51
+ } from "./index-zsvzhjtk.js";
52
+ import {
53
+ isRecord
54
+ } from "./index-9jfdm5tg.js";
55
+
56
+ // src/server/oauth/metadata.ts
57
+ var PROTECTED_RESOURCE_PATH2 = "/.well-known/oauth-protected-resource";
58
+ function metadataPath(resource) {
59
+ const { pathname } = new URL(resource);
60
+ return pathname === "/" ? PROTECTED_RESOURCE_PATH2 : `${PROTECTED_RESOURCE_PATH2}${pathname}`;
61
+ }
62
+ function protectedResourceMetadataUrl2(resource) {
63
+ return `${new URL(resource).origin}${metadataPath(resource)}`;
64
+ }
65
+ function wwwAuthenticateHeader2(resource) {
66
+ return `Bearer resource_metadata="${protectedResourceMetadataUrl2(resource)}"`;
67
+ }
68
+ var PUBLIC_CORS = {
69
+ "Access-Control-Allow-Origin": "*",
70
+ "Access-Control-Allow-Methods": "GET, OPTIONS",
71
+ "Access-Control-Allow-Headers": DEFAULT_CORS_ALLOW_HEADERS2
72
+ };
73
+ function oauthProtectedResourceRoute2(config) {
74
+ const body = JSON.stringify({
75
+ resource: config.resource,
76
+ authorization_servers: config.authorizationServers,
77
+ ...config.scopesSupported && { scopes_supported: config.scopesSupported },
78
+ bearer_methods_supported: ["header"]
79
+ });
80
+ return {
81
+ method: "ALL",
82
+ path: metadataPath(config.resource),
83
+ handler: (req) => {
84
+ if (req.method === "OPTIONS")
85
+ return new Response(null, { status: 204, headers: PUBLIC_CORS });
86
+ if (req.method !== "GET")
87
+ return new Response("Method Not Allowed", { status: 405 });
88
+ return new Response(body, {
89
+ status: 200,
90
+ headers: { "Content-Type": "application/json", ...PUBLIC_CORS }
91
+ });
92
+ }
93
+ };
94
+ }
95
+
96
+ // src/tools/mcp/app.ts
97
+ import { readFileSync } from "node:fs";
98
+ import { fileURLToPath } from "node:url";
99
+ var RESOURCE_MIME_TYPE2 = "text/html;profile=mcp-app";
100
+ var EXT_APPS_BUNDLE_PLACEHOLDER2 = "/*__EXT_APPS_BUNDLE__*/";
101
+ function inlineMcpAppBundle2(html) {
102
+ if (!html.includes(EXT_APPS_BUNDLE_PLACEHOLDER2))
103
+ return html;
104
+ let bundlePath;
105
+ try {
106
+ bundlePath = fileURLToPath(import.meta.resolve("@modelcontextprotocol/ext-apps/app-with-deps"));
107
+ } catch {
108
+ throw new Error("[stitchkit] inlineMcpAppBundle: '@modelcontextprotocol/ext-apps' is not installed. " + "Add it as a dependency to serve MCP App widgets.");
109
+ }
110
+ const bundle = readFileSync(bundlePath, "utf8").replace(/export\{([^}]+)\};?\s*$/, (_match, body) => `globalThis.ExtApps={${body.split(",").map((pair) => {
111
+ const [local, exported] = pair.split(" as ").map((s) => s.trim());
112
+ return `${exported ?? local}:${local}`;
113
+ }).join(",")}};`);
114
+ return html.replace(EXT_APPS_BUNDLE_PLACEHOLDER2, () => bundle);
115
+ }
116
+
117
+ // src/tools/mcp/catalog.ts
118
+ import { createHash } from "node:crypto";
119
+ var MCP_CATALOG_META_KEY2 = "stitchkit/catalog";
120
+ var stampCache = new WeakMap;
121
+ function toolFingerprint(descriptor) {
122
+ const { mountable } = descriptor;
123
+ return {
124
+ name: mountable.name,
125
+ description: mountable.method.desc,
126
+ input: descriptor.inputSchema,
127
+ output: descriptor.outputSchema ? toJsonSchema(descriptor.outputSchema, "output", "any") : null,
128
+ annotations: mountable.method.annotations ?? null
129
+ };
130
+ }
131
+ function mcpCatalogStamp(surface) {
132
+ const cached = stampCache.get(surface);
133
+ if (cached)
134
+ return cached;
135
+ const descriptors = [
136
+ ...surface.contractTools,
137
+ ...surface.runtimeTools.map((entry) => entry.descriptor)
138
+ ].sort((left, right) => compareCodeUnits(left.mountable.name, right.mountable.name));
139
+ const bytes = serializeCanonicalJson(descriptors.map(toolFingerprint));
140
+ const stamp = {
141
+ digest: createHash("sha256").update(bytes).digest("hex").slice(0, 16),
142
+ tools: descriptors.length
143
+ };
144
+ stampCache.set(surface, stamp);
145
+ return stamp;
146
+ }
147
+ function mcpCatalogMeta2(stamp) {
148
+ return { [MCP_CATALOG_META_KEY2]: { ...stamp } };
149
+ }
150
+ function readMcpCatalogStamp2(meta) {
151
+ if (!isRecord(meta))
152
+ return null;
153
+ const value = meta[MCP_CATALOG_META_KEY2];
154
+ if (!isRecord(value))
155
+ return null;
156
+ const { digest, tools } = value;
157
+ if (typeof digest !== "string" || digest.length === 0)
158
+ return null;
159
+ if (typeof tools !== "number" || !Number.isInteger(tools) || tools < 0)
160
+ return null;
161
+ return { digest, tools };
162
+ }
163
+ function stampToolRegistration(config, stamp) {
164
+ if (!stamp)
165
+ return config;
166
+ return { ...config, _meta: { ...config._meta, ...mcpCatalogMeta2(stamp) } };
167
+ }
168
+ function stampToolResult(result, stamp) {
169
+ if (!stamp)
170
+ return result;
171
+ return { ...result, _meta: { ...result._meta, ...mcpCatalogMeta2(stamp) } };
172
+ }
173
+
174
+ // src/tools/mcp/mount.ts
175
+ import {
176
+ createRequestStateCodec,
177
+ McpServer
178
+ } from "@modelcontextprotocol/server";
179
+
180
+ // src/tools/mcp/prepare.ts
181
+ function textBlock(text) {
182
+ return [{ type: "text", text }];
183
+ }
184
+ function formatMcpResult(result, mode, toolName, errorHint) {
185
+ if (result.ok) {
186
+ if (mode === "none")
187
+ return { content: [] };
188
+ let serialized;
189
+ try {
190
+ serialized = JSON.stringify(result.data, null, 2);
191
+ } catch {
192
+ serialized = undefined;
193
+ }
194
+ if (serialized === undefined) {
195
+ const safe = redact2(result.data, { sensitiveKeys: /(?!)/ });
196
+ return { content: textBlock(JSON.stringify(safe, null, 2)), structuredContent: safe };
197
+ }
198
+ return { content: textBlock(serialized), structuredContent: result.data };
199
+ }
200
+ return {
201
+ content: textBlock(JSON.stringify(formatToolError(result, toolName, errorHint), null, 2)),
202
+ isError: true
203
+ };
204
+ }
205
+ function prepareMcpSurface(services, config = {}) {
206
+ const serviceList = Array.isArray(services) ? services : [services];
207
+ const tools = serviceList.flatMap((service) => collectTools2(service, "MCP", config));
208
+ return prepareMcpTools(tools, config);
209
+ }
210
+ function prepareMcpTools(tools, config = {}) {
211
+ const prepared = prepareProjectedMcpTools(tools.map((mountable) => {
212
+ const outputSchema = toolSurfaceOutputSchema(mountable.method);
213
+ return {
214
+ tool: mountable,
215
+ name: mountable.name,
216
+ ...mountable.method.paramsSchema !== undefined && {
217
+ paramsSchema: mountable.method.paramsSchema
218
+ },
219
+ ...mountable.method.inputSchema !== undefined && {
220
+ inputSchema: mountable.method.inputSchema
221
+ },
222
+ ...mountable.method.toolView?.defaults !== undefined && {
223
+ inputDefaults: mountable.method.toolView.defaults
224
+ },
225
+ ...outputSchema !== undefined && { outputSchema },
226
+ shouldExtend: mountable.shouldExtend,
227
+ ...mountable.method.mcp !== undefined && { mcp: mountable.method.mcp }
228
+ };
229
+ }), config).map((entry) => {
230
+ Object.freeze(entry.tool);
231
+ return Object.freeze({
232
+ mountable: entry.tool,
233
+ inputSchema: entry.inputSchema,
234
+ ...entry.outputSchema !== undefined && { outputSchema: entry.outputSchema },
235
+ outputMode: entry.outputSchema === undefined ? "none" : "direct"
236
+ });
237
+ });
238
+ return Object.freeze(prepared);
239
+ }
240
+ function prepareMcpServerSurface(surface, config = {}) {
241
+ const contractMountables = [];
242
+ const definitions = [];
243
+ const runtimeMountables = [];
244
+ for (const entry of collectToolSurface({
245
+ surface,
246
+ transport: "MCP",
247
+ extend: config.extend,
248
+ flattenUnionInput: config.flattenUnionInput
249
+ })) {
250
+ if (entry.kind === "contract") {
251
+ contractMountables.push(entry.mountable);
252
+ } else {
253
+ definitions.push(entry.definition);
254
+ runtimeMountables.push(entry.mountable);
255
+ }
256
+ }
257
+ const contractTools = prepareMcpTools(contractMountables, config);
258
+ const runtimeDescriptors = prepareMcpTools(runtimeMountables, {
259
+ schemaValidation: config.schemaValidation,
260
+ logger: config.logger,
261
+ multiRound: config.multiRound
262
+ });
263
+ const descriptorsByName = new Map(runtimeDescriptors.map((descriptor) => [descriptor.mountable.name, descriptor]));
264
+ const runtimeTools = [];
265
+ for (const definition of definitions) {
266
+ const descriptor = descriptorsByName.get(definition.name);
267
+ if (descriptor)
268
+ runtimeTools.push(Object.freeze({ definition, descriptor }));
269
+ }
270
+ return Object.freeze({ contractTools, runtimeTools: Object.freeze(runtimeTools) });
271
+ }
272
+
273
+ // src/tools/mcp/register.ts
274
+ import {
275
+ isCallToolResult
276
+ } from "@modelcontextprotocol/server";
277
+ import { z as z2 } from "zod";
278
+
279
+ // src/tools/mcp/round.ts
280
+ import {
281
+ acceptedContent,
282
+ inputRequired,
283
+ inputResponse
284
+ } from "@modelcontextprotocol/server";
285
+ import { z } from "zod";
286
+
287
+ // src/tools/mcp/round-context.ts
288
+ import {
289
+ CLIENT_INFO_META_KEY,
290
+ PROTOCOL_VERSION_META_KEY
291
+ } from "@modelcontextprotocol/server";
292
+
293
+ // src/tools/mcp/progress.ts
294
+ function mcpProgressToken(context) {
295
+ const token = context.mcpReq._meta?.progressToken;
296
+ return typeof token === "string" || typeof token === "number" ? token : undefined;
297
+ }
298
+ function createMcpProgressReporter(context) {
299
+ const token = mcpProgressToken(context);
300
+ if (token === undefined)
301
+ return async () => {
302
+ return;
303
+ };
304
+ let sent = 0;
305
+ let highWater = 0;
306
+ return async (update) => {
307
+ sent += 1;
308
+ const value = update.progress ?? Math.max(sent, highWater + 1);
309
+ highWater = Math.max(highWater, value);
310
+ try {
311
+ await context.mcpReq.notify({
312
+ method: "notifications/progress",
313
+ params: {
314
+ progressToken: token,
315
+ progress: value,
316
+ ...update.total !== undefined && { total: update.total },
317
+ ...update.message !== undefined && { message: update.message }
318
+ }
319
+ });
320
+ } catch {}
321
+ };
322
+ }
323
+
324
+ // src/tools/mcp/round-context.ts
325
+ function transportContext(context, toolName, outcome, round) {
326
+ const protocolVersionValue = isRecord(context.mcpReq.envelope) ? Reflect.get(context.mcpReq.envelope, PROTOCOL_VERSION_META_KEY) : undefined;
327
+ const protocolVersion = typeof protocolVersionValue === "string" ? protocolVersionValue : undefined;
328
+ const clientInfoValue = isRecord(context.mcpReq.envelope) ? Reflect.get(context.mcpReq.envelope, CLIENT_INFO_META_KEY) : undefined;
329
+ const clientInfo = isRecord(clientInfoValue) && typeof clientInfoValue.name === "string" && typeof clientInfoValue.version === "string" ? { name: clientInfoValue.name, version: clientInfoValue.version } : undefined;
330
+ const token = mcpProgressToken(context);
331
+ return {
332
+ signal: context.mcpReq.signal,
333
+ mcp: {
334
+ era: context.mcpReq.envelope ? "modern" : "legacy",
335
+ method: context.mcpReq.method,
336
+ toolName,
337
+ ...protocolVersion !== undefined && { protocolVersion },
338
+ ...clientInfo !== undefined && { clientInfo },
339
+ ...outcome !== undefined && { outcome },
340
+ ...round !== undefined && { round },
341
+ ...token !== undefined && { progressToken: token }
342
+ },
343
+ reportProgress: createMcpProgressReporter(context)
344
+ };
345
+ }
346
+ function operationIdentity(tool) {
347
+ return {
348
+ toolName: tool.name,
349
+ serviceName: tool.method.serviceName,
350
+ action: tool.method.key,
351
+ method: tool.method.method,
352
+ ...tool.method.scope !== undefined && { scope: tool.method.scope }
353
+ };
354
+ }
355
+ function sameIdentity(left, right) {
356
+ return left.toolName === right.toolName && left.serviceName === right.serviceName && left.action === right.action && left.method === right.method && left.scope === right.scope;
357
+ }
358
+ async function runRoundSuccess(tool, rawArgs, runTool, context) {
359
+ return runTool({
360
+ ...tool,
361
+ method: {
362
+ ...tool.method,
363
+ outputSchema: undefined,
364
+ handler: () => {
365
+ return;
366
+ }
367
+ }
368
+ }, rawArgs, context);
369
+ }
370
+ async function runRoundFailure(tool, rawArgs, runTool, context, code, message) {
371
+ return runTool({
372
+ ...tool,
373
+ method: {
374
+ ...tool.method,
375
+ outputSchema: undefined,
376
+ handler: () => {
377
+ throw new AppError2(code, message, 400);
378
+ }
379
+ }
380
+ }, rawArgs, context);
381
+ }
382
+ async function failedResolution(options) {
383
+ const result = await runRoundFailure(options.tool, options.rawArgs, options.runTool, options.context, options.code, options.message);
384
+ return { kind: "response", response: options.formatFailure(result) };
385
+ }
386
+
387
+ // src/tools/mcp/round.ts
388
+ function roundPlanDigest(requests) {
389
+ return argumentsDigest({
390
+ plan: requests.map((request) => ({
391
+ key: request.key,
392
+ message: request.message,
393
+ schema: z.toJSONSchema(request.schema, { io: "input", unrepresentable: "any" })
394
+ }))
395
+ });
396
+ }
397
+ async function resolveRequests(options, policy) {
398
+ const declared = staticInputRounds(policy);
399
+ if (declared)
400
+ return { requests: declared, guarded: false };
401
+ const parsed = parseToolCallArguments(options.tool.method, options.rawArgs, {
402
+ coerceJson: options.coerceJsonArgs ?? true,
403
+ toolSurface: true
404
+ });
405
+ if (!parsed.ok) {
406
+ return { requests: [], guarded: false };
407
+ }
408
+ const resolver = policy.inputRequired;
409
+ const resolved = await resolver({ params: parsed.params, input: parsed.input });
410
+ validateResolvedInputRounds(options.tool, resolved, options.runtime?.maxRounds ?? 1);
411
+ return { requests: resolved, guarded: false };
412
+ }
413
+ async function askRound(runtime, context, request, state) {
414
+ const requestState = await runtime.codec.mint(state, context);
415
+ return {
416
+ kind: "response",
417
+ response: inputRequired({
418
+ inputRequests: {
419
+ [request.key]: inputRequired.elicit({
420
+ message: request.message,
421
+ requestedSchema: request.schema
422
+ })
423
+ },
424
+ requestState
425
+ })
426
+ };
427
+ }
428
+ function readRoundAnswer(context, request) {
429
+ const view = inputResponse(context.mcpReq.inputResponses, request.key);
430
+ if (view.kind !== "elicit") {
431
+ return {
432
+ refusal: {
433
+ outcome: "invalid",
434
+ code: "INVALID_INPUT_RESPONSE",
435
+ message: "Expected an elicitation response for the current round"
436
+ }
437
+ };
438
+ }
439
+ if (view.action !== "accept") {
440
+ const declined = view.action === "decline";
441
+ return {
442
+ refusal: {
443
+ outcome: declined ? "declined" : "cancelled",
444
+ code: declined ? "INPUT_DECLINED" : "INPUT_CANCELLED",
445
+ message: declined ? "Required input was declined" : "Required input was cancelled"
446
+ }
447
+ };
448
+ }
449
+ const content = acceptedContent(context.mcpReq.inputResponses, request.key, request.schema);
450
+ if (!content) {
451
+ return {
452
+ refusal: {
453
+ outcome: "invalid",
454
+ code: "INVALID_INPUT_RESPONSE",
455
+ message: "Accepted input failed its declared schema"
456
+ }
457
+ };
458
+ }
459
+ return { content };
460
+ }
461
+ async function resolveMcpRound(options) {
462
+ const { policy } = options;
463
+ if (!policy) {
464
+ return {
465
+ kind: "continue",
466
+ context: transportContext(options.context, options.tool.name)
467
+ };
468
+ }
469
+ if (!options.runtime) {
470
+ throw new Error(`[stitchkit] MCP tool "${options.tool.name}" declares inputRequired but no multiRound.state key is configured`);
471
+ }
472
+ validateMcpRoundPolicy(options.tool, policy, {
473
+ stateConfigured: true,
474
+ maxRounds: options.runtime.maxRounds
475
+ });
476
+ const state = options.context.mcpReq.requestState();
477
+ const digest = argumentsDigest(options.rawArgs);
478
+ if (state) {
479
+ const mismatched = !sameIdentity(state.identity, operationIdentity(options.tool)) || state.argumentsDigest !== digest || !Number.isInteger(state.round) || state.round < 0 || state.round >= options.runtime.maxRounds || !isRecord(state.accepted);
480
+ if (mismatched) {
481
+ return failedResolution({
482
+ ...options,
483
+ context: transportContext(options.context, options.tool.name, "invalid", Number.isInteger(state.round) ? state.round : 0),
484
+ code: "INVALID_REQUEST_STATE",
485
+ message: "Continuation state does not match this operation, arguments or round"
486
+ });
487
+ }
488
+ }
489
+ const dynamic = staticInputRounds(policy) === undefined;
490
+ if (dynamic) {
491
+ const guarded = await runRoundSuccess(options.tool, options.rawArgs, options.runTool, transportContext(options.context, options.tool.name, "input_required", state?.round ?? 0));
492
+ if (!guarded.ok)
493
+ return { kind: "response", response: options.formatFailure(guarded) };
494
+ }
495
+ const resolution = await resolveRequests(options, policy);
496
+ if ("failure" in resolution)
497
+ return resolution.failure;
498
+ const requests = resolution.requests;
499
+ const alreadyGuarded = dynamic;
500
+ const planDigest = roundPlanDigest(requests);
501
+ if (requests.length === 0 && !state) {
502
+ return {
503
+ kind: "continue",
504
+ context: transportContext(options.context, options.tool.name)
505
+ };
506
+ }
507
+ if (!state) {
508
+ if (isRecord(options.context.mcpReq.inputResponses)) {
509
+ return failedResolution({
510
+ ...options,
511
+ context: transportContext(options.context, options.tool.name, "invalid", 0),
512
+ code: "INVALID_REQUEST_STATE",
513
+ message: "Input responses require a valid continuation state"
514
+ });
515
+ }
516
+ if (!alreadyGuarded) {
517
+ const guarded = await runRoundSuccess(options.tool, options.rawArgs, options.runTool, transportContext(options.context, options.tool.name, "input_required", 0));
518
+ if (!guarded.ok) {
519
+ return { kind: "response", response: options.formatFailure(guarded) };
520
+ }
521
+ }
522
+ const request = requests[0];
523
+ if (!request)
524
+ throw new Error("[stitchkit] validated MRTR policy has no first round");
525
+ return askRound(options.runtime, options.context, request, {
526
+ identity: operationIdentity(options.tool),
527
+ argumentsDigest: digest,
528
+ round: 0,
529
+ accepted: {},
530
+ planDigest
531
+ });
532
+ }
533
+ if (state.planDigest !== planDigest || state.round >= requests.length) {
534
+ return failedResolution({
535
+ ...options,
536
+ context: transportContext(options.context, options.tool.name, "invalid", state.round),
537
+ code: "INVALID_REQUEST_STATE",
538
+ message: state.round >= requests.length ? "Continuation state does not match this operation, arguments or round" : "The questions for this call changed between rounds"
539
+ });
540
+ }
541
+ const request = requests[state.round];
542
+ if (!request)
543
+ throw new Error("[stitchkit] validated MRTR state points outside its policy");
544
+ const answer = readRoundAnswer(options.context, request);
545
+ if ("refusal" in answer) {
546
+ return failedResolution({
547
+ ...options,
548
+ context: transportContext(options.context, options.tool.name, answer.refusal.outcome, state.round),
549
+ code: answer.refusal.code,
550
+ message: answer.refusal.message
551
+ });
552
+ }
553
+ const { content } = answer;
554
+ const accepted = { ...state.accepted, [request.key]: content };
555
+ const nextRound = state.round + 1;
556
+ const nextRequest = requests[nextRound];
557
+ if (nextRequest) {
558
+ if (!alreadyGuarded) {
559
+ const guarded = await runRoundSuccess(options.tool, options.rawArgs, options.runTool, transportContext(options.context, options.tool.name, "input_required", nextRound));
560
+ if (!guarded.ok) {
561
+ return { kind: "response", response: options.formatFailure(guarded) };
562
+ }
563
+ }
564
+ return askRound(options.runtime, options.context, nextRequest, {
565
+ identity: state.identity,
566
+ argumentsDigest: state.argumentsDigest,
567
+ round: nextRound,
568
+ accepted,
569
+ planDigest
570
+ });
571
+ }
572
+ return {
573
+ kind: "continue",
574
+ context: {
575
+ ...transportContext(options.context, options.tool.name, "complete", state.round),
576
+ mcpInput: accepted
577
+ }
578
+ };
579
+ }
580
+
581
+ // src/tools/mcp/trace.ts
582
+ import {
583
+ BAGGAGE_META_KEY,
584
+ TRACEPARENT_META_KEY,
585
+ TRACESTATE_META_KEY
586
+ } from "@modelcontextprotocol/server";
587
+ function runInMcpRequestContext(context, toolName, body) {
588
+ const ambient = getRequestContext2();
589
+ const metadata = context.mcpReq._meta;
590
+ const propagation = metadata ? {
591
+ traceparent: metadata[TRACEPARENT_META_KEY],
592
+ tracestate: metadata[TRACESTATE_META_KEY],
593
+ baggage: metadata[BAGGAGE_META_KEY]
594
+ } : undefined;
595
+ const request = {
596
+ ...ambient ?? {
597
+ source: "mcp",
598
+ method: "MCP",
599
+ path: `/mcp/${toolName}`,
600
+ startedAt: process.hrtime.bigint()
601
+ },
602
+ trace: resolvePropagationContext2(propagation, ambient?.trace),
603
+ source: "mcp",
604
+ method: "MCP",
605
+ path: `/mcp/${toolName}`,
606
+ startedAt: process.hrtime.bigint(),
607
+ dimensions: ambient?.dimensions ? { ...ambient.dimensions } : undefined,
608
+ error: undefined
609
+ };
610
+ return runWithRequestContext2(request, body);
611
+ }
612
+
613
+ // src/tools/mcp/register.ts
614
+ function registerMcpTool(server, presentation, descriptor, config) {
615
+ const { mountable } = descriptor;
616
+ const toolConfig = {
617
+ description: presentation.description,
618
+ inputSchema: z2.looseObject({}).meta(presentationMetadata(descriptor.inputSchema))
619
+ };
620
+ if (descriptor.outputSchema)
621
+ toolConfig.outputSchema = descriptor.outputSchema;
622
+ if (presentation.annotations)
623
+ toolConfig.annotations = presentation.annotations;
624
+ if (presentation.ui) {
625
+ toolConfig._meta = {
626
+ ui: presentation.ui,
627
+ "ui/resourceUri": presentation.ui.resourceUri
628
+ };
629
+ }
630
+ const { name } = presentation;
631
+ const format = config.formatResult;
632
+ server.registerTool(name, stampToolRegistration(toolConfig, config.catalog), async (rawArgs, mcpContext) => stampToolResult(await runInMcpRequestContext(mcpContext, name, async () => {
633
+ const args = isRecord(rawArgs) ? rawArgs : {};
634
+ const startedAt = Date.now();
635
+ try {
636
+ const round = await resolveMcpRound({
637
+ tool: mountable,
638
+ rawArgs: args,
639
+ context: mcpContext,
640
+ policy: mountable.method.mcp,
641
+ runtime: config.multiRoundRuntime,
642
+ runTool: config.runTool,
643
+ formatFailure: (result) => format(result, "none", name)
644
+ });
645
+ if (round.kind === "response")
646
+ return round.response;
647
+ const { present } = presentation;
648
+ if (!present) {
649
+ const result = await config.runTool(mountable, args, round.context);
650
+ return format(result, descriptor.outputMode, name);
651
+ }
652
+ const result = await config.runTool(mountable, args, round.context, {
653
+ finalizeOutput: async (data) => {
654
+ const presented = await present(data);
655
+ if (!isRecord(presented)) {
656
+ throw new AppError2("INTERNAL_SERVER_ERROR", "Runtime MCP presenter did not return a result", 500);
657
+ }
658
+ if ("structuredContent" in presented || "isError" in presented) {
659
+ throw new AppError2("INTERNAL_SERVER_ERROR", "Runtime MCP presenter cannot set framework-owned structuredContent or isError", 500);
660
+ }
661
+ const structured = format({ ok: true, data }, descriptor.outputMode, name).structuredContent;
662
+ const response = structured === undefined ? presented : { ...presented, structuredContent: structured };
663
+ if (!isCallToolResult(response)) {
664
+ throw new AppError2("INTERNAL_SERVER_ERROR", "Runtime MCP presenter returned an invalid CallToolResult", 500);
665
+ }
666
+ return response;
667
+ }
668
+ });
669
+ if (!result.ok)
670
+ return format(result, "none", name);
671
+ if (!isCallToolResult(result.data)) {
672
+ throw new Error("[stitchkit] Runtime MCP presenter invariant failed");
673
+ }
674
+ return result.data;
675
+ } catch (err) {
676
+ const context = {
677
+ ...config.context,
678
+ ...transportContext(mcpContext, name),
679
+ source: "mcp"
680
+ };
681
+ const result = toolResultFromError(err);
682
+ await observed("onToolError", () => config.hooks?.onToolError?.({
683
+ toolName: name,
684
+ error: err,
685
+ context,
686
+ endpoint: mountable.method
687
+ }));
688
+ await observed("afterToolCall", () => config.hooks?.afterToolCall?.({
689
+ toolName: name,
690
+ args,
691
+ result,
692
+ durationMs: Date.now() - startedAt,
693
+ error: err,
694
+ context,
695
+ endpoint: mountable.method
696
+ }));
697
+ return format(result, "none", name);
698
+ }
699
+ }), config.catalog));
700
+ }
701
+
702
+ // src/tools/mcp/runtime-tools.ts
703
+ function mountPreparedRuntimeMcp(server, tools, config) {
704
+ const runTool = createToolRunner({
705
+ source: "mcp",
706
+ toolSurface: true,
707
+ context: config.context,
708
+ hooks: config.hooks,
709
+ lifecycle: config.lifecycle,
710
+ coerceJsonArgs: config.coerceJsonArgs,
711
+ onOutputStrip: config.onOutputStrip
712
+ });
713
+ for (const { definition, descriptor } of tools) {
714
+ const present = definition.present?.mcp;
715
+ registerMcpTool(server, {
716
+ name: definition.name,
717
+ description: definition.description,
718
+ annotations: definition.annotations,
719
+ ui: definition.ui,
720
+ ...present && { present }
721
+ }, descriptor, {
722
+ runTool,
723
+ formatResult: config.formatResult,
724
+ catalog: config.catalog,
725
+ multiRoundRuntime: config.multiRoundRuntime,
726
+ hooks: config.hooks,
727
+ context: config.context
728
+ });
729
+ }
730
+ }
731
+
732
+ // src/tools/mcp/mount.ts
733
+ function validateMcpSchemas2(config) {
734
+ prepareMcpSurface(config.services, {
735
+ extend: config.extend,
736
+ flattenUnionInput: config.flattenUnionInput,
737
+ schemaValidation: config,
738
+ logger: config.logger
739
+ });
740
+ }
741
+ function mountMcp2(mcpServer, services, config = {}) {
742
+ const prepared = prepareMcpSurface(services, {
743
+ extend: config.extend,
744
+ flattenUnionInput: config.flattenUnionInput,
745
+ schemaValidation: config.schemaValidation,
746
+ logger: config.logger
747
+ });
748
+ mountPreparedMcp(mcpServer, prepared, {
749
+ ...config,
750
+ catalog: mcpCatalogStamp({ contractTools: prepared, runtimeTools: [] })
751
+ });
752
+ }
753
+ function mountPreparedMcp(mcpServer, prepared, config = {}) {
754
+ const runTool = createToolRunner({
755
+ source: "mcp",
756
+ toolSurface: true,
757
+ extend: config.extend,
758
+ context: config.context,
759
+ hooks: config.hooks,
760
+ lifecycle: config.lifecycle,
761
+ coerceJsonArgs: config.coerceJsonArgs,
762
+ onOutputStrip: config.onOutputStrip
763
+ });
764
+ for (const descriptor of prepared) {
765
+ const { method, name } = descriptor.mountable;
766
+ registerMcpTool(mcpServer, {
767
+ name,
768
+ description: method.desc,
769
+ annotations: method.annotations,
770
+ ui: method.ui
771
+ }, descriptor, {
772
+ runTool,
773
+ formatResult: (result, mode, toolName) => formatMcpResult(result, mode, toolName, config.errorHint),
774
+ catalog: config.catalog,
775
+ multiRoundRuntime: config.multiRoundRuntime,
776
+ hooks: config.hooks,
777
+ context: config.context
778
+ });
779
+ }
780
+ }
781
+ function buildMcpServer2(config, auth) {
782
+ if (!isMcpServerBuildConfig(config)) {
783
+ throw new TypeError("[stitchkit] buildMcpServer requires a valid config object");
784
+ }
785
+ let surface;
786
+ if (config.surfaces) {
787
+ const key = config.selectSurface(auth);
788
+ if (!Object.hasOwn(config.surfaces, key)) {
789
+ throw new Error(`[stitchkit] Unknown MCP surface "${key}"`);
790
+ }
791
+ const selected = config.surfaces[key];
792
+ if (!selected)
793
+ throw new Error(`[stitchkit] Unknown MCP surface "${key}"`);
794
+ surface = selected;
795
+ } else {
796
+ const services = typeof config.services === "function" ? config.services(auth) : config.services;
797
+ const runtimeTools = typeof config.runtimeTools === "function" ? config.runtimeTools(auth) : config.runtimeTools;
798
+ surface = { services, runtimeTools };
799
+ }
800
+ const prepared = prepareMcpServerSurface(surface, {
801
+ extend: config.extend,
802
+ flattenUnionInput: config.flattenUnionInput,
803
+ schemaValidation: config.schemaValidation,
804
+ logger: config.logger,
805
+ multiRound: {
806
+ stateConfigured: config.multiRound !== undefined,
807
+ maxRounds: config.multiRound?.serving?.maxRounds ?? 10
808
+ }
809
+ });
810
+ return buildMcpServerFromPrepared(config, auth, prepared);
811
+ }
812
+ function isMcpServerBuildConfig(value) {
813
+ return isRecord(value) && isRecord(value.serverInfo);
814
+ }
815
+ function buildMcpServerFromPrepared(config, auth, prepared) {
816
+ let roundCodec;
817
+ if (config.multiRound) {
818
+ const principal = config.multiRound.state.principal(auth);
819
+ roundCodec = createRequestStateCodec({
820
+ key: config.multiRound.state.key,
821
+ ttlSeconds: config.multiRound.state.ttlSeconds,
822
+ bind: (mcpContext) => `${mcpContext.mcpReq.method}\x00${principal}`
823
+ });
824
+ }
825
+ const dynamicSurface = !config.surfaces && (typeof config.services === "function" || typeof config.runtimeTools === "function");
826
+ const serverOptions = {
827
+ ...config.instructions !== undefined && { instructions: config.instructions },
828
+ ...!dynamicSurface && config.cache?.operations !== undefined && {
829
+ cacheHints: config.cache.operations
830
+ },
831
+ ...config.multiRound?.serving !== undefined && {
832
+ inputRequired: config.multiRound.serving
833
+ },
834
+ ...roundCodec !== undefined && {
835
+ requestState: { verify: roundCodec.verify }
836
+ }
837
+ };
838
+ const server = new McpServer(config.serverInfo, serverOptions);
839
+ const context = config.context?.(auth);
840
+ const catalog = mcpCatalogStamp(prepared);
841
+ mountPreparedMcp(server, prepared.contractTools, {
842
+ catalog,
843
+ context,
844
+ hooks: config.hooks,
845
+ lifecycle: config.lifecycle,
846
+ extend: config.extend,
847
+ schemaValidation: config.schemaValidation,
848
+ logger: config.logger,
849
+ coerceJsonArgs: config.coerceJsonArgs,
850
+ flattenUnionInput: config.flattenUnionInput,
851
+ errorHint: config.errorHint,
852
+ onOutputStrip: config.onOutputStrip,
853
+ ...roundCodec !== undefined && {
854
+ multiRoundRuntime: {
855
+ codec: roundCodec,
856
+ maxRounds: config.multiRound?.serving?.maxRounds ?? 10
857
+ }
858
+ }
859
+ });
860
+ mountPreparedRuntimeMcp(server, prepared.runtimeTools, {
861
+ catalog,
862
+ context,
863
+ hooks: config.hooks,
864
+ lifecycle: config.lifecycle,
865
+ coerceJsonArgs: config.coerceJsonArgs,
866
+ onOutputStrip: config.onOutputStrip,
867
+ formatResult: (result, mode, toolName) => formatMcpResult(result, mode, toolName, config.errorHint),
868
+ ...roundCodec !== undefined && {
869
+ multiRoundRuntime: {
870
+ codec: roundCodec,
871
+ maxRounds: config.multiRound?.serving?.maxRounds ?? 10
872
+ }
873
+ }
874
+ });
875
+ config.rawTools?.(server, auth);
876
+ for (const resource of config.resources ?? []) {
877
+ mountMcpResource2(server, resource);
878
+ }
879
+ return server;
880
+ }
881
+ function mountMcpResource2(server, resource) {
882
+ server.registerResource(resource.name, resource.uri, {
883
+ mimeType: resource.mimeType ?? RESOURCE_MIME_TYPE2,
884
+ ...resource.cacheHint !== undefined && { cacheHint: resource.cacheHint }
885
+ }, async () => ({
886
+ contents: [
887
+ {
888
+ uri: resource.uri,
889
+ mimeType: resource.mimeType ?? RESOURCE_MIME_TYPE2,
890
+ text: await resource.read(),
891
+ ...resource.ui && { _meta: { ui: resource.ui } }
892
+ }
893
+ ]
894
+ }));
895
+ }
896
+
897
+ // src/tools/mcp/handler.ts
898
+ import {
899
+ createMcpHandler as createSdkMcpHandler,
900
+ hostHeaderValidationResponse,
901
+ localhostAllowedHostnames,
902
+ localhostAllowedOrigins,
903
+ originValidationResponse
904
+ } from "@modelcontextprotocol/server";
905
+ function jsonRpcError(code, message, status, headers) {
906
+ return Response.json({ jsonrpc: "2.0", error: { code, message }, id: null }, { status, headers });
907
+ }
908
+ function hostValidationResponse(request, allowedHosts) {
909
+ if (request.headers.has("host")) {
910
+ return hostHeaderValidationResponse(request, [...allowedHosts]);
911
+ }
912
+ const hostname = new URL(request.url).hostname;
913
+ if (allowedHosts.includes(hostname))
914
+ return;
915
+ return jsonRpcError(-32000, "Invalid Host header", 403);
916
+ }
917
+ function prepareStaticSurface(config) {
918
+ const preparation = {
919
+ logger: config.logger,
920
+ extend: config.extend,
921
+ flattenUnionInput: config.flattenUnionInput,
922
+ schemaValidation: config.schemaValidation,
923
+ multiRound: {
924
+ stateConfigured: config.multiRound !== undefined,
925
+ maxRounds: config.multiRound?.serving?.maxRounds ?? 10
926
+ }
927
+ };
928
+ if (config.surfaces && config.selectSurface) {
929
+ const preparedByKey = new Map;
930
+ const preparedBySurface = new WeakMap;
931
+ for (const key in config.surfaces) {
932
+ if (!Object.hasOwn(config.surfaces, key))
933
+ continue;
934
+ const surface = config.surfaces[key];
935
+ if (!surface)
936
+ continue;
937
+ const existing = preparedBySurface.get(surface);
938
+ const prepared = existing ?? prepareMcpServerSurface(surface, preparation);
939
+ if (!existing)
940
+ preparedBySurface.set(surface, prepared);
941
+ preparedByKey.set(key, prepared);
942
+ }
943
+ return (auth) => {
944
+ const key = config.selectSurface(auth);
945
+ const prepared = preparedByKey.get(key);
946
+ if (!prepared)
947
+ throw new Error(`[stitchkit] Unknown MCP surface "${key}"`);
948
+ return buildMcpServerFromPrepared(config, auth, prepared);
949
+ };
950
+ }
951
+ if (config.services && typeof config.services !== "function" && typeof config.runtimeTools !== "function") {
952
+ const prepared = prepareMcpServerSurface({ services: config.services, runtimeTools: config.runtimeTools }, preparation);
953
+ return (auth) => buildMcpServerFromPrepared(config, auth, prepared);
954
+ }
955
+ return null;
956
+ }
957
+ function createMcpHandler2(config) {
958
+ const buildPrepared = prepareStaticSurface(config);
959
+ const authByCarrier = new WeakMap;
960
+ let closed = false;
961
+ let resolveClosed;
962
+ const closedSignal = new Promise((resolve) => {
963
+ resolveClosed = resolve;
964
+ });
965
+ const sdkHandler = createSdkMcpHandler(({ authInfo }) => {
966
+ const resolved = authInfo ? authByCarrier.get(authInfo) : undefined;
967
+ if (!resolved) {
968
+ throw new Error("[stitchkit] MCP request reached the SDK without resolved auth");
969
+ }
970
+ const auth = resolved.value;
971
+ return buildPrepared ? buildPrepared(auth) : buildMcpServer2(config, auth);
972
+ }, {
973
+ legacy: config.legacy === "reject" ? "reject" : "stateless",
974
+ maxSubscriptions: 0
975
+ });
976
+ const unauthorized = () => {
977
+ const headers = config.protectedResource ? { "WWW-Authenticate": wwwAuthenticateHeader2(config.protectedResource.resource) } : undefined;
978
+ return jsonRpcError(-32001, "Authorization required", 401, headers);
979
+ };
980
+ const observeRejection = (request, response) => {
981
+ if (response.status < 400 || !config.onTransportRejected)
982
+ return response;
983
+ Promise.resolve(config.onTransportRejected({ request, response: response.clone() })).catch(() => {
984
+ return;
985
+ });
986
+ return response;
987
+ };
988
+ return {
989
+ fetch: async (request) => {
990
+ if (closed) {
991
+ return observeRejection(request, jsonRpcError(-32000, "MCP handler is closed", 503));
992
+ }
993
+ const allowedHosts = config.security?.allowedHosts ?? localhostAllowedHostnames();
994
+ const hostRejection = hostValidationResponse(request, allowedHosts);
995
+ if (hostRejection)
996
+ return observeRejection(request, hostRejection);
997
+ const allowedOrigins = config.security?.allowedOrigins ?? localhostAllowedOrigins();
998
+ const originRejection = originValidationResponse(request, [...allowedOrigins]);
999
+ if (originRejection)
1000
+ return observeRejection(request, originRejection);
1001
+ const auth = await Promise.race([
1002
+ Promise.resolve(config.auth(request)),
1003
+ closedSignal.then(() => null)
1004
+ ]);
1005
+ if (closed) {
1006
+ return observeRejection(request, jsonRpcError(-32000, "MCP handler is closed", 503));
1007
+ }
1008
+ if (auth == null)
1009
+ return observeRejection(request, unauthorized());
1010
+ const authCarrier = {
1011
+ token: "",
1012
+ clientId: config.multiRound?.state.principal(auth) ?? "stitchkit",
1013
+ scopes: []
1014
+ };
1015
+ authByCarrier.set(authCarrier, { value: auth });
1016
+ try {
1017
+ return observeRejection(request, await sdkHandler.fetch(request, { authInfo: authCarrier }));
1018
+ } finally {
1019
+ authByCarrier.delete(authCarrier);
1020
+ }
1021
+ },
1022
+ close: async () => {
1023
+ if (closed)
1024
+ return;
1025
+ closed = true;
1026
+ resolveClosed?.();
1027
+ await sdkHandler.close();
1028
+ }
1029
+ };
1030
+ }
1031
+ function createMcpHttpRoute2(config) {
1032
+ return {
1033
+ method: "ALL",
1034
+ path: config.path,
1035
+ handler: (request) => config.handler.fetch(request)
1036
+ };
1037
+ }
1038
+
1039
+ // src/tools/mcp/stdio.ts
1040
+ import { serveStdio } from "@modelcontextprotocol/server/stdio";
1041
+ async function createStdioMcpServer2(config) {
1042
+ const auth = await config.auth;
1043
+ return serveStdio(() => buildMcpServer2(config, auth), {
1044
+ legacy: config.legacy ?? "serve",
1045
+ maxSubscriptions: 0
1046
+ });
1047
+ }
1048
+
1049
+ // src/tools/mcp/stdio-signals.ts
1050
+ var bound = new WeakSet;
1051
+ function bindStdioProcessSignals2(handle, options = {}) {
1052
+ if (bound.has(handle)) {
1053
+ throw new Error("[stitchkit] bindStdioProcessSignals: this stdio handle is already bound; close the first binding before creating another");
1054
+ }
1055
+ bound.add(handle);
1056
+ const signals = [...new Set(options.signals ?? DEFAULT_PROCESS_SIGNALS)];
1057
+ const source = options.signalSource ?? defaultSignalSource;
1058
+ const handlers = [];
1059
+ let started = false;
1060
+ let sameTurnAsStart = false;
1061
+ let closed = false;
1062
+ let resolveChain = () => {
1063
+ return;
1064
+ };
1065
+ let rejectChain = () => {
1066
+ return;
1067
+ };
1068
+ const promise = new Promise((resolve, reject) => {
1069
+ resolveChain = resolve;
1070
+ rejectChain = reject;
1071
+ });
1072
+ promise.catch(() => {
1073
+ return;
1074
+ });
1075
+ const removeListeners = () => {
1076
+ if (closed)
1077
+ return;
1078
+ closed = true;
1079
+ for (const [signal, handler] of handlers)
1080
+ source.off(signal, handler);
1081
+ if (!started)
1082
+ bound.delete(handle);
1083
+ };
1084
+ const close = () => {
1085
+ const hadStarted = started;
1086
+ removeListeners();
1087
+ if (!hadStarted)
1088
+ resolveChain();
1089
+ };
1090
+ const run = async (signal) => {
1091
+ try {
1092
+ await options.onClose?.(signal);
1093
+ } catch (error) {
1094
+ reportSignalError("prepare", error, options.onError);
1095
+ }
1096
+ try {
1097
+ await handle.close();
1098
+ } catch (error) {
1099
+ rejectChain(error);
1100
+ reportSignalError("close", error, options.onError);
1101
+ removeListeners();
1102
+ return;
1103
+ }
1104
+ resolveChain();
1105
+ try {
1106
+ await options.onComplete?.();
1107
+ } catch (error) {
1108
+ reportSignalError("complete", error, options.onError);
1109
+ } finally {
1110
+ removeListeners();
1111
+ }
1112
+ };
1113
+ const onSignal = (signal) => {
1114
+ if (!started) {
1115
+ started = true;
1116
+ sameTurnAsStart = true;
1117
+ queueMicrotask(() => {
1118
+ sameTurnAsStart = false;
1119
+ });
1120
+ run(signal).catch((error) => reportSignalError("close", error, options.onError));
1121
+ return;
1122
+ }
1123
+ if (sameTurnAsStart)
1124
+ return;
1125
+ removeListeners();
1126
+ guardSignalCallback(() => options.onRepeatedSignal?.(signal), "close", options.onError);
1127
+ let restored = false;
1128
+ try {
1129
+ restored = source.raiseDefault(signal);
1130
+ } catch (error) {
1131
+ reportSignalError("close", error, options.onError);
1132
+ }
1133
+ if (!restored) {
1134
+ guardSignalCallback(() => options.onEscalationBlocked?.(signal), "close", options.onError);
1135
+ }
1136
+ };
1137
+ for (const signal of signals) {
1138
+ const handler = () => onSignal(signal);
1139
+ handlers.push([signal, handler]);
1140
+ source.on(signal, handler);
1141
+ }
1142
+ return { promise, close };
1143
+ }
1144
+
1145
+ export { PROTECTED_RESOURCE_PATH2, protectedResourceMetadataUrl2, wwwAuthenticateHeader2, oauthProtectedResourceRoute2, RESOURCE_MIME_TYPE2, EXT_APPS_BUNDLE_PLACEHOLDER2, inlineMcpAppBundle2, MCP_CATALOG_META_KEY2, mcpCatalogMeta2, readMcpCatalogStamp2, createMcpProgressReporter, validateMcpSchemas2, mountMcp2, buildMcpServer2, mountMcpResource2, createMcpHandler2, createMcpHttpRoute2, createStdioMcpServer2, bindStdioProcessSignals2 };