@zackbart/connecta 0.34.1 → 0.36.0
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/CHANGELOG.md +418 -0
- package/README.md +65 -105
- package/SECURITY.md +3 -2
- package/bin/connecta.mjs +41 -16
- package/bin/version.mjs +1 -1
- package/dist/access-tokens.d.ts +2 -1
- package/dist/access-tokens.js +66 -30
- package/dist/activity.d.ts +1 -1
- package/dist/activity.js +2 -0
- package/dist/agent-config.d.ts +23 -0
- package/dist/agent-config.js +59 -0
- package/dist/agent-description.js +16 -0
- package/dist/agent-identity.js +4 -4
- package/dist/agent-observation.js +39 -0
- package/dist/auth/clerk-sdk/client.d.ts +2 -0
- package/dist/auth/clerk-sdk/generated.js +554 -563
- package/dist/auth/clerk-transport.js +70 -3
- package/dist/auth/clerk.d.ts +13 -0
- package/dist/auth/clerk.js +653 -246
- package/dist/auth/cloudflare-access.js +46 -42
- package/dist/auth/downstream-client-metadata.js +2 -2
- package/dist/auth/downstream-oauth.js +2044 -1161
- package/dist/auth/downstream-revocation.js +88 -88
- package/dist/auth/native-call.js +20 -0
- package/dist/auth/native.js +184 -0
- package/dist/auth/oauth-start-reset.js +11 -5
- package/dist/auth/oauth-token-response.js +121 -102
- package/dist/auth/oauth-v2-migration.js +97 -78
- package/dist/auth/renewable-grant.js +10 -0
- package/dist/auth/static-oauth.js +751 -281
- package/dist/auth/vercel-agent.js +266 -0
- package/dist/auth-admission.js +204 -0
- package/dist/auth-elicitation.js +350 -241
- package/dist/authorization-handoff.js +164 -90
- package/dist/call-admission-context.js +3 -10
- package/dist/call-admission.js +2 -0
- package/dist/call-classification.js +9 -0
- package/dist/call-recovery.js +32 -13
- package/dist/canonical-json.js +8 -0
- package/dist/catalog-cache.js +369 -213
- package/dist/catalog-service.js +659 -530
- package/dist/catalog.js +5 -1
- package/dist/concurrency.js +0 -25
- package/dist/config-defaults.d.ts +1 -0
- package/dist/config-defaults.js +1 -0
- package/dist/config-schema.d.ts +3 -1
- package/dist/config-schema.js +13 -3
- package/dist/config.d.ts +40 -6
- package/dist/config.js +40 -26
- package/dist/connector-caller.d.ts +4 -0
- package/dist/connector-caller.js +17 -4
- package/dist/connectors/api-connector.d.ts +29 -7
- package/dist/connectors/api-connector.js +145 -57
- package/dist/connectors/guarded-fetch.d.ts +51 -17
- package/dist/connectors/guarded-fetch.js +215 -121
- package/dist/connectors/negotiation-cache.js +20 -10
- package/dist/connectors/remote-mcp.d.ts +10 -30
- package/dist/connectors/remote-mcp.js +955 -563
- package/dist/d1.js +10 -243
- package/dist/derived-context.js +5 -0
- package/dist/describe-config.d.ts +3 -1
- package/dist/describe-config.js +2 -0
- package/dist/downstream-input.js +347 -260
- package/dist/errors.d.ts +87 -6
- package/dist/errors.js +114 -4
- package/dist/execute.js +87 -50
- package/dist/executor-admission.js +3 -0
- package/dist/executors/quickjs.js +163 -112
- package/dist/guarded-transport.js +50 -0
- package/dist/guest-runtime.js +1 -1
- package/dist/http/client.js +439 -0
- package/dist/http/errors.js +558 -0
- package/dist/http/fields.js +49 -0
- package/dist/http/hatch-paging.js +318 -0
- package/dist/http/hatch.js +1279 -0
- package/dist/http/json-schema.js +267 -0
- package/dist/http/node-transport.js +241 -0
- package/dist/http/oauth.js +162 -0
- package/dist/http/paging.js +227 -0
- package/dist/http/provider.js +221 -0
- package/dist/http/safety.js +82 -0
- package/dist/http/services.js +79 -0
- package/dist/http/tools.js +379 -0
- package/dist/http/vendor-index.js +138 -0
- package/dist/http/vendor.js +448 -0
- package/dist/http/write-request.js +66 -0
- package/dist/http-policy.d.ts +11 -0
- package/dist/http-policy.js +45 -0
- package/dist/inbound-credential.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -0
- package/dist/invocation-auth.js +30 -19
- package/dist/invocation.js +218 -85
- package/dist/mcp-result.js +0 -4
- package/dist/membership.d.ts +4 -0
- package/dist/meta-output.js +1 -1
- package/dist/meta-tools.js +531 -406
- package/dist/native-recovery.js +52 -0
- package/dist/native-tools.js +382 -0
- package/dist/node.d.ts +4 -3
- package/dist/node.js +45 -8
- package/dist/oauth-handoff.js +177 -104
- package/dist/oauth-partition.js +6 -20
- package/dist/oauth-sealing.js +6 -7
- package/dist/operation-vocabulary.js +101 -0
- package/dist/operator-record.d.ts +2 -2
- package/dist/operator-record.js +17 -4
- package/dist/operator-ui/contract.d.ts +8 -3
- package/dist/operator-ui/generated.js +4 -4
- package/dist/operator-ui/model.d.ts +2 -3
- package/dist/program-writes.js +47 -26
- package/dist/provider-call-facts.d.ts +115 -0
- package/dist/provider-call-facts.js +182 -0
- package/dist/provider-call.d.ts +112 -0
- package/dist/provider-call.js +271 -0
- package/dist/provider.d.ts +65 -31
- package/dist/provider.js +116 -39
- package/dist/providers/_shared/google/hatch.js +91 -0
- package/dist/providers/_shared/google/kit.js +427 -0
- package/dist/providers/_shared/google/result-size.js +15 -2
- package/dist/providers/_shared/google/service-account.d.ts +2 -1
- package/dist/providers/_shared/google/service-account.js +8 -4
- package/dist/providers/_shared/google/workspace.d.ts +6 -210
- package/dist/providers/_shared/google/workspace.js +6 -425
- package/dist/providers/_shared/rest/compact-index.js +86 -0
- package/dist/providers/_shared/rest/kit.js +146 -0
- package/dist/providers/_shared/rest/operation-index.js +230 -14
- package/dist/providers/_shared/rest/response-media.js +11 -0
- package/dist/providers/_shared/rest/value-safety.js +353 -195
- package/dist/providers/_shared/sensitive-fields.js +27 -0
- package/dist/providers/breeze/api.js +584 -0
- package/dist/providers/breeze/index.d.ts +1 -3
- package/dist/providers/breeze/index.js +67 -1420
- package/dist/providers/breeze/openapi.generated.js +60 -0
- package/dist/providers/breeze/skill.generated.js +2 -6
- package/dist/providers/breeze/tools.js +696 -0
- package/dist/providers/ccb/api.js +496 -0
- package/dist/providers/ccb/index.d.ts +15 -37
- package/dist/providers/ccb/index.js +77 -1702
- package/dist/providers/ccb/lib/constants.d.ts +34 -0
- package/dist/providers/ccb/lib/constants.js +74 -0
- package/dist/providers/ccb/lib/guide.js +9 -0
- package/dist/providers/ccb/lib/verdicts.js +387 -0
- package/dist/providers/ccb/openapi.generated.js +397 -0
- package/dist/providers/ccb/safety.js +295 -0
- package/dist/providers/ccb/skill.generated.js +2 -6
- package/dist/providers/ccb/tools.js +853 -0
- package/dist/providers/cloudflare/api.js +1066 -0
- package/dist/providers/cloudflare/index.d.ts +5 -22
- package/dist/providers/cloudflare/index.js +79 -197
- package/dist/providers/cloudflare/lib/config.d.ts +16 -0
- package/dist/providers/cloudflare/lib/config.js +2 -0
- package/dist/providers/cloudflare/lib/discovery.js +29 -0
- package/dist/providers/cloudflare/{graphql.js → lib/graphql.js} +1 -1
- package/dist/providers/cloudflare/lib/hatch.js +103 -0
- package/dist/providers/cloudflare/lib/path-bounds.js +28 -0
- package/dist/providers/cloudflare/lib/response-media.js +75 -0
- package/dist/providers/cloudflare/openapi.generated.js +4 -3
- package/dist/providers/cloudflare/{value-safety.js → safety.js} +487 -0
- package/dist/providers/cloudflare/skill.generated.js +2 -10
- package/dist/providers/cloudflare/tools.js +360 -0
- package/dist/providers/docs/api.js +51 -0
- package/dist/providers/docs/index.d.ts +1 -12
- package/dist/providers/docs/index.js +53 -1165
- package/dist/providers/docs/lib/constants.d.ts +18 -0
- package/dist/providers/docs/lib/constants.js +18 -0
- package/dist/providers/docs/lib/discovery.js +26 -0
- package/dist/providers/docs/lib/requests.js +48 -0
- package/dist/providers/docs/lib/schemas.js +168 -0
- package/dist/providers/docs/openapi.generated.js +16 -0
- package/dist/providers/docs/safety.js +54 -0
- package/dist/providers/docs/skill.generated.js +2 -6
- package/dist/providers/docs/tools.js +801 -0
- package/dist/providers/drive/api.js +177 -0
- package/dist/providers/drive/index.d.ts +1 -12
- package/dist/providers/drive/index.js +38 -1448
- package/dist/providers/drive/lib/constants.d.ts +6 -0
- package/dist/providers/drive/lib/constants.js +6 -0
- package/dist/providers/drive/lib/contracts.js +532 -0
- package/dist/providers/drive/lib/hatch.js +197 -0
- package/dist/providers/drive/openapi.generated.js +77 -0
- package/dist/providers/drive/safety.js +90 -0
- package/dist/providers/drive/skill.generated.js +2 -6
- package/dist/providers/drive/tools.js +1290 -0
- package/dist/providers/forms/api.js +71 -0
- package/dist/providers/forms/index.d.ts +1 -12
- package/dist/providers/forms/index.js +49 -1145
- package/dist/providers/forms/lib/constants.d.ts +14 -0
- package/dist/providers/forms/lib/constants.js +19 -0
- package/dist/providers/forms/lib/hatch.js +72 -0
- package/dist/providers/forms/openapi.generated.js +23 -0
- package/dist/providers/forms/safety.js +34 -0
- package/dist/providers/forms/skill.generated.js +2 -8
- package/dist/providers/forms/tools.js +1023 -0
- package/dist/providers/github/api.js +150 -0
- package/dist/providers/github/index.d.ts +4 -5
- package/dist/providers/github/index.js +30 -87
- package/dist/providers/github/{auth.d.ts → lib/auth.d.ts} +5 -3
- package/dist/providers/github/{auth.js → lib/auth.js} +40 -43
- package/dist/providers/github/lib/discovery.js +132 -0
- package/dist/providers/github/lib/operations.js +58 -0
- package/dist/providers/github/{scope.d.ts → lib/scope.d.ts} +2 -0
- package/dist/providers/github/{scope.js → lib/scope.js} +25 -1
- package/dist/providers/github/openapi.generated.js +1216 -0
- package/dist/providers/github/safety.js +216 -0
- package/dist/providers/github/skill.generated.js +2 -5
- package/dist/providers/github/tools.js +506 -0
- package/dist/providers/gmail/api.js +114 -0
- package/dist/providers/gmail/index.d.ts +1 -12
- package/dist/providers/gmail/index.js +73 -1621
- package/dist/providers/gmail/lib/constants.d.ts +13 -0
- package/dist/providers/gmail/lib/constants.js +16 -0
- package/dist/providers/gmail/lib/index.js +173 -0
- package/dist/providers/gmail/lib/schemas.js +176 -0
- package/dist/providers/gmail/lib/threading.js +120 -0
- package/dist/providers/gmail/openapi.generated.js +92 -0
- package/dist/providers/gmail/safety.js +21 -0
- package/dist/providers/gmail/skill.generated.js +2 -6
- package/dist/providers/gmail/tools.js +1435 -0
- package/dist/providers/overflow/api.js +1008 -0
- package/dist/providers/overflow/index.d.ts +1 -17
- package/dist/providers/overflow/index.js +60 -1488
- package/dist/providers/overflow/skill.generated.js +2 -5
- package/dist/providers/overflow/tools.js +873 -0
- package/dist/providers/planning-center/api.js +467 -0
- package/dist/providers/planning-center/index.d.ts +4 -35
- package/dist/providers/planning-center/index.js +69 -2445
- package/dist/providers/planning-center/lib/constants.d.ts +58 -0
- package/dist/providers/planning-center/lib/constants.js +83 -0
- package/dist/providers/planning-center/lib/discovery.js +40 -0
- package/dist/providers/planning-center/lib/guide.js +39 -0
- package/dist/providers/planning-center/lib/json-api.js +83 -0
- package/dist/providers/planning-center/lib/verdicts.js +1758 -0
- package/dist/providers/planning-center/openapi.generated.js +1764 -0
- package/dist/providers/planning-center/safety.js +188 -0
- package/dist/providers/planning-center/skill.generated.js +2 -27
- package/dist/providers/planning-center/tools.js +1010 -0
- package/dist/providers/quickbooks/api.js +299 -0
- package/dist/providers/quickbooks/index.d.ts +13 -0
- package/dist/providers/quickbooks/index.js +93 -0
- package/dist/providers/quickbooks/lib/query.js +118 -0
- package/dist/providers/quickbooks/lib/writes.js +168 -0
- package/dist/providers/quickbooks/safety.js +222 -0
- package/dist/providers/quickbooks/skill.generated.js +5 -0
- package/dist/providers/quickbooks/tools.js +117 -0
- package/dist/providers/sheets/api.js +133 -0
- package/dist/providers/sheets/index.d.ts +4 -16
- package/dist/providers/sheets/index.js +61 -1172
- package/dist/providers/sheets/lib/constants.d.ts +14 -0
- package/dist/providers/sheets/lib/constants.js +14 -0
- package/dist/providers/sheets/lib/hatch.js +163 -0
- package/dist/providers/sheets/lib/requests.js +105 -0
- package/dist/providers/sheets/lib/schemas.js +138 -0
- package/dist/providers/sheets/openapi.generated.js +30 -0
- package/dist/providers/sheets/safety.js +27 -0
- package/dist/providers/sheets/skill.generated.js +2 -7
- package/dist/providers/sheets/tools.js +733 -0
- package/dist/providers/slides/api.js +147 -0
- package/dist/providers/slides/index.d.ts +2 -10
- package/dist/providers/slides/index.js +48 -2555
- package/dist/providers/slides/lib/constants.d.ts +11 -0
- package/dist/providers/slides/lib/constants.js +11 -0
- package/dist/providers/slides/lib/requests.js +60 -0
- package/dist/providers/slides/lib/schemas.js +565 -0
- package/dist/providers/slides/openapi.generated.js +18 -0
- package/dist/providers/slides/skill.generated.js +2 -6
- package/dist/providers/slides/tools.js +1968 -0
- package/dist/providers/stripe/api.js +401 -0
- package/dist/providers/stripe/index.d.ts +5 -27
- package/dist/providers/stripe/index.js +74 -210
- package/dist/providers/stripe/lib/config.d.ts +4 -0
- package/dist/providers/stripe/lib/config.js +2 -0
- package/dist/providers/stripe/openapi.generated.js +4 -3
- package/dist/providers/stripe/{value-safety.js → safety.js} +5 -5
- package/dist/providers/stripe/skill.generated.js +2 -11
- package/dist/providers/stripe/tools.js +77 -0
- package/dist/providers/tithely/api.js +531 -0
- package/dist/providers/tithely/index.d.ts +17 -0
- package/dist/providers/tithely/index.js +86 -965
- package/dist/providers/tithely/skill.generated.js +2 -5
- package/dist/providers/tithely/tools.js +426 -0
- package/dist/providers/vercel/api.js +551 -0
- package/dist/providers/vercel/index.d.ts +9 -25
- package/dist/providers/vercel/index.js +65 -260
- package/dist/providers/vercel/lib/config.d.ts +2 -0
- package/dist/providers/vercel/lib/config.js +2 -0
- package/dist/providers/vercel/lib/index.js +117 -0
- package/dist/providers/vercel/lib/logs.js +100 -0
- package/dist/providers/vercel/lib/pin.js +270 -0
- package/dist/providers/vercel/openapi.generated.js +10 -5
- package/dist/providers/vercel/{value-safety.js → safety.js} +247 -38
- package/dist/providers/vercel/skill.generated.js +2 -7
- package/dist/providers/vercel/tools.js +642 -0
- package/dist/redaction-identity.js +47 -0
- package/dist/redaction-marker.js +125 -0
- package/dist/registry.d.ts +21 -15
- package/dist/registry.js +409 -376
- package/dist/routes/access-tokens.js +1 -1
- package/dist/routes/actor-labels.js +1 -1
- package/dist/routes/connect.js +1 -1
- package/dist/routes/dispatch.js +103 -0
- package/dist/routes/mcp.js +170 -79
- package/dist/routes/oauth.js +6 -1
- package/dist/routes/operator.js +2 -2
- package/dist/routes/shared.d.ts +5 -4
- package/dist/routes/shared.js +244 -177
- package/dist/routes/skills.js +10 -8
- package/dist/routes/ui-config.js +27 -3
- package/dist/routes/ui-data.js +21 -20
- package/dist/routes/ui.js +1 -1
- package/dist/runtime/call-admission.js +0 -26
- package/dist/runtime/call-entry.js +30 -0
- package/dist/runtime/call.js +552 -0
- package/dist/runtime/connector-scope.js +7 -0
- package/dist/runtime/credential-probe.js +34 -0
- package/dist/runtime/executor.js +40 -0
- package/dist/runtime/native-storage.js +63 -0
- package/dist/runtime/registry.js +27 -0
- package/dist/runtime/request-scope.js +72 -0
- package/dist/runtime/run.js +15 -7
- package/dist/runtime/shared-read.js +34 -5
- package/dist/runtime/storage.js +34 -21
- package/dist/sent-secrets.d.ts +194 -37
- package/dist/sent-secrets.js +1743 -340
- package/dist/server.js +5 -56
- package/dist/skills.js +322 -223
- package/dist/sqlite.js +15 -14
- package/dist/storage/d1-copy.js +255 -0
- package/dist/storage/d1-driver.js +15 -0
- package/dist/storage/keys.js +28 -0
- package/dist/storage/sql-driver.js +88 -0
- package/dist/storage/sql-native.js +544 -0
- package/dist/storage/sql-schema.d.ts +6 -0
- package/dist/storage/sql-schema.js +15 -0
- package/dist/storage/sql.d.ts +14 -20
- package/dist/storage/sql.js +29 -535
- package/dist/timeout.js +0 -26
- package/dist/tool-safety.js +20 -10
- package/dist/types.d.ts +9 -109
- package/dist/ui.d.ts +1 -1
- package/dist/ui.js +0 -1
- package/dist/upward.js +3 -0
- package/dist/usage-guide.js +3 -1
- package/dist/validate.d.ts +4 -2
- package/dist/validate.js +86 -9
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker.js +312 -292
- package/dist/workspace-subject.js +5 -1
- package/dist/write-outcome.js +454 -34
- package/documentation/README.md +28 -7
- package/documentation/architecture.md +2409 -255
- package/documentation/auth.md +867 -114
- package/documentation/code-mode.md +49 -16
- package/documentation/deploying.md +164 -20
- package/documentation/hosted-mcp-migration.md +460 -0
- package/documentation/integrating.md +129 -144
- package/documentation/meta-tools.md +409 -32
- package/documentation/operating.md +27 -3
- package/documentation/operator-tests.md +3 -0
- package/documentation/operator-ui.md +84 -48
- package/documentation/provider-auth-migration.md +16 -63
- package/documentation/provider-migration-0.29.md +10 -5
- package/documentation/provider-migration-0.35.md +975 -0
- package/documentation/providers.md +1347 -0
- package/examples/worker/AGENTS.md +23 -9
- package/examples/worker/README.md +254 -135
- package/examples/worker/alchemy.run.ts +116 -0
- package/examples/worker/gitignore +10 -0
- package/examples/worker/src/connecta.config.ts +23 -21
- package/examples/worker/wrangler.jsonc +14 -5
- package/guides/connecting-agents.md +157 -0
- package/guides/how-it-works.md +156 -0
- package/guides/safety-and-trust.md +153 -0
- package/package.json +30 -50
- package/templates/node/.env.example +3 -0
- package/templates/node/README.md +22 -2
- package/templates/node/docker-compose.yml +1 -0
- package/templates/node/package.json +3 -2
- package/templates/node/src/connecta.config.ts +8 -0
- package/dist/catalog-drift.d.ts +0 -138
- package/dist/catalog-drift.js +0 -578
- package/dist/operator-ui/agents.d.ts +0 -74
- package/dist/operator-ui/agents.js +0 -104
- package/dist/operator-ui/config-summary.js +0 -565
- package/dist/operator-ui/connection-map.js +0 -376
- package/dist/operator-ui/endpoints.js +0 -221
- package/dist/operator-ui/fix-prompts.d.ts +0 -18
- package/dist/operator-ui/fix-prompts.js +0 -87
- package/dist/operator-ui/health.js +0 -52
- package/dist/operator-ui/member-page.js +0 -97
- package/dist/operator-ui/overview.js +0 -123
- package/dist/operator-ui/setup-commands.js +0 -51
- package/dist/operator-ui/view.d.ts +0 -401
- package/dist/operator-ui/view.js +0 -705
- package/dist/providers/_shared/request-token.js +0 -83
- package/dist/providers/_shared/rest/dispatch.js +0 -41
- package/dist/providers/_shared/rest/operation-index.d.ts +0 -133
- package/dist/providers/_shared/rest/tools.d.ts +0 -141
- package/dist/providers/_shared/rest/tools.js +0 -569
- package/dist/providers/_shared/rest/value-safety.d.ts +0 -201
- package/dist/providers/basecamp/index.d.ts +0 -45
- package/dist/providers/basecamp/index.js +0 -997
- package/dist/providers/basecamp/skill.generated.js +0 -9
- package/dist/providers/cloudflare/rest.d.ts +0 -37
- package/dist/providers/cloudflare/rest.js +0 -1097
- package/dist/providers/github/hosted.js +0 -218
- package/dist/providers/github/rest.js +0 -321
- package/dist/providers/infisical/index.d.ts +0 -7
- package/dist/providers/infisical/index.js +0 -713
- package/dist/providers/infisical/skill.generated.js +0 -8
- package/dist/providers/intercom/index.d.ts +0 -24
- package/dist/providers/intercom/index.js +0 -121
- package/dist/providers/intercom/skill.generated.js +0 -8
- package/dist/providers/linear/index.d.ts +0 -55
- package/dist/providers/linear/index.js +0 -537
- package/dist/providers/linear/skill.generated.js +0 -8
- package/dist/providers/mixpanel/index.d.ts +0 -59
- package/dist/providers/mixpanel/index.js +0 -472
- package/dist/providers/mixpanel/operations.js +0 -20
- package/dist/providers/mixpanel/rest.js +0 -603
- package/dist/providers/mixpanel/skill.generated.js +0 -10
- package/dist/providers/mixpanel/value-safety.js +0 -16
- package/dist/providers/notion/index.d.ts +0 -67
- package/dist/providers/notion/index.js +0 -368
- package/dist/providers/notion/openapi.generated.js +0 -75
- package/dist/providers/notion/rest.d.ts +0 -24
- package/dist/providers/notion/rest.js +0 -1226
- package/dist/providers/notion/skill.generated.js +0 -11
- package/dist/providers/notion/value-safety.js +0 -56
- package/dist/providers/revenuecat/index.d.ts +0 -51
- package/dist/providers/revenuecat/index.js +0 -697
- package/dist/providers/revenuecat/skill.generated.js +0 -11
- package/dist/providers/stripe/rest.d.ts +0 -14
- package/dist/providers/stripe/rest.js +0 -402
- package/dist/providers/vercel/rest.d.ts +0 -20
- package/dist/providers/vercel/rest.js +0 -1020
- package/dist/providers/wispr-flow/index.d.ts +0 -14
- package/dist/providers/wispr-flow/index.js +0 -117
- package/dist/providers/wispr-flow/skill.generated.js +0 -8
- /package/dist/providers/github/{key.js → lib/key.js} +0 -0
package/dist/downstream-input.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import { RequestScope, requestOwner } from "./runtime/request-scope.js";
|
|
2
|
+
import { Effect } from "effect";
|
|
3
|
+
import { upwardStep } from "./upward.js";
|
|
4
|
+
import { authAwait, authCall, authInvoke, authFlow, authStorage, registerAuth, registerAuthMethod, authOwnMethod, } from "./auth/native.js";
|
|
5
|
+
import { runEdge } from "./runtime/run.js";
|
|
1
6
|
import { inputRequired, specTypeSchemas, ProtocolError, INVALID_PARAMS, CLIENT_CAPABILITIES_META_KEY, PROTOCOL_VERSION_META_KEY, } from "@modelcontextprotocol/server";
|
|
2
7
|
import { ConnectorCallError } from "./errors.js";
|
|
3
8
|
import { sentSecretsForRequest } from "./sent-secrets.js";
|
|
@@ -25,90 +30,106 @@ function invalidInput() {
|
|
|
25
30
|
throw new ConnectorCallError("input_required_invalid", "The downstream returned malformed or unsafe input requests.");
|
|
26
31
|
}
|
|
27
32
|
/** Capture opaque state before any result redaction or unwrapping can alter it. */
|
|
28
|
-
export
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (entries.length > MAX_REQUESTS)
|
|
40
|
-
throw new ConnectorCallError("input_required_limit", "The downstream requested more than 16 inputs.");
|
|
41
|
-
const inputRequests = Object.create(null);
|
|
42
|
-
const inputs = Object.create(null);
|
|
43
|
-
const elicitation = downstreamInputCapabilities(scope).elicitation;
|
|
44
|
-
for (const [index, [key, request]] of entries.entries()) {
|
|
45
|
-
if (!key || key.length > 256 || !stateObject(request))
|
|
46
|
-
return invalidInput();
|
|
47
|
-
if (request.method !== "elicitation/create") {
|
|
48
|
-
throw new ConnectorCallError("input_required_unsupported", "Connecta relays downstream form and URL elicitation only.");
|
|
49
|
-
}
|
|
50
|
-
if (!stateObject(request.params))
|
|
33
|
+
export function captureDownstreamInput(scope, connector, address, raw, secrets, write) {
|
|
34
|
+
return runEdge(captureDownstreamInputNative(scope, connector, address, raw, secrets, write));
|
|
35
|
+
}
|
|
36
|
+
const captureDownstreamInputNative = /* @__PURE__ */ registerAuth(captureDownstreamInput, function (scope, connector, address, raw, secrets, write) {
|
|
37
|
+
return authFlow(function* () {
|
|
38
|
+
if (!withinBudget(raw))
|
|
39
|
+
throw new ConnectorCallError("input_required_limit", "The downstream input payload exceeds 64 KiB.");
|
|
40
|
+
if (!stateObject(raw) ||
|
|
41
|
+
(raw.requestState !== undefined && typeof raw.requestState !== "string") ||
|
|
42
|
+
(raw.inputRequests !== undefined && !stateObject(raw.inputRequests)) ||
|
|
43
|
+
(raw.requestState === undefined && raw.inputRequests === undefined))
|
|
51
44
|
return invalidInput();
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
if (mode !== "form" && mode !== "url")
|
|
45
|
+
const entries = Object.entries(raw.inputRequests ?? {});
|
|
46
|
+
if (!entries.length && raw.requestState === undefined)
|
|
55
47
|
return invalidInput();
|
|
56
|
-
if (
|
|
57
|
-
throw new ConnectorCallError("
|
|
58
|
-
|
|
59
|
-
|
|
48
|
+
if (entries.length > MAX_REQUESTS)
|
|
49
|
+
throw new ConnectorCallError("input_required_limit", "The downstream requested more than 16 inputs.");
|
|
50
|
+
const inputRequests = Object.create(null);
|
|
51
|
+
const inputs = Object.create(null);
|
|
52
|
+
const elicitation = downstreamInputCapabilities(scope).elicitation;
|
|
53
|
+
for (const [index, [key, request]] of entries.entries()) {
|
|
54
|
+
if (!key || key.length > 256 || !stateObject(request))
|
|
60
55
|
return invalidInput();
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
// An opaque downstream nonce is retained. Credentials are never put
|
|
64
|
-
// in browser URLs; refusal preserves semantics instead of rewriting it.
|
|
65
|
-
if (url.protocol !== "https:" ||
|
|
66
|
-
url.username ||
|
|
67
|
-
url.password ||
|
|
68
|
-
[...params.url].some((char) => char.charCodeAt(0) <= 0x20 || char.charCodeAt(0) === 0x7f) ||
|
|
69
|
-
secrets.containsUrl(params.url) ||
|
|
70
|
-
secrets.redact(params.url) !== params.url)
|
|
71
|
-
return invalidInput();
|
|
56
|
+
if (request.method !== "elicitation/create") {
|
|
57
|
+
throw new ConnectorCallError("input_required_unsupported", "Connecta relays downstream form and URL elicitation only.");
|
|
72
58
|
}
|
|
73
|
-
|
|
59
|
+
if (!stateObject(request.params))
|
|
74
60
|
return invalidInput();
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
url: params.url,
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
const parsed = await specTypeSchemas.ElicitRequestFormParams["~standard"].validate(params);
|
|
83
|
-
if (parsed.issues)
|
|
84
|
-
return invalidInput();
|
|
85
|
-
// A schema controls the accepted answer. Refuse redaction changes rather
|
|
86
|
-
// than asking the host to submit different field names or enum values.
|
|
87
|
-
if (secrets.redact(parsed.value.requestedSchema) !== parsed.value.requestedSchema)
|
|
61
|
+
const params = request.params;
|
|
62
|
+
const mode = params.mode ?? "form";
|
|
63
|
+
if (mode !== "form" && mode !== "url")
|
|
88
64
|
return invalidInput();
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
65
|
+
if (!elicitation?.[mode])
|
|
66
|
+
throw new ConnectorCallError("input_required_unsupported", "The client did not declare support for the downstream elicitation kind.");
|
|
67
|
+
if (mode === "url") {
|
|
68
|
+
if (typeof params.message !== "string" || typeof params.url !== "string")
|
|
69
|
+
return invalidInput();
|
|
70
|
+
try {
|
|
71
|
+
const url = new URL(params.url);
|
|
72
|
+
// An opaque downstream nonce is retained. Credentials are never put
|
|
73
|
+
// in browser URLs; refusal preserves semantics instead of rewriting it.
|
|
74
|
+
if (url.protocol !== "https:" ||
|
|
75
|
+
url.username ||
|
|
76
|
+
url.password ||
|
|
77
|
+
[...params.url].some((char) => char.charCodeAt(0) <= 0x20 || char.charCodeAt(0) === 0x7f) ||
|
|
78
|
+
secrets.containsUrl(params.url) ||
|
|
79
|
+
secrets.redact(params.url) !== params.url)
|
|
80
|
+
return invalidInput();
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return invalidInput();
|
|
84
|
+
}
|
|
85
|
+
inputRequests[namespace(connector, index)] = inputRequired.elicitUrl({
|
|
86
|
+
message: `Downstream ${connector}: ${params.message}`,
|
|
87
|
+
url: params.url,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
const parsed = yield* upwardStep(authInvoke(specTypeSchemas.ElicitRequestFormParams["~standard"], "validate", [params]));
|
|
92
|
+
if (parsed.issues)
|
|
93
|
+
return invalidInput();
|
|
94
|
+
// A schema controls the accepted answer. Refuse redaction changes rather
|
|
95
|
+
// than asking the host to submit different field names or enum values.
|
|
96
|
+
if (secrets.changes(parsed.value.requestedSchema))
|
|
97
|
+
return invalidInput();
|
|
98
|
+
inputRequests[namespace(connector, index)] = inputRequired.elicit({
|
|
99
|
+
message: `Downstream ${connector}: ${parsed.value.message}`,
|
|
100
|
+
requestedSchema: parsed.value.requestedSchema,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
inputs[namespace(connector, index)] = { key, mode };
|
|
93
104
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
105
|
+
// Opaque state is private even when a downstream echoes it in a prompt,
|
|
106
|
+
// URL, or schema. Keep the continuation intact and refuse the public prompt.
|
|
107
|
+
assertDownstreamOutputSafe(scope, inputRequests);
|
|
108
|
+
assertNoPrivateStateEchoes(inputRequests, raw.requestState !== undefined ? [raw.requestState] : []);
|
|
109
|
+
if (pendingInputs.has(scope))
|
|
110
|
+
return invalidInput();
|
|
111
|
+
pendingInputs.set(scope, {
|
|
112
|
+
connector,
|
|
113
|
+
address,
|
|
114
|
+
write,
|
|
115
|
+
...(raw.requestState !== undefined ? { requestState: raw.requestState } : {}),
|
|
116
|
+
inputRequests,
|
|
117
|
+
inputs,
|
|
118
|
+
});
|
|
108
119
|
});
|
|
109
|
-
}
|
|
110
|
-
function failure(code, message) {
|
|
111
|
-
|
|
120
|
+
});
|
|
121
|
+
function failure(code, message, write = false) {
|
|
122
|
+
// A handler asks for input from inside its run: a suspended write may
|
|
123
|
+
// already have applied, so it is never told to start over (INV-9).
|
|
124
|
+
const error = write
|
|
125
|
+
? {
|
|
126
|
+
code,
|
|
127
|
+
message: `${message} Whether the write took effect is unknown; check its target before any fresh call.`,
|
|
128
|
+
retryable: false,
|
|
129
|
+
writeOutcome: "unknown",
|
|
130
|
+
}
|
|
131
|
+
: { code, message, retryable: false };
|
|
132
|
+
const structuredContent = { ok: false, error };
|
|
112
133
|
return { content: [{ type: "text", text: JSON.stringify(structuredContent) }], structuredContent, isError: true };
|
|
113
134
|
}
|
|
114
135
|
/** MCP-only continuation coordination. No downstream prompt enters operator records. */
|
|
@@ -135,191 +156,257 @@ export class DownstreamElicitation {
|
|
|
135
156
|
}
|
|
136
157
|
: {});
|
|
137
158
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if (!vault?.open ||
|
|
141
|
-
!principal ||
|
|
142
|
-
typeof wrapper.connector !== "string" ||
|
|
143
|
-
!registry.getConnector(wrapper.connector) ||
|
|
144
|
-
typeof wrapper.sealed !== "string" ||
|
|
145
|
-
wrapper.sealed.length > MAX_RELAY_STATE_CHARS)
|
|
146
|
-
return invalidRequestState();
|
|
147
|
-
let state;
|
|
148
|
-
try {
|
|
149
|
-
state = JSON.parse(await vault.open(wrapper.connector, PURPOSE, wrapper.sealed));
|
|
150
|
-
}
|
|
151
|
-
catch {
|
|
152
|
-
return invalidRequestState();
|
|
153
|
-
}
|
|
154
|
-
if (!stateObject(state) ||
|
|
155
|
-
state.version !== 2 ||
|
|
156
|
-
state.kind !== "downstream" ||
|
|
157
|
-
state.principal !== principal ||
|
|
158
|
-
state.endpoint !== endpoint ||
|
|
159
|
-
state.connector !== wrapper.connector ||
|
|
160
|
-
typeof state.address !== "string" ||
|
|
161
|
-
typeof state.target !== "string" ||
|
|
162
|
-
!state.target.startsWith(`${state.connector}.`) ||
|
|
163
|
-
!["call_tool", "call_destructive_tool"].includes(String(state.tool)) ||
|
|
164
|
-
typeof state.digest !== "string" ||
|
|
165
|
-
!/^[a-f0-9]{64}$/.test(state.digest) ||
|
|
166
|
-
!Number.isSafeInteger(state.expiresAt) ||
|
|
167
|
-
state.expiresAt <= Date.now() ||
|
|
168
|
-
state.expiresAt > Date.now() + REQUEST_STATE_TTL_MS ||
|
|
169
|
-
!Number.isInteger(state.round) ||
|
|
170
|
-
state.round < 1 ||
|
|
171
|
-
state.round > MAX_INPUT_ROUNDS ||
|
|
172
|
-
typeof state.nonce !== "string" ||
|
|
173
|
-
!/^[a-f0-9-]{36}$/.test(state.nonce) ||
|
|
174
|
-
(state.requestState !== undefined && typeof state.requestState !== "string") ||
|
|
175
|
-
!Array.isArray(state.previousStates) ||
|
|
176
|
-
state.previousStates.length >= state.round ||
|
|
177
|
-
!state.previousStates.every((value) => typeof value === "string") ||
|
|
178
|
-
!withinBudget([...state.previousStates, ...(state.requestState !== undefined ? [state.requestState] : [])]) ||
|
|
179
|
-
!stateObject(state.inputs) ||
|
|
180
|
-
Object.keys(state.inputs).length > MAX_REQUESTS ||
|
|
181
|
-
!Object.entries(state.inputs).every(([wireKey, binding], index) => wireKey === namespace(wrapper.connector, index) &&
|
|
182
|
-
stateObject(binding) &&
|
|
183
|
-
typeof binding.key === "string" &&
|
|
184
|
-
binding.key.length > 0 &&
|
|
185
|
-
binding.key.length <= 256 &&
|
|
186
|
-
(binding.mode === "url" || binding.mode === "form")) ||
|
|
187
|
-
context.mcpReq.method !== "tools/call" ||
|
|
188
|
-
context.http?.req?.headers.get("Mcp-Name") !== state.tool)
|
|
189
|
-
return invalidRequestState();
|
|
190
|
-
return state;
|
|
159
|
+
verify(wrapper, context) {
|
|
160
|
+
return runEdge(authOwnMethod(DownstreamElicitation.prototype, this, "verify", [wrapper, context]));
|
|
191
161
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const response = context.mcpReq.inputResponses?.[wireKey];
|
|
201
|
-
if (response === undefined)
|
|
202
|
-
continue;
|
|
203
|
-
const parsed = await specTypeSchemas.ElicitResult["~standard"].validate(response);
|
|
204
|
-
if (parsed.issues)
|
|
205
|
-
throw new ProtocolError(INVALID_PARAMS, "Invalid downstream input response", {
|
|
206
|
-
reason: "invalid_input_response",
|
|
207
|
-
});
|
|
208
|
-
// URL acknowledgements and decline/cancel never carry form data.
|
|
209
|
-
responses[key] =
|
|
210
|
-
mode === "url" || parsed.value.action !== "accept" ? { action: parsed.value.action } : parsed.value;
|
|
211
|
-
}
|
|
212
|
-
if (!withinBudget(responses))
|
|
213
|
-
throw new ProtocolError(INVALID_PARAMS, "Input responses exceed 64 KiB", { reason: "invalid_input_response" });
|
|
214
|
-
const storage = this.options.registry.contextFor(state.connector, this.options.endpoint, this.options.requestScope).storage;
|
|
215
|
-
// Claim before any downstream dispatch, including decline/cancel. A failed
|
|
216
|
-
// or ambiguous continuation must not leave a reusable write permission.
|
|
217
|
-
if (!(await storage.compareAndSet(inputRetryKeys.used(state.nonce), null, "used", {
|
|
218
|
-
ttlSeconds: REQUEST_STATE_TTL_MS / 1000,
|
|
219
|
-
})))
|
|
220
|
-
return invalidRequestState();
|
|
221
|
-
bindDownstreamContinuation(this.options.requestScope, {
|
|
222
|
-
connector: state.connector,
|
|
223
|
-
address: state.target,
|
|
224
|
-
write: state.tool === "call_destructive_tool",
|
|
225
|
-
privateStates: [...state.previousStates, ...(state.requestState !== undefined ? [state.requestState] : [])],
|
|
226
|
-
input: {
|
|
227
|
-
...(state.requestState !== undefined ? { requestState: state.requestState } : {}),
|
|
228
|
-
inputResponses: responses,
|
|
229
|
-
},
|
|
230
|
-
});
|
|
231
|
-
try {
|
|
232
|
-
const result = await this.finish(tool, args, context, await operation(), state);
|
|
233
|
-
if (!result.isError || !stateObject(result.structuredContent))
|
|
234
|
-
return result;
|
|
235
|
-
const error = result.structuredContent.error;
|
|
236
|
-
if (!stateObject(error))
|
|
237
|
-
return result;
|
|
238
|
-
// The nonce is spent even when the downstream failure would normally
|
|
239
|
-
// allow a retry. Preserve write reconciliation and auth prerequisites.
|
|
240
|
-
const echoed = downstreamArgumentEcho(this.options.requestScope);
|
|
241
|
-
const structuredContent = {
|
|
242
|
-
...result.structuredContent,
|
|
243
|
-
error: {
|
|
244
|
-
...error,
|
|
245
|
-
retryable: false,
|
|
246
|
-
...(state.tool === "call_tool" && error.nextAction === undefined && error.retry === undefined
|
|
247
|
-
? {
|
|
248
|
-
nextAction: {
|
|
249
|
-
tool: state.tool,
|
|
250
|
-
arguments: { address: state.address, ...echoed },
|
|
251
|
-
purpose: "Re-issue the original direct call without requestState or inputResponses to start a fresh input round. Do not resend this continuation." +
|
|
252
|
-
(echoed.argsRedacted
|
|
253
|
-
? " Re-send the original arguments; sensitive fields are omitted."
|
|
254
|
-
: args.args !== undefined && !("args" in echoed)
|
|
255
|
-
? " Use the exact arguments you sent; they could not be echoed safely."
|
|
256
|
-
: ""),
|
|
257
|
-
},
|
|
258
|
-
}
|
|
259
|
-
: {}),
|
|
260
|
-
},
|
|
261
|
-
};
|
|
262
|
-
return { ...result, structuredContent, content: [{ type: "text", text: JSON.stringify(structuredContent) }] };
|
|
263
|
-
}
|
|
264
|
-
finally {
|
|
265
|
-
clearDownstreamContinuation(this.options.requestScope);
|
|
266
|
-
}
|
|
162
|
+
resume(state, tool, args, context, operation) {
|
|
163
|
+
return runEdge(authOwnMethod(DownstreamElicitation.prototype, this, "resume", [
|
|
164
|
+
state,
|
|
165
|
+
tool,
|
|
166
|
+
args,
|
|
167
|
+
context,
|
|
168
|
+
operation,
|
|
169
|
+
]));
|
|
267
170
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
171
|
+
finish(tool, args, context, result, previous) {
|
|
172
|
+
return runEdge(authOwnMethod(DownstreamElicitation.prototype, this, "finish", [
|
|
173
|
+
tool,
|
|
174
|
+
args,
|
|
175
|
+
context,
|
|
176
|
+
result,
|
|
177
|
+
previous,
|
|
178
|
+
]));
|
|
179
|
+
}
|
|
180
|
+
static {
|
|
181
|
+
registerAuthMethod(DownstreamElicitation.prototype, "finish", function (tool, args, context, result, previous) {
|
|
182
|
+
const self = this;
|
|
183
|
+
return authAwait(authFlow(function* () {
|
|
184
|
+
const scope = self.options.requestScope;
|
|
185
|
+
const pending = pendingInputs.get(scope);
|
|
186
|
+
pendingInputs.delete(scope);
|
|
187
|
+
if (!pending)
|
|
188
|
+
return result;
|
|
189
|
+
const { vault, principal, endpoint } = self.options;
|
|
190
|
+
if (!modernRequests.has(scope) ||
|
|
191
|
+
!vault?.requestStateKey ||
|
|
192
|
+
!vault.seal ||
|
|
193
|
+
!vault.open ||
|
|
194
|
+
!principal ||
|
|
195
|
+
!["call_tool", "call_destructive_tool"].includes(tool))
|
|
196
|
+
return failure("input_required_unsupported", "Downstream input requires an authenticated direct call and a sealing vault.", pending.write);
|
|
197
|
+
if ((previous?.round ?? 0) >= MAX_INPUT_ROUNDS)
|
|
198
|
+
return failure("input_required_round_limit", `The downstream still requires input after three rounds.${pending.write ? "" : " Start a new direct request."}`, pending.write);
|
|
199
|
+
const previousStates = [
|
|
200
|
+
...new Set([
|
|
201
|
+
...(previous?.previousStates ?? []),
|
|
202
|
+
...(previous?.requestState !== undefined ? [previous.requestState] : []),
|
|
203
|
+
]),
|
|
204
|
+
].filter((value) => value !== pending.requestState);
|
|
205
|
+
if (!withinBudget([...previousStates, ...(pending.requestState !== undefined ? [pending.requestState] : [])])) {
|
|
206
|
+
return failure("input_required_limit", "The downstream private state history exceeds 64 KiB.", pending.write);
|
|
207
|
+
}
|
|
208
|
+
const state = {
|
|
209
|
+
version: 2,
|
|
210
|
+
kind: "downstream",
|
|
211
|
+
principal,
|
|
212
|
+
endpoint,
|
|
213
|
+
connector: pending.connector,
|
|
214
|
+
address: String(args.address),
|
|
215
|
+
target: pending.address,
|
|
216
|
+
tool: tool,
|
|
217
|
+
wireTool: context.http?.req?.headers.get("Mcp-Name") ?? tool,
|
|
218
|
+
digest: yield* upwardStep(authCall(requestDigest, [tool, args])),
|
|
219
|
+
round: (previous?.round ?? 0) + 1,
|
|
220
|
+
expiresAt: previous?.expiresAt ?? Date.now() + REQUEST_STATE_TTL_MS,
|
|
221
|
+
nonce: crypto.randomUUID(),
|
|
222
|
+
...(pending.requestState !== undefined ? { requestState: pending.requestState } : {}),
|
|
223
|
+
previousStates,
|
|
224
|
+
inputs: pending.inputs,
|
|
225
|
+
};
|
|
226
|
+
const requestState = yield* upwardStep(authInvoke(self.options, "mint", [
|
|
227
|
+
{
|
|
228
|
+
version: 2,
|
|
229
|
+
kind: "downstream",
|
|
230
|
+
connector: pending.connector,
|
|
231
|
+
sealed: yield* upwardStep(authInvoke(vault, "seal", [pending.connector, PURPOSE, JSON.stringify(state)])),
|
|
232
|
+
},
|
|
233
|
+
context,
|
|
234
|
+
]));
|
|
235
|
+
if (requestState.length > MAX_RELAY_STATE_CHARS)
|
|
236
|
+
return failure("input_required_limit", "The sealed downstream state exceeds 128 KiB.", pending.write);
|
|
237
|
+
// Authenticated, encrypted Connecta state, never the vendor's plaintext
|
|
238
|
+
// state: a coincidental match must not corrupt it.
|
|
239
|
+
sentSecretsForRequest(scope).mintedState(requestState);
|
|
240
|
+
// Opaque echoes and mutable schemas/URLs were refused during capture.
|
|
241
|
+
// Downstream prompt messages are prose under the payload rules.
|
|
242
|
+
return sentSecretsForRequest(scope).redact(inputRequired({
|
|
243
|
+
...(Object.keys(pending.inputRequests).length ? { inputRequests: pending.inputRequests } : {}),
|
|
244
|
+
requestState,
|
|
245
|
+
}));
|
|
246
|
+
}), context.mcpReq.signal).pipe(Effect.provideService(RequestScope, requestOwner(this.options.requestScope)));
|
|
247
|
+
}, DownstreamElicitation.prototype.finish);
|
|
248
|
+
registerAuthMethod(DownstreamElicitation.prototype, "resume", function (state, tool, args, context, operation) {
|
|
249
|
+
const self = this;
|
|
250
|
+
return authAwait(authFlow(function* () {
|
|
251
|
+
if (state.wireTool !== context.http?.req?.headers.get("Mcp-Name") ||
|
|
252
|
+
state.tool !== tool ||
|
|
253
|
+
state.address !== args.address ||
|
|
254
|
+
state.digest !== (yield* upwardStep(authCall(requestDigest, [tool, args]))))
|
|
255
|
+
return failure("invalid_request_state", "Invalid or expired requestState");
|
|
256
|
+
const responses = Object.create(null);
|
|
257
|
+
const elicitation = downstreamInputCapabilities(self.options.requestScope).elicitation;
|
|
258
|
+
for (const [wireKey, { key, mode }] of Object.entries(state.inputs)) {
|
|
259
|
+
if (!elicitation?.[mode])
|
|
260
|
+
return failure("input_required_unsupported", "The client did not declare support for this continuation's input kind.");
|
|
261
|
+
const response = context.mcpReq.inputResponses?.[wireKey];
|
|
262
|
+
if (response === undefined)
|
|
263
|
+
continue;
|
|
264
|
+
const parsed = yield* upwardStep(authInvoke(specTypeSchemas.ElicitResult["~standard"], "validate", [response]));
|
|
265
|
+
if (parsed.issues)
|
|
266
|
+
throw new ProtocolError(INVALID_PARAMS, "Invalid downstream input response", {
|
|
267
|
+
reason: "invalid_input_response",
|
|
268
|
+
});
|
|
269
|
+
// URL acknowledgements and decline/cancel never carry form data.
|
|
270
|
+
responses[key] =
|
|
271
|
+
mode === "url" || parsed.value.action !== "accept" ? { action: parsed.value.action } : parsed.value;
|
|
272
|
+
}
|
|
273
|
+
if (!withinBudget(responses))
|
|
274
|
+
throw new ProtocolError(INVALID_PARAMS, "Input responses exceed 64 KiB", {
|
|
275
|
+
reason: "invalid_input_response",
|
|
276
|
+
});
|
|
277
|
+
const storage = self.options.registry.contextFor(state.connector, self.options.endpoint, self.options.requestScope).storage;
|
|
278
|
+
// Claim before any downstream dispatch, including decline/cancel. A failed
|
|
279
|
+
// or ambiguous continuation must not leave a reusable write permission.
|
|
280
|
+
if (!(yield* upwardStep(authStorage(storage, "compareAndSet", [
|
|
281
|
+
inputRetryKeys.used(state.nonce),
|
|
282
|
+
null,
|
|
283
|
+
"used",
|
|
284
|
+
{
|
|
285
|
+
ttlSeconds: REQUEST_STATE_TTL_MS / 1000,
|
|
286
|
+
},
|
|
287
|
+
]))))
|
|
288
|
+
return invalidRequestState();
|
|
289
|
+
bindDownstreamContinuation(self.options.requestScope, {
|
|
290
|
+
connector: state.connector,
|
|
291
|
+
address: state.target,
|
|
292
|
+
write: state.tool === "call_destructive_tool",
|
|
293
|
+
privateStates: [
|
|
294
|
+
...state.previousStates,
|
|
295
|
+
...(state.requestState !== undefined ? [state.requestState] : []),
|
|
296
|
+
],
|
|
297
|
+
input: {
|
|
298
|
+
...(state.requestState !== undefined ? { requestState: state.requestState } : {}),
|
|
299
|
+
inputResponses: responses,
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
try {
|
|
303
|
+
const result = yield* upwardStep(authInvoke(self, "finish", [
|
|
304
|
+
tool,
|
|
305
|
+
args,
|
|
306
|
+
context,
|
|
307
|
+
yield* upwardStep(authCall(operation, [])),
|
|
308
|
+
state,
|
|
309
|
+
]));
|
|
310
|
+
if (!result.isError || !stateObject(result.structuredContent))
|
|
311
|
+
return result;
|
|
312
|
+
const error = result.structuredContent.error;
|
|
313
|
+
if (!stateObject(error))
|
|
314
|
+
return result;
|
|
315
|
+
// The nonce is spent even when the downstream failure would normally
|
|
316
|
+
// allow a retry. Preserve write reconciliation and auth prerequisites.
|
|
317
|
+
const echoed = downstreamArgumentEcho(self.options.requestScope);
|
|
318
|
+
const structuredContent = {
|
|
319
|
+
...result.structuredContent,
|
|
320
|
+
error: {
|
|
321
|
+
...error,
|
|
322
|
+
retryable: false,
|
|
323
|
+
...(state.tool === "call_tool" && error.nextAction === undefined && error.retry === undefined
|
|
324
|
+
? {
|
|
325
|
+
nextAction: {
|
|
326
|
+
tool: state.tool,
|
|
327
|
+
arguments: { address: state.address, ...echoed },
|
|
328
|
+
purpose: "Re-issue the original direct call without requestState or inputResponses to start a fresh input round. Do not resend this continuation." +
|
|
329
|
+
(echoed.argsRedacted
|
|
330
|
+
? " Re-send the original arguments; sensitive fields are omitted."
|
|
331
|
+
: args.args !== undefined && !("args" in echoed)
|
|
332
|
+
? " Use the exact arguments you sent; they could not be echoed safely."
|
|
333
|
+
: ""),
|
|
334
|
+
},
|
|
335
|
+
}
|
|
336
|
+
: {}),
|
|
337
|
+
},
|
|
338
|
+
};
|
|
339
|
+
return {
|
|
340
|
+
...result,
|
|
341
|
+
structuredContent,
|
|
342
|
+
content: [{ type: "text", text: JSON.stringify(structuredContent) }],
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
finally {
|
|
346
|
+
clearDownstreamContinuation(self.options.requestScope);
|
|
347
|
+
}
|
|
348
|
+
}), context.mcpReq.signal).pipe(Effect.provideService(RequestScope, requestOwner(this.options.requestScope)));
|
|
349
|
+
}, DownstreamElicitation.prototype.resume);
|
|
350
|
+
registerAuthMethod(DownstreamElicitation.prototype, "verify", function (wrapper, context) {
|
|
351
|
+
const self = this;
|
|
352
|
+
return authAwait(authFlow(function* () {
|
|
353
|
+
const { vault, registry, principal, endpoint } = self.options;
|
|
354
|
+
if (!vault?.open ||
|
|
355
|
+
!principal ||
|
|
356
|
+
typeof wrapper.connector !== "string" ||
|
|
357
|
+
!registry.getConnector(wrapper.connector) ||
|
|
358
|
+
typeof wrapper.sealed !== "string" ||
|
|
359
|
+
wrapper.sealed.length > MAX_RELAY_STATE_CHARS)
|
|
360
|
+
return invalidRequestState();
|
|
361
|
+
let state;
|
|
362
|
+
try {
|
|
363
|
+
state = JSON.parse(yield* upwardStep(authInvoke(vault, "open", [wrapper.connector, PURPOSE, wrapper.sealed])));
|
|
364
|
+
}
|
|
365
|
+
catch {
|
|
366
|
+
return invalidRequestState();
|
|
367
|
+
}
|
|
368
|
+
if (!stateObject(state) ||
|
|
369
|
+
state.version !== 2 ||
|
|
370
|
+
state.kind !== "downstream" ||
|
|
371
|
+
state.principal !== principal ||
|
|
372
|
+
state.endpoint !== endpoint ||
|
|
373
|
+
state.connector !== wrapper.connector ||
|
|
374
|
+
typeof state.address !== "string" ||
|
|
375
|
+
typeof state.target !== "string" ||
|
|
376
|
+
!state.target.startsWith(`${state.connector}.`) ||
|
|
377
|
+
!["call_tool", "call_destructive_tool"].includes(String(state.tool)) ||
|
|
378
|
+
typeof state.wireTool !== "string" ||
|
|
379
|
+
typeof state.digest !== "string" ||
|
|
380
|
+
!/^[a-f0-9]{64}$/.test(state.digest) ||
|
|
381
|
+
!Number.isSafeInteger(state.expiresAt) ||
|
|
382
|
+
state.expiresAt <= Date.now() ||
|
|
383
|
+
state.expiresAt > Date.now() + REQUEST_STATE_TTL_MS ||
|
|
384
|
+
!Number.isInteger(state.round) ||
|
|
385
|
+
state.round < 1 ||
|
|
386
|
+
state.round > MAX_INPUT_ROUNDS ||
|
|
387
|
+
typeof state.nonce !== "string" ||
|
|
388
|
+
!/^[a-f0-9-]{36}$/.test(state.nonce) ||
|
|
389
|
+
(state.requestState !== undefined && typeof state.requestState !== "string") ||
|
|
390
|
+
!Array.isArray(state.previousStates) ||
|
|
391
|
+
state.previousStates.length >= state.round ||
|
|
392
|
+
!state.previousStates.every((value) => typeof value === "string") ||
|
|
393
|
+
!withinBudget([
|
|
394
|
+
...state.previousStates,
|
|
395
|
+
...(state.requestState !== undefined ? [state.requestState] : []),
|
|
396
|
+
]) ||
|
|
397
|
+
!stateObject(state.inputs) ||
|
|
398
|
+
Object.keys(state.inputs).length > MAX_REQUESTS ||
|
|
399
|
+
!Object.entries(state.inputs).every(([wireKey, binding], index) => wireKey === namespace(wrapper.connector, index) &&
|
|
400
|
+
stateObject(binding) &&
|
|
401
|
+
typeof binding.key === "string" &&
|
|
402
|
+
binding.key.length > 0 &&
|
|
403
|
+
binding.key.length <= 256 &&
|
|
404
|
+
(binding.mode === "url" || binding.mode === "form")) ||
|
|
405
|
+
context.mcpReq.method !== "tools/call" ||
|
|
406
|
+
context.http?.req?.headers.get("Mcp-Name") !== state.wireTool)
|
|
407
|
+
return invalidRequestState();
|
|
408
|
+
return state;
|
|
409
|
+
}), context.mcpReq.signal).pipe(Effect.provideService(RequestScope, requestOwner(this.options.requestScope)));
|
|
410
|
+
}, DownstreamElicitation.prototype.verify);
|
|
324
411
|
}
|
|
325
412
|
}
|