@akira-tl/forgerelay 1.1.0 → 1.1.2

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 (153) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +4 -2
  3. package/dist/activity/runtime/mcp-query-tools.js +16 -10
  4. package/dist/cli/mcp/external-mcp.js +481 -0
  5. package/dist/cli/mcp/status.js +295 -0
  6. package/dist/cli.js +14 -1
  7. package/dist/lsp/test-support/server-fixture.js +1 -2
  8. package/dist/mcp/oauth/auth-protocol.js +375 -0
  9. package/dist/mcp/oauth/oauth-provider.js +11 -12
  10. package/dist/mcp/oauth/oauth-store.js +2 -2
  11. package/dist/mcp/oauth/router.js +2 -6
  12. package/dist/mcp/operations/batch/executor.js +1 -1
  13. package/dist/mcp/operations/external-mcp/external-mcp-oauth.js +387 -0
  14. package/dist/mcp/operations/external-mcp/external-mcp-runtime.js +23 -6
  15. package/dist/mcp/operations/external-mcp/external-mcp.js +178 -23
  16. package/dist/mcp/operations/native-bulk-mutations.js +1 -1
  17. package/dist/mcp/process/tools.js +19 -15
  18. package/dist/mcp/request-context.js +18 -0
  19. package/dist/mcp/request-meta.js +28 -3
  20. package/dist/mcp/server/core/capabilities.js +1 -4
  21. package/dist/mcp/server/operations/runtime/filesystem-tools.js +35 -28
  22. package/dist/mcp/server/operations/runtime/operation-runtime.js +7 -7
  23. package/dist/mcp/server/transport/http-server.js +41 -20
  24. package/dist/mcp/server/workspace/runtime/workspace-open-presentation.js +2 -0
  25. package/dist/mcp/server/workspace/runtime/workspace-open.js +13 -9
  26. package/dist/mcp/server/workspace/runtime/workspace-tools.js +31 -27
  27. package/dist/runtime/config/external-mcp-auth-store.js +225 -0
  28. package/dist/runtime/config/external-mcp-config.js +76 -22
  29. package/dist/runtime/config/external-mcp-registry.js +165 -0
  30. package/dist/runtime/testing/server-fixture.js +4 -4
  31. package/dist/server.js +15 -9
  32. package/dist/ui/.vite/manifest.json +297 -298
  33. package/dist/ui/activity-panel-app.html +1 -1
  34. package/dist/ui/assets/activity-panel-app-Bw7ZFX2g.js +114 -0
  35. package/dist/ui/assets/angular-html-DeUNP12X.js +1 -0
  36. package/dist/ui/assets/{angular-ts-BearSZDb.js → angular-ts-B3v8FKi_.js} +1 -1
  37. package/dist/ui/assets/{apl-CYQtr-y6.js → apl-Bs3iQm4P.js} +1 -1
  38. package/dist/ui/assets/{astro-DDzqKIjK.js → astro-D6GHecBY.js} +1 -1
  39. package/dist/ui/assets/{blade-DkwEyHvQ.js → blade-BMNbVOHv.js} +1 -1
  40. package/dist/ui/assets/c-BquuA6bv.js +1 -0
  41. package/dist/ui/assets/{cobol-xBn5JxHE.js → cobol-BuZ6kfps.js} +1 -1
  42. package/dist/ui/assets/{coffee-BaXXvoJw.js → coffee-DgC5uBzL.js} +1 -1
  43. package/dist/ui/assets/cpp-BdvWqCaB.js +1 -0
  44. package/dist/ui/assets/{crystal-DjOpXVU1.js → crystal-0Q5qBvNI.js} +1 -1
  45. package/dist/ui/assets/css-Be_T8idh.js +1 -0
  46. package/dist/ui/assets/{edge-DNaRizoR.js → edge-DaGLdgh_.js} +1 -1
  47. package/dist/ui/assets/{elixir-CwBMwZmy.js → elixir-CMgiFIzB.js} +1 -1
  48. package/dist/ui/assets/{elm-gOUD2CW3.js → elm-B-ufMlMC.js} +1 -1
  49. package/dist/ui/assets/{erb-BcR-fjUp.js → erb-CaGiOZM-.js} +1 -1
  50. package/dist/ui/assets/{git-rebase-BAQPKHF6.js → git-rebase-CpwUWCzK.js} +1 -1
  51. package/dist/ui/assets/{glimmer-js-P_wOfSz_.js → glimmer-js-BRxccvnE.js} +1 -1
  52. package/dist/ui/assets/{glimmer-ts-B44aY1fZ.js → glimmer-ts-CgQbPzlT.js} +1 -1
  53. package/dist/ui/assets/glsl-D7JmHFr0.js +1 -0
  54. package/dist/ui/assets/graphql-C8n0eUBK.js +1 -0
  55. package/dist/ui/assets/{hack-ddU2k0nt.js → hack-lVN7KVQv.js} +1 -1
  56. package/dist/ui/assets/haml-h18lG0pS.js +1 -0
  57. package/dist/ui/assets/{handlebars-BpzaUpmz.js → handlebars-lRFjRPfj.js} +1 -1
  58. package/dist/ui/assets/heavy-payload-Cqx9MOkH.js +290 -0
  59. package/dist/ui/assets/html-DtILqTE1.js +1 -0
  60. package/dist/ui/assets/{html-derivative-B2beVdmA.js → html-derivative-CSTSmPup.js} +1 -1
  61. package/dist/ui/assets/{http-iEskZy8U.js → http-DNI8fsbY.js} +1 -1
  62. package/dist/ui/assets/{hurl-BuUsLSKv.js → hurl-DgRi60JG.js} +1 -1
  63. package/dist/ui/assets/java-CW9pZ5bh.js +1 -0
  64. package/dist/ui/assets/javascript-Cvg2UrWL.js +1 -0
  65. package/dist/ui/assets/{jinja-CRpkWwrH.js → jinja-CZw_S1FB.js} +1 -1
  66. package/dist/ui/assets/{jison-BYfSnuHj.js → jison-C56u_FZv.js} +1 -1
  67. package/dist/ui/assets/json-DW2UTv1q.js +1 -0
  68. package/dist/ui/assets/jsx-Zj5gobdD.js +1 -0
  69. package/dist/ui/assets/{julia-B8Rxy7vD.js → julia-Cvcrre-L.js} +1 -1
  70. package/dist/ui/assets/{just-DzhIWpFP.js → just-8CZ2u6Tj.js} +1 -1
  71. package/dist/ui/assets/{latex-CUNjUUL-.js → latex-D289AXWG.js} +1 -1
  72. package/dist/ui/assets/{liquid-hUg7nC61.js → liquid-CFEX3SkZ.js} +1 -1
  73. package/dist/ui/assets/lua-BKpJ0ybO.js +1 -0
  74. package/dist/ui/assets/{marko-CQmIkwMF.js → marko-D9maZ0a6.js} +1 -1
  75. package/dist/ui/assets/{mdc-CB-6dHYa.js → mdc-rM9l0qWR.js} +1 -1
  76. package/dist/ui/assets/{nginx-CQORo_Bt.js → nginx-C9w6p2C9.js} +1 -1
  77. package/dist/ui/assets/{nim-BNwBNzdn.js → nim-BRePHk0J.js} +1 -1
  78. package/dist/ui/assets/{perl-Cnz14fAJ.js → perl-CskTmiPQ.js} +1 -1
  79. package/dist/ui/assets/{php-YywDo63b.js → php-DCU_FvNI.js} +1 -1
  80. package/dist/ui/assets/{pug-BwbJp4cM.js → pug-XLaVxTvv.js} +1 -1
  81. package/dist/ui/assets/{qml-D2DY6dMH.js → qml-CaONKSl-.js} +1 -1
  82. package/dist/ui/assets/r-BCVR0ZE3.js +1 -0
  83. package/dist/ui/assets/{razor-pMTG7k41.js → razor-DDnGXxnU.js} +1 -1
  84. package/dist/ui/assets/regexp-DWGTYYVx.js +1 -0
  85. package/dist/ui/assets/{rst-CO_ndDm4.js → rst-Dz3GWvQm.js} +1 -1
  86. package/dist/ui/assets/{ruby-xzRn9gTh.js → ruby-DC3jGtMY.js} +1 -1
  87. package/dist/ui/assets/{sas-CBdtFKLA.js → sas-7mNpvsjZ.js} +1 -1
  88. package/dist/ui/assets/scss-uBsoCJVy.js +1 -0
  89. package/dist/ui/assets/shellscript-BCN9N_Jf.js +1 -0
  90. package/dist/ui/assets/{shellsession-BaZnm5rZ.js → shellsession-Hsw46VXV.js} +1 -1
  91. package/dist/ui/assets/{soy-IouG3xdB.js → soy-D6KV69HU.js} +1 -1
  92. package/dist/ui/assets/sql-CVzVQkJh.js +1 -0
  93. package/dist/ui/assets/{stata-dgbkIZHP.js → stata-BwUjXNhI.js} +1 -1
  94. package/dist/ui/assets/{surrealql-CBROnw_p.js → surrealql-CgloWqRa.js} +1 -1
  95. package/dist/ui/assets/{svelte-6r6BpX4-.js → svelte-DcC3n6IC.js} +1 -1
  96. package/dist/ui/assets/{templ-BKD_PsbL.js → templ-DOFfnWTJ.js} +1 -1
  97. package/dist/ui/assets/{tex-CKY1bwHb.js → tex-ln89VPaX.js} +1 -1
  98. package/dist/ui/assets/{ts-tags-BiYP5N_C.js → ts-tags-CWMzCstH.js} +1 -1
  99. package/dist/ui/assets/tsx-DgzfiGeK.js +1 -0
  100. package/dist/ui/assets/{twig-BTe1oPFW.js → twig-Bi2itZL9.js} +1 -1
  101. package/dist/ui/assets/typescript-CQ24f4zW.js +1 -0
  102. package/dist/ui/assets/{vue-CT0IiySh.js → vue-BS8jE5u6.js} +1 -1
  103. package/dist/ui/assets/{vue-html-B-tXcbat.js → vue-html-o38xqhyB.js} +1 -1
  104. package/dist/ui/assets/{vue-vine-C5ZyDCrs.js → vue-vine-D2NvKv50.js} +1 -1
  105. package/dist/ui/assets/xml-_44rx9_2.js +1 -0
  106. package/dist/ui/assets/{xsl-DvDSndNI.js → xsl-B5QWQPoi.js} +1 -1
  107. package/dist/ui/assets/yaml-CKL2pEWe.js +1 -0
  108. package/dist/workspaces/relay/auth/remote-auth.js +3 -5
  109. package/dist/workspaces/relay/tests/test-support.js +7 -4
  110. package/dist/workspaces/relay/workspace-relay.js +14 -8
  111. package/dist/workspaces/resources/skills.js +1 -1
  112. package/docs/chatgpt-coding-workflow.md +12 -6
  113. package/docs/configuration.md +158 -6
  114. package/docs/debugging.md +8 -5
  115. package/docs/roadmap.md +64 -3
  116. package/docs/security.md +18 -0
  117. package/package.json +11 -4
  118. package/scripts/debug/accept/bootstrap.mjs +2 -0
  119. package/scripts/debug/accept/harness.mjs +9 -2
  120. package/scripts/debug/accept/modern-http.mjs +169 -0
  121. package/scripts/debug/accept/support.mjs +10 -6
  122. package/scripts/debug/accept.mjs +1 -0
  123. package/scripts/debug/runtime.mjs +4 -1
  124. package/scripts/debug/runtime.test.mjs +9 -0
  125. package/scripts/release/parity-sandbox.mjs +35 -0
  126. package/scripts/release/release-gate.test.mjs +42 -1
  127. package/scripts/release-parity.mjs +3 -22
  128. package/scripts/release-proof.mjs +24 -4
  129. package/scripts/release-proof.test.mjs +12 -4
  130. package/dist/ui/assets/activity-panel-app-XlqSanXT.js +0 -115
  131. package/dist/ui/assets/angular-html-tB9EchbM.js +0 -1
  132. package/dist/ui/assets/c-Dv-N5hjn.js +0 -1
  133. package/dist/ui/assets/cpp-C3unXX-3.js +0 -1
  134. package/dist/ui/assets/css-8Xr4kNNH.js +0 -1
  135. package/dist/ui/assets/glsl-BvCQg9MI.js +0 -1
  136. package/dist/ui/assets/graphql-C9S90gn_.js +0 -1
  137. package/dist/ui/assets/haml-Bb1w9rMG.js +0 -1
  138. package/dist/ui/assets/heavy-payload-CJKqi8Ac.js +0 -290
  139. package/dist/ui/assets/html-dUOs5pA9.js +0 -1
  140. package/dist/ui/assets/java-BFZCrORY.js +0 -1
  141. package/dist/ui/assets/javascript-DaIQ0Zqj.js +0 -1
  142. package/dist/ui/assets/json-BQC_Yp48.js +0 -1
  143. package/dist/ui/assets/jsx-Dd-CJRf9.js +0 -1
  144. package/dist/ui/assets/lua-BoZTiL2I.js +0 -1
  145. package/dist/ui/assets/r-B5nYsy-M.js +0 -1
  146. package/dist/ui/assets/regexp-oP0lbks_.js +0 -1
  147. package/dist/ui/assets/scss-CEqgKQNf.js +0 -1
  148. package/dist/ui/assets/shellscript-DzJJiWQC.js +0 -1
  149. package/dist/ui/assets/sql-DC2rdTzV.js +0 -1
  150. package/dist/ui/assets/tsx-D2OMiPNF.js +0 -1
  151. package/dist/ui/assets/typescript-BGIABksB.js +0 -1
  152. package/dist/ui/assets/xml-CfeBNlMP.js +0 -1
  153. package/dist/ui/assets/yaml-umbj2r1g.js +0 -1
@@ -0,0 +1 @@
1
+ import{t as e}from"./java-CW9pZ5bh.js";import{t}from"./heavy-payload-Cqx9MOkH.js";var n=t({default:()=>i}),r=Object.freeze(JSON.parse(`{"displayName":"XML","name":"xml","patterns":[{"begin":"(<\\\\?)\\\\s*([-0-9A-Z_a-z]+)","captures":{"1":{"name":"punctuation.definition.tag.xml"},"2":{"name":"entity.name.tag.xml"}},"end":"(\\\\?>)","name":"meta.tag.preprocessor.xml","patterns":[{"match":" ([-A-Za-z]+)","name":"entity.other.attribute-name.xml"},{"include":"#doublequotedString"},{"include":"#singlequotedString"}]},{"begin":"(<!)(DOCTYPE)\\\\s+([:A-Z_a-z][-.0-:A-Z_a-z]*)","captures":{"1":{"name":"punctuation.definition.tag.xml"},"2":{"name":"keyword.other.doctype.xml"},"3":{"name":"variable.language.documentroot.xml"}},"end":"\\\\s*(>)","name":"meta.tag.sgml.doctype.xml","patterns":[{"include":"#internalSubset"}]},{"include":"#comments"},{"begin":"(<)((?:([-0-9A-Z_a-z]+)(:))?([-0-:A-Z_a-z]+))(?=(\\\\s[^>]*)?></\\\\2>)","beginCaptures":{"1":{"name":"punctuation.definition.tag.xml"},"2":{"name":"entity.name.tag.xml"},"3":{"name":"entity.name.tag.namespace.xml"},"4":{"name":"punctuation.separator.namespace.xml"},"5":{"name":"entity.name.tag.localname.xml"}},"end":"(>)(</)((?:([-0-9A-Z_a-z]+)(:))?([-0-:A-Z_a-z]+))(>)","endCaptures":{"1":{"name":"punctuation.definition.tag.xml"},"2":{"name":"punctuation.definition.tag.xml"},"3":{"name":"entity.name.tag.xml"},"4":{"name":"entity.name.tag.namespace.xml"},"5":{"name":"punctuation.separator.namespace.xml"},"6":{"name":"entity.name.tag.localname.xml"},"7":{"name":"punctuation.definition.tag.xml"}},"name":"meta.tag.no-content.xml","patterns":[{"include":"#tagStuff"}]},{"begin":"(</?)(?:([-.\\\\w]+)((:)))?([-.:\\\\w]+)","captures":{"1":{"name":"punctuation.definition.tag.xml"},"2":{"name":"entity.name.tag.namespace.xml"},"3":{"name":"entity.name.tag.xml"},"4":{"name":"punctuation.separator.namespace.xml"},"5":{"name":"entity.name.tag.localname.xml"}},"end":"(/?>)","name":"meta.tag.xml","patterns":[{"include":"#tagStuff"}]},{"include":"#entity"},{"include":"#bare-ampersand"},{"begin":"<%@","beginCaptures":{"0":{"name":"punctuation.section.embedded.begin.xml"}},"end":"%>","endCaptures":{"0":{"name":"punctuation.section.embedded.end.xml"}},"name":"source.java-props.embedded.xml","patterns":[{"match":"page|include|taglib","name":"keyword.other.page-props.xml"}]},{"begin":"<%[!=]?(?!--)","beginCaptures":{"0":{"name":"punctuation.section.embedded.begin.xml"}},"end":"(?!--)%>","endCaptures":{"0":{"name":"punctuation.section.embedded.end.xml"}},"name":"source.java.embedded.xml","patterns":[{"include":"source.java"}]},{"begin":"<!\\\\[CDATA\\\\[","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.xml"}},"end":"]]>","endCaptures":{"0":{"name":"punctuation.definition.string.end.xml"}},"name":"string.unquoted.cdata.xml"}],"repository":{"EntityDecl":{"begin":"(<!)(ENTITY)\\\\s+(%\\\\s+)?([:A-Z_a-z][-.0-:A-Z_a-z]*)(\\\\s+(?:SYSTEM|PUBLIC)\\\\s+)?","captures":{"1":{"name":"punctuation.definition.tag.xml"},"2":{"name":"keyword.other.entity.xml"},"3":{"name":"punctuation.definition.entity.xml"},"4":{"name":"variable.language.entity.xml"},"5":{"name":"keyword.other.entitytype.xml"}},"end":"(>)","patterns":[{"include":"#doublequotedString"},{"include":"#singlequotedString"}]},"bare-ampersand":{"match":"&","name":"invalid.illegal.bad-ampersand.xml"},"comments":{"patterns":[{"begin":"<%--","captures":{"0":{"name":"punctuation.definition.comment.xml"},"end":"--%>","name":"comment.block.xml"}},{"begin":"<!--","captures":{"0":{"name":"punctuation.definition.comment.xml"}},"end":"-->","name":"comment.block.xml","patterns":[{"begin":"--(?!>)","captures":{"0":{"name":"invalid.illegal.bad-comments-or-CDATA.xml"}}}]}]},"doublequotedString":{"begin":"\\"","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.xml"}},"end":"\\"","endCaptures":{"0":{"name":"punctuation.definition.string.end.xml"}},"name":"string.quoted.double.xml","patterns":[{"include":"#entity"},{"include":"#bare-ampersand"}]},"entity":{"captures":{"1":{"name":"punctuation.definition.constant.xml"},"3":{"name":"punctuation.definition.constant.xml"}},"match":"(&)([:A-Z_a-z][-.0-:A-Z_a-z]*|#[0-9]+|#x\\\\h+)(;)","name":"constant.character.entity.xml"},"internalSubset":{"begin":"(\\\\[)","captures":{"1":{"name":"punctuation.definition.constant.xml"}},"end":"(])","name":"meta.internalsubset.xml","patterns":[{"include":"#EntityDecl"},{"include":"#parameterEntity"},{"include":"#comments"}]},"parameterEntity":{"captures":{"1":{"name":"punctuation.definition.constant.xml"},"3":{"name":"punctuation.definition.constant.xml"}},"match":"(%)([:A-Z_a-z][-.0-:A-Z_a-z]*)(;)","name":"constant.character.parameter-entity.xml"},"singlequotedString":{"begin":"'","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.xml"}},"end":"'","endCaptures":{"0":{"name":"punctuation.definition.string.end.xml"}},"name":"string.quoted.single.xml","patterns":[{"include":"#entity"},{"include":"#bare-ampersand"}]},"tagStuff":{"patterns":[{"captures":{"1":{"name":"entity.other.attribute-name.namespace.xml"},"2":{"name":"entity.other.attribute-name.xml"},"3":{"name":"punctuation.separator.namespace.xml"},"4":{"name":"entity.other.attribute-name.localname.xml"}},"match":"(?:^|\\\\s+)(?:([-.\\\\w]+)((:)))?([-.:\\\\w]+)\\\\s*="},{"include":"#doublequotedString"},{"include":"#singlequotedString"}]}},"scopeName":"text.xml","embeddedLangs":["java"]}`)),i=[...e,r];export{n,i as t};
@@ -1 +1 @@
1
- import{t as e}from"./xml-CfeBNlMP.js";var t=Object.freeze(JSON.parse(`{"displayName":"XSL","name":"xsl","patterns":[{"begin":"(<)(xsl)((:))(template)","captures":{"1":{"name":"punctuation.definition.tag.xml"},"2":{"name":"entity.name.tag.namespace.xml"},"3":{"name":"entity.name.tag.xml"},"4":{"name":"punctuation.separator.namespace.xml"},"5":{"name":"entity.name.tag.localname.xml"}},"end":"(>)","name":"meta.tag.xml.template","patterns":[{"captures":{"1":{"name":"entity.other.attribute-name.namespace.xml"},"2":{"name":"entity.other.attribute-name.xml"},"3":{"name":"punctuation.separator.namespace.xml"},"4":{"name":"entity.other.attribute-name.localname.xml"}},"match":" (?:([-0-9A-Z_a-z]+)((:)))?([-A-Za-z]+)"},{"include":"#doublequotedString"},{"include":"#singlequotedString"}]},{"include":"text.xml"}],"repository":{"doublequotedString":{"begin":"\\"","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.xml"}},"end":"\\"","endCaptures":{"0":{"name":"punctuation.definition.string.end.xml"}},"name":"string.quoted.double.xml"},"singlequotedString":{"begin":"'","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.xml"}},"end":"'","endCaptures":{"0":{"name":"punctuation.definition.string.end.xml"}},"name":"string.quoted.single.xml"}},"scopeName":"text.xml.xsl","embeddedLangs":["xml"]}`)),n=[...e,t];export{n as default};
1
+ import{t as e}from"./xml-_44rx9_2.js";var t=Object.freeze(JSON.parse(`{"displayName":"XSL","name":"xsl","patterns":[{"begin":"(<)(xsl)((:))(template)","captures":{"1":{"name":"punctuation.definition.tag.xml"},"2":{"name":"entity.name.tag.namespace.xml"},"3":{"name":"entity.name.tag.xml"},"4":{"name":"punctuation.separator.namespace.xml"},"5":{"name":"entity.name.tag.localname.xml"}},"end":"(>)","name":"meta.tag.xml.template","patterns":[{"captures":{"1":{"name":"entity.other.attribute-name.namespace.xml"},"2":{"name":"entity.other.attribute-name.xml"},"3":{"name":"punctuation.separator.namespace.xml"},"4":{"name":"entity.other.attribute-name.localname.xml"}},"match":" (?:([-0-9A-Z_a-z]+)((:)))?([-A-Za-z]+)"},{"include":"#doublequotedString"},{"include":"#singlequotedString"}]},{"include":"text.xml"}],"repository":{"doublequotedString":{"begin":"\\"","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.xml"}},"end":"\\"","endCaptures":{"0":{"name":"punctuation.definition.string.end.xml"}},"name":"string.quoted.double.xml"},"singlequotedString":{"begin":"'","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.xml"}},"end":"'","endCaptures":{"0":{"name":"punctuation.definition.string.end.xml"}},"name":"string.quoted.single.xml"}},"scopeName":"text.xml.xsl","embeddedLangs":["xml"]}`)),n=[...e,t];export{n as default};
@@ -0,0 +1 @@
1
+ import{t as e}from"./heavy-payload-Cqx9MOkH.js";var t=e({default:()=>n}),n=[Object.freeze(JSON.parse(`{"displayName":"YAML","fileTypes":["yaml","yml","rviz","reek","clang-format","yaml-tmlanguage","syntax","sublime-syntax"],"firstLineMatch":"^%YAML( ?1.\\\\d+)?","name":"yaml","patterns":[{"include":"#comment"},{"include":"#property"},{"include":"#directive"},{"match":"^---","name":"entity.other.document.begin.yaml"},{"match":"^\\\\.{3}","name":"entity.other.document.end.yaml"},{"include":"#node"}],"repository":{"block-collection":{"patterns":[{"include":"#block-sequence"},{"include":"#block-mapping"}]},"block-mapping":{"patterns":[{"include":"#block-pair"}]},"block-node":{"patterns":[{"include":"#prototype"},{"include":"#block-scalar"},{"include":"#block-collection"},{"include":"#flow-scalar-plain-out"},{"include":"#flow-node"}]},"block-pair":{"patterns":[{"begin":"\\\\?","beginCaptures":{"1":{"name":"punctuation.definition.key-value.begin.yaml"}},"end":"(?=\\\\?)|^ *(:)|(:)","endCaptures":{"1":{"name":"punctuation.separator.key-value.mapping.yaml"},"2":{"name":"invalid.illegal.expected-newline.yaml"}},"name":"meta.block-mapping.yaml","patterns":[{"include":"#block-node"}]},{"begin":"(?=(?:[^-\\\\]!\\"#%\\\\&'*,:>?@\\\\[\`{|}\\\\s]|[-:?]\\\\S)([^:\\\\s]|:\\\\S|\\\\s+(?![#\\\\s]))*\\\\s*:(\\\\s|$))","end":"(?=\\\\s*$|\\\\s+#|\\\\s*:(\\\\s|$))","patterns":[{"include":"#flow-scalar-plain-out-implicit-type"},{"begin":"[^-\\\\]!\\"#%\\\\&'*,:>?@\\\\[\`{|}\\\\s]|[-:?]\\\\S","beginCaptures":{"0":{"name":"entity.name.tag.yaml"}},"contentName":"entity.name.tag.yaml","end":"(?=\\\\s*$|\\\\s+#|\\\\s*:(\\\\s|$))","name":"string.unquoted.plain.out.yaml"}]},{"match":":(?=\\\\s|$)","name":"punctuation.separator.key-value.mapping.yaml"}]},"block-scalar":{"begin":"(?:(\\\\|)|(>))([1-9])?([-+])?(.*\\\\n?)","beginCaptures":{"1":{"name":"keyword.control.flow.block-scalar.literal.yaml"},"2":{"name":"keyword.control.flow.block-scalar.folded.yaml"},"3":{"name":"constant.numeric.indentation-indicator.yaml"},"4":{"name":"storage.modifier.chomping-indicator.yaml"},"5":{"patterns":[{"include":"#comment"},{"match":".+","name":"invalid.illegal.expected-comment-or-newline.yaml"}]}},"end":"^(?=\\\\S)|(?!\\\\G)","patterns":[{"begin":"^( +)(?! )","end":"^(?!\\\\1|\\\\s*$)","name":"string.unquoted.block.yaml"}]},"block-sequence":{"match":"(-)(?!\\\\S)","name":"punctuation.definition.block.sequence.item.yaml"},"comment":{"begin":"(?:^([\\\\t ]*)|[\\\\t ]+)(?=#\\\\p{print}*$)","beginCaptures":{"1":{"name":"punctuation.whitespace.comment.leading.yaml"}},"end":"(?!\\\\G)","patterns":[{"begin":"#","beginCaptures":{"0":{"name":"punctuation.definition.comment.yaml"}},"end":"\\\\n","name":"comment.line.number-sign.yaml"}]},"directive":{"begin":"^%","beginCaptures":{"0":{"name":"punctuation.definition.directive.begin.yaml"}},"end":"(?=$|[\\\\t ]+($|#))","name":"meta.directive.yaml","patterns":[{"captures":{"1":{"name":"keyword.other.directive.yaml.yaml"},"2":{"name":"constant.numeric.yaml-version.yaml"}},"match":"\\\\G(YAML)[\\\\t ]+(\\\\d+\\\\.\\\\d+)"},{"captures":{"1":{"name":"keyword.other.directive.tag.yaml"},"2":{"name":"storage.type.tag-handle.yaml"},"3":{"name":"support.type.tag-prefix.yaml"}},"match":"\\\\G(TAG)(?:[\\\\t ]+(!(?:[-0-9A-Za-z]*!)?)(?:[\\\\t ]+(!(?:%\\\\h{2}|[]!#$\\\\&-;=?-\\\\[_a-z~])*|(?![]!,\\\\[{}])(?:%\\\\h{2}|[]!#$\\\\&-;=?-\\\\[_a-z~])+))?)?"},{"captures":{"1":{"name":"support.other.directive.reserved.yaml"},"2":{"name":"string.unquoted.directive-name.yaml"},"3":{"name":"string.unquoted.directive-parameter.yaml"}},"match":"\\\\G(\\\\w+)(?:[\\\\t ]+(\\\\w+)(?:[\\\\t ]+(\\\\w+))?)?"},{"match":"\\\\S+","name":"invalid.illegal.unrecognized.yaml"}]},"flow-alias":{"captures":{"1":{"name":"keyword.control.flow.alias.yaml"},"2":{"name":"punctuation.definition.alias.yaml"},"3":{"name":"variable.other.alias.yaml"},"4":{"name":"invalid.illegal.character.anchor.yaml"}},"match":"((\\\\*))([^],/\\\\[{}\\\\s]+)([^],}\\\\s]\\\\S*)?"},"flow-collection":{"patterns":[{"include":"#flow-sequence"},{"include":"#flow-mapping"}]},"flow-mapping":{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.mapping.begin.yaml"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.mapping.end.yaml"}},"name":"meta.flow-mapping.yaml","patterns":[{"include":"#prototype"},{"match":",","name":"punctuation.separator.mapping.yaml"},{"include":"#flow-pair"}]},"flow-node":{"patterns":[{"include":"#prototype"},{"include":"#flow-alias"},{"include":"#flow-collection"},{"include":"#flow-scalar"}]},"flow-pair":{"patterns":[{"begin":"\\\\?","beginCaptures":{"0":{"name":"punctuation.definition.key-value.begin.yaml"}},"end":"(?=[],}])","name":"meta.flow-pair.explicit.yaml","patterns":[{"include":"#prototype"},{"include":"#flow-pair"},{"include":"#flow-node"},{"begin":":(?=\\\\s|$|[],\\\\[{}])","beginCaptures":{"0":{"name":"punctuation.separator.key-value.mapping.yaml"}},"end":"(?=[],}])","patterns":[{"include":"#flow-value"}]}]},{"begin":"(?=(?:[^-\\\\]!\\"#%\\\\&'*,:>?@\\\\[\`{|}\\\\s]|[-:?][^],\\\\[{}\\\\s])([^],:\\\\[{}\\\\s]|:[^],\\\\[{}\\\\s]|\\\\s+(?![#\\\\s]))*\\\\s*:(\\\\s|$))","end":"(?=\\\\s*$|\\\\s+#|\\\\s*:(\\\\s|$)|\\\\s*:[],\\\\[{}]|\\\\s*[],\\\\[{}])","name":"meta.flow-pair.key.yaml","patterns":[{"include":"#flow-scalar-plain-in-implicit-type"},{"begin":"[^-\\\\]!\\"#%\\\\&'*,:>?@\\\\[\`{|}\\\\s]|[-:?][^],\\\\[{}\\\\s]","beginCaptures":{"0":{"name":"entity.name.tag.yaml"}},"contentName":"entity.name.tag.yaml","end":"(?=\\\\s*$|\\\\s+#|\\\\s*:(\\\\s|$)|\\\\s*:[],\\\\[{}]|\\\\s*[],\\\\[{}])","name":"string.unquoted.plain.in.yaml"}]},{"include":"#flow-node"},{"begin":":(?=\\\\s|$|[],\\\\[{}])","captures":{"0":{"name":"punctuation.separator.key-value.mapping.yaml"}},"end":"(?=[],}])","name":"meta.flow-pair.yaml","patterns":[{"include":"#flow-value"}]}]},"flow-scalar":{"patterns":[{"include":"#flow-scalar-double-quoted"},{"include":"#flow-scalar-single-quoted"},{"include":"#flow-scalar-plain-in"}]},"flow-scalar-double-quoted":{"begin":"\\"","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.yaml"}},"end":"\\"","endCaptures":{"0":{"name":"punctuation.definition.string.end.yaml"}},"name":"string.quoted.double.yaml","patterns":[{"match":"\\\\\\\\([ \\"/0LN\\\\\\\\_abefnprtv]|x\\\\d\\\\d|u\\\\d{4}|U\\\\d{8})","name":"constant.character.escape.yaml"},{"match":"\\\\\\\\\\\\n","name":"constant.character.escape.double-quoted.newline.yaml"}]},"flow-scalar-plain-in":{"patterns":[{"include":"#flow-scalar-plain-in-implicit-type"},{"begin":"[^-\\\\]!\\"#%\\\\&'*,:>?@\\\\[\`{|}\\\\s]|[-:?][^],\\\\[{}\\\\s]","end":"(?=\\\\s*$|\\\\s+#|\\\\s*:(\\\\s|$)|\\\\s*:[],\\\\[{}]|\\\\s*[],\\\\[{}])","name":"string.unquoted.plain.in.yaml"}]},"flow-scalar-plain-in-implicit-type":{"patterns":[{"captures":{"1":{"name":"constant.language.null.yaml"},"2":{"name":"constant.language.boolean.yaml"},"3":{"name":"constant.numeric.integer.yaml"},"4":{"name":"constant.numeric.float.yaml"},"5":{"name":"constant.other.timestamp.yaml"},"6":{"name":"constant.language.value.yaml"},"7":{"name":"constant.language.merge.yaml"}},"match":"(?:(null|Null|NULL|~)|([Yy]|yes|Yes|YES|[Nn]|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF)|([-+]?0b[01_]+|[-+]?0[0-7_]+|[-+]?(?:0|[1-9][0-9_]*)|[-+]?0x[_\\\\h]+|[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)|([-+]?(?:[0-9][0-9_]*)?\\\\.[.0-9]*(?:[Ee][-+][0-9]+)?|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\\\.[0-9_]*|[-+]?\\\\.(?:inf|Inf|INF)|\\\\.(?:nan|NaN|NAN))|(\\\\d{4}-\\\\d{2}-\\\\d{2}|\\\\d{4}-\\\\d{1,2}-\\\\d{1,2}(?:[Tt]|[\\\\t ]+)\\\\d{1,2}:\\\\d{2}:\\\\d{2}(?:\\\\.\\\\d*)?(?:[\\\\t ]*Z|[-+]\\\\d{1,2}(?::\\\\d{1,2})?)?)|(=)|(<<))(?=\\\\s*$|\\\\s+#|\\\\s*:(\\\\s|$)|\\\\s*:[],\\\\[{}]|\\\\s*[],\\\\[{}])"}]},"flow-scalar-plain-out":{"patterns":[{"include":"#flow-scalar-plain-out-implicit-type"},{"begin":"[^-\\\\]!\\"#%\\\\&'*,:>?@\\\\[\`{|}\\\\s]|[-:?]\\\\S","end":"(?=\\\\s*$|\\\\s+#|\\\\s*:(\\\\s|$))","name":"string.unquoted.plain.out.yaml"}]},"flow-scalar-plain-out-implicit-type":{"patterns":[{"captures":{"1":{"name":"constant.language.null.yaml"},"2":{"name":"constant.language.boolean.yaml"},"3":{"name":"constant.numeric.integer.yaml"},"4":{"name":"constant.numeric.float.yaml"},"5":{"name":"constant.other.timestamp.yaml"},"6":{"name":"constant.language.value.yaml"},"7":{"name":"constant.language.merge.yaml"}},"match":"(?:(null|Null|NULL|~)|([Yy]|yes|Yes|YES|[Nn]|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF)|([-+]?0b[01_]+|[-+]?0[0-7_]+|[-+]?(?:0|[1-9][0-9_]*)|[-+]?0x[_\\\\h]+|[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)|([-+]?(?:[0-9][0-9_]*)?\\\\.[.0-9]*(?:[Ee][-+][0-9]+)?|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\\\.[0-9_]*|[-+]?\\\\.(?:inf|Inf|INF)|\\\\.(?:nan|NaN|NAN))|(\\\\d{4}-\\\\d{2}-\\\\d{2}|\\\\d{4}-\\\\d{1,2}-\\\\d{1,2}(?:[Tt]|[\\\\t ]+)\\\\d{1,2}:\\\\d{2}:\\\\d{2}(?:\\\\.\\\\d*)?(?:[\\\\t ]*Z|[-+]\\\\d{1,2}(?::\\\\d{1,2})?)?)|(=)|(<<))(?=\\\\s*$|\\\\s+#|\\\\s*:(\\\\s|$))"}]},"flow-scalar-single-quoted":{"begin":"'","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.yaml"}},"end":"'(?!')","endCaptures":{"0":{"name":"punctuation.definition.string.end.yaml"}},"name":"string.quoted.single.yaml","patterns":[{"match":"''","name":"constant.character.escape.single-quoted.yaml"}]},"flow-sequence":{"begin":"\\\\[","beginCaptures":{"0":{"name":"punctuation.definition.sequence.begin.yaml"}},"end":"]","endCaptures":{"0":{"name":"punctuation.definition.sequence.end.yaml"}},"name":"meta.flow-sequence.yaml","patterns":[{"include":"#prototype"},{"match":",","name":"punctuation.separator.sequence.yaml"},{"include":"#flow-pair"},{"include":"#flow-node"}]},"flow-value":{"patterns":[{"begin":"\\\\G(?![],}])","end":"(?=[],}])","name":"meta.flow-pair.value.yaml","patterns":[{"include":"#flow-node"}]}]},"node":{"patterns":[{"include":"#block-node"}]},"property":{"begin":"(?=[!\\\\&])","end":"(?!\\\\G)","name":"meta.property.yaml","patterns":[{"captures":{"1":{"name":"keyword.control.property.anchor.yaml"},"2":{"name":"punctuation.definition.anchor.yaml"},"3":{"name":"entity.name.type.anchor.yaml"},"4":{"name":"invalid.illegal.character.anchor.yaml"}},"match":"\\\\G((&))([^],/\\\\[{}\\\\s]+)(\\\\S+)?"},{"match":"\\\\G!(?:<(?:%\\\\h{2}|[]!#$\\\\&-;=?-\\\\[_a-z~])+>|(?:[-0-9A-Za-z]*!)?(?:%\\\\h{2}|[#$\\\\&-+\\\\--;=?-Z_a-z~])+|)(?=[\\\\t ]|$)","name":"storage.type.tag-handle.yaml"},{"match":"\\\\S+","name":"invalid.illegal.tag-handle.yaml"}]},"prototype":{"patterns":[{"include":"#comment"},{"include":"#property"}]}},"scopeName":"source.yaml","aliases":["yml"]}`))];export{t as n,n as t};
@@ -1,7 +1,5 @@
1
1
  import { readFileSync } from "node:fs";
2
- import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js";
3
- import { Client } from "@modelcontextprotocol/sdk/client/index.js";
4
- import { StreamableHTTPClientTransport, StreamableHTTPError, } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
2
+ import { Client, SdkHttpError, StreamableHTTPClientTransport, UnauthorizedError, } from "@modelcontextprotocol/client";
5
3
  import { publicEndpointUrl } from "../../../mcp/oauth/public-url.js";
6
4
  const REMOTE_AUTH_TIMEOUT_MS = 15_000;
7
5
  const packageVersion = readForgeRelayVersion();
@@ -45,11 +43,11 @@ export async function refreshRemoteAuthentication(remote, endpointInput = remote
45
43
  }
46
44
  export function isRemoteMcpUnauthorized(error) {
47
45
  return error instanceof UnauthorizedError ||
48
- (error instanceof StreamableHTTPError && error.code === 401);
46
+ (error instanceof SdkHttpError && error.status === 401);
49
47
  }
50
48
  export async function connectRemoteMcpClient(remote, endpointInput) {
51
49
  const endpoint = normalizeRemoteServiceTarget(endpointInput);
52
- const client = new Client({ name: "forgerelay-cli", version: packageVersion });
50
+ const client = new Client({ name: "forgerelay-cli", version: packageVersion }, { versionNegotiation: { mode: "legacy" } });
53
51
  const transport = new StreamableHTTPClientTransport(publicEndpointUrl(endpoint, "mcp"), {
54
52
  requestInit: {
55
53
  headers: { Authorization: `Bearer ${remote.accessToken}` },
@@ -3,9 +3,8 @@ import { execFileSync } from "node:child_process";
3
3
  import { mkdir, writeFile } from "node:fs/promises";
4
4
  import { createRequire, syncBuiltinESMExports } from "node:module";
5
5
  import { dirname, join } from "node:path";
6
- import { Client } from "@modelcontextprotocol/sdk/client/index.js";
7
- import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
8
- import { CallToolResultSchema } from "@modelcontextprotocol/sdk/types.js";
6
+ import { Client, InMemoryTransport } from "@modelcontextprotocol/client";
7
+ import { CompatibilityCallToolResultSchema } from "@modelcontextprotocol/core";
9
8
  import { ActivityAuditStore } from "../../../activity/history/audit-store.js";
10
9
  import { BashOutputStore } from "../../../activity/history/bash-output-store.js";
11
10
  import { HostTurnStore } from "../../../activity/history/host-turn-store.js";
@@ -224,5 +223,9 @@ export function resultText(result) {
224
223
  .join("\n");
225
224
  }
226
225
  function parseToolResult(result) {
227
- return CallToolResultSchema.parse(result);
226
+ const parsed = CompatibilityCallToolResultSchema.parse(result);
227
+ if (!Array.isArray(parsed.content)) {
228
+ throw new Error("Expected a content-based compatibility tool result.");
229
+ }
230
+ return parsed;
228
231
  }
@@ -1,7 +1,7 @@
1
1
  import { randomBytes } from "node:crypto";
2
2
  import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync, } from "node:fs";
3
3
  import { join } from "node:path";
4
- import { CallToolResultSchema } from "@modelcontextprotocol/sdk/types.js";
4
+ import { CompatibilityCallToolResultSchema } from "@modelcontextprotocol/core";
5
5
  import { isRemoteMcpUnauthorized, refreshRemoteAuthentication, } from "./auth/remote-auth.js";
6
6
  import { RemoteMcpConnectionPool } from "./transport/remote-mcp-connection-pool.js";
7
7
  import { withFileLock } from "../../runtime/state/lock/file-lock.js";
@@ -465,13 +465,19 @@ export class RemoteWorkspaceRelay {
465
465
  throw new Error(`Remote ForgeRelay ${alias} request failed: ${errorMessage(error)}`, { cause: error });
466
466
  }
467
467
  }
468
- const invoke = async (active) => CallToolResultSchema.parse(await active.client.callTool({
469
- name,
470
- arguments: args,
471
- ...(conversationScopeId
472
- ? { _meta: { "openai/session": conversationScopeId } }
473
- : {}),
474
- }));
468
+ const invoke = async (active) => {
469
+ const parsed = CompatibilityCallToolResultSchema.parse(await active.client.callTool({
470
+ name,
471
+ arguments: args,
472
+ ...(conversationScopeId
473
+ ? { _meta: { "openai/session": conversationScopeId } }
474
+ : {}),
475
+ }));
476
+ if (!Array.isArray(parsed.content)) {
477
+ throw new Error("Remote ForgeRelay returned a modern-only tool result on the legacy Relay protocol path.");
478
+ }
479
+ return parsed;
480
+ };
475
481
  try {
476
482
  return await invoke(connection);
477
483
  }
@@ -7,8 +7,8 @@ import { expandHomePath, isPathInsideRoot } from "../../mcp/filesystem/roots.js"
7
7
  const FRONTMATTER_DELIMITER = "---";
8
8
  export function effectiveSkillPaths(config, cwd) {
9
9
  const defaultPathCandidates = [
10
- join(homedir(), ".agents", "skills"),
11
10
  resolve(cwd, ".agents", "skills"),
11
+ join(homedir(), ".agents", "skills"),
12
12
  config.configSkillsDir,
13
13
  join(config.agentDir, "skills"),
14
14
  ];
@@ -201,12 +201,16 @@ gateway rather than each receiving another top-level tool schema.
201
201
  `open_workspace` adds lightweight discovery surfaces:
202
202
 
203
203
  - `capabilityFingerprint` is returned on every open/resume and includes the
204
- ForgeRelay version, active tool mode, and stable semantic capability names;
205
- - `capabilityCatalog` lists currently available registered actions such as
206
- `hooks.check`, with compact guide metadata;
204
+ ForgeRelay version, active tool mode, and stable semantic capability names. It
205
+ is a feature fingerprint, not a callable registry;
206
+ - `capabilityCatalog` is the callable registry for the `capability` gateway and
207
+ lists currently available registered actions such as `hooks.check`, with
208
+ compact guide metadata. Only names present here are valid for
209
+ `capability(action="describe"|"run")`;
207
210
  - `capabilityGuides` is returned with bootstrap context and contains compact
208
211
  descriptors for ForgeRelay-owned, versioned guides that can be loaded with
209
- the normal `read` tool.
212
+ the normal `read` tool. Guide names are documentation descriptors, not
213
+ callable capability names.
210
214
 
211
215
  Do not preload every capability guide. Read a guide only when the current task
212
216
  needs that domain. Built-in guides cover lifecycle Hooks, advanced managed
@@ -228,14 +232,16 @@ force the Host to discard a cached tool schema.
228
232
 
229
233
  ## Agent Skills
230
234
 
231
- ForgeRelay discovers standard Agent Skills from:
235
+ ForgeRelay discovers standard Agent Skills in precedence order from:
232
236
 
233
- - `~/.agents/skills`
234
237
  - project `.agents/skills`
238
+ - `~/.agents/skills`
235
239
  - the active ForgeRelay config directory's `skills` folder
236
240
  - `FORGERELAY_AGENT_DIR/skills` (defaults to `~/.codex/skills`)
237
241
  - paths from `FORGERELAY_SKILL_PATHS`
238
242
 
243
+ Same-named collisions use the first source, so project Skills override global Skills.
244
+
239
245
  When a task matches an advertised skill, read its `SKILL.md` before using other
240
246
  files in the skill directory.
241
247
 
@@ -10,8 +10,18 @@ New installations use:
10
10
  ```text
11
11
  ~/.forgerelay/config.json
12
12
  ~/.forgerelay/auth.json
13
+ ~/.forgerelay/mcp.json # optional External MCP registry
14
+ ~/.forgerelay/mcp-auth.json # created only when External MCP OAuth state exists
13
15
  ```
14
16
 
17
+ A Workspace may also define project-scoped External MCP servers in:
18
+
19
+ ```text
20
+ <workspace>/.forgerelay/mcp.json
21
+ ```
22
+
23
+ `mcp-auth.json` is ForgeRelay-private credential state and must not be copied into a project.
24
+
15
25
  Override the directory with:
16
26
 
17
27
  ```bash
@@ -26,6 +36,10 @@ npx @akira-tl/forgerelay serve
26
36
  npx @akira-tl/forgerelay doctor
27
37
  npx @akira-tl/forgerelay config get
28
38
  npx @akira-tl/forgerelay config set publicBaseUrl https://forge.example.com/forgerelay/main,https://forge-alt.example.com/relay
39
+ npx @akira-tl/forgerelay mcp list
40
+ npx @akira-tl/forgerelay mcp test <server>
41
+ npx @akira-tl/forgerelay mcp auth <server>
42
+ npx @akira-tl/forgerelay mcp logout <server>
29
43
  npx @akira-tl/forgerelay maintenance inspect
30
44
  ```
31
45
 
@@ -160,11 +174,27 @@ The same value may be persisted as `mediaMaxBytes` in `config.json`. The budget
160
174
 
161
175
  Media content is transient. Image base64 is present only in the live MCP result delivered to the Host; Activity/Audit, structured output, UI state, Workspace state, and logs keep bounded metadata such as MIME type and decoded byte size. Reading or forwarding an image does **not** create an Artifact. Artifact download/materialization remains a separate explicit workflow.
162
176
 
163
- User-configured MCP servers are registered in `config.json` under `mcpServers` and are exposed through the workspace-scoped `mcp.external` Capability rather than becoming new top-level MCP tools. For example:
177
+ User-configured MCP servers are exposed through the workspace-scoped `mcp.external` Capability rather than becoming new top-level MCP tools. The capability remains discoverable even when the effective server registry is empty, so servers added later by hot reload become usable without reconnecting the Host.
178
+
179
+ ### External MCP configuration files
180
+
181
+ New configuration uses a dedicated `mcp.json` file. The machine-wide registry is:
182
+
183
+ ```text
184
+ ~/.forgerelay/mcp.json
185
+ ```
186
+
187
+ or the equivalent path below `FORGERELAY_CONFIG_DIR`. A Workspace may add or override servers in:
188
+
189
+ ```text
190
+ <workspace>/.forgerelay/mcp.json
191
+ ```
192
+
193
+ Both files use this shape:
164
194
 
165
195
  ```json
166
196
  {
167
- "mcpServers": {
197
+ "servers": {
168
198
  "renderer": {
169
199
  "transport": "stdio",
170
200
  "command": "node",
@@ -172,15 +202,135 @@ User-configured MCP servers are registered in `config.json` under `mcpServers` a
172
202
  },
173
203
  "remote-renderer": {
174
204
  "transport": "streamable-http",
175
- "url": "https://renderer.example.com/mcp",
205
+ "url": "https://renderer.example.com/mcp"
206
+ }
207
+ }
208
+ }
209
+ ```
210
+
211
+ Server names are stable lowercase identifiers matching `^[a-z][a-z0-9._-]{0,63}$`; one source may contain at most 32 servers.
212
+
213
+ Effective configuration is merged by server name in this order:
214
+
215
+ ```text
216
+ Project mcp.json > global mcp.json > legacy config.json.mcpServers
217
+ ```
218
+
219
+ A Project entry may explicitly hide an inherited server:
220
+
221
+ ```json
222
+ {
223
+ "servers": {
224
+ "remote-renderer": {
225
+ "disabled": true
226
+ }
227
+ }
228
+ }
229
+ ```
230
+
231
+ `disabled` defaults to `false` and normally does not need to be written. Removing the Project entry exposes the lower-precedence global/legacy server again. A complete server entry may also contain `"disabled": true`; ForgeRelay validates the entry but does not activate it.
232
+
233
+ The historical `config.json -> mcpServers` object remains a deprecated read-compatible source. New configuration and documentation use `mcp.json`; the legacy source is retained for at least the next two feature-release compatibility windows and should not be used for new setups.
234
+
235
+ ForgeRelay reloads global and Project `mcp.json` on demand before External MCP operations. A valid changed file becomes visible to the next `servers`, `tools`, or `call` operation without restarting ForgeRelay. Each operation uses one immutable snapshot, so an in-flight call is not switched underneath itself. If a previously valid source becomes invalid, that running ForgeRelay process keeps the whole previous valid source as last-known-good and reports a bounded diagnostic; no partially parsed subset is activated. A fresh CLI process cannot reconstruct another process's in-memory last-known-good snapshot, so it reports the invalid source while noting that an existing runtime may still be using its prior valid snapshot.
236
+
237
+ ### External MCP transports and authentication
238
+
239
+ A stdio server supports `command`, optional `args`, `env`, and `cwd`. It is launched directly, not through a shell. Its process still runs with the operating-system authority of the user running ForgeRelay. A Project `.forgerelay/mcp.json` can therefore introduce executable project configuration; opening an allowed project is the trust boundary, and ForgeRelay does not add a second per-server approval prompt.
240
+
241
+ A Streamable HTTP server supports `url` and optional static `headers`:
242
+
243
+ ```json
244
+ {
245
+ "servers": {
246
+ "private-api": {
247
+ "transport": "streamable-http",
248
+ "url": "https://mcp.example.com/mcp",
176
249
  "headers": {
177
- "Authorization": "Bearer <token>"
250
+ "Authorization": "Bearer <user-managed-secret>"
178
251
  }
179
252
  }
180
253
  }
181
254
  }
182
255
  ```
183
256
 
257
+ ForgeRelay does not require every HTTP MCP server to use OAuth. The effective models are:
258
+
259
+ - no authentication: connect directly;
260
+ - static credentials: user-managed `headers` for HTTP or `env` for stdio;
261
+ - interactive OAuth: explicitly initiated by a human through the CLI.
262
+
263
+ Do not commit static secrets in Project `mcp.json`. ForgeRelay does not move project-defined header/env secrets into a separate secret manager in v1.1.1.
264
+
265
+ Interactive OAuth state is stored only in the machine-private:
266
+
267
+ ```text
268
+ ~/.forgerelay/mcp-auth.json
269
+ ```
270
+
271
+ (or the configured ForgeRelay config directory). Project OAuth credentials are still stored there, keyed to the canonical Project root and server; they are not written into the project. Global/legacy servers use a global credential identity. Project credentials are not reused across Projects merely because the server name or URL matches.
272
+
273
+ Authenticate explicitly:
274
+
275
+ ```bash
276
+ forgerelay mcp auth <server>
277
+ forgerelay mcp auth <server> --project /path/to/project
278
+ forgerelay mcp auth <server> --global
279
+ ```
280
+
281
+ The CLI performs OAuth discovery, PKCE and the browser/callback exchange, then atomically commits the credential. The running ForgeRelay server is not an OAuth callback broker; it simply reads the updated credential store on later External MCP calls. On a desktop the CLI opens the authorization URL and listens on a short-lived loopback callback. On SSH/headless systems it prints the authorization URL and asks for the final callback URL to be pasted; pasted input is displayed only as `*` mask characters.
282
+
283
+ Normal token refresh is non-interactive and may be performed by the runtime. If refresh fails, the resource/issuer binding changes, or the server requires additional scope, the runtime does not open a browser: it returns `mcp.auth_required` or `mcp.reauthorization_required`, and the human reruns `forgerelay mcp auth <server>`. Multiple ForgeRelay processes coordinate refresh through the shared credential store so refresh-token rotation does not turn a successful refresh in one process into a false reauthorization failure in another.
284
+
285
+ ForgeRelay uses Dynamic Client Registration when the authorization server supports the compatible flow. For CIMD-only authorization servers, configure a stable public HTTPS client metadata document and the exact loopback callback port it advertises:
286
+
287
+ ```json
288
+ {
289
+ "servers": {
290
+ "modern": {
291
+ "transport": "streamable-http",
292
+ "url": "https://mcp.example.com/mcp",
293
+ "oauth": {
294
+ "clientMetadataUrl": "https://client.example.com/forgerelay.json",
295
+ "callbackPort": 49152
296
+ }
297
+ }
298
+ }
299
+ }
300
+ ```
301
+
302
+ `clientMetadataUrl` must use HTTPS with a non-root path; `callbackPort` must be `1024..65535`. ForgeRelay does not operate a hosted CIMD service, so this advanced configuration is supplied by the operator when a server requires it.
303
+
304
+ Remove local OAuth state with:
305
+
306
+ ```bash
307
+ forgerelay mcp logout <server>
308
+ ```
309
+
310
+ ForgeRelay removes the local credential even when remote token revocation is unsupported or fails, and reports that distinction.
311
+
312
+ ### External MCP diagnostics
313
+
314
+ Use the CLI before editing runtime state by guesswork:
315
+
316
+ ```bash
317
+ forgerelay mcp list
318
+ forgerelay mcp test <server>
319
+ forgerelay doctor
320
+ ```
321
+
322
+ `mcp list` is passive: it reports the resolved Project/global/legacy sources, disabled entries, authentication state, credential-store health, and invalid/last-known-good configuration diagnostics. With no explicit scope it uses the active Workspace root when supplied by ForgeRelay, otherwise walks upward from the current directory for the nearest `.forgerelay/mcp.json`; `--project` selects a Project explicitly and `--global` excludes Project configuration.
323
+
324
+ `mcp test` is the active probe. It connects to the selected server and performs tool discovery, reporting source, transport, auth state, negotiated MCP era/version, tool count, and actionable failures such as authentication required, unreachable transport, or tool discovery failure.
325
+
326
+ `doctor` remains passive. Its External MCP section reads configuration and credential metadata only; it does not launch configured stdio servers or make HTTP MCP requests. Use `mcp test` when an active connectivity check is intended.
327
+
328
+ ### External MCP protocol compatibility and Relay
329
+
330
+ ForgeRelay's External MCP client negotiates modern MCP 2026-07-28 when available and falls back to supported legacy MCP servers. Host-facing HTTP similarly supports the modern stateless protocol while retaining the legacy sessionful path for compatible Hosts.
331
+
332
+ For a relayed Workspace, External MCP configuration, credentials, process launches, OAuth refreshes, and upstream MCP calls belong to the **Execution ForgeRelay**. The Gateway does not copy or forward External MCP credentials. Run `forgerelay mcp auth` on the machine/configuration that owns execution when a relayed Workspace needs OAuth.
333
+
184
334
  The Agent explicitly selects the configured server, advertised tool, and arguments through `capability(name="mcp.external", action="run", ...)`. Direct upstream PNG/JPEG/WebP/GIF `ImageContent` is validated and forwarded without decode/re-encode. Text, paths, URLs, `resource_link` values, and other references remain exactly that by default: ForgeRelay does not automatically fetch them, call `read`, infer that they refer to an image, or create an Artifact.
185
335
 
186
336
  A known server/tool may opt into explicit request/result adaptation with `ExternalMcpBeforeForward` or `ExternalMcpAfterForward` project/global Hooks. These transforms use the structured versioned stdin/stdout protocol described under [Lifecycle hooks](#lifecycle-hooks); ordinary Hook stdout never rewrites MCP data. For example, a project can match one renderer tool whose normal result is a path and deliberately convert that result into `ImageContent`. The Hook command performs any file/network access with its normal local-user authority, and the transformed result still passes through the same MCP shape, MIME/base64, and `mediaMaxBytes` validation before Host delivery.
@@ -710,14 +860,16 @@ select a global instruction file; its `skills` child is an additional Agent Skil
710
860
  | `FORGERELAY_AGENT_DIR` | Defaults to `~/.codex`; its `skills` child is included as an additional Skill source. |
711
861
  | `FORGERELAY_SKILL_PATHS` | Optional comma-separated additional skill directories. |
712
862
 
713
- Standard Agent Skills are discovered from:
863
+ Standard Agent Skills are discovered in precedence order from:
714
864
 
715
- - `~/.agents/skills`
716
865
  - project `.agents/skills`
866
+ - `~/.agents/skills`
717
867
  - the active ForgeRelay config directory's `skills` folder
718
868
  - `FORGERELAY_AGENT_DIR/skills`
719
869
  - paths from `FORGERELAY_SKILL_PATHS`
720
870
 
871
+ When the same Skill name appears in more than one source, the first source wins. Project Skills therefore override same-named global `~/.agents/skills` entries, matching ForgeRelay's project-over-global configuration model.
872
+
721
873
  When subagents are enabled, profiles are discovered from:
722
874
 
723
875
  - the active ForgeRelay config directory's `agents/*.md`;
package/docs/debugging.md CHANGED
@@ -54,7 +54,7 @@ The selected debug config is authoritative for its own `host` and `port`; the
54
54
  launcher reports endpoints derived from that file instead of assuming `7677`.
55
55
  This makes parallel isolated instances straightforward: give each instance its
56
56
  own config, state, and worktree directories and configure different ports (for
57
- example `7677` for a Gateway and `6768` for an Execution ForgeRelay), then launch
57
+ example `7677` for a Gateway and `7678` for an Execution ForgeRelay), then launch
58
58
  each with its own `FORGERELAY_DEBUG_CONFIG_DIR`.
59
59
 
60
60
  ## Run a local MCP Apps Host
@@ -107,12 +107,15 @@ behavior.
107
107
 
108
108
  ```bash
109
109
  npm run debug:accept
110
+ npm run debug:accept:modern
111
+ npm run debug:accept:relay
110
112
  ```
111
113
 
112
- The acceptance runner starts its own fully isolated ForgeRelay fixture on
113
- `127.0.0.1:7677`, with its own generated Owner password and checked-in debug
114
- configuration. It sends real HTTP requests with `curl` and shuts the server down
115
- when it finishes.
114
+ `debug:accept` starts its own fully isolated legacy/sessionful ForgeRelay fixture on
115
+ `127.0.0.1:7677`. `debug:accept:modern` exercises the MCP 2026-07-28 stateless Host
116
+ path on isolated `7678`, and `debug:accept:relay` exercises an isolated
117
+ 7677 Gateway → 7678 Execution topology. Each runner owns its temporary config/state,
118
+ generates its own authentication material, and shuts down what it started.
116
119
  It refuses to start when port `7677` is already occupied so it cannot
117
120
  accidentally validate an older debug process.
118
121
 
package/docs/roadmap.md CHANGED
@@ -348,9 +348,70 @@ recovery, checkpoint, and retention facts; the Gateway only routes and presents
348
348
  results. Development acceptance remains on isolated 7677/7678 instances and never uses
349
349
  the normal 7676 installation.
350
350
 
351
- Native Windows Command Shell Runtime support was intentionally kept out of the
352
- 0.9 recovery/history line and was subsequently delivered across the 0.10.x P3
353
- work for PowerShell 7, Windows PowerShell 5.1, and `cmd.exe`.
351
+ ## 0.10 Command Shell Runtime P3
352
+
353
+ 0.10 ForgeRelay 的命令执行从“默认假设 Bash”提升为显式、可观察、跨平台的
354
+ Command Shell Runtime,同时保持公开 Core tool 名称 `bash` 不变,避免因为 Windows
355
+ 支持而扩大 Host 的常驻 MCP surface。
356
+
357
+ - **0.10.0** — 建立 server-lifetime Command Shell Runtime identity 与选择优先级;
358
+ `forgerelay init` 管理可编辑 shell Instructions;系统级高权限启动默认拒绝,只有
359
+ 显式 `--allow-elevated` 才允许继续;
360
+ - **0.10.1** — 将 PowerShell 7(`pwsh`)作为 Windows 上的一等 Agent/Hook/PTY
361
+ runtime,并补齐真实 packaged npm acceptance;
362
+ - **0.10.2** — 加入 Windows PowerShell 5.1,完成 ConPTY、Unicode、进程树清理和
363
+ packaged launcher 路径;
364
+ - **0.10.3** — 将 `cmd.exe` 提升为一等 runtime,覆盖其独立 quoting、escaping、
365
+ `%ERRORLEVEL%`、pipe/PTY 与 `.cmd` launcher contract;
366
+ - **0.10.4** — 让 shell identity 跟随实际 Execution ForgeRelay,并贯通 Workspace
367
+ Relay、Composite Workspace、doctor、UI、Instructions 与公开 tool description;
368
+ - **0.10.5** — 移除 ForgeRelay 对 legacy `disable-model-invocation` Skill frontmatter
369
+ 的过滤,保持 Skill discovery 由 Agent 侧匹配决定。
370
+
371
+ P3 的发布验收以真实 Windows shell matrix 与 packed artifact 为核心,同时继续保留
372
+ Linux/macOS 回归验证。开发与运行时验收只使用隔离的 7677/7678 实例,不触碰 7676。
373
+
374
+ ## 1.0 — Stable product baseline
375
+
376
+ 1.0 将已经完成的 Workspace、Capability Gateway、Hooks、LSP、Activity/Audit、Tasks、
377
+ Recovery/Checkpoint、Relay/Composite 与跨平台 shell contract 提升为稳定的 1.x 产品
378
+ 基线,而不是重新设计一套运行时模型。
379
+
380
+ - **1.0.0** — 固化当前 MCP Core tool surface、CLI、Workspace 生命周期、managed
381
+ worktree finalize 与 Capability Gateway,现有 0.10.5 配置和持久化状态无需迁移;
382
+ - **1.0.1** — 修复 Capability 与 Workspace lifecycle pretty logging,使日志保留
383
+ semantic operation identity,并补齐 Composite capability attribution。
384
+
385
+ 1.x 继续遵守现有发布规则:稳定版本只有在本地 release gate 与真实 7677/7678 路径
386
+ 验收通过后才创建 tag;tag-triggered Linux/macOS/Windows workflow 成功后才形成正式
387
+ npm/GitHub Release。
388
+
389
+ ## 1.1 — First-class image transport
390
+
391
+ 1.1 把 image-bearing MCP result 纳入 ForgeRelay 的一等、受预算约束的传输路径,
392
+ 同时保持“ForgeRelay 不是图片仓库,也不自动解引用外部引用”的边界。
393
+
394
+ - `read` 依据文件签名把 PNG、JPEG、WebP、GIF 返回为标准 MCP `ImageContent`,bulk
395
+ Read 保留 text/image 输入顺序;
396
+ - Workspace Relay 在 Execution ForgeRelay → Gateway ForgeRelay 路径中原样保留
397
+ image content,并在 Gateway 侧再次执行 media budget;
398
+ - 用户显式配置的外部 MCP server 通过 `mcp.external` Capability 调用,原生
399
+ `ImageContent` 可直接转发,但 path/URL/resource reference 默认仍只是 reference;
400
+ - `ExternalMcpBeforeForward` / `ExternalMcpAfterForward` Transform Hook 为特定
401
+ server/tool 提供显式结构化适配 seam,但不能改写已选择的 server/tool;
402
+ - Media content 保持瞬时 MCP payload,不自动成为 Artifact;Activity/Audit、日志、
403
+ structured output 与 Workspace state 只持久化受限 metadata,不保存 image base64;
404
+ - 每个 tool result 默认采用 20 MiB decoded-media budget,超限显式失败,不静默截断、
405
+ resize 或转码。
406
+
407
+ 1.1.0 已完成真实 7677 HTTP/OAuth/MCP、7677 → 7678 Relay、external MCP、Transform
408
+ Hook、packed artifact 和 ChatGPT Host 图像呈现验收,并作为正式稳定版本发布。
409
+
410
+ ## 1.x forward planning
411
+
412
+ 当前 roadmap 没有承诺 1.1 之后的下一条 minor feature line。新的 1.x 产品工作应从
413
+ 明确的用户目标、架构问题或外部 issue 出发,先形成可验证的 spec/tickets,再进入实现;
414
+ 不要仅根据历史版本号顺序推断或虚构 1.2 功能范围。
354
415
 
355
416
  ## Workspace Task Lists
356
417
 
package/docs/security.md CHANGED
@@ -147,6 +147,24 @@ Workspace from another conversation intentionally shares the same completion sco
147
147
  as well. Hook handlers keep their separate bounded timeout
148
148
  policy because they are lifecycle gates rather than user-command execution.
149
149
 
150
+ ## External MCP configuration and credentials
151
+
152
+ External MCP extends the same local-authority model rather than creating a sandbox. Machine-wide servers live in the ForgeRelay config directory's `mcp.json`; Project servers may live in `<workspace>/.forgerelay/mcp.json`. A Project stdio entry can launch a configured executable with the same operating-system user authority as ForgeRelay, so Project MCP configuration is executable project configuration. ForgeRelay does not add a second per-server approval prompt after the operator has allowed and opened that project root.
153
+
154
+ Static HTTP headers and stdio environment values are user-managed configuration. Do not commit secrets in Project `mcp.json`; v1.1.1 does not automatically extract project-defined secrets into a secret manager.
155
+
156
+ Interactive External MCP OAuth credentials are different. They are kept in the machine-private ForgeRelay config directory:
157
+
158
+ ```text
159
+ ~/.forgerelay/mcp-auth.json
160
+ ```
161
+
162
+ Project OAuth credentials are keyed to the Project root but still stay in this private machine file; ForgeRelay does not write access tokens, refresh tokens, authorization codes, PKCE verifiers, or client secrets into the project. Callback URLs pasted in headless mode are masked in the terminal. Runtime token refresh is non-interactive; any fresh consent/authorization requires an explicit human `forgerelay mcp auth <server>` invocation.
163
+
164
+ External MCP configuration and OAuth state belong to the Execution ForgeRelay for relayed Workspaces. The Gateway does not receive or replicate those credentials merely because it forwards the Workspace operation.
165
+
166
+ External MCP result references are not implicitly dereferenced. Paths, URLs and MCP resource references returned by an upstream server remain references unless the Agent explicitly reads them or an explicitly configured External MCP transform Hook changes the result.
167
+
150
168
  ## Lifecycle hooks
151
169
 
152
170
  Hook command 是本地代码执行,使用与 ForgeRelay 相同的操作系统用户权限并继承进程环境。