@code-yeongyu/senpi 2026.10.25-2 → 2026.10.25-4

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 (226) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/bundle/chunks/{args-JXULI4EH.js → args-B444OQBK.js} +1 -1
  3. package/dist/bundle/chunks/{auth-4NUNUSTF.js → auth-C77UQA55.js} +1 -1
  4. package/dist/bundle/chunks/{chunk-XBUSVKWC.js → chunk-2RB5EL4X.js} +1 -1
  5. package/dist/bundle/chunks/{chunk-KUE4AQN6.js → chunk-3QCYBOKN.js} +1 -1
  6. package/dist/bundle/chunks/{chunk-2EFVQWXZ.js → chunk-4MOZXOJX.js} +5 -5
  7. package/dist/bundle/chunks/{chunk-MXZPAUFB.js → chunk-5JJDR6XI.js} +1 -1
  8. package/dist/bundle/chunks/chunk-6VWIMHMH.js +2 -0
  9. package/dist/bundle/chunks/{chunk-Y5OTHMXO.js → chunk-7CNOCJCN.js} +1 -1
  10. package/dist/bundle/chunks/chunk-AX72VNRS.js +27 -0
  11. package/dist/bundle/chunks/{chunk-VNN2V74Y.js → chunk-BZ7726QO.js} +1 -1
  12. package/dist/bundle/chunks/{chunk-J2U5VDG7.js → chunk-C2MQ4QXN.js} +30 -30
  13. package/dist/bundle/chunks/{chunk-JRXKLIEQ.js → chunk-DODH3N6U.js} +5 -5
  14. package/dist/bundle/chunks/{chunk-EHQXLHRB.js → chunk-E6D4RIT7.js} +2 -2
  15. package/dist/bundle/chunks/chunk-J37GRED2.js +5 -0
  16. package/dist/bundle/chunks/chunk-JMOUU75H.js +2 -0
  17. package/dist/bundle/chunks/chunk-JPGT64RV.js +2 -0
  18. package/dist/bundle/chunks/chunk-KKD4CYCI.js +3 -0
  19. package/dist/bundle/chunks/{chunk-YN7ELQ43.js → chunk-KNVNKDER.js} +1 -1
  20. package/dist/bundle/chunks/chunk-KXB5WIS5.js +4 -0
  21. package/dist/bundle/chunks/chunk-L73UYAH5.js +3 -0
  22. package/dist/bundle/chunks/{chunk-G2UETS2R.js → chunk-LOIF6PHI.js} +4 -4
  23. package/dist/bundle/chunks/chunk-LTU6UFEX.js +2 -0
  24. package/dist/bundle/chunks/chunk-N7IN46Q5.js +27 -0
  25. package/dist/bundle/chunks/{chunk-LYGAV6KU.js → chunk-PT6JJUN2.js} +1 -1
  26. package/dist/bundle/chunks/{chunk-WFXUCVIR.js → chunk-SBGIXMX3.js} +2 -5
  27. package/dist/bundle/chunks/{chunk-W2BKZBYJ.js → chunk-STMXIBXF.js} +2 -2
  28. package/dist/bundle/chunks/chunk-TBU6VYHN.js +13 -0
  29. package/dist/bundle/chunks/{chunk-RXKNOEQQ.js → chunk-V2UYXTRR.js} +1 -1
  30. package/dist/bundle/chunks/{chunk-U67ML6CQ.js → chunk-WD6WBYC5.js} +1 -1
  31. package/dist/bundle/chunks/chunk-WJOT3DFL.js +2 -0
  32. package/dist/bundle/chunks/chunk-XMJKZK5H.js +2 -0
  33. package/dist/bundle/chunks/chunk-YI7SPFDN.js +5 -0
  34. package/dist/bundle/chunks/cli-main-YYH3IBC6.js +3 -0
  35. package/dist/bundle/chunks/{client-KWTLTKVV.js → client-WTYEICLH.js} +3 -3
  36. package/dist/bundle/chunks/dispatch-MXG5YWLV.js +9 -0
  37. package/dist/bundle/chunks/{help-fast-path-ZHZGXAUG.js → help-fast-path-3Z26DQSV.js} +1 -1
  38. package/dist/bundle/chunks/host-command-TDLLHXVI.js +2 -0
  39. package/dist/bundle/chunks/host-lifecycle-3VHHIGDL.js +3 -0
  40. package/dist/bundle/chunks/host-lifecycle.js +8 -6
  41. package/dist/bundle/chunks/host-session-command-56XF6ACS.js +20 -0
  42. package/dist/bundle/chunks/{host-socket-request-AS2OCRLP.js → host-socket-request-QL2WEZTN.js} +1 -1
  43. package/dist/bundle/chunks/interactive-mode-OEPULLZK.js +2 -0
  44. package/dist/bundle/chunks/mcp-presenter-bootstrap-XUSCQVRC.js +2 -0
  45. package/dist/bundle/chunks/{multi-session-host-VTTY5MFT.js → multi-session-host-Z2CW4SDM.js} +12 -9
  46. package/dist/bundle/chunks/{package-manager-cli-DZFOC3MF.js → package-manager-cli-N55U6PEO.js} +1 -1
  47. package/dist/bundle/chunks/rotation-stream-CBGCBYHV.js +2 -0
  48. package/dist/bundle/chunks/rpc-mode-ETXQWEB2.js +2 -0
  49. package/dist/bundle/chunks/{rpc-monitor-source-B5NQ4ZVH.js → rpc-monitor-source-AE3C6EQV.js} +1 -1
  50. package/dist/bundle/chunks/{schedule-command-SLISTG4V.js → schedule-command-VS7CZC2J.js} +1 -1
  51. package/dist/bundle/chunks/session-control-endpoint-4LEIDIVI.js +3 -0
  52. package/dist/bundle/chunks/session-picker-E4KUJDLO.js +2 -0
  53. package/dist/bundle/chunks/session-worker.js +292 -278
  54. package/dist/bundle/chunks/{stdio-IEC635SY.js → stdio-YXDKKKQZ.js} +1 -1
  55. package/dist/bundle/chunks/{streamableHttp-KJI5KE4K.js → streamableHttp-DKIBUWVY.js} +1 -1
  56. package/dist/bundle/chunks/{types-LLNV23LW.js → types-BJUEQ4II.js} +1 -1
  57. package/dist/bundle/chunks/zod-compat-RJDXCU7S.js +2 -0
  58. package/dist/bundle/cli.js +2 -2
  59. package/dist/bundle/host-supervisor.js +8 -6
  60. package/dist/bundle/index.js +1 -1
  61. package/dist/bundle/rpc-entry.js +1 -1
  62. package/dist/bundle/runtime-manifest.json +1 -1
  63. package/dist/cli/args.d.ts +2 -0
  64. package/dist/cli/args.js +9 -0
  65. package/dist/cli/host-command-args.d.ts +1 -0
  66. package/dist/cli/host-command-args.js +7 -1
  67. package/dist/cli/host-command.js +3 -1
  68. package/dist/core/agent-session.js +22 -7
  69. package/dist/core/extensions/builtin/goal/index.js +1 -0
  70. package/dist/core/extensions/builtin/goal/lifecycle-facts.d.ts +3 -0
  71. package/dist/core/extensions/builtin/mcp/catalog.d.ts +3 -1
  72. package/dist/core/extensions/builtin/mcp/catalog.js +2 -0
  73. package/dist/core/extensions/builtin/mcp/connection.d.ts +1 -0
  74. package/dist/core/extensions/builtin/mcp/connection.js +46 -1
  75. package/dist/core/extensions/builtin/mcp/elicitation.d.ts +6 -1
  76. package/dist/core/extensions/builtin/mcp/elicitation.js +54 -2
  77. package/dist/core/extensions/builtin/mcp/expose/call.js +6 -2
  78. package/dist/core/extensions/builtin/mcp/expose/session.d.ts +4 -1
  79. package/dist/core/extensions/builtin/mcp/expose/session.js +7 -0
  80. package/dist/core/extensions/builtin/mcp/health.js +5 -0
  81. package/dist/core/extensions/builtin/mcp/host-registry.js +7 -1
  82. package/dist/core/extensions/builtin/mcp/index.js +2 -0
  83. package/dist/core/extensions/builtin/mcp/modern/client.d.ts +5 -0
  84. package/dist/core/extensions/builtin/mcp/modern/client.js +15 -0
  85. package/dist/core/extensions/builtin/mcp/modern/consent-queue.d.ts +2 -0
  86. package/dist/core/extensions/builtin/mcp/modern/consent-queue.js +59 -0
  87. package/dist/core/extensions/builtin/mcp/modern/diagnostics.d.ts +4 -0
  88. package/dist/core/extensions/builtin/mcp/modern/diagnostics.js +39 -0
  89. package/dist/core/extensions/builtin/mcp/modern/dispatch.js +21 -11
  90. package/dist/core/extensions/builtin/mcp/modern/elicitation.d.ts +14 -0
  91. package/dist/core/extensions/builtin/mcp/modern/elicitation.js +145 -0
  92. package/dist/core/extensions/builtin/mcp/modern/errors.d.ts +21 -0
  93. package/dist/core/extensions/builtin/mcp/modern/errors.js +23 -0
  94. package/dist/core/extensions/builtin/mcp/modern/form-schema.d.ts +20 -0
  95. package/dist/core/extensions/builtin/mcp/modern/form-schema.js +153 -0
  96. package/dist/core/extensions/builtin/mcp/modern/http-fetch.d.ts +3 -1
  97. package/dist/core/extensions/builtin/mcp/modern/http-fetch.js +8 -2
  98. package/dist/core/extensions/builtin/mcp/modern/inputs.d.ts +11 -0
  99. package/dist/core/extensions/builtin/mcp/modern/inputs.js +65 -0
  100. package/dist/core/extensions/builtin/mcp/modern/parameter-headers.d.ts +11 -0
  101. package/dist/core/extensions/builtin/mcp/modern/parameter-headers.js +109 -0
  102. package/dist/core/extensions/builtin/mcp/modern/request.d.ts +20 -0
  103. package/dist/core/extensions/builtin/mcp/modern/request.js +292 -0
  104. package/dist/core/extensions/builtin/mcp/modern/stdio-probe.d.ts +1 -1
  105. package/dist/core/extensions/builtin/mcp/modern/stdio-probe.js +54 -6
  106. package/dist/core/extensions/builtin/mcp/modern/transport.js +80 -14
  107. package/dist/core/extensions/builtin/mcp/modern/wire.js +11 -7
  108. package/dist/core/extensions/builtin/mcp/prompts.d.ts +5 -1
  109. package/dist/core/extensions/builtin/mcp/prompts.js +6 -1
  110. package/dist/core/extensions/builtin/mcp/request-origin.d.ts +47 -0
  111. package/dist/core/extensions/builtin/mcp/request-origin.js +59 -0
  112. package/dist/core/extensions/builtin/mcp/resources.d.ts +4 -1
  113. package/dist/core/extensions/builtin/mcp/resources.js +19 -4
  114. package/dist/core/extensions/builtin/mcp/sdk.lazy.d.ts +3 -1
  115. package/dist/core/extensions/builtin/mcp/sdk.lazy.js +3 -1
  116. package/dist/core/extensions/builtin/mcp/service-register.d.ts +4 -1
  117. package/dist/core/extensions/builtin/mcp/service-register.js +3 -0
  118. package/dist/core/extensions/builtin/mcp/service-types.d.ts +2 -1
  119. package/dist/core/extensions/builtin/mcp/service.d.ts +1 -0
  120. package/dist/core/extensions/builtin/mcp/service.js +79 -4
  121. package/dist/core/extensions/builtin/mcp/shared-connection.js +36 -2
  122. package/dist/core/extensions/builtin/mcp/shared-lease.d.ts +1 -0
  123. package/dist/core/extensions/builtin/mcp/shared-lease.js +9 -0
  124. package/dist/core/extensions/builtin/mcp/transport-sdk.d.ts +3 -2
  125. package/dist/core/extensions/builtin/mcp/transport-sdk.js +56 -25
  126. package/dist/core/extensions/builtin/mcp/wrap.d.ts +7 -0
  127. package/dist/core/extensions/builtin/mcp/wrap.js +6 -0
  128. package/dist/core/extensions/runner.js +3 -2
  129. package/dist/core/extensions/types.d.ts +2 -0
  130. package/dist/core/lifecycle-ledger.d.ts +1 -0
  131. package/dist/core/lifecycle-ledger.js +7 -2
  132. package/dist/core/lifecycle-record.js +3 -0
  133. package/dist/core/lifecycle-session-binding.d.ts +11 -1
  134. package/dist/core/lifecycle-session-binding.js +29 -2
  135. package/dist/core/lifecycle-stop.d.ts +41 -0
  136. package/dist/core/lifecycle-stop.js +92 -0
  137. package/dist/core/mcp-form-types.d.ts +89 -0
  138. package/dist/core/mcp-form-types.js +23 -0
  139. package/dist/core/mcp-mrtr-presentations.d.ts +15 -0
  140. package/dist/core/mcp-mrtr-presentations.js +17 -0
  141. package/dist/core/session-control-actions.js +0 -2
  142. package/dist/core/session-lifecycle-types.d.ts +15 -0
  143. package/dist/core/session-lifecycle.d.ts +17 -1
  144. package/dist/core/session-lifecycle.js +40 -2
  145. package/dist/main.js +5 -1
  146. package/dist/modes/interactive/components/extension-selector.d.ts +1 -0
  147. package/dist/modes/interactive/components/extension-selector.js +4 -0
  148. package/dist/modes/interactive/interactive-mode.d.ts +4 -0
  149. package/dist/modes/interactive/interactive-mode.js +34 -8
  150. package/dist/modes/interactive/mcp-local-presenter.d.ts +5 -0
  151. package/dist/modes/interactive/mcp-local-presenter.js +109 -0
  152. package/dist/modes/interactive/session-control-attach.d.ts +20 -0
  153. package/dist/modes/interactive/session-control-attach.js +13 -0
  154. package/dist/modes/interactive/session-control-commands.js +16 -6
  155. package/dist/modes/interactive/session-control-registry.d.ts +2 -1
  156. package/dist/modes/interactive/session-control-registry.js +3 -2
  157. package/dist/modes/interactive/session-control-rpc-commands.d.ts +4 -2
  158. package/dist/modes/interactive/session-control-rpc-commands.js +114 -4
  159. package/dist/modes/interactive/session-control-server.js +9 -3
  160. package/dist/modes/rpc/connection-handler.d.ts +1 -0
  161. package/dist/modes/rpc/connection-handler.js +13 -41
  162. package/dist/modes/rpc/custom-capability.d.ts +6 -0
  163. package/dist/modes/rpc/custom-capability.js +6 -0
  164. package/dist/modes/rpc/get-entries-command.d.ts +13 -0
  165. package/dist/modes/rpc/get-entries-command.js +61 -0
  166. package/dist/modes/rpc/get-entries-page.d.ts +22 -0
  167. package/dist/modes/rpc/get-entries-page.js +41 -2
  168. package/dist/modes/rpc/host-capabilities.d.ts +1 -0
  169. package/dist/modes/rpc/host-capabilities.js +6 -2
  170. package/dist/modes/rpc/host-ensure-start.js +13 -2
  171. package/dist/modes/rpc/host-ensure-types.d.ts +2 -0
  172. package/dist/modes/rpc/host-ensure.js +1 -0
  173. package/dist/modes/rpc/host-handoff.d.ts +2 -0
  174. package/dist/modes/rpc/host-handover-wire.d.ts +2 -0
  175. package/dist/modes/rpc/host-handover-wire.js +1 -0
  176. package/dist/modes/rpc/host-lifecycle-launch.d.ts +1 -0
  177. package/dist/modes/rpc/host-lifecycle-launch.js +43 -2
  178. package/dist/modes/rpc/host-observe-request.d.ts +2 -1
  179. package/dist/modes/rpc/host-observe-request.js +4 -1
  180. package/dist/modes/rpc/host-runner.d.ts +2 -0
  181. package/dist/modes/rpc/host-runner.js +8 -6
  182. package/dist/modes/rpc/host-successor.js +11 -1
  183. package/dist/modes/rpc/mcp-form-dialog.d.ts +45 -0
  184. package/dist/modes/rpc/mcp-form-dialog.js +159 -0
  185. package/dist/modes/rpc/mcp-form-worker.d.ts +14 -0
  186. package/dist/modes/rpc/mcp-form-worker.js +167 -0
  187. package/dist/modes/rpc/mcp-presenter-authority.d.ts +53 -0
  188. package/dist/modes/rpc/mcp-presenter-authority.js +309 -0
  189. package/dist/modes/rpc/mcp-presenter-bootstrap.d.ts +8 -0
  190. package/dist/modes/rpc/mcp-presenter-bootstrap.js +111 -0
  191. package/dist/modes/rpc/mcp-presenter-control.d.ts +24 -0
  192. package/dist/modes/rpc/mcp-presenter-control.js +330 -0
  193. package/dist/modes/rpc/multi-session-host.d.ts +3 -0
  194. package/dist/modes/rpc/multi-session-host.js +36 -1
  195. package/dist/modes/rpc/rpc-read-response-types.d.ts +2 -0
  196. package/dist/modes/rpc/rpc-session-command-types.d.ts +8 -1
  197. package/dist/modes/rpc/session-binding.js +6 -1
  198. package/dist/modes/rpc/session-event-writer.d.ts +14 -0
  199. package/dist/modes/rpc/session-event-writer.js +128 -1
  200. package/dist/modes/rpc/session-router-routing.js +1 -0
  201. package/dist/modes/rpc/session-worker-client.d.ts +3 -0
  202. package/dist/modes/rpc/session-worker-client.js +89 -0
  203. package/dist/modes/rpc/session-worker-protocol.d.ts +41 -0
  204. package/dist/modes/rpc/session-worker.js +9 -0
  205. package/docs/extensions.md +5 -0
  206. package/docs/mcp.md +20 -3
  207. package/docs/rpc.md +151 -8
  208. package/package.json +6 -6
  209. package/dist/bundle/chunks/chunk-3LNSLJ7N.js +0 -2
  210. package/dist/bundle/chunks/chunk-B6TPQDBW.js +0 -5
  211. package/dist/bundle/chunks/chunk-CDRFFHLL.js +0 -4
  212. package/dist/bundle/chunks/chunk-HPCS4HM4.js +0 -27
  213. package/dist/bundle/chunks/chunk-KJZAY5YH.js +0 -27
  214. package/dist/bundle/chunks/chunk-MSQCTH6V.js +0 -2
  215. package/dist/bundle/chunks/chunk-OI4NMCKP.js +0 -3
  216. package/dist/bundle/chunks/chunk-SCVRKIFK.js +0 -2
  217. package/dist/bundle/chunks/cli-main-SWKFMTZQ.js +0 -3
  218. package/dist/bundle/chunks/dispatch-DEP4PEDZ.js +0 -10
  219. package/dist/bundle/chunks/host-command-BC6UWP33.js +0 -2
  220. package/dist/bundle/chunks/host-lifecycle-TN5NEGNF.js +0 -3
  221. package/dist/bundle/chunks/host-session-command-MGVBLBRV.js +0 -20
  222. package/dist/bundle/chunks/interactive-mode-SCSROHD5.js +0 -2
  223. package/dist/bundle/chunks/rotation-stream-U7YQMC7H.js +0 -2
  224. package/dist/bundle/chunks/rpc-mode-TOXYD4XA.js +0 -2
  225. package/dist/bundle/chunks/session-control-endpoint-HFFMBALG.js +0 -3
  226. package/dist/bundle/chunks/session-picker-465FVXQG.js +0 -2
@@ -0,0 +1,109 @@
1
+ import { Buffer } from "node:buffer";
2
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3
+ /** RFC 9110 field values, preserving whitespace and literal sentinel strings. */
4
+ export function encodeMcpHeaderValue(value) {
5
+ return /^[\x09\x20-\x7e]*$/.test(value) &&
6
+ value.trim() === value &&
7
+ !(value.startsWith("=?base64?") && value.endsWith("?="))
8
+ ? value
9
+ : `=?base64?${Buffer.from(value, "utf8").toString("base64")}?=`;
10
+ }
11
+ /** Configured headers must never supply parameter metadata absent from the request body. */
12
+ export function clearMcpParameterHeaders(headers) {
13
+ for (const name of [...headers.keys()]) {
14
+ if (name.toLowerCase().startsWith("mcp-param-"))
15
+ headers.delete(name);
16
+ }
17
+ }
18
+ function parseParameterHeaders(schema) {
19
+ const headers = [];
20
+ const names = new Set();
21
+ const visit = (node, path) => {
22
+ if (!isRecord(node))
23
+ return true;
24
+ if (Object.hasOwn(node, "x-mcp-header")) {
25
+ const name = node["x-mcp-header"];
26
+ const type = node.type;
27
+ if (!path?.length ||
28
+ typeof name !== "string" ||
29
+ !/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/.test(name) ||
30
+ names.has(name.toLowerCase()) ||
31
+ (type !== "string" && type !== "integer" && type !== "boolean"))
32
+ return false;
33
+ names.add(name.toLowerCase());
34
+ headers.push({ name, path, type });
35
+ }
36
+ // Only schema-valued keywords are traversed: examples/default/const are instance data.
37
+ for (const [keyword, value] of Object.entries(node)) {
38
+ if (["properties", "patternProperties", "$defs", "definitions", "dependentSchemas", "dependencies"].includes(keyword)) {
39
+ if (!isRecord(value))
40
+ continue;
41
+ for (const [key, child] of Object.entries(value)) {
42
+ const childPath = keyword === "properties" && path !== undefined ? [...path, key] : undefined;
43
+ if (!visit(child, childPath))
44
+ return false;
45
+ }
46
+ }
47
+ else if ([
48
+ "items",
49
+ "prefixItems",
50
+ "allOf",
51
+ "anyOf",
52
+ "oneOf",
53
+ "additionalItems",
54
+ "additionalProperties",
55
+ "unevaluatedItems",
56
+ "unevaluatedProperties",
57
+ "contains",
58
+ "propertyNames",
59
+ "not",
60
+ "if",
61
+ "then",
62
+ "else",
63
+ "contentSchema",
64
+ ].includes(keyword)) {
65
+ for (const child of Array.isArray(value) ? value : [value]) {
66
+ if (!visit(child, undefined))
67
+ return false;
68
+ }
69
+ }
70
+ }
71
+ return true;
72
+ };
73
+ return visit(schema, []) ? headers : undefined;
74
+ }
75
+ /** Connection-local catalog: never shared with another server or transport generation. */
76
+ export class ModernParameterHeaders {
77
+ #tools = new Map();
78
+ filterTools(tools, firstPage) {
79
+ if (firstPage)
80
+ this.#tools.clear();
81
+ return tools.filter((tool) => {
82
+ if (!isRecord(tool) || typeof tool.name !== "string")
83
+ return true;
84
+ const headers = parseParameterHeaders(tool.inputSchema);
85
+ if (headers === undefined) {
86
+ this.#tools.delete(tool.name);
87
+ return false;
88
+ }
89
+ this.#tools.set(tool.name, headers);
90
+ return true;
91
+ });
92
+ }
93
+ append(headers, name, args) {
94
+ if (typeof name !== "string")
95
+ return;
96
+ for (const parameter of this.#tools.get(name) ?? []) {
97
+ let value = args;
98
+ for (const key of parameter.path) {
99
+ value = isRecord(value) && Object.hasOwn(value, key) ? value[key] : undefined;
100
+ }
101
+ const valid = (parameter.type === "string" && typeof value === "string") ||
102
+ (parameter.type === "boolean" && typeof value === "boolean") ||
103
+ (parameter.type === "integer" && typeof value === "number" && Number.isSafeInteger(value));
104
+ if (valid)
105
+ headers.set(`Mcp-Param-${parameter.name}`, encodeMcpHeaderValue(String(value)));
106
+ }
107
+ }
108
+ }
109
+ //# sourceMappingURL=parameter-headers.js.map
@@ -0,0 +1,20 @@
1
+ import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
+ import { type McpClock } from "../wrap.ts";
3
+ export declare const MAX_MCP_MRTR_ROUNDS = 8;
4
+ /** Local dispatch guard survives SDK object spreads but is never JSON serialized. */
5
+ export declare const modernDispatchGuard: unique symbol;
6
+ export interface ModernProtocolState {
7
+ privateForms?: boolean;
8
+ legacyFormEligible?: boolean;
9
+ version?: string;
10
+ legacy?: boolean;
11
+ resolveStdioDiagnostics?: (modern: boolean) => void;
12
+ activateRequests?: () => void;
13
+ }
14
+ export type MrtrClock = McpClock;
15
+ export declare const isModernMcpClient: (client: Client) => boolean;
16
+ export declare const legacyMcpFormsAdvertised: (client: Client) => boolean | undefined;
17
+ export declare function getModernDispatchGuard(params: unknown): ((credential?: string) => void) | undefined;
18
+ /** One instance-local adapter. Captured SDK requests retain ID allocation and final output-schema validation. */
19
+ export declare function installModernRequestAdapter(client: Client, protocol: ModernProtocolState, clock?: MrtrClock, readCredential?: () => string | undefined): void;
20
+ //# sourceMappingURL=request.d.ts.map
@@ -0,0 +1,292 @@
1
+ import { beginLegacyMcpOperation, physicalMcpClient, takeMcpRequestOrigin, } from "../request-origin.js";
2
+ import { AuthError } from "../errors.js";
3
+ import { loadMcpSdkTypes, loadMcpSdkZodCompat } from "../sdk.lazy.js";
4
+ import { mcpElapsedClock } from "../wrap.js";
5
+ import { collectMrtrForm } from "./elicitation.js";
6
+ import { isRecord, validateMrtrForm } from "./form-schema.js";
7
+ import { McpMrtrError } from "./errors.js";
8
+ import { queuedForm } from "./consent-queue.js";
9
+ import { parseInputs } from "./inputs.js";
10
+ export const MAX_MCP_MRTR_ROUNDS = 8;
11
+ const capableMethods = new Set(["tools/call", "resources/read", "prompts/get"]);
12
+ const capabilitiesKey = "io.modelcontextprotocol/clientCapabilities";
13
+ /** Local dispatch guard survives SDK object spreads but is never JSON serialized. */
14
+ export const modernDispatchGuard = Symbol("mcp-modern-dispatch");
15
+ const states = new WeakMap();
16
+ export const isModernMcpClient = (client) => states.get(physicalMcpClient(client))?.version !== undefined;
17
+ export const legacyMcpFormsAdvertised = (client) => states.get(physicalMcpClient(client))?.legacyFormEligible;
18
+ const own = (value, key) => Object.hasOwn(value, key);
19
+ const byteSize = (value) => Buffer.byteLength(JSON.stringify(value), "utf8");
20
+ export function getModernDispatchGuard(params) {
21
+ return isRecord(params)
22
+ ? params[modernDispatchGuard]
23
+ : undefined;
24
+ }
25
+ /** One instance-local adapter. Captured SDK requests retain ID allocation and final output-schema validation. */
26
+ export function installModernRequestAdapter(client, protocol, clock = mcpElapsedClock, readCredential = () => undefined) {
27
+ const sdkRequest = client.request.bind(client);
28
+ states.set(client, protocol);
29
+ const requestWithMrtr = async (request, schema, options) => {
30
+ if (request.method === "initialize")
31
+ return sdkRequest(request, schema, options);
32
+ const { origin, sdkOptions } = takeMcpRequestOrigin(options, client);
33
+ if (protocol.version === undefined) {
34
+ const end = request.method === "tools/call" && origin
35
+ ? beginLegacyMcpOperation(client, origin, sdkOptions.signal)
36
+ : undefined;
37
+ try {
38
+ return await sdkRequest(request, schema, sdkOptions);
39
+ }
40
+ finally {
41
+ end?.();
42
+ }
43
+ }
44
+ if (own(request.params ?? {}, "requestState") || own(request.params ?? {}, "inputResponses") || options?.task)
45
+ throw new McpMrtrError("MCP_MRTR_INVALID_INPUT");
46
+ const params = structuredClone(request.params ?? {});
47
+ const start = clock.now();
48
+ const timeout = sdkOptions.timeout ?? 60_000;
49
+ const duration = Math.min(sdkOptions.maxTotalTimeout ?? Infinity, 9 * timeout + 300_000);
50
+ const deadline = start + duration;
51
+ const operation = new AbortController();
52
+ const credential = readCredential();
53
+ let authority;
54
+ let authorityCaptured = false;
55
+ const controller = origin?.connection;
56
+ const generation = controller?.generation;
57
+ let presentationLost = origin?.presentation === undefined || origin.presentation.signal.aborted;
58
+ let consentSpent = 0;
59
+ let continuation = {};
60
+ let failure;
61
+ const stop = (code) => {
62
+ failure ??= new McpMrtrError(code);
63
+ operation.abort(failure);
64
+ };
65
+ const onCallerAbort = () => stop("MCP_MRTR_CANCELLED");
66
+ const onOwnerAbort = () => stop("MCP_MRTR_CONTEXT_CHANGED");
67
+ const onPresentationLost = () => {
68
+ presentationLost = true;
69
+ };
70
+ const check = () => {
71
+ if (clock.now() >= deadline)
72
+ stop("MCP_MRTR_TIMEOUT");
73
+ if (origin &&
74
+ (!origin.isCurrent() ||
75
+ origin.signal.aborted ||
76
+ controller?.generation !== generation ||
77
+ controller?.state !== "connected"))
78
+ stop("MCP_MRTR_CONTEXT_CHANGED");
79
+ if (failure)
80
+ throw failure;
81
+ };
82
+ const resolveAuthority = async () => {
83
+ check();
84
+ if (origin) {
85
+ await origin.privateForm?.synchronize?.();
86
+ const snapshot = JSON.stringify(await interrupted(origin.resolveAuthority(operation.signal), operation.signal));
87
+ if (!authorityCaptured) {
88
+ authority = snapshot;
89
+ authorityCaptured = true;
90
+ }
91
+ else if (authority !== snapshot)
92
+ stop("MCP_MRTR_CONTEXT_CHANGED");
93
+ }
94
+ check();
95
+ };
96
+ const off = controller?.onStateChange(() => {
97
+ try {
98
+ check();
99
+ }
100
+ catch {
101
+ /* Abort reaches the active attempt and consent. */
102
+ }
103
+ });
104
+ sdkOptions.signal?.addEventListener("abort", onCallerAbort, { once: true });
105
+ origin?.signal.addEventListener("abort", onOwnerAbort, { once: true });
106
+ origin?.presentation?.signal.addEventListener("abort", onPresentationLost, { once: true });
107
+ const timer = clock.schedule(() => stop("MCP_MRTR_TIMEOUT"), Math.max(0, duration));
108
+ if (sdkOptions.signal?.aborted)
109
+ onCallerAbort();
110
+ if (origin?.signal.aborted)
111
+ onOwnerAbort();
112
+ try {
113
+ const { safeParse } = await interrupted(loadMcpSdkZodCompat(), operation.signal);
114
+ // Use the SDK's own Zod identity, including when npm installs another copy for this package.
115
+ const { ResultSchema } = await interrupted(loadMcpSdkTypes(), operation.signal);
116
+ for (let round = 0;; round++) {
117
+ await resolveAuthority();
118
+ const remaining = deadline - clock.now();
119
+ const attempt = new AbortController();
120
+ const forward = () => attempt.abort("MCP request canceled");
121
+ operation.signal.addEventListener("abort", forward, { once: true });
122
+ const metadata = isRecord(params._meta) ? params._meta : {};
123
+ const capabilities = origin?.privateFormsEnforced
124
+ ? origin.privateForm?.ready()
125
+ ? { elicitation: {} }
126
+ : {}
127
+ : !presentationLost && origin?.presentation
128
+ ? { elicitation: {} }
129
+ : {};
130
+ const outgoing = {
131
+ ...params,
132
+ ...continuation,
133
+ _meta: { ...metadata, [capabilitiesKey]: capabilities },
134
+ [modernDispatchGuard]: (snapshot) => {
135
+ check();
136
+ if ((snapshot ?? readCredential()) !== credential)
137
+ throw new McpMrtrError("MCP_MRTR_CONTEXT_CHANGED");
138
+ },
139
+ };
140
+ let result;
141
+ try {
142
+ check();
143
+ result = await sdkRequest({ method: request.method, params: outgoing }, ResultSchema, {
144
+ ...sdkOptions,
145
+ signal: attempt.signal,
146
+ timeout: Math.min(timeout, remaining),
147
+ maxTotalTimeout: remaining,
148
+ onprogress: sdkOptions.onprogress
149
+ ? (progress) => {
150
+ check();
151
+ if (!Number.isFinite(progress.progress) ||
152
+ (progress.total !== undefined && !Number.isFinite(progress.total)))
153
+ return;
154
+ sdkOptions.onprogress?.({
155
+ progress: progress.progress,
156
+ ...(progress.total === undefined ? {} : { total: progress.total }),
157
+ });
158
+ }
159
+ : undefined,
160
+ });
161
+ }
162
+ finally {
163
+ operation.signal.removeEventListener("abort", forward);
164
+ }
165
+ check();
166
+ const type = own(result, "resultType") ? result.resultType : "complete";
167
+ if (type === "complete") {
168
+ if (["requestState", "inputRequests", "inputResponses"].some((key) => own(result, key)))
169
+ throw new McpMrtrError("MCP_MRTR_INVALID_RESULT");
170
+ const parsed = safeParse(schema, result);
171
+ if (!parsed.success)
172
+ throw new McpMrtrError("MCP_MRTR_INVALID_RESULT");
173
+ return parsed.data;
174
+ }
175
+ if (type !== "input_required" || !capableMethods.has(request.method))
176
+ throw new McpMrtrError("MCP_MRTR_INVALID_RESULT");
177
+ if (round >= MAX_MCP_MRTR_ROUNDS)
178
+ throw new McpMrtrError("MCP_MRTR_ROUND_LIMIT");
179
+ const inputs = parseInputs(result);
180
+ continuation = own(result, "requestState") ? { requestState: result.requestState } : {};
181
+ if (inputs) {
182
+ const responses = Object.create(null);
183
+ let cancelled = false;
184
+ for (const [key, input] of inputs) {
185
+ await resolveAuthority();
186
+ if (input.params.mode === "url") {
187
+ throw new McpMrtrError("MCP_MRTR_UNSUPPORTED_INPUT");
188
+ }
189
+ else if (cancelled) {
190
+ // A human stopped this batch, not necessarily the caller's AbortSignal.
191
+ // Keep the cancellation code; never fabricate results for unshown inputs.
192
+ throw new McpMrtrError("MCP_MRTR_CANCELLED");
193
+ }
194
+ else if (consentSpent >= 300_000) {
195
+ throw new McpMrtrError("MCP_MRTR_PROCESSING_UNAVAILABLE");
196
+ }
197
+ else if (origin?.privateFormsEnforced) {
198
+ const presenter = origin.privateForm;
199
+ const schema = input.params.requestedSchema;
200
+ if (!presenter?.ready() || !validateMrtrForm(schema))
201
+ throw new McpMrtrError("MCP_MRTR_PROCESSING_UNAVAILABLE");
202
+ const began = clock.now();
203
+ const outcome = await presenter.present({
204
+ operationId: String(start),
205
+ inputId: key,
206
+ round,
207
+ server: { label: origin.serverLabel, source: origin.serverSource ?? "user" },
208
+ message: input.params.message,
209
+ schema,
210
+ operationSignal: operation.signal,
211
+ presentationSignal: presenter.signal,
212
+ deadlineMs: Math.max(0, 300_000 - consentSpent),
213
+ });
214
+ consentSpent += clock.now() - began;
215
+ check();
216
+ if (outcome.kind !== "result")
217
+ throw new McpMrtrError(outcome.kind === "operation_aborted"
218
+ ? "MCP_MRTR_CANCELLED"
219
+ : "MCP_MRTR_PROCESSING_UNAVAILABLE");
220
+ responses[key] = outcome.result;
221
+ cancelled = outcome.result.action !== "accept";
222
+ }
223
+ else if (presentationLost || !origin?.presentation) {
224
+ throw new McpMrtrError("MCP_MRTR_PROCESSING_UNAVAILABLE");
225
+ }
226
+ else {
227
+ const consent = new AbortController();
228
+ const began = clock.now();
229
+ const consentTimer = clock.schedule(() => consent.abort(), Math.max(0, 300_000 - consentSpent));
230
+ const signal = AbortSignal.any([operation.signal, origin.presentation.signal, consent.signal]);
231
+ let presented = false;
232
+ try {
233
+ const answer = await interrupted(queuedForm(origin.binding, signal, () => {
234
+ presented = true;
235
+ return collectMrtrForm(origin.presentation, origin.serverLabel, { message: input.params.message, requestedSchema: input.params.requestedSchema }, signal);
236
+ }), operation.signal);
237
+ check();
238
+ if (!presented)
239
+ throw new McpMrtrError("MCP_MRTR_PROCESSING_UNAVAILABLE");
240
+ responses[key] = answer;
241
+ cancelled = answer.action !== "accept";
242
+ }
243
+ finally {
244
+ consentSpent += clock.now() - began;
245
+ clock.cancel(consentTimer);
246
+ consent.abort();
247
+ }
248
+ }
249
+ }
250
+ if (byteSize(responses) > 512 * 1024)
251
+ throw new McpMrtrError("MCP_MRTR_INPUT_LIMIT");
252
+ continuation.inputResponses = responses;
253
+ }
254
+ }
255
+ }
256
+ catch (error) {
257
+ check();
258
+ if (error instanceof McpMrtrError)
259
+ throw error;
260
+ if (error instanceof AuthError)
261
+ throw error;
262
+ const remoteCode = isRecord(error) && typeof error.code === "number" ? error.code : undefined;
263
+ throw new McpMrtrError("MCP_MRTR_TRANSPORT_FAILED", remoteCode);
264
+ }
265
+ finally {
266
+ continuation = {};
267
+ clock.cancel(timer);
268
+ off?.();
269
+ sdkOptions.signal?.removeEventListener("abort", onCallerAbort);
270
+ origin?.signal.removeEventListener("abort", onOwnerAbort);
271
+ origin?.presentation?.signal.removeEventListener("abort", onPresentationLost);
272
+ }
273
+ };
274
+ // Preserve the SDK method itself until negotiation positively selects modern.
275
+ protocol.activateRequests = () => {
276
+ client.request = requestWithMrtr;
277
+ };
278
+ // Default legacy clients retain the SDK method and its original admission behavior.
279
+ // Enforced legacy calls need private operation ownership before the first tools/call.
280
+ if (protocol.privateForms || protocol.version !== undefined)
281
+ protocol.activateRequests();
282
+ }
283
+ function interrupted(work, signal) {
284
+ return new Promise((resolve, reject) => {
285
+ const onabort = () => reject(signal.reason);
286
+ signal.addEventListener("abort", onabort, { once: true });
287
+ if (signal.aborted)
288
+ onabort();
289
+ void work.then(resolve, reject).finally(() => signal.removeEventListener("abort", onabort));
290
+ });
291
+ }
292
+ //# sourceMappingURL=request.js.map
@@ -10,5 +10,5 @@ export declare const STDIO_PROBE_MS = 2000;
10
10
  * keeps the same process for the legacy handshake; a discovery reply that arrives late still upgrades it to modern.
11
11
  * Only a child that exited or wrote non-JSON to stdout is replaced by a fresh process for the legacy handshake.
12
12
  */
13
- export declare function probeStdioTransport(transport: Transport, options: ProbeOptions, logger: McpLogger, timeoutMs: number, serverName: string, onModern?: (version: string) => void, probeMs?: number): void;
13
+ export declare function probeStdioTransport(transport: Transport, options: ProbeOptions, logger: McpLogger, timeoutMs: number, serverName: string, onModern?: (version: string) => void, probeMs?: number, onLegacy?: () => void): void;
14
14
  //# sourceMappingURL=stdio-probe.d.ts.map
@@ -3,6 +3,9 @@ import { reapProcessTree } from "../process-tree.js";
3
3
  import { discoveryReply, probeStdioEra, STDIO_PROBE_ID, StdioProbeSilence, } from "./era.js";
4
4
  import { modernInitializeResult, normalizeModernResult } from "./sdk-bridge.js";
5
5
  import { ModernTransportError } from "./transport.js";
6
+ import { sanitizeModernMessage } from "./diagnostics.js";
7
+ import { McpMrtrError } from "./errors.js";
8
+ import { getModernDispatchGuard } from "./request.js";
6
9
  import { MODERN_PROTOCOL_VERSION, modernizeRequest } from "./wire.js";
7
10
  /** How long a stdio child gets to answer discovery (at most a quarter of the connect timeout) before it is treated as legacy. */
8
11
  export const STDIO_PROBE_MS = 2_000;
@@ -13,7 +16,7 @@ export const STDIO_PROBE_MS = 2_000;
13
16
  * keeps the same process for the legacy handshake; a discovery reply that arrives late still upgrades it to modern.
14
17
  * Only a child that exited or wrote non-JSON to stdout is replaced by a fresh process for the legacy handshake.
15
18
  */
16
- export function probeStdioTransport(transport, options, logger, timeoutMs, serverName, onModern, probeMs = STDIO_PROBE_MS) {
19
+ export function probeStdioTransport(transport, options, logger, timeoutMs, serverName, onModern, probeMs = STDIO_PROBE_MS, onLegacy) {
17
20
  const start = transport.start.bind(transport);
18
21
  const close = transport.close.bind(transport);
19
22
  const send = transport.send.bind(transport);
@@ -31,6 +34,8 @@ export function probeStdioTransport(transport, options, logger, timeoutMs, serve
31
34
  await restart();
32
35
  else if (outcome.silent)
33
36
  awaitLateDiscovery();
37
+ if (!outcome.silent)
38
+ onLegacy?.();
34
39
  return;
35
40
  }
36
41
  if (outcome.kind === "indeterminate" || !outcome.discovery) {
@@ -161,8 +166,10 @@ export function probeStdioTransport(transport, options, logger, timeoutMs, serve
161
166
  }, extra);
162
167
  return;
163
168
  }
164
- if (isInitializeReply)
169
+ if (isInitializeReply) {
165
170
  settle();
171
+ onLegacy?.();
172
+ }
166
173
  deliver?.(message, extra);
167
174
  };
168
175
  transport.send = async (message, sendOptions) => {
@@ -178,8 +185,34 @@ export function probeStdioTransport(transport, options, logger, timeoutMs, serve
178
185
  function speakModern(discovery, version) {
179
186
  const initializeResult = modernInitializeResult(discovery, version);
180
187
  const deliver = transport.onmessage;
188
+ const live = new Set();
181
189
  let initialized = false;
182
- transport.onmessage = (message, extra) => deliver?.(normalizeModernResult(message), extra);
190
+ transport.onerror = () => {
191
+ // A parser error may quote a wire body; never forward it.
192
+ const failure = new McpMrtrError("MCP_MRTR_TRANSPORT_FAILED");
193
+ logger.warn(failure.message);
194
+ void transport.close();
195
+ };
196
+ transport.onmessage = (message, extra) => {
197
+ if ("id" in message && "method" in message) {
198
+ void send({
199
+ jsonrpc: "2.0",
200
+ id: message.id,
201
+ error: { code: -32601, message: "Unsupported MCP method." },
202
+ }).catch(() => { });
203
+ return;
204
+ }
205
+ if ("id" in message && message.id !== undefined && !live.delete(message.id))
206
+ return;
207
+ if ("method" in message && message.method === "notifications/progress") {
208
+ const token = message.params?.progressToken;
209
+ if ((typeof token !== "string" && typeof token !== "number") || !live.has(token))
210
+ return;
211
+ }
212
+ const safe = sanitizeModernMessage(message);
213
+ if (safe)
214
+ deliver?.(normalizeModernResult(safe), extra);
215
+ };
183
216
  transport.send = async (message, sendOptions) => {
184
217
  if (!("method" in message))
185
218
  return send(message, sendOptions);
@@ -188,14 +221,29 @@ export function probeStdioTransport(transport, options, logger, timeoutMs, serve
188
221
  throw new ModernTransportError("Modern MCP stdio has already initialized");
189
222
  }
190
223
  initialized = true;
191
- transport.onmessage?.({ jsonrpc: "2.0", id: message.id, result: initializeResult });
224
+ deliver?.({ jsonrpc: "2.0", id: message.id, result: initializeResult });
192
225
  return;
193
226
  }
194
227
  if (message.method === "notifications/initialized" && !("id" in message))
195
228
  return;
229
+ if (message.method === "notifications/cancelled" && !("id" in message)) {
230
+ const id = message.params?.requestId;
231
+ if ((typeof id !== "string" && typeof id !== "number") || !live.delete(id))
232
+ return;
233
+ return send({ jsonrpc: "2.0", method: message.method, params: { requestId: id, reason: "MCP request canceled" } }, sendOptions);
234
+ }
235
+ getModernDispatchGuard(message.params)?.();
196
236
  const request = modernizeRequest("id" in message ? message.id : 0, message.method, options.clientInfo, options.clientCapabilities, version, message.params);
197
- if ("id" in message)
198
- return send(request, sendOptions);
237
+ if ("id" in message) {
238
+ live.add(message.id);
239
+ try {
240
+ return await send(request, sendOptions);
241
+ }
242
+ catch {
243
+ live.delete(message.id);
244
+ throw new McpMrtrError("MCP_MRTR_TRANSPORT_FAILED");
245
+ }
246
+ }
199
247
  const { id: _id, ...notification } = request;
200
248
  return send(notification, sendOptions);
201
249
  };