@redocly/realm 0.138.0-next.1 → 0.138.0-next.11
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 +191 -0
- package/dist/bin.js +0 -0
- package/dist/cli/build/analytics/collect-analytics.js +1 -1
- package/dist/cli/build/analytics/collectors/get-sidebars-usage.d.ts +12 -0
- package/dist/cli/build/analytics/collectors/get-sidebars-usage.js +1 -0
- package/dist/cli/build/index.js +1 -1
- package/dist/cli/develop.js +1 -1
- package/dist/cli/eject/resolveEjectParams.js +1 -1
- package/dist/cli/generate/string-utils.js +1 -1
- package/dist/cli/stats/collectors/extension-sampling.js +1 -1
- package/dist/cli/translations/collect-config-translations.js +1 -1
- package/dist/client/App.js +1 -1
- package/dist/client/TestProvider.d.ts +3 -1
- package/dist/client/TestProvider.js +1 -1
- package/dist/client/app/Link.js +1 -1
- package/dist/client/app/ReviewComments/ReviewComments.js +2 -2
- package/dist/client/app/Sidebar/Sidebar.js +1 -1
- package/dist/client/app/Sidebar/useSidebarItems.js +1 -1
- package/dist/client/app/hooks/codeHighlight/treeview-transformer.js +1 -1
- package/dist/client/app/hooks/codeHighlight/useCodeHighlight.js +1 -1
- package/dist/client/app/hooks/index.d.ts +1 -0
- package/dist/client/app/hooks/index.js +1 -1
- package/dist/client/app/hooks/markdown/useMarkdownText.js +1 -1
- package/dist/client/app/hooks/useConsent.d.ts +11 -0
- package/dist/client/app/hooks/useConsent.js +1 -0
- package/dist/client/app/l10n/L10nContext.d.ts +10 -0
- package/dist/client/app/l10n/L10nContext.js +1 -0
- package/dist/client/app/l10n/hooks/useL10n.js +1 -1
- package/dist/client/app/l10n/hooks/useTranslate.js +1 -1
- package/dist/client/app/l10n/hooks/useTranslationKeys.d.ts +2 -0
- package/dist/client/app/l10n/hooks/useTranslationKeys.js +1 -1
- package/dist/client/app/l10n/ssrL10n.d.ts +4 -0
- package/dist/client/app/l10n/ssrL10n.js +1 -0
- package/dist/client/app/markdoc/custom-components/openapi/openapi-example.js +1 -1
- package/dist/client/app/markdoc/custom-components/openapi/openapi-response-sample.js +1 -1
- package/dist/client/app/markdoc/custom-components/openapi/replay-openapi.js +1 -1
- package/dist/client/app/markdoc/hooks/use-store.d.ts +2 -3
- package/dist/client/app/pages/DevLogin/DevLoginForm.js +1 -1
- package/dist/client/app/pages/Invite/Invite.js +1 -1
- package/dist/client/app/search/index.d.ts +1 -1
- package/dist/client/app/search/index.js +1 -1
- package/dist/client/app/search/useSearch.d.ts +3 -0
- package/dist/client/app/search/useSearch.js +1 -1
- package/dist/client/app/webmcp/index.d.ts +2 -0
- package/dist/client/app/webmcp/index.js +1 -0
- package/dist/client/app/webmcp/tools/api-tools.d.ts +8 -0
- package/dist/client/app/webmcp/tools/api-tools.js +1 -0
- package/dist/client/app/webmcp/tools/shared.d.ts +13 -0
- package/dist/client/app/webmcp/tools/shared.js +1 -0
- package/dist/client/app/webmcp/tools/site-tools.d.ts +5 -0
- package/dist/client/app/webmcp/tools/site-tools.js +3 -0
- package/dist/client/app/webmcp/types.d.ts +36 -0
- package/dist/client/app/webmcp/types.js +0 -0
- package/dist/client/app/webmcp/useWebMcpTools.d.ts +9 -0
- package/dist/client/app/webmcp/useWebMcpTools.js +1 -0
- package/dist/client/providers/page-data/hooks.d.ts +2 -0
- package/dist/client/providers/page-data/hooks.js +1 -1
- package/dist/client/providers/theme/ThemeDataProvider.js +1 -1
- package/dist/client/runtime/loader.js +1 -1
- package/dist/client/server-entry.d.ts +2 -1
- package/dist/client/server-entry.js +1 -1
- package/dist/client/templates/api-docs/helpers.js +1 -1
- package/dist/client/templates/api-docs/template.js +7 -3
- package/dist/client/utils/catalog/parse-filter-query.js +1 -1
- package/dist/client/{app/Sidebar/helpers → utils}/filter-out-versioned-items.d.ts +12 -2
- package/dist/client/utils/filter-out-versioned-items.js +1 -0
- package/dist/client/utils/navigation-tabs.d.ts +14 -0
- package/dist/client/utils/navigation-tabs.js +1 -0
- package/dist/compiled/svgo/svgo-node.js +39 -39
- package/dist/constants/common.d.ts +1 -0
- package/dist/constants/common.js +1 -1
- package/dist/constants/l10n/langs/ar.js +1 -1
- package/dist/constants/l10n/langs/de.js +1 -1
- package/dist/constants/l10n/langs/en.js +1 -1
- package/dist/constants/l10n/langs/es.js +1 -1
- package/dist/constants/l10n/langs/fr.js +1 -1
- package/dist/constants/l10n/langs/hi.js +1 -1
- package/dist/constants/l10n/langs/it.js +1 -1
- package/dist/constants/l10n/langs/ja.js +1 -1
- package/dist/constants/l10n/langs/ko.js +1 -1
- package/dist/constants/l10n/langs/pl.js +1 -1
- package/dist/constants/l10n/langs/pt-BR.js +1 -1
- package/dist/constants/l10n/langs/pt.js +1 -1
- package/dist/constants/l10n/langs/ru.js +1 -1
- package/dist/constants/l10n/langs/uk.js +1 -1
- package/dist/constants/l10n/langs/zh.js +1 -1
- package/dist/markdoc/nodes/table.d.ts +2 -0
- package/dist/markdoc/nodes/table.js +1 -1
- package/dist/server/config/env-config.d.ts +0 -6
- package/dist/server/config/env-schema.d.ts +0 -15
- package/dist/server/config/env-schemas/feature-flags.d.ts +0 -12
- package/dist/server/config/env-schemas/feature-flags.js +1 -1
- package/dist/server/constants/common.d.ts +3 -1
- package/dist/server/constants/common.js +1 -1
- package/dist/server/constants/entitlements.js +1 -1
- package/dist/server/constants/plugins/consent.d.ts +3 -0
- package/dist/server/constants/plugins/consent.js +1 -0
- package/dist/server/constants/plugins/search.js +1 -1
- package/dist/server/esbuild/esbuild-logger.js +3 -3
- package/dist/server/plugins/analytics/adobe/index.js +1 -1
- package/dist/server/plugins/analytics/amplitude/browser-hooks.js +1 -1
- package/dist/server/plugins/analytics/amplitude/index.js +1 -1
- package/dist/server/plugins/analytics/fullstory/index.js +1 -1
- package/dist/server/plugins/analytics/ga/index.js +1 -1
- package/dist/server/plugins/analytics/gtm/browser-hooks.js +1 -1
- package/dist/server/plugins/analytics/gtm/index.js +1 -1
- package/dist/server/plugins/analytics/heap/browser-hooks.js +1 -1
- package/dist/server/plugins/analytics/rudderstack/index.js +1 -1
- package/dist/server/plugins/analytics/segment/index.js +1 -1
- package/dist/server/plugins/api-docs/index.js +1 -1
- package/dist/server/plugins/api-docs/utils/apply-nav-item-to-root.d.ts +9 -0
- package/dist/server/plugins/api-docs/utils/apply-nav-item-to-root.js +1 -0
- package/dist/server/plugins/api-docs/utils/decorators.js +1 -1
- package/dist/server/plugins/catalog-entities/database/repositories/common/revision-repository.d.ts +1 -1
- package/dist/server/plugins/catalog-entities/database/repositories/common/revision-repository.js +1 -1
- package/dist/server/plugins/catalog-entities/database/repositories/common/version-repository.d.ts +5 -1
- package/dist/server/plugins/catalog-entities/database/repositories/common/version-repository.js +7 -1
- package/dist/server/plugins/catalog-entities/database/repositories/entities/entities-write-repository.js +1 -1
- package/dist/server/plugins/catalog-entities/utils/ajv-validator.js +1 -1
- package/dist/server/plugins/catalog-entities/utils/extract-parts-from-components-ref.js +1 -1
- package/dist/server/plugins/catalog-entities/utils/is-valid-tag-name.js +1 -1
- package/dist/server/plugins/catalog-entities/utils/openapi-component-content-extractor.js +1 -1
- package/dist/server/plugins/config-parser/format-custom-tags.d.ts +1 -0
- package/dist/server/plugins/config-parser/format-custom-tags.js +1 -1
- package/dist/server/plugins/config-parser/index.js +1 -1
- package/dist/server/plugins/config-parser/loaders/redocly-config-loader.js +1 -1
- package/dist/server/plugins/config-parser/loaders/versions-config-loader.js +1 -1
- package/dist/server/plugins/config-parser/safe-parse.js +1 -1
- package/dist/server/plugins/consent/build-consent-script.d.ts +6 -0
- package/dist/server/plugins/consent/build-consent-script.js +230 -0
- package/dist/server/plugins/consent/gate-ssr-components.d.ts +5 -0
- package/dist/server/plugins/consent/gate-ssr-components.js +1 -0
- package/dist/server/plugins/consent/index.d.ts +3 -0
- package/dist/server/plugins/consent/index.js +1 -0
- package/dist/server/plugins/consent/is-consent-enabled.d.ts +8 -0
- package/dist/server/plugins/consent/is-consent-enabled.js +1 -0
- package/dist/server/plugins/consent/zone-modes.d.ts +8 -0
- package/dist/server/plugins/consent/zone-modes.js +1 -0
- package/dist/server/plugins/default-theme/extract-team-names.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/App.js +5 -5
- package/dist/server/plugins/dev-onboarding/template/components/ApiKeyOverview.js +3 -3
- package/dist/server/plugins/get-reserved-routes.d.ts +4 -2
- package/dist/server/plugins/get-reserved-routes.js +1 -1
- package/dist/server/plugins/l10n/index.js +1 -1
- package/dist/server/plugins/lifecycle.js +2 -2
- package/dist/server/plugins/markdown/markdoc/markdoc-options.d.ts +2 -0
- package/dist/server/plugins/markdown/search/get-ai-search-documents.js +9 -9
- package/dist/server/plugins/markdown/search/nodes/tag-node.d.ts +1 -1
- package/dist/server/plugins/markdown/search/nodes/tag-node.js +1 -1
- package/dist/server/plugins/markdown/search/to-markdown.js +1 -1
- package/dist/server/plugins/markdown/utils/sanitize-malformed-md-content.js +1 -1
- package/dist/server/plugins/mcp/audit/audit-log.js +1 -1
- package/dist/server/plugins/mcp/audit/secret-patterns.js +1 -1
- package/dist/server/plugins/mcp/auth/assistant-anonymous-token.d.ts +9 -0
- package/dist/server/plugins/mcp/auth/assistant-anonymous-token.js +1 -0
- package/dist/server/plugins/mcp/auth/auth-handlers.d.ts +5 -3
- package/dist/server/plugins/mcp/auth/auth-handlers.js +1 -1
- package/dist/server/plugins/mcp/codemode/build-tool-list.d.ts +6 -1
- package/dist/server/plugins/mcp/codemode/build-tool-list.js +2 -2
- package/dist/server/plugins/mcp/codemode/build-tool-type-declarations.js +4 -4
- package/dist/server/plugins/mcp/codemode/capabilities/fetch/index.js +3 -3
- package/dist/server/plugins/mcp/codemode/capabilities/types.d.ts +0 -1
- package/dist/server/plugins/mcp/codemode/capabilities/utils.d.ts +1 -2
- package/dist/server/plugins/mcp/codemode/capabilities/utils.js +4 -4
- package/dist/server/plugins/mcp/codemode/code-execution-audit.d.ts +3 -2
- package/dist/server/plugins/mcp/codemode/code-execution-audit.js +1 -1
- package/dist/server/plugins/mcp/codemode/code-execution-telemetry.d.ts +3 -4
- package/dist/server/plugins/mcp/codemode/code-execution-telemetry.js +1 -1
- package/dist/server/plugins/mcp/codemode/prompts.js +1 -1
- package/dist/server/plugins/mcp/codemode/tools/describe-tools.js +2 -2
- package/dist/server/plugins/mcp/codemode/tools/execute-schema.d.ts +0 -2
- package/dist/server/plugins/mcp/codemode/tools/execute-schema.js +1 -1
- package/dist/server/plugins/mcp/codemode/tools/execute.js +1 -1
- package/dist/server/plugins/mcp/codemode/tools/manage-credentials.js +2 -2
- package/dist/server/plugins/mcp/custom-tools/scan-custom-tools.js +1 -1
- package/dist/server/plugins/mcp/docs-mcp/tool-schemas.js +1 -1
- package/dist/server/plugins/mcp/docs-mcp/tools/core/create-anonymous-session.js +1 -1
- package/dist/server/plugins/mcp/docs-mcp/tools/core/whoami.js +1 -1
- package/dist/server/plugins/mcp/docs-mcp/tools/openapi/constants.d.ts +2 -0
- package/dist/server/plugins/mcp/docs-mcp/tools/openapi/constants.js +1 -1
- package/dist/server/plugins/mcp/docs-mcp/tools/openapi/endpoint-suggestions.d.ts +6 -0
- package/dist/server/plugins/mcp/docs-mcp/tools/openapi/endpoint-suggestions.js +1 -1
- package/dist/server/plugins/mcp/docs-mcp/tools/openapi/get-endpoint-info.js +1 -1
- package/dist/server/plugins/mcp/docs-mcp/tools/openapi/get-security-schemes.js +1 -1
- package/dist/server/plugins/mcp/docs-mcp/tools/openapi/load-api-description.d.ts +2 -0
- package/dist/server/plugins/mcp/docs-mcp/tools/openapi/load-api-description.js +1 -1
- package/dist/server/plugins/mcp/docs-mcp/tools/openapi/utils.d.ts +6 -2
- package/dist/server/plugins/mcp/docs-mcp/tools/openapi/utils.js +1 -1
- package/dist/server/plugins/mcp/docs-mcp/utils/resolve-refs.d.ts +20 -14
- package/dist/server/plugins/mcp/docs-mcp/utils/resolve-refs.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/credential-validation.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/elicitation.d.ts +16 -10
- package/dist/server/plugins/mcp/gateway-mcp/elicitation.js +5 -5
- package/dist/server/plugins/mcp/gateway-mcp/fetch/allowed-hosts.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/fetch/fetch-bridge.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/fetch/ssrf-guard.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/fetch/url-placeholders.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/ip-key.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/upstream-auth-scheme.d.ts +0 -6
- package/dist/server/plugins/mcp/gateway-mcp/upstream-auth-scheme.js +2 -2
- package/dist/server/plugins/mcp/handlers/docs-mcp-handler.js +1 -1
- package/dist/server/plugins/mcp/handlers/mcp-credentials-handler.js +1 -1
- package/dist/server/plugins/mcp/handlers/mcp-credentials-page.js +35 -35
- package/dist/server/plugins/mcp/index.js +1 -1
- package/dist/server/plugins/mcp/servers/docs-server.js +1 -1
- package/dist/server/plugins/mcp/servers/mcp-server.js +2 -4
- package/dist/server/plugins/mcp/types.d.ts +5 -11
- package/dist/server/plugins/mcp/utils/disabled-tools.d.ts +3 -0
- package/dist/server/plugins/mcp/utils/disabled-tools.js +1 -0
- package/dist/server/plugins/mcp/utils/json-schema-validator.d.ts +2 -2
- package/dist/server/plugins/mcp/utils/json-schema-validator.js +1 -1
- package/dist/server/plugins/mcp/utils/mcp-client.js +1 -1
- package/dist/server/plugins/mcp/utils/public-endpoint.d.ts +9 -0
- package/dist/server/plugins/mcp/utils/public-endpoint.js +1 -0
- package/dist/server/plugins/mcp/utils/telemetry-attributes.d.ts +2 -1
- package/dist/server/plugins/mcp/utils/telemetry-attributes.js +1 -1
- package/dist/server/plugins/mcp/utils/tool-registration.d.ts +2 -1
- package/dist/server/plugins/mcp/utils/tool-registration.js +1 -1
- package/dist/server/plugins/mcp/utils/xmcp-utils.d.ts +0 -7
- package/dist/server/plugins/mcp/utils/xmcp-utils.js +1 -1
- package/dist/server/plugins/mcp/workers/execute-mcp-tool.js +1 -1
- package/dist/server/plugins/mcp/workers/mcp-tool-telemetry.d.ts +5 -7
- package/dist/server/plugins/mcp/workers/mcp-tool-telemetry.js +1 -1
- package/dist/server/plugins/nav-utils.js +1 -1
- package/dist/server/plugins/scorecard-classic/index.js +1 -1
- package/dist/server/plugins/scorecard-classic/template/Grid/GridHeader.js +21 -8
- package/dist/server/plugins/scorecard-classic/template/ScorecardDashboard.js +3 -3
- package/dist/server/plugins/scorecard-classic/template/index.styles.d.ts +1 -0
- package/dist/server/plugins/scorecard-classic/template/index.styles.js +15 -5
- package/dist/server/plugins/scorecard-classic/template/useData.js +1 -1
- package/dist/server/plugins/scorecard-classic/template/views.js +1 -1
- package/dist/server/plugins/search/engines/flexsearch/index.js +2 -1
- package/dist/server/plugins/search/engines/flexsearch/search-index.d.ts +2 -2
- package/dist/server/plugins/search/engines/flexsearch/search-index.js +1 -1
- package/dist/server/plugins/search/llmstxt/index.js +1 -1
- package/dist/server/plugins/sidebars/index.js +7 -3
- package/dist/server/plugins/sidebars/tabs.d.ts +19 -0
- package/dist/server/plugins/sidebars/tabs.js +1 -0
- package/dist/server/plugins/sso/index.js +1 -1
- package/dist/server/ssr/render.js +1 -1
- package/dist/server/store.d.ts +2 -0
- package/dist/server/store.js +1 -1
- package/dist/server/tools/notifiers/helpers/privacy/mask-subject.js +1 -1
- package/dist/server/tools/notifiers/helpers/privacy/remove-pii.js +1 -1
- package/dist/server/types/plugins/search.d.ts +7 -5
- package/dist/server/utils/catalog-classic/get-catalog-shared-data-id.d.ts +3 -0
- package/dist/server/utils/catalog-classic/get-catalog-shared-data-id.js +1 -0
- package/dist/server/utils/envs/interpolate-env-variables.js +1 -1
- package/dist/server/utils/rbac-expand.js +1 -1
- package/dist/server/utils/rbac.js +1 -1
- package/dist/server/utils/redact-url.d.ts +6 -0
- package/dist/server/utils/redact-url.js +1 -0
- package/dist/server/utils/report-all-errors.js +1 -1
- package/dist/server/utils/sandbox-context.js +1 -1
- package/dist/server/utils/search/highlight-text-for-search.d.ts +1 -0
- package/dist/server/utils/search/highlight-text-for-search.js +1 -1
- package/dist/server/utils/search/text-match.d.ts +6 -0
- package/dist/server/utils/search/text-match.js +1 -0
- package/dist/server/utils/static-data.js +1 -1
- package/dist/server/utils/write-problems-report.d.ts +3 -0
- package/dist/server/utils/write-problems-report.js +1 -0
- package/dist/server/web-server/auth.d.ts +16 -2
- package/dist/server/web-server/auth.js +4 -4
- package/dist/server/web-server/jwt/jwt.js +1 -1
- package/dist/server/web-server/jwt/token-types.d.ts +11 -0
- package/dist/server/web-server/jwt/token-types.js +1 -0
- package/dist/server/web-server/jwt/types.d.ts +4 -4
- package/dist/server/web-server/jwt/types.js +1 -1
- package/dist/server/web-server/middleware/ensureSearchData.js +1 -1
- package/dist/server/web-server/middleware/responseHeadersMiddleware.js +1 -1
- package/dist/server/web-server/middleware/telemetry-middleware.js +1 -1
- package/dist/server/web-server/routes/ask-ai.js +1 -1
- package/dist/server/web-server/routes/auth.d.ts +1 -1
- package/dist/server/web-server/routes/auth.js +1 -1
- package/dist/server/web-server/routes/cors-proxy.js +2 -2
- package/dist/server/web-server/routes/dynamic-route.js +1 -1
- package/dist/server/web-server/routes/index.js +1 -1
- package/dist/server/web-server/routes/mcp-routes/mcp-oauth.js +1 -1
- package/dist/server/web-server/routes/mcp-routes/mcp-server-card.js +1 -1
- package/dist/server/web-server/routes/semantic-search.d.ts +1 -1
- package/dist/server/web-server/routes/semantic-search.js +1 -1
- package/dist/server/web-server/routes/static-content.js +1 -1
- package/dist/server/workers/worker-pool.js +1 -1
- package/dist/types/entitlements.d.ts +1 -0
- package/dist/utils/path/parse-path-versions.js +1 -1
- package/dist/utils/string/to-kebab-case.js +1 -1
- package/package.json +9 -9
- package/dist/client/app/Sidebar/helpers/filter-out-versioned-items.js +0 -1
- package/dist/server/plugins/mcp/auth/token-audience-telemetry.d.ts +0 -7
- package/dist/server/plugins/mcp/auth/token-audience-telemetry.js +0 -1
- package/dist/server/plugins/mcp/auth/token-audience.d.ts +0 -6
- package/dist/server/plugins/mcp/auth/token-audience.js +0 -1
- /package/dist/server/{plugins/mcp/gateway-mcp/fetch → web-server/utils}/ssrf-blocklist.d.ts +0 -0
- /package/dist/server/{plugins/mcp/gateway-mcp/fetch → web-server/utils}/ssrf-blocklist.js +0 -0
- /package/dist/server/{plugins/mcp/gateway-mcp/fetch → web-server/utils}/ssrf-safe-lookup.d.ts +0 -0
- /package/dist/server/{plugins/mcp/gateway-mcp/fetch → web-server/utils}/ssrf-safe-lookup.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
import{describeAvailableApis as
|
|
1
|
+
import{describeAvailableApis as l,findApiDescriptionByNameAndVersion as b,formatCandidates as d}from"../../utils/api-name-matching.js";import{apiTitleKey as A,compareApiDescriptions as P}from"../../utils/api-titles.js";import{recoveryToolNames as f,visibleMethodsAt as g}from"./endpoint-suggestions.js";import{absolutePath as u,getApiDescriptionFromFs as I,matchDocumentedPath as M}from"./utils.js";function m(i){return`Pass \`name\` (the API title, or a unique part of it) or \`filePath\` from ${f(i).listApis}.`}const D=10;function N({name:i,version:e,filePath:n},t){const o=Object.values(t);if(n)return o.filter(a=>a.relativePath===n);const r=o.find(a=>a.relativePath===i);if(r)return[r];const s=b(t,i??"",e);return s?[s.description,...s.otherMatches??[]]:[]}function w({version:i,relativePath:e}){return{...i?{version:i}:{},filePath:e}}function h({relativePath:i},e){return I({relativePath:i,outdir:e.outdir||"",user:e.user,rbac:e?.config?.access?.rbac||{},requiresLogin:!!e?.config?.access?.requiresLogin})}async function S(i,e){const{version:n,filePath:t}=i,o=i.name??i.apiName??i.api;if(!t&&!o)return y(i,e);const r=N({name:o,version:n,filePath:t},e.apiDescriptionsMap);return F(r,{name:o,filePath:t},e)}async function y({path:i,method:e,version:n},t){const o=Object.values(t.apiDescriptionsMap);if(o.length===0)throw new Error("This project documents no OpenAPI description.");if(o.length===1){const[p]=o;if(n&&p.version!==n)throw new Error(`"${p.name}" has no version "${n}". Available versions: ${p.version||"(no version)"}.`);const $=await h(p,t);if(!$)throw new Error(`Could not read the API definition "${p.relativePath}". ${m(t.mcpMode)}`);return{description:$,filePath:p.relativePath}}if(!i||!e||o.length>D)throw new Error(`${m(t.mcpMode)} Available APIs: ${l(t.apiDescriptionsMap,"")}.`);const r=await k(o,i,e,t),{documenting:s,otherMethods:a}=r;if(s.length===0)throw C(t,a,i,e);if(!n)return v(s,r.best,i,e,t);const c=s.filter(p=>p.version===n);if(c.length===0)throw j(s,i,n);return v(c,r.best,i,e,t)}async function v(i,e,n,t,o){const r=new Set(i.map(({name:p})=>A(p)));if(r.size>1)throw E(i,r,n,t,o.mcpMode);const[s,...a]=i,c=s===e?.api?e.description:await h(s,o);if(!c)throw new Error(`Could not read the API definition "${s.relativePath}". ${m(o.mcpMode)}`);return{description:c,filePath:s.relativePath,otherMatches:a.length>0?a.map(w):void 0}}function E(i,e,n,t,o){const r=[...e].map(s=>{const[a]=i.filter(c=>A(c.name)===s);return`"${a.name}" (${a.relativePath})`});return new Error(`${t.toUpperCase()} ${u(n)} is documented by ${e.size} APIs: ${d(r)}. Pass \`name\` or \`filePath\` from ${f(o).listApis} to pick one.`)}function C(i,e,n,t){if(e.length>0){const s=e.map(({api:a,methods:c})=>`"${a.name}" (${a.relativePath}) documents ${d(c)}.`).join(" ");return new Error(`No API documents ${t.toUpperCase()} ${u(n)}. ${s}`)}const{endpoints:o,search:r}=f(i.mcpMode);return new Error(`No API in this project documents ${u(n)}. Available APIs: ${l(i.apiDescriptionsMap,"")}. Call ${o} with one of them, or ${r} with a keyword from the request.`)}function j(i,e,n){const t=[...new Set(i.map(({version:o})=>o||"(no version)"))];return new Error(`No API documenting "${u(e)}" has version "${n}". Available versions: ${d(t)}.`)}async function k(i,e,n,t){const o=[],r=[];let s;for(const a of i){const c=await h(a,t);if(!c)continue;const p=g(c,M(c,e));p.includes(n.toUpperCase())?(o.push(a),(!s||P(a,s.api)<0)&&(s={api:a,description:c})):p.length>0&&r.push({api:a,methods:p})}return{documenting:o.sort(P),best:s,otherMethods:r}}async function F(i,{name:e,filePath:n},t){if(i.length===0){if(n)throw new Error(`No API found with filePath "${n}". File paths are returned by the ${f(t.mcpMode).listApis} tool.`);const r=l(t.apiDescriptionsMap,e??"");throw new Error(r?`No API found matching "${e}". Available APIs: ${r}.`:`No API found matching "${e}".`)}for(const r of i){const s=await h(r,t);if(!s)continue;const a=i.filter(c=>c!==r);return{description:s,filePath:r.relativePath,otherMatches:a.length>0?a.map(w):void 0}}const o=i.map(({relativePath:r})=>`"${r}"`).join(", ");throw new Error(i.length>1?`No API found matching "${n||e}". None of the matching definitions could be loaded: ${o}.`:`No API found matching "${n||e}".`)}export{S as loadApiDescription};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { OpenAPIDefinition, OpenAPIOperation } from '@redocly/api-docs';
|
|
2
2
|
import type { ApiFunctionsUser, RbacConfig } from '@redocly/config';
|
|
3
|
-
import type { McpEndpoint } from '../../../types';
|
|
4
|
-
|
|
3
|
+
import type { McpEndpoint, McpPath } from '../../../types';
|
|
4
|
+
/** The operations of `pathItem` that `x-mcp` does not hide, keyed as the definition keys them. */
|
|
5
|
+
export declare function visibleOperations(pathItem: McpPath | undefined): [string, McpEndpoint][];
|
|
5
6
|
type EndpointInfo = Pick<OpenAPIOperation, 'summary' | 'description' | 'security'> & {
|
|
6
7
|
method: string;
|
|
7
8
|
path: string;
|
|
@@ -14,5 +15,8 @@ export declare function getApiDescriptionFromFs({ relativePath, outdir, user, rb
|
|
|
14
15
|
rbac?: RbacConfig;
|
|
15
16
|
requiresLogin?: boolean;
|
|
16
17
|
}): Promise<OpenAPIDefinition | undefined>;
|
|
18
|
+
export declare function absolutePath(requestedPath: string): string;
|
|
19
|
+
/** Models copy a path and lose its casing; the definition's own spelling still answers. */
|
|
20
|
+
export declare function matchDocumentedPath(description: OpenAPIDefinition, requestedPath: string): string;
|
|
17
21
|
export {};
|
|
18
22
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import m from"node:path";import{existsSync as f}from"node:fs";import{readFile as u}from"node:fs/promises";import{replaceFileExtension as d}from"../../../../../plugins/api-docs/utils/replace-file-extension.js";import{PUBLIC_API_DEFINITIONS_FOLDER as h}from"../../../../../constants/common.js";import{filterDataByAccessDeep as l}from"../../../../../utils/rbac.js";import{isEndpointHidden as c}from"../../../utils/xmcp-utils.js";import{HTTP_METHODS as x}from"./constants.js";function y(t){return!t||c(t,"docs")?[]:Object.entries(t).filter(r=>{const[e,o]=r;return x.has(e.toLowerCase())&&typeof o=="object"&&o!==null&&!c(o,"docs")})}function P(t){const{paths:r={}}=t,e=[];for(const[o,i]of Object.entries(r))for(const[n,s]of y(i))e.push({path:o,method:n.toUpperCase(),summary:s.summary,description:s.description,security:s.security});return e}async function I({relativePath:t,outdir:r,user:e,rbac:o={},requiresLogin:i=!1}){const n=m.join(r||"",h,d(t,".json"));if(!f(n))return;const a=await u(n,"utf-8"),p=JSON.parse(a);return l(p,{isAuthenticated:e?.isAuthenticated,email:e?.email,teams:e?.teams},o,i)}function D(t){return t.startsWith("/")?t:`/${t}`}function S(t,r){const e=D(r);if(t.paths?.[e])return e;const o=e.toLowerCase();return Object.keys(t.paths??{}).find(n=>n.toLowerCase()===o)??e}export{D as absolutePath,I as getApiDescriptionFromFs,P as getEndpointsFromPaths,S as matchDocumentedPath,y as visibleOperations};
|
|
@@ -1,20 +1,26 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
type
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
currentDepth?: number;
|
|
1
|
+
import type { OpenAPIParameter, Referenced, OpenAPIDefinition } from '@redocly/api-docs';
|
|
2
|
+
import type { McpEndpoint } from '../../types.js';
|
|
3
|
+
type JsonValue = string | number | boolean | null | JsonValue[] | {
|
|
4
|
+
[key: string]: JsonValue;
|
|
5
|
+
};
|
|
6
|
+
type JsonObject = {
|
|
7
|
+
[key: string]: JsonValue;
|
|
9
8
|
};
|
|
10
|
-
export
|
|
11
|
-
|
|
9
|
+
export type ResolvedOperation = {
|
|
10
|
+
operation: JsonObject;
|
|
11
|
+
components?: Record<string, JsonObject>;
|
|
12
|
+
};
|
|
13
|
+
export declare function resolveRef<T>(ref: string, def?: OpenAPIDefinition): T | undefined;
|
|
14
|
+
type ResolveOperationParams = {
|
|
15
|
+
operation: McpEndpoint;
|
|
12
16
|
pathParams: Referenced<OpenAPIParameter>[];
|
|
13
|
-
opParams: Referenced<OpenAPIParameter>[];
|
|
14
17
|
definition: OpenAPIDefinition;
|
|
15
18
|
};
|
|
16
|
-
export declare function
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
export declare function resolveOperation({ operation, pathParams, definition, }: ResolveOperationParams): ResolvedOperation;
|
|
20
|
+
/**
|
|
21
|
+
* The operation with every `$ref` it follows inlined, or undefined when the followed targets pass
|
|
22
|
+
* `maxBytes` or the inliner cannot read the operation.
|
|
23
|
+
*/
|
|
24
|
+
export declare function inlineOperation({ operation, pathParams, definition }: ResolveOperationParams, maxBytes: number): ResolvedOperation | undefined;
|
|
19
25
|
export {};
|
|
20
26
|
//# sourceMappingURL=resolve-refs.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function
|
|
1
|
+
const S=/^#\/components\/([^/]+)\/([^/]+)(\/.*)?$/;function w(e){return e.replace(/~1/g,"/").replace(/~0/g,"~")}function d(e,s){if(!s||!e?.startsWith("#/"))return;const n=e.slice(2).split("/").map(w);let t=s;for(const r of n)if(typeof t=="object"&&t!==null&&r in t)t=t[r];else return;return t}function h(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function O(e,s){const n={},t=new Set,r=new Set,o=[e];for(;o.length>0;){const c=o.pop();if(!(c===null||typeof c!="object")){if(h(c)&&typeof c.$ref=="string"&&!t.has(c.$ref)){t.add(c.$ref);const[,i,f]=S.exec(c.$ref)??[],u=f&&w(f);if(i&&u&&!r.has(`${i}/${u}`)){const l=d(`#/components/${i}/${f}`,s);l!==void 0&&(r.add(`${i}/${u}`),(n[i]??={})[u]=l,o.push(l))}}for(const i of Object.values(c))o.push(i)}}return Object.keys(n).length>0?n:void 0}function R(e,s){const n=h(e)&&typeof e.$ref=="string"?d(e.$ref,s)??e:e,{name:t,in:r}=h(n)?n:{};return typeof t=="string"&&typeof r=="string"?`${r}:${t}`:JSON.stringify(e)}function g(e,s,n){const t=new Map;for(const r of[...e,...s])r&&t.set(R(r,n),r);return[...t.values()]}function C({operation:e,pathParams:s,definition:n}){const t={...e,parameters:g(s,e.parameters??[],n)};return{operation:t,components:O(t,n)}}class E extends Error{}function j(e,s){if(e.remainingBytes-=s,e.remainingBytes<0)throw new E}function $(e,s){return j(e,JSON.stringify(s).length),s}function m(e,s,n){const t=d(e,s);if(t===void 0)return;const r=n.bytesByRef.get(e)??JSON.stringify(t).length;return n.bytesByRef.set(e,r),j(n,r),t}function y(e,s,n){const t=$(n,Object.fromEntries(Object.entries(e).filter(([r,o])=>r!=="$ref"&&o!==void 0)));return{...s,...t}}function a({schema:e,definition:s,budget:n,visitedRefs:t=new Set,maxDepth:r=50,currentDepth:o=0}){if(!e)return;if(o>=r)return $(n,{type:"object",description:`Maximum resolution depth exceeded (${r})`});if("$ref"in e&&e.$ref){if(t.has(e.$ref))return $(n,{type:"object",description:`Circular reference detected: ${e.$ref}`,"x-circular-ref":e.$ref});const f=new Set(t);f.add(e.$ref);const u=m(e.$ref,s,n);if(!u)return $(n,{type:"object",description:`Could not resolve reference: ${e.$ref}`,"x-unresolved-ref":e.$ref});const l=a({schema:u,definition:s,budget:n,visitedRefs:f,maxDepth:r,currentDepth:o+1});return l&&y(e,l,n)}const c={...e};if(c.properties){const f={};for(const[u,l]of Object.entries(c.properties)){const p=a({schema:l,definition:s,budget:n,visitedRefs:new Set(t),maxDepth:r,currentDepth:o+1});f[u]=p||l}c.properties=f}if(c.items&&typeof c.items=="object")if(Array.isArray(c.items))c.items=c.items.map(f=>a({schema:f,definition:s,budget:n,visitedRefs:new Set(t),maxDepth:r,currentDepth:o+1})||f);else{const f=a({schema:c.items,definition:s,budget:n,visitedRefs:new Set(t),maxDepth:r,currentDepth:o+1});f&&(c.items=f)}return["allOf","anyOf","oneOf"].forEach(f=>{const u=c[f];Array.isArray(u)&&(c[f]=u.reduce((l,p)=>{const v=a({schema:p,definition:s,budget:n,visitedRefs:new Set(t),maxDepth:r,currentDepth:o+1});return v&&l.push(v),l},[]))}),c}function P({pathParams:e,opParams:s,definition:n,budget:t}){return[...e,...s].reduce((r,o)=>{if(!o)return r;if("$ref"in o&&o.$ref){const c=m(o.$ref,n,t);if(!c)return r;const i=y(o,c,t);return r.push({...i,schema:"schema"in i&&i.schema?a({schema:i.schema,definition:n,budget:t}):void 0}),r}return r.push({...o,schema:"schema"in o&&o.schema?a({schema:o.schema,definition:n,budget:t}):void 0}),r},[])}function x(e,s,n){const t={...e};if(e.example!==void 0&&(t.example=e.example),!e.examples)return t;const r={};for(const[o,c]of Object.entries(e.examples))if("$ref"in c){const i=m(c.$ref,s,n);i&&(r[o]=i)}else r[o]=c;return t.examples=r,t}function A(e,s,n){if(!e)return;const t="$ref"in e&&e.$ref?m(e.$ref,s,n):e;if(!t)return;const r=y(e,t,n),o={...r};if("content"in r&&r.content){const c={};for(const[i,f]of Object.entries(r.content))c[i]={...x(f,s,n),schema:f.schema?a({schema:f.schema,definition:s,budget:n}):void 0};o.content=c}return o}function B(e,s,n){if(e)return Object.entries(e).reduce((t,[r,o])=>{const c="$ref"in o&&o.$ref?m(o.$ref,s,n):o;if(!c)return t;const i=y(o,c,n),f={...i};if("content"in i&&i.content){const u={};for(const[l,p]of Object.entries(i.content))u[l]={...x(p,s,n),schema:p.schema?a({schema:p.schema,definition:s,budget:n}):void 0};f.content=u}return t[r]=f,t},{})}function N({operation:e,pathParams:s,definition:n},t){const r={remainingBytes:t,bytesByRef:new Map};try{const o={...e,parameters:P({pathParams:s,opParams:e.parameters||[],definition:n,budget:r}),requestBody:A(e.requestBody,n,r),responses:B(e.responses,n,r)};return{operation:o,components:O(o,n)}}catch{return}}export{N as inlineOperation,C as resolveOperation,d as resolveRef};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const r=/[\r\n]/,
|
|
1
|
+
const r=/[\r\n]/,s=255,i=/^bearer$/i;function a(t){for(let e=0;e<t.length;e+=1){const n=t.charCodeAt(e);if(n===0||n>255)return!0}return!1}function c(t,e){if(!e)return"Enter a value before saving.";if(r.test(e))return"The value contains a line break. Paste it as a single line and save again.";if(a(e))return"The value contains a character that cannot be sent in an HTTP header, such as a curly quote or an invisible character. Copy the value again as plain text and save.";if(t.kind==="cookie")return o(t.name,e);if((t.kind==="bearer"||t.kind==="oauth2ClientCredentials")&&i.test(e))return"Paste the token that follows the word Bearer, not only the word."}function o(t,e){if(e.includes(";")||e.toLowerCase().includes(`${t.toLowerCase()}=`))return`This looks like a whole cookie header. Paste only the value of the ${t} cookie, without the "${t}=" prefix and without any other name=value pairs.`}function h(t,e){if(!(!r.test(e)&&!a(e)))throw new Error(`The credential cannot be sent as the ${t} header value.`)}export{h as assertHeaderSafe,c as rejectCredential};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { InputRequiredResult } from '@modelcontextprotocol/server';
|
|
2
2
|
import type { UpstreamAuthRequiredEvent } from './upstream-auth.js';
|
|
3
|
-
import type {
|
|
3
|
+
import type { UpstreamApiAuth, UpstreamAuthScheme } from './upstream-auth-scheme.js';
|
|
4
4
|
import type { KvService } from '@redocly/config';
|
|
5
5
|
export declare const CREDENTIALS_INPUT_REQUEST_KEY = "credentials";
|
|
6
6
|
export type CredentialTarget = {
|
|
@@ -31,6 +31,16 @@ type CredentialElicitationRequest = {
|
|
|
31
31
|
hasExistingSecret: boolean;
|
|
32
32
|
reason: CredentialElicitationReason;
|
|
33
33
|
};
|
|
34
|
+
type AcceptedLinkScope = {
|
|
35
|
+
kv: KvService;
|
|
36
|
+
subject: string;
|
|
37
|
+
baseUrl: string;
|
|
38
|
+
apis: readonly string[];
|
|
39
|
+
};
|
|
40
|
+
export type AcceptedCredentialsLink = {
|
|
41
|
+
apiName: string;
|
|
42
|
+
url: string;
|
|
43
|
+
};
|
|
34
44
|
/** Concurrent prompts for one subject and scheme share the link the first one mints. */
|
|
35
45
|
export declare function dedupeElicitation(key: string, run: () => Promise<string>): Promise<string>;
|
|
36
46
|
export declare function supportsUrlElicitation(capabilities: {
|
|
@@ -42,13 +52,15 @@ export declare function supportsUrlElicitation(capabilities: {
|
|
|
42
52
|
export declare function isStatelessRequest(mcpReq: {
|
|
43
53
|
envelope?: unknown;
|
|
44
54
|
}): boolean;
|
|
45
|
-
|
|
55
|
+
/** How the user answered the credentials prompt, read from a retried request. */
|
|
56
|
+
export declare function credentialElicitationAnswer(inputResponses: Record<string, unknown> | undefined): 'accepted' | 'declined' | undefined;
|
|
57
|
+
export declare function buildCredentialsAcceptedHint(apiName: string, url?: string): string;
|
|
58
|
+
export declare function acceptedCredentialsLink(requestState: unknown, { kv, subject, baseUrl, apis }: AcceptedLinkScope): Promise<AcceptedCredentialsLink | undefined>;
|
|
46
59
|
export declare function schemeLabelOf(schemes: Record<string, unknown>, scheme: string): string | undefined;
|
|
47
60
|
/**
|
|
48
61
|
* The 2026-07-28-era credentials prompt: the tool call returns an input-required result the
|
|
49
62
|
* client fulfils and retries, with no push `elicitation/create` and no session behind it.
|
|
50
|
-
*
|
|
51
|
-
* recomputed from the caller's identity on every request.
|
|
63
|
+
* `requestState` carries the link token, so the retry after an accepted prompt can repeat the link.
|
|
52
64
|
*/
|
|
53
65
|
export declare function buildUpstreamCredentialInputRequired(request: CredentialElicitationRequest): Promise<InputRequiredResult>;
|
|
54
66
|
/** Fallback text for a client without URL-elicitation support. */
|
|
@@ -59,11 +71,5 @@ export declare function buildUpstreamCredentialFallback(request: CredentialElici
|
|
|
59
71
|
* likely a permissions problem than a missing key.
|
|
60
72
|
*/
|
|
61
73
|
export declare function buildCredentialsToolHint(event: UpstreamAuthRequiredEvent, schemeLabel?: string): string;
|
|
62
|
-
/**
|
|
63
|
-
* Text for an operation that accepts several security schemes and has no stored credential for
|
|
64
|
-
* any of them. Picking one here is what sent an API key as a session cookie, so the model is told
|
|
65
|
-
* to ask the user which credential they hold and pass it back as `scheme`.
|
|
66
|
-
*/
|
|
67
|
-
export declare function buildSchemeChoiceHint(event: UpstreamAuthRequiredEvent, choices: readonly SchemeChoice[]): string;
|
|
68
74
|
export {};
|
|
69
75
|
//# sourceMappingURL=elicitation.d.ts.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import{inputRequired as
|
|
2
|
-
`)}const
|
|
3
|
-
`)}
|
|
4
|
-
`)}async function
|
|
5
|
-
`)}export{m as CREDENTIALS_INPUT_REQUEST_KEY,
|
|
1
|
+
import{inputRequired as h,inputResponse as y}from"@modelcontextprotocol/server";import{withPathPrefix as I}from"@redocly/theme/core/utils";import{MCP_CREDENTIALS_ROUTE as A}from"../../../constants/mcp.js";import{MANAGE_API_CREDENTIALS_TOOL_NAME as u}from"../constants.js";import{secretStore as l,ELICITATION_TTL_MS as P}from"./secret-store.js";import{operationsForScheme as E,orderedSchemesOf as g}from"./upstream-auth-scheme.js";import{ANONYMOUS_SUBJECT_PREFIX as p}from"./subject.js";const m="credentials",d=new Map;function F(e,t){const r=d.get(e);if(r)return r;const n=t().finally(()=>{d.delete(e)});return d.set(e,n),n}function D(e){return!!e?.elicitation?.url}function H(e){return e.envelope!==void 0}function q(e){const t=y(e,m);if(t.kind==="elicit")return t.action==="accept"?"accepted":"declined"}function B(e,t){return[`The user accepted the prompt to add credentials for the "${e}" API.`,...t?["The credentials page:",t]:[],"",`Wait until the user says they saved the credentials, then retry the call that needed them. Do not call \`${u}\` again unless the user asks for a new link.`].join(`
|
|
2
|
+
`)}async function Y(e,{kv:t,subject:r,baseUrl:n,apis:o}){if(typeof e!="string")return;const i=await l.peekElicitation(t,e);if(!(i?.subject!==r||!o.includes(i.api)))return{apiName:i.name,url:T(n,e)}}function W(e,t){return Object.keys(e).length>1?t:void 0}async function z(e){const{token:t,url:r}=await f(e);return h({inputRequests:{[m]:h.elicitUrl({message:C(e),url:r})},requestState:t})}async function G(e){const{url:t}=await f(e);return _(e,t,S(e.subject))}function J(e,t){const r=b(e);if(e.status===401&&!e.sentScheme){const[i,s]=e.callerHeader?[`Your code set the "${e.callerHeader}" header itself, so the stored credential was not sent.`,"Remove that header from the request and retry. Do not replace the stored credentials."]:["No stored credential was sent with this request, so this result says nothing about the stored credentials.","Do not replace them. Check whether this operation needs a different kind of credential or a different route."];return[`The "${e.name}" API returned ${r}. ${i}`,"",s].join(`
|
|
3
|
+
`)}const n=e.status===401?`The "${e.name}" API rejected the stored ${t?`"${t}" `:""}credential (${r}).`:`The "${e.name}" API rejected the request with ${r}.`,o=t?`api: "${e.api}" and scheme: "${t}"`:`api: "${e.api}"`;return[n,"",`Do not retry the same call. Call \`${u}\` with ${o} to get a link the user has to open in a browser to replace or remove the stored credentials, then retry once they confirm they saved them.`].join(`
|
|
4
|
+
`)}function b({status:e,detail:t}){return t?`HTTP ${e}: ${t}`:`HTTP ${e}`}async function f({kv:e,subject:t,baseUrl:r,target:n,auth:o,apiAuth:i,hasExistingSecret:s}){const a=i?await k(e,t,n.api,i):[],c=await l.createElicitation(e,{subject:t,api:n.api,name:n.name,scheme:n.scheme,...n.schemeLabel?{schemeLabel:n.schemeLabel}:{},auth:o,hasExistingSecret:s,...a.length>1?{schemes:a}:{}});return{token:c,url:T(r,c)}}async function k(e,t,r,n){const i=(await l.getSecretsForSubject(e,t))[r]??{};return g(n.schemes).map(s=>{const a=E(n,s),c=n.descriptions?.[s];return{name:s,...c?{label:c}:{},auth:n.schemes[s],hasExistingSecret:!!i[s],...a.length>0?{operations:a}:{}}})}function S(e){return e.startsWith(p)?e.slice(p.length):void 0}function C({target:e,hasExistingSecret:t,reason:r}){const n=`The link works for ${$()} minutes.`,o=x(e),i=r.kind==="authFailure"&&r.detail?` The API answered: ${r.detail}.`:"";return t?`Open this page in a browser to replace or remove the stored credentials for ${o}. They go straight to the API and never enter the chat. ${n}`:`${O(o)} needs credentials.${i} Open this page in a browser to paste them - they go straight to the API and never enter the chat. ${n}`}function x({name:e,schemeLabel:t}){return t?`the "${t}" scheme of the "${e}" API`:`the "${e}" API`}function O(e){return e.charAt(0).toUpperCase()+e.slice(1)}function $(){return Math.round(P/6e4)}function T(e,t){const r=I(A);if(!e)return`${r}?token=${t}`;try{const n=new URL(r,e);return n.searchParams.set("token",t),n.toString()}catch{return`${e}${r}?token=${t}`}}function _({target:e,reason:t,hasExistingSecret:r},n,o){const s=[`**Stop and show the user this link. They have to open it in a browser to ${r?`replace or remove the stored credentials for the "${e.name}" API`:`add credentials for the "${e.name}" API`} - nothing continues until they do.**`,"",n,"",`The link is valid ${$()} minutes and opens a form on this site. Credentials go straight to the API from there: never ask for them in the conversation and never pass them as a tool argument.`,"",t.kind==="authFailure"?`The upstream API "${e.name}" returned ${t.status}${t.detail?` (${t.detail})`:""} and needs credentials.`:r?`Credentials are already stored for the "${e.name}" API; the link lets the user replace or remove them.`:`The "${e.name}" API needs credentials.`,...e.schemeLabel?[`The operation authenticates with its "${e.schemeLabel}" security scheme.`]:[],o?`Once the user confirms they saved them, call this tool again with sessionHandle: "${o}" - the credentials are stored against that handle, applied server-side, and never appear in the conversation.`:"Once the user confirms they saved them, call this tool again - the credentials are applied server-side and never appear in the conversation."],a=t.kind==="authFailure"?t.otherApiNames??[]:[];if(a.length>0){const c=a.map(w=>`"${w}"`).join(", ");s.push("",`The remaining APIs (${c}) will prompt for credentials on subsequent calls.`)}return s.join(`
|
|
5
|
+
`)}export{m as CREDENTIALS_INPUT_REQUEST_KEY,Y as acceptedCredentialsLink,B as buildCredentialsAcceptedHint,J as buildCredentialsToolHint,G as buildUpstreamCredentialFallback,z as buildUpstreamCredentialInputRequired,q as credentialElicitationAnswer,F as dedupeElicitation,H as isStatelessRequest,W as schemeLabelOf,D as supportsUrlElicitation};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const h=/\{([^}]+)\}/g;function g(n=[]){const e=[];for(const t of n){const r=p(t);r&&e.push(r)}return e}function x(n,e){return e.some(t=>t.test(n))}function p(n){const e=m(n.url);if(!e)return null;let t="",r=0,o=!1;for(const l of e.matchAll(h)){const[i,f]=l,c=e.slice(r,l.index);c.includes(".")&&(o=!0),t+=s(c);const u=d(n.variables?.[f]);u.guaranteesDot&&(o=!0),t+=u.pattern,r=l.index+i.length}const a=e.slice(r);return a.includes(".")&&(o=!0),t+=s(a),o?new RegExp(`^${t}$`,"i"):null}function d(n){const e=n?.enum?.filter(Boolean);if(e&&e.length>0)return{pattern:`(?:${e.map(s).join("|")})`,guaranteesDot:e.every(r=>r.includes("."))};const t=n?.default?.split(".").slice(1).join(".");return t?{pattern:`[^.]+\\.${s(t)}`,guaranteesDot:!0}:{pattern:"[^.]+",guaranteesDot:!1}}function m(n){if(!n)return null;const t=n.replace(/^[a-z][a-z0-9+.-]*:\/\//i,"").split(/[/?#]/,1)[0];return t&&(t.includes("@")?t.slice(t.lastIndexOf("@")+1):t).replace(/:(?:\d+|\{[^}]+\})$/,"")||null}function s(n){return n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}export{g as buildAllowedHostMatchers,x as isHostAllowed};
|
|
1
|
+
const h=/\{(?<!\{[^}]*?\{)([^}]+)\}/g;function g(n=[]){const e=[];for(const t of n){const r=p(t);r&&e.push(r)}return e}function x(n,e){return e.some(t=>t.test(n))}function p(n){const e=m(n.url);if(!e)return null;let t="",r=0,o=!1;for(const l of e.matchAll(h)){const[i,f]=l,c=e.slice(r,l.index);c.includes(".")&&(o=!0),t+=s(c);const u=d(n.variables?.[f]);u.guaranteesDot&&(o=!0),t+=u.pattern,r=l.index+i.length}const a=e.slice(r);return a.includes(".")&&(o=!0),t+=s(a),o?new RegExp(`^${t}$`,"i"):null}function d(n){const e=n?.enum?.filter(Boolean);if(e&&e.length>0)return{pattern:`(?:${e.map(s).join("|")})`,guaranteesDot:e.every(r=>r.includes("."))};const t=n?.default?.split(".").slice(1).join(".");return t?{pattern:`[^.]+\\.${s(t)}`,guaranteesDot:!0}:{pattern:"[^.]+",guaranteesDot:!1}}function m(n){if(!n)return null;const t=n.replace(/^[a-z][a-z0-9+.-]*:\/\//i,"").split(/[/?#]/,1)[0];return t&&(t.includes("@")?t.slice(t.lastIndexOf("@")+1):t).replace(/:(?:\d+|\{[^}]+\})$/,"")||null}function s(n){return n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}export{g as buildAllowedHostMatchers,x as isHostAllowed};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Agent as Q}from"undici";import{envConfig as Z}from"../../../../config/env-config.js";import{logger as N}from"../../../../tools/notifiers/logger.js";import{assertUrlAllowed as I}from"./ssrf-guard.js";import{createSsrfSafeLookup as q}from"
|
|
1
|
+
import{Agent as Q}from"undici";import{envConfig as Z}from"../../../../config/env-config.js";import{logger as N}from"../../../../tools/notifiers/logger.js";import{assertUrlAllowed as I}from"./ssrf-guard.js";import{createSsrfSafeLookup as q}from"../../../../web-server/utils/ssrf-safe-lookup.js";import{SsrfBlockedError as W}from"../../../../web-server/utils/ssrf-blocklist.js";import{GatewayFetchBlockedError as R,GatewayFetchSystemError as ee,GatewayFetchUserError as g}from"./fetch-errors.js";import{redactHeaders as J,redactSecretsFromString as te}from"./redact.js";import{extractUpstreamDetail as re}from"./upstream-detail.js";import{normalizeFetchPath as ne}from"./normalize-path.js";import{reportGatewayFetch as K}from"./fetch-telemetry.js";import{logGatewayFetchAudit as V}from"./fetch-audit.js";const X="x-forwarded-for",oe=3e4,se=1024*1024,ae=new Set([401,403,407]);let C;function ce(){if(!C){const e=Z.isDevelopMode;e&&N.warn("Gateway MCP SSRF guard: loopback upstreams are ALLOWED (develop mode only, never in runtime)."),C=new Q({connect:{lookup:q({allowLoopback:e})}})}return C}function _e({allowedHosts:e,apis:t=[],authorization:r,onAuthRequired:n,fetchImpl:c=fetch,dispatcher:a=ce(),timeoutMs:h=oe,maxResponseBytes:l=se,maxFetchesPerExec:w,clientIp:k}){const f=[...e,...t.flatMap(o=>o.matchers)],$=[...new Set(t.map(o=>o.name).filter(Boolean))].join(", "),m=t.flatMap(o=>Object.values(o.secrets??{})),D=[r,...m.map(o=>o.value)].filter(o=>!!o),x=m.map(o=>o.header);let O=0;return async o=>{const y=o.method??"GET",T=Date.now();let B,b,A,E={},H,_,j;try{if(w!==void 0){const u=Math.max(0,w);if(O+=1,O>u)throw new g(`Outbound fetch budget exceeded (max ${u} fetches per execute); split the work across calls`)}const d=le(o.url,f,$);B=d.hostname,b=ne(d.pathname);const v=t.filter(u=>u.matchers.some(z=>z.test(d.hostname))),s=v.length===1?v[0]:void 0;A=s?.slug;const S=pe(o.securityScheme,s),F=S?[S]:s?ye(s,d.pathname,y):void 0,L=s&&F?Se(s,F):void 0,P=L?s?.secrets?.[L]:void 0,M=be({callerHeaders:o.headers,secret:P,authorization:r,clientIp:k,credentialHeaders:s?.credentialHeaders});E=M.headers,H=P&&!M.callerHeader?L:void 0;const G=new AbortController;j=setTimeout(()=>G.abort(),h);const i=await c(d,{method:y,headers:E,body:o.body,redirect:"manual",signal:G.signal,dispatcher:a});_=i.status;let U="";try{U=await we(i,l)}finally{if(ae.has(i.status)&&s&&n){const u=re(U,i.headers.get("content-type"),D);n({api:s.slug,name:s.name,status:i.status,...L?{scheme:L}:{},...F?.length?{schemes:[...F]}:{},...H?{sentScheme:H}:{},...M.callerHeader?{callerHeader:M.callerHeader}:{},...u?{detail:u}:{}})}}let p=Ae(i.headers);if(fe(i.status)){p=J(p,x);const{location:u}=p;u&&!he(u,d,f)&&delete p.location}return K({host:d.hostname,method:y,outcome:"success",durationMs:Date.now()-T,path:b,api:A,status:i.status,bytes:Buffer.byteLength(U,"utf8")}),V({rawUrl:d.href,method:y,host:d.hostname,outcome:"success",durationMs:Date.now()-T,injectedSecretValues:D,path:b,api:A,status:i.status,injectedScheme:H,requestHeaders:E,requestBody:o.body,responseHeaders:p,responseBody:U}),{status:i.status,statusText:i.statusText,ok:i.ok,headers:p,bodyText:U}}catch(d){const{outcome:v,userSafe:s}=ie(d),S=B??(s instanceof R?s.host:void 0)??ue(o.url)??"invalid-url";throw K({host:S,method:y,outcome:v,durationMs:Date.now()-T,path:b,api:A,status:_}),V({rawUrl:o.url,method:y,host:S,outcome:v,durationMs:Date.now()-T,injectedSecretValues:D,path:b,api:A,status:_,injectedScheme:H,requestHeaders:E,requestBody:o.body}),s||(N.error(`Gateway MCP fetch failed [${y} ${S}]: ${te(me(d),D)} headers=${JSON.stringify(J(E,x))}`),new ee("Upstream fetch failed due to an internal error"))}finally{j!==void 0&&clearTimeout(j)}}}function de(e){let t=e;for(let r=0;r<5&&t instanceof Error;r++){if(t instanceof g||t instanceof W)return t;t=t.cause}}function ie(e){const t=de(e);return{outcome:t instanceof R||t instanceof W?"blocked":t?"user_error":"system_error",userSafe:t}}function le(e,t,r){try{return I(e,t)}catch(n){throw n instanceof R&&r?new R(`${n.message}. \`fetch\` reaches only the documented hosts of the callable APIs (${r}); other documented APIs can be read through the tools but not called.`,n.host):n}}function ue(e){try{return new URL(e).hostname||void 0}catch{return}}function fe(e){return e>=300&&e<400}function he(e,t,r){try{return!!I(new URL(e,t).href,r)}catch{return!1}}function me(e){if(!(e instanceof Error))return String(e);const t=e.cause;return t instanceof Error?`${e.message}: ${t.message}`:e.message}async function we(e,t){const r=Number(e.headers.get("content-length"));if(Number.isFinite(r)&&r>t)throw Y(t,r);if(!e.body)return"";const n=e.body.getReader(),c=new TextDecoder;let a=0,h="";try{for(;;){const{done:l,value:w}=await n.read();if(l)break;if(a+=w.byteLength,a>t)throw Y(t,a);h+=c.decode(w,{stream:!0})}return h+c.decode()}finally{await n.cancel().catch(()=>{})}}function Y(e,t){return new g(`Response is too large (over ${e} bytes, got ${t}); request less data`)}function ye(e,t,r){const n=e.routes?.find(c=>c.method===r&&c.pattern.test(t));return n?n.schemes:ge(e)}function Se(e,t){const r=t.filter(n=>e.secrets?.[n]);return r.length===0?t[0]:r.reduce((n,c)=>{const a=e.secrets?.[n]?.savedAt??0;return(e.secrets?.[c]?.savedAt??0)>a?c:n})}function pe(e,t){if(e==null)return;if(typeof e!="string")throw new g("securityScheme must be a security scheme name (a string)");if(!t)throw new g("securityScheme cannot be applied: the URL host does not identify exactly one callable API");const r=t.schemePriority??[];if(r.length>0&&!r.includes(e))throw new g(`Unknown securityScheme "${e}" for ${t.name}; valid values: ${r.join(", ")}`);return e}function ge(e){const t=Object.keys(e.secrets??{});if(t.length===0)return e.defaultScheme?[e.defaultScheme]:[];const r=(e.schemePriority??[]).filter(n=>t.includes(n));return[...r,...t.filter(n=>!r.includes(n))]}function be({callerHeaders:e,secret:t,authorization:r,clientIp:n,credentialHeaders:c}){const a={},h=new Set,l=t??(r?{header:"Authorization",value:r}:void 0),w=l?new Set((c??[]).map(f=>f.toLowerCase()).filter(f=>f!==l.header.toLowerCase())):new Set;for(const[f,$]of Object.entries(e??{})){const m=f.toLowerCase();m==="host"||m==="content-length"||m!==X&&(w.has(m)||(h.add(m),a[f]=$))}if(n&&(a[X]=n),!l)return{headers:a};const k=l.header.toLowerCase();return h.has(k)?{headers:a,callerHeader:k}:(a[l.header]=l.value,{headers:a})}function Ae(e){const t={};return e.forEach((r,n)=>{t[n.toLowerCase()]=r}),t}export{_e as buildFetchBridge,ce as getDefaultAgent,we as readCappedBody};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isIP as d}from"node:net";import{isHostAllowed as h}from"./allowed-hosts.js";import{isBlockedIp as w}from"
|
|
1
|
+
import{isIP as d}from"node:net";import{isHostAllowed as h}from"./allowed-hosts.js";import{isBlockedIp as w}from"../../../../web-server/utils/ssrf-blocklist.js";import{GatewayFetchBlockedError as p,GatewayFetchUserError as r}from"./fetch-errors.js";import{findUnresolvedPlaceholders as a,unresolvedPlaceholderMessage as i}from"./url-placeholders.js";function v(o,t){let e;try{e=new URL(o)}catch{throw new r(`Invalid URL: ${o}`)}if(e.protocol!=="http:"&&e.protocol!=="https:")throw new r(`Only http(s) URLs are allowed, got ${e.protocol}`);if(e.username||e.password)throw new r("URLs must not contain embedded credentials");const n=a(e.hostname);if(n.length>0)throw new r(i(n));const s=c(e.hostname);if(f(s)||!h(s,t))throw new p(`Host "${s}" is not allowed`,s);const l=a(e.pathname);if(l.length>0)throw new r(i(l));return e}function f(o){const t=c(o.toLowerCase()).split("%")[0];return t==="localhost"||t.endsWith(".localhost")?!0:d(t)!==0?w(t):!1}function c(o){return o.replace(/^\[|\]$/g,"")}export{v as assertUrlAllowed,f as isBlockedAddress};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const o=/\{([^}/]+)\}/g,t=/^[A-Za-z_][A-Za-z0-9_]*$/;function n(e){const a=e.replace(/%7B/gi,"{").replace(/%7D/gi,"}"),l=new Set;for(const[,r]of a.matchAll(o))t.test(r)&&l.add(r);return[...l]}function s(e){const a=e.length>1?"placeholders":"placeholder",l=e.map(r=>`"${r}"`).join(", ");return`Unresolved ${a} ${l} in the URL. This is not a credentials problem. Replace every placeholder with a real value. Use the server variable default, or ask the user.`}export{n as findUnresolvedPlaceholders,s as unresolvedPlaceholderMessage};
|
|
1
|
+
const o=/\{(?<!\{[^}/]*?\{)([^}/]+)\}/g,t=/^[A-Za-z_][A-Za-z0-9_]*$/;function n(e){const a=e.replace(/%7B/gi,"{").replace(/%7D/gi,"}"),l=new Set;for(const[,r]of a.matchAll(o))t.test(r)&&l.add(r);return[...l]}function s(e){const a=e.length>1?"placeholders":"placeholder",l=e.map(r=>`"${r}"`).join(", ");return`Unresolved ${a} ${l} in the URL. This is not a credentials problem. Replace every placeholder with a real value. Use the server variable default, or ask the user.`}export{n as findUnresolvedPlaceholders,s as unresolvedPlaceholderMessage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isIP as l}from"node:net";import{getFeedbackClientIp as a}from"../../../../web-server/utils/feedback-ip.js";import{extractMappedIpv4 as c}from"
|
|
1
|
+
import{isIP as l}from"node:net";import{getFeedbackClientIp as a}from"../../../../web-server/utils/feedback-ip.js";import{extractMappedIpv4 as c}from"../../../../web-server/utils/ssrf-blocklist.js";function I(t){const n=a(t);if(n)return f(n)}function f(t){if(l(t)===6){const n=c(t);return n||`${u(t)}::/64`}return t}function u(t){const n=t.split("%")[0],[e,i=""]=n.split("::"),r=e?e.split(":"):[],o=i?i.split(":"):[],s=8-r.length-o.length;return[...r,...Array(Math.max(0,s)).fill("0"),...o].slice(0,4).map(p=>parseInt(p||"0",16).toString(16)).join(":")}export{I as ipRateLimitKey};
|
|
@@ -27,15 +27,9 @@ export type UpstreamApiAuth = {
|
|
|
27
27
|
routes: UpstreamAuthRoute[];
|
|
28
28
|
descriptions?: Record<string, string>;
|
|
29
29
|
};
|
|
30
|
-
export type SchemeChoice = {
|
|
31
|
-
name: string;
|
|
32
|
-
label: string;
|
|
33
|
-
description?: string;
|
|
34
|
-
};
|
|
35
30
|
type SecuritySchemeLoader = typeof getApiDescriptionFromFs;
|
|
36
31
|
export declare const DEFAULT_SCHEME_NAME = "default";
|
|
37
32
|
export declare function resolveUpstreamApiAuth(context: McpToolContext, apiRelativePath: string, loadDescription?: SecuritySchemeLoader): Promise<UpstreamApiAuth>;
|
|
38
|
-
export declare function schemeChoicesOf(auth: UpstreamApiAuth, names: readonly string[]): SchemeChoice[];
|
|
39
33
|
export declare function credentialHeadersOf(schemes: Record<string, UpstreamAuthScheme>): string[];
|
|
40
34
|
export declare function orderedSchemesOf(schemes: Record<string, UpstreamAuthScheme>): string[];
|
|
41
35
|
export declare function operationsForScheme(auth: UpstreamApiAuth, scheme: string): string[];
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{getApiDescriptionFromFs as
|
|
2
|
-
`)[0].trim();s&&(n[r]=s.slice(0,A))}return n}function
|
|
1
|
+
import{getApiDescriptionFromFs as S}from"../docs-mcp/tools/openapi/utils.js";import{OPERATION_METHODS as b}from"../docs-mcp/tools/openapi/constants.js";const A=120,l="default",g={kind:"raw"},h=["oauth2ClientCredentials","bearer","apiKey","cookie","raw"],E=new Set(b),j=/\{(?<!\{[^}]*?\{)[^}]*\}/g;function c(){return{schemes:{[l]:g},defaultScheme:l,routes:[]}}async function L(e,t,n=S){const r=Object.values(e.apiDescriptionsMap??{}).find(i=>i.relativePath===t);if(!r)return c();try{const i=await n({relativePath:r.relativePath,outdir:e.outdir||"",user:e.user,rbac:e.config?.access?.rbac||{},requiresLogin:!!e.config?.access?.requiresLogin});return i?w(i,r.servers??[]):c()}catch{return c()}}function w(e,t){const n=x(e),r=Object.keys(n);if(r.length===0)return c();const i=m(e.security,n)[0]??a(r,n)[0],o=P(e,r);return{schemes:n,defaultScheme:i,routes:H(e,n,t),...Object.keys(o).length>0?{descriptions:o}:{}}}function M(e){return[...new Set(Object.values(e).map(C))]}function N(e){return a(Object.keys(e),e)}function U(e,t){const n=new Set;for(const r of e.routes)r.schemes.includes(t)&&n.add(`${r.method} ${r.path}`);return[...n]}function C(e){return e.kind==="apiKey"?e.header:e.kind==="cookie"?"Cookie":"Authorization"}function P(e,t){const n={};for(const r of t){const i=e.components?.securitySchemes?.[r],o=k(i)?i.description:void 0;if(typeof o!="string")continue;const s=o.split(`
|
|
2
|
+
`)[0].trim();s&&(n[r]=s.slice(0,A))}return n}function x(e){const t={};for(const[n,r]of Object.entries(e.components?.securitySchemes??{})){if(!k(r))continue;const i=T(r);i&&(t[n]=i)}return t}function T(e){if(e.type==="oauth2"){const t=e.flows?.clientCredentials;return t?{kind:"oauth2ClientCredentials",tokenUrl:t.tokenUrl,scopes:Object.keys(t.scopes??{})}:{kind:"bearer"}}if(e.type==="openIdConnect")return{kind:"bearer"};if(e.type==="http"&&e.scheme?.toLowerCase()==="bearer")return{kind:"bearer"};if(e.type==="apiKey"&&e.in==="header")return{kind:"apiKey",header:e.name||"X-Api-Key"};if(e.type==="apiKey"&&e.in==="cookie"&&e.name)return{kind:"cookie",name:e.name}}function a(e,t){return[...e].sort((n,r)=>h.indexOf(t[n].kind)-h.indexOf(t[r].kind)||e.indexOf(n)-e.indexOf(r))}function m(e,t){const n=[];for(const r of e??[])for(const i of Object.keys(r))t[i]&&!n.includes(i)&&n.push(i);return n}function H(e,t,n){const r=_(n),i=[];for(const[o,s]of Object.entries(e.paths??{})){if(!s||typeof s!="object")continue;const f=R(r,o);if(f)for(const[p,O]of Object.entries(s)){if(!E.has(p.toLowerCase()))continue;const u=O?.security??e.security;if(u===void 0)continue;const d=m(u,t);d.length===0&&u.length>0||i.push({pattern:f,path:o,method:p.toUpperCase(),schemes:a(d,t)})}}return i}function R(e,t){if(t.startsWith("/"))return new RegExp(`^${e}${y(t)}/?$`)}function y(e){return e.split(j).map(v).join("[^/]+").replace(/(?<!\/)\/+$/,"")}function _(e){const t=[...new Set(e.map(n=>$(n.url)).filter(Boolean).map(y))];return t.length>0?`(?:${t.join("|")})?`:""}function $(e){if(!e)return"";const t=e.replace(/^[a-z][a-z0-9+.-]*:\/\//i,""),n=t.indexOf("/");return n===-1?"":t.slice(n).split(/[?#]/)[0].replace(/(?<!\/)\/+$/,"")}function v(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function k(e){return typeof e=="object"&&e!==null&&"type"in e}export{l as DEFAULT_SCHEME_NAME,M as credentialHeadersOf,U as operationsForScheme,N as orderedSchemesOf,L as resolveUpstreamApiAuth};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{logger as
|
|
1
|
+
import{logger as h}from"../../../tools/notifiers/logger.js";import{getFeedbackClientIp as N}from"../../../web-server/utils/feedback-ip.js";import{getForwardedOrigin as A,getInternalOrigin as v}from"../../../web-server/utils/get-request-origin.js";import{GATEWAY_FETCH_LIMIT_DEFAULTS as F,DEFAULT_ANONYMOUS_VISITOR_TEAM as B,RbacFeatures as L}from"../../../../constants/common.js";import{isObject as H}from"../../../../utils/guards/is-object.js";import{getAccessibleSkills as V}from"../../../utils/skills/get-accessible-skills.js";import{getSkillUrl as $}from"../../../utils/skills/get-skill-url.js";import{createMcpHandler as j}from"@modelcontextprotocol/server";import{createDocsMcpServer as G}from"../servers/docs-server.js";import{filterApiDescriptionsByRbac as J,filterGraphqlDescriptionsByRbac as K}from"../utils.js";import{McpClientInputError as Y,createMethodNotAllowedError as z,withErrorHandling as W}from"./errors.js";import{constructForbiddenResponse as X,constructInvalidTokenResponse as Q,constructUnauthorizedResponse as Z,handleMcpAuth as q}from"../auth/auth-handlers.js";import{isAssistantAnonymousToken as x}from"../auth/assistant-anonymous-token.js";import{shouldHandleMcpAuth as ee}from"../../../utils/rbac/should-handle-mcp-auth.js";import{extractTokenFromAuthHeader as re}from"../utils/jwt.js";import{resolveMcpClient as oe}from"../utils/mcp-client.js";import{randomUUID as se}from"../../../utils/crypto/random-uuid.js";import{getPublicMcpEndpointStatus as ne}from"../utils/public-endpoint.js";import{DEFAULT_DOCS_MCP_SERVER_NAME as ie,SSE_KEEPALIVE_INTERVAL_MS as te}from"../constants.js";import{withPathPrefix as ae}from"@redocly/theme/core/utils";import{PUBLIC_MCP_ROUTE as ce}from"@redocly/theme/core/constants/mcp";import{canAccessFeature as pe}from"../../../utils/rbac.js";import{ANONYMOUS_AUTH_DETAILS as de}from"../../../constants/rbac.js";const M="X-Redocly-AI-Metadata";function le(r){if(r)try{const e=JSON.parse(r);if(H(e))return e;h.warn(`Ignoring ${M} header: not a JSON object.`)}catch{h.warn(`Ignoring ${M} header: invalid JSON.`)}}function b(){return{...de,email:"",idpAccessToken:void 0,idpId:void 0}}const me=async(r,e,i)=>{const t=!!e?.config?.access?.requiresLogin,a=e?.config?.access?.rbac||{},E=i?.props?.config?.mcpMode??"tools",y=i?.props?.config?.gatewayFetchBudgetPerExec??F.budgetPerExec,S=i?.props?.config?.searchIgnoredRoutes??[],s=i,U=!!s?.props?.config?.isPublicEndpoint;let m;const T=r.headers.get("Authorization");if(U)e.user=b();else if(ee(t,a)){const{isAuthenticated:n,isTokenValid:p,isFirstPartyAnonymous:u,currentUser:d,accessToken:f}=await q(r,e);if(u)e.user=b();else if(n)d&&(e.user=d),m=f;else{const c=A(r),l=ne(e.config)==="served"?`${c}${ae(ce)}`:void 0;return p===!1?Q(c,l):Z(c,l)}}else if(T){const n=re(T)??void 0;m=(n?await x(n):!1)?void 0:n}const w={isAuthenticated:!!e.user?.isAuthenticated,teams:e.user?.teams||[B],claims:e.user?.claims??{}};return pe(L.MCP,w,a,t)?await W(async()=>{const n=i?.props?.config?.elicitationEnabled??!1;if(r.method==="GET")return new Response(JSON.stringify({error:"Method Not Allowed",message:`In order to use this MCP server, you need to register it in your MCP client (VS Code, Cursor, Claude Code, etc.) using this URL: ${r.url}`}),{status:405,headers:{"Content-Type":"application/json"}});if(r.method!=="POST")return z();const p=s?.props?.config?.apiDescriptionsMap||{},u=s?.props?.config?.graphqlDescriptions||[],d=s?.props?.config?.mcpDocsServerName||ie,{config:{mcp:f={}}}=e,c=J(p,e.user,a,t),l=K(u,e.user,a,t),C=e.config.products?Object.values(e.config.products).map(o=>o?.name):[],D=f.docs?.name||d,g=le(r.headers.get(M)),P=s?.props?.tools||[],R=s?.props?.skillsBaseUrl||A(r),_=V(s?.props?.skills||[],e.user,a,t).map(o=>({slug:o.slug,name:o.name,description:o.description,content:o.content,uri:$(R,o.slug)})),I=await ue(r),O={name:D,tools:P,skills:_,context:{...e,accessToken:m,outdir:e.outdir||"",baseUrl:e.baseUrl||A(r),internalBaseUrl:v(r),apiDescriptionsMap:c,graphqlDescriptions:l,products:C,metadata:g,mcpMode:E,elicitationEnabled:n,gatewayFetchBudgetPerExec:y,searchIgnoredRoutes:S,clientIp:N(r),...oe(r),requestId:se(),messageId:typeof g?.messageId=="string"?g.messageId:void 0}},k=await G({...O,getKv:e.getKv});return await j(async()=>k,{keepAliveMs:te,onerror:o=>h.warn(`MCP handler: ${o.message}`)}).fetch(r,I!==void 0?{parsedBody:I}:void 0)},E):X()};async function ue(r){try{return await r.json()}catch(e){throw new Y(e instanceof Error?e.message:String(e),{cause:e})}}var Le=me;export{Le as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{secretStore as u}from"../gateway-mcp/secret-store.js";import{rejectCredential as
|
|
1
|
+
import{secretStore as u}from"../gateway-mcp/secret-store.js";import{rejectCredential as b}from"../gateway-mcp/credential-validation.js";import{buildAllowedHostMatchers as A}from"../gateway-mcp/fetch/allowed-hosts.js";import{assertUrlAllowed as j}from"../gateway-mcp/fetch/ssrf-guard.js";import{readCappedBody as v,getDefaultAgent as O}from"../gateway-mcp/fetch/fetch-bridge.js";import{logger as F}from"../../../tools/notifiers/logger.js";import{clearedPage as I,formPage as x,multiOutcomePage as N,notFoundPage as D,successPage as $}from"./mcp-credentials-page.js";const g={"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-store","X-Frame-Options":"DENY","Referrer-Policy":"no-referrer","Content-Security-Policy":"default-src 'none'; style-src 'unsafe-inline'; form-action 'self'"},B=3e4,M=64*1024,U=/^bearer\s+/i;let P=fetch;function ee(e){P=e}const G=async(e,i)=>{const t=await i.getKv();if(e.method==="GET"){const n=new URL(e.url).searchParams.get("token")??"",o=await u.peekElicitation(t,n);return o?new Response(x(o,n),{status:200,headers:g}):m()}if(e.method==="POST"){const n=await e.formData(),o=d(n,"token"),s=d(n,"mode")||"token";return s==="multi"?z(t,n,o):s==="client-credentials"?K(t,n,o):s==="clear"?X(t,o):H(t,n,o)}return new Response("Method Not Allowed",{status:405,headers:{Allow:"GET, POST"}})};async function H(e,i,t){const n=d(i,"secret").trim(),o=await u.peekElicitation(e,t);if(!o)return m();const s=b(o.auth,n);if(s)return h(o,t,s);const l=await u.consumeElicitation(e,t);return l?(await u.setSecret(e,l.subject,l.api,l.scheme,S(l.auth,n)),new Response($(l),{status:200,headers:g})):m()}async function X(e,i){const t=await u.consumeElicitation(e,i);return t?(await u.deleteSecret(e,t.subject,t.api,t.scheme),new Response(I(t),{status:200,headers:g})):m()}async function z(e,i,t){const n=await u.peekElicitation(e,t);if(!n?.schemes||n.schemes.length<=1)return m();const o=n.schemes,s=[];for(let r=0;r<o.length;r++){const a=o[r],p=d(i,`secret__${r}`).trim(),E=d(i,`client_id__${r}`).trim(),_=d(i,`client_secret__${r}`).trim();if(a.hasExistingSecret&&d(i,`remove__${r}`)){if(p||E||_)return h(n,t,`${a.name}: enter a new value or tick "Remove the stored credential", not both.`);s.push({kind:"remove",scheme:a.name});continue}if(a.auth.kind==="oauth2ClientCredentials"){if(p){const C=b(a.auth,p);if(C)return h(n,t,`${a.name}: ${C}`);s.push({kind:"set",scheme:a.name,secret:S(a.auth,p)})}else if(E&&_)s.push({kind:"exchange",scheme:a.name,auth:a.auth,clientId:E,clientSecret:_});else if(E||_)return h(n,t,`${a.name}: enter both the client ID and the client secret, or leave both blank.`);continue}if(!p)continue;const y=b(a.auth,p);if(y)return h(n,t,`${a.name}: ${y}`);s.push({kind:"set",scheme:a.name,secret:S(a.auth,p)})}if(s.length===0)return h(n,t,"Fill in at least one credential, or check a box to remove one, before saving.");const l=[];for(const r of s){if(r.kind!=="exchange"){l.push(r);continue}const a=await R(r.auth,r.clientId,r.clientSecret);if(!a.ok)return T(n,t,a.reason,r.scheme);l.push({kind:"set",scheme:r.scheme,secret:a.secret})}const c=await u.consumeElicitation(e,t);if(!c)return m();for(const r of l)r.kind==="remove"?await u.deleteSecret(e,c.subject,c.api,r.scheme):await u.setSecret(e,c.subject,c.api,r.scheme,r.secret);const k=await u.getSecretsForSubject(e,c.subject),f={saved:l.filter(r=>r.kind==="set").map(r=>r.scheme),removed:l.filter(r=>r.kind==="remove").map(r=>r.scheme),stored:Object.keys(k[c.api]??{})};return new Response(N(c,f),{status:200,headers:g})}async function K(e,i,t){const n=d(i,"client_id").trim(),o=d(i,"client_secret").trim(),s=await u.peekElicitation(e,t);if(!s||s.auth.kind!=="oauth2ClientCredentials")return m();if(!n||!o)return h(s,t,"Enter both the client ID and the client secret before saving.");const l=await R(s.auth,n,o);if(!l.ok)return T(s,t,l.reason);const c=await u.consumeElicitation(e,t);return c?(await u.setSecret(e,c.subject,c.api,c.scheme,l.secret),new Response($(c),{status:200,headers:g})):m()}async function R(e,i,t){try{const n=A([{url:e.tokenUrl}]),o=j(e.tokenUrl,n),s=new AbortController,l=setTimeout(()=>s.abort(),B);let c;try{c=await P(o,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"client_credentials",client_id:i,client_secret:t}),redirect:"manual",signal:s.signal,dispatcher:O()})}finally{clearTimeout(l)}if(!c.ok)return w(`token endpoint returned ${c.status}`);let k;try{k=await v(c,M)}catch{return w("token response exceeded the size cap")}let f;try{f=JSON.parse(k)}catch{return w("token response was not valid JSON")}const r=typeof f=="object"&&f!==null&&"access_token"in f?f.access_token:void 0,a=typeof r=="string"?r.trim():"";return a?b(e,a)?w("token response had an access_token that cannot be sent in a header"):{ok:!0,secret:{header:"Authorization",value:`Bearer ${a}`}}:w("token response had no access_token")}catch(n){return w(n instanceof Error?n.message:String(n))}}function w(e){return{ok:!1,reason:e}}function T(e,i,t,n){return F.warn(`gateway oauth2 client_credentials exchange failed for ${e.api}: ${t}`),h(e,i,`${n?`${n}: we`:"We"} could not exchange those client credentials for a token. Check the client ID and secret, then try again.`)}function d(e,i){const t=e.get(i);return typeof t=="string"?t:""}function h(e,i,t){return new Response(x(e,i,t),{status:400,headers:g})}function m(){return new Response(D(),{status:404,headers:g})}function S(e,i){return e.kind==="apiKey"?{header:e.header,value:i}:e.kind==="cookie"?{header:"Cookie",value:`${e.name}=${i}`}:e.kind==="bearer"||e.kind==="oauth2ClientCredentials"?{header:"Authorization",value:J(i)}:{header:"Authorization",value:i}}function J(e){return`Bearer ${e.trim().replace(U,"")}`}var te=G;export{te as default,ee as setCredentialsFetchImpl};
|