@remnic/core 9.66.7 → 9.66.8

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 (224) hide show
  1. package/dist/access-admin-ops-surface.d.ts +1 -1
  2. package/dist/access-admin-ops-surface.js +35 -35
  3. package/dist/access-authorization-probe.d.ts +1 -1
  4. package/dist/access-authorization-probe.js +36 -36
  5. package/dist/access-boundary.d.ts +1 -1
  6. package/dist/access-boundary.js +35 -35
  7. package/dist/access-cli.js +85 -84
  8. package/dist/access-cli.js.map +1 -1
  9. package/dist/access-extraction-force-flush.d.ts +1 -1
  10. package/dist/access-extraction-force-flush.js +35 -35
  11. package/dist/access-http-lcm-compaction.d.ts +1 -1
  12. package/dist/access-http-lifecycle-flush.d.ts +1 -1
  13. package/dist/access-http-offline-stream.d.ts +1 -1
  14. package/dist/access-http-query.js +37 -37
  15. package/dist/access-http.d.ts +2 -2
  16. package/dist/access-http.js +50 -49
  17. package/dist/access-identity-continuity-surface.js +35 -35
  18. package/dist/access-lcm-surface.d.ts +1 -1
  19. package/dist/access-lcm-surface.js +35 -35
  20. package/dist/access-mcp-sse.d.ts +20 -0
  21. package/dist/access-mcp-sse.js +8 -0
  22. package/dist/access-mcp-sse.js.map +1 -0
  23. package/dist/access-mcp.d.ts +1 -1
  24. package/dist/access-mcp.js +42 -42
  25. package/dist/access-namespace-preflight.js +35 -35
  26. package/dist/access-observe-write-surface.d.ts +1 -1
  27. package/dist/access-observe-write-surface.js +35 -35
  28. package/dist/access-offline-manifest.js +5 -5
  29. package/dist/access-operations-batch.js +36 -36
  30. package/dist/access-operations.d.ts +6 -6
  31. package/dist/access-operations.js +40 -40
  32. package/dist/access-recall-concurrency.d.ts +1 -1
  33. package/dist/access-recall-concurrency.js +35 -35
  34. package/dist/access-recall-response.d.ts +1 -1
  35. package/dist/access-recall-response.js +35 -35
  36. package/dist/access-recall-surface.d.ts +1 -1
  37. package/dist/access-recall-surface.js +35 -35
  38. package/dist/access-schema.d.ts +89 -89
  39. package/dist/access-service-helpers.d.ts +1 -1
  40. package/dist/access-service.d.ts +1 -1
  41. package/dist/access-service.js +35 -35
  42. package/dist/access-surface-catalog.d.ts +1 -1
  43. package/dist/access-surface-catalog.js +37 -37
  44. package/dist/active-recall.js +1 -1
  45. package/dist/briefing.js +12 -12
  46. package/dist/buffer.js +3 -3
  47. package/dist/causal-consolidation.js +13 -13
  48. package/dist/{chunk-BWZVFWMX.js → chunk-2MYBDWUY.js} +2 -2
  49. package/dist/{chunk-TNXNECAN.js → chunk-2OXIPF3A.js} +4 -4
  50. package/dist/{chunk-ZKE227GO.js → chunk-2V4YPDIX.js} +2 -2
  51. package/dist/{chunk-XJE6NHLT.js → chunk-2YDU4VR7.js} +46 -46
  52. package/dist/{chunk-POSD62F5.js → chunk-5IYJWCLC.js} +2 -2
  53. package/dist/{chunk-2W4MTU4P.js → chunk-5S6TQ36T.js} +31 -25
  54. package/dist/chunk-5S6TQ36T.js.map +1 -0
  55. package/dist/{chunk-CWPTYKCF.js → chunk-5UP5R37C.js} +3 -3
  56. package/dist/{chunk-D35XLYQ7.js → chunk-6BYJQ3L2.js} +4 -4
  57. package/dist/{chunk-CEK6AHIG.js → chunk-6ULLVM2V.js} +7 -7
  58. package/dist/{chunk-ABPLUXVY.js → chunk-6ZRLH4VS.js} +3 -3
  59. package/dist/{chunk-VVWOEKOZ.js → chunk-7X63THIR.js} +40 -40
  60. package/dist/{chunk-IN6D6BJG.js → chunk-AVYNKPFS.js} +14 -14
  61. package/dist/{chunk-YV4ST46A.js → chunk-AZLSFKGU.js} +4 -4
  62. package/dist/{chunk-MSXUEYH6.js → chunk-BDGWA5GK.js} +2 -2
  63. package/dist/{chunk-NMFPYAJ6.js → chunk-EKUABFXQ.js} +7 -7
  64. package/dist/{chunk-2C34EKRJ.js → chunk-FI6FT6YX.js} +2 -2
  65. package/dist/{chunk-OMGQAXUK.js → chunk-FNK4KIYR.js} +2 -2
  66. package/dist/{chunk-MGVOBLUK.js → chunk-FY62KW6Q.js} +2 -2
  67. package/dist/{chunk-XZBXINXZ.js → chunk-GIC6BASP.js} +4 -4
  68. package/dist/{chunk-PPAKBRI7.js → chunk-I2TIUGLC.js} +2 -2
  69. package/dist/{chunk-GNWLTCKC.js → chunk-IE7N2YK6.js} +2 -2
  70. package/dist/{chunk-Y3QQZOZ4.js → chunk-K7NUAY36.js} +15 -15
  71. package/dist/{chunk-7O24F2VS.js → chunk-KH2LZMD5.js} +2 -2
  72. package/dist/{chunk-QD55QEEY.js → chunk-KKGWB2LG.js} +5 -5
  73. package/dist/{chunk-FHY6HIAY.js → chunk-KMPEBHH5.js} +2 -2
  74. package/dist/{chunk-52DBRDP5.js → chunk-LWI43S46.js} +1 -1
  75. package/dist/{chunk-V6LJENGW.js → chunk-MBXTGOFR.js} +2 -2
  76. package/dist/{chunk-MAWHEUAA.js → chunk-MFIFCXS3.js} +2 -2
  77. package/dist/{chunk-KSZKQTZS.js → chunk-MWLBRFLY.js} +2 -2
  78. package/dist/{chunk-RACZB5HT.js → chunk-N37X5RU7.js} +4 -4
  79. package/dist/{chunk-6LMRUFBH.js → chunk-NEDBOZ7G.js} +2 -2
  80. package/dist/{chunk-BEUB5ABO.js → chunk-NEWY74GS.js} +2 -2
  81. package/dist/{chunk-AG46P24C.js → chunk-NNBZS3DY.js} +4 -4
  82. package/dist/{chunk-HQI2OQB5.js → chunk-O2RJZR6J.js} +5 -5
  83. package/dist/{chunk-6FUMA6AA.js → chunk-OERZXNSK.js} +2 -2
  84. package/dist/{chunk-KDYU3KZT.js → chunk-OMA2B7GY.js} +5 -5
  85. package/dist/{chunk-726CXBJO.js → chunk-QK375NXZ.js} +2 -2
  86. package/dist/{chunk-6TMIW4HE.js → chunk-RI4CMRTZ.js} +2 -2
  87. package/dist/{chunk-O7ERGZDA.js → chunk-RJ57OOCD.js} +2 -2
  88. package/dist/{chunk-SFJPCNBI.js → chunk-RPHCLGQ7.js} +2 -2
  89. package/dist/{chunk-42GDJAU7.js → chunk-S63O62DN.js} +2 -2
  90. package/dist/chunk-SRT6SHVZ.js +34 -0
  91. package/dist/chunk-SRT6SHVZ.js.map +1 -0
  92. package/dist/{chunk-2SW3N5HX.js → chunk-SVATDJNY.js} +2 -2
  93. package/dist/{chunk-4NKIGSJM.js → chunk-UJICQ77Y.js} +4 -4
  94. package/dist/{chunk-4COPICMD.js → chunk-ULTK4NZY.js} +6 -6
  95. package/dist/{chunk-DB2MKBQ7.js → chunk-UMSTKKMV.js} +2 -2
  96. package/dist/{chunk-Z3YEGSR4.js → chunk-UMZUKOJU.js} +4 -4
  97. package/dist/{chunk-PORKQEAW.js → chunk-UOQZOV2J.js} +99 -99
  98. package/dist/{chunk-PMNN247H.js → chunk-VYIPVLRX.js} +2 -2
  99. package/dist/{chunk-J2GY4UCR.js → chunk-X32ALJQB.js} +2 -2
  100. package/dist/{chunk-OCYRMVPT.js → chunk-XDUQR5R7.js} +2 -2
  101. package/dist/{chunk-QVEKPVCG.js → chunk-XORZMKVF.js} +3 -3
  102. package/dist/{chunk-THZ5UEQZ.js → chunk-XSPVSEAN.js} +3 -3
  103. package/dist/{chunk-FKBRP3J2.js → chunk-XUKS57W7.js} +2 -2
  104. package/dist/{chunk-ZW4SVXEY.js → chunk-XVCYV7WQ.js} +6 -6
  105. package/dist/{chunk-67U6UK5Q.js → chunk-YSYHSZWD.js} +2 -2
  106. package/dist/{cli-BOuEb4Ia.d.ts → cli-CsfFMe6n.d.ts} +1 -1
  107. package/dist/cli.d.ts +2 -2
  108. package/dist/cli.js +65 -64
  109. package/dist/coding/export-okf-codegraph.js +12 -12
  110. package/dist/compounding/engine.js +12 -12
  111. package/dist/config.js +1 -1
  112. package/dist/connectors/codex-materialize-runner.js +12 -12
  113. package/dist/connectors/index.js +13 -13
  114. package/dist/convergence-refresh.js +16 -16
  115. package/dist/corpus-watermark.js +15 -15
  116. package/dist/entity-retrieval.js +12 -12
  117. package/dist/explicit-capture.js +1 -1
  118. package/dist/external-wiki-access.d.ts +1 -1
  119. package/dist/external-wiki-access.js +36 -36
  120. package/dist/external-wiki-mcp-tools.d.ts +1 -1
  121. package/dist/extraction.js +25 -25
  122. package/dist/harmonic-retrieval.js +1 -1
  123. package/dist/index.d.ts +680 -680
  124. package/dist/index.js +135 -134
  125. package/dist/lcm/engine.js +2 -2
  126. package/dist/lcm/index.js +4 -4
  127. package/dist/location/index.js +45 -45
  128. package/dist/maintenance/memory-governance.js +13 -13
  129. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +13 -13
  130. package/dist/maintenance/rebuild-memory-projection.js +15 -15
  131. package/dist/mcp-memory-inspector-app.d.ts +1 -1
  132. package/dist/memory-subject-cli.d.ts +1 -1
  133. package/dist/memory-subject.d.ts +1 -1
  134. package/dist/namespaces/migrate.js +13 -13
  135. package/dist/namespaces/search.js +6 -6
  136. package/dist/namespaces/storage.js +12 -12
  137. package/dist/offline-sync-impression-drain.js +4 -4
  138. package/dist/operator-doctor-corpus.js +16 -16
  139. package/dist/operator-toolkit.js +24 -24
  140. package/dist/orchestration/maintenance.js +26 -26
  141. package/dist/orchestrator.js +110 -109
  142. package/dist/{public-http-CmQRjQCD.d.ts → public-http-Cwvl4QZr.d.ts} +1 -1
  143. package/dist/resume-bundles.js +1 -1
  144. package/dist/schemas.d.ts +176 -176
  145. package/dist/search/factory.js +5 -5
  146. package/dist/search/index.js +5 -5
  147. package/dist/search/lancedb-backend.js +2 -2
  148. package/dist/search/meilisearch-backend.js +2 -2
  149. package/dist/search/orama-backend.js +2 -2
  150. package/dist/semantic-consolidation.js +13 -13
  151. package/dist/semantic-rule-promotion.js +12 -12
  152. package/dist/semantic-rule-verifier.js +12 -12
  153. package/dist/shared-context/manager.d.ts +6 -6
  154. package/dist/source-agent-qualifier.js +23 -23
  155. package/dist/storage.js +11 -11
  156. package/dist/support-passport/index.d.ts +4 -4
  157. package/dist/support-passport/index.js +38 -38
  158. package/dist/transfer/export-okf.js +13 -13
  159. package/dist/transfer/types.d.ts +78 -78
  160. package/dist/verified-recall.js +12 -12
  161. package/dist/who-knows-cli.d.ts +1 -1
  162. package/dist/who-knows-cli.js +13 -13
  163. package/dist/who-knows.d.ts +1 -1
  164. package/dist/who-knows.js +12 -12
  165. package/package.json +2 -2
  166. package/src/access-http.test.ts +41 -17
  167. package/src/access-http.ts +10 -10
  168. package/src/access-mcp-sse.ts +50 -0
  169. package/dist/chunk-2W4MTU4P.js.map +0 -1
  170. package/dist/{access-service-Dz6I9AZ-.d.ts → access-service-C2tV2-uL.d.ts} +64 -64
  171. /package/dist/{chunk-BWZVFWMX.js.map → chunk-2MYBDWUY.js.map} +0 -0
  172. /package/dist/{chunk-TNXNECAN.js.map → chunk-2OXIPF3A.js.map} +0 -0
  173. /package/dist/{chunk-ZKE227GO.js.map → chunk-2V4YPDIX.js.map} +0 -0
  174. /package/dist/{chunk-XJE6NHLT.js.map → chunk-2YDU4VR7.js.map} +0 -0
  175. /package/dist/{chunk-POSD62F5.js.map → chunk-5IYJWCLC.js.map} +0 -0
  176. /package/dist/{chunk-CWPTYKCF.js.map → chunk-5UP5R37C.js.map} +0 -0
  177. /package/dist/{chunk-D35XLYQ7.js.map → chunk-6BYJQ3L2.js.map} +0 -0
  178. /package/dist/{chunk-CEK6AHIG.js.map → chunk-6ULLVM2V.js.map} +0 -0
  179. /package/dist/{chunk-ABPLUXVY.js.map → chunk-6ZRLH4VS.js.map} +0 -0
  180. /package/dist/{chunk-VVWOEKOZ.js.map → chunk-7X63THIR.js.map} +0 -0
  181. /package/dist/{chunk-IN6D6BJG.js.map → chunk-AVYNKPFS.js.map} +0 -0
  182. /package/dist/{chunk-YV4ST46A.js.map → chunk-AZLSFKGU.js.map} +0 -0
  183. /package/dist/{chunk-MSXUEYH6.js.map → chunk-BDGWA5GK.js.map} +0 -0
  184. /package/dist/{chunk-NMFPYAJ6.js.map → chunk-EKUABFXQ.js.map} +0 -0
  185. /package/dist/{chunk-2C34EKRJ.js.map → chunk-FI6FT6YX.js.map} +0 -0
  186. /package/dist/{chunk-OMGQAXUK.js.map → chunk-FNK4KIYR.js.map} +0 -0
  187. /package/dist/{chunk-MGVOBLUK.js.map → chunk-FY62KW6Q.js.map} +0 -0
  188. /package/dist/{chunk-XZBXINXZ.js.map → chunk-GIC6BASP.js.map} +0 -0
  189. /package/dist/{chunk-PPAKBRI7.js.map → chunk-I2TIUGLC.js.map} +0 -0
  190. /package/dist/{chunk-GNWLTCKC.js.map → chunk-IE7N2YK6.js.map} +0 -0
  191. /package/dist/{chunk-Y3QQZOZ4.js.map → chunk-K7NUAY36.js.map} +0 -0
  192. /package/dist/{chunk-7O24F2VS.js.map → chunk-KH2LZMD5.js.map} +0 -0
  193. /package/dist/{chunk-QD55QEEY.js.map → chunk-KKGWB2LG.js.map} +0 -0
  194. /package/dist/{chunk-FHY6HIAY.js.map → chunk-KMPEBHH5.js.map} +0 -0
  195. /package/dist/{chunk-52DBRDP5.js.map → chunk-LWI43S46.js.map} +0 -0
  196. /package/dist/{chunk-V6LJENGW.js.map → chunk-MBXTGOFR.js.map} +0 -0
  197. /package/dist/{chunk-MAWHEUAA.js.map → chunk-MFIFCXS3.js.map} +0 -0
  198. /package/dist/{chunk-KSZKQTZS.js.map → chunk-MWLBRFLY.js.map} +0 -0
  199. /package/dist/{chunk-RACZB5HT.js.map → chunk-N37X5RU7.js.map} +0 -0
  200. /package/dist/{chunk-6LMRUFBH.js.map → chunk-NEDBOZ7G.js.map} +0 -0
  201. /package/dist/{chunk-BEUB5ABO.js.map → chunk-NEWY74GS.js.map} +0 -0
  202. /package/dist/{chunk-AG46P24C.js.map → chunk-NNBZS3DY.js.map} +0 -0
  203. /package/dist/{chunk-HQI2OQB5.js.map → chunk-O2RJZR6J.js.map} +0 -0
  204. /package/dist/{chunk-6FUMA6AA.js.map → chunk-OERZXNSK.js.map} +0 -0
  205. /package/dist/{chunk-KDYU3KZT.js.map → chunk-OMA2B7GY.js.map} +0 -0
  206. /package/dist/{chunk-726CXBJO.js.map → chunk-QK375NXZ.js.map} +0 -0
  207. /package/dist/{chunk-6TMIW4HE.js.map → chunk-RI4CMRTZ.js.map} +0 -0
  208. /package/dist/{chunk-O7ERGZDA.js.map → chunk-RJ57OOCD.js.map} +0 -0
  209. /package/dist/{chunk-SFJPCNBI.js.map → chunk-RPHCLGQ7.js.map} +0 -0
  210. /package/dist/{chunk-42GDJAU7.js.map → chunk-S63O62DN.js.map} +0 -0
  211. /package/dist/{chunk-2SW3N5HX.js.map → chunk-SVATDJNY.js.map} +0 -0
  212. /package/dist/{chunk-4NKIGSJM.js.map → chunk-UJICQ77Y.js.map} +0 -0
  213. /package/dist/{chunk-4COPICMD.js.map → chunk-ULTK4NZY.js.map} +0 -0
  214. /package/dist/{chunk-DB2MKBQ7.js.map → chunk-UMSTKKMV.js.map} +0 -0
  215. /package/dist/{chunk-Z3YEGSR4.js.map → chunk-UMZUKOJU.js.map} +0 -0
  216. /package/dist/{chunk-PORKQEAW.js.map → chunk-UOQZOV2J.js.map} +0 -0
  217. /package/dist/{chunk-PMNN247H.js.map → chunk-VYIPVLRX.js.map} +0 -0
  218. /package/dist/{chunk-J2GY4UCR.js.map → chunk-X32ALJQB.js.map} +0 -0
  219. /package/dist/{chunk-OCYRMVPT.js.map → chunk-XDUQR5R7.js.map} +0 -0
  220. /package/dist/{chunk-QVEKPVCG.js.map → chunk-XORZMKVF.js.map} +0 -0
  221. /package/dist/{chunk-THZ5UEQZ.js.map → chunk-XSPVSEAN.js.map} +0 -0
  222. /package/dist/{chunk-FKBRP3J2.js.map → chunk-XUKS57W7.js.map} +0 -0
  223. /package/dist/{chunk-ZW4SVXEY.js.map → chunk-XVCYV7WQ.js.map} +0 -0
  224. /package/dist/{chunk-67U6UK5Q.js.map → chunk-YSYHSZWD.js.map} +0 -0
@@ -2354,7 +2354,7 @@ test("HTTP 401 www-authenticate is the bare Bearer challenge when resourceMetada
2354
2354
  }
2355
2355
  });
2356
2356
 
2357
- test("HTTP /mcp returns 405 with Allow: POST for GET and DELETE (authorized requests)", async () => {
2357
+ test("HTTP /mcp serves GET SSE, DELETE 204, and 405 without SSE Accept (issue #2718)", async () => {
2358
2358
  const service = {} as EngramAccessService;
2359
2359
  const server = new EngramAccessHttpServer({
2360
2360
  service,
@@ -2364,23 +2364,47 @@ test("HTTP /mcp returns 405 with Allow: POST for GET and DELETE (authorized requ
2364
2364
  });
2365
2365
  const status = await server.start();
2366
2366
  try {
2367
- for (const method of ["GET", "DELETE"]) {
2368
- const response = await fetch(`http://127.0.0.1:${status.port}/mcp`, {
2369
- method,
2370
- headers: { authorization: "Bearer test-token" },
2371
- });
2372
- assert.equal(response.status, 405, `${method} /mcp must be 405`);
2373
- assert.equal(
2374
- response.headers.get("allow"),
2375
- "POST",
2376
- `${method} /mcp must advertise Allow: POST`,
2377
- );
2378
- const body = (await response.json()) as { code?: string };
2379
- assert.equal(body.code, "method_not_allowed");
2380
- }
2381
- // Unauthenticated GET /mcp still gets 401 first (auth gate beats method-conformance).
2367
+ // Authorized GET with SSE Accept → 200 text/event-stream whose first
2368
+ // bytes are comment-only heartbeats (never JSON-RPC). Read one bounded
2369
+ // chunk, then cancel so the open stream cannot hang the test.
2370
+ const controller = new AbortController();
2371
+ const sse = await fetch(`http://127.0.0.1:${status.port}/mcp`, {
2372
+ headers: { authorization: "Bearer test-token", accept: "text/event-stream" },
2373
+ signal: controller.signal,
2374
+ });
2375
+ assert.equal(sse.status, 200, "GET /mcp with SSE Accept must be 200");
2376
+ assert.match(
2377
+ sse.headers.get("content-type") ?? "",
2378
+ /^text\/event-stream/,
2379
+ "GET /mcp SSE stream must advertise text/event-stream",
2380
+ );
2381
+ const reader = sse.body!.getReader();
2382
+ const { value: firstChunk } = await reader.read();
2383
+ const firstBytes = Buffer.from(firstChunk ?? []).toString("utf8");
2384
+ assert.ok(
2385
+ !firstBytes.includes("jsonrpc"),
2386
+ `GET /mcp stream must carry no JSON-RPC payload: ${JSON.stringify(firstBytes)}`,
2387
+ );
2388
+ controller.abort();
2389
+
2390
+ // Authorized GET without the SSE Accept → 405 + full method set.
2391
+ const rejected = await fetch(`http://127.0.0.1:${status.port}/mcp`, {
2392
+ headers: { authorization: "Bearer test-token" },
2393
+ });
2394
+ assert.equal(rejected.status, 405);
2395
+ assert.equal(rejected.headers.get("allow"), "GET, POST, DELETE");
2396
+
2397
+ // Authorized DELETE → 204 with empty body.
2398
+ const deleted = await fetch(`http://127.0.0.1:${status.port}/mcp`, {
2399
+ method: "DELETE",
2400
+ headers: { authorization: "Bearer test-token" },
2401
+ });
2402
+ assert.equal(deleted.status, 204);
2403
+ assert.equal(await deleted.text(), "");
2404
+
2405
+ // Unauthenticated GET /mcp still gets 401 first (auth gate beats method handling).
2382
2406
  const unauth = await fetch(`http://127.0.0.1:${status.port}/mcp`, {
2383
- method: "GET",
2407
+ headers: { accept: "text/event-stream" },
2384
2408
  });
2385
2409
  assert.equal(unauth.status, 401);
2386
2410
  } finally {
@@ -27,6 +27,7 @@ import { nonEmptyQueryParam, optionalNamespaceKindQueryParam, optionalQueryStrin
27
27
  import { CorrectionContractError } from "./correction/correction-contract.js";
28
28
  import { respondWearablesErrorGlue } from "./wearables/http-glue.js"; import { respondMeetingsList, respondMeetingsGet, respondMeetingsBuild } from "./meetings/http-glue.js"; import { respondLocationStatus, respondLocationCheck, respondLocationSync, respondLocationBackfill, respondLocationDay } from "./location/http-glue.js"; import { respondStandup } from "./standup/http-glue.js";
29
29
  import { EngramMcpServer, MCP_SUPPORTED_PROTOCOL_VERSIONS } from "./access-mcp.js";
30
+ import { handleMcpGetSse } from "./access-mcp-sse.js";
30
31
  import { validateRequest, type SchemaName, type SchemaTypeFor } from "./access-schema.js";
31
32
  import {
32
33
  OFFLINE_SYNC_APPLY_MAX_BODY_BYTES,
@@ -884,17 +885,16 @@ export class EngramAccessHttpServer extends ReviewDeckAccessHttpBase {
884
885
  correlationId: string,
885
886
  abortSignal: AbortSignal,
886
887
  ): Promise<void> {
887
- // Method-conformance for the streamable-HTTP MCP endpoint:
888
- // GET/DELETE on /mcp must return 405 + Allow: POST instead of
889
- // silently falling through to the generic 404. POST continues
890
- // to the normal handler below.
891
- if (pathname === "/mcp" && (req.method === "GET" || req.method === "DELETE")) {
888
+ // Method-conformance for the streamable-HTTP MCP endpoint (issue #2718):
889
+ // DELETE 204, GET upgrades to the comment-only SSE stream in
890
+ // access-mcp-sse.ts when the client accepts text/event-stream, else 405
891
+ // with the full method set. POST continues to the handler below.
892
+ if (pathname === "/mcp" && req.method === "DELETE") { res.writeHead(204); res.end(); return; }
893
+ if (pathname === "/mcp" && req.method === "GET") {
894
+ if ((req.headers.accept ?? "").includes("text/event-stream"))
895
+ return handleMcpGetSse(req, res, this.sseCleanupFns);
892
896
  const body = JSON.stringify({ error: "method_not_allowed", code: "method_not_allowed" });
893
- res.writeHead(405, {
894
- "content-type": "application/json; charset=utf-8",
895
- allow: "POST",
896
- "x-request-id": correlationId,
897
- });
897
+ res.writeHead(405, { "content-type": "application/json; charset=utf-8", allow: "GET, POST, DELETE", "x-request-id": correlationId });
898
898
  res.end(body);
899
899
  return;
900
900
  }
@@ -0,0 +1,50 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+
3
+ /**
4
+ * GET /mcp SSE half of the MCP streamable-HTTP transport (issue #2718).
5
+ *
6
+ * Clients such as the Grok Build TUI open a GET stream after `initialize`;
7
+ * that stream is reserved for server-initiated messages and this server has
8
+ * none to push, so it carries only SSE comment heartbeats — never a
9
+ * JSON-RPC payload. Header/heartbeat/cleanup shape mirrors
10
+ * `handleGraphEventsSSE` in access-http.ts (25 s heartbeat, cleanup
11
+ * registered in the server's `sseCleanupFns` set so `stop()` releases the
12
+ * interval even if the client never disconnects), but it subscribes to no
13
+ * event bus, resolves no namespace, and ignores `Last-Event-ID`.
14
+ *
15
+ * Lives in a sibling module rather than access-http.ts so the handler does
16
+ * not push that file past its fileSizeGrandfather ceiling (issue #1995).
17
+ */
18
+ export function handleMcpGetSse(
19
+ req: IncomingMessage,
20
+ res: ServerResponse,
21
+ sseCleanupFns: Set<() => void>,
22
+ ): void {
23
+ res.writeHead(200, {
24
+ "content-type": "text/event-stream; charset=utf-8",
25
+ "cache-control": "no-cache, no-store, must-revalidate",
26
+ "connection": "keep-alive",
27
+ "x-accel-buffering": "no",
28
+ "transfer-encoding": "chunked",
29
+ });
30
+
31
+ // Comment-only frames: `: ping\n\n` is a valid SSE comment that carries no
32
+ // data and cannot be mistaken for a JSON-RPC message. One is written
33
+ // immediately so clients and proxies see the stream is live without
34
+ // waiting for the first 25 s tick.
35
+ const heartbeat = (): void => {
36
+ try { res.write(": ping\n\n"); } catch { /* client gone; cleanup fires via "close" */ }
37
+ };
38
+ heartbeat();
39
+ const heartbeatInterval = setInterval(heartbeat, 25_000);
40
+
41
+ const cleanup = (): void => {
42
+ clearInterval(heartbeatInterval);
43
+ sseCleanupFns.delete(cleanup);
44
+ try { res.end(); } catch { /* ignore */ }
45
+ };
46
+ sseCleanupFns.add(cleanup);
47
+
48
+ req.once("close", cleanup);
49
+ req.once("error", cleanup);
50
+ }