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

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 (199) hide show
  1. package/CHANGELOG.md +20 -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-6O4SEW2W.js +5 -0
  5. package/dist/bundle/chunks/{chunk-Y5OTHMXO.js → chunk-7CNOCJCN.js} +1 -1
  6. package/dist/bundle/chunks/{chunk-JRXKLIEQ.js → chunk-7W7OJDIY.js} +4 -4
  7. package/dist/bundle/chunks/chunk-AX72VNRS.js +27 -0
  8. package/dist/bundle/chunks/{chunk-LYGAV6KU.js → chunk-C5UFJ7B3.js} +1 -1
  9. package/dist/bundle/chunks/{chunk-G2UETS2R.js → chunk-CNL6QSUB.js} +4 -4
  10. package/dist/bundle/chunks/{chunk-2EFVQWXZ.js → chunk-FFQOEC76.js} +3 -3
  11. package/dist/bundle/chunks/chunk-H3HNMY2W.js +13 -0
  12. package/dist/bundle/chunks/{chunk-W2BKZBYJ.js → chunk-HVJI5T75.js} +1 -1
  13. package/dist/bundle/chunks/{chunk-YN7ELQ43.js → chunk-JA2R2ZSE.js} +1 -1
  14. package/dist/bundle/chunks/chunk-JPGT64RV.js +2 -0
  15. package/dist/bundle/chunks/chunk-KXB5WIS5.js +4 -0
  16. package/dist/bundle/chunks/chunk-L73UYAH5.js +3 -0
  17. package/dist/bundle/chunks/chunk-LTU6UFEX.js +2 -0
  18. package/dist/bundle/chunks/{chunk-KUE4AQN6.js → chunk-MJ5NNWX4.js} +1 -1
  19. package/dist/bundle/chunks/{chunk-WFXUCVIR.js → chunk-O3Q2ET6W.js} +2 -5
  20. package/dist/bundle/chunks/{chunk-MXZPAUFB.js → chunk-ODTGC6YU.js} +1 -1
  21. package/dist/bundle/chunks/chunk-P3M2PF6K.js +27 -0
  22. package/dist/bundle/chunks/{chunk-VNN2V74Y.js → chunk-SWJXRC2I.js} +1 -1
  23. package/dist/bundle/chunks/{chunk-RXKNOEQQ.js → chunk-UQNRW3VU.js} +1 -1
  24. package/dist/bundle/chunks/{chunk-U67ML6CQ.js → chunk-WD6WBYC5.js} +1 -1
  25. package/dist/bundle/chunks/chunk-WJOT3DFL.js +2 -0
  26. package/dist/bundle/chunks/{chunk-XBUSVKWC.js → chunk-WLHNEUGD.js} +1 -1
  27. package/dist/bundle/chunks/chunk-YI7SPFDN.js +5 -0
  28. package/dist/bundle/chunks/{chunk-J2U5VDG7.js → chunk-YRF4I43T.js} +28 -28
  29. package/dist/bundle/chunks/chunk-Z3UYNSVR.js +3 -0
  30. package/dist/bundle/chunks/cli-main-HEKFFB5S.js +3 -0
  31. package/dist/bundle/chunks/{client-KWTLTKVV.js → client-WTYEICLH.js} +3 -3
  32. package/dist/bundle/chunks/dispatch-NYAULNVW.js +9 -0
  33. package/dist/bundle/chunks/{help-fast-path-ZHZGXAUG.js → help-fast-path-3Z26DQSV.js} +1 -1
  34. package/dist/bundle/chunks/host-command-Z3WPHJOW.js +2 -0
  35. package/dist/bundle/chunks/host-lifecycle-ONQIMWMX.js +3 -0
  36. package/dist/bundle/chunks/host-lifecycle.js +7 -5
  37. package/dist/bundle/chunks/host-session-command-J65ENSV5.js +20 -0
  38. package/dist/bundle/chunks/{host-socket-request-AS2OCRLP.js → host-socket-request-3CWE4265.js} +1 -1
  39. package/dist/bundle/chunks/interactive-mode-DEPQC6TX.js +2 -0
  40. package/dist/bundle/chunks/mcp-presenter-bootstrap-XUSCQVRC.js +2 -0
  41. package/dist/bundle/chunks/{multi-session-host-VTTY5MFT.js → multi-session-host-YI2WZOET.js} +12 -9
  42. package/dist/bundle/chunks/{package-manager-cli-DZFOC3MF.js → package-manager-cli-GHZPJMRF.js} +1 -1
  43. package/dist/bundle/chunks/{rotation-stream-U7YQMC7H.js → rotation-stream-ZPVCFOVD.js} +1 -1
  44. package/dist/bundle/chunks/rpc-mode-ORHBGBBV.js +2 -0
  45. package/dist/bundle/chunks/{rpc-monitor-source-B5NQ4ZVH.js → rpc-monitor-source-FFEQS744.js} +1 -1
  46. package/dist/bundle/chunks/{schedule-command-SLISTG4V.js → schedule-command-YWG6D6ZF.js} +1 -1
  47. package/dist/bundle/chunks/session-control-endpoint-36HAQSQO.js +3 -0
  48. package/dist/bundle/chunks/{session-picker-465FVXQG.js → session-picker-25H5KNYP.js} +1 -1
  49. package/dist/bundle/chunks/session-worker.js +278 -264
  50. package/dist/bundle/chunks/{stdio-IEC635SY.js → stdio-YXDKKKQZ.js} +1 -1
  51. package/dist/bundle/chunks/{streamableHttp-KJI5KE4K.js → streamableHttp-DKIBUWVY.js} +1 -1
  52. package/dist/bundle/chunks/{types-LLNV23LW.js → types-BJUEQ4II.js} +1 -1
  53. package/dist/bundle/chunks/zod-compat-RJDXCU7S.js +2 -0
  54. package/dist/bundle/cli.js +2 -2
  55. package/dist/bundle/host-supervisor.js +7 -5
  56. package/dist/bundle/index.js +1 -1
  57. package/dist/bundle/rpc-entry.js +1 -1
  58. package/dist/bundle/runtime-manifest.json +1 -1
  59. package/dist/cli/args.d.ts +2 -0
  60. package/dist/cli/args.js +9 -0
  61. package/dist/cli/host-command-args.d.ts +1 -0
  62. package/dist/cli/host-command-args.js +7 -1
  63. package/dist/cli/host-command.js +3 -1
  64. package/dist/core/agent-session.js +22 -7
  65. package/dist/core/extensions/builtin/goal/index.js +1 -0
  66. package/dist/core/extensions/builtin/goal/lifecycle-facts.d.ts +3 -0
  67. package/dist/core/extensions/builtin/mcp/catalog.d.ts +3 -1
  68. package/dist/core/extensions/builtin/mcp/catalog.js +2 -0
  69. package/dist/core/extensions/builtin/mcp/connection.d.ts +1 -0
  70. package/dist/core/extensions/builtin/mcp/connection.js +46 -1
  71. package/dist/core/extensions/builtin/mcp/elicitation.d.ts +6 -1
  72. package/dist/core/extensions/builtin/mcp/elicitation.js +56 -2
  73. package/dist/core/extensions/builtin/mcp/expose/call.js +6 -2
  74. package/dist/core/extensions/builtin/mcp/expose/session.d.ts +4 -1
  75. package/dist/core/extensions/builtin/mcp/expose/session.js +7 -0
  76. package/dist/core/extensions/builtin/mcp/health.js +5 -0
  77. package/dist/core/extensions/builtin/mcp/host-registry.js +7 -1
  78. package/dist/core/extensions/builtin/mcp/index.js +2 -0
  79. package/dist/core/extensions/builtin/mcp/modern/client.d.ts +5 -0
  80. package/dist/core/extensions/builtin/mcp/modern/client.js +15 -0
  81. package/dist/core/extensions/builtin/mcp/modern/consent-queue.d.ts +2 -0
  82. package/dist/core/extensions/builtin/mcp/modern/consent-queue.js +59 -0
  83. package/dist/core/extensions/builtin/mcp/modern/diagnostics.d.ts +4 -0
  84. package/dist/core/extensions/builtin/mcp/modern/diagnostics.js +39 -0
  85. package/dist/core/extensions/builtin/mcp/modern/dispatch.js +21 -11
  86. package/dist/core/extensions/builtin/mcp/modern/elicitation.d.ts +14 -0
  87. package/dist/core/extensions/builtin/mcp/modern/elicitation.js +145 -0
  88. package/dist/core/extensions/builtin/mcp/modern/errors.d.ts +21 -0
  89. package/dist/core/extensions/builtin/mcp/modern/errors.js +23 -0
  90. package/dist/core/extensions/builtin/mcp/modern/form-schema.d.ts +20 -0
  91. package/dist/core/extensions/builtin/mcp/modern/form-schema.js +153 -0
  92. package/dist/core/extensions/builtin/mcp/modern/http-fetch.d.ts +3 -1
  93. package/dist/core/extensions/builtin/mcp/modern/http-fetch.js +8 -2
  94. package/dist/core/extensions/builtin/mcp/modern/inputs.d.ts +11 -0
  95. package/dist/core/extensions/builtin/mcp/modern/inputs.js +65 -0
  96. package/dist/core/extensions/builtin/mcp/modern/parameter-headers.d.ts +11 -0
  97. package/dist/core/extensions/builtin/mcp/modern/parameter-headers.js +109 -0
  98. package/dist/core/extensions/builtin/mcp/modern/request.d.ts +20 -0
  99. package/dist/core/extensions/builtin/mcp/modern/request.js +292 -0
  100. package/dist/core/extensions/builtin/mcp/modern/stdio-probe.d.ts +1 -1
  101. package/dist/core/extensions/builtin/mcp/modern/stdio-probe.js +54 -6
  102. package/dist/core/extensions/builtin/mcp/modern/transport.js +80 -14
  103. package/dist/core/extensions/builtin/mcp/modern/wire.js +11 -7
  104. package/dist/core/extensions/builtin/mcp/prompts.d.ts +5 -1
  105. package/dist/core/extensions/builtin/mcp/prompts.js +6 -1
  106. package/dist/core/extensions/builtin/mcp/request-origin.d.ts +47 -0
  107. package/dist/core/extensions/builtin/mcp/request-origin.js +59 -0
  108. package/dist/core/extensions/builtin/mcp/resources.d.ts +4 -1
  109. package/dist/core/extensions/builtin/mcp/resources.js +19 -4
  110. package/dist/core/extensions/builtin/mcp/sdk.lazy.d.ts +3 -1
  111. package/dist/core/extensions/builtin/mcp/sdk.lazy.js +3 -1
  112. package/dist/core/extensions/builtin/mcp/service-register.d.ts +4 -1
  113. package/dist/core/extensions/builtin/mcp/service-register.js +3 -0
  114. package/dist/core/extensions/builtin/mcp/service-types.d.ts +2 -1
  115. package/dist/core/extensions/builtin/mcp/service.d.ts +1 -0
  116. package/dist/core/extensions/builtin/mcp/service.js +79 -4
  117. package/dist/core/extensions/builtin/mcp/shared-connection.js +36 -2
  118. package/dist/core/extensions/builtin/mcp/shared-lease.d.ts +1 -0
  119. package/dist/core/extensions/builtin/mcp/shared-lease.js +9 -0
  120. package/dist/core/extensions/builtin/mcp/transport-sdk.d.ts +3 -2
  121. package/dist/core/extensions/builtin/mcp/transport-sdk.js +56 -25
  122. package/dist/core/extensions/builtin/mcp/wrap.d.ts +7 -0
  123. package/dist/core/extensions/builtin/mcp/wrap.js +6 -0
  124. package/dist/core/extensions/runner.js +3 -2
  125. package/dist/core/extensions/types.d.ts +2 -0
  126. package/dist/core/lifecycle-ledger.d.ts +1 -0
  127. package/dist/core/lifecycle-ledger.js +7 -2
  128. package/dist/core/lifecycle-record.js +3 -0
  129. package/dist/core/lifecycle-session-binding.d.ts +11 -1
  130. package/dist/core/lifecycle-session-binding.js +29 -2
  131. package/dist/core/lifecycle-stop.d.ts +41 -0
  132. package/dist/core/lifecycle-stop.js +92 -0
  133. package/dist/core/mcp-form-types.d.ts +89 -0
  134. package/dist/core/mcp-form-types.js +23 -0
  135. package/dist/core/mcp-mrtr-presentations.d.ts +15 -0
  136. package/dist/core/mcp-mrtr-presentations.js +17 -0
  137. package/dist/core/session-lifecycle-types.d.ts +15 -0
  138. package/dist/core/session-lifecycle.d.ts +17 -1
  139. package/dist/core/session-lifecycle.js +40 -2
  140. package/dist/main.js +5 -1
  141. package/dist/modes/interactive/components/extension-selector.d.ts +1 -0
  142. package/dist/modes/interactive/components/extension-selector.js +4 -0
  143. package/dist/modes/interactive/interactive-mode.d.ts +4 -0
  144. package/dist/modes/interactive/interactive-mode.js +34 -8
  145. package/dist/modes/interactive/mcp-local-presenter.d.ts +5 -0
  146. package/dist/modes/interactive/mcp-local-presenter.js +109 -0
  147. package/dist/modes/rpc/connection-handler.d.ts +1 -0
  148. package/dist/modes/rpc/connection-handler.js +7 -1
  149. package/dist/modes/rpc/host-capabilities.d.ts +1 -0
  150. package/dist/modes/rpc/host-capabilities.js +2 -1
  151. package/dist/modes/rpc/host-ensure-start.js +13 -2
  152. package/dist/modes/rpc/host-ensure-types.d.ts +2 -0
  153. package/dist/modes/rpc/host-ensure.js +1 -0
  154. package/dist/modes/rpc/host-handoff.d.ts +2 -0
  155. package/dist/modes/rpc/host-handover-wire.d.ts +2 -0
  156. package/dist/modes/rpc/host-handover-wire.js +1 -0
  157. package/dist/modes/rpc/host-lifecycle-launch.d.ts +1 -0
  158. package/dist/modes/rpc/host-lifecycle-launch.js +43 -2
  159. package/dist/modes/rpc/host-runner.d.ts +2 -0
  160. package/dist/modes/rpc/host-runner.js +8 -6
  161. package/dist/modes/rpc/host-successor.js +11 -1
  162. package/dist/modes/rpc/mcp-form-dialog.d.ts +45 -0
  163. package/dist/modes/rpc/mcp-form-dialog.js +159 -0
  164. package/dist/modes/rpc/mcp-form-worker.d.ts +14 -0
  165. package/dist/modes/rpc/mcp-form-worker.js +167 -0
  166. package/dist/modes/rpc/mcp-presenter-authority.d.ts +53 -0
  167. package/dist/modes/rpc/mcp-presenter-authority.js +309 -0
  168. package/dist/modes/rpc/mcp-presenter-bootstrap.d.ts +8 -0
  169. package/dist/modes/rpc/mcp-presenter-bootstrap.js +111 -0
  170. package/dist/modes/rpc/mcp-presenter-control.d.ts +24 -0
  171. package/dist/modes/rpc/mcp-presenter-control.js +330 -0
  172. package/dist/modes/rpc/multi-session-host.d.ts +3 -0
  173. package/dist/modes/rpc/multi-session-host.js +36 -1
  174. package/dist/modes/rpc/session-binding.js +6 -1
  175. package/dist/modes/rpc/session-event-writer.d.ts +14 -0
  176. package/dist/modes/rpc/session-event-writer.js +128 -1
  177. package/dist/modes/rpc/session-router-routing.js +1 -0
  178. package/dist/modes/rpc/session-worker-client.d.ts +3 -0
  179. package/dist/modes/rpc/session-worker-client.js +89 -0
  180. package/dist/modes/rpc/session-worker-protocol.d.ts +41 -0
  181. package/dist/modes/rpc/session-worker.js +9 -0
  182. package/docs/extensions.md +5 -0
  183. package/docs/mcp.md +20 -3
  184. package/docs/rpc.md +111 -0
  185. package/package.json +6 -6
  186. package/dist/bundle/chunks/chunk-B6TPQDBW.js +0 -5
  187. package/dist/bundle/chunks/chunk-CDRFFHLL.js +0 -4
  188. package/dist/bundle/chunks/chunk-HPCS4HM4.js +0 -27
  189. package/dist/bundle/chunks/chunk-KJZAY5YH.js +0 -27
  190. package/dist/bundle/chunks/chunk-OI4NMCKP.js +0 -3
  191. package/dist/bundle/chunks/chunk-SCVRKIFK.js +0 -2
  192. package/dist/bundle/chunks/cli-main-SWKFMTZQ.js +0 -3
  193. package/dist/bundle/chunks/dispatch-DEP4PEDZ.js +0 -10
  194. package/dist/bundle/chunks/host-command-BC6UWP33.js +0 -2
  195. package/dist/bundle/chunks/host-lifecycle-TN5NEGNF.js +0 -3
  196. package/dist/bundle/chunks/host-session-command-MGVBLBRV.js +0 -20
  197. package/dist/bundle/chunks/interactive-mode-SCSROHD5.js +0 -2
  198. package/dist/bundle/chunks/rpc-mode-TOXYD4XA.js +0 -2
  199. package/dist/bundle/chunks/session-control-endpoint-HFFMBALG.js +0 -3
@@ -1,5 +1,10 @@
1
1
  import { JSONRPCMessageSchema } from "@modelcontextprotocol/sdk/types.js";
2
2
  import { deriveModernHeaders, MODERN_PROTOCOL_VERSION, modernizeRequest } from "./wire.js";
3
+ import { AuthError } from "../errors.js";
4
+ import { sanitizeModernMessage } from "./diagnostics.js";
5
+ import { McpMrtrError } from "./errors.js";
6
+ import { getModernDispatchGuard, modernDispatchGuard } from "./request.js";
7
+ import { clearMcpParameterHeaders, ModernParameterHeaders } from "./parameter-headers.js";
3
8
  export class ModernTransportError extends Error {
4
9
  constructor(message) {
5
10
  super(message);
@@ -10,6 +15,9 @@ export class ModernTransportError extends Error {
10
15
  export class ModernHttpTransport {
11
16
  #options;
12
17
  #abort = new AbortController();
18
+ #attempts = new Map();
19
+ #toolLists = new Map();
20
+ #parameterHeaders = new ModernParameterHeaders();
13
21
  #started = false;
14
22
  #closed = false;
15
23
  constructor(options) {
@@ -30,9 +38,24 @@ export class ModernHttpTransport {
30
38
  async send(message) {
31
39
  if (!this.#started || this.#closed)
32
40
  throw new ModernTransportError("Modern MCP transport is not open");
41
+ if ("method" in message && message.method === "notifications/cancelled") {
42
+ const id = message.params?.requestId;
43
+ if (typeof id === "string" || typeof id === "number")
44
+ this.#attempts.get(id)?.abort();
45
+ return;
46
+ }
47
+ const id = "method" in message && "id" in message ? message.id : undefined;
48
+ if (id !== undefined && "method" in message && message.method === "tools/list") {
49
+ this.#toolLists.set(id, message.params?.cursor === undefined);
50
+ }
51
+ const controller = new AbortController();
52
+ if (id !== undefined)
53
+ this.#attempts.set(id, controller);
54
+ const signal = AbortSignal.any([this.#abort.signal, controller.signal]);
33
55
  try {
34
56
  const version = this.#options.protocolVersion ?? MODERN_PROTOCOL_VERSION;
35
57
  const headers = new Headers(this.#options.headers);
58
+ clearMcpParameterHeaders(headers);
36
59
  // Auth headers pass through, but protocol routing is exclusively derived from the body.
37
60
  for (const name of ["Mcp-Session-Id", "Mcp-Name", "Mcp-Method", "MCP-Protocol-Version"])
38
61
  headers.delete(name);
@@ -43,6 +66,9 @@ export class ModernHttpTransport {
43
66
  const request = modernizeRequest("id" in message ? message.id : 0, message.method, this.#options.clientInfo, this.#options.clientCapabilities, version, message.params);
44
67
  for (const [name, value] of Object.entries(deriveModernHeaders(request)))
45
68
  headers.set(name, value);
69
+ if (request.method === "tools/call") {
70
+ this.#parameterHeaders.append(headers, request.params.name, request.params.arguments);
71
+ }
46
72
  if ("id" in message) {
47
73
  body = request;
48
74
  }
@@ -55,13 +81,24 @@ export class ModernHttpTransport {
55
81
  // JSON-RPC replies have no method or params; do not turn them into requests.
56
82
  headers.set("MCP-Protocol-Version", version);
57
83
  }
58
- const response = await this.#options.fetch(this.#options.url, {
84
+ const guard = "method" in message ? getModernDispatchGuard(message.params) : undefined;
85
+ guard?.();
86
+ const init = {
59
87
  method: "POST",
60
88
  headers,
61
89
  body: JSON.stringify(body),
62
- signal: this.#abort.signal,
63
- });
64
- this.#abort.signal.throwIfAborted();
90
+ signal,
91
+ [modernDispatchGuard]: guard,
92
+ };
93
+ const response = await this.#options.fetch(this.#options.url, init);
94
+ signal.throwIfAborted();
95
+ if (response.status === 401 || response.status === 403) {
96
+ await response.body?.cancel();
97
+ throw new AuthError("MCP server needs authentication; use /mcp auth-start.", {
98
+ phase: "auth",
99
+ retriable: false,
100
+ });
101
+ }
65
102
  if (response.status === 202 || response.status === 204)
66
103
  return;
67
104
  const mediaType = response.headers.get("Content-Type")?.split(";")[0].trim().toLowerCase();
@@ -71,35 +108,64 @@ export class ModernHttpTransport {
71
108
  if (!response.ok && !("error" in incoming)) {
72
109
  throw new ModernTransportError(`Modern MCP POST failed with HTTP ${response.status}`);
73
110
  }
74
- this.#abort.signal.throwIfAborted();
75
- this.onmessage?.(incoming);
111
+ signal.throwIfAborted();
112
+ this.#deliver(incoming, id);
76
113
  }
77
114
  else if (response.ok && mediaType === "text/event-stream" && response.body) {
78
- await this.#consumeSse(response.body, "method" in message && "id" in message ? message.id : undefined);
115
+ await this.#consumeSse(response.body, id, signal);
79
116
  }
80
117
  else {
81
118
  throw new ModernTransportError(`Modern MCP POST failed with HTTP ${response.status} (${mediaType ?? "no content type"})`);
82
119
  }
83
120
  }
84
121
  catch (error) {
85
- const failure = error instanceof Error ? error : new ModernTransportError(String(error));
122
+ const failure = error instanceof McpMrtrError || error instanceof AuthError
123
+ ? error
124
+ : new McpMrtrError("MCP_MRTR_TRANSPORT_FAILED");
86
125
  this.onerror?.(failure);
87
126
  throw failure;
88
127
  }
128
+ finally {
129
+ if (id !== undefined)
130
+ this.#attempts.delete(id);
131
+ if (id !== undefined)
132
+ this.#toolLists.delete(id);
133
+ }
134
+ }
135
+ #deliver(incoming, expected) {
136
+ if ("id" in incoming && (expected === undefined || incoming.id !== expected))
137
+ throw new McpMrtrError("MCP_MRTR_INVALID_RESULT");
138
+ if ("id" in incoming && "result" in incoming && this.#toolLists.has(incoming.id)) {
139
+ const result = incoming.result;
140
+ if (Array.isArray(result.tools)) {
141
+ incoming = {
142
+ ...incoming,
143
+ result: {
144
+ ...result,
145
+ tools: this.#parameterHeaders.filterTools(result.tools, this.#toolLists.get(incoming.id) === true),
146
+ },
147
+ };
148
+ }
149
+ }
150
+ const safe = sanitizeModernMessage(incoming);
151
+ if (safe)
152
+ this.onmessage?.(safe);
89
153
  }
90
- async #consumeSse(stream, requestId) {
154
+ async #consumeSse(stream, requestId, signal) {
91
155
  const reader = stream.getReader();
92
156
  const decoder = new TextDecoder();
93
157
  const aborted = Promise.withResolvers();
94
- const onabort = () => aborted.reject(this.#abort.signal.reason);
95
- this.#abort.signal.addEventListener("abort", onabort, { once: true });
158
+ const onabort = () => aborted.reject(signal.reason);
159
+ signal.addEventListener("abort", onabort, { once: true });
160
+ if (signal.aborted)
161
+ onabort();
96
162
  let buffer = "";
97
163
  let data = [];
98
164
  let event = "";
99
165
  try {
100
166
  while (true) {
101
167
  const { value, done } = await Promise.race([reader.read(), aborted.promise]);
102
- this.#abort.signal.throwIfAborted();
168
+ signal.throwIfAborted();
103
169
  buffer += done ? decoder.decode() : decoder.decode(value, { stream: true });
104
170
  while (true) {
105
171
  const match = /[\r\n]/.exec(buffer);
@@ -114,7 +180,7 @@ export class ModernHttpTransport {
114
180
  if (line === "") {
115
181
  if (data.length > 0 && (event === "" || event === "message")) {
116
182
  const incoming = JSONRPCMessageSchema.parse(JSON.parse(data.join("\n")));
117
- this.onmessage?.(incoming);
183
+ this.#deliver(incoming, requestId);
118
184
  if ("id" in incoming && !("method" in incoming) && incoming.id === requestId)
119
185
  return;
120
186
  }
@@ -139,7 +205,7 @@ export class ModernHttpTransport {
139
205
  }
140
206
  }
141
207
  finally {
142
- this.#abort.signal.removeEventListener("abort", onabort);
208
+ signal.removeEventListener("abort", onabort);
143
209
  await reader.cancel();
144
210
  reader.releaseLock();
145
211
  }
@@ -1,4 +1,4 @@
1
- import { Buffer } from "node:buffer";
1
+ import { encodeMcpHeaderValue } from "./parameter-headers.js";
2
2
  export const MODERN_PROTOCOL_VERSION = "2026-07-28";
3
3
  export const MODERN_ERROR_CODES = {
4
4
  HeaderMismatch: -32020,
@@ -24,9 +24,17 @@ export function buildModernRequest(id, method, clientInfo, clientCapabilities, p
24
24
  /** Rewrite an outgoing SDK request or notification into the modern wire shape at the negotiated version. */
25
25
  export function modernizeRequest(id, method, clientInfo, clientCapabilities, version, params) {
26
26
  const built = buildModernRequest(id, method, clientInfo, clientCapabilities, params);
27
+ const capabilities = params?._meta?.["io.modelcontextprotocol/clientCapabilities"];
27
28
  return {
28
29
  ...built,
29
- params: { ...built.params, _meta: { ...built.params._meta, "io.modelcontextprotocol/protocolVersion": version } },
30
+ params: {
31
+ ...built.params,
32
+ _meta: {
33
+ ...built.params._meta,
34
+ "io.modelcontextprotocol/clientCapabilities": isRecord(capabilities) ? capabilities : clientCapabilities,
35
+ "io.modelcontextprotocol/protocolVersion": version,
36
+ },
37
+ },
30
38
  };
31
39
  }
32
40
  /** Derive protocol headers from the same request that will be serialized. */
@@ -41,11 +49,7 @@ export function deriveModernHeaders(request) {
41
49
  ? request.params.name
42
50
  : undefined;
43
51
  if (typeof name === "string") {
44
- // Encode the sentinel itself too, so a literal name cannot be mistaken for encoded text.
45
- headers["Mcp-Name"] =
46
- /^[\x20-\x7E]*$/.test(name) && !name.startsWith("base64:")
47
- ? name
48
- : `base64:${Buffer.from(name, "utf8").toString("base64url")}`;
52
+ headers["Mcp-Name"] = encodeMcpHeaderValue(name);
49
53
  }
50
54
  return headers;
51
55
  }
@@ -1,8 +1,12 @@
1
- import type { ExtensionAPI } from "../../types.ts";
1
+ import type { ExtensionAPI, ExtensionUIContext } from "../../types.ts";
2
2
  import type { McpCachedServerCatalog } from "./catalog-cache.ts";
3
3
  import type { ServerConnection } from "./connection.ts";
4
4
  import type { McpService } from "./service.ts";
5
+ import { type McpRequestOrigin } from "./request-origin.ts";
5
6
  export interface McpPromptServer {
7
+ readonly ensureFresh?: () => Promise<void>;
8
+ readonly requestOrigin?: () => McpRequestOrigin | undefined;
9
+ readonly bindPresentation?: (ui: ExtensionUIContext) => void;
6
10
  readonly server: string;
7
11
  readonly connection: ServerConnection;
8
12
  readonly requestTimeoutMs?: number;
@@ -10,6 +10,8 @@
10
10
  // last-wins, and prompts removed server-side keep a stale command until the
11
11
  // next session — invoking one surfaces the server error as a notice.
12
12
  import { createMcpLogger } from "./log.js";
13
+ import { mcpRequestOptions } from "./request-origin.js";
14
+ import { ensureMcpToolCallConnection } from "./health.js";
13
15
  const registeredCommandNames = new Set();
14
16
  /** Test seam: forget which commands were registered (module-level last-wins). */
15
17
  export function resetMcpPromptCommandsForTests() {
@@ -34,10 +36,13 @@ export function registerMcpPromptCommands(serviceOrPi, piOrServers, serversArg)
34
36
  description: prompt.description ?? `MCP prompt ${prompt.name} from ${server.server}`,
35
37
  handler: async (_args, ctx) => {
36
38
  try {
39
+ server.bindPresentation?.(ctx.ui);
37
40
  const collected = await collectPromptArguments(prompt.arguments ?? [], ctx);
38
41
  if (collected === undefined)
39
42
  return;
40
- const result = await server.connection.client.getPrompt({ arguments: collected, name: prompt.name }, { timeout: server.requestTimeoutMs });
43
+ if (server.requestOrigin)
44
+ await ensureMcpToolCallConnection(server.connection, server.ensureFresh);
45
+ const result = await server.connection.client.getPrompt({ arguments: collected, name: prompt.name }, mcpRequestOptions(server.requestOrigin?.(), { timeout: server.requestTimeoutMs }));
41
46
  ctx.ui.setEditorText(flattenPromptMessages(result.messages));
42
47
  }
43
48
  catch (error) {
@@ -0,0 +1,47 @@
1
+ import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
+ import type { RequestOptions } from "@modelcontextprotocol/sdk/shared/protocol.js";
3
+ import type { McpMrtrPresentation } from "../../../mcp-mrtr-presentations.ts";
4
+ import type { McpFormPresentation } from "../../../mcp-form-types.ts";
5
+ import type { ServerConnection } from "./connection.ts";
6
+ declare const originOption: unique symbol;
7
+ export declare const physicalMcpClient: (client: Client) => Client;
8
+ export declare function registerMcpClientAlias(alias: Client, physical: Client): void;
9
+ export interface McpRequestOrigin {
10
+ readonly binding: object;
11
+ readonly connection: ServerConnection;
12
+ readonly signal: AbortSignal;
13
+ readonly presentation?: McpMrtrPresentation;
14
+ readonly privateForm?: McpFormPresentation;
15
+ readonly privateFormsEnforced?: boolean;
16
+ readonly serverLabel: string;
17
+ readonly serverSource?: "user" | "project" | "plugin";
18
+ readonly isCurrent: () => boolean;
19
+ readonly resolveAuthority: (signal: AbortSignal) => Promise<unknown>;
20
+ }
21
+ export declare function beginLegacyMcpOperation(client: Client, origin: McpRequestOrigin, caller?: AbortSignal): () => void;
22
+ export declare function legacyMcpOperation(client: Client): "ambiguous" | {
23
+ readonly origin: McpRequestOrigin;
24
+ readonly signal: AbortSignal;
25
+ } | undefined;
26
+ /** Issued by the binding-specific registration boundary, never from JSON/model arguments. */
27
+ export declare function createMcpRequestOrigin(origin: McpRequestOrigin): McpRequestOrigin;
28
+ export declare function mcpRequestOptions(origin: McpRequestOrigin | undefined, options?: RequestOptions): RequestOptions & {
29
+ [originOption]?: McpRequestOrigin;
30
+ };
31
+ export declare function takeMcpRequestOrigin(options: RequestOptions | undefined, client: Client): {
32
+ origin: McpRequestOrigin | undefined;
33
+ sdkOptions: {
34
+ relatedRequestId?: import("@modelcontextprotocol/sdk/types").RequestId;
35
+ resumptionToken?: string;
36
+ onresumptiontoken?: (token: string) => void;
37
+ onprogress?: import("@modelcontextprotocol/sdk/shared/protocol.js").ProgressCallback;
38
+ signal?: AbortSignal;
39
+ timeout?: number;
40
+ resetTimeoutOnProgress?: boolean;
41
+ maxTotalTimeout?: number;
42
+ task?: import("@modelcontextprotocol/sdk/types").TaskCreationParams;
43
+ relatedTask?: import("@modelcontextprotocol/sdk/types").RelatedTaskMetadata;
44
+ };
45
+ };
46
+ export {};
47
+ //# sourceMappingURL=request-origin.d.ts.map
@@ -0,0 +1,59 @@
1
+ import { McpMrtrError } from "./modern/errors.js";
2
+ const originOption = Symbol("mcp-request-origin");
3
+ const origins = new WeakSet();
4
+ const aliases = new WeakMap();
5
+ export const physicalMcpClient = (client) => aliases.get(client) ?? client;
6
+ export function registerMcpClientAlias(alias, physical) {
7
+ aliases.set(alias, physical);
8
+ }
9
+ const legacyOperations = new WeakMap();
10
+ export function beginLegacyMcpOperation(client, origin, caller) {
11
+ const lifetime = new AbortController();
12
+ const operation = {
13
+ origin,
14
+ signal: AbortSignal.any([origin.signal, lifetime.signal, ...(caller ? [caller] : [])]),
15
+ };
16
+ let operations = legacyOperations.get(client);
17
+ if (!operations) {
18
+ operations = new Set();
19
+ legacyOperations.set(client, operations);
20
+ }
21
+ operations.add(operation);
22
+ return () => {
23
+ operations.delete(operation);
24
+ lifetime.abort();
25
+ if (operations.size === 0)
26
+ legacyOperations.delete(client);
27
+ };
28
+ }
29
+ export function legacyMcpOperation(client) {
30
+ const operations = legacyOperations.get(client);
31
+ if (operations && operations.size > 1)
32
+ return "ambiguous";
33
+ if (operations?.size !== 1)
34
+ return undefined;
35
+ const operation = operations.values().next().value;
36
+ return operation && !operation.signal.aborted && operation.origin.isCurrent() ? operation : undefined;
37
+ }
38
+ /** Issued by the binding-specific registration boundary, never from JSON/model arguments. */
39
+ export function createMcpRequestOrigin(origin) {
40
+ origins.add(origin);
41
+ return origin;
42
+ }
43
+ export function mcpRequestOptions(origin, options = {}) {
44
+ return origin === undefined ? options : { ...options, [originOption]: origin };
45
+ }
46
+ export function takeMcpRequestOrigin(options, client) {
47
+ const supplied = options;
48
+ const origin = supplied?.[originOption];
49
+ const { [originOption]: _origin, ...sdkOptions } = supplied ?? {};
50
+ if (origin !== undefined) {
51
+ if (!origins.has(origin) || !origin.isCurrent())
52
+ throw new McpMrtrError("MCP_MRTR_CONTEXT_CHANGED");
53
+ const candidate = origin.connection.client;
54
+ if (physicalMcpClient(candidate) !== client)
55
+ throw new McpMrtrError("MCP_MRTR_CONTEXT_CHANGED");
56
+ }
57
+ return { origin, sdkOptions };
58
+ }
59
+ //# sourceMappingURL=request-origin.js.map
@@ -6,7 +6,10 @@ import type { ServerConnection } from "./connection.ts";
6
6
  import type { McpToolDetails } from "./expose/register.ts";
7
7
  import type { McpOutputArtifacts } from "./guard/output-guard.ts";
8
8
  import { applyMcpOutputGuard } from "./guard/output-guard.ts";
9
+ import { type McpRequestOrigin } from "./request-origin.ts";
9
10
  export interface McpResourceServer {
11
+ readonly ensureFresh?: () => Promise<void>;
12
+ readonly requestOrigin?: () => McpRequestOrigin | undefined;
10
13
  readonly server: string;
11
14
  readonly connection: ServerConnection;
12
15
  readonly agentDir?: string;
@@ -22,7 +25,7 @@ export declare function ensureMcpResourceSubscriptions(client: Client, resources
22
25
  uri: string;
23
26
  }>): Promise<void>;
24
27
  /** Read one resource and flatten its contents to guarded text. */
25
- export declare function readMcpResourceAsText(server: McpResourceServer, uri: string): Promise<string>;
28
+ export declare function readMcpResourceAsText(server: McpResourceServer, uri: string, signal?: AbortSignal): Promise<string>;
26
29
  declare const ListParams: Type.TObject<{
27
30
  server: Type.TOptional<Type.TString>;
28
31
  }>;
@@ -16,6 +16,9 @@ import { redactResolvedReferenceData, redactResolvedReferenceValues } from "../.
16
16
  import { ToolExecError } from "./errors.js";
17
17
  import { applyMcpOutputGuard } from "./guard/output-guard.js";
18
18
  import { ResourceUpdatedNotificationSchema, registerMcpNotificationHandler } from "./notification-schemas.js";
19
+ import { mcpRequestOptions } from "./request-origin.js";
20
+ import { McpMrtrError } from "./modern/errors.js";
21
+ import { ensureMcpToolCallConnection } from "./health.js";
19
22
  /** Route resource updated notifications into the shared refresh path. */
20
23
  export function subscribeMcpResourceUpdated(client, onChange) {
21
24
  registerMcpNotificationHandler(client, ResourceUpdatedNotificationSchema, () => {
@@ -29,13 +32,20 @@ export async function ensureMcpResourceSubscriptions(client, resources) {
29
32
  await Promise.all(resources.map((resource) => client.subscribeResource({ uri: resource.uri }).catch(() => undefined)));
30
33
  }
31
34
  /** Read one resource and flatten its contents to guarded text. */
32
- export async function readMcpResourceAsText(server, uri) {
35
+ export async function readMcpResourceAsText(server, uri, signal) {
33
36
  let contents;
34
37
  try {
35
- const result = await server.connection.client.readResource({ uri }, { timeout: server.requestTimeoutMs });
38
+ if (server.requestOrigin)
39
+ await ensureMcpToolCallConnection(server.connection, server.ensureFresh);
40
+ const result = await server.connection.client.readResource({ uri }, mcpRequestOptions(server.requestOrigin?.(), {
41
+ timeout: server.requestTimeoutMs,
42
+ ...(signal === undefined ? {} : { signal }),
43
+ }));
36
44
  contents = result.contents;
37
45
  }
38
46
  catch (error) {
47
+ if (error instanceof McpMrtrError)
48
+ throw error;
39
49
  throw new ToolExecError(redactResolvedReferenceValues(`Failed to read MCP resource ${uri}: ${error instanceof Error ? error.message : String(error)}`), {
40
50
  phase: "call",
41
51
  serverName: server.server,
@@ -93,7 +103,7 @@ export function createMcpResourceTools(servers) {
93
103
  description: "Read one MCP resource by server name and URI; returns its (guarded) content.",
94
104
  parameters: ReadParams,
95
105
  executionMode: "parallel",
96
- async execute(_id, params) {
106
+ async execute(_id, params, signal) {
97
107
  const entry = servers().find((candidate) => candidate.server === params.server);
98
108
  if (entry === undefined) {
99
109
  throw new ToolExecError(redactResolvedReferenceValues(`Unknown MCP server '${params.server}' for resource ${params.uri}.`), {
@@ -101,7 +111,7 @@ export function createMcpResourceTools(servers) {
101
111
  serverName: params.server,
102
112
  });
103
113
  }
104
- const text = await readMcpResourceAsText(entry, params.uri);
114
+ const text = await readMcpResourceAsText(entry, params.uri, signal);
105
115
  return {
106
116
  content: [{ type: "text", text }],
107
117
  details: redactResolvedReferenceData({
@@ -137,6 +147,11 @@ export async function expandMcpResourceMentions(text, servers) {
137
147
  expanded = expanded.replace(mention, `<mcp-resource server="${serverName}" uri="${uri}">\n${body}\n</mcp-resource>`);
138
148
  }
139
149
  catch (error) {
150
+ if (error instanceof McpMrtrError &&
151
+ ["MCP_MRTR_CANCELLED", "MCP_MRTR_CONTEXT_CHANGED", "MCP_MRTR_TIMEOUT"].includes(error.code)) {
152
+ notices.push("MCP resource request canceled; mention left unchanged.");
153
+ continue;
154
+ }
140
155
  notices.push(`@mcp mention left as-is: ${error instanceof Error ? error.message : String(error)}`);
141
156
  }
142
157
  }
@@ -15,9 +15,11 @@
15
15
  */
16
16
  /** `Client` — the MCP client class, built once per transport connection. */
17
17
  export declare const loadMcpSdkClient: () => Promise<typeof import("@modelcontextprotocol/sdk/client/index.js")>;
18
+ /** Compatibility parser for final caller schemas at the modern request boundary. */
19
+ export declare const loadMcpSdkZodCompat: () => Promise<typeof import("@modelcontextprotocol/sdk/server/zod-compat.js")>;
18
20
  /** `StdioClientTransport` + `getDefaultEnvironment` — stdio servers and the stdio diagnostic rerun. */
19
21
  export declare const loadMcpSdkStdioTransport: () => Promise<typeof import("@modelcontextprotocol/sdk/client/stdio.js")>;
20
- /** Protocol schemas (currently only the elicitation request schema). */
22
+ /** SDK-owned protocol schemas for elicitation and modern request results. */
21
23
  export declare const loadMcpSdkTypes: () => Promise<typeof import("@modelcontextprotocol/sdk/types.js")>;
22
24
  /** OAuth 2.1 discovery, authorization, token exchange and refresh. */
23
25
  export declare const loadMcpSdkAuth: () => Promise<typeof import("@modelcontextprotocol/sdk/client/auth.js")>;
@@ -36,9 +36,11 @@ function memoizeLoader(load) {
36
36
  }
37
37
  /** `Client` — the MCP client class, built once per transport connection. */
38
38
  export const loadMcpSdkClient = memoizeLoader(() => import("@modelcontextprotocol/sdk/client/index.js"));
39
+ /** Compatibility parser for final caller schemas at the modern request boundary. */
40
+ export const loadMcpSdkZodCompat = memoizeLoader(() => import("@modelcontextprotocol/sdk/server/zod-compat.js"));
39
41
  /** `StdioClientTransport` + `getDefaultEnvironment` — stdio servers and the stdio diagnostic rerun. */
40
42
  export const loadMcpSdkStdioTransport = memoizeLoader(() => import("@modelcontextprotocol/sdk/client/stdio.js"));
41
- /** Protocol schemas (currently only the elicitation request schema). */
43
+ /** SDK-owned protocol schemas for elicitation and modern request results. */
42
44
  export const loadMcpSdkTypes = memoizeLoader(() => import("@modelcontextprotocol/sdk/types.js"));
43
45
  let authModule;
44
46
  /** OAuth 2.1 discovery, authorization, token exchange and refresh. */
@@ -1,10 +1,13 @@
1
- import type { ExtensionAPI } from "../../types.ts";
1
+ import type { ExtensionAPI, ExtensionUIContext } from "../../types.ts";
2
2
  import type { ToolSearchService } from "../tool-search/service.ts";
3
3
  import type { ResolvedMcpConfig } from "./config-schema.ts";
4
4
  import type { McpSessionRegistration } from "./expose/session.ts";
5
5
  import type { McpConnectionEntry } from "./service-types.ts";
6
+ import type { McpRequestOrigin } from "./request-origin.ts";
6
7
  type McpToolRegistrar = Pick<ExtensionAPI, "getActiveTools" | "setActiveTools" | "registerTool">;
7
8
  export interface McpServiceDirectToolRegistrationOptions {
9
+ readonly requestOrigin?: (entry: McpConnectionEntry) => McpRequestOrigin;
10
+ readonly bindPresentation?: (ui: ExtensionUIContext) => void;
8
11
  readonly refreshActiveSetWhenEmpty?: boolean;
9
12
  readonly onRegistered?: (entry: McpConnectionEntry, identity: string) => void;
10
13
  readonly isCurrent?: (entry: McpConnectionEntry) => boolean;
@@ -39,6 +39,7 @@ export async function registerMcpServiceDirectTools(pi, config, entries, toolSea
39
39
  ensureFresh: () => entry.authPlan?.refresh?.ensureFresh().then(() => undefined) ?? Promise.resolve(),
40
40
  outputGuard: config.settings.outputGuard,
41
41
  invocation,
42
+ requestOrigin: () => options.requestOrigin?.(entry),
42
43
  });
43
44
  return computeMcpExposurePolicy(current, serverConfig, config.settings).filteredEntries.find((candidate) => candidate.tool === tool);
44
45
  },
@@ -54,6 +55,8 @@ export async function registerMcpServiceDirectTools(pi, config, entries, toolSea
54
55
  startupCatalogPending,
55
56
  localCatalogOnly: true,
56
57
  invocation,
58
+ requestOrigin: () => options.requestOrigin?.(entry),
59
+ bindPresentation: options.bindPresentation,
57
60
  onRegistered: (identity) => options.onRegistered?.(entry, identity),
58
61
  connection: entry.connection,
59
62
  ensureFresh: () => entry.authPlan?.refresh?.ensureFresh().then(() => undefined) ?? Promise.resolve(),
@@ -1,4 +1,4 @@
1
- import type { ExtensionContext, SessionShutdownEvent, SessionStartEvent } from "../../types.ts";
1
+ import type { ExtensionContext, ExtensionUIContext, SessionShutdownEvent, SessionStartEvent } from "../../types.ts";
2
2
  import type { ServerAuthPlan } from "./auth/context.ts";
3
3
  import type { McpCachedServerCatalog } from "./catalog-cache.ts";
4
4
  import type { ResolvedMcpServer } from "./config-schema.ts";
@@ -65,6 +65,7 @@ export type McpWireStatusSnapshot = {
65
65
  readonly servers: readonly McpWireStatusServer[];
66
66
  };
67
67
  export type McpSessionContext = Pick<ExtensionContext, "cwd" | "isProjectTrusted"> & {
68
+ ui?: ExtensionUIContext;
68
69
  mode?: ExtensionContext["mode"];
69
70
  /**
70
71
  * Session history access, present on the real ExtensionContext. Only
@@ -89,6 +89,7 @@ export declare class McpService {
89
89
  setMcpInstructions(instructions: string): void;
90
90
  getMcpInstructions(): string;
91
91
  setMcpElicitationUiProvider(provider: (() => McpElicitationUi | undefined) | undefined): void;
92
+ bindMcpMrtrPresentation(pi: object, ui: ExtensionUIContext): void;
92
93
  getMcpElicitationUi(): McpElicitationUi | undefined;
93
94
  isMcpPromptCommandRegistered(name: string): boolean;
94
95
  markMcpPromptCommandRegistered(name: string): void;