@agent-native/core 0.19.0 → 0.19.1

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 (72) hide show
  1. package/dist/a2a/caller-auth.d.ts +1 -0
  2. package/dist/a2a/caller-auth.d.ts.map +1 -1
  3. package/dist/a2a/caller-auth.js +1 -1
  4. package/dist/a2a/caller-auth.js.map +1 -1
  5. package/dist/agent/production-agent.d.ts +1 -1
  6. package/dist/agent/production-agent.d.ts.map +1 -1
  7. package/dist/agent/production-agent.js +34 -2
  8. package/dist/agent/production-agent.js.map +1 -1
  9. package/dist/cli/code-agent-executor.d.ts.map +1 -1
  10. package/dist/cli/code-agent-executor.js +47 -256
  11. package/dist/cli/code-agent-executor.js.map +1 -1
  12. package/dist/client/AgentPanel.d.ts +3 -1
  13. package/dist/client/AgentPanel.d.ts.map +1 -1
  14. package/dist/client/AgentPanel.js +4 -4
  15. package/dist/client/AgentPanel.js.map +1 -1
  16. package/dist/client/AssistantChat.d.ts +3 -0
  17. package/dist/client/AssistantChat.d.ts.map +1 -1
  18. package/dist/client/AssistantChat.js +11 -3
  19. package/dist/client/AssistantChat.js.map +1 -1
  20. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  21. package/dist/client/MultiTabAssistantChat.js +4 -1
  22. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  23. package/dist/client/dynamic-suggestions.d.ts +43 -0
  24. package/dist/client/dynamic-suggestions.d.ts.map +1 -0
  25. package/dist/client/dynamic-suggestions.js +344 -0
  26. package/dist/client/dynamic-suggestions.js.map +1 -0
  27. package/dist/client/index.d.ts +1 -0
  28. package/dist/client/index.d.ts.map +1 -1
  29. package/dist/client/index.js +1 -0
  30. package/dist/client/index.js.map +1 -1
  31. package/dist/client/settings/SettingsPanel.js +2 -2
  32. package/dist/client/settings/SettingsPanel.js.map +1 -1
  33. package/dist/coding-tools/index.d.ts +31 -0
  34. package/dist/coding-tools/index.d.ts.map +1 -0
  35. package/dist/coding-tools/index.js +411 -0
  36. package/dist/coding-tools/index.js.map +1 -0
  37. package/dist/mcp/builtin-tools.d.ts.map +1 -1
  38. package/dist/mcp/builtin-tools.js +85 -26
  39. package/dist/mcp/builtin-tools.js.map +1 -1
  40. package/dist/mcp/connect-route.d.ts.map +1 -1
  41. package/dist/mcp/connect-route.js +148 -42
  42. package/dist/mcp/connect-route.js.map +1 -1
  43. package/dist/mcp/org-directory.d.ts +83 -0
  44. package/dist/mcp/org-directory.d.ts.map +1 -0
  45. package/dist/mcp/org-directory.js +201 -0
  46. package/dist/mcp/org-directory.js.map +1 -0
  47. package/dist/mcp/server.d.ts +38 -1
  48. package/dist/mcp/server.d.ts.map +1 -1
  49. package/dist/mcp/server.js +208 -77
  50. package/dist/mcp/server.js.map +1 -1
  51. package/dist/scripts/dev/index.d.ts +6 -4
  52. package/dist/scripts/dev/index.d.ts.map +1 -1
  53. package/dist/scripts/dev/index.js +28 -13
  54. package/dist/scripts/dev/index.js.map +1 -1
  55. package/dist/server/agent-chat-plugin.d.ts +6 -6
  56. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  57. package/dist/server/agent-chat-plugin.js +32 -32
  58. package/dist/server/agent-chat-plugin.js.map +1 -1
  59. package/dist/server/agent-teams.js +2 -2
  60. package/dist/server/agent-teams.js.map +1 -1
  61. package/dist/server/agents-bundle.d.ts +3 -3
  62. package/dist/server/agents-bundle.js +5 -5
  63. package/dist/server/agents-bundle.js.map +1 -1
  64. package/dist/server/sentry.d.ts.map +1 -1
  65. package/dist/server/sentry.js +17 -2
  66. package/dist/server/sentry.js.map +1 -1
  67. package/docs/content/client.md +15 -0
  68. package/docs/content/code-agents-ui.md +11 -1
  69. package/docs/content/drop-in-agent.md +3 -1
  70. package/docs/content/frames.md +1 -1
  71. package/docs/content/migration-workbench.md +5 -0
  72. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"connect-route.js","sourceRoot":"","sources":["../../src/mcp/connect-route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAGH,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EACL,iBAAiB,EACjB,UAAU,EACV,WAAW,EACX,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,sBAAsB,EACtB,oBAAoB,EACpB,qBAAqB,EACrB,gBAAgB,EAChB,iBAAiB,EACjB,sBAAsB,EACtB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAE5B,gDAAgD;AAChD,MAAM,sBAAsB,GAAG,CAAC,CAAC;AAEjC,6DAA6D;AAC7D,MAAM,YAAY,GAAG,2BAA2B,CAAC;AASjD,SAAS,IAAI,CAAC,IAAa,EAAE,MAAM,GAAG,GAAG;IACvC,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE;QACxC,MAAM;QACN,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;KAChD,CAAC,CAAC;AACL,CAAC;AAED,SAAS,IAAI,CAAC,IAAY,EAAE,MAAM,GAAG,GAAG;IACtC,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE;QACxB,MAAM;QACN,OAAO,EAAE,EAAE,cAAc,EAAE,0BAA0B,EAAE;KACxD,CAAC,CAAC;AACL,CAAC;AAED;8EAC8E;AAC9E,SAAS,YAAY,CAAC,KAAc;IAClC,MAAM,cAAc,GAAG,SAAS,CAAC,KAAK,EAAE,mBAAmB,CAAC,CAAC;IAC7D,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,EAAE,kBAAkB,CAAC,IAAI,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAC9E,MAAM,KAAK,GACT,cAAc,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE;QACrC,CAAC,IAAI,IAAI,gCAAgC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAC3E,OAAO,IAAI,CAAC,CAAC,CAAC,GAAG,KAAK,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAC1C,CAAC;AAED,SAAS,iBAAiB,CAAC,GAAuB;IAChD,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACnC,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,GAAG;QAAE,OAAO,EAAE,CAAC;IAC3C,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,EAAE,CAAC;IACpE,OAAO,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACvC,CAAC;AAED,SAAS,kBAAkB;IACzB,OAAO,iBAAiB,CACtB,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAC5D,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,QAAgB,EAAE,IAAY;IACjD,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,IAAI,IAAI,KAAK,GAAG;QAAE,OAAO,QAAQ,CAAC;IAClC,OAAO,GAAG,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;AAClE,CAAC;AAED,SAAS,QAAQ,CAAC,MAAc,EAAE,OAA+B;IAC/D,IAAI,OAAO,CAAC,KAAK;QAAE,OAAO,OAAO,CAAC,KAAK,CAAC;IACxC,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC;QACnC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC,OAAO,IAAI,KAAK,CAAC;IAClC,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,MAAc,EAAE,OAA+B;IACjE,OAAO,gBAAgB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;AACrD,CAAC;AAED,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC;SACL,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC7B,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,gBAAgB,CAC7B,KAAyB;IAEzB,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,YAAY,CAAC,KAAK,CAAC,CAAC,IAAI,SAAS,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IAClC,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IACxB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,sBAAsB,CAAC;IACvD,OAAO,IAAI,CAAC,GAAG,CACb,kBAAkB,EAClB,IAAI,CAAC,GAAG,CAAC,kBAAkB,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAC5C,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,gBAAgB,CAAC,MAK/B;IACC,MAAM,SAAS,GAAG,MAAM,gBAAgB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvD,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;IACzB,0EAA0E;IAC1E,2EAA2E;IAC3E,yEAAyE;IACzE,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE;QACnE,kBAAkB,EAAE,IAAI;QACxB,SAAS,EAAE,GAAG,MAAM,CAAC,OAAO,GAAG;QAC/B,WAAW,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,iBAAiB,EAAE;KAC/C,CAAC,CAAC;IACH,MAAM,iBAAiB,CAAC;QACtB,GAAG;QACH,UAAU,EAAE,MAAM,CAAC,KAAK;QACxB,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,IAAI;QAC3B,KAAK,EAAE,MAAM,CAAC,KAAK;KACpB,CAAC,CAAC;IACH,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;AACxB,CAAC;AAED,SAAS,gBAAgB,CACvB,MAAc,EACd,KAAa,EACb,OAA+B;IAE/B,MAAM,MAAM,GAAG,GAAG,MAAM,oBAAoB,CAAC;IAC7C,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,OAAO;QACL,KAAK;QACL,MAAM;QACN,UAAU,EAAE,IAAI;QAChB,cAAc,EAAE;YACd,IAAI,EAAE,MAAe;YACrB,GAAG,EAAE,MAAM;YACX,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE;SAC9C;QACD,GAAG,EAAE,wBAAwB,MAAM,EAAE;KACtC,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,6CAA6C;AAC7C,8EAA8E;AAE9E,SAAS,iBAAiB,CAAC,MAM1B;IACC,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;IACrE,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IACtC,MAAM,SAAS,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IACpC,MAAM,YAAY,GAChB,QAAQ,IAAI,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACtE,OAAO;;;;;iBAKQ,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uDA+E+B,OAAO;kCAC5B,SAAS,aAAa,UAAU;;8CAEpB,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ;;4CAE9B,YAAY;;;;;;;;;;;;+DAYO,sBAAsB;;gDAErC,YAAY,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,yBAAyB;;;;;;;;;;;;;;;;;eAiB9F,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,eAAe,EAAE,4BAA4B,CAAC,CAAC;oBACrE,IAAI,CAAC,SAAS,CAAC,YAAY,IAAI,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA4GhD,CAAC;AACT,CAAC;AAED,8EAA8E;AAC9E,2EAA2E;AAC3E,8EAA8E;AAE9E;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,KAAc,EACd,OAAe,EACf,UAAkC,EAAE;IAEpC,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IAChC,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,QAAQ,GAAG,kBAAkB,EAAE,CAAC;IACtC,MAAM,MAAM,GAAG,GAAG,MAAM,GAAG,QAAQ,EAAE,CAAC;IACtC,MAAM,GAAG,GAAG,CAAC,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CACzE,MAAM,EACN,EAAE,CACH,CAAC;IAEF,yEAAyE;IACzE,IAAI,GAAG,KAAK,EAAE,EAAE,CAAC;QACf,IAAI,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;YAC1C,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,oBAAoB,EAAE,EAAE,GAAG,CAAC,CAAC;QACpD,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,CAAC;QACxC,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;YACpB,oEAAoE;YACpE,iEAAiE;YACjE,MAAM,SAAS,GAAG,sBAAsB,CAAC,KAAK,CAAC,CAAC;YAChD,IAAI,SAAS;gBAAE,OAAO,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;YAC3C,8DAA8D;YAC9D,OAAO,IAAI,CACT,iBAAiB,CAAC;gBAChB,MAAM,EAAE,MAAM;gBACd,eAAe,EAAE,QAAQ;gBACzB,KAAK,EAAE,sBAAsB;gBAC7B,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;gBACrD,QAAQ,EAAE,IAAI;aACf,CAAC,CACH,CAAC;QACJ,CAAC;QACD,IAAI,QAAQ,GAAkB,IAAI,CAAC;QACnC,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,IAAI,GAAG,CACf,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,KAAK,CAAC,IAAI,IAAI,GAAG,EACzC,mBAAmB,CACpB,CAAC;YACF,MAAM,GAAG,GAAG,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YAC5C,IAAI,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC;gBAAE,QAAQ,GAAG,GAAG,CAAC;QACpD,CAAC;QAAC,MAAM,CAAC;YACP,QAAQ,GAAG,IAAI,CAAC;QAClB,CAAC;QACD,OAAO,IAAI,CACT,iBAAiB,CAAC;YAChB,MAAM,EAAE,MAAM;YACd,eAAe,EAAE,QAAQ;YACzB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;YACrD,QAAQ;SACT,CAAC,CACH,CAAC;IACJ,CAAC;IAED,yEAAyE;IACzE,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;QACrB,IAAI,MAAM,KAAK,MAAM;YAAE,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,oBAAoB,EAAE,EAAE,GAAG,CAAC,CAAC;QACzE,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,CAAC;QACxC,IAAI,CAAC,OAAO,EAAE,KAAK;YAAE,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE,GAAG,CAAC,CAAC;QACjE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC;YAC5B,OAAO,IAAI,CACT;gBACE,KAAK,EACH,mFAAmF;aACtF,EACD,GAAG,CACJ,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,QAAQ,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAG5D,CAAC;QACF,MAAM,KAAK,GACT,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;YACjD,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;YACjC,CAAC,CAAC,IAAI,CAAC;QACX,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC3C,IAAI,CAAC;YACH,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,gBAAgB,CAAC;gBACvC,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,KAAK;gBACL,OAAO;aACR,CAAC,CAAC;YACH,OAAO,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;QACxD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,EAAE,GAAG,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAED,yEAAyE;IACzE,IAAI,GAAG,KAAK,eAAe,EAAE,CAAC;QAC5B,IAAI,MAAM,KAAK,MAAM;YAAE,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,oBAAoB,EAAE,EAAE,GAAG,CAAC,CAAC;QACzE,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,gBAAgB,EAAE,CAAC;YACrC,MAAM,eAAe,GAAG,GAAG,MAAM,4BAA4B,CAAC;YAC9D,OAAO,IAAI,CAAC;gBACV,WAAW,EAAE,GAAG,CAAC,UAAU;gBAC3B,SAAS,EAAE,GAAG,CAAC,QAAQ;gBACvB,gBAAgB,EAAE,eAAe;gBACjC,yBAAyB,EAAE,GAAG,eAAe,cAAc,GAAG,CAAC,QAAQ,EAAE;gBACzE,QAAQ,EAAE,sBAAsB;gBAChC,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,kBAAkB,GAAG,IAAI,CAAC;aAClD,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,IAAI,GAAG,EAAE,OAAO,KAAK,cAAc,EAAE,CAAC;gBACpC,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,kCAAkC,EAAE,EAAE,GAAG,CAAC,CAAC;YAClE,CAAC;YACD,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,8BAA8B,EAAE,EAAE,GAAG,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IAED,yEAAyE;IACzE,IAAI,GAAG,KAAK,mBAAmB,EAAE,CAAC;QAChC,IAAI,MAAM,KAAK,MAAM;YAAE,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,oBAAoB,EAAE,EAAE,GAAG,CAAC,CAAC;QACzE,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,CAAC;QACxC,IAAI,CAAC,OAAO,EAAE,KAAK;YAAE,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE,GAAG,CAAC,CAAC;QACjE,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,QAAQ,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAE5D,CAAC;QACF,MAAM,QAAQ,GACZ,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAClE,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YACjC,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,oBAAoB,EAAE,EAAE,GAAG,CAAC,CAAC;QACpD,CAAC;QACD,MAAM,KAAK,GACT,OAAO,OAAO,CAAC,KAAK,KAAK,QAAQ,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE;YACvD,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE;YACtB,CAAC,CAAC,IAAI,CAAC;QACX,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,QAAQ,EAAE,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACvE,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;YAC3B,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,sBAAsB,EAAE,EAAE,GAAG,CAAC,CAAC;QACtD,CAAC;QACD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,+BAA+B,EAAE,EAAE,GAAG,CAAC,CAAC;QAC/D,CAAC;QACD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,oCAAoC,EAAE,EAAE,GAAG,CAAC,CAAC;QACpE,CAAC;QACD,OAAO,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;IACtC,CAAC;IAED,yEAAyE;IACzE,IAAI,GAAG,KAAK,cAAc,EAAE,CAAC;QAC3B,IAAI,MAAM,KAAK,MAAM;YAAE,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,oBAAoB,EAAE,EAAE,GAAG,CAAC,CAAC;QACzE,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,QAAQ,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAE5D,CAAC;QACF,MAAM,UAAU,GACd,OAAO,IAAI,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/D,IAAI,CAAC,UAAU;YAAE,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,sBAAsB,EAAE,EAAE,GAAG,CAAC,CAAC;QACrE,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,UAAU,CAAC,CAAC;QAC5C,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,GAAG,CAAC,CAAC;QACpD,IAAI,GAAG,CAAC,MAAM,KAAK,UAAU;YAAE,OAAO,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;QACnE,IACE,GAAG,CAAC,MAAM,KAAK,SAAS;YACxB,CAAC,GAAG,CAAC,SAAS,IAAI,IAAI,IAAI,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,EACrD,CAAC;YACD,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS;gBAAE,KAAK,gBAAgB,CAAC,UAAU,CAAC,CAAC;YAChE,OAAO,IAAI,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;QACrC,CAAC;QACD,IACE,GAAG,CAAC,MAAM,KAAK,SAAS;YACxB,GAAG,CAAC,MAAM,KAAK,SAAS;YACxB,CAAC,GAAG,CAAC,UAAU,EACf,CAAC;YACD,OAAO,IAAI,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;QACrC,CAAC;QACD,iEAAiE;QACjE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC;YAC5B,OAAO,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,2BAA2B,EAAE,EAAE,GAAG,CAAC,CAAC;QAC5E,CAAC;QACD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;YACzB,wEAAwE;YACxE,8DAA8D;YAC9D,MAAM,OAAO,GAAG,MAAM,sBAAsB,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;YAC9D,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,UAAU,CAAC,CAAC;gBAC9C,IAAI,KAAK,EAAE,MAAM,KAAK,UAAU;oBAAE,OAAO,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;gBACtE,OAAO,IAAI,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;YACrC,CAAC;YACD,IAAI,KAAa,CAAC;YAClB,IAAI,CAAC;gBACH,MAAM,SAAS,GAAG,MAAM,gBAAgB,CAAC,OAAO,CAAC,KAAK,IAAI,SAAS,CAAC,CAAC;gBACrE,KAAK,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,UAAW,EAAE,SAAS,EAAE,SAAS,EAAE;oBACpE,kBAAkB,EAAE,IAAI;oBACxB,SAAS,EAAE,GAAG,sBAAsB,GAAG;oBACvC,WAAW,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,iBAAiB,EAAE;iBAC/C,CAAC,CAAC;gBACH,MAAM,iBAAiB,CAAC;oBACtB,GAAG;oBACH,UAAU,EAAE,OAAO,CAAC,UAAW;oBAC/B,KAAK,EAAE,OAAO,CAAC,KAAK;oBACpB,KAAK,EAAE,mBAAmB;iBAC3B,CAAC,CAAC;gBACH,IAAI,CAAC,CAAC,MAAM,oBAAoB,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;oBACnD,OAAO,IAAI,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;gBACrC,CAAC;YACH,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,qBAAqB,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;gBAC7C,MAAM,GAAG,CAAC;YACZ,CAAC;YACD,OAAO,IAAI,CAAC;gBACV,MAAM,EAAE,UAAU;gBAClB,GAAG,gBAAgB,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC;aAC5C,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,uBAAuB,EAAE,EAAE,GAAG,CAAC,CAAC;QACxE,CAAC;IACH,CAAC;IAED,yEAAyE;IACzE,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,IAAI,MAAM,KAAK,KAAK;YAAE,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,oBAAoB,EAAE,EAAE,GAAG,CAAC,CAAC;QACxE,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,CAAC;QACxC,IAAI,CAAC,OAAO,EAAE,KAAK;YAAE,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE,GAAG,CAAC,CAAC;QACjE,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC7C,OAAO,IAAI,CAAC;YACV,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACvB,EAAE,EAAE,CAAC,CAAC,EAAE;gBACR,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,UAAU,EAAE,CAAC,CAAC,UAAU;gBACxB,SAAS,EAAE,CAAC,CAAC,SAAS;aACvB,CAAC,CAAC;SACJ,CAAC,CAAC;IACL,CAAC;IAED,yEAAyE;IACzE,IAAI,GAAG,KAAK,gBAAgB,EAAE,CAAC;QAC7B,IAAI,MAAM,KAAK,MAAM;YAAE,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,oBAAoB,EAAE,EAAE,GAAG,CAAC,CAAC;QACzE,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,CAAC;QACxC,IAAI,CAAC,OAAO,EAAE,KAAK;YAAE,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE,GAAG,CAAC,CAAC;QACjE,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,QAAQ,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAE5D,CAAC;QACF,MAAM,EAAE,GAAG,OAAO,IAAI,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtD,IAAI,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,GAAG,CAAC,CAAC;QACpD,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACrD,OAAO,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,GAAG,CAAC,CAAC;AAC3C,CAAC","sourcesContent":["/**\n * `/_agent-native/mcp/connect` — frictionless external-agent connection.\n *\n * A logged-in user on a deployed agent-native app (e.g. mail.agent-native.com)\n * mints a per-user, scoped, revocable MCP bearer token WITHOUT ever copying a\n * shared deployment secret. Two surfaces:\n *\n * 1. Browser — `GET /connect` renders a minimal in-app page (same inline\n * HTML approach as the auth pages). The Authorize button POSTs to\n * `/connect/token`, then shows the ready-to-paste `.mcp.json` entry, the\n * `agent-native connect <origin>` one-liner, and the user's existing\n * tokens with Revoke buttons.\n * 2. CLI — an OAuth-2.0-device-authorization-style flow:\n * POST /connect/device/start (unauth) → device_code + user_code\n * GET /connect?user_code=… (browser) → user signs in & approves\n * POST /connect/device/authorize (session) → binds user to the code\n * POST /connect/device/poll (unauth) → mints + returns the token\n *\n * The minted token reuses the existing A2A signer (`signA2AToken`) — no new\n * crypto. We only add a random `jti` + `scope: \"mcp-connect\"` claim so it can\n * be revoked. `verifyAuth` already verifies A2A_SECRET JWTs and extracts\n * `sub`/`org_domain`, so a minted token works against `/_agent-native/mcp`\n * with no verify changes for the happy path (the revoke check is the only\n * addition there).\n *\n * Node-only (crypto + the A2A signer), bundled alongside the other framework\n * routes. Dialect-agnostic SQL lives in `connect-store.ts`.\n */\n\nimport type { H3Event } from \"h3\";\nimport { getMethod, getHeader } from \"h3\";\nimport { readBody } from \"../server/h3-helpers.js\";\nimport { getSession, getConfiguredLoginHtml } from \"../server/auth.js\";\nimport { signA2AToken } from \"../a2a/client.js\";\nimport { getOrgDomain } from \"../org/context.js\";\nimport { randomUUID } from \"node:crypto\";\nimport {\n recordMintedToken,\n listTokens,\n revokeToken,\n createDeviceCode,\n getDeviceCode,\n approveDeviceCode,\n claimDeviceCodeForMint,\n finishDeviceCodeMint,\n releaseDeviceCodeMint,\n expireDeviceCode,\n MCP_CONNECT_SCOPE,\n DEFAULT_TOKEN_TTL_DAYS,\n MIN_TOKEN_TTL_DAYS,\n MAX_TOKEN_TTL_DAYS,\n DEVICE_CODE_TTL_MS,\n} from \"./connect-store.js\";\n\n/** Device-flow poll interval hint (seconds). */\nconst DEVICE_POLL_INTERVAL_S = 3;\n\n// Human-typable user code: 8 base32 chars, dashed XXXX-XXXX.\nconst USER_CODE_RE = /^[A-Z2-7]{4}-[A-Z2-7]{4}$/;\n\nexport interface McpConnectRouteOptions {\n /** App id (directory under apps/, e.g. `mail`). Used for the server name. */\n appId?: string;\n /** Human app name shown on the connect page. */\n appName?: string;\n}\n\nfunction json(body: unknown, status = 200): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { \"Content-Type\": \"application/json\" },\n });\n}\n\nfunction html(body: string, status = 200): Response {\n return new Response(body, {\n status,\n headers: { \"Content-Type\": \"text/html; charset=utf-8\" },\n });\n}\n\n/** Derive the running app's origin from request headers (same logic mountMCP\n * uses) — `https` in prod / for non-loopback hosts, `http` for localhost. */\nfunction deriveOrigin(event: H3Event): string {\n const forwardedProto = getHeader(event, \"x-forwarded-proto\");\n const host = getHeader(event, \"x-forwarded-host\") || getHeader(event, \"host\");\n const proto =\n forwardedProto?.split(\",\")[0]?.trim() ||\n (host && /^(localhost|127\\.0\\.0\\.1)(:|$)/.test(host) ? \"http\" : \"https\");\n return host ? `${proto}://${host}` : \"\";\n}\n\nfunction normalizeBasePath(raw: string | undefined): string {\n const trimmed = (raw ?? \"\").trim();\n if (!trimmed || trimmed === \"/\") return \"\";\n const withSlash = trimmed.startsWith(\"/\") ? trimmed : `/${trimmed}`;\n return withSlash.replace(/\\/+$/, \"\");\n}\n\nfunction configuredBasePath(): string {\n return normalizeBasePath(\n process.env.APP_BASE_PATH || process.env.VITE_APP_BASE_PATH,\n );\n}\n\nfunction joinAppPath(basePath: string, path: string): string {\n if (!basePath) return path;\n if (path === \"/\") return basePath;\n return `${basePath}${path.startsWith(\"/\") ? path : `/${path}`}`;\n}\n\nfunction appLabel(origin: string, options: McpConnectRouteOptions): string {\n if (options.appId) return options.appId;\n try {\n const h = new URL(origin).hostname;\n return h.split(\".\")[0] || h;\n } catch {\n return options.appName || \"app\";\n }\n}\n\nfunction serverName(origin: string, options: McpConnectRouteOptions): string {\n return `agent-native-${appLabel(origin, options)}`;\n}\n\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\");\n}\n\n/**\n * Resolve the org domain for a session. Used as the JWT `org_domain` claim so\n * the receiving MCP endpoint can map it back to an org id (same as A2A). Best\n * effort — a missing org just yields a user-scoped (no-org) token.\n */\nasync function resolveOrgDomain(\n orgId: string | undefined,\n): Promise<string | undefined> {\n if (!orgId) return undefined;\n try {\n return (await getOrgDomain(orgId)) ?? undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction clampTtlDays(input: unknown): number {\n const n = Number(input);\n if (!Number.isFinite(n)) return DEFAULT_TOKEN_TTL_DAYS;\n return Math.min(\n MAX_TOKEN_TTL_DAYS,\n Math.max(MIN_TOKEN_TTL_DAYS, Math.floor(n)),\n );\n}\n\n/**\n * Mint a connect-scoped JWT and record it. The JWT is signed by the existing\n * A2A signer (HS256 over A2A_SECRET); we add a random `jti` and\n * `scope: \"mcp-connect\"` so the token is individually revocable. The token\n * value is returned to the caller exactly once and never persisted.\n */\nasync function mintConnectToken(params: {\n email: string;\n orgId: string | undefined;\n label: string | null;\n ttlDays: number;\n}): Promise<{ token: string; jti: string }> {\n const orgDomain = await resolveOrgDomain(params.orgId);\n const jti = randomUUID();\n // signA2AToken signs { sub: email, org_domain? } over A2A_SECRET (global)\n // or the org secret. We extend its claims via the standard jose builder by\n // re-using the same signer with extra claims threaded through `options`.\n const token = await signA2AToken(params.email, orgDomain, undefined, {\n preferGlobalSecret: true,\n expiresIn: `${params.ttlDays}d`,\n extraClaims: { jti, scope: MCP_CONNECT_SCOPE },\n });\n await recordMintedToken({\n jti,\n ownerEmail: params.email,\n orgId: params.orgId ?? null,\n label: params.label,\n });\n return { token, jti };\n}\n\nfunction mcpResultPayload(\n appUrl: string,\n token: string,\n options: McpConnectRouteOptions,\n) {\n const mcpUrl = `${appUrl}/_agent-native/mcp`;\n const name = serverName(appUrl, options);\n return {\n token,\n mcpUrl,\n serverName: name,\n mcpServerEntry: {\n type: \"http\" as const,\n url: mcpUrl,\n headers: { Authorization: `Bearer ${token}` },\n },\n cli: `agent-native connect ${appUrl}`,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Connect page (server-rendered HTML string)\n// ---------------------------------------------------------------------------\n\nfunction renderConnectPage(params: {\n origin: string;\n connectBasePath: string;\n email: string;\n appName: string;\n userCode: string | null;\n}): string {\n const { origin, connectBasePath, email, appName, userCode } = params;\n const safeOrigin = escapeHtml(origin);\n const safeEmail = escapeHtml(email);\n const safeApp = escapeHtml(appName);\n const safeUserCode =\n userCode && USER_CODE_RE.test(userCode) ? escapeHtml(userCode) : \"\";\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Connect ${safeApp}</title>\n<style>\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n :root {\n color-scheme: dark;\n --bg: #09090b; --panel: #141417; --border: rgba(255,255,255,0.1);\n --text: #f4f4f5; --muted: #a1a1aa; --subtle: #71717a;\n --accent: #f4f4f5; --accent-fg: #09090b;\n --error: #fca5a5; --error-bg: rgba(127,29,29,0.18);\n --ok: #86efac; --ok-bg: rgba(20,83,45,0.2);\n }\n body {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n background: linear-gradient(180deg, #111114 0%, var(--bg) 58%);\n color: var(--text); display: flex; align-items: center;\n justify-content: center; min-height: 100vh; padding: 1rem;\n }\n .card {\n width: 100%; max-width: 520px; padding: 2rem;\n background: var(--panel); border: 1px solid var(--border);\n border-radius: 12px; box-shadow: 0 24px 80px rgba(0,0,0,0.35);\n }\n h1 { font-size: 1.35rem; font-weight: 650; margin-bottom: 0.35rem; }\n .sub { color: var(--muted); font-size: 0.9rem; margin-bottom: 1.25rem; }\n .row { color: var(--subtle); font-size: 0.8rem; margin-bottom: 1.25rem; }\n .code-callout {\n border: 1px solid var(--border); border-radius: 8px; padding: 0.85rem 1rem;\n margin-bottom: 1.25rem; background: rgba(255,255,255,0.03);\n }\n .code-callout .label { font-size: 0.72rem; color: var(--subtle);\n text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 0.35rem; }\n .code-callout .value { font-size: 1.5rem; font-weight: 700;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n letter-spacing: 0.08em; }\n button {\n cursor: pointer; font: inherit; font-weight: 600; border: none;\n border-radius: 8px; padding: 0.7rem 1.1rem;\n }\n .primary { background: var(--accent); color: var(--accent-fg); width: 100%; }\n .primary:disabled { opacity: 0.6; cursor: default; }\n .ghost {\n background: transparent; color: var(--muted);\n border: 1px solid var(--border); padding: 0.35rem 0.7rem;\n font-size: 0.78rem; font-weight: 500;\n }\n pre {\n background: #0c0c0e; border: 1px solid var(--border); border-radius: 8px;\n padding: 0.9rem; font-size: 0.78rem; line-height: 1.5; overflow-x: auto;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n color: #d4d4d8; margin: 0.5rem 0 1rem;\n }\n .field { margin-bottom: 1rem; }\n .field label { display: block; font-size: 0.8rem; color: var(--muted);\n margin-bottom: 0.35rem; }\n .field input {\n width: 100%; padding: 0.55rem 0.7rem; font: inherit; color: var(--text);\n background: #0c0c0e; border: 1px solid var(--border); border-radius: 6px;\n }\n .inline { display: flex; gap: 0.5rem; }\n .inline input { flex: 1; }\n .tokens { margin-top: 1.75rem; border-top: 1px solid var(--border);\n padding-top: 1.25rem; }\n .tokens h2 { font-size: 0.95rem; font-weight: 600; margin-bottom: 0.75rem; }\n .tok { display: flex; align-items: center; justify-content: space-between;\n gap: 0.75rem; padding: 0.55rem 0; border-bottom: 1px solid var(--border);\n font-size: 0.83rem; }\n .tok:last-child { border-bottom: none; }\n .tok .meta { color: var(--subtle); font-size: 0.74rem; }\n .tok.revoked { opacity: 0.45; }\n .msg { font-size: 0.83rem; padding: 0.6rem 0.8rem; border-radius: 6px;\n margin-bottom: 1rem; display: none; }\n .msg.err { display: block; color: var(--error); background: var(--error-bg); }\n .msg.ok { display: block; color: var(--ok); background: var(--ok-bg); }\n .hidden { display: none !important; }\n</style>\n</head>\n<body>\n<div class=\"card\">\n <h1>Connect an external agent</h1>\n <div class=\"sub\">Mint a personal token for <strong>${safeApp}</strong> so a coding agent (Claude Code, Codex, Cowork) can act as you.</div>\n <div class=\"row\">Signed in as ${safeEmail} &middot; ${safeOrigin}</div>\n\n <div id=\"codeCallout\" class=\"code-callout ${safeUserCode ? \"\" : \"hidden\"}\">\n <div class=\"label\">Authorizing device code</div>\n <div class=\"value\" id=\"userCodeValue\">${safeUserCode}</div>\n </div>\n\n <div id=\"msg\" class=\"msg\"></div>\n\n <div id=\"mintForm\">\n <div class=\"field\">\n <label for=\"label\">Label (optional)</label>\n <input id=\"label\" type=\"text\" placeholder=\"e.g. Claude Code on my laptop\" maxlength=\"120\" />\n </div>\n <div class=\"field\">\n <label for=\"ttl\">Expires in (days, 1–365)</label>\n <input id=\"ttl\" type=\"number\" min=\"1\" max=\"365\" value=\"${DEFAULT_TOKEN_TTL_DAYS}\" />\n </div>\n <button id=\"authorizeBtn\" class=\"primary\">${safeUserCode ? \"Authorize device\" : \"Create connection token\"}</button>\n </div>\n\n <div id=\"result\" class=\"hidden\">\n <p class=\"sub\" id=\"resultMsg\">Token created. Paste this into your agent's MCP config:</p>\n <pre id=\"mcpJson\"></pre>\n <p class=\"sub\">Or from a terminal:</p>\n <pre id=\"cliLine\"></pre>\n </div>\n\n <div class=\"tokens\">\n <h2>Your connections</h2>\n <div id=\"tokenList\"><div class=\"meta\">Loading…</div></div>\n </div>\n</div>\n<script>\n(function () {\n var BASE = ${JSON.stringify(joinAppPath(connectBasePath, \"/_agent-native/mcp/connect\"))};\n var USER_CODE = ${JSON.stringify(safeUserCode || null)};\n var msgEl = document.getElementById(\"msg\");\n function showMsg(text, kind) {\n msgEl.textContent = text;\n msgEl.className = \"msg \" + (kind || \"err\");\n }\n function clearMsg() { msgEl.className = \"msg\"; msgEl.textContent = \"\"; }\n\n function renderResult(data) {\n document.getElementById(\"mintForm\").classList.add(\"hidden\");\n var entry = {};\n entry[data.serverName] = data.mcpServerEntry;\n document.getElementById(\"mcpJson\").textContent =\n JSON.stringify({ mcpServers: entry }, null, 2);\n document.getElementById(\"cliLine\").textContent = data.cli;\n document.getElementById(\"result\").classList.remove(\"hidden\");\n }\n\n async function postJson(path, body) {\n var res = await fetch(BASE + path, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n credentials: \"same-origin\",\n body: JSON.stringify(body || {})\n });\n var data = null;\n try { data = await res.json(); } catch (e) {}\n return { ok: res.ok, status: res.status, data: data };\n }\n\n async function loadTokens() {\n var listEl = document.getElementById(\"tokenList\");\n try {\n var res = await fetch(BASE + \"/tokens\", { credentials: \"same-origin\" });\n if (!res.ok) { listEl.innerHTML = '<div class=\"meta\">Could not load.</div>'; return; }\n var data = await res.json();\n var tokens = (data && data.tokens) || [];\n if (!tokens.length) { listEl.innerHTML = '<div class=\"meta\">No connections yet.</div>'; return; }\n listEl.innerHTML = \"\";\n tokens.forEach(function (t) {\n var div = document.createElement(\"div\");\n div.className = \"tok\" + (t.revokedAt ? \" revoked\" : \"\");\n var when = t.createdAt ? new Date(t.createdAt).toLocaleString() : \"\";\n var used = t.lastUsedAt ? \" · last used \" + new Date(t.lastUsedAt).toLocaleString() : \"\";\n var left = document.createElement(\"div\");\n var label = document.createElement(\"div\");\n label.textContent = t.label || \"(unlabeled)\";\n var meta = document.createElement(\"div\");\n meta.className = \"meta\";\n meta.textContent = (t.revokedAt ? \"Revoked · \" : \"Created \") + when + used;\n left.appendChild(label); left.appendChild(meta);\n div.appendChild(left);\n if (!t.revokedAt) {\n var btn = document.createElement(\"button\");\n btn.className = \"ghost\";\n btn.textContent = \"Revoke\";\n btn.onclick = async function () {\n btn.disabled = true;\n var r = await postJson(\"/tokens/revoke\", { id: t.id });\n if (r.ok) { loadTokens(); }\n else { btn.disabled = false; showMsg(\"Could not revoke token.\"); }\n };\n div.appendChild(btn);\n }\n listEl.appendChild(div);\n });\n } catch (e) {\n listEl.innerHTML = '<div class=\"meta\">Could not load.</div>';\n }\n }\n\n document.getElementById(\"authorizeBtn\").onclick = async function () {\n var btn = this;\n btn.disabled = true;\n clearMsg();\n var label = document.getElementById(\"label\").value || undefined;\n var ttlDays = parseInt(document.getElementById(\"ttl\").value, 10) || undefined;\n try {\n if (USER_CODE) {\n var a = await postJson(\"/device/authorize\", { user_code: USER_CODE });\n if (!a.ok) {\n btn.disabled = false;\n showMsg((a.data && a.data.error) || \"Could not authorize this device code.\");\n return;\n }\n showMsg(\"Device authorized. You can return to your terminal — it will connect automatically.\", \"ok\");\n btn.classList.add(\"hidden\");\n document.getElementById(\"mintForm\").classList.add(\"hidden\");\n } else {\n var m = await postJson(\"/token\", { label: label, ttlDays: ttlDays });\n if (!m.ok) {\n btn.disabled = false;\n showMsg((m.data && m.data.error) || \"Could not create token.\");\n return;\n }\n renderResult(m.data);\n }\n loadTokens();\n } catch (e) {\n btn.disabled = false;\n showMsg(\"Network error. Please try again.\");\n }\n };\n\n loadTokens();\n})();\n</script>\n</body>\n</html>`;\n}\n\n// ---------------------------------------------------------------------------\n// Handler — single entry point; core-routes-plugin dispatches the subpath.\n// ---------------------------------------------------------------------------\n\n/**\n * Handle a `/_agent-native/mcp/connect[...]` request. `subpath` is the part\n * after `/connect` (empty string = the page itself, otherwise e.g.\n * `/token`, `/device/start`). The core-routes-plugin computes it from the\n * stripped event path so this module stays mount-agnostic.\n */\nexport async function handleMcpConnect(\n event: H3Event,\n subpath: string,\n options: McpConnectRouteOptions = {},\n): Promise<Response> {\n const method = getMethod(event);\n const origin = deriveOrigin(event);\n const basePath = configuredBasePath();\n const appUrl = `${origin}${basePath}`;\n const sub = (\"/\" + subpath.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\")).replace(\n /^\\/$/,\n \"\",\n );\n\n // ---- The connect page (GET) ------------------------------------------\n if (sub === \"\") {\n if (method !== \"GET\" && method !== \"HEAD\") {\n return json({ error: \"Method not allowed\" }, 405);\n }\n const session = await getSession(event);\n if (!session?.email) {\n // Serve the SAME login form the guard would, at this same URL — the\n // login form reloads window.location so we re-enter here authed.\n const loginHtml = getConfiguredLoginHtml(event);\n if (loginHtml) return html(loginHtml, 200);\n // Fully-open app (no auth guard): nothing to scope a mint to.\n return html(\n renderConnectPage({\n origin: appUrl,\n connectBasePath: basePath,\n email: \"(no auth configured)\",\n appName: options.appName || appLabel(appUrl, options),\n userCode: null,\n }),\n );\n }\n let userCode: string | null = null;\n try {\n const u = new URL(\n event.node?.req?.url ?? event.path ?? \"/\",\n \"http://an.invalid\",\n );\n const raw = u.searchParams.get(\"user_code\");\n if (raw && USER_CODE_RE.test(raw)) userCode = raw;\n } catch {\n userCode = null;\n }\n return html(\n renderConnectPage({\n origin: appUrl,\n connectBasePath: basePath,\n email: session.email,\n appName: options.appName || appLabel(appUrl, options),\n userCode,\n }),\n );\n }\n\n // ---- POST /token (session-required) ---------------------------------\n if (sub === \"/token\") {\n if (method !== \"POST\") return json({ error: \"Method not allowed\" }, 405);\n const session = await getSession(event);\n if (!session?.email) return json({ error: \"Unauthorized\" }, 401);\n if (!process.env.A2A_SECRET) {\n return json(\n {\n error:\n \"This deployment has no A2A_SECRET configured, so connect tokens cannot be minted.\",\n },\n 503,\n );\n }\n const body = ((await readBody(event).catch(() => ({}))) ?? {}) as {\n label?: unknown;\n ttlDays?: unknown;\n };\n const label =\n typeof body.label === \"string\" && body.label.trim()\n ? body.label.trim().slice(0, 120)\n : null;\n const ttlDays = clampTtlDays(body.ttlDays);\n try {\n const { token } = await mintConnectToken({\n email: session.email,\n orgId: session.orgId,\n label,\n ttlDays,\n });\n return json(mcpResultPayload(appUrl, token, options));\n } catch {\n return json({ error: \"Failed to mint token.\" }, 500);\n }\n }\n\n // ---- POST /device/start (UNAUTH) ------------------------------------\n if (sub === \"/device/start\") {\n if (method !== \"POST\") return json({ error: \"Method not allowed\" }, 405);\n try {\n const row = await createDeviceCode();\n const verificationUri = `${appUrl}/_agent-native/mcp/connect`;\n return json({\n device_code: row.deviceCode,\n user_code: row.userCode,\n verification_uri: verificationUri,\n verification_uri_complete: `${verificationUri}?user_code=${row.userCode}`,\n interval: DEVICE_POLL_INTERVAL_S,\n expires_in: Math.floor(DEVICE_CODE_TTL_MS / 1000),\n });\n } catch (err: any) {\n if (err?.message === \"RATE_LIMITED\") {\n return json({ error: \"Rate limited. Try again shortly.\" }, 429);\n }\n return json({ error: \"Could not start device flow.\" }, 500);\n }\n }\n\n // ---- POST /device/authorize (session-required) ----------------------\n if (sub === \"/device/authorize\") {\n if (method !== \"POST\") return json({ error: \"Method not allowed\" }, 405);\n const session = await getSession(event);\n if (!session?.email) return json({ error: \"Unauthorized\" }, 401);\n const body = ((await readBody(event).catch(() => ({}))) ?? {}) as {\n user_code?: unknown;\n };\n const userCode =\n typeof body.user_code === \"string\" ? body.user_code.trim() : \"\";\n if (!USER_CODE_RE.test(userCode)) {\n return json({ error: \"Invalid user code.\" }, 400);\n }\n const orgId =\n typeof session.orgId === \"string\" && session.orgId.trim()\n ? session.orgId.trim()\n : null;\n const result = await approveDeviceCode(userCode, session.email, orgId);\n if (result === \"not_found\") {\n return json({ error: \"Unknown device code.\" }, 404);\n }\n if (result === \"expired\") {\n return json({ error: \"This device code has expired.\" }, 410);\n }\n if (result === \"already\") {\n return json({ error: \"This device code was already used.\" }, 409);\n }\n return json({ status: \"approved\" });\n }\n\n // ---- POST /device/poll (UNAUTH) -------------------------------------\n if (sub === \"/device/poll\") {\n if (method !== \"POST\") return json({ error: \"Method not allowed\" }, 405);\n const body = ((await readBody(event).catch(() => ({}))) ?? {}) as {\n device_code?: unknown;\n };\n const deviceCode =\n typeof body.device_code === \"string\" ? body.device_code : \"\";\n if (!deviceCode) return json({ error: \"device_code required\" }, 400);\n const row = await getDeviceCode(deviceCode);\n if (!row) return json({ status: \"not_found\" }, 404);\n if (row.status === \"consumed\") return json({ status: \"consumed\" });\n if (\n row.status === \"expired\" ||\n (row.expiresAt != null && row.expiresAt < Date.now())\n ) {\n if (row.status !== \"expired\") void expireDeviceCode(deviceCode);\n return json({ status: \"expired\" });\n }\n if (\n row.status === \"pending\" ||\n row.status === \"minting\" ||\n !row.ownerEmail\n ) {\n return json({ status: \"pending\" });\n }\n // status === \"approved\" && ownerEmail bound → mint exactly once.\n if (!process.env.A2A_SECRET) {\n return json({ status: \"error\", error: \"A2A_SECRET not configured\" }, 503);\n }\n try {\n const jti = randomUUID();\n // Claim a retryable minting state first. If signing or recording fails,\n // release the row back to approved so the CLI can poll again.\n const claimed = await claimDeviceCodeForMint(deviceCode, jti);\n if (!claimed) {\n const fresh = await getDeviceCode(deviceCode);\n if (fresh?.status === \"consumed\") return json({ status: \"consumed\" });\n return json({ status: \"pending\" });\n }\n let token: string;\n try {\n const orgDomain = await resolveOrgDomain(claimed.orgId ?? undefined);\n token = await signA2AToken(claimed.ownerEmail!, orgDomain, undefined, {\n preferGlobalSecret: true,\n expiresIn: `${DEFAULT_TOKEN_TTL_DAYS}d`,\n extraClaims: { jti, scope: MCP_CONNECT_SCOPE },\n });\n await recordMintedToken({\n jti,\n ownerEmail: claimed.ownerEmail!,\n orgId: claimed.orgId,\n label: \"Device connection\",\n });\n if (!(await finishDeviceCodeMint(deviceCode, jti))) {\n return json({ status: \"pending\" });\n }\n } catch (err) {\n await releaseDeviceCodeMint(deviceCode, jti);\n throw err;\n }\n return json({\n status: \"approved\",\n ...mcpResultPayload(appUrl, token, options),\n });\n } catch {\n return json({ status: \"error\", error: \"Failed to mint token.\" }, 500);\n }\n }\n\n // ---- GET /tokens (session-required) ---------------------------------\n if (sub === \"/tokens\") {\n if (method !== \"GET\") return json({ error: \"Method not allowed\" }, 405);\n const session = await getSession(event);\n if (!session?.email) return json({ error: \"Unauthorized\" }, 401);\n const rows = await listTokens(session.email);\n return json({\n tokens: rows.map((r) => ({\n id: r.id,\n label: r.label,\n createdAt: r.createdAt,\n lastUsedAt: r.lastUsedAt,\n revokedAt: r.revokedAt,\n })),\n });\n }\n\n // ---- POST /tokens/revoke (session-required) -------------------------\n if (sub === \"/tokens/revoke\") {\n if (method !== \"POST\") return json({ error: \"Method not allowed\" }, 405);\n const session = await getSession(event);\n if (!session?.email) return json({ error: \"Unauthorized\" }, 401);\n const body = ((await readBody(event).catch(() => ({}))) ?? {}) as {\n id?: unknown;\n };\n const id = typeof body.id === \"string\" ? body.id : \"\";\n if (!id) return json({ error: \"id required\" }, 400);\n const revoked = await revokeToken(session.email, id);\n return json({ ok: revoked });\n }\n\n return json({ error: \"Not found\" }, 404);\n}\n"]}
1
+ {"version":3,"file":"connect-route.js","sourceRoot":"","sources":["../../src/mcp/connect-route.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `/_agent-native/mcp/connect` — frictionless external-agent connection.\n *\n * A logged-in user on a deployed agent-native app (e.g. mail.agent-native.com)\n * mints a per-user, scoped, revocable MCP bearer token WITHOUT ever copying a\n * shared deployment secret. Two surfaces:\n *\n * 1. Browser — `GET /connect` renders a minimal in-app page (same inline\n * HTML approach as the auth pages). The Authorize button POSTs to\n * `/connect/token`, then shows the ready-to-paste `.mcp.json` entry, the\n * `agent-native connect <origin>` one-liner, and the user's existing\n * tokens with Revoke buttons.\n * 2. CLI — an OAuth-2.0-device-authorization-style flow:\n * POST /connect/device/start (unauth) → device_code + user_code\n * GET /connect?user_code=… (browser) → user signs in & approves\n * POST /connect/device/authorize (session) → binds user to the code\n * POST /connect/device/poll (unauth) → mints + returns the token\n *\n * The minted token reuses the existing A2A signer (`signA2AToken`) — no new\n * crypto. We only add a random `jti` + `scope: \"mcp-connect\"` claim so it can\n * be revoked. `verifyAuth` already verifies A2A_SECRET JWTs and extracts\n * `sub`/`org_domain`, so a minted token works against `/_agent-native/mcp`\n * with no verify changes for the happy path (the revoke check is the only\n * addition there).\n *\n * Node-only (crypto + the A2A signer), bundled alongside the other framework\n * routes. Dialect-agnostic SQL lives in `connect-store.ts`.\n */\n\nimport type { H3Event } from \"h3\";\nimport { getMethod, getHeader } from \"h3\";\nimport { readBody } from \"../server/h3-helpers.js\";\nimport { getSession, getConfiguredLoginHtml } from \"../server/auth.js\";\nimport { signA2AToken } from \"../a2a/client.js\";\nimport { getOrgDomain } from \"../org/context.js\";\nimport { randomUUID } from \"node:crypto\";\nimport {\n recordMintedToken,\n listTokens,\n revokeToken,\n createDeviceCode,\n getDeviceCode,\n approveDeviceCode,\n claimDeviceCodeForMint,\n finishDeviceCodeMint,\n releaseDeviceCodeMint,\n expireDeviceCode,\n MCP_CONNECT_SCOPE,\n DEFAULT_TOKEN_TTL_DAYS,\n MIN_TOKEN_TTL_DAYS,\n MAX_TOKEN_TTL_DAYS,\n DEVICE_CODE_TTL_MS,\n} from \"./connect-store.js\";\n\n/** Device-flow poll interval hint (seconds). */\nconst DEVICE_POLL_INTERVAL_S = 3;\n\n// Human-typable user code: 8 base32 chars, dashed XXXX-XXXX.\nconst USER_CODE_RE = /^[A-Z2-7]{4}-[A-Z2-7]{4}$/;\n\nexport interface McpConnectRouteOptions {\n /** App id (directory under apps/, e.g. `mail`). Used for the server name. */\n appId?: string;\n /** Human app name shown on the connect page. */\n appName?: string;\n}\n\nfunction json(body: unknown, status = 200): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { \"Content-Type\": \"application/json\" },\n });\n}\n\nfunction html(body: string, status = 200): Response {\n return new Response(body, {\n status,\n headers: { \"Content-Type\": \"text/html; charset=utf-8\" },\n });\n}\n\n/** Derive the running app's origin from request headers (same logic mountMCP\n * uses) — `https` in prod / for non-loopback hosts, `http` for localhost. */\nfunction deriveOrigin(event: H3Event): string {\n const forwardedProto = getHeader(event, \"x-forwarded-proto\");\n const host = getHeader(event, \"x-forwarded-host\") || getHeader(event, \"host\");\n const proto =\n forwardedProto?.split(\",\")[0]?.trim() ||\n (host && /^(localhost|127\\.0\\.0\\.1)(:|$)/.test(host) ? \"http\" : \"https\");\n return host ? `${proto}://${host}` : \"\";\n}\n\nfunction normalizeBasePath(raw: string | undefined): string {\n const trimmed = (raw ?? \"\").trim();\n if (!trimmed || trimmed === \"/\") return \"\";\n const withSlash = trimmed.startsWith(\"/\") ? trimmed : `/${trimmed}`;\n return withSlash.replace(/\\/+$/, \"\");\n}\n\nfunction configuredBasePath(): string {\n return normalizeBasePath(\n process.env.APP_BASE_PATH || process.env.VITE_APP_BASE_PATH,\n );\n}\n\nfunction joinAppPath(basePath: string, path: string): string {\n if (!basePath) return path;\n if (path === \"/\") return basePath;\n return `${basePath}${path.startsWith(\"/\") ? path : `/${path}`}`;\n}\n\nfunction appLabel(origin: string, options: McpConnectRouteOptions): string {\n if (options.appId) return options.appId;\n try {\n const h = new URL(origin).hostname;\n return h.split(\".\")[0] || h;\n } catch {\n return options.appName || \"app\";\n }\n}\n\nfunction serverName(origin: string, options: McpConnectRouteOptions): string {\n return `agent-native-${appLabel(origin, options)}`;\n}\n\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\");\n}\n\n/**\n * Resolve the org domain for a session. Used as the JWT `org_domain` claim so\n * the receiving MCP endpoint can map it back to an org id (same as A2A). Best\n * effort — a missing org just yields a user-scoped (no-org) token.\n */\nasync function resolveOrgDomain(\n orgId: string | undefined,\n): Promise<string | undefined> {\n if (!orgId) return undefined;\n try {\n return (await getOrgDomain(orgId)) ?? undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction clampTtlDays(input: unknown): number {\n const n = Number(input);\n if (!Number.isFinite(n)) return DEFAULT_TOKEN_TTL_DAYS;\n return Math.min(\n MAX_TOKEN_TTL_DAYS,\n Math.max(MIN_TOKEN_TTL_DAYS, Math.floor(n)),\n );\n}\n\n/**\n * Mint a connect-scoped JWT and record it. The JWT is signed by the existing\n * A2A signer (HS256 over A2A_SECRET); we add a random `jti` and\n * `scope: \"mcp-connect\"` so the token is individually revocable. The token\n * value is returned to the caller exactly once and never persisted.\n */\nasync function mintConnectToken(params: {\n email: string;\n orgId: string | undefined;\n label: string | null;\n ttlDays: number;\n}): Promise<{ token: string; jti: string }> {\n const orgDomain = await resolveOrgDomain(params.orgId);\n const jti = randomUUID();\n // signA2AToken signs { sub: email, org_domain? } over A2A_SECRET (global)\n // or the org secret. We extend its claims via the standard jose builder by\n // re-using the same signer with extra claims threaded through `options`.\n const token = await signA2AToken(params.email, orgDomain, undefined, {\n preferGlobalSecret: true,\n expiresIn: `${params.ttlDays}d`,\n extraClaims: { jti, scope: MCP_CONNECT_SCOPE },\n });\n await recordMintedToken({\n jti,\n ownerEmail: params.email,\n orgId: params.orgId ?? null,\n label: params.label,\n });\n return { token, jti };\n}\n\nfunction mcpResultPayload(\n appUrl: string,\n token: string,\n options: McpConnectRouteOptions,\n) {\n const mcpUrl = `${appUrl}/_agent-native/mcp`;\n const name = serverName(appUrl, options);\n return {\n token,\n mcpUrl,\n serverName: name,\n mcpServerEntry: {\n type: \"http\" as const,\n url: mcpUrl,\n headers: { Authorization: `Bearer ${token}` },\n },\n cli: `agent-native connect ${appUrl}`,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Connect page (server-rendered HTML string)\n// ---------------------------------------------------------------------------\n\nfunction renderConnectPage(params: {\n origin: string;\n connectBasePath: string;\n email: string;\n appName: string;\n userCode: string | null;\n}): string {\n const { origin, connectBasePath, email, appName, userCode } = params;\n const safeOrigin = escapeHtml(origin);\n const safeEmail = escapeHtml(email);\n const safeApp = escapeHtml(appName);\n const safeUserCode =\n userCode && USER_CODE_RE.test(userCode) ? escapeHtml(userCode) : \"\";\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Connect ${safeApp}</title>\n<style>\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n :root {\n color-scheme: dark;\n --bg: #08080a; --panel: #131316; --panel-2: #0d0d10;\n --border: rgba(255,255,255,0.08); --border-strong: rgba(255,255,255,0.14);\n --text: #fafafa; --muted: #a1a1aa; --subtle: #71717a;\n --accent: #fafafa; --accent-fg: #09090b;\n --ring: rgba(250,250,250,0.55);\n --error: #fca5a5; --error-bg: rgba(127,29,29,0.18);\n --ok: #86efac; --ok-bg: rgba(20,83,45,0.2);\n }\n html, body { -webkit-font-smoothing: antialiased; }\n body {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n background:\n radial-gradient(900px 540px at 50% -14%, rgba(255,255,255,0.05), transparent 60%),\n linear-gradient(180deg, #121215 0%, var(--bg) 56%);\n color: var(--text); display: flex; align-items: center;\n justify-content: center; min-height: 100vh; padding: 1.5rem 1rem;\n }\n .card {\n width: 100%; max-width: 420px;\n background: var(--panel); border: 1px solid var(--border);\n border-radius: 18px; box-shadow: 0 1px 0 rgba(255,255,255,0.04) inset,\n 0 30px 90px rgba(0,0,0,0.5);\n padding: 2.25rem 2rem 1.75rem;\n }\n /* App-to-app glyph */\n .glyph {\n display: flex; align-items: center; justify-content: center;\n gap: 0; margin: 0 auto 1.5rem; width: fit-content;\n }\n .glyph .tile {\n width: 52px; height: 52px; border-radius: 14px;\n display: flex; align-items: center; justify-content: center;\n background: var(--panel-2); border: 1px solid var(--border-strong);\n color: var(--text); flex-shrink: 0;\n }\n .glyph .tile svg { display: block; }\n .glyph .conn {\n width: 38px; height: 2px; flex-shrink: 0;\n background-image: radial-gradient(circle, var(--subtle) 1px, transparent 1.4px);\n background-size: 7px 2px; background-repeat: repeat-x;\n background-position: center;\n }\n .eyebrow {\n text-align: center; font-size: 0.72rem; font-weight: 600;\n letter-spacing: 0.08em; text-transform: uppercase;\n color: var(--subtle); margin-bottom: 0.5rem;\n }\n h1 {\n text-align: center; font-size: 1.4rem; font-weight: 680;\n line-height: 1.25; margin-bottom: 0.55rem;\n letter-spacing: -0.01em;\n }\n .sub {\n text-align: center; color: var(--muted); font-size: 0.9rem;\n line-height: 1.5; margin: 0 auto 0.85rem; max-width: 34ch;\n }\n .identity {\n text-align: center; color: var(--subtle); font-size: 0.78rem;\n margin-bottom: 1.5rem;\n }\n .identity strong { color: var(--muted); font-weight: 600; }\n .code-callout {\n border: 1px solid var(--border-strong); border-radius: 12px;\n padding: 0.85rem 1rem; margin-bottom: 1.25rem;\n background: var(--panel-2); text-align: center;\n }\n .code-callout .label { font-size: 0.68rem; color: var(--subtle);\n text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 0.4rem; }\n .code-callout .value { font-size: 1.6rem; font-weight: 700;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n letter-spacing: 0.14em; color: var(--text); }\n button {\n cursor: pointer; font: inherit; font-weight: 600; border: none;\n border-radius: 10px; padding: 0.8rem 1.1rem;\n }\n button:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }\n .primary {\n background: var(--accent); color: var(--accent-fg); width: 100%;\n font-size: 0.95rem;\n }\n .primary:hover:not(:disabled) { background: #e4e4e7; }\n .primary:disabled { opacity: 0.55; cursor: default; }\n .ghost {\n background: transparent; color: var(--muted);\n border: 1px solid var(--border-strong); padding: 0.35rem 0.7rem;\n font-size: 0.78rem; font-weight: 500; border-radius: 8px;\n }\n .ghost:hover:not(:disabled) { color: var(--text); border-color: var(--subtle); }\n pre {\n background: var(--panel-2); border: 1px solid var(--border); border-radius: 10px;\n padding: 0.9rem; font-size: 0.78rem; line-height: 1.5; overflow-x: auto;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n color: #d4d4d8; margin: 0.5rem 0 1rem;\n }\n /* Advanced disclosure */\n .advanced { margin: 0 0 1rem; }\n .advanced > summary {\n list-style: none; cursor: pointer; user-select: none;\n display: flex; align-items: center; justify-content: center; gap: 0.35rem;\n color: var(--subtle); font-size: 0.8rem; font-weight: 500;\n padding: 0.5rem 0; text-align: center;\n }\n .advanced > summary::-webkit-details-marker { display: none; }\n .advanced > summary:hover { color: var(--muted); }\n .advanced > summary:focus-visible { outline: 2px solid var(--ring);\n outline-offset: 2px; border-radius: 6px; }\n .advanced > summary .chev {\n width: 14px; height: 14px; transition: transform 0.15s ease;\n }\n .advanced[open] > summary .chev { transform: rotate(180deg); }\n .advanced-body {\n padding: 0.85rem 0.1rem 0.25rem;\n }\n .field { margin-bottom: 0.9rem; }\n .field:last-child { margin-bottom: 0; }\n .field label { display: block; font-size: 0.78rem; color: var(--muted);\n margin-bottom: 0.35rem; }\n .field input {\n width: 100%; padding: 0.6rem 0.7rem; font: inherit; color: var(--text);\n background: var(--panel-2); border: 1px solid var(--border-strong);\n border-radius: 8px;\n }\n .field input:focus-visible {\n outline: none; border-color: var(--ring);\n box-shadow: 0 0 0 3px rgba(250,250,250,0.12);\n }\n .inline { display: flex; gap: 0.5rem; }\n .inline input { flex: 1; }\n .tokens { margin-top: 1.5rem; border-top: 1px solid var(--border);\n padding-top: 1.25rem; }\n .tokens h2 { font-size: 0.78rem; font-weight: 600; color: var(--muted);\n text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 0.6rem; }\n .tok { display: flex; align-items: center; justify-content: space-between;\n gap: 0.75rem; padding: 0.6rem 0; border-bottom: 1px solid var(--border);\n font-size: 0.83rem; }\n .tok:last-child { border-bottom: none; }\n .tok .meta { color: var(--subtle); font-size: 0.74rem; margin-top: 0.1rem; }\n .tok.revoked { opacity: 0.45; }\n .msg { font-size: 0.83rem; padding: 0.6rem 0.8rem; border-radius: 8px;\n margin-bottom: 1rem; display: none; }\n .msg.err { display: block; color: var(--error); background: var(--error-bg); }\n .msg.ok { display: block; color: var(--ok); background: var(--ok-bg); }\n .hidden { display: none !important; }\n</style>\n</head>\n<body>\n<div class=\"card\">\n <!-- \"Connect an external agent\" — kept as an accessible label / consent eyebrow -->\n <div class=\"glyph\" role=\"img\" aria-label=\"Connect an external agent to ${safeApp}\">\n <span class=\"tile\" aria-hidden=\"true\">\n <svg width=\"22\" height=\"22\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"3\" y=\"3\" width=\"8\" height=\"8\" rx=\"2.2\" fill=\"currentColor\"/>\n <rect x=\"13\" y=\"3\" width=\"8\" height=\"8\" rx=\"2.2\" fill=\"currentColor\" opacity=\"0.55\"/>\n <rect x=\"3\" y=\"13\" width=\"8\" height=\"8\" rx=\"2.2\" fill=\"currentColor\" opacity=\"0.55\"/>\n <rect x=\"13\" y=\"13\" width=\"8\" height=\"8\" rx=\"2.2\" fill=\"currentColor\"/>\n </svg>\n </span>\n <span class=\"conn\" aria-hidden=\"true\"></span>\n <span class=\"tile\" aria-hidden=\"true\">\n <svg width=\"22\" height=\"22\" viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\">\n <path d=\"M9 8 L5 12 L9 16\"/>\n <path d=\"M15 8 L19 12 L15 16\"/>\n </svg>\n </span>\n </div>\n\n <div class=\"eyebrow\">Connect an external agent</div>\n <h1>Authorize ${safeApp}?</h1>\n <p class=\"sub\">Mint a personal token so a coding agent (Claude Code, Codex, Cowork) can act as you on ${safeApp}.</p>\n <p class=\"identity\">Signed in as <strong>${safeEmail}</strong> &middot; ${safeOrigin}</p>\n\n <div id=\"codeCallout\" class=\"code-callout ${safeUserCode ? \"\" : \"hidden\"}\">\n <div class=\"label\">Authorizing device code</div>\n <div class=\"value\" id=\"userCodeValue\">${safeUserCode}</div>\n </div>\n\n <div id=\"msg\" class=\"msg\"></div>\n\n <div id=\"mintForm\">\n <button id=\"authorizeBtn\" class=\"primary\">${safeUserCode ? \"Authorize device\" : \"Create connection token\"}</button>\n <details class=\"advanced\">\n <summary>\n Advanced options\n <svg class=\"chev\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\n aria-hidden=\"true\"><path d=\"M6 9l6 6 6-6\"/></svg>\n </summary>\n <div class=\"advanced-body\">\n <div class=\"field\">\n <label for=\"label\">Label (optional)</label>\n <input id=\"label\" type=\"text\" placeholder=\"e.g. Claude Code on my laptop\" maxlength=\"120\" />\n </div>\n <div class=\"field\">\n <label for=\"ttl\">Expires in (days, 1–365)</label>\n <input id=\"ttl\" type=\"number\" min=\"1\" max=\"365\" value=\"${DEFAULT_TOKEN_TTL_DAYS}\" />\n </div>\n </div>\n </details>\n </div>\n\n <div id=\"result\" class=\"hidden\">\n <p class=\"sub\" id=\"resultMsg\">Token created. Paste this into your agent's MCP config:</p>\n <pre id=\"mcpJson\"></pre>\n <p class=\"sub\">Or from a terminal:</p>\n <pre id=\"cliLine\"></pre>\n </div>\n\n <div class=\"tokens\">\n <h2>Your connections</h2>\n <div id=\"tokenList\"><div class=\"meta\">Loading…</div></div>\n </div>\n</div>\n<script>\n(function () {\n var BASE = ${JSON.stringify(joinAppPath(connectBasePath, \"/_agent-native/mcp/connect\"))};\n var USER_CODE = ${JSON.stringify(safeUserCode || null)};\n var msgEl = document.getElementById(\"msg\");\n function showMsg(text, kind) {\n msgEl.textContent = text;\n msgEl.className = \"msg \" + (kind || \"err\");\n }\n function clearMsg() { msgEl.className = \"msg\"; msgEl.textContent = \"\"; }\n\n function renderResult(data) {\n document.getElementById(\"mintForm\").classList.add(\"hidden\");\n var entry = {};\n entry[data.serverName] = data.mcpServerEntry;\n document.getElementById(\"mcpJson\").textContent =\n JSON.stringify({ mcpServers: entry }, null, 2);\n document.getElementById(\"cliLine\").textContent = data.cli;\n document.getElementById(\"result\").classList.remove(\"hidden\");\n }\n\n async function postJson(path, body) {\n var res = await fetch(BASE + path, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n credentials: \"same-origin\",\n body: JSON.stringify(body || {})\n });\n var data = null;\n try { data = await res.json(); } catch (e) {}\n return { ok: res.ok, status: res.status, data: data };\n }\n\n async function loadTokens() {\n var listEl = document.getElementById(\"tokenList\");\n try {\n var res = await fetch(BASE + \"/tokens\", { credentials: \"same-origin\" });\n if (!res.ok) { listEl.innerHTML = '<div class=\"meta\">Could not load.</div>'; return; }\n var data = await res.json();\n var tokens = (data && data.tokens) || [];\n if (!tokens.length) { listEl.innerHTML = '<div class=\"meta\">No connections yet.</div>'; return; }\n listEl.innerHTML = \"\";\n tokens.forEach(function (t) {\n var div = document.createElement(\"div\");\n div.className = \"tok\" + (t.revokedAt ? \" revoked\" : \"\");\n var when = t.createdAt ? new Date(t.createdAt).toLocaleString() : \"\";\n var used = t.lastUsedAt ? \" · last used \" + new Date(t.lastUsedAt).toLocaleString() : \"\";\n var left = document.createElement(\"div\");\n var label = document.createElement(\"div\");\n label.textContent = t.label || \"(unlabeled)\";\n var meta = document.createElement(\"div\");\n meta.className = \"meta\";\n meta.textContent = (t.revokedAt ? \"Revoked · \" : \"Created \") + when + used;\n left.appendChild(label); left.appendChild(meta);\n div.appendChild(left);\n if (!t.revokedAt) {\n var btn = document.createElement(\"button\");\n btn.className = \"ghost\";\n btn.textContent = \"Revoke\";\n btn.onclick = async function () {\n btn.disabled = true;\n var r = await postJson(\"/tokens/revoke\", { id: t.id });\n if (r.ok) { loadTokens(); }\n else { btn.disabled = false; showMsg(\"Could not revoke token.\"); }\n };\n div.appendChild(btn);\n }\n listEl.appendChild(div);\n });\n } catch (e) {\n listEl.innerHTML = '<div class=\"meta\">Could not load.</div>';\n }\n }\n\n document.getElementById(\"authorizeBtn\").onclick = async function () {\n var btn = this;\n btn.disabled = true;\n clearMsg();\n var label = document.getElementById(\"label\").value || undefined;\n var ttlDays = parseInt(document.getElementById(\"ttl\").value, 10) || undefined;\n try {\n if (USER_CODE) {\n var a = await postJson(\"/device/authorize\", { user_code: USER_CODE });\n if (!a.ok) {\n btn.disabled = false;\n showMsg((a.data && a.data.error) || \"Could not authorize this device code.\");\n return;\n }\n showMsg(\"Device authorized. You can return to your terminal — it will connect automatically.\", \"ok\");\n btn.classList.add(\"hidden\");\n document.getElementById(\"mintForm\").classList.add(\"hidden\");\n } else {\n var m = await postJson(\"/token\", { label: label, ttlDays: ttlDays });\n if (!m.ok) {\n btn.disabled = false;\n showMsg((m.data && m.data.error) || \"Could not create token.\");\n return;\n }\n renderResult(m.data);\n }\n loadTokens();\n } catch (e) {\n btn.disabled = false;\n showMsg(\"Network error. Please try again.\");\n }\n };\n\n loadTokens();\n})();\n</script>\n</body>\n</html>`;\n}\n\n// ---------------------------------------------------------------------------\n// Handler — single entry point; core-routes-plugin dispatches the subpath.\n// ---------------------------------------------------------------------------\n\n/**\n * Handle a `/_agent-native/mcp/connect[...]` request. `subpath` is the part\n * after `/connect` (empty string = the page itself, otherwise e.g.\n * `/token`, `/device/start`). The core-routes-plugin computes it from the\n * stripped event path so this module stays mount-agnostic.\n */\nexport async function handleMcpConnect(\n event: H3Event,\n subpath: string,\n options: McpConnectRouteOptions = {},\n): Promise<Response> {\n const method = getMethod(event);\n const origin = deriveOrigin(event);\n const basePath = configuredBasePath();\n const appUrl = `${origin}${basePath}`;\n const sub = (\"/\" + subpath.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\")).replace(\n /^\\/$/,\n \"\",\n );\n\n // ---- The connect page (GET) ------------------------------------------\n if (sub === \"\") {\n if (method !== \"GET\" && method !== \"HEAD\") {\n return json({ error: \"Method not allowed\" }, 405);\n }\n const session = await getSession(event);\n if (!session?.email) {\n // Serve the SAME login form the guard would, at this same URL — the\n // login form reloads window.location so we re-enter here authed.\n const loginHtml = getConfiguredLoginHtml(event);\n if (loginHtml) return html(loginHtml, 200);\n // Fully-open app (no auth guard): nothing to scope a mint to.\n return html(\n renderConnectPage({\n origin: appUrl,\n connectBasePath: basePath,\n email: \"(no auth configured)\",\n appName: options.appName || appLabel(appUrl, options),\n userCode: null,\n }),\n );\n }\n let userCode: string | null = null;\n try {\n const u = new URL(\n event.node?.req?.url ?? event.path ?? \"/\",\n \"http://an.invalid\",\n );\n const raw = u.searchParams.get(\"user_code\");\n if (raw && USER_CODE_RE.test(raw)) userCode = raw;\n } catch {\n userCode = null;\n }\n return html(\n renderConnectPage({\n origin: appUrl,\n connectBasePath: basePath,\n email: session.email,\n appName: options.appName || appLabel(appUrl, options),\n userCode,\n }),\n );\n }\n\n // ---- POST /token (session-required) ---------------------------------\n if (sub === \"/token\") {\n if (method !== \"POST\") return json({ error: \"Method not allowed\" }, 405);\n const session = await getSession(event);\n if (!session?.email) return json({ error: \"Unauthorized\" }, 401);\n if (!process.env.A2A_SECRET) {\n return json(\n {\n error:\n \"This deployment has no A2A_SECRET configured, so connect tokens cannot be minted.\",\n },\n 503,\n );\n }\n const body = ((await readBody(event).catch(() => ({}))) ?? {}) as {\n label?: unknown;\n ttlDays?: unknown;\n };\n const label =\n typeof body.label === \"string\" && body.label.trim()\n ? body.label.trim().slice(0, 120)\n : null;\n const ttlDays = clampTtlDays(body.ttlDays);\n try {\n const { token } = await mintConnectToken({\n email: session.email,\n orgId: session.orgId,\n label,\n ttlDays,\n });\n return json(mcpResultPayload(appUrl, token, options));\n } catch {\n return json({ error: \"Failed to mint token.\" }, 500);\n }\n }\n\n // ---- POST /device/start (UNAUTH) ------------------------------------\n if (sub === \"/device/start\") {\n if (method !== \"POST\") return json({ error: \"Method not allowed\" }, 405);\n try {\n const row = await createDeviceCode();\n const verificationUri = `${appUrl}/_agent-native/mcp/connect`;\n return json({\n device_code: row.deviceCode,\n user_code: row.userCode,\n verification_uri: verificationUri,\n verification_uri_complete: `${verificationUri}?user_code=${row.userCode}`,\n interval: DEVICE_POLL_INTERVAL_S,\n expires_in: Math.floor(DEVICE_CODE_TTL_MS / 1000),\n });\n } catch (err: any) {\n if (err?.message === \"RATE_LIMITED\") {\n return json({ error: \"Rate limited. Try again shortly.\" }, 429);\n }\n return json({ error: \"Could not start device flow.\" }, 500);\n }\n }\n\n // ---- POST /device/authorize (session-required) ----------------------\n if (sub === \"/device/authorize\") {\n if (method !== \"POST\") return json({ error: \"Method not allowed\" }, 405);\n const session = await getSession(event);\n if (!session?.email) return json({ error: \"Unauthorized\" }, 401);\n const body = ((await readBody(event).catch(() => ({}))) ?? {}) as {\n user_code?: unknown;\n };\n const userCode =\n typeof body.user_code === \"string\" ? body.user_code.trim() : \"\";\n if (!USER_CODE_RE.test(userCode)) {\n return json({ error: \"Invalid user code.\" }, 400);\n }\n const orgId =\n typeof session.orgId === \"string\" && session.orgId.trim()\n ? session.orgId.trim()\n : null;\n const result = await approveDeviceCode(userCode, session.email, orgId);\n if (result === \"not_found\") {\n return json({ error: \"Unknown device code.\" }, 404);\n }\n if (result === \"expired\") {\n return json({ error: \"This device code has expired.\" }, 410);\n }\n if (result === \"already\") {\n return json({ error: \"This device code was already used.\" }, 409);\n }\n return json({ status: \"approved\" });\n }\n\n // ---- POST /device/poll (UNAUTH) -------------------------------------\n if (sub === \"/device/poll\") {\n if (method !== \"POST\") return json({ error: \"Method not allowed\" }, 405);\n const body = ((await readBody(event).catch(() => ({}))) ?? {}) as {\n device_code?: unknown;\n };\n const deviceCode =\n typeof body.device_code === \"string\" ? body.device_code : \"\";\n if (!deviceCode) return json({ error: \"device_code required\" }, 400);\n const row = await getDeviceCode(deviceCode);\n if (!row) return json({ status: \"not_found\" }, 404);\n if (row.status === \"consumed\") return json({ status: \"consumed\" });\n if (\n row.status === \"expired\" ||\n (row.expiresAt != null && row.expiresAt < Date.now())\n ) {\n if (row.status !== \"expired\") void expireDeviceCode(deviceCode);\n return json({ status: \"expired\" });\n }\n if (\n row.status === \"pending\" ||\n row.status === \"minting\" ||\n !row.ownerEmail\n ) {\n return json({ status: \"pending\" });\n }\n // status === \"approved\" && ownerEmail bound → mint exactly once.\n if (!process.env.A2A_SECRET) {\n return json({ status: \"error\", error: \"A2A_SECRET not configured\" }, 503);\n }\n try {\n const jti = randomUUID();\n // Claim a retryable minting state first. If signing or recording fails,\n // release the row back to approved so the CLI can poll again.\n const claimed = await claimDeviceCodeForMint(deviceCode, jti);\n if (!claimed) {\n const fresh = await getDeviceCode(deviceCode);\n if (fresh?.status === \"consumed\") return json({ status: \"consumed\" });\n return json({ status: \"pending\" });\n }\n let token: string;\n try {\n const orgDomain = await resolveOrgDomain(claimed.orgId ?? undefined);\n token = await signA2AToken(claimed.ownerEmail!, orgDomain, undefined, {\n preferGlobalSecret: true,\n expiresIn: `${DEFAULT_TOKEN_TTL_DAYS}d`,\n extraClaims: { jti, scope: MCP_CONNECT_SCOPE },\n });\n await recordMintedToken({\n jti,\n ownerEmail: claimed.ownerEmail!,\n orgId: claimed.orgId,\n label: \"Device connection\",\n });\n if (!(await finishDeviceCodeMint(deviceCode, jti))) {\n return json({ status: \"pending\" });\n }\n } catch (err) {\n await releaseDeviceCodeMint(deviceCode, jti);\n throw err;\n }\n return json({\n status: \"approved\",\n ...mcpResultPayload(appUrl, token, options),\n });\n } catch {\n return json({ status: \"error\", error: \"Failed to mint token.\" }, 500);\n }\n }\n\n // ---- GET /tokens (session-required) ---------------------------------\n if (sub === \"/tokens\") {\n if (method !== \"GET\") return json({ error: \"Method not allowed\" }, 405);\n const session = await getSession(event);\n if (!session?.email) return json({ error: \"Unauthorized\" }, 401);\n const rows = await listTokens(session.email);\n return json({\n tokens: rows.map((r) => ({\n id: r.id,\n label: r.label,\n createdAt: r.createdAt,\n lastUsedAt: r.lastUsedAt,\n revokedAt: r.revokedAt,\n })),\n });\n }\n\n // ---- POST /tokens/revoke (session-required) -------------------------\n if (sub === \"/tokens/revoke\") {\n if (method !== \"POST\") return json({ error: \"Method not allowed\" }, 405);\n const session = await getSession(event);\n if (!session?.email) return json({ error: \"Unauthorized\" }, 401);\n const body = ((await readBody(event).catch(() => ({}))) ?? {}) as {\n id?: unknown;\n };\n const id = typeof body.id === \"string\" ? body.id : \"\";\n if (!id) return json({ error: \"id required\" }, 400);\n const revoked = await revokeToken(session.email, id);\n return json({ ok: revoked });\n }\n\n return json({ error: \"Not found\" }, 404);\n}\n"]}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Org-directory discovery for the generic cross-app MCP verbs
3
+ * (`list_apps` / `ask_app` in `builtin-tools.ts`).
4
+ *
5
+ * Phase 3b of cross-app auto-wiring. Today the cross-app verbs resolve sibling
6
+ * apps from *local workspace* info only (`workspace-resolve.ts`), so the mail
7
+ * agent can only reach the calendar agent in a local dev workspace. When the
8
+ * deployment runs against an org directory (Dispatch is also the identity hub
9
+ * for the org), this module discovers the org's *deployed* sibling apps so the
10
+ * same verbs work cross-app in production with ZERO manual setup.
11
+ *
12
+ * ## The directory request
13
+ *
14
+ * GET <directoryOrigin>/_agent-native/org/apps
15
+ * Auth Authorization: Bearer <org A2A token> (same signed token A2A peers
16
+ * already mint — reuses `resolveA2ACallerAuth()`; the org A2A secret /
17
+ * global `A2A_SECRET` is loaded exactly how outgoing A2A calls load it)
18
+ * ⇒ { org, apps: [ { id, name, url, a2aUrl, capabilities? } ] }
19
+ * (allow-listed first-party apps only, prod URLs — enforced by the
20
+ * authority side, Phase 3a, on Dispatch)
21
+ *
22
+ * ## Resolution + safety model
23
+ *
24
+ * - The directory origin is read from env: `AGENT_NATIVE_ORG_DIRECTORY_URL`
25
+ * (dedicated) or `AGENT_NATIVE_IDENTITY_HUB_URL` (Dispatch is also the
26
+ * identity hub). When *neither* is set the feature is simply inactive —
27
+ * `fetchOrgApps()` returns `[]` and nothing changes anywhere (asserted by
28
+ * a test). This makes the whole feature opt-in and back-compat.
29
+ * - On ANY error (no env, unreachable, 401, non-2xx, bad JSON, no signed
30
+ * token) `fetchOrgApps()` returns `[]` and NEVER throws — the cross-app
31
+ * verbs degrade silently to their exact current local-only behavior.
32
+ * - A short in-memory TTL cache (default 60s) keyed by directory origin and
33
+ * caller identity/org scope so sibling app lists never cross tenants.
34
+ * Empty authenticated results are cached too (with a shorter TTL) so a
35
+ * transient failure doesn't hammer the directory on every call.
36
+ * - No secrets are ever logged.
37
+ *
38
+ * Bundled alongside `mountMCP` (no Node-only top-level imports). The A2A
39
+ * caller-auth + a2a client are dynamically imported inside `fetchOrgApps()`.
40
+ */
41
+ export interface OrgApp {
42
+ /** Canonical app id, e.g. `calendar`. */
43
+ id: string;
44
+ /** Human-readable name, e.g. `Calendar`. */
45
+ name: string;
46
+ /** Deployed app origin/URL, e.g. `https://calendar.acme.com`. */
47
+ url: string;
48
+ /**
49
+ * A2A endpoint to route `ask_app` to. The authority side returns this; we
50
+ * fall back to the app `url` (the A2A client appends `/_agent-native/a2a`).
51
+ */
52
+ a2aUrl: string;
53
+ /** Optional capability hints the authority side may include. */
54
+ capabilities?: string[];
55
+ }
56
+ /**
57
+ * Resolve the org-directory origin from env. Returns `null` when neither env
58
+ * var is set — the caller treats `null` as "feature inactive".
59
+ *
60
+ * `env` is injectable for tests; defaults to `process.env`.
61
+ */
62
+ export declare function resolveOrgDirectoryOrigin(env?: NodeJS.ProcessEnv): string | null;
63
+ /**
64
+ * Fetch the org's first-party sibling apps from the org directory.
65
+ *
66
+ * - Returns `[]` (never throws) on ANY failure or when the directory env is
67
+ * unset — the cross-app verbs then keep their exact local-only behavior.
68
+ * - Short in-memory TTL cache so it isn't fetched on every tool call.
69
+ * - Strips the current app from the result (compared by id and by origin) so
70
+ * `list_apps` / `ask_app` never offer to route to themselves.
71
+ *
72
+ * @param opts.selfId Current app id (so it's stripped from the result).
73
+ * @param opts.selfOrigin Current app origin (so it's stripped by origin too).
74
+ * @param opts.env Injectable env (tests). Defaults to `process.env`.
75
+ */
76
+ export declare function fetchOrgApps(opts?: {
77
+ selfId?: string;
78
+ selfOrigin?: string;
79
+ env?: NodeJS.ProcessEnv;
80
+ }): Promise<OrgApp[]>;
81
+ /** Test-only: clear the in-memory cache between cases. */
82
+ export declare function _resetOrgDirectoryCache(): void;
83
+ //# sourceMappingURL=org-directory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"org-directory.d.ts","sourceRoot":"","sources":["../../src/mcp/org-directory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,MAAM,WAAW,MAAM;IACrB,yCAAyC;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,GAAG,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAeD;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,GAAG,IAAI,CAcf;AAwDD;;;;;;;;;;;;GAYG;AACH,wBAAsB,YAAY,CAAC,IAAI,CAAC,EAAE;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAmEpB;AAED,0DAA0D;AAC1D,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C"}
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Org-directory discovery for the generic cross-app MCP verbs
3
+ * (`list_apps` / `ask_app` in `builtin-tools.ts`).
4
+ *
5
+ * Phase 3b of cross-app auto-wiring. Today the cross-app verbs resolve sibling
6
+ * apps from *local workspace* info only (`workspace-resolve.ts`), so the mail
7
+ * agent can only reach the calendar agent in a local dev workspace. When the
8
+ * deployment runs against an org directory (Dispatch is also the identity hub
9
+ * for the org), this module discovers the org's *deployed* sibling apps so the
10
+ * same verbs work cross-app in production with ZERO manual setup.
11
+ *
12
+ * ## The directory request
13
+ *
14
+ * GET <directoryOrigin>/_agent-native/org/apps
15
+ * Auth Authorization: Bearer <org A2A token> (same signed token A2A peers
16
+ * already mint — reuses `resolveA2ACallerAuth()`; the org A2A secret /
17
+ * global `A2A_SECRET` is loaded exactly how outgoing A2A calls load it)
18
+ * ⇒ { org, apps: [ { id, name, url, a2aUrl, capabilities? } ] }
19
+ * (allow-listed first-party apps only, prod URLs — enforced by the
20
+ * authority side, Phase 3a, on Dispatch)
21
+ *
22
+ * ## Resolution + safety model
23
+ *
24
+ * - The directory origin is read from env: `AGENT_NATIVE_ORG_DIRECTORY_URL`
25
+ * (dedicated) or `AGENT_NATIVE_IDENTITY_HUB_URL` (Dispatch is also the
26
+ * identity hub). When *neither* is set the feature is simply inactive —
27
+ * `fetchOrgApps()` returns `[]` and nothing changes anywhere (asserted by
28
+ * a test). This makes the whole feature opt-in and back-compat.
29
+ * - On ANY error (no env, unreachable, 401, non-2xx, bad JSON, no signed
30
+ * token) `fetchOrgApps()` returns `[]` and NEVER throws — the cross-app
31
+ * verbs degrade silently to their exact current local-only behavior.
32
+ * - A short in-memory TTL cache (default 60s) keyed by directory origin and
33
+ * caller identity/org scope so sibling app lists never cross tenants.
34
+ * Empty authenticated results are cached too (with a shorter TTL) so a
35
+ * transient failure doesn't hammer the directory on every call.
36
+ * - No secrets are ever logged.
37
+ *
38
+ * Bundled alongside `mountMCP` (no Node-only top-level imports). The A2A
39
+ * caller-auth + a2a client are dynamically imported inside `fetchOrgApps()`.
40
+ */
41
+ /** Default cache TTL for a successful directory fetch. */
42
+ const SUCCESS_TTL_MS = 60_000;
43
+ /** Shorter TTL for an empty/failed fetch so transient errors recover fast. */
44
+ const EMPTY_TTL_MS = 10_000;
45
+ /** In-memory cache keyed by resolved directory origin (+ identity scope). */
46
+ const cache = new Map();
47
+ /**
48
+ * Resolve the org-directory origin from env. Returns `null` when neither env
49
+ * var is set — the caller treats `null` as "feature inactive".
50
+ *
51
+ * `env` is injectable for tests; defaults to `process.env`.
52
+ */
53
+ export function resolveOrgDirectoryOrigin(env = process.env) {
54
+ const raw = env.AGENT_NATIVE_ORG_DIRECTORY_URL || env.AGENT_NATIVE_IDENTITY_HUB_URL;
55
+ if (!raw || typeof raw !== "string")
56
+ return null;
57
+ const trimmed = raw.trim().replace(/\/+$/, "");
58
+ if (!trimmed)
59
+ return null;
60
+ try {
61
+ // Validate it's an absolute http(s) URL; reject anything else.
62
+ const u = new URL(trimmed);
63
+ if (u.protocol !== "http:" && u.protocol !== "https:")
64
+ return null;
65
+ return trimmed;
66
+ }
67
+ catch {
68
+ return null;
69
+ }
70
+ }
71
+ function normalizeApp(raw) {
72
+ if (!raw || typeof raw !== "object")
73
+ return null;
74
+ const r = raw;
75
+ const id = typeof r.id === "string" ? r.id.trim().toLowerCase() : "";
76
+ const url = typeof r.url === "string" ? r.url.trim() : "";
77
+ if (!id || !url)
78
+ return null;
79
+ // Only accept absolute http(s) URLs from the directory.
80
+ try {
81
+ const u = new URL(url);
82
+ if (u.protocol !== "http:" && u.protocol !== "https:")
83
+ return null;
84
+ }
85
+ catch {
86
+ return null;
87
+ }
88
+ const name = typeof r.name === "string" && r.name.trim() ? r.name.trim() : id;
89
+ const a2aUrl = typeof r.a2aUrl === "string" && r.a2aUrl.trim() ? r.a2aUrl.trim() : url;
90
+ const capabilities = Array.isArray(r.capabilities)
91
+ ? r.capabilities.filter((c) => typeof c === "string")
92
+ : undefined;
93
+ return {
94
+ id,
95
+ name,
96
+ url: url.replace(/\/+$/, ""),
97
+ a2aUrl: a2aUrl.replace(/\/+$/, ""),
98
+ ...(capabilities && capabilities.length ? { capabilities } : {}),
99
+ };
100
+ }
101
+ /** Compare two origins by host (ignores trailing slash / protocol noise). */
102
+ function sameOrigin(a, b) {
103
+ try {
104
+ const ua = new URL(a);
105
+ const ub = new URL(b);
106
+ return ua.host === ub.host && ua.protocol === ub.protocol;
107
+ }
108
+ catch {
109
+ return a.replace(/\/+$/, "") === b.replace(/\/+$/, "");
110
+ }
111
+ }
112
+ function scopedCacheKey(origin, auth) {
113
+ return [
114
+ origin,
115
+ `user:${auth.userEmail ?? ""}`,
116
+ `org:${auth.orgId ?? auth.orgDomain ?? ""}`,
117
+ ].join("|");
118
+ }
119
+ /**
120
+ * Fetch the org's first-party sibling apps from the org directory.
121
+ *
122
+ * - Returns `[]` (never throws) on ANY failure or when the directory env is
123
+ * unset — the cross-app verbs then keep their exact local-only behavior.
124
+ * - Short in-memory TTL cache so it isn't fetched on every tool call.
125
+ * - Strips the current app from the result (compared by id and by origin) so
126
+ * `list_apps` / `ask_app` never offer to route to themselves.
127
+ *
128
+ * @param opts.selfId Current app id (so it's stripped from the result).
129
+ * @param opts.selfOrigin Current app origin (so it's stripped by origin too).
130
+ * @param opts.env Injectable env (tests). Defaults to `process.env`.
131
+ */
132
+ export async function fetchOrgApps(opts) {
133
+ const env = opts?.env ?? process.env;
134
+ const origin = resolveOrgDirectoryOrigin(env);
135
+ // Feature inactive: no directory configured ⇒ behave exactly as before.
136
+ if (!origin)
137
+ return [];
138
+ const selfId = (opts?.selfId ?? "").trim().toLowerCase();
139
+ const selfOrigin = (opts?.selfOrigin ?? "").trim();
140
+ const stripSelf = (apps) => apps.filter((a) => {
141
+ if (selfId && a.id === selfId)
142
+ return false;
143
+ if (selfOrigin && sameOrigin(a.url, selfOrigin))
144
+ return false;
145
+ return true;
146
+ });
147
+ let cacheKey = null;
148
+ let apps = [];
149
+ let ttl = EMPTY_TTL_MS;
150
+ try {
151
+ // Reuse the existing A2A caller-auth: it reads userEmail + orgId from the
152
+ // request context, loads the org A2A secret via getOrgA2ASecret (falling
153
+ // back to the global A2A_SECRET env), and signs the same bearer JWT A2A
154
+ // peers already use. No new secret loading is invented here.
155
+ const { resolveA2ACallerAuth } = await import("../a2a/caller-auth.js");
156
+ const auth = await resolveA2ACallerAuth();
157
+ if (!auth.apiKey) {
158
+ // No signed token available (no A2A secret / no caller identity) — the
159
+ // directory requires the org bearer, so degrade silently to local-only.
160
+ return [];
161
+ }
162
+ const now = Date.now();
163
+ cacheKey = scopedCacheKey(origin, auth);
164
+ const cached = cache.get(cacheKey);
165
+ if (cached && cached.expiresAt > now) {
166
+ return stripSelf(cached.apps);
167
+ }
168
+ const res = await fetch(`${origin}/_agent-native/org/apps`, {
169
+ method: "GET",
170
+ headers: {
171
+ Authorization: `Bearer ${auth.apiKey}`,
172
+ Accept: "application/json",
173
+ },
174
+ signal: AbortSignal.timeout(4000),
175
+ });
176
+ if (res.ok) {
177
+ const json = (await res.json());
178
+ const list = Array.isArray(json?.apps) ? json.apps : [];
179
+ apps = list.map(normalizeApp).filter((a) => a !== null);
180
+ ttl = SUCCESS_TTL_MS;
181
+ }
182
+ // Non-2xx ⇒ leave apps=[] with the short EMPTY_TTL (silent degrade).
183
+ }
184
+ catch {
185
+ // Unreachable / parse error / abort ⇒ silent degrade to local-only.
186
+ apps = [];
187
+ ttl = EMPTY_TTL_MS;
188
+ }
189
+ if (cacheKey) {
190
+ cache.set(cacheKey, {
191
+ apps,
192
+ expiresAt: Date.now() + ttl,
193
+ });
194
+ }
195
+ return stripSelf(apps);
196
+ }
197
+ /** Test-only: clear the in-memory cache between cases. */
198
+ export function _resetOrgDirectoryCache() {
199
+ cache.clear();
200
+ }
201
+ //# sourceMappingURL=org-directory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"org-directory.js","sourceRoot":"","sources":["../../src/mcp/org-directory.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Org-directory discovery for the generic cross-app MCP verbs\n * (`list_apps` / `ask_app` in `builtin-tools.ts`).\n *\n * Phase 3b of cross-app auto-wiring. Today the cross-app verbs resolve sibling\n * apps from *local workspace* info only (`workspace-resolve.ts`), so the mail\n * agent can only reach the calendar agent in a local dev workspace. When the\n * deployment runs against an org directory (Dispatch is also the identity hub\n * for the org), this module discovers the org's *deployed* sibling apps so the\n * same verbs work cross-app in production with ZERO manual setup.\n *\n * ## The directory request\n *\n * GET <directoryOrigin>/_agent-native/org/apps\n * Auth Authorization: Bearer <org A2A token> (same signed token A2A peers\n * already mint — reuses `resolveA2ACallerAuth()`; the org A2A secret /\n * global `A2A_SECRET` is loaded exactly how outgoing A2A calls load it)\n * ⇒ { org, apps: [ { id, name, url, a2aUrl, capabilities? } ] }\n * (allow-listed first-party apps only, prod URLs — enforced by the\n * authority side, Phase 3a, on Dispatch)\n *\n * ## Resolution + safety model\n *\n * - The directory origin is read from env: `AGENT_NATIVE_ORG_DIRECTORY_URL`\n * (dedicated) or `AGENT_NATIVE_IDENTITY_HUB_URL` (Dispatch is also the\n * identity hub). When *neither* is set the feature is simply inactive —\n * `fetchOrgApps()` returns `[]` and nothing changes anywhere (asserted by\n * a test). This makes the whole feature opt-in and back-compat.\n * - On ANY error (no env, unreachable, 401, non-2xx, bad JSON, no signed\n * token) `fetchOrgApps()` returns `[]` and NEVER throws — the cross-app\n * verbs degrade silently to their exact current local-only behavior.\n * - A short in-memory TTL cache (default 60s) keyed by directory origin and\n * caller identity/org scope so sibling app lists never cross tenants.\n * Empty authenticated results are cached too (with a shorter TTL) so a\n * transient failure doesn't hammer the directory on every call.\n * - No secrets are ever logged.\n *\n * Bundled alongside `mountMCP` (no Node-only top-level imports). The A2A\n * caller-auth + a2a client are dynamically imported inside `fetchOrgApps()`.\n */\n\nexport interface OrgApp {\n /** Canonical app id, e.g. `calendar`. */\n id: string;\n /** Human-readable name, e.g. `Calendar`. */\n name: string;\n /** Deployed app origin/URL, e.g. `https://calendar.acme.com`. */\n url: string;\n /**\n * A2A endpoint to route `ask_app` to. The authority side returns this; we\n * fall back to the app `url` (the A2A client appends `/_agent-native/a2a`).\n */\n a2aUrl: string;\n /** Optional capability hints the authority side may include. */\n capabilities?: string[];\n}\n\n/** Default cache TTL for a successful directory fetch. */\nconst SUCCESS_TTL_MS = 60_000;\n/** Shorter TTL for an empty/failed fetch so transient errors recover fast. */\nconst EMPTY_TTL_MS = 10_000;\n\ninterface CacheEntry {\n apps: OrgApp[];\n expiresAt: number;\n}\n\n/** In-memory cache keyed by resolved directory origin (+ identity scope). */\nconst cache = new Map<string, CacheEntry>();\n\n/**\n * Resolve the org-directory origin from env. Returns `null` when neither env\n * var is set — the caller treats `null` as \"feature inactive\".\n *\n * `env` is injectable for tests; defaults to `process.env`.\n */\nexport function resolveOrgDirectoryOrigin(\n env: NodeJS.ProcessEnv = process.env,\n): string | null {\n const raw =\n env.AGENT_NATIVE_ORG_DIRECTORY_URL || env.AGENT_NATIVE_IDENTITY_HUB_URL;\n if (!raw || typeof raw !== \"string\") return null;\n const trimmed = raw.trim().replace(/\\/+$/, \"\");\n if (!trimmed) return null;\n try {\n // Validate it's an absolute http(s) URL; reject anything else.\n const u = new URL(trimmed);\n if (u.protocol !== \"http:\" && u.protocol !== \"https:\") return null;\n return trimmed;\n } catch {\n return null;\n }\n}\n\nfunction normalizeApp(raw: unknown): OrgApp | null {\n if (!raw || typeof raw !== \"object\") return null;\n const r = raw as Record<string, unknown>;\n const id = typeof r.id === \"string\" ? r.id.trim().toLowerCase() : \"\";\n const url = typeof r.url === \"string\" ? r.url.trim() : \"\";\n if (!id || !url) return null;\n // Only accept absolute http(s) URLs from the directory.\n try {\n const u = new URL(url);\n if (u.protocol !== \"http:\" && u.protocol !== \"https:\") return null;\n } catch {\n return null;\n }\n const name = typeof r.name === \"string\" && r.name.trim() ? r.name.trim() : id;\n const a2aUrl =\n typeof r.a2aUrl === \"string\" && r.a2aUrl.trim() ? r.a2aUrl.trim() : url;\n const capabilities = Array.isArray(r.capabilities)\n ? r.capabilities.filter((c): c is string => typeof c === \"string\")\n : undefined;\n return {\n id,\n name,\n url: url.replace(/\\/+$/, \"\"),\n a2aUrl: a2aUrl.replace(/\\/+$/, \"\"),\n ...(capabilities && capabilities.length ? { capabilities } : {}),\n };\n}\n\n/** Compare two origins by host (ignores trailing slash / protocol noise). */\nfunction sameOrigin(a: string, b: string): boolean {\n try {\n const ua = new URL(a);\n const ub = new URL(b);\n return ua.host === ub.host && ua.protocol === ub.protocol;\n } catch {\n return a.replace(/\\/+$/, \"\") === b.replace(/\\/+$/, \"\");\n }\n}\n\nfunction scopedCacheKey(\n origin: string,\n auth: {\n userEmail?: string;\n orgId?: string;\n orgDomain?: string;\n },\n): string {\n return [\n origin,\n `user:${auth.userEmail ?? \"\"}`,\n `org:${auth.orgId ?? auth.orgDomain ?? \"\"}`,\n ].join(\"|\");\n}\n\n/**\n * Fetch the org's first-party sibling apps from the org directory.\n *\n * - Returns `[]` (never throws) on ANY failure or when the directory env is\n * unset — the cross-app verbs then keep their exact local-only behavior.\n * - Short in-memory TTL cache so it isn't fetched on every tool call.\n * - Strips the current app from the result (compared by id and by origin) so\n * `list_apps` / `ask_app` never offer to route to themselves.\n *\n * @param opts.selfId Current app id (so it's stripped from the result).\n * @param opts.selfOrigin Current app origin (so it's stripped by origin too).\n * @param opts.env Injectable env (tests). Defaults to `process.env`.\n */\nexport async function fetchOrgApps(opts?: {\n selfId?: string;\n selfOrigin?: string;\n env?: NodeJS.ProcessEnv;\n}): Promise<OrgApp[]> {\n const env = opts?.env ?? process.env;\n const origin = resolveOrgDirectoryOrigin(env);\n // Feature inactive: no directory configured ⇒ behave exactly as before.\n if (!origin) return [];\n\n const selfId = (opts?.selfId ?? \"\").trim().toLowerCase();\n const selfOrigin = (opts?.selfOrigin ?? \"\").trim();\n\n const stripSelf = (apps: OrgApp[]): OrgApp[] =>\n apps.filter((a) => {\n if (selfId && a.id === selfId) return false;\n if (selfOrigin && sameOrigin(a.url, selfOrigin)) return false;\n return true;\n });\n\n let cacheKey: string | null = null;\n let apps: OrgApp[] = [];\n let ttl = EMPTY_TTL_MS;\n try {\n // Reuse the existing A2A caller-auth: it reads userEmail + orgId from the\n // request context, loads the org A2A secret via getOrgA2ASecret (falling\n // back to the global A2A_SECRET env), and signs the same bearer JWT A2A\n // peers already use. No new secret loading is invented here.\n const { resolveA2ACallerAuth } = await import(\"../a2a/caller-auth.js\");\n const auth = await resolveA2ACallerAuth();\n if (!auth.apiKey) {\n // No signed token available (no A2A secret / no caller identity) — the\n // directory requires the org bearer, so degrade silently to local-only.\n return [];\n }\n\n const now = Date.now();\n cacheKey = scopedCacheKey(origin, auth);\n const cached = cache.get(cacheKey);\n if (cached && cached.expiresAt > now) {\n return stripSelf(cached.apps);\n }\n\n const res = await fetch(`${origin}/_agent-native/org/apps`, {\n method: \"GET\",\n headers: {\n Authorization: `Bearer ${auth.apiKey}`,\n Accept: \"application/json\",\n },\n signal: AbortSignal.timeout(4000),\n });\n if (res.ok) {\n const json = (await res.json()) as { apps?: unknown };\n const list = Array.isArray(json?.apps) ? json.apps : [];\n apps = list.map(normalizeApp).filter((a): a is OrgApp => a !== null);\n ttl = SUCCESS_TTL_MS;\n }\n // Non-2xx ⇒ leave apps=[] with the short EMPTY_TTL (silent degrade).\n } catch {\n // Unreachable / parse error / abort ⇒ silent degrade to local-only.\n apps = [];\n ttl = EMPTY_TTL_MS;\n }\n\n if (cacheKey) {\n cache.set(cacheKey, {\n apps,\n expiresAt: Date.now() + ttl,\n });\n }\n return stripSelf(apps);\n}\n\n/** Test-only: clear the in-memory cache between cases. */\nexport function _resetOrgDirectoryCache(): void {\n cache.clear();\n}\n"]}
@@ -1,13 +1,50 @@
1
+ import type { H3Event } from "h3";
1
2
  import { createMCPServerForRequest, verifyAuth, getAccessTokens, resolveOrgIdFromDomain, buildLinkArtifacts, type MCPConfig, type MCPCallerIdentity, type MCPRequestMeta } from "./build-server.js";
2
3
  export { createMCPServerForRequest, verifyAuth, getAccessTokens, resolveOrgIdFromDomain, buildLinkArtifacts, };
3
4
  export type { MCPConfig, MCPCallerIdentity, MCPRequestMeta };
5
+ /**
6
+ * Handle a single `{routePrefix}/mcp` request on either runtime.
7
+ *
8
+ * - **Node fast-path** (real Node HTTP server): unchanged — delegate to the
9
+ * SDK's `StreamableHTTPServerTransport.handleRequest(nodeReq, nodeRes,
10
+ * body)`, which writes directly to the Node response (full protocol incl.
11
+ * SSE).
12
+ * - **Web-standard fallback** (Nitro 3 / Netlify web runtime, Cloudflare,
13
+ * Deno, Bun — where there is no Node req/res): build the SAME MCP `Server`
14
+ * from the SAME config + identity, drive it through the SDK's
15
+ * `WebStandardStreamableHTTPServerTransport` (which the Node transport is
16
+ * itself just a thin wrapper around), and return the resulting Web
17
+ * `Response` as a normal h3 return value.
18
+ *
19
+ * Auth, the `runWithRequestContext` identity wrap, the deep-link `_meta` /
20
+ * markdown append, `requestMeta` origin/target derivation and the stateless
21
+ * semantics are IDENTICAL on both paths because both build the same server
22
+ * via `createMCPServerForRequest` and both transports funnel into the same
23
+ * `WebStandardStreamableHTTPServerTransport.handleRequest(webRequest, {
24
+ * parsedBody })` with the same options.
25
+ *
26
+ * Returns:
27
+ * - `undefined` when the request targets a sub-route (so management/status
28
+ * routes mounted under `/_agent-native/mcp/*` handle it themselves) — the
29
+ * h3 mount falls through to the next handler.
30
+ * - a Web `Response` (web fallback) or a string/object (Node path /
31
+ * auth-error path) otherwise. The Node path also sets `_handled` so h3
32
+ * doesn't double-write.
33
+ */
34
+ export declare function handleMcpRequest(event: H3Event, config: MCPConfig): Promise<Response | string | {
35
+ error: string;
36
+ } | undefined>;
4
37
  /**
5
38
  * Mount an MCP remote server on an H3/Nitro app.
6
39
  *
7
40
  * Endpoint: `{routePrefix}/mcp` (default `/_agent-native/mcp`)
8
41
  *
9
42
  * Uses stateless Streamable HTTP transport — no in-memory sessions,
10
- * compatible with serverless deployments.
43
+ * compatible with serverless deployments. Runtime-agnostic: a real Node
44
+ * server uses the SDK's Node transport; the web-standard runtime (Nitro 3 /
45
+ * Netlify web runtime, Cloudflare, Deno, Bun) uses the SDK's web-standard
46
+ * transport. Both build the same server and produce identical JSON-RPC
47
+ * output.
11
48
  *
12
49
  * Auth: Bearer token matching ACCESS_TOKEN/ACCESS_TOKENS or JWT via A2A_SECRET.
13
50
  * No auth required when neither is configured (dev mode).
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,yBAAyB,EACzB,UAAU,EACV,eAAe,EACf,sBAAsB,EACtB,kBAAkB,EAClB,KAAK,SAAS,EACd,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACpB,MAAM,mBAAmB,CAAC;AAK3B,OAAO,EACL,yBAAyB,EACzB,UAAU,EACV,eAAe,EACf,sBAAsB,EACtB,kBAAkB,GACnB,CAAC;AACF,YAAY,EAAE,SAAS,EAAE,iBAAiB,EAAE,cAAc,EAAE,CAAC;AAM7D;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CACtB,QAAQ,EAAE,GAAG,EACb,MAAM,EAAE,SAAS,EACjB,WAAW,SAAmB,GAC7B,IAAI,CAwHN"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AASlC,OAAO,EACL,yBAAyB,EACzB,UAAU,EACV,eAAe,EACf,sBAAsB,EACtB,kBAAkB,EAClB,KAAK,SAAS,EACd,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACpB,MAAM,mBAAmB,CAAC;AAK3B,OAAO,EACL,yBAAyB,EACzB,UAAU,EACV,eAAe,EACf,sBAAsB,EACtB,kBAAkB,GACnB,CAAC;AACF,YAAY,EAAE,SAAS,EAAE,iBAAiB,EAAE,cAAc,EAAE,CAAC;AA2G7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,SAAS,GAChB,OAAO,CAAC,QAAQ,GAAG,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAAC,CAgH5D;AAMD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,QAAQ,CACtB,QAAQ,EAAE,GAAG,EACb,MAAM,EAAE,SAAS,EACjB,WAAW,SAAmB,GAC7B,IAAI,CAYN"}