@cursor/july 0.1.95 → 0.1.97
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.
- package/AGENTS.md +3 -4
- package/README.md +3 -2
- package/dist/bin/agent-serve.js +14 -3
- package/dist/channels/deployments/types.d.ts +1 -1
- package/dist/channels/deployments/types.d.ts.map +1 -1
- package/dist/channels/deployments/types.js +1 -0
- package/dist/channels/origin/api.d.ts +72 -15
- package/dist/channels/origin/api.d.ts.map +1 -1
- package/dist/channels/origin/api.js +185 -28
- package/dist/channels/origin/binding.d.ts +13 -2
- package/dist/channels/origin/binding.d.ts.map +1 -1
- package/dist/channels/origin/binding.js +41 -25
- package/dist/channels/origin/checks.d.ts +6 -6
- package/dist/channels/origin/checks.js +6 -6
- package/dist/channels/origin/cursor-account.d.ts +5 -3
- package/dist/channels/origin/cursor-account.d.ts.map +1 -1
- package/dist/channels/origin/cursor-account.js +5 -3
- package/dist/channels/origin/index.d.ts +1 -0
- package/dist/channels/origin/index.d.ts.map +1 -1
- package/dist/channels/origin/index.js +1 -0
- package/dist/channels/origin/origin-channel.d.ts.map +1 -1
- package/dist/channels/origin/origin-channel.js +3 -0
- package/dist/channels/origin/reactions.d.ts +56 -0
- package/dist/channels/origin/reactions.d.ts.map +1 -0
- package/dist/channels/origin/reactions.js +67 -0
- package/dist/channels/origin/types.d.ts +11 -0
- package/dist/channels/origin/types.d.ts.map +1 -1
- package/dist/channels/slack/api.d.ts +9 -0
- package/dist/channels/slack/api.d.ts.map +1 -1
- package/dist/channels/slack/api.js +36 -5
- package/dist/channels/slack/approvals.d.ts +16 -0
- package/dist/channels/slack/approvals.d.ts.map +1 -1
- package/dist/channels/slack/approvals.js +86 -0
- package/dist/channels/slack/cli-project.d.ts +19 -0
- package/dist/channels/slack/cli-project.d.ts.map +1 -0
- package/dist/channels/slack/cli-project.js +234 -0
- package/dist/channels/slack/defaults.d.ts.map +1 -1
- package/dist/channels/slack/defaults.js +110 -81
- package/dist/channels/slack/dispatch.d.ts +7 -1
- package/dist/channels/slack/dispatch.d.ts.map +1 -1
- package/dist/channels/slack/dispatch.js +47 -23
- package/dist/channels/slack/inbound.d.ts +8 -0
- package/dist/channels/slack/inbound.d.ts.map +1 -1
- package/dist/channels/slack/inbound.js +32 -0
- package/dist/channels/slack/init.d.ts +3 -2
- package/dist/channels/slack/init.d.ts.map +1 -1
- package/dist/channels/slack/init.js +5 -5
- package/dist/channels/slack/interactive.d.ts +2 -1
- package/dist/channels/slack/interactive.d.ts.map +1 -1
- package/dist/channels/slack/interactive.js +15 -4
- package/dist/channels/slack/live-delivery.d.ts +26 -29
- package/dist/channels/slack/live-delivery.d.ts.map +1 -1
- package/dist/channels/slack/live-delivery.js +172 -73
- package/dist/channels/slack/manifest.d.ts +1 -1
- package/dist/channels/slack/manifest.d.ts.map +1 -1
- package/dist/channels/slack/manifest.js +6 -2
- package/dist/channels/slack/nudge-carrier.d.ts +2 -0
- package/dist/channels/slack/nudge-carrier.d.ts.map +1 -1
- package/dist/channels/slack/nudge-carrier.js +48 -1
- package/dist/channels/slack/setup.d.ts +1 -1
- package/dist/channels/slack/setup.d.ts.map +1 -1
- package/dist/channels/slack/setup.js +10 -6
- package/dist/channels/slack/slack-channel.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.js +3 -2
- package/dist/channels/slack/tool-approval-events.d.ts.map +1 -1
- package/dist/channels/slack/tool-approval-events.js +91 -1
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +4 -4
- package/dist/docs/assets/{app.BBj0klBO.js → app.B3rWNYE1.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.BU9nHdnQ.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.CUEXpL78.js → VPLocalSearchBox.C1lhqJJR.js} +1 -1
- package/dist/docs/assets/chunks/{theme.DabmQnia.js → theme.B40_SXuv.js} +2 -2
- package/dist/docs/assets/{deployment.md.D9msOFOW.js → deployment.md.MFPKqsqI.js} +2 -2
- package/dist/docs/assets/{guides_mcp-oauth.md.DPYmBCbV.js → guides_mcp-oauth.md.CN-6YmTJ.js} +4 -4
- package/dist/docs/assets/{guides_mcp-oauth.md.DPYmBCbV.lean.js → guides_mcp-oauth.md.CN-6YmTJ.lean.js} +1 -1
- package/dist/docs/assets/guides_slack.md.VDXQV3ja.js +22 -0
- package/dist/docs/assets/guides_slack.md.VDXQV3ja.lean.js +1 -0
- package/dist/docs/assets/index.md.CVeRUOeZ.js +5 -0
- package/dist/docs/assets/{index.md.BoHaMdeZ.lean.js → index.md.CVeRUOeZ.lean.js} +1 -1
- package/dist/docs/assets/{reference_agent-config.md.Cp_x38Nl.js → reference_agent-config.md.BHeYMgme.js} +1 -1
- package/dist/docs/assets/{reference_cli.md.BsMOxDvh.js → reference_cli.md.RyZf5OTE.js} +10 -6
- package/dist/docs/assets/{reference_cli.md.BsMOxDvh.lean.js → reference_cli.md.RyZf5OTE.lean.js} +1 -1
- package/dist/docs/assets/{reference_connections.md.BH8Oc0D0.js → reference_connections.md.DxldvyIB.js} +4 -3
- package/dist/docs/assets/{reference_http-api.md.D89k1mdm.js → reference_http-api.md.5zOAbV86.js} +1 -1
- package/dist/docs/assets/reference_sessions.md.NxFP_CAC.js +1 -0
- package/dist/docs/assets/{reference_tools.md.BHeXn2id.js → reference_tools.md.B84gw9Ii.js} +1 -1
- package/dist/docs/assets/templates_agents-md.md.CXSMZljE.js +2 -0
- package/dist/docs/assets/templates_agents-md.md.CXSMZljE.lean.js +1 -0
- package/dist/docs/assets/templates_code-wiki.md.CDYZIBFN.js +2 -0
- package/dist/docs/assets/templates_code-wiki.md.CDYZIBFN.lean.js +1 -0
- package/dist/docs/assets/troubleshooting.md.DCiPBhYs.js +1 -0
- package/dist/docs/building-with-agents.html +4 -4
- package/dist/docs/concepts.html +4 -4
- package/dist/docs/deployment.html +7 -7
- package/dist/docs/deployment.md +5 -4
- package/dist/docs/evals.html +4 -4
- package/dist/docs/guides/agent-to-agent.html +4 -4
- package/dist/docs/guides/cloud-runtime.html +4 -4
- package/dist/docs/guides/convert-automation.html +4 -4
- package/dist/docs/guides/github.html +4 -4
- package/dist/docs/guides/human-in-the-loop.html +4 -4
- package/dist/docs/guides/mcp-oauth.html +8 -8
- package/dist/docs/guides/mcp-oauth.md +26 -8
- package/dist/docs/guides/opentelemetry.html +4 -4
- package/dist/docs/guides/slack.html +17 -33
- package/dist/docs/guides/slack.md +92 -201
- package/dist/docs/guides/webhooks.html +4 -4
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +4 -4
- package/dist/docs/index.html +6 -6
- package/dist/docs/index.md +7 -3
- package/dist/docs/llms-full.txt +398 -653
- package/dist/docs/llms.txt +3 -2
- package/dist/docs/quickstart.html +4 -4
- package/dist/docs/reference/agent-config.html +6 -6
- package/dist/docs/reference/agent-config.md +2 -2
- package/dist/docs/reference/artifacts.html +4 -4
- package/dist/docs/reference/channels.html +4 -4
- package/dist/docs/reference/cli.html +14 -10
- package/dist/docs/reference/cli.md +39 -16
- package/dist/docs/reference/connections.html +9 -8
- package/dist/docs/reference/connections.md +6 -3
- package/dist/docs/reference/hooks.html +4 -4
- package/dist/docs/reference/http-api.html +6 -6
- package/dist/docs/reference/http-api.md +2 -1
- package/dist/docs/reference/instructions.html +4 -4
- package/dist/docs/reference/playground.html +4 -4
- package/dist/docs/reference/project-layout.html +4 -4
- package/dist/docs/reference/prompt.html +4 -4
- package/dist/docs/reference/schedules.html +4 -4
- package/dist/docs/reference/sessions.html +5 -5
- package/dist/docs/reference/sessions.md +4 -2
- package/dist/docs/reference/skills.html +4 -4
- package/dist/docs/reference/subagents.html +4 -4
- package/dist/docs/reference/tools.html +6 -6
- package/dist/docs/reference/tools.md +9 -2
- package/dist/docs/scaffolding-agents.html +4 -4
- package/dist/docs/storage.html +4 -4
- package/dist/docs/templates/agentic-owners.html +4 -4
- package/dist/docs/templates/agents-md.html +28 -0
- package/dist/docs/templates/agents-md.md +105 -0
- package/dist/docs/templates/code-wiki.html +28 -0
- package/dist/docs/templates/code-wiki.md +91 -0
- package/dist/docs/templates/demo.html +5 -5
- package/dist/docs/templates/pr-autofixer.html +4 -4
- package/dist/docs/templates/security-help.html +4 -4
- package/dist/docs/templates/security-reviewer.html +5 -5
- package/dist/docs/templates/triage.html +4 -4
- package/dist/docs/troubleshooting.html +5 -5
- package/dist/docs/troubleshooting.md +2 -2
- package/dist/evals/run-facts.d.ts.map +1 -1
- package/dist/evals/run-facts.js +27 -0
- package/dist/files-backends/cursor-hosted.d.ts +26 -4
- package/dist/files-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/files-backends/cursor-hosted.js +32 -4
- package/dist/internal/advertise-tools.d.ts +2 -0
- package/dist/internal/advertise-tools.d.ts.map +1 -1
- package/dist/internal/advertise-tools.js +30 -10
- package/dist/internal/approval-gate.d.ts +12 -13
- package/dist/internal/approval-gate.d.ts.map +1 -1
- package/dist/internal/approval-gate.js +16 -21
- package/dist/internal/bounded.d.ts +7 -0
- package/dist/internal/bounded.d.ts.map +1 -0
- package/dist/internal/bounded.js +32 -0
- package/dist/internal/chat-client.js +10 -0
- package/dist/internal/cli-deploy.d.ts +5 -0
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +57 -18
- package/dist/internal/cli-mcp-oauth.js +8 -18
- package/dist/internal/cli-slack-manual.d.ts +24 -2
- package/dist/internal/cli-slack-manual.d.ts.map +1 -1
- package/dist/internal/cli-slack-manual.js +251 -20
- package/dist/internal/cli-slack.d.ts +9 -2
- package/dist/internal/cli-slack.d.ts.map +1 -1
- package/dist/internal/cli-slack.js +29 -82
- package/dist/internal/conversation-mirror.d.ts +11 -8
- package/dist/internal/conversation-mirror.d.ts.map +1 -1
- package/dist/internal/conversation-mirror.js +116 -55
- package/dist/internal/cursor/account-mcp.d.ts +1 -0
- package/dist/internal/cursor/account-mcp.d.ts.map +1 -1
- package/dist/internal/cursor/account-mcp.js +17 -0
- package/dist/internal/cursor/backend-client.d.ts.map +1 -1
- package/dist/internal/cursor/backend-client.js +22 -48
- package/dist/internal/cursor/session-token.d.ts +45 -0
- package/dist/internal/cursor/session-token.d.ts.map +1 -0
- package/dist/internal/cursor/session-token.js +99 -0
- package/dist/internal/cursor-agent-template.d.ts +1 -1
- package/dist/internal/cursor-agent-template.d.ts.map +1 -1
- package/dist/internal/cursor-agent-template.js +2 -0
- package/dist/internal/deploy-client.d.ts +17 -0
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +31 -10
- package/dist/internal/env-file.d.ts +19 -0
- package/dist/internal/env-file.d.ts.map +1 -0
- package/dist/internal/env-file.js +90 -0
- package/dist/internal/framework-file-storage.d.ts +61 -0
- package/dist/internal/framework-file-storage.d.ts.map +1 -0
- package/dist/internal/framework-file-storage.js +210 -0
- package/dist/internal/framework-storage-selection.d.ts +11 -5
- package/dist/internal/framework-storage-selection.d.ts.map +1 -1
- package/dist/internal/framework-storage-selection.js +38 -11
- package/dist/internal/hosted-delivery-protocol.d.ts +7 -0
- package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -1
- package/dist/internal/hosted-delivery-protocol.js +7 -0
- package/dist/internal/hosted-delivery.d.ts +13 -0
- package/dist/internal/hosted-delivery.d.ts.map +1 -1
- package/dist/internal/hosted-delivery.js +64 -19
- package/dist/internal/hosted-execution-diag.d.ts +48 -0
- package/dist/internal/hosted-execution-diag.d.ts.map +1 -0
- package/dist/internal/hosted-execution-diag.js +150 -0
- package/dist/internal/http-channel.d.ts.map +1 -1
- package/dist/internal/http-channel.js +81 -1
- package/dist/internal/init-project.d.ts.map +1 -1
- package/dist/internal/init-project.js +4 -0
- package/dist/internal/install-cursor-skills.d.ts.map +1 -1
- package/dist/internal/install-cursor-skills.js +1 -3
- package/dist/internal/logs-client.d.ts +5 -0
- package/dist/internal/logs-client.d.ts.map +1 -1
- package/dist/internal/logs-client.js +5 -0
- package/dist/internal/managed-delivery.d.ts +80 -0
- package/dist/internal/managed-delivery.d.ts.map +1 -0
- package/dist/internal/managed-delivery.js +86 -0
- package/dist/internal/mcp-endpoint.js +2 -3
- package/dist/internal/mcp-host.d.ts +10 -0
- package/dist/internal/mcp-host.d.ts.map +1 -1
- package/dist/internal/mcp-host.js +57 -8
- package/dist/internal/mcp-oauth-on-demand.d.ts +68 -0
- package/dist/internal/mcp-oauth-on-demand.d.ts.map +1 -0
- package/dist/internal/mcp-oauth-on-demand.js +287 -0
- package/dist/internal/mcp-oauth-persist.d.ts +42 -0
- package/dist/internal/mcp-oauth-persist.d.ts.map +1 -0
- package/dist/internal/mcp-oauth-persist.js +92 -0
- package/dist/internal/mcp-oauth.d.ts +1 -0
- package/dist/internal/mcp-oauth.d.ts.map +1 -1
- package/dist/internal/mcp-oauth.js +10 -2
- package/dist/internal/otel/collector.d.ts.map +1 -1
- package/dist/internal/otel/collector.js +22 -9
- package/dist/internal/runtime-dispatch-runner.d.ts +5 -1
- package/dist/internal/runtime-dispatch-runner.d.ts.map +1 -1
- package/dist/internal/runtime-dispatch-runner.js +7 -0
- package/dist/internal/sdk-runner.d.ts +11 -0
- package/dist/internal/sdk-runner.d.ts.map +1 -1
- package/dist/internal/sdk-runner.js +39 -8
- package/dist/internal/server.d.ts +3 -0
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +49 -21
- package/dist/internal/session-engine.d.ts +101 -3
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +577 -46
- package/dist/internal/sessions-client.d.ts +9 -7
- package/dist/internal/sessions-client.d.ts.map +1 -1
- package/dist/internal/sessions-client.js +3 -1
- package/dist/internal/steering.d.ts +15 -0
- package/dist/internal/steering.d.ts.map +1 -0
- package/dist/internal/steering.js +18 -0
- package/dist/internal/storage-coordinator.d.ts +7 -0
- package/dist/internal/storage-coordinator.d.ts.map +1 -1
- package/dist/internal/storage-coordinator.js +28 -5
- package/dist/internal/storage-roles.d.ts +5 -0
- package/dist/internal/storage-roles.d.ts.map +1 -1
- package/dist/internal/stream-progress.d.ts.map +1 -1
- package/dist/internal/stream-progress.js +10 -0
- package/dist/internal/tool-policy.d.ts +9 -1
- package/dist/internal/tool-policy.d.ts.map +1 -1
- package/dist/internal/tool-policy.js +13 -0
- package/dist/internal/trajectory.d.ts.map +1 -1
- package/dist/internal/trajectory.js +10 -0
- package/dist/internal/turn-outbox.d.ts +24 -0
- package/dist/internal/turn-outbox.d.ts.map +1 -0
- package/dist/internal/turn-outbox.js +80 -0
- package/dist/playground/assets/{index-CZA1uEWA.js → index-DrkI6y5O.js} +51 -51
- package/dist/playground/assets/index-DzNGwm7q.css +1 -0
- package/dist/playground/index.html +2 -2
- package/dist/storage-backends/cursor-hosted.d.ts +9 -6
- package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/storage-backends/cursor-hosted.js +16 -3
- package/dist/storage.d.ts +8 -0
- package/dist/storage.d.ts.map +1 -1
- package/dist/types.d.ts +65 -8
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +7 -3
- package/docs/deployment.md +5 -4
- package/docs/guides/mcp-oauth.md +26 -8
- package/docs/guides/slack.md +93 -202
- package/docs/reference/agent-config.md +2 -2
- package/docs/reference/cli.md +39 -16
- package/docs/reference/connections.md +6 -3
- package/docs/reference/http-api.md +2 -1
- package/docs/reference/sessions.md +4 -2
- package/docs/reference/tools.md +9 -2
- package/docs/templates/agents-md.md +110 -0
- package/docs/templates/code-wiki.md +96 -0
- package/docs/troubleshooting.md +2 -2
- package/package.json +2 -2
- package/skills/ab/SKILL.md +13 -97
- package/skills/create-agent/SKILL.md +124 -223
- package/skills/debug/SKILL.md +34 -68
- package/skills/evals/SKILL.md +55 -180
- package/skills/framework-map/SKILL.md +83 -145
- package/skills/github/SKILL.md +53 -111
- package/skills/hillclimb/SKILL.md +40 -163
- package/skills/mcp-auth/SKILL.md +51 -107
- package/skills/otel/SKILL.md +22 -68
- package/skills/setup-slack/SKILL.md +91 -212
- package/src/bin/agent-serve.ts +13 -1
- package/src/channels/deployments/types.ts +1 -0
- package/src/channels/origin/api.ts +264 -33
- package/src/channels/origin/binding.ts +57 -28
- package/src/channels/origin/checks.ts +6 -6
- package/src/channels/origin/cursor-account.ts +5 -3
- package/src/channels/origin/index.ts +1 -0
- package/src/channels/origin/origin-channel.ts +4 -0
- package/src/channels/origin/reactions.ts +82 -0
- package/src/channels/origin/types.ts +11 -0
- package/src/channels/slack/api.ts +41 -5
- package/src/channels/slack/approvals.ts +105 -0
- package/src/channels/slack/cli-project.ts +267 -0
- package/src/channels/slack/defaults.ts +122 -93
- package/src/channels/slack/dispatch.ts +56 -22
- package/src/channels/slack/inbound.ts +43 -0
- package/src/channels/slack/init.ts +10 -5
- package/src/channels/slack/interactive.ts +35 -9
- package/src/channels/slack/live-delivery.ts +235 -114
- package/src/channels/slack/manifest.ts +6 -2
- package/src/channels/slack/nudge-carrier.ts +48 -1
- package/src/channels/slack/setup.ts +10 -6
- package/src/channels/slack/slack-channel.ts +3 -0
- package/src/channels/slack/tool-approval-events.ts +93 -0
- package/src/evals/run-facts.ts +32 -0
- package/src/files-backends/cursor-hosted.ts +40 -7
- package/src/internal/advertise-tools.ts +49 -12
- package/src/internal/approval-gate.ts +20 -29
- package/src/internal/bounded.ts +28 -0
- package/src/internal/chat-client.ts +19 -0
- package/src/internal/cli-deploy.ts +91 -9
- package/src/internal/cli-mcp-oauth.ts +7 -21
- package/src/internal/cli-slack-manual.ts +350 -18
- package/src/internal/cli-slack.ts +40 -84
- package/src/internal/conversation-mirror.ts +145 -69
- package/src/internal/cursor/account-mcp.ts +21 -0
- package/src/internal/cursor/backend-client.ts +34 -58
- package/src/internal/cursor/session-token.ts +115 -0
- package/src/internal/cursor-agent-template.ts +2 -0
- package/src/internal/deploy-client.ts +43 -0
- package/src/internal/env-file.ts +84 -0
- package/src/internal/framework-file-storage.ts +228 -0
- package/src/internal/framework-storage-selection.ts +51 -11
- package/src/internal/hosted-delivery-protocol.ts +7 -0
- package/src/internal/hosted-delivery.ts +107 -24
- package/src/internal/hosted-execution-diag.ts +207 -0
- package/src/internal/http-channel.ts +99 -0
- package/src/internal/init-project.ts +4 -0
- package/src/internal/install-cursor-skills.ts +1 -4
- package/src/internal/logs-client.ts +5 -0
- package/src/internal/managed-delivery.ts +125 -0
- package/src/internal/mcp-endpoint.ts +13 -4
- package/src/internal/mcp-host.ts +77 -11
- package/src/internal/mcp-oauth-on-demand.ts +352 -0
- package/src/internal/mcp-oauth-persist.ts +127 -0
- package/src/internal/mcp-oauth.ts +17 -1
- package/src/internal/otel/collector.ts +30 -0
- package/src/internal/runtime-dispatch-runner.ts +10 -0
- package/src/internal/sdk-runner.ts +56 -8
- package/src/internal/server.ts +61 -9
- package/src/internal/session-engine.ts +723 -60
- package/src/internal/sessions-client.ts +17 -8
- package/src/internal/steering.ts +17 -0
- package/src/internal/storage-coordinator.ts +38 -8
- package/src/internal/storage-roles.ts +7 -0
- package/src/internal/stream-progress.ts +19 -0
- package/src/internal/tool-policy.ts +18 -0
- package/src/internal/trajectory.ts +10 -0
- package/src/internal/turn-outbox.ts +85 -0
- package/src/storage-backends/cursor-hosted.ts +22 -8
- package/src/storage.ts +12 -0
- package/src/types.ts +70 -9
- package/templates/agents-md/README.md +39 -0
- package/templates/agents-md/agent/agent.ts +8 -0
- package/templates/agents-md/agent/channels/github.ts +10 -0
- package/templates/agents-md/agent/channels/queue.ts +28 -0
- package/templates/agents-md/agent/channels/slack.ts +9 -0
- package/templates/agents-md/agent/instructions.md +39 -0
- package/templates/agents-md/agent/lib/collect.ts +439 -0
- package/templates/agents-md/agent/lib/config.ts +8 -0
- package/templates/agents-md/agent/lib/paths.ts +51 -0
- package/templates/agents-md/agent/schedules/weekly.ts +17 -0
- package/templates/agents-md/agent/tools/apply_agents.ts +226 -0
- package/templates/agents-md/evals/agents.eval.ts +135 -0
- package/templates/agents-md/evals/evals.config.ts +5 -0
- package/templates/agents-md/init.json +38 -0
- package/templates/agents-md/package.json +18 -0
- package/templates/agents-md/tsconfig.json +12 -0
- package/templates/code-wiki/README.md +34 -0
- package/templates/code-wiki/agent/agent.ts +8 -0
- package/templates/code-wiki/agent/channels/github.ts +144 -0
- package/templates/code-wiki/agent/instructions.md +39 -0
- package/templates/code-wiki/agent/lib/repos.ts +5 -0
- package/templates/code-wiki/agent/lib/wiki.ts +22 -0
- package/templates/code-wiki/agent/tools/apply_wiki.ts +219 -0
- package/templates/code-wiki/evals/evals.config.ts +5 -0
- package/templates/code-wiki/evals/wiki.eval.ts +132 -0
- package/templates/code-wiki/init.json +25 -0
- package/templates/code-wiki/package.json +18 -0
- package/templates/code-wiki/tsconfig.json +12 -0
- package/dist/docs/assets/chunks/@localSearchIndexroot.oqPawjiD.js +0 -1
- package/dist/docs/assets/guides_slack.md.C32HsdKk.js +0 -38
- package/dist/docs/assets/guides_slack.md.C32HsdKk.lean.js +0 -1
- package/dist/docs/assets/index.md.BoHaMdeZ.js +0 -5
- package/dist/docs/assets/reference_sessions.md.D0mIh4KK.js +0 -1
- package/dist/docs/assets/troubleshooting.md.Ctv3T8C2.js +0 -1
- package/dist/docs/design/agsh.md +0 -406
- package/dist/internal/deployment-scm-event-stream.d.ts +0 -116
- package/dist/internal/deployment-scm-event-stream.d.ts.map +0 -1
- package/dist/internal/deployment-scm-event-stream.js +0 -474
- package/dist/playground/assets/index-CF7hUDnQ.css +0 -1
- package/docs/design/agsh.md +0 -406
- package/src/internal/deployment-scm-event-stream.ts +0 -593
- /package/dist/docs/assets/{deployment.md.D9msOFOW.lean.js → deployment.md.MFPKqsqI.lean.js} +0 -0
- /package/dist/docs/assets/{reference_agent-config.md.Cp_x38Nl.lean.js → reference_agent-config.md.BHeYMgme.lean.js} +0 -0
- /package/dist/docs/assets/{reference_connections.md.BH8Oc0D0.lean.js → reference_connections.md.DxldvyIB.lean.js} +0 -0
- /package/dist/docs/assets/{reference_http-api.md.D89k1mdm.lean.js → reference_http-api.md.5zOAbV86.lean.js} +0 -0
- /package/dist/docs/assets/{reference_sessions.md.D0mIh4KK.lean.js → reference_sessions.md.NxFP_CAC.lean.js} +0 -0
- /package/dist/docs/assets/{reference_tools.md.BHeXn2id.lean.js → reference_tools.md.B84gw9Ii.lean.js} +0 -0
- /package/dist/docs/assets/{troubleshooting.md.Ctv3T8C2.lean.js → troubleshooting.md.DCiPBhYs.lean.js} +0 -0
package/src/internal/mcp-host.ts
CHANGED
|
@@ -26,6 +26,47 @@ import type {
|
|
|
26
26
|
import { CLI_COMMAND_NAME as CLI } from "./distribution.js";
|
|
27
27
|
import { createHostMcpOAuthProvider } from "./mcp-oauth.js";
|
|
28
28
|
|
|
29
|
+
export class McpNeedsAuthError extends Error {
|
|
30
|
+
readonly connection: string;
|
|
31
|
+
|
|
32
|
+
constructor(connection: string, cause?: unknown) {
|
|
33
|
+
super(
|
|
34
|
+
`MCP connection "${connection}" needs authorization. Run \`${CLI} mcp oauth ${connection}\`.`
|
|
35
|
+
);
|
|
36
|
+
this.name = "McpNeedsAuthError";
|
|
37
|
+
this.connection = connection;
|
|
38
|
+
if (cause !== undefined) {
|
|
39
|
+
this.cause = cause;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function isMcpNeedsAuthError(
|
|
45
|
+
error: unknown
|
|
46
|
+
): error is McpNeedsAuthError {
|
|
47
|
+
return error instanceof McpNeedsAuthError;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function hostMcpCallNeedsAuth(result: HostMcpCallResult): boolean {
|
|
51
|
+
if (result.isError !== true) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
const text = result.content
|
|
55
|
+
.map((part) => (part.type === "text" ? part.text : ""))
|
|
56
|
+
.join("\n");
|
|
57
|
+
return text.includes("needsAuth") || text.includes("needs authorization");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function throwIfCallNeedsAuth(
|
|
61
|
+
connection: string,
|
|
62
|
+
result: HostMcpCallResult
|
|
63
|
+
): HostMcpCallResult {
|
|
64
|
+
if (hostMcpCallNeedsAuth(result)) {
|
|
65
|
+
throw new McpNeedsAuthError(connection);
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
|
|
29
70
|
interface OpenedConnection {
|
|
30
71
|
client: Client;
|
|
31
72
|
close: () => Promise<void>;
|
|
@@ -46,6 +87,7 @@ export interface HostMcpOneOff {
|
|
|
46
87
|
export class McpHost implements HostMcpRegistry {
|
|
47
88
|
private readonly byName = new Map<string, DiscoveredConnection>();
|
|
48
89
|
private readonly clients = new Map<string, Promise<OpenedConnection>>();
|
|
90
|
+
private oauthRedirectUrl: string | undefined;
|
|
49
91
|
|
|
50
92
|
constructor(connections: DiscoveredConnection[]) {
|
|
51
93
|
for (const connection of connections) {
|
|
@@ -53,6 +95,11 @@ export class McpHost implements HostMcpRegistry {
|
|
|
53
95
|
}
|
|
54
96
|
}
|
|
55
97
|
|
|
98
|
+
/** Public serve callback for host OAuth (not the CLI loopback port on pods). */
|
|
99
|
+
setOAuthRedirectUrl(url: string): void {
|
|
100
|
+
this.oauthRedirectUrl = url;
|
|
101
|
+
}
|
|
102
|
+
|
|
56
103
|
/**
|
|
57
104
|
* Register a connection after construction (serve-time resolved peers).
|
|
58
105
|
* Clients open lazily on first use, so adding before first call is safe.
|
|
@@ -82,10 +129,11 @@ export class McpHost implements HostMcpRegistry {
|
|
|
82
129
|
* mapping as registry calls.
|
|
83
130
|
*/
|
|
84
131
|
oneOff(connection: DiscoveredConnection): HostMcpOneOff {
|
|
132
|
+
const redirectUrl = this.oauthRedirectUrl;
|
|
85
133
|
const withFreshClient = async <T>(
|
|
86
134
|
run: (client: Client) => Promise<T>
|
|
87
135
|
): Promise<T> => {
|
|
88
|
-
const opened = await openConnection(connection);
|
|
136
|
+
const opened = await openConnection(connection, redirectUrl);
|
|
89
137
|
try {
|
|
90
138
|
return await run(opened.client);
|
|
91
139
|
} finally {
|
|
@@ -104,7 +152,10 @@ export class McpHost implements HostMcpRegistry {
|
|
|
104
152
|
name: toolName,
|
|
105
153
|
arguments: args,
|
|
106
154
|
});
|
|
107
|
-
return
|
|
155
|
+
return throwIfCallNeedsAuth(
|
|
156
|
+
connection.name,
|
|
157
|
+
normalizeCallResult(result)
|
|
158
|
+
);
|
|
108
159
|
}),
|
|
109
160
|
};
|
|
110
161
|
}
|
|
@@ -119,10 +170,23 @@ export class McpHost implements HostMcpRegistry {
|
|
|
119
170
|
name: toolName,
|
|
120
171
|
arguments: args,
|
|
121
172
|
});
|
|
122
|
-
return normalizeCallResult(result);
|
|
173
|
+
return throwIfCallNeedsAuth(connectionName, normalizeCallResult(result));
|
|
123
174
|
});
|
|
124
175
|
}
|
|
125
176
|
|
|
177
|
+
async dropClient(name: string): Promise<void> {
|
|
178
|
+
const pending = this.clients.get(name);
|
|
179
|
+
this.clients.delete(name);
|
|
180
|
+
if (pending === undefined) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
try {
|
|
184
|
+
await (await pending).close();
|
|
185
|
+
} catch {
|
|
186
|
+
// Best-effort teardown.
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
126
190
|
async close(): Promise<void> {
|
|
127
191
|
const opened = await Promise.allSettled([...this.clients.values()]);
|
|
128
192
|
this.clients.clear();
|
|
@@ -198,7 +262,7 @@ export class McpHost implements HostMcpRegistry {
|
|
|
198
262
|
`Unknown MCP connection "${name}". Authored MCP connections: ${known}.`
|
|
199
263
|
);
|
|
200
264
|
}
|
|
201
|
-
return openConnection(connection);
|
|
265
|
+
return openConnection(connection, this.oauthRedirectUrl);
|
|
202
266
|
}
|
|
203
267
|
}
|
|
204
268
|
|
|
@@ -283,13 +347,14 @@ function toToolInfo(tool: {
|
|
|
283
347
|
}
|
|
284
348
|
|
|
285
349
|
async function openConnection(
|
|
286
|
-
connection: DiscoveredConnection
|
|
350
|
+
connection: DiscoveredConnection,
|
|
351
|
+
oauthRedirectUrl?: string
|
|
287
352
|
): Promise<OpenedConnection> {
|
|
288
353
|
const client = new Client({
|
|
289
354
|
name: "agent-serve",
|
|
290
355
|
version: "0.1.0",
|
|
291
356
|
});
|
|
292
|
-
const transport = await createTransport(connection);
|
|
357
|
+
const transport = await createTransport(connection, oauthRedirectUrl);
|
|
293
358
|
try {
|
|
294
359
|
await client.connect(transport);
|
|
295
360
|
} catch (error) {
|
|
@@ -297,10 +362,7 @@ async function openConnection(
|
|
|
297
362
|
error instanceof UnauthorizedError &&
|
|
298
363
|
isOauthHttpTransport(connection.transport)
|
|
299
364
|
) {
|
|
300
|
-
throw new
|
|
301
|
-
`MCP connection "${connection.name}" needs authorization. Run \`${CLI} mcp oauth ${connection.name}\`.`,
|
|
302
|
-
{ cause: error }
|
|
303
|
-
);
|
|
365
|
+
throw new McpNeedsAuthError(connection.name, error);
|
|
304
366
|
}
|
|
305
367
|
throw error;
|
|
306
368
|
}
|
|
@@ -322,7 +384,10 @@ async function openConnection(
|
|
|
322
384
|
};
|
|
323
385
|
}
|
|
324
386
|
|
|
325
|
-
async function createTransport(
|
|
387
|
+
async function createTransport(
|
|
388
|
+
connection: DiscoveredConnection,
|
|
389
|
+
oauthRedirectUrl?: string
|
|
390
|
+
) {
|
|
326
391
|
const transport = connection.transport;
|
|
327
392
|
if ("agent" in transport) {
|
|
328
393
|
// Peers are resolved to concrete URLs by the serve host before any call
|
|
@@ -365,6 +430,7 @@ async function createTransport(connection: DiscoveredConnection) {
|
|
|
365
430
|
? await createHostMcpOAuthProvider({
|
|
366
431
|
identifier: connection.name,
|
|
367
432
|
resourceUrl: transport.url,
|
|
433
|
+
redirectUrl: oauthRedirectUrl,
|
|
368
434
|
})
|
|
369
435
|
: undefined;
|
|
370
436
|
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mid-run host MCP OAuth: mint an authUrl, finish on the public serve
|
|
3
|
+
* callback (file tokens). Deployment secrets persist from the
|
|
4
|
+
* authenticated park, not from that callback.
|
|
5
|
+
*
|
|
6
|
+
* CLI `mcp oauth` still uses the loopback callback. This path is recovery
|
|
7
|
+
* and first-use without a separate `--store` step.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js";
|
|
11
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
12
|
+
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
13
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
14
|
+
import { describeError } from "./describe-error.js";
|
|
15
|
+
import { LOG_PREFIX } from "./distribution.js";
|
|
16
|
+
import {
|
|
17
|
+
createHostMcpOAuthProvider,
|
|
18
|
+
FileMcpOAuthStore,
|
|
19
|
+
MCP_OAUTH_CALLBACK_URL,
|
|
20
|
+
} from "./mcp-oauth.js";
|
|
21
|
+
import {
|
|
22
|
+
type McpOauthSecretsTarget,
|
|
23
|
+
type ResolveHostedMcpOauthSecretsArgs,
|
|
24
|
+
resolveHostedMcpOauthSecretsTarget,
|
|
25
|
+
upsertHostMcpOauthSecrets,
|
|
26
|
+
} from "./mcp-oauth-persist.js";
|
|
27
|
+
|
|
28
|
+
export function mcpOauthServeCallbackPath(basePath = ""): string {
|
|
29
|
+
return `${basePath}/v1/mcp-oauth/callback`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function mcpOauthServeCallbackUrl(
|
|
33
|
+
origin: string,
|
|
34
|
+
basePath = ""
|
|
35
|
+
): string {
|
|
36
|
+
return `${origin.replace(/\/$/, "")}${mcpOauthServeCallbackPath(basePath)}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type BeginHostMcpOauthResult =
|
|
40
|
+
| { status: "authorized" }
|
|
41
|
+
| {
|
|
42
|
+
status: "needs_auth";
|
|
43
|
+
authUrl: string;
|
|
44
|
+
state: string;
|
|
45
|
+
finish: (code: string) => Promise<void>;
|
|
46
|
+
abort: () => Promise<void>;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export async function beginHostMcpOauth(args: {
|
|
50
|
+
connection: string;
|
|
51
|
+
url: string;
|
|
52
|
+
type?: "http" | "sse";
|
|
53
|
+
headers?: Record<string, string>;
|
|
54
|
+
redirectUrl: string;
|
|
55
|
+
store?: FileMcpOAuthStore;
|
|
56
|
+
}): Promise<BeginHostMcpOauthResult> {
|
|
57
|
+
let authorizationUrl: URL | undefined;
|
|
58
|
+
const provider = await createHostMcpOAuthProvider({
|
|
59
|
+
identifier: args.connection,
|
|
60
|
+
resourceUrl: args.url,
|
|
61
|
+
store: args.store,
|
|
62
|
+
redirectUrl: args.redirectUrl,
|
|
63
|
+
onRedirect: (url) => {
|
|
64
|
+
authorizationUrl = url;
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const client = new Client({ name: "agent-sdk-mcp-oauth", version: "0.1.0" });
|
|
69
|
+
const url = new URL(args.url);
|
|
70
|
+
const requestInit =
|
|
71
|
+
args.headers === undefined ? undefined : { headers: args.headers };
|
|
72
|
+
|
|
73
|
+
const makeTransport = ():
|
|
74
|
+
| StreamableHTTPClientTransport
|
|
75
|
+
| SSEClientTransport => {
|
|
76
|
+
if (args.type === "sse") {
|
|
77
|
+
return new SSEClientTransport(url, {
|
|
78
|
+
authProvider: provider,
|
|
79
|
+
requestInit,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return new StreamableHTTPClientTransport(url, {
|
|
83
|
+
authProvider: provider,
|
|
84
|
+
requestInit,
|
|
85
|
+
});
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const transport = makeTransport();
|
|
89
|
+
try {
|
|
90
|
+
await client.connect(transport);
|
|
91
|
+
await client.close().catch(() => undefined);
|
|
92
|
+
return { status: "authorized" };
|
|
93
|
+
} catch (error) {
|
|
94
|
+
if (!(error instanceof UnauthorizedError)) {
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const redirect = authorizationUrl ?? provider.lastRedirectUrl;
|
|
100
|
+
if (redirect === undefined) {
|
|
101
|
+
throw new Error(
|
|
102
|
+
`Auth required for "${args.connection}" but no authorization URL was provided.`
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
const state = redirect.searchParams.get("state") ?? "";
|
|
106
|
+
if (state === "") {
|
|
107
|
+
throw new Error(
|
|
108
|
+
`Auth required for "${args.connection}" but the authorization URL had no state.`
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
status: "needs_auth",
|
|
114
|
+
authUrl: redirect.toString(),
|
|
115
|
+
state,
|
|
116
|
+
finish: async (code: string) => {
|
|
117
|
+
await transport.finishAuth(code);
|
|
118
|
+
await transport.close?.().catch(() => undefined);
|
|
119
|
+
},
|
|
120
|
+
abort: async () => {
|
|
121
|
+
await transport.close?.().catch(() => undefined);
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
type BoundAttempt = {
|
|
127
|
+
connection: string;
|
|
128
|
+
resourceUrl: string;
|
|
129
|
+
finish: (code: string) => Promise<void>;
|
|
130
|
+
abort: () => Promise<void>;
|
|
131
|
+
sessionId?: string;
|
|
132
|
+
callId?: string;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export type McpOauthCompleteResult = {
|
|
136
|
+
connection: string;
|
|
137
|
+
sessionId?: string;
|
|
138
|
+
callId?: string;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export class McpOauthOnDemandHub {
|
|
142
|
+
private readonly attempts = new Map<string, BoundAttempt>();
|
|
143
|
+
private readonly completed = new Map<string, BoundAttempt>();
|
|
144
|
+
private readonly finishing = new Map<
|
|
145
|
+
string,
|
|
146
|
+
Promise<McpOauthCompleteResult>
|
|
147
|
+
>();
|
|
148
|
+
private readonly aborted = new Set<string>();
|
|
149
|
+
callbackUrl: string | undefined;
|
|
150
|
+
secrets?: McpOauthSecretsTarget;
|
|
151
|
+
// Identity only — resolve the key at persist, after freeze bind.
|
|
152
|
+
secretsBind?: ResolveHostedMcpOauthSecretsArgs;
|
|
153
|
+
logger?: (line: string) => void;
|
|
154
|
+
private store?: FileMcpOAuthStore;
|
|
155
|
+
|
|
156
|
+
async mint(args: {
|
|
157
|
+
connection: string;
|
|
158
|
+
url: string;
|
|
159
|
+
type?: "http" | "sse";
|
|
160
|
+
headers?: Record<string, string>;
|
|
161
|
+
sessionId?: string;
|
|
162
|
+
}): Promise<{ authUrl: string; state: string } | { authorized: true }> {
|
|
163
|
+
const redirectUrl = this.callbackUrl ?? MCP_OAUTH_CALLBACK_URL;
|
|
164
|
+
const store = this.fileStore();
|
|
165
|
+
const started = await beginHostMcpOauth({
|
|
166
|
+
connection: args.connection,
|
|
167
|
+
url: args.url,
|
|
168
|
+
type: args.type,
|
|
169
|
+
headers: args.headers,
|
|
170
|
+
redirectUrl,
|
|
171
|
+
store,
|
|
172
|
+
});
|
|
173
|
+
if (started.status === "authorized") {
|
|
174
|
+
return { authorized: true };
|
|
175
|
+
}
|
|
176
|
+
this.attempts.set(started.state, {
|
|
177
|
+
connection: args.connection,
|
|
178
|
+
resourceUrl: args.url,
|
|
179
|
+
finish: started.finish,
|
|
180
|
+
abort: started.abort,
|
|
181
|
+
sessionId: args.sessionId,
|
|
182
|
+
});
|
|
183
|
+
return { authUrl: started.authUrl, state: started.state };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async abortCall(sessionId: string, callId: string): Promise<void> {
|
|
187
|
+
await this.dropWhere(
|
|
188
|
+
(attempt) => attempt.sessionId === sessionId && attempt.callId === callId,
|
|
189
|
+
true
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async abortSession(sessionId: string): Promise<void> {
|
|
194
|
+
await this.dropWhere((attempt) => attempt.sessionId === sessionId, false);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async close(): Promise<void> {
|
|
198
|
+
await this.dropWhere(() => true, true);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
private async dropWhere(
|
|
202
|
+
match: (attempt: BoundAttempt) => boolean,
|
|
203
|
+
includeCompleted: boolean
|
|
204
|
+
): Promise<void> {
|
|
205
|
+
const jobs: Array<Promise<void>> = [];
|
|
206
|
+
for (const [state, attempt] of this.attempts) {
|
|
207
|
+
if (!match(attempt)) {
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
this.aborted.add(state);
|
|
211
|
+
this.attempts.delete(state);
|
|
212
|
+
jobs.push(attempt.abort().catch(() => undefined));
|
|
213
|
+
}
|
|
214
|
+
if (includeCompleted) {
|
|
215
|
+
for (const [state, attempt] of this.completed) {
|
|
216
|
+
if (!match(attempt)) {
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
this.aborted.add(state);
|
|
220
|
+
this.completed.delete(state);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
await Promise.all(jobs);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
bind(
|
|
227
|
+
state: string,
|
|
228
|
+
sessionId: string,
|
|
229
|
+
callId: string
|
|
230
|
+
): "pending" | "already_done" | "unknown" {
|
|
231
|
+
const live = this.attempts.get(state);
|
|
232
|
+
if (live !== undefined) {
|
|
233
|
+
live.sessionId = sessionId;
|
|
234
|
+
live.callId = callId;
|
|
235
|
+
return "pending";
|
|
236
|
+
}
|
|
237
|
+
const done = this.completed.get(state);
|
|
238
|
+
if (done !== undefined) {
|
|
239
|
+
done.sessionId = sessionId;
|
|
240
|
+
done.callId = callId;
|
|
241
|
+
return "already_done";
|
|
242
|
+
}
|
|
243
|
+
return "unknown";
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async complete(state: string, code: string): Promise<McpOauthCompleteResult> {
|
|
247
|
+
if (this.aborted.has(state)) {
|
|
248
|
+
throw new Error("Unknown OAuth state.");
|
|
249
|
+
}
|
|
250
|
+
const pending = this.finishing.get(state);
|
|
251
|
+
if (pending !== undefined) {
|
|
252
|
+
return await pending;
|
|
253
|
+
}
|
|
254
|
+
const attempt = this.attempts.get(state);
|
|
255
|
+
if (attempt === undefined) {
|
|
256
|
+
throw new Error("Unknown OAuth state.");
|
|
257
|
+
}
|
|
258
|
+
const run = this.finishAttempt(state, attempt, code);
|
|
259
|
+
this.finishing.set(state, run);
|
|
260
|
+
return await run;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
private async finishAttempt(
|
|
264
|
+
state: string,
|
|
265
|
+
attempt: BoundAttempt,
|
|
266
|
+
code: string
|
|
267
|
+
): Promise<McpOauthCompleteResult> {
|
|
268
|
+
try {
|
|
269
|
+
await attempt.finish(code);
|
|
270
|
+
this.attempts.delete(state);
|
|
271
|
+
if (!this.aborted.has(state)) {
|
|
272
|
+
this.completed.set(state, attempt);
|
|
273
|
+
}
|
|
274
|
+
return {
|
|
275
|
+
connection: attempt.connection,
|
|
276
|
+
sessionId: attempt.sessionId,
|
|
277
|
+
callId: attempt.callId,
|
|
278
|
+
};
|
|
279
|
+
} finally {
|
|
280
|
+
this.finishing.delete(state);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
private fileStore(): FileMcpOAuthStore {
|
|
285
|
+
this.store ??= new FileMcpOAuthStore();
|
|
286
|
+
return this.store;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
async persistFor(connection: string, resourceUrl: string): Promise<void> {
|
|
290
|
+
const target =
|
|
291
|
+
this.secrets ??
|
|
292
|
+
resolveHostedMcpOauthSecretsTarget(this.secretsBind ?? {});
|
|
293
|
+
if (target === undefined) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
const store = this.fileStore();
|
|
297
|
+
const tokens = await store.loadTokens(connection, resourceUrl);
|
|
298
|
+
if (tokens === undefined || tokens.access_token.trim() === "") {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
const client = await store.loadClientInformation(connection, resourceUrl);
|
|
302
|
+
try {
|
|
303
|
+
const names = await upsertHostMcpOauthSecrets({
|
|
304
|
+
connection,
|
|
305
|
+
tokens,
|
|
306
|
+
clientId: client?.client_id,
|
|
307
|
+
target,
|
|
308
|
+
});
|
|
309
|
+
this.logger?.(
|
|
310
|
+
`${LOG_PREFIX} MCP OAuth tokens stored on the deployment (${names.join(", ")}).`
|
|
311
|
+
);
|
|
312
|
+
} catch (error) {
|
|
313
|
+
this.logger?.(
|
|
314
|
+
`${LOG_PREFIX} MCP OAuth persist failed for "${connection}": ${describeError(error)}`
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
async handleCallback(request: Request): Promise<Response> {
|
|
320
|
+
const url = new URL(request.url);
|
|
321
|
+
const code = url.searchParams.get("code");
|
|
322
|
+
const state = url.searchParams.get("state") ?? "";
|
|
323
|
+
if (this.aborted.has(state)) {
|
|
324
|
+
return oauthHtml(404, "<h1>Not found</h1>");
|
|
325
|
+
}
|
|
326
|
+
if (this.completed.has(state)) {
|
|
327
|
+
return oauthHtml(200, "<h1>Authorized</h1><p>Return to the agent.</p>");
|
|
328
|
+
}
|
|
329
|
+
if (!this.attempts.has(state)) {
|
|
330
|
+
return oauthHtml(404, "<h1>Not found</h1>");
|
|
331
|
+
}
|
|
332
|
+
if (code !== null && code !== "") {
|
|
333
|
+
try {
|
|
334
|
+
await this.complete(state, code);
|
|
335
|
+
} catch {
|
|
336
|
+
return oauthHtml(400, "<h1>Authorization failed</h1>");
|
|
337
|
+
}
|
|
338
|
+
if (this.aborted.has(state)) {
|
|
339
|
+
return oauthHtml(404, "<h1>Not found</h1>");
|
|
340
|
+
}
|
|
341
|
+
return oauthHtml(200, "<h1>Authorized</h1><p>Return to the agent.</p>");
|
|
342
|
+
}
|
|
343
|
+
return oauthHtml(400, "<h1>Authorization failed</h1>");
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function oauthHtml(status: number, body: string): Response {
|
|
348
|
+
return new Response(`<!doctype html><html><body>${body}</body></html>`, {
|
|
349
|
+
status,
|
|
350
|
+
headers: { "content-type": "text/html; charset=utf-8" },
|
|
351
|
+
});
|
|
352
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deployment-secret upsert for host MCP OAuth (`mcp oauth --store` and
|
|
3
|
+
* hosted Connect). File writes stay on FileMcpOAuthStore / the OAuth
|
|
4
|
+
* provider. Resolve the `--store` client at persist time so v2 freeze
|
|
5
|
+
* bind can drop the key after listen.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { OAuthTokens } from "@modelcontextprotocol/sdk/shared/auth.js";
|
|
9
|
+
import { AGENT_SERVE_DEPLOYMENT_ID_ENV_VAR } from "../storage-backends/cursor-hosted.js";
|
|
10
|
+
import {
|
|
11
|
+
cursorExternalApiUrl,
|
|
12
|
+
resolveApiKeySync,
|
|
13
|
+
} from "./cursor/credentials.js";
|
|
14
|
+
import {
|
|
15
|
+
AgentServeDeployClient,
|
|
16
|
+
parseAgentServeMcpGatewayUrl,
|
|
17
|
+
} from "./deploy-client.js";
|
|
18
|
+
import { mcpOauthSecretNames } from "./mcp-oauth.js";
|
|
19
|
+
|
|
20
|
+
export type McpOauthSecretsClient = {
|
|
21
|
+
setSecrets(args: {
|
|
22
|
+
teamId: number;
|
|
23
|
+
slug: string;
|
|
24
|
+
secrets: Array<{ name: string; value: string }>;
|
|
25
|
+
}): Promise<string[]>;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type McpOauthSecretsTarget = {
|
|
29
|
+
client: McpOauthSecretsClient;
|
|
30
|
+
teamId: number;
|
|
31
|
+
slug: string;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
function mcpOauthSecretsPayload(args: {
|
|
35
|
+
connection: string;
|
|
36
|
+
tokens: OAuthTokens;
|
|
37
|
+
clientId?: string;
|
|
38
|
+
}): Array<{ name: string; value: string }> {
|
|
39
|
+
const names = mcpOauthSecretNames(args.connection);
|
|
40
|
+
const secrets: Array<{ name: string; value: string }> = [
|
|
41
|
+
{ name: names.accessToken, value: args.tokens.access_token },
|
|
42
|
+
];
|
|
43
|
+
const refresh = args.tokens.refresh_token?.trim();
|
|
44
|
+
if (refresh !== undefined && refresh !== "") {
|
|
45
|
+
secrets.push({ name: names.refreshToken, value: refresh });
|
|
46
|
+
}
|
|
47
|
+
const clientId = args.clientId?.trim();
|
|
48
|
+
if (clientId !== undefined && clientId !== "") {
|
|
49
|
+
secrets.push({ name: names.clientId, value: clientId });
|
|
50
|
+
}
|
|
51
|
+
return secrets;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function upsertHostMcpOauthSecrets(args: {
|
|
55
|
+
connection: string;
|
|
56
|
+
tokens: OAuthTokens;
|
|
57
|
+
clientId?: string;
|
|
58
|
+
target: McpOauthSecretsTarget;
|
|
59
|
+
}): Promise<string[]> {
|
|
60
|
+
const payload = mcpOauthSecretsPayload(args);
|
|
61
|
+
if (payload.length === 0) {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
return await args.target.client.setSecrets({
|
|
65
|
+
teamId: args.target.teamId,
|
|
66
|
+
slug: args.target.slug,
|
|
67
|
+
secrets: payload,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export type ResolveHostedMcpOauthSecretsArgs = {
|
|
72
|
+
slug?: string;
|
|
73
|
+
team?: string;
|
|
74
|
+
apiKey?: string;
|
|
75
|
+
cloudToolsUrl?: string;
|
|
76
|
+
env?: NodeJS.ProcessEnv;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Bind the CLI `--store` client when this process is a hosted engine
|
|
81
|
+
* (`AGENT_SERVE_DEPLOYMENT_ID`) and a live API key is present.
|
|
82
|
+
* Self-hosted / no deployment / unsigned: undefined.
|
|
83
|
+
*/
|
|
84
|
+
export function resolveHostedMcpOauthSecretsTarget(
|
|
85
|
+
args: ResolveHostedMcpOauthSecretsArgs = {}
|
|
86
|
+
): McpOauthSecretsTarget | undefined {
|
|
87
|
+
const env = args.env ?? process.env;
|
|
88
|
+
const deploymentId = env[AGENT_SERVE_DEPLOYMENT_ID_ENV_VAR]?.trim();
|
|
89
|
+
if (deploymentId === undefined || deploymentId === "") {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
const resolved = resolveApiKeySync(
|
|
93
|
+
args.apiKey === undefined ? undefined : { apiKey: args.apiKey }
|
|
94
|
+
);
|
|
95
|
+
if (resolved === undefined) {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
const fromUrl =
|
|
99
|
+
args.cloudToolsUrl === undefined
|
|
100
|
+
? undefined
|
|
101
|
+
: parseAgentServeMcpGatewayUrl(args.cloudToolsUrl);
|
|
102
|
+
const slug =
|
|
103
|
+
args.slug !== undefined && args.slug !== "" ? args.slug : fromUrl?.slug;
|
|
104
|
+
const teamId = parseTeamId(args.team) ?? fromUrl?.teamId;
|
|
105
|
+
if (slug === undefined || teamId === undefined) {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
client: new AgentServeDeployClient({
|
|
110
|
+
backendUrl: cursorExternalApiUrl(),
|
|
111
|
+
apiKey: resolved.apiKey,
|
|
112
|
+
}),
|
|
113
|
+
teamId,
|
|
114
|
+
slug,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function parseTeamId(team: string | undefined): number | undefined {
|
|
119
|
+
if (team === undefined || team === "") {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
const teamId = Number.parseInt(team, 10);
|
|
123
|
+
if (!Number.isInteger(teamId) || teamId <= 0) {
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
return teamId;
|
|
127
|
+
}
|
|
@@ -411,13 +411,25 @@ export class HostMcpOAuthProvider implements OAuthClientProvider {
|
|
|
411
411
|
}
|
|
412
412
|
}
|
|
413
413
|
|
|
414
|
+
function clientForRedirect(
|
|
415
|
+
info: OAuthClientInformationFull | undefined,
|
|
416
|
+
redirectUrl: string
|
|
417
|
+
): OAuthClientInformationFull | undefined {
|
|
418
|
+
if (info === undefined || !info.redirect_uris.includes(redirectUrl)) {
|
|
419
|
+
return undefined;
|
|
420
|
+
}
|
|
421
|
+
return info;
|
|
422
|
+
}
|
|
423
|
+
|
|
414
424
|
export async function createHostMcpOAuthProvider(args: {
|
|
415
425
|
identifier: string;
|
|
416
426
|
resourceUrl: string;
|
|
417
427
|
store?: FileMcpOAuthStore;
|
|
418
428
|
onRedirect?: (url: URL) => void;
|
|
429
|
+
redirectUrl?: string;
|
|
419
430
|
}): Promise<HostMcpOAuthProvider> {
|
|
420
431
|
const store = args.store ?? new FileMcpOAuthStore();
|
|
432
|
+
const redirectUrl = args.redirectUrl ?? MCP_OAUTH_CALLBACK_URL;
|
|
421
433
|
const [fileTokens, fileClient, omitResourceIndicator] = await Promise.all([
|
|
422
434
|
store.loadTokens(args.identifier, args.resourceUrl),
|
|
423
435
|
store.loadClientInformation(args.identifier, args.resourceUrl),
|
|
@@ -427,8 +439,12 @@ export async function createHostMcpOAuthProvider(args: {
|
|
|
427
439
|
identifier: args.identifier,
|
|
428
440
|
resourceUrl: args.resourceUrl,
|
|
429
441
|
store,
|
|
442
|
+
redirectUrl,
|
|
430
443
|
tokens: fileTokens ?? tokensFromEnv(args.identifier),
|
|
431
|
-
clientInformation:
|
|
444
|
+
clientInformation: clientForRedirect(
|
|
445
|
+
fileClient ?? clientIdFromEnv(args.identifier),
|
|
446
|
+
redirectUrl
|
|
447
|
+
),
|
|
432
448
|
onRedirect: args.onRedirect,
|
|
433
449
|
omitResourceIndicator,
|
|
434
450
|
});
|