@redocly/realm 0.136.0-next.8 → 0.136.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 +221 -0
- package/dist/client/App.js +1 -1
- package/dist/client/ScriptLoader.js +1 -1
- package/dist/client/TestProvider.js +1 -1
- package/dist/client/app/Link.js +1 -1
- package/dist/client/app/Sidebar/useBreadcrumbs.js +1 -1
- package/dist/client/app/Sidebar/useSidebarItems.js +1 -1
- package/dist/client/app/Sidebar/utils.d.ts +1 -1
- package/dist/client/app/hooks/catalog/useCatalogClassic.js +1 -1
- package/dist/client/app/hooks/catalog/useCatalogFilter.js +1 -1
- package/dist/client/app/hooks/catalog/useCatalogSearch.js +1 -1
- package/dist/client/app/hooks/catalog/useCatalogSort.js +1 -1
- package/dist/client/app/hooks/products/useCurrentProduct.js +1 -1
- package/dist/client/app/hooks/useAutoScroll.js +1 -1
- package/dist/client/app/hooks/useBanner.js +1 -1
- package/dist/client/app/hooks/useL10nConfig.js +1 -1
- package/dist/client/app/hooks/useLoginUrl.js +1 -1
- package/dist/client/app/hooks/usePageTimeTracker.js +1 -1
- package/dist/client/app/hooks/useRouteChangeTracker.js +1 -1
- package/dist/client/app/markdoc/custom-components/openapi/replay-openapi.js +1 -1
- package/dist/client/app/pages/DevLogin/DevLogin.js +1 -1
- package/dist/client/app/search/useSearch.d.ts +1 -0
- package/dist/client/app/search/useSearch.js +1 -1
- package/dist/client/app/seo/hooks/useHreflangLinks.js +1 -1
- package/dist/client/app/useMobileMenu.js +1 -1
- package/dist/client/app/usePreloadHistory.d.ts +1 -1
- package/dist/client/app/usePreloadHistory.js +1 -1
- package/dist/client/app/utils/loadAndNavigate.d.ts +1 -1
- package/dist/client/browser-entry.js +2 -2
- package/dist/client/providers/hooks.d.ts +1 -1
- package/dist/client/providers/hooks.js +1 -1
- package/dist/client/providers/page-data/hooks.js +1 -1
- package/dist/client/providers/post-message/PostMessageProvider.js +1 -1
- package/dist/constants/api.d.ts +2 -0
- package/dist/constants/api.js +1 -1
- package/dist/server/api-routes/helpers/enhance-context.js +1 -1
- package/dist/server/api-routes/run-api-routes-worker.js +1 -1
- package/dist/server/config/env-config.d.ts +0 -2
- package/dist/server/config/env-schema.d.ts +0 -5
- package/dist/server/config/env-schemas/feature-flags.d.ts +0 -4
- package/dist/server/config/env-schemas/feature-flags.js +1 -1
- package/dist/server/constants/common.d.ts +2 -0
- package/dist/server/constants/common.js +1 -1
- package/dist/server/constants/plugins/search.d.ts +1 -1
- package/dist/server/constants/plugins/search.js +1 -1
- package/dist/server/esbuild/esbuild.js +1 -1
- package/dist/server/esbuild/plugins/themes-resolver.js +4 -2
- package/dist/server/plugins/catalog-entities/database/repositories/catalog-entities-repository.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/App.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/AppsList.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/ApiKeyItem.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/ApiLogDetails.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/ApiLogs.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/CreateApiKey.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/DeleteAppDialog.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/EditApis.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/EditAppDescriptionDialog.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/EditAppDialog.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/EditCallbackUrlDialog.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/RevokeAPIKey.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/RollAPIKey.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/UpdateApiKey.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/index.js +1 -1
- package/dist/server/plugins/mcp/audit/audit-log.d.ts +36 -0
- package/dist/server/plugins/mcp/audit/audit-log.js +1 -0
- package/dist/server/plugins/mcp/audit/constants.d.ts +6 -0
- package/dist/server/plugins/mcp/audit/constants.js +1 -0
- package/dist/server/plugins/mcp/audit/secret-patterns.d.ts +2 -0
- package/dist/server/plugins/mcp/audit/secret-patterns.js +1 -0
- package/dist/server/plugins/mcp/codemode/build-tool-list.d.ts +12 -0
- package/dist/server/plugins/mcp/codemode/build-tool-list.js +2 -0
- package/dist/server/plugins/mcp/codemode/capabilities/fetch/index.js +4 -4
- package/dist/server/plugins/mcp/codemode/capabilities/types.d.ts +2 -1
- package/dist/server/plugins/mcp/codemode/capabilities/utils.d.ts +1 -1
- package/dist/server/plugins/mcp/codemode/capabilities/utils.js +3 -3
- package/dist/server/plugins/mcp/codemode/code-audit.d.ts +12 -0
- package/dist/server/plugins/mcp/codemode/code-audit.js +1 -0
- package/dist/server/plugins/mcp/codemode/code-execution-audit.d.ts +17 -0
- package/dist/server/plugins/mcp/codemode/code-execution-audit.js +1 -0
- package/dist/server/plugins/mcp/codemode/code-execution-telemetry.d.ts +11 -0
- package/dist/server/plugins/mcp/codemode/code-execution-telemetry.js +1 -0
- package/dist/server/plugins/mcp/codemode/constants.js +1 -1
- package/dist/server/plugins/mcp/codemode/prompts.d.ts +6 -3
- package/dist/server/plugins/mcp/codemode/prompts.js +7 -4
- package/dist/server/plugins/mcp/codemode/sandbox/create-sandbox-setup.js +1 -1
- package/dist/server/plugins/mcp/codemode/sandbox/polyfills/fetch-shim.d.ts +6 -0
- package/dist/server/plugins/mcp/codemode/sandbox/polyfills/fetch-shim.js +54 -0
- package/dist/server/plugins/mcp/codemode/sandbox/polyfills/form-data.d.ts +3 -0
- package/dist/server/plugins/mcp/codemode/sandbox/polyfills/form-data.js +17 -0
- package/dist/server/plugins/mcp/codemode/sandbox/polyfills/index.d.ts +3 -0
- package/dist/server/plugins/mcp/codemode/sandbox/polyfills/index.js +2 -0
- package/dist/server/plugins/mcp/codemode/sandbox/polyfills/url-search-params.d.ts +3 -0
- package/dist/server/plugins/mcp/codemode/sandbox/polyfills/url-search-params.js +64 -0
- package/dist/server/plugins/mcp/codemode/sandbox/polyfills/url.d.ts +3 -0
- package/dist/server/plugins/mcp/codemode/sandbox/polyfills/url.js +239 -0
- package/dist/server/plugins/mcp/codemode/tools/describe-tools.d.ts +2 -3
- package/dist/server/plugins/mcp/codemode/tools/describe-tools.js +1 -1
- package/dist/server/plugins/mcp/codemode/tools/execute-schema.d.ts +1 -1
- package/dist/server/plugins/mcp/codemode/tools/execute.js +1 -1
- package/dist/server/plugins/mcp/codemode/tools/manage-credentials.d.ts +19 -0
- package/dist/server/plugins/mcp/codemode/tools/manage-credentials.js +2 -0
- package/dist/server/plugins/mcp/codemode/tools/tool-errors.d.ts +4 -0
- package/dist/server/plugins/mcp/codemode/tools/tool-errors.js +1 -0
- package/dist/server/plugins/mcp/constants.d.ts +2 -1
- package/dist/server/plugins/mcp/constants.js +1 -1
- package/dist/server/plugins/mcp/docs-mcp/tool-schemas.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-endpoints.js +1 -1
- package/dist/server/plugins/mcp/docs-mcp/tools/openapi/get-full-api-description.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/list-apis.js +1 -1
- package/dist/server/plugins/mcp/docs-mcp/tools/openapi/load-api-description.d.ts +5 -1
- package/dist/server/plugins/mcp/docs-mcp/tools/openapi/load-api-description.js +1 -1
- package/dist/server/plugins/mcp/docs-mcp/tools/openapi/types.d.ts +2 -0
- package/dist/server/plugins/mcp/docs-mcp/utils.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/elicitation.d.ts +38 -17
- package/dist/server/plugins/mcp/gateway-mcp/elicitation.js +3 -2
- package/dist/server/plugins/mcp/gateway-mcp/fetch/fetch-audit.d.ts +24 -0
- package/dist/server/plugins/mcp/gateway-mcp/fetch/fetch-audit.js +1 -0
- package/dist/server/plugins/mcp/gateway-mcp/fetch/fetch-bridge.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/fetch/fetch-telemetry.d.ts +2 -1
- package/dist/server/plugins/mcp/gateway-mcp/fetch/fetch-telemetry.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/fetch/normalize-path.d.ts +6 -0
- package/dist/server/plugins/mcp/gateway-mcp/fetch/normalize-path.js +1 -0
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/apply-edge-rate-limit.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/edge-limiter.d.ts +2 -2
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/edge-limiter.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/rate-limit-telemetry.d.ts +8 -0
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/rate-limit-telemetry.js +1 -0
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/window-counter.d.ts +2 -2
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/window-counter.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/secret-store.d.ts +3 -0
- package/dist/server/plugins/mcp/gateway-mcp/secret-store.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/upstream-auth.d.ts +2 -1
- package/dist/server/plugins/mcp/gateway-mcp/upstream-auth.js +1 -1
- package/dist/server/plugins/mcp/handlers/docs-mcp-handler.js +1 -1
- package/dist/server/plugins/mcp/handlers/errors.js +1 -1
- package/dist/server/plugins/mcp/handlers/mcp-credentials-handler.js +47 -24
- package/dist/server/plugins/mcp/servers/docs-server.d.ts +2 -3
- package/dist/server/plugins/mcp/servers/docs-server.js +1 -1
- package/dist/server/plugins/mcp/servers/mcp-server.d.ts +11 -14
- package/dist/server/plugins/mcp/servers/mcp-server.js +3 -1
- package/dist/server/plugins/mcp/types.d.ts +6 -6
- package/dist/server/plugins/mcp/utils/json-schema-validator.d.ts +9 -0
- package/dist/server/plugins/mcp/utils/json-schema-validator.js +1 -0
- package/dist/server/plugins/mcp/utils/mode.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 +20 -1
- package/dist/server/plugins/mcp/workers/mcp-tool-telemetry.js +1 -1
- package/dist/server/plugins/scorecard-classic/template/ApiDetailsPage.js +1 -1
- package/dist/server/plugins/scorecard-classic/template/RuleDetailsPage.js +1 -1
- package/dist/server/plugins/scorecard-classic/template/index.js +1 -1
- package/dist/server/plugins/scorecard-classic/template/utils.d.ts +1 -1
- package/dist/server/plugins/scorecard-classic/template/views.js +1 -1
- package/dist/server/plugins/search/engines/flexsearch/index.js +1 -1
- package/dist/server/plugins/search/engines/flexsearch/search-index.js +1 -1
- package/dist/server/plugins/search/engines/typesense/index.js +1 -1
- package/dist/server/ssr/render.js +1 -1
- package/dist/server/tools/notifiers/formatter.d.ts +1 -0
- package/dist/server/tools/notifiers/formatter.js +3 -3
- package/dist/server/tools/notifiers/logger.d.ts +1 -0
- package/dist/server/tools/notifiers/logger.js +2 -2
- package/dist/server/tools/notifiers/reporter.js +6 -6
- package/dist/server/types/plugin-hooks.d.ts +1 -1
- package/dist/server/types/plugins/common.d.ts +1 -1
- package/dist/server/utils/llmstxt/agent-feedback-llms-footer.js +1 -1
- package/dist/server/web-server/auth.js +3 -3
- package/dist/server/web-server/middleware/telemetry-middleware.js +1 -1
- package/dist/server/web-server/routes/ask-ai.d.ts +11 -0
- package/dist/server/web-server/routes/ask-ai.js +1 -1
- package/dist/server/web-server/routes/catalog/bff-catalog-related-entities.js +1 -1
- package/dist/server/web-server/routes/catalog/bff-catalog-revisions.js +1 -1
- package/dist/server/web-server/routes/catalog/bff-catalog.js +1 -1
- package/dist/server/web-server/routes/catalog/catalog-relations.js +1 -1
- package/dist/server/web-server/routes/catalog/catalog.js +1 -1
- 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/a2a.js +1 -1
- package/dist/server/web-server/routes/mcp-routes/agent-card.js +1 -1
- package/dist/server/web-server/routes/mcp-routes/mcp-server-card.js +1 -1
- package/dist/server/web-server/routes/page-data.js +1 -1
- package/dist/server/web-server/routes/search.js +1 -1
- package/dist/server/web-server/routes/skills/skill-markdown.js +1 -1
- package/dist/server/web-server/routes/skills/skills-index.js +1 -1
- package/dist/types/api.d.ts +3 -0
- package/dist/utils/url/parse-preview-url.d.ts +22 -0
- package/dist/utils/url/parse-preview-url.js +1 -0
- package/package.json +21 -22
- package/dist/server/plugins/mcp/codemode/sandbox/polyfills.d.ts +0 -2
- package/dist/server/plugins/mcp/codemode/sandbox/polyfills.js +0 -99
- package/dist/server/plugins/mcp/handlers/sse-keepalive.d.ts +0 -15
- package/dist/server/plugins/mcp/handlers/sse-keepalive.js +0 -3
- package/dist/server/plugins/mcp/servers/session-manager.d.ts +0 -15
- package/dist/server/plugins/mcp/servers/session-manager.js +0 -1
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
const t=`
|
|
2
|
+
// Collapses '.' and '..'. A backslash separates path segments, the way the host parser reads
|
|
3
|
+
// one, so it is folded into '/' first \u2014 otherwise the two disagree about where the host name
|
|
4
|
+
// ends in 'https://a.example\\@b.example/', and guest code reads a host the gateway never calls.
|
|
5
|
+
const normalizePath = (rawPath) => {
|
|
6
|
+
const path = rawPath.replace(/\\\\/g, '/');
|
|
7
|
+
const out = [];
|
|
8
|
+
for (const segment of path.split('/')) {
|
|
9
|
+
if (segment === '.') continue;
|
|
10
|
+
if (segment === '..') { if (out.length > 1) out.pop(); continue; }
|
|
11
|
+
out.push(segment);
|
|
12
|
+
}
|
|
13
|
+
let joined = out.join('/');
|
|
14
|
+
if ((path.endsWith('/.') || path.endsWith('/..')) && !joined.endsWith('/')) joined += '/';
|
|
15
|
+
return joined;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const DEFAULT_PORTS = { 'http:': '80', 'https:': '443', 'ws:': '80', 'wss:': '443', 'ftp:': '21' };
|
|
19
|
+
|
|
20
|
+
// Percent-encode what cannot travel literally in a url, so a hand-assembled path or query
|
|
21
|
+
// reaches the host parser as the caller meant it. Non-ASCII goes out as UTF-8, in runs, so a
|
|
22
|
+
// surrogate pair is encoded as the one character it stands for.
|
|
23
|
+
const encodeLiterals = (value) =>
|
|
24
|
+
value.replace(/[\\s"<>\\\`{}\\\\^]|[^\\x00-\\x7F]+/g, (chunk) => {
|
|
25
|
+
try { return encodeURIComponent(chunk); } catch (error) { return chunk; }
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// A non-ASCII host name is lower-cased but not punycoded \u2014 that needs an IDNA table this sandbox
|
|
29
|
+
// does not carry. The host re-parses the url before it fetches, so the request still goes to the
|
|
30
|
+
// punycoded name; only what guest code reads back differs.
|
|
31
|
+
const parseHostPort = (hostport, scheme) => {
|
|
32
|
+
let hostname = hostport;
|
|
33
|
+
let port = '';
|
|
34
|
+
if (hostport.startsWith('[')) {
|
|
35
|
+
const close = hostport.indexOf(']');
|
|
36
|
+
hostname = hostport.slice(0, close + 1);
|
|
37
|
+
const after = hostport.slice(close + 1);
|
|
38
|
+
if (after.startsWith(':')) port = after.slice(1);
|
|
39
|
+
} else {
|
|
40
|
+
const colon = hostport.lastIndexOf(':');
|
|
41
|
+
if (colon !== -1) { hostname = hostport.slice(0, colon); port = hostport.slice(colon + 1); }
|
|
42
|
+
}
|
|
43
|
+
if (port !== '' && !/^[0-9]+$/.test(port)) return null;
|
|
44
|
+
if (hostname === '') return null;
|
|
45
|
+
return {
|
|
46
|
+
hostname: hostname.toLowerCase(),
|
|
47
|
+
port: DEFAULT_PORTS[scheme] === port ? '' : port,
|
|
48
|
+
// A host written without a port leaves the port alone, rather than clearing it.
|
|
49
|
+
hasPort: port !== '',
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const ABSOLUTE_RE = /^([A-Za-z][A-Za-z0-9+\\-.]*):(\\/\\/)?([\\s\\S]*)$/;
|
|
54
|
+
|
|
55
|
+
const parseAuthorityUrl = (scheme, rest) => {
|
|
56
|
+
const cut = rest.search(/[/\\\\?#]/);
|
|
57
|
+
const authority = cut === -1 ? rest : rest.slice(0, cut);
|
|
58
|
+
let tail = cut === -1 ? '' : rest.slice(cut);
|
|
59
|
+
let username = '';
|
|
60
|
+
let password = '';
|
|
61
|
+
let hostport = authority;
|
|
62
|
+
const at = authority.lastIndexOf('@');
|
|
63
|
+
if (at !== -1) {
|
|
64
|
+
const credentials = authority.slice(0, at);
|
|
65
|
+
hostport = authority.slice(at + 1);
|
|
66
|
+
const colon = credentials.indexOf(':');
|
|
67
|
+
username = colon === -1 ? credentials : credentials.slice(0, colon);
|
|
68
|
+
password = colon === -1 ? '' : credentials.slice(colon + 1);
|
|
69
|
+
}
|
|
70
|
+
const hostPort = parseHostPort(hostport, scheme);
|
|
71
|
+
if (!hostPort) return null;
|
|
72
|
+
let hash = '';
|
|
73
|
+
const hashAt = tail.indexOf('#');
|
|
74
|
+
if (hashAt !== -1) { hash = tail.slice(hashAt); tail = tail.slice(0, hashAt); }
|
|
75
|
+
let search = '';
|
|
76
|
+
const queryAt = tail.indexOf('?');
|
|
77
|
+
if (queryAt !== -1) { search = tail.slice(queryAt); tail = tail.slice(0, queryAt); }
|
|
78
|
+
let pathname = tail === '' ? '/' : tail;
|
|
79
|
+
if (!/^[/\\\\]/.test(pathname)) pathname = '/' + pathname;
|
|
80
|
+
return {
|
|
81
|
+
protocol: scheme,
|
|
82
|
+
username,
|
|
83
|
+
password,
|
|
84
|
+
hostname: hostPort.hostname,
|
|
85
|
+
port: hostPort.port,
|
|
86
|
+
pathname: encodeLiterals(normalizePath(pathname)),
|
|
87
|
+
search: encodeLiterals(search === '?' ? '' : search),
|
|
88
|
+
hash: encodeLiterals(hash === '#' ? '' : hash),
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const parseUrl = (input, base) => {
|
|
93
|
+
// A tab or newline inside a url is dropped, not encoded \u2014 the host parser does the same, and
|
|
94
|
+
// a url copied out of a wrapped response body is the usual way one gets in here.
|
|
95
|
+
const str = String(input).trim().replace(/[\\t\\n\\r]/g, '');
|
|
96
|
+
const matched = ABSOLUTE_RE.exec(str);
|
|
97
|
+
// A scheme with no \`//\` (mailto:, data:) is not a fetch target, so it is not resolved.
|
|
98
|
+
if (matched) return matched[2] ? parseAuthorityUrl(matched[1].toLowerCase() + ':', matched[3]) : null;
|
|
99
|
+
if (base === undefined) return null;
|
|
100
|
+
const resolved = parseUrl(base, undefined);
|
|
101
|
+
if (!resolved) return null;
|
|
102
|
+
if (/^[/\\\\]{2}/.test(str)) return parseAuthorityUrl(resolved.protocol, str.slice(2));
|
|
103
|
+
const parts = { ...resolved };
|
|
104
|
+
if (str.startsWith('#')) {
|
|
105
|
+
parts.hash = encodeLiterals(str === '#' ? '' : str);
|
|
106
|
+
return parts;
|
|
107
|
+
}
|
|
108
|
+
let rest = str;
|
|
109
|
+
let hash = '';
|
|
110
|
+
const hashAt = rest.indexOf('#');
|
|
111
|
+
if (hashAt !== -1) { hash = rest.slice(hashAt); rest = rest.slice(0, hashAt); }
|
|
112
|
+
let search = '';
|
|
113
|
+
const queryAt = rest.indexOf('?');
|
|
114
|
+
if (queryAt !== -1) { search = rest.slice(queryAt); rest = rest.slice(0, queryAt); }
|
|
115
|
+
if (rest !== '') {
|
|
116
|
+
const absolute = /^[/\\\\]/.test(rest)
|
|
117
|
+
? rest
|
|
118
|
+
: resolved.pathname.slice(0, resolved.pathname.lastIndexOf('/') + 1) + rest;
|
|
119
|
+
parts.pathname = encodeLiterals(normalizePath(absolute));
|
|
120
|
+
}
|
|
121
|
+
// An empty reference with no query of its own keeps the base query (RFC 3986 section 5.3).
|
|
122
|
+
parts.search = rest === '' && search === '' ? resolved.search : encodeLiterals(search === '?' ? '' : search);
|
|
123
|
+
parts.hash = encodeLiterals(hash === '#' ? '' : hash);
|
|
124
|
+
return parts;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const URL = class URL {
|
|
128
|
+
constructor(input, base) {
|
|
129
|
+
const parsed = parseUrl(input, base === undefined ? undefined : String(base));
|
|
130
|
+
if (!parsed) throw new TypeError('Invalid URL: ' + String(input));
|
|
131
|
+
this._parts = parsed;
|
|
132
|
+
this._params = undefined;
|
|
133
|
+
this._edited = false;
|
|
134
|
+
}
|
|
135
|
+
// The query stays exactly as it arrived until searchParams edits it, so reading a url apart
|
|
136
|
+
// and putting it back together does not re-encode what the server sent.
|
|
137
|
+
_applyParams() {
|
|
138
|
+
if (this._edited && this._params) {
|
|
139
|
+
const query = this._params.toString();
|
|
140
|
+
this._parts.search = query ? '?' + query : '';
|
|
141
|
+
this._edited = false;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Every part a caller can read, it can also write. Guest code runs sloppy, so a missing
|
|
145
|
+
// setter would swallow \`url.host = \u2026\` and fetch the address the caller meant to replace.
|
|
146
|
+
get protocol() { return this._parts.protocol; }
|
|
147
|
+
set protocol(value) {
|
|
148
|
+
const next = String(value).toLowerCase().replace(/:$/, '') + ':';
|
|
149
|
+
// Only a scheme that carries an authority can replace one, the way the host parser reads it.
|
|
150
|
+
if (DEFAULT_PORTS[next] !== undefined) this._parts.protocol = next;
|
|
151
|
+
}
|
|
152
|
+
get username() { return this._parts.username; }
|
|
153
|
+
set username(value) { this._parts.username = String(value); }
|
|
154
|
+
get password() { return this._parts.password; }
|
|
155
|
+
set password(value) { this._parts.password = String(value); }
|
|
156
|
+
get hostname() { return this._parts.hostname; }
|
|
157
|
+
set hostname(value) { this._parts.hostname = String(value).toLowerCase(); }
|
|
158
|
+
get port() { return this._parts.port; }
|
|
159
|
+
set port(value) {
|
|
160
|
+
const port = String(value);
|
|
161
|
+
if (port !== '' && !/^[0-9]+$/.test(port)) return;
|
|
162
|
+
this._parts.port = DEFAULT_PORTS[this._parts.protocol] === port ? '' : port;
|
|
163
|
+
}
|
|
164
|
+
get host() {
|
|
165
|
+
return this._parts.port ? this._parts.hostname + ':' + this._parts.port : this._parts.hostname;
|
|
166
|
+
}
|
|
167
|
+
set host(value) {
|
|
168
|
+
const parsed = parseHostPort(String(value), this._parts.protocol);
|
|
169
|
+
if (!parsed) return;
|
|
170
|
+
this._parts.hostname = parsed.hostname;
|
|
171
|
+
if (parsed.hasPort) this._parts.port = parsed.port;
|
|
172
|
+
}
|
|
173
|
+
get origin() { return this._parts.protocol + '//' + this.host; }
|
|
174
|
+
get pathname() { return this._parts.pathname; }
|
|
175
|
+
set pathname(value) {
|
|
176
|
+
const path = String(value);
|
|
177
|
+
this._parts.pathname = encodeLiterals(path.startsWith('/') ? path : '/' + path);
|
|
178
|
+
}
|
|
179
|
+
get search() {
|
|
180
|
+
this._applyParams();
|
|
181
|
+
return this._parts.search;
|
|
182
|
+
}
|
|
183
|
+
set search(value) {
|
|
184
|
+
const query = String(value);
|
|
185
|
+
const body = query.startsWith('?') ? query.slice(1) : query;
|
|
186
|
+
this._parts.search = body ? encodeLiterals('?' + body) : '';
|
|
187
|
+
this._params = undefined;
|
|
188
|
+
this._edited = false;
|
|
189
|
+
}
|
|
190
|
+
get searchParams() {
|
|
191
|
+
if (!this._params) {
|
|
192
|
+
const params = new URLSearchParams(this._parts.search);
|
|
193
|
+
const url = this;
|
|
194
|
+
// Every method that can change the parameters has to mark the query for re-serialization.
|
|
195
|
+
for (const method of ['append', 'set', 'delete', 'sort']) {
|
|
196
|
+
const original = params[method];
|
|
197
|
+
params[method] = function (...args) {
|
|
198
|
+
url._edited = true;
|
|
199
|
+
return original.apply(params, args);
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
this._params = params;
|
|
203
|
+
}
|
|
204
|
+
return this._params;
|
|
205
|
+
}
|
|
206
|
+
get hash() { return this._parts.hash; }
|
|
207
|
+
set hash(value) {
|
|
208
|
+
const fragment = String(value);
|
|
209
|
+
this._parts.hash = fragment === ''
|
|
210
|
+
? ''
|
|
211
|
+
: encodeLiterals(fragment.startsWith('#') ? fragment : '#' + fragment);
|
|
212
|
+
}
|
|
213
|
+
get href() {
|
|
214
|
+
this._applyParams();
|
|
215
|
+
const credentials = this._parts.username
|
|
216
|
+
? this._parts.username + (this._parts.password ? ':' + this._parts.password : '') + '@'
|
|
217
|
+
: '';
|
|
218
|
+
return this._parts.protocol + '//' + credentials + this.host +
|
|
219
|
+
this._parts.pathname + this._parts.search + this._parts.hash;
|
|
220
|
+
}
|
|
221
|
+
set href(value) {
|
|
222
|
+
const parsed = parseUrl(value, undefined);
|
|
223
|
+
if (!parsed) throw new TypeError('Invalid URL: ' + String(value));
|
|
224
|
+
this._parts = parsed;
|
|
225
|
+
this._params = undefined;
|
|
226
|
+
this._edited = false;
|
|
227
|
+
}
|
|
228
|
+
toString() { return this.href; }
|
|
229
|
+
toJSON() { return this.href; }
|
|
230
|
+
static canParse(input, base) {
|
|
231
|
+
return parseUrl(input, base === undefined ? undefined : String(base)) !== null;
|
|
232
|
+
}
|
|
233
|
+
static parse(input, base) {
|
|
234
|
+
const parsed = parseUrl(input, base === undefined ? undefined : String(base));
|
|
235
|
+
return parsed ? new URL(input, base) : null;
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
globalThis.URL = URL;
|
|
239
|
+
`;export{t as URL_POLYFILL};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type { ToolCallback } from '@redocly/mcp-typescript-sdk/server/mcp.js';
|
|
2
1
|
import type { McpToolSchema } from '../../../../types';
|
|
3
|
-
import type { McpToolContext } from '../../types.js';
|
|
2
|
+
import type { McpToolCallback, McpToolContext } from '../../types.js';
|
|
4
3
|
import { type SandboxCapability } from '../capabilities/index.js';
|
|
5
4
|
export declare function describeToolsSchema(tools: McpToolSchema[], capabilities: SandboxCapability[]): Record<string, unknown>;
|
|
6
|
-
export declare function describeTools(tools: McpToolSchema[], isToolAvailable: (toolName: string) => Promise<boolean>, capabilities: SandboxCapability[], context: McpToolContext):
|
|
5
|
+
export declare function describeTools(tools: McpToolSchema[], isToolAvailable: (toolName: string) => Promise<boolean>, capabilities: SandboxCapability[], context: McpToolContext): McpToolCallback;
|
|
7
6
|
//# sourceMappingURL=describe-tools.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{renderDescribeToolsOutput as
|
|
1
|
+
import{renderDescribeToolsOutput as k}from"../capabilities/index.js";import{McpServerType as h}from"../../constants.js";import{reportMcpToolCalled as E,reportMcpToolError as S}from"../../workers/mcp-tool-telemetry.js";import{DESCRIBE_TOOLS_TOOL_NAME as w}from"../constants.js";import{sanitizeToolName as s}from"../build-tool-type-declarations.js";import{UnknownToolError as T}from"./tool-errors.js";function x(i,l){const r=[...new Set([...i.map(n=>s(n.name)),...l.map(n=>s(n.id))])];return{type:"object",required:[],additionalProperties:!1,properties:{tools:{type:"array",description:`Tool names to describe: ${r.join(", ")}. Omit to describe everything.`,items:r.length>0?{type:"string",enum:r}:{type:"string"},nullable:!0}}}}function L(i,l,r,n){const c=new Map(i.map(t=>[s(t.name),t])),p=new Map(r.map(t=>[s(t.id),t]));return async t=>{const m=performance.now(),d=n.mcpMode??"code";try{const o=[...new Set((t.tools??[]).map(s))],a=o.length===0,f=o.filter(e=>!c.has(e)&&!p.has(e));if(f.length>0){const e=[...c.keys(),...p.keys()];throw new T(`Unknown tool(s): ${f.join(", ")}. Valid tools: ${e.join(", ")}.`)}const g=a?r:r.filter(e=>o.includes(s(e.id))),u=a?i:o.filter(e=>c.has(e)).map(e=>c.get(e)),M=await Promise.all(u.map(e=>l(e.name))),b=u.filter((e,v)=>M[v]),y=k(b,g,n);return E({tool:w,mode:d,serverType:h.Docs,durationMs:Math.round(performance.now()-m),outputLength:y.length}),{content:[{type:"text",text:y}]}}catch(o){const a=o instanceof T;throw S({tool:w,mode:d,serverType:h.Docs,durationMs:Math.round(performance.now()-m),message:o instanceof Error?o.message:String(o),stack:!a&&o instanceof Error&&o.stack||"",errorClass:a?"tool_error":"server_error"}),o}}}export{L as describeTools,x as describeToolsSchema};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ToolAnnotations } from '@
|
|
1
|
+
import type { ToolAnnotations } from '@modelcontextprotocol/server';
|
|
2
2
|
import type { McpToolSchema } from '../../../../types';
|
|
3
3
|
export declare const EXECUTE_BASE_ANNOTATIONS: ToolAnnotations;
|
|
4
4
|
export declare const EXECUTE_TOOL_SCHEMA: McpToolSchema;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{mcpToolHandlers as l}from"../../../../../client/mcp-tool-handlers-entry.js";import{runInSandbox as
|
|
1
|
+
import{mcpToolHandlers as l}from"../../../../../client/mcp-tool-handlers-entry.js";import{runInSandbox as p}from"../../../../sandbox/sandbox.js";import{isMcpToolAvailable as h}from"../../workers/execute-mcp-tool.js";import{buildExecuteHostFetch as b,isFetchCapable as E}from"../../gateway-mcp/fetch-capability.js";import{withAuthRequiredMeta as T}from"../../gateway-mcp/upstream-auth.js";import{CODE_MODE_TOOL_NAMES as C,EXECUTE_TOOL_NAME as O}from"../constants.js";import{sanitizeToolName as A}from"../build-tool-type-declarations.js";import{invokeTool as w}from"../invoke-tool.js";import{createSandboxSetup as x}from"../sandbox/create-sandbox-setup.js";import{auditCode as y}from"../code-audit.js";import{reportCodeExecuted as M}from"../code-execution-telemetry.js";import{logCodeExecutionAudit as N}from"../code-execution-audit.js";const v=async(e,t,r)=>{const o=await F(t,r),n=[],i=E(t)?b(t,c=>n.push(c)):void 0;let a=0;const u=i?c=>(a+=1,i(c)):void 0,d=y(e.code);N({audit:d,code:e.code,description:e.description,sessionHandle:e.sessionHandle,toolNames:Object.keys(o),fetchBudget:t.gatewayFetchBudgetPerExec,fetchCapable:i!==void 0});const m=Date.now(),{result:f,error:s}=await p(e.code,{setupContext:x(o,u)});return M({audit:d,fetchCount:a,outcome:s===void 0?"ok":"error",durationMs:Date.now()-m}),T({content:[{type:"text",text:JSON.stringify({result:f,error:s})}],isError:s!==void 0},n)};async function F(e,t){const r={};for(const o of Object.keys(l))C.has(o)||!await h({toolName:o,context:e}).catch(()=>!1)||(r[A(o)]=i=>w(o,i,e,t));return r}var J={[O]:v};export{J as default};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { CallToolResult, InputRequiredResult } from '@modelcontextprotocol/server';
|
|
2
|
+
import type { McpToolSchema } from '../../../../types';
|
|
3
|
+
import type { McpToolContext } from '../../types.js';
|
|
4
|
+
import type { KvService } from '@redocly/config';
|
|
5
|
+
export type ManageApiCredentialsDeps = {
|
|
6
|
+
getClientCapabilities: () => {
|
|
7
|
+
elicitation?: {
|
|
8
|
+
url?: unknown;
|
|
9
|
+
};
|
|
10
|
+
} | undefined;
|
|
11
|
+
getKv: () => Promise<KvService>;
|
|
12
|
+
};
|
|
13
|
+
export declare function isManageApiCredentialsToolAvailable(context: McpToolContext): boolean;
|
|
14
|
+
export declare function manageApiCredentialsSchema(context: McpToolContext): McpToolSchema;
|
|
15
|
+
export declare function manageApiCredentials(context: McpToolContext, deps: ManageApiCredentialsDeps): (args: {
|
|
16
|
+
api?: unknown;
|
|
17
|
+
sessionHandle?: unknown;
|
|
18
|
+
}) => Promise<CallToolResult | InputRequiredResult>;
|
|
19
|
+
//# sourceMappingURL=manage-credentials.d.ts.map
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{MANAGE_API_CREDENTIALS_TOOL_NAME as m}from"../../constants.js";import{buildUpstreamCredentialFallback as h,buildUpstreamCredentialInputRequired as f}from"../../gateway-mcp/elicitation.js";import{getCallableApis as o,isFetchCapable as g}from"../../gateway-mcp/fetch-capability.js";import{secretStore as A}from"../../gateway-mcp/secret-store.js";import{MISSING_SESSION_HANDLE_MESSAGE as v,resolveSecretSubject as b}from"../../gateway-mcp/subject.js";import{resolveUpstreamAuthScheme as y}from"../../gateway-mcp/upstream-auth-scheme.js";function k(e){return!!e.elicitationEnabled&&g(e)}function j(e){const t=o(e).map(n=>n.relativePath);return{name:m,description:["Get a secure link where the user sets, replaces, or removes the credentials this server sends to a documented API.","Call it when the user wants to change or clear an API key, or when a tool result tells you the stored credentials were rejected.","It returns a link for the user to open - never ask for the secret in the conversation and never pass it as an argument."].join(`
|
|
2
|
+
`),annotations:{title:"Manage API credentials",readOnlyHint:!1,openWorldHint:!1},schema:{type:"object",required:["api"],additionalProperties:!1,properties:{api:{type:"string",description:`The API to manage credentials for.${t.length>0?` One of: ${t.join(", ")}.`:""}`,minLength:1,...t.length>0?{enum:t}:{}},sessionHandle:{type:"string",description:"Handle returned by createAnonymousSession. Required while the caller is anonymous, so the credentials are stored against the same subject the API calls use.",minLength:1}}}}}function H(e,i){return async t=>{const n=b(e.user,typeof t.sessionHandle=="string"?t.sessionHandle:void 0);if(n.kind==="invalid")return l(n.message);if(n.kind==="anonymous")return l(v);const{subject:u}=n,s=typeof t.api=="string"?t.api:"",r=o(e).find(a=>a.relativePath===s||a.name===s);if(!r){const a=o(e).map(d=>d.relativePath).join(", ");return l(`Unknown api "${s}". Credentials can only be managed for the APIs this server can call: ${a||"none"}.`)}const c=await i.getKv(),p={subject:u,baseUrl:e.baseUrl??"",target:{api:r.relativePath,name:r.name},auth:await y(e,r.relativePath),kv:c,hasExistingSecret:await A.hasSecret(c,u,r.relativePath),reason:{kind:"requested"}};return i.getClientCapabilities()?.elicitation?.url?await f(p):{content:[{type:"text",text:await h(p)}],isError:!1}}}function l(e){return{content:[{type:"text",text:e}],isError:!0}}export{k as isManageApiCredentialsToolAvailable,H as manageApiCredentials,j as manageApiCredentialsSchema};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class n extends Error{constructor(r){super(r),this.name="UnknownToolError"}}export{n as UnknownToolError};
|
|
@@ -8,11 +8,12 @@ export declare const McpErrorCodes: {
|
|
|
8
8
|
readonly MethodNotFound: -32601;
|
|
9
9
|
readonly InvalidParams: -32602;
|
|
10
10
|
};
|
|
11
|
-
export declare const MCP_PROTOCOL_VERSION = "2025-
|
|
11
|
+
export declare const MCP_PROTOCOL_VERSION = "2025-11-25";
|
|
12
12
|
export declare const DEFAULT_CONNECTION_TIMEOUT_MS = 30000;
|
|
13
13
|
export declare const SSE_KEEPALIVE_INTERVAL_MS = 15000;
|
|
14
14
|
export declare const MAX_DOCUMENTS_PER_CATEGORY = 3;
|
|
15
15
|
export declare const MCP_NOT_ALLOWED_TEMPLATE_ID = "mcp-not-allowed";
|
|
16
16
|
export declare const DEFAULT_DOCS_MCP_SERVER_NAME = "Docs MCP server";
|
|
17
17
|
export declare const CREATE_ANONYMOUS_SESSION_TOOL_NAME = "createAnonymousSession";
|
|
18
|
+
export declare const MANAGE_API_CREDENTIALS_TOOL_NAME = "manageApiCredentials";
|
|
18
19
|
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
1
|
+
import{LATEST_PROTOCOL_VERSION as o}from"@modelcontextprotocol/server";const r={Docs:"docs"},e={InternalError:-32603,ServerError:-32e3,InvalidRequest:-32600,MethodNotFound:-32601,InvalidParams:-32602},_=o,t=3e4,O=15e3,n=3,s="mcp-not-allowed",A="Docs MCP server",T="createAnonymousSession",M="manageApiCredentials";export{T as CREATE_ANONYMOUS_SESSION_TOOL_NAME,t as DEFAULT_CONNECTION_TIMEOUT_MS,A as DEFAULT_DOCS_MCP_SERVER_NAME,M as MANAGE_API_CREDENTIALS_TOOL_NAME,n as MAX_DOCUMENTS_PER_CATEGORY,s as MCP_NOT_ALLOWED_TEMPLATE_ID,_ as MCP_PROTOCOL_VERSION,e as McpErrorCodes,r as McpServerType,O as SSE_KEEPALIVE_INTERVAL_MS};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{shouldHandleMcpAuth as d}from"../auth/auth-handlers.js";import{CREATE_ANONYMOUS_SESSION_TOOL_NAME as m}from"../constants.js";import{GRAPHQL_TYPE_KINDS as o,GRAPHQL_LIST_SCOPES as h}from"./tools/graphql/constants.js";import{OPERATION_METHODS as p}from"./tools/openapi/constants.js";const i={type:"string",nullable:!0},n={limit:{type:"number"},total:{type:"number"},page:{type:"number"},totalPages:{type:"number"}},r={codemodeType:"OpenApiServer[]"},s={codemodeType:"OpenApiSecurityRequirement[]"},l={codemodeType:"Record<string, OpenApiSecurityScheme>"},y={codemodeType:"OpenApiDocument"},f={codemodeType:"OpenApiOperation & { path: string; method: string }"},g={type:"object",additionalProperties:!1,required:[],properties:{filter:{type:"string",description:"API name (or part of it)",minLength:1,nullable:!0},page:{type:"number",description:"Page number",minimum:1,default:1,nullable:!0},limit:{type:"number",description:"Number of APIs per page. Default is 300",minimum:1,default:300,nullable:!0}}},a={type:"object",required:["name"],additionalProperties:!1,properties:{name:{type:"string",description:"API name",minLength:1},version:{type:"string",description:"API version (required when multiple APIs share the same name but differ by version)",nullable:!0}}},b={type:"object",required:["name","path","method"],additionalProperties:!1,properties:{name:{type:"string",description:"API name",minLength:1},path:{type:"string",description:"Endpoint path (e.g. /api/v1/users)",minLength:1},method:{type:"string",description:"HTTP method (GET, POST, PUT, DELETE, etc.)",enum:[...p.map(e=>e.toUpperCase()),...p],minLength:1},version:{type:"string",description:"API version (required when multiple APIs share the same name but differ by version)",nullable:!0}}},S={type:"object",required:[],additionalProperties:!1,properties:{}},v={type:"object",required:[],additionalProperties:!1,properties:{}};function I(e){const t=[{name:"list-apis",description:"Lists available APIs with their context and purpose",schema:g,annotations:{title:"List APIs",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{items:{type:"array",items:{type:"object",properties:{name:{type:"string"},description:i,version:i,servers:r}}},...n}}},{name:"get-endpoints",description:"Get all endpoints for a specific API",schema:a,annotations:{title:"Get Endpoints",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{api:{type:"string"},version:{type:"string"},servers:r,endpoints:{type:"array",items:{type:"object",properties:{path:{type:"string"},method:{type:"string"},summary:i,description:i,security:s}}}}}},{name:"get-endpoint-info",description:"Get comprehensive information about specific endpoint including parameters, security, and examples",schema:b,annotations:{title:"Get Endpoint Info",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{api:{type:"string"},version:{type:"string"},servers:r,endpoint:f,globalSecurity:s,securitySchemes:l}}},{name:"get-security-schemes",description:"Get the security schemes for a specific API",schema:a,annotations:{title:"Get Security Schemes",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{name:i,version:i,securitySchemes:l,security:s}}},{name:"get-full-api-description",description:"Get the complete OpenAPI description",schema:a,annotations:{title:"Get Full API Description",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{api:{type:"string"},version:{type:"string"},definition:y}}},q(e.products,e.locales)];return d(e.requiresLogin,e.rbac)&&t.push({name:"whoami",description:"Get information about the currently authenticated user",schema:S,annotations:{title:"Who Am I",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{email:{type:"string"},name:{type:"string"},subject:{type:"string"},clientId:{type:"string"},scope:{type:"string"},issuedAt:{type:"string"},expiresAt:{type:"string"}}}}),e.hasGraphqlApis&&t.push(...T()),e.elicitationEnabled&&t.push({name:m,description:"Create an anonymous session handle for storing upstream API credentials. Pass the returned sessionHandle to execute when an API needs credentials and the caller is not signed in. Treat it as a secret and reuse it across calls.",schema:v,annotations:{title:"Create anonymous session",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},outputSchema:{type:"object",required:["sessionHandle"],properties:{sessionHandle:{type:"string"}}}}),t}const H={type:"object",required:[],additionalProperties:!1,properties:{filter:{type:"string",description:"GraphQL schema name (or part of it)",minLength:1,nullable:!0},page:{type:"number",description:"Page number",minimum:1,default:1,nullable:!0},limit:{type:"number",description:"Number of results per page. Default is 300",minimum:1,default:300,nullable:!0}}},L={type:"object",required:["name"],additionalProperties:!1,properties:{name:{type:"string",description:"GraphQL schema name",minLength:1},kind:{type:"string",description:'Optional: return only a single category of schema members. With scope "fields", restricts to the fields of that parent category ("objects", "interfaces", or "inputs" only).',enum:[...o],nullable:!0},scope:{type:"string",description:'Optional: what each list item is. "members" (default) lists top-level types and operations. "fields" flattens every field of every object/interface/input type into "ParentType.fieldName" entries with their descriptions \u2014 combine with `filter` to search nested fields anywhere in the schema.',enum:[...h],nullable:!0},filter:{type:"string",description:'Optional: only members whose name or description contains this substring, case-insensitive (e.g. "custom tags")',minLength:1,nullable:!0},referencesType:{type:"string",description:'Optional: only members that reference this type directly or transitively (via return, field, argument, union-member or input-field types). Finds cross-cutting members that never name it \u2014 e.g. referencesType "Book" surfaces a search query returning a union that includes Book.',minLength:1,nullable:!0},page:{type:"number",description:"Page number",minimum:1,default:1,nullable:!0},limit:{type:"number",description:"Number of results per page. Default is 300",minimum:1,default:300,nullable:!0}}},P={type:"object",required:["name","type"],additionalProperties:!1,properties:{name:{type:"string",description:"GraphQL schema name",minLength:1},type:{type:"string",description:"Name of the type, operation, or directive",minLength:1},includeReferencedTypes:{type:"boolean",description:"Also include the SDL of every type referenced transitively. Gives the whole subgraph at once, but can be large for highly-connected types.",default:!1,nullable:!0}}},A={type:"object",required:["name"],additionalProperties:!1,properties:{name:{type:"string",description:"GraphQL schema name",minLength:1}}};function T(){return[{name:"list-graphql-apis",description:"Lists available GraphQL schemas with their context and purpose",schema:H,annotations:{title:"List GraphQL APIs",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{items:{type:"array",items:{type:"object",properties:{name:{type:"string"},description:i}}},...n}}},{name:"get-graphql-type-list",description:'List all operations and types in a GraphQL schema as a flat, paginated list. Each item is tagged with its kind (query, object, enum, \u2026) and carries a one-line description; operations also include their return type and argument signature \u2014 use it to decide which members to fetch in detail. Narrow the result with `kind` (one category), `filter` (case-insensitive substring of the member name or description, e.g. "book"), or `referencesType` (only members that use a given type, directly or transitively) to explore a domain cheaply instead of pulling the full schema. To search nested fields \u2014 not just type names \u2014 set scope "fields": it flattens every field of every object/interface/input type into "ParentType.fieldName" entries with their descriptions, so `filter` finds things like an input field whose docstring mentions your keyword.',schema:L,annotations:{title:"Get GraphQL Type List",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{name:{type:"string"},items:{type:"array",items:{type:"object",properties:{kind:{type:"string",enum:o},name:{type:"string"},description:i,type:{type:"string"},args:{type:"string"},directives:{type:"string"}}}},...n}}},{name:"get-graphql-type",description:'Get the SDL for a single GraphQL type, operation (query/mutation/subscription field), or directive. Without includeReferencedTypes, the SDL is followed by a one-level manifest of every directly referenced type\'s field names (e.g. "input ActionOperationInput { addTags, removeTags }"), so you can see what nested types contain before fetching them. Set includeReferencedTypes to instead pull in the full SDL of every type it references transitively \u2014 the whole structural subgraph in one call, ideal for complete exploration of an entity (e.g. Book and all the types it touches). That traversal only follows what the target depends on; to go the other direction and find the members that use a type, call get-graphql-type-list with `referencesType`.',schema:P,annotations:{title:"Get GraphQL Type",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"string"}},{name:"get-graphql-schema",description:"Get the complete GraphQL schema as SDL \u2014 the whole type graph in one call. Returns everything, so it can be very large and token-expensive for big schemas; prefer get-graphql-type-list (filter/paginate) plus targeted get-graphql-type calls, and reach for this only for small schemas or when you truly need the full SDL.",schema:A,annotations:{title:"Get GraphQL Schema",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"string"}}]}function q(e,t){const c=e&&e.length>0,u=t&&t.length>0;return{name:"search",description:"Search across the documentation to fetch relevant content for a given query",schema:{type:"object",required:["query"],additionalProperties:!1,properties:{query:{type:"string",description:"Search query. Should be a single word or that phrase that is presented in a documentation.",minLength:1},...c?{product:{type:"string",description:"Optional product name to filter search results by specific product.",enum:e,nullable:!0}}:{},...u?{locale:{type:"string",description:"Optional locale code to filter search results by documentation language (e.g. 'en', 'ja'). Use the locale matching the language of the user's request. Defaults to the project's default locale.",enum:t,nullable:!0}}:{}}},annotations:{title:"Search Documentation",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},outputSchema:{type:"string"}}}export{I as getDefaultToolSchemas};
|
|
1
|
+
import{shouldHandleMcpAuth as d}from"../auth/auth-handlers.js";import{CREATE_ANONYMOUS_SESSION_TOOL_NAME as m}from"../constants.js";import{GRAPHQL_TYPE_KINDS as o,GRAPHQL_LIST_SCOPES as h}from"./tools/graphql/constants.js";import{OPERATION_METHODS as p}from"./tools/openapi/constants.js";const i={type:"string",nullable:!0},n={limit:{type:"number"},total:{type:"number"},page:{type:"number"},totalPages:{type:"number"}},r={codemodeType:"OpenApiServer[]"},s={codemodeType:"OpenApiSecurityRequirement[]"},l={codemodeType:"Record<string, OpenApiSecurityScheme>"},y={codemodeType:"OpenApiDocument"},f={codemodeType:"OpenApiOperation & { path: string; method: string }"},g={type:"object",additionalProperties:!1,required:[],properties:{filter:{type:"string",description:"API name (or part of it)",minLength:1,nullable:!0},page:{type:"number",description:"Page number",minimum:1,default:1,nullable:!0},limit:{type:"number",description:"Number of APIs per page. Default is 300",minimum:1,default:300,nullable:!0}}},a={type:"object",required:["name"],additionalProperties:!1,properties:{name:{type:"string",description:"API name",minLength:1},version:{type:"string",description:"API version",nullable:!0},filePath:{type:"string",description:"API definition file path",nullable:!0}}},b={type:"object",required:["name","path","method"],additionalProperties:!1,properties:{name:{type:"string",description:"API name",minLength:1},path:{type:"string",description:"Endpoint path (e.g. /api/v1/users)",minLength:1},method:{type:"string",description:"HTTP method (GET, POST, PUT, DELETE, etc.)",enum:[...p.map(e=>e.toUpperCase()),...p],minLength:1},version:{type:"string",description:"API version (required when multiple APIs share the same name but differ by version)",nullable:!0},filePath:{type:"string",description:"API definition file path",nullable:!0}}},S={type:"object",required:[],additionalProperties:!1,properties:{}},v={type:"object",required:[],additionalProperties:!1,properties:{}};function I(e){const t=[{name:"list-apis",description:"Lists available APIs with their context and purpose",schema:g,annotations:{title:"List APIs",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{items:{type:"array",items:{type:"object",properties:{name:{type:"string"},description:i,version:i,servers:r,filePath:{type:"string",description:"API definition file path"}}}},...n}}},{name:"get-endpoints",description:"Get all endpoints for a specific API",schema:a,annotations:{title:"Get Endpoints",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{api:{type:"string"},version:{type:"string"},servers:r,endpoints:{type:"array",items:{type:"object",properties:{path:{type:"string"},method:{type:"string"},summary:i,description:i,security:s}}}}}},{name:"get-endpoint-info",description:"Get comprehensive information about specific endpoint including parameters, security, and examples",schema:b,annotations:{title:"Get Endpoint Info",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{api:{type:"string"},version:{type:"string"},servers:r,endpoint:f,globalSecurity:s,securitySchemes:l}}},{name:"get-security-schemes",description:"Get the security schemes for a specific API",schema:a,annotations:{title:"Get Security Schemes",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{name:i,version:i,securitySchemes:l,security:s}}},{name:"get-full-api-description",description:"Get the complete OpenAPI description",schema:a,annotations:{title:"Get Full API Description",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{api:{type:"string"},version:{type:"string"},definition:y}}},G(e.products,e.locales)];return d(e.requiresLogin,e.rbac)&&t.push({name:"whoami",description:"Get information about the currently authenticated user",schema:S,annotations:{title:"Who Am I",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{email:{type:"string"},name:{type:"string"},subject:{type:"string"},clientId:{type:"string"},scope:{type:"string"},issuedAt:{type:"string"},expiresAt:{type:"string"}}}}),e.hasGraphqlApis&&t.push(...T()),e.elicitationEnabled&&t.push({name:m,description:"Create an anonymous session handle for storing upstream API credentials. Pass the returned sessionHandle to execute when an API needs credentials and the caller is not signed in. Treat it as a secret and reuse it across calls.",schema:v,annotations:{title:"Create anonymous session",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},outputSchema:{type:"object",required:["sessionHandle"],properties:{sessionHandle:{type:"string"}}}}),t}const H={type:"object",required:[],additionalProperties:!1,properties:{filter:{type:"string",description:"GraphQL schema name (or part of it)",minLength:1,nullable:!0},page:{type:"number",description:"Page number",minimum:1,default:1,nullable:!0},limit:{type:"number",description:"Number of results per page. Default is 300",minimum:1,default:300,nullable:!0}}},P={type:"object",required:["name"],additionalProperties:!1,properties:{name:{type:"string",description:"GraphQL schema name",minLength:1},kind:{type:"string",description:'Optional: return only a single category of schema members. With scope "fields", restricts to the fields of that parent category ("objects", "interfaces", or "inputs" only).',enum:[...o],nullable:!0},scope:{type:"string",description:'Optional: what each list item is. "members" (default) lists top-level types and operations. "fields" flattens every field of every object/interface/input type into "ParentType.fieldName" entries with their descriptions \u2014 combine with `filter` to search nested fields anywhere in the schema.',enum:[...h],nullable:!0},filter:{type:"string",description:'Optional: only members whose name or description contains this substring, case-insensitive (e.g. "custom tags")',minLength:1,nullable:!0},referencesType:{type:"string",description:'Optional: only members that reference this type directly or transitively (via return, field, argument, union-member or input-field types). Finds cross-cutting members that never name it \u2014 e.g. referencesType "Book" surfaces a search query returning a union that includes Book.',minLength:1,nullable:!0},page:{type:"number",description:"Page number",minimum:1,default:1,nullable:!0},limit:{type:"number",description:"Number of results per page. Default is 300",minimum:1,default:300,nullable:!0}}},A={type:"object",required:["name","type"],additionalProperties:!1,properties:{name:{type:"string",description:"GraphQL schema name",minLength:1},type:{type:"string",description:"Name of the type, operation, or directive",minLength:1},includeReferencedTypes:{type:"boolean",description:"Also include the SDL of every type referenced transitively. Gives the whole subgraph at once, but can be large for highly-connected types.",default:!1,nullable:!0}}},L={type:"object",required:["name"],additionalProperties:!1,properties:{name:{type:"string",description:"GraphQL schema name",minLength:1}}};function T(){return[{name:"list-graphql-apis",description:"Lists available GraphQL schemas with their context and purpose",schema:H,annotations:{title:"List GraphQL APIs",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{items:{type:"array",items:{type:"object",properties:{name:{type:"string"},description:i}}},...n}}},{name:"get-graphql-type-list",description:'List all operations and types in a GraphQL schema as a flat, paginated list. Each item is tagged with its kind (query, object, enum, \u2026) and carries a one-line description; operations also include their return type and argument signature \u2014 use it to decide which members to fetch in detail. Narrow the result with `kind` (one category), `filter` (case-insensitive substring of the member name or description, e.g. "book"), or `referencesType` (only members that use a given type, directly or transitively) to explore a domain cheaply instead of pulling the full schema. To search nested fields \u2014 not just type names \u2014 set scope "fields": it flattens every field of every object/interface/input type into "ParentType.fieldName" entries with their descriptions, so `filter` finds things like an input field whose docstring mentions your keyword.',schema:P,annotations:{title:"Get GraphQL Type List",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{name:{type:"string"},items:{type:"array",items:{type:"object",properties:{kind:{type:"string",enum:o},name:{type:"string"},description:i,type:{type:"string"},args:{type:"string"},directives:{type:"string"}}}},...n}}},{name:"get-graphql-type",description:'Get the SDL for a single GraphQL type, operation (query/mutation/subscription field), or directive. Without includeReferencedTypes, the SDL is followed by a one-level manifest of every directly referenced type\'s field names (e.g. "input ActionOperationInput { addTags, removeTags }"), so you can see what nested types contain before fetching them. Set includeReferencedTypes to instead pull in the full SDL of every type it references transitively \u2014 the whole structural subgraph in one call, ideal for complete exploration of an entity (e.g. Book and all the types it touches). That traversal only follows what the target depends on; to go the other direction and find the members that use a type, call get-graphql-type-list with `referencesType`.',schema:A,annotations:{title:"Get GraphQL Type",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"string"}},{name:"get-graphql-schema",description:"Get the complete GraphQL schema as SDL \u2014 the whole type graph in one call. Returns everything, so it can be very large and token-expensive for big schemas; prefer get-graphql-type-list (filter/paginate) plus targeted get-graphql-type calls, and reach for this only for small schemas or when you truly need the full SDL.",schema:L,annotations:{title:"Get GraphQL Schema",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"string"}}]}function G(e,t){const c=e&&e.length>0,u=t&&t.length>0;return{name:"search",description:"Search across the documentation to fetch relevant content for a given query",schema:{type:"object",required:["query"],additionalProperties:!1,properties:{query:{type:"string",description:"Search query. Should be a single word or that phrase that is presented in a documentation.",minLength:1},...c?{product:{type:"string",description:"Optional product name to filter search results by specific product.",enum:e,nullable:!0}}:{},...u?{locale:{type:"string",description:"Optional locale code to filter search results by documentation language (e.g. 'en', 'ja'). Use the locale matching the language of the user's request. Defaults to the project's default locale.",enum:t,nullable:!0}}:{}}},annotations:{title:"Search Documentation",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},outputSchema:{type:"string"}}}export{I as getDefaultToolSchemas};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{resolveParameters as
|
|
1
|
+
import{resolveParameters as f,resolveRequestBody as l,resolveResponses as y}from"../../utils.js";import{isMcpEndpoint as x}from"./utils.js";import{loadApiDescription as E}from"./load-api-description.js";import{checkEndpointAndDeleteXMcp as v}from"../../../utils/xmcp-utils.js";const g=async(s,i)=>{const{path:o,method:r}=s;if(!o||!r)return{content:[{type:"text",text:'Both "path" and "method" are required (e.g. path: "/tickets", method: "GET").'}],isError:!0};let t;try{t=await E(s,i)}catch(h){return{content:[{type:"text",text:h.message}],isError:!0}}const p=o.startsWith("/")?o:`/${o}`,{title:a=""}=t.info||{},n=t.paths?.[p],c=r.toLowerCase();if(!n)return{content:[{type:"text",text:"Endpoint not found"}],isError:!0};const e=n[c];if(!x(e)||!v(e,"docs"))return{content:[{type:"text",text:"Endpoint not found"}],isError:!0};const d=n?.parameters||[],m=e.parameters||[],u={...e,parameters:f({pathParams:d,opParams:m,definition:t}),requestBody:l(e.requestBody,t),responses:y(e.responses,t)};return{content:[{type:"text",text:JSON.stringify({api:a,version:t.info?.version||"",servers:t.servers||[],endpoint:{path:o,method:r.toUpperCase(),...u},globalSecurity:t.security||[],securitySchemes:t.components?.securitySchemes||{}},null,2)}]}};var w={"get-endpoint-info":g};export{w as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{loadApiDescription as
|
|
1
|
+
import{loadApiDescription as i}from"./load-api-description.js";import{getEndpointsFromPaths as s}from"./utils.js";const p=async(n,r)=>{let t;try{t=await i(n,r)}catch(o){return{content:[{type:"text",text:o.message}],isError:!0}}const e=s(t);return e.length===0?{content:[{type:"text",text:"No endpoints found"}]}:{content:[{type:"text",text:JSON.stringify({api:t.info?.title||"",version:t.info?.version||"",servers:t.servers||[],endpoints:e},null,2)}]}};var d={"get-endpoints":p};export{d as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{loadApiDescription as
|
|
1
|
+
import{loadApiDescription as n}from"./load-api-description.js";const o=async(e,i)=>{let t;try{t=await n(e,i)}catch(r){return{content:[{type:"text",text:r.message}],isError:!0}}return{content:[{type:"text",text:JSON.stringify({api:t.info?.title||"",version:t.info?.version||"",definition:t},null,2)}]}};var s={"get-full-api-description":o};export{s as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{loadApiDescription as
|
|
1
|
+
import{loadApiDescription as s}from"./load-api-description.js";const c=async(t,r)=>{let e;try{e=await s(t,r)}catch(i){return{content:[{type:"text",text:i.message}],isError:!0}}return{content:[{type:"text",text:JSON.stringify({name:e.info?.title,version:e.info?.version,securitySchemes:e.components?.securitySchemes||{},security:e.security||[]},null,2)}]}};var o={"get-security-schemes":c};export{o as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{filterApiDescriptionsByName as
|
|
1
|
+
import{filterApiDescriptionsByName as g}from"../../utils.js";const h=async(l,a)=>{const{filter:i,page:n=1,limit:e=300}=l;let t=Object.values(a.apiDescriptionsMap);i&&(t=g(t,i));const s=(n-1)*e,o=s+e,c=Math.ceil(t.length/e),p=t.length;return t=t.slice(s,o),t.length===0?{content:[{type:"text",text:"No APIs available"}]}:{content:[{type:"text",text:JSON.stringify({items:t.map(({relativePath:r,fetchEligible:x,...f})=>({filePath:r,...f})),limit:e,total:p,page:n,totalPages:c})}]}};var u={"list-apis":h};export{u as default};
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { OpenAPIDefinition } from '@redocly/api-docs';
|
|
2
2
|
import type { McpToolContext } from '../../../types.js';
|
|
3
|
-
export declare function loadApiDescription(name
|
|
3
|
+
export declare function loadApiDescription({ name, version, filePath }: {
|
|
4
|
+
name: string;
|
|
5
|
+
version?: string;
|
|
6
|
+
filePath?: string;
|
|
7
|
+
}, context: McpToolContext): Promise<OpenAPIDefinition>;
|
|
4
8
|
//# sourceMappingURL=load-api-description.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{findApiDescriptionByNameAndVersion as
|
|
1
|
+
import{findApiDescriptionByNameAndVersion as p}from"../../utils.js";import{getApiDescriptionFromFs as u}from"./utils.js";async function g({name:o,version:n,filePath:r},i){const s=r?Object.values(i.apiDescriptionsMap).find(c=>c.relativePath===r):p(i.apiDescriptionsMap,o,n);if(!s)throw new Error(r?`No API found with filePath "${r}". File paths are returned by the list-apis tool.`:`No API found matching "${o}".`);const t=!!i?.config?.access?.requiresLogin,a=i?.config?.access?.rbac||{},e=await u({relativePath:s.relativePath||"",outdir:i.outdir||"",user:i.user,rbac:a,requiresLogin:t});if(!e)throw new Error(`No API found matching "${r||o}".`);return e}export{g as loadApiDescription};
|
|
@@ -6,11 +6,13 @@ export type ListApisArgs = {
|
|
|
6
6
|
export type ApiNameVersionArgs = {
|
|
7
7
|
name: string;
|
|
8
8
|
version?: string;
|
|
9
|
+
filePath?: string;
|
|
9
10
|
};
|
|
10
11
|
export type GetEndpointInfoArgs = {
|
|
11
12
|
name: string;
|
|
12
13
|
path: string;
|
|
13
14
|
method: string;
|
|
14
15
|
version?: string;
|
|
16
|
+
filePath?: string;
|
|
15
17
|
};
|
|
16
18
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function v(
|
|
1
|
+
function v(e,o,s){if(!o)return;const r=o.toLowerCase(),t=Object.values(e).filter(({name:f})=>f.toLowerCase()===r);if(t.length===0)return;const n=s?t.filter(f=>f.version===s):t;if(n.length===0)return;if(n.length===1)return n[0];if(new Set(n.map(({version:f})=>f||"")).size>1){const f=n.map(({version:l})=>l||"(no version)").join(", ");throw new Error(`Multiple versions of "${o}" found: ${f}. Specify a version to disambiguate.`)}const i=n.map(({relativePath:f})=>`"${f||"(unknown path)"}"`).join(", "),u=n[0].version?` at version "${n[0].version}"`:"";throw new Error(`Multiple APIs are titled "${o}"${u}. Pass the "filePath" parameter to select one of: ${i}.`)}function h(e,o){return Object.values(e).filter(({name:s})=>s.toLowerCase().includes(o.toLowerCase()))}function d(e,o){if(!o||!e?.startsWith("#/"))return;const s=e.slice(2).split("/");let r=o;for(const t of s)if(typeof r=="object"&&r!==null&&t in r)r=r[t];else return;return r}function p(e,o){const s=Object.fromEntries(Object.entries(e).filter(([r,t])=>r!=="$ref"&&t!==void 0));return{...o,...s}}function a({schema:e,definition:o,visitedRefs:s=new Set,maxDepth:r=50,currentDepth:t=0}){if(!e)return;if(t>=r)return{type:"object",description:`Maximum resolution depth exceeded (${r})`};if("$ref"in e&&e.$ref){if(s.has(e.$ref))return{type:"object",description:`Circular reference detected: ${e.$ref}`,"x-circular-ref":e.$ref};const i=new Set(s);i.add(e.$ref);const u=d(e.$ref,o);if(!u)return{type:"object",description:`Could not resolve reference: ${e.$ref}`,"x-unresolved-ref":e.$ref};const f=a({schema:u,definition:o,visitedRefs:i,maxDepth:r,currentDepth:t+1});return f&&p(e,f)}const n={...e};if(n.properties){const i={};for(const[u,f]of Object.entries(n.properties)){const l=a({schema:f,definition:o,visitedRefs:new Set(s),maxDepth:r,currentDepth:t+1});i[u]=l||f}n.properties=i}if(n.items&&typeof n.items=="object")if(Array.isArray(n.items))n.items=n.items.map(i=>a({schema:i,definition:o,visitedRefs:new Set(s),maxDepth:r,currentDepth:t+1})||i);else{const i=a({schema:n.items,definition:o,visitedRefs:new Set(s),maxDepth:r,currentDepth:t+1});i&&(n.items=i)}return["allOf","anyOf","oneOf"].forEach(i=>{const u=n[i];Array.isArray(u)&&(n[i]=u.reduce((f,l)=>{const m=a({schema:l,definition:o,visitedRefs:new Set(s),maxDepth:r,currentDepth:t+1});return m&&f.push(m),f},[]))}),n}function w({pathParams:e,opParams:o,definition:s}){return[...e,...o].reduce((r,t)=>{if(!t)return r;if("$ref"in t&&t.$ref){const n=d(t.$ref,s);if(!n)return r;const c=p(t,n);return r.push({...c,schema:"schema"in c&&c.schema?a({schema:c.schema,definition:s}):void 0}),r}return r.push({...t,schema:"schema"in t&&t.schema?a({schema:t.schema,definition:s}):void 0}),r},[])}function $(e,o){const s={...e};if(e.example!==void 0&&(s.example=e.example),!e.examples)return s;const r={};for(const[t,n]of Object.entries(e.examples))if("$ref"in n){const c=d(n.$ref,o);c&&(r[t]=c)}else r[t]=n;return s.examples=r,s}function b(e,o){if(!e)return;const s="$ref"in e&&e.$ref?d(e.$ref,o):e;if(!s)return;const r=p(e,s),t={...r};if("content"in r&&r.content){const n={};for(const[c,i]of Object.entries(r.content))n[c]={...$(i,o),schema:i.schema?a({schema:i.schema,definition:o}):void 0};t.content=n}return t}function j(e,o){return Object.entries(e).reduce((s,[r,t])=>{const n="$ref"in t&&t.$ref?d(t.$ref,o):t;if(!n)return s;const c=p(t,n),i={...c};if("content"in c&&c.content){const u={};for(const[f,l]of Object.entries(c.content))u[f]={...$(l,o),schema:l.schema?a({schema:l.schema,definition:o}):void 0};i.content=u}return s[r]=i,s},{})}export{h as filterApiDescriptionsByName,v as findApiDescriptionByNameAndVersion,w as resolveParameters,d as resolveRef,b as resolveRequestBody,j as resolveResponses,a as resolveSchemaRefs};
|
|
@@ -1,27 +1,48 @@
|
|
|
1
|
+
import type { InputRequiredResult } from '@modelcontextprotocol/server';
|
|
1
2
|
import type { UpstreamAuthRequiredEvent } from './upstream-auth.js';
|
|
2
3
|
import type { UpstreamAuthScheme } from './upstream-auth-scheme.js';
|
|
3
4
|
import type { KvService } from '@redocly/config';
|
|
4
|
-
export declare const
|
|
5
|
-
export type
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
5
|
+
export declare const CREDENTIALS_INPUT_REQUEST_KEY = "credentials";
|
|
6
|
+
export type CredentialTarget = {
|
|
7
|
+
api: string;
|
|
8
|
+
name: string;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Why the prompt is being raised. `authFailure` is the automatic path behind an upstream 401;
|
|
12
|
+
* `requested` is the model calling the credentials tool on the user's behalf.
|
|
13
|
+
*/
|
|
14
|
+
export type CredentialElicitationReason = {
|
|
15
|
+
kind: 'authFailure';
|
|
16
|
+
status: number;
|
|
17
|
+
otherApiNames?: string[];
|
|
15
18
|
} | {
|
|
16
|
-
|
|
19
|
+
kind: 'requested';
|
|
17
20
|
};
|
|
18
|
-
|
|
19
|
-
sendRequest: SendElicitRequest;
|
|
20
|
-
clientSupportsElicitation: boolean;
|
|
21
|
+
type CredentialElicitationRequest = {
|
|
21
22
|
subject: string;
|
|
22
23
|
baseUrl: string;
|
|
23
|
-
|
|
24
|
+
target: CredentialTarget;
|
|
24
25
|
auth: UpstreamAuthScheme;
|
|
25
26
|
kv: KvService;
|
|
26
|
-
|
|
27
|
+
hasExistingSecret: boolean;
|
|
28
|
+
reason: CredentialElicitationReason;
|
|
29
|
+
};
|
|
30
|
+
export declare function isCredentialElicitationDeclined(inputResponses: Record<string, unknown> | undefined): boolean;
|
|
31
|
+
export declare function targetOf(event: UpstreamAuthRequiredEvent): CredentialTarget;
|
|
32
|
+
/**
|
|
33
|
+
* The 2026-07-28-era credentials prompt: the tool call returns an input-required result the
|
|
34
|
+
* client fulfils and retries, with no push `elicitation/create` and no session behind it.
|
|
35
|
+
* Nothing has to survive the round trip - the secrets are keyed by `subject`, which is
|
|
36
|
+
* recomputed from the caller's identity on every request.
|
|
37
|
+
*/
|
|
38
|
+
export declare function buildUpstreamCredentialInputRequired(request: CredentialElicitationRequest): Promise<InputRequiredResult>;
|
|
39
|
+
/** Fallback text for a client without URL-elicitation support. */
|
|
40
|
+
export declare function buildUpstreamCredentialFallback(request: CredentialElicitationRequest): Promise<string>;
|
|
41
|
+
/**
|
|
42
|
+
* Text for an authorization failure we deliberately do NOT prompt for: a stored secret the
|
|
43
|
+
* upstream rejected, or a 403/407 that is more likely a permissions problem than a missing key.
|
|
44
|
+
* Minting another link here is what produced the credentials-link loop.
|
|
45
|
+
*/
|
|
46
|
+
export declare function buildCredentialsToolHint(event: UpstreamAuthRequiredEvent): string;
|
|
47
|
+
export {};
|
|
27
48
|
//# sourceMappingURL=elicitation.d.ts.map
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import{
|
|
2
|
-
`)}
|
|
1
|
+
import{inputRequired as c,inputResponse as f}from"@modelcontextprotocol/server";import{MANAGE_API_CREDENTIALS_TOOL_NAME as T}from"../constants.js";import{secretStore as $,ELICITATION_TTL_MS as A}from"./secret-store.js";import{ANONYMOUS_SUBJECT_PREFIX as l}from"./subject.js";const h="credentials";function C(e){const t=f(e,h);return t.kind==="elicit"&&(t.action==="decline"||t.action==="cancel")}function _(e){return{api:e.api,name:e.name}}async function b(e){const{url:t}=await d(e);return c({inputRequests:{[h]:c.elicitUrl({message:P(e.target.name,e.hasExistingSecret),url:t})}})}async function O(e){const{url:t}=await d(e);return y(e,t,I(e.subject))}function R(e){return[e.status===401?`The stored credentials for "${e.name}" were rejected (HTTP 401).`:`The "${e.name}" API rejected the request with HTTP ${e.status}.`,"",`Do not retry the same call. Call \`${T}\` with api: "${e.api}" 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(`
|
|
2
|
+
`)}async function d({kv:e,subject:t,baseUrl:n,target:i,auth:a,hasExistingSecret:o}){const r=await $.createElicitation(e,{subject:t,api:i.api,name:i.name,auth:a,hasExistingSecret:o});return{token:r,url:g(n,r)}}function I(e){return e.startsWith(l)?e.slice(l.length):void 0}function P(e,t){const n=`The link works for ${u()} minutes.`;return t?`Open this page in a browser to replace or remove the stored credentials for the "${e}" API. They go straight to the API and never enter the chat. ${n}`:`The "${e}" API needs credentials. Open this page in a browser to paste them - they go straight to the API and never enter the chat. ${n}`}function u(){return Math.round(A/6e4)}function g(e,t){if(!e)return`/mcp/credentials?token=${t}`;try{const n=new URL("/mcp/credentials",e);return n.searchParams.set("token",t),n.toString()}catch{return`${e}/mcp/credentials?token=${t}`}}function y({target:e,reason:t,hasExistingSecret:n},i,a){const r=[`**Stop and show the user this link. They have to open it in a browser to ${n?`replace or remove the stored credentials for the "${e.name}" API`:`add credentials for the "${e.name}" API`} - nothing continues until they do.**`,"",i,"",`The link is valid ${u()} 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} and needs credentials.`:n?`Credentials are already stored for the "${e.name}" API; the link lets the user replace or remove them.`:`The "${e.name}" API needs credentials.`,a?`Once the user confirms they saved them, call this tool again with sessionHandle: "${a}" - 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."],s=t.kind==="authFailure"?t.otherApiNames??[]:[];if(s.length>0){const p=s.map(m=>`"${m}"`).join(", ");r.push("",`The remaining APIs (${p}) will prompt for credentials on subsequent calls.`)}return r.join(`
|
|
3
|
+
`)}export{h as CREDENTIALS_INPUT_REQUEST_KEY,R as buildCredentialsToolHint,O as buildUpstreamCredentialFallback,b as buildUpstreamCredentialInputRequired,C as isCredentialElicitationDeclined,_ as targetOf};
|