@redocly/realm 0.136.0-next.13 → 0.136.0-next.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/constants/api.d.ts +1 -0
  3. package/dist/constants/api.js +1 -1
  4. package/dist/server/constants/common.d.ts +1 -0
  5. package/dist/server/constants/common.js +1 -1
  6. package/dist/server/plugins/mcp/audit/audit-log.d.ts +36 -0
  7. package/dist/server/plugins/mcp/audit/audit-log.js +1 -0
  8. package/dist/server/plugins/mcp/audit/constants.d.ts +6 -0
  9. package/dist/server/plugins/mcp/audit/constants.js +1 -0
  10. package/dist/server/plugins/mcp/audit/secret-patterns.d.ts +2 -0
  11. package/dist/server/plugins/mcp/audit/secret-patterns.js +1 -0
  12. package/dist/server/plugins/mcp/codemode/build-tool-list.d.ts +12 -0
  13. package/dist/server/plugins/mcp/codemode/build-tool-list.js +2 -0
  14. package/dist/server/plugins/mcp/codemode/capabilities/fetch/index.js +2 -2
  15. package/dist/server/plugins/mcp/codemode/capabilities/types.d.ts +1 -0
  16. package/dist/server/plugins/mcp/codemode/capabilities/utils.js +3 -3
  17. package/dist/server/plugins/mcp/codemode/code-execution-audit.d.ts +17 -0
  18. package/dist/server/plugins/mcp/codemode/code-execution-audit.js +1 -0
  19. package/dist/server/plugins/mcp/codemode/prompts.d.ts +6 -3
  20. package/dist/server/plugins/mcp/codemode/prompts.js +7 -4
  21. package/dist/server/plugins/mcp/codemode/tools/describe-tools.js +1 -1
  22. package/dist/server/plugins/mcp/codemode/tools/execute.js +1 -1
  23. package/dist/server/plugins/mcp/codemode/tools/tool-errors.d.ts +4 -0
  24. package/dist/server/plugins/mcp/codemode/tools/tool-errors.js +1 -0
  25. package/dist/server/plugins/mcp/docs-mcp/tools/openapi/get-endpoint-info.js +1 -1
  26. package/dist/server/plugins/mcp/docs-mcp/utils.js +1 -1
  27. package/dist/server/plugins/mcp/gateway-mcp/fetch/fetch-audit.d.ts +24 -0
  28. package/dist/server/plugins/mcp/gateway-mcp/fetch/fetch-audit.js +1 -0
  29. package/dist/server/plugins/mcp/gateway-mcp/fetch/fetch-bridge.js +1 -1
  30. package/dist/server/plugins/mcp/servers/docs-server.js +1 -1
  31. package/dist/server/plugins/mcp/workers/execute-mcp-tool.js +1 -1
  32. package/dist/server/plugins/mcp/workers/mcp-tool-telemetry.d.ts +11 -0
  33. package/dist/server/plugins/mcp/workers/mcp-tool-telemetry.js +1 -1
  34. package/dist/server/tools/notifiers/formatter.d.ts +1 -0
  35. package/dist/server/tools/notifiers/formatter.js +3 -3
  36. package/dist/server/tools/notifiers/logger.d.ts +1 -0
  37. package/dist/server/tools/notifiers/logger.js +2 -2
  38. package/dist/server/web-server/routes/ask-ai.d.ts +5 -0
  39. package/dist/server/web-server/routes/ask-ai.js +1 -1
  40. package/dist/server/web-server/routes/index.js +1 -1
  41. package/dist/server/web-server/routes/mcp-routes/mcp-server-card.js +1 -1
  42. package/package.json +11 -11
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @redocly/realm
2
2
 
3
+ ## 0.136.0-next.14
4
+
5
+ ### Patch Changes
6
+
7
+ - c6e9480ee86: Added auditing to the experimental gateway MCP.
8
+ - 7c05ebc08b6: Fixed endpoint details returned by the Docs MCP server to correctly display documentation for individual endpoints.
9
+ - 330a3197c9e: Fixed security vulnerabilities `CVE-2026-67214` and `CVE-2026-67213` by upgrading `nanoid` to version `5.1.16`.
10
+ - 330a3197c9e: Fixed security vulnerability `GHSA-5p4m-2wfm-xmqj` by upgrading `js-yaml` to version `4.3.1`.
11
+ - Updated dependencies [c6e9480ee86]
12
+ - Updated dependencies [330a3197c9e]
13
+ - @redocly/realm-asyncapi-sdk@0.14.0-next.4
14
+ - @redocly/api-docs@0.1.0-next.6
15
+ - @redocly/openapi-docs@3.24.0-next.12
16
+ - @redocly/theme@0.68.0-next.8
17
+ - @redocly/portal-plugin-mock-server@0.21.0-next.12
18
+ - @redocly/asyncapi-docs@1.13.0-next.12
19
+ - @redocly/graphql-docs@1.13.0-next.12
20
+
3
21
  ## 0.136.0-next.13
4
22
 
5
23
  ### Patch Changes
@@ -34,6 +34,7 @@ export declare const ServerRoutes: {
34
34
  readonly HEALTH: '/_health';
35
35
  readonly ASK_AI: '/_ask-ai';
36
36
  readonly AI_ASSISTANT_ESCALATIONS: '/_ai-assistant-escalations';
37
+ readonly AI_ASSISTANT_FEEDBACK: '/_ai-assistant-feedback';
37
38
  readonly CATALOG_ENTITIES: "/catalog-entities/:entityId?";
38
39
  readonly CATALOG_ENTITIES_RELATIONS: "/catalog-entities-relations/:entityRelationId?";
39
40
  readonly BFF_CATALOG_ENTITIES: "/bff/catalog-entities/:entityKey?";
@@ -1 +1 @@
1
- const _="_auth",e="entityKey",t="entityId",A="entityRelationId",T={CORS_PROXY:"/_api/cors",INFO:"/_info",EJECT_COMPONENT:"/eject-component/:componentName",SHARED_PAGE_DATA:"/page-data/shared/*",PAGE_DATA:"/page-data/*",APP_DATA:"/app-data.json",AUTHORIZATION:"/authorize",OIDC_CALLBACK:`/${_}/oidc`,SAML_CALLBACK:`/${_}/saml2`,REDOCLY_CALLBACK:`/${_}/redocly`,REPLAY_OAUTH2_CALLBACK:`/${_}/replay-oauth2`,REDOCLY_LOGIN_CALLBACK:`/${_}/redocly/redocly-login`,REDOCLY_TOKEN_LOGIN:`/${_}/redocly-token-login`,SEARCH:"/_search",SEARCH_FACETS:"/_search-facets",SEMANTIC_SEARCH:"/_semantic-search",LOGOUT:"/logout",POST_LOGOUT:"/post-logout",LOGIN:"/login",IDP_LOGIN:`/${_}/idp-login`,INVITE:"/invite/:code",RESOLVE_ROUTE_BY_PATH:"/resolve-route-by-path",RESOLVE_ROUTES_BY_PATHS:"/resolve-routes-by-paths",RESOLVE_ROUTE_BY_SLUG:"/resolve-route-by-slug",GET_ROUTES_BY_LINE:"/get-routes-by-line",FEEDBACK:"/feedback",TELEMETRY:"/_events",OTEL_TRACES:"/_otel/v1/traces",HEALTH:"/_health",ASK_AI:"/_ask-ai",AI_ASSISTANT_ESCALATIONS:"/_ai-assistant-escalations",CATALOG_ENTITIES:`/catalog-entities/:${t}?`,CATALOG_ENTITIES_RELATIONS:`/catalog-entities-relations/:${A}?`,BFF_CATALOG_ENTITIES:`/bff/catalog-entities/:${e}?`,BFF_CATALOG_RELATED_ENTITIES:`/bff/catalog-related-entities/:${e}`,BFF_CATALOG_REVISIONS:"/bff/catalog-revisions",MCP_OAUTH_AUTHORIZATION_SERVER:"/.well-known/oauth-authorization-server",MCP_OAUTH_PROTECTED_RESOURCE:"/.well-known/oauth-protected-resource",MCP_DYNAMIC_CLIENT_REGISTRATION:"/_mcp/register",MCP_AUTHORIZATION:"/_mcp/oauth2/auth",MCP_TOKEN_PORTAL:"/_mcp/oauth2/token-portal",MCP_CALLBACK:"/_mcp/oauth/callback",SCORECARDS:"/scorecards"};export{_ as AUTH_SEGMENT,t as CATALOG_ENTITY_ID,e as CATALOG_ENTITY_KEY,A as CATALOG_ENTITY_RELATION_ID,T as ServerRoutes};
1
+ const _="_auth",e="entityKey",t="entityId",A="entityRelationId",T={CORS_PROXY:"/_api/cors",INFO:"/_info",EJECT_COMPONENT:"/eject-component/:componentName",SHARED_PAGE_DATA:"/page-data/shared/*",PAGE_DATA:"/page-data/*",APP_DATA:"/app-data.json",AUTHORIZATION:"/authorize",OIDC_CALLBACK:`/${_}/oidc`,SAML_CALLBACK:`/${_}/saml2`,REDOCLY_CALLBACK:`/${_}/redocly`,REPLAY_OAUTH2_CALLBACK:`/${_}/replay-oauth2`,REDOCLY_LOGIN_CALLBACK:`/${_}/redocly/redocly-login`,REDOCLY_TOKEN_LOGIN:`/${_}/redocly-token-login`,SEARCH:"/_search",SEARCH_FACETS:"/_search-facets",SEMANTIC_SEARCH:"/_semantic-search",LOGOUT:"/logout",POST_LOGOUT:"/post-logout",LOGIN:"/login",IDP_LOGIN:`/${_}/idp-login`,INVITE:"/invite/:code",RESOLVE_ROUTE_BY_PATH:"/resolve-route-by-path",RESOLVE_ROUTES_BY_PATHS:"/resolve-routes-by-paths",RESOLVE_ROUTE_BY_SLUG:"/resolve-route-by-slug",GET_ROUTES_BY_LINE:"/get-routes-by-line",FEEDBACK:"/feedback",TELEMETRY:"/_events",OTEL_TRACES:"/_otel/v1/traces",HEALTH:"/_health",ASK_AI:"/_ask-ai",AI_ASSISTANT_ESCALATIONS:"/_ai-assistant-escalations",AI_ASSISTANT_FEEDBACK:"/_ai-assistant-feedback",CATALOG_ENTITIES:`/catalog-entities/:${t}?`,CATALOG_ENTITIES_RELATIONS:`/catalog-entities-relations/:${A}?`,BFF_CATALOG_ENTITIES:`/bff/catalog-entities/:${e}?`,BFF_CATALOG_RELATED_ENTITIES:`/bff/catalog-related-entities/:${e}`,BFF_CATALOG_REVISIONS:"/bff/catalog-revisions",MCP_OAUTH_AUTHORIZATION_SERVER:"/.well-known/oauth-authorization-server",MCP_OAUTH_PROTECTED_RESOURCE:"/.well-known/oauth-protected-resource",MCP_DYNAMIC_CLIENT_REGISTRATION:"/_mcp/register",MCP_AUTHORIZATION:"/_mcp/oauth2/auth",MCP_TOKEN_PORTAL:"/_mcp/oauth2/token-portal",MCP_CALLBACK:"/_mcp/oauth/callback",SCORECARDS:"/scorecards"};export{_ as AUTH_SEGMENT,t as CATALOG_ENTITY_ID,e as CATALOG_ENTITY_KEY,A as CATALOG_ENTITY_RELATION_ID,T as ServerRoutes};
@@ -32,6 +32,7 @@ export declare const ENTITLEMENTS_JWKS_URL: string;
32
32
  export declare const FEEDBACK_API_URL: string;
33
33
  export declare const ASK_AI_API_URL: string;
34
34
  export declare const AI_ASSISTANT_ESCALATIONS_API_URL: string;
35
+ export declare const AI_ASSISTANT_FEEDBACK_API_URL: string;
35
36
  export declare const SEMANTIC_SEARCH_API_URL: string;
36
37
  export declare const CACHE_CONTROL_NO_STORE_HEADER_VALUE = "no-store";
37
38
  export declare const DEFAULT_IMMUTABLE_CACHE_MAX_AGE: number;
@@ -1 +1 @@
1
- import{ServerRoutes as I}from"../../constants/api.js";import{USER_THEME_ALIAS as L}from"../../constants/common.js";import{envConfig as _}from"../config/env-config.js";const N="runtime",x="/assets",U="/_spec",a="/_bundle",M="static",P="redocly:::linkOriginal",l="@",i="default",m=new RegExp("((?!@i18n\\/|@l10n\\/)\\/?@.*?(?=\\/))","g"),F="@redocly/theme/plugin.js",H="styles.css",u=["@theme/plugin.js","@theme/plugin.cjs","@theme/plugin.mjs"],d=_.JWT_SECRET_KEY||"test_secret_key",G=3600,K=1440*60,c="@i18n",S="@l10n",Y=[L,c,S],B="https://redocly.com/auth/aud",g="catalog.json",h="scorecard.json",J="page-count.json",W="sitemap.xml",f="llms.txt",o=_.PROJECT_ID,R=_.PROJECT_SLUG,e=_.ORGANIZATION_ID,n=_.ORG_SLUG,E=_.BH_API_URL,s=E&&n&&R?`${E}/orgs/${n}/projects/${R}`:void 0,t=E&&e&&o?`${E}/orgs/${e}/projects/${o}`:void 0,p=E?`${E}/entitlements/.well-known/jwks`:"https://app.cloud.redocly.com/api/entitlements/.well-known/jwks",j=_.ENTITLEMENTS_JWKS_CDN_URL||p,k=s?`${s}/feedback`:"",w=t?`${t}/ai-agentic-conversations`:"",$=t?`${t}/ai-assistant-escalations`:"",V=t?`${t}/semantic-search`:"",X="no-store",v=365*24*60*60,y="X-Redocly-Cache-Control",b="no-cache",Z="public, s-maxage=86400, max-age=10, must-revalidate",q=_.AUTH_URL,z="Developer Documentation",T=_.REDOCLY_SSR_RENDER_MODE==="worker"?"worker":"main",Q=T==="main"?0:_.REDOCLY_SSR_WORKERS_MIN!=null?Number(_.REDOCLY_SSR_WORKERS_MIN):2,__=T==="main"?1:_.REDOCLY_SSR_WORKERS_MAX!=null?Number(_.REDOCLY_SSR_WORKERS_MAX):5,E_="-----BEGIN CERTIFICATE-----",t_="-----END CERTIFICATE-----",o_=!_.isDevelopMode&&_.REDOCLY_TELEMETRY!=="off"||_.REDOCLY_TELEMETRY==="on",R_="http://localhost:4318/v1/traces",e_=[I.HEALTH],A=_.SERVER_EDITOR_APP_URL||"http://127.0.0.1:3000",n_=_.REDOCLY_CORS_ORIGINS?_.REDOCLY_CORS_ORIGINS.split(",").map(r=>r.trim()).filter(Boolean).concat(A):[A],s_="markdown/partials",T_="markdown/partials-deps";export{$ as AI_ASSISTANT_ESCALATIONS_API_URL,n_ as ALLOWED_CORS_ORIGINS,B as ALTERNATIVE_AUD_CLAIM_NAME,w as ASK_AI_API_URL,q as AUTH_URL,E_ as BEGIN_CERTIFICATE_DELIMITER,s as BH_API_PROJECTS_URL,t as BH_API_PROJECTS_URL_V2,X as CACHE_CONTROL_NO_STORE_HEADER_VALUE,b as CACHE_CONTROL_PUBLIC_HTML,g as CATALOG_OUTPUT_FILE_NAME,v as DEFAULT_IMMUTABLE_CACHE_MAX_AGE,u as DEFAULT_PROJECT_PLUGIN_PATHS,F as DEFAULT_THEME_PLUGIN_PATH,z as DEFAULT_TITLE,i as DEFAULT_VERSION_NAME,U as DEPRECATED_PUBLIC_API_DEFINITIONS_FOLDER,t_ as END_CERTIFICATE_DELIMITER,j as ENTITLEMENTS_JWKS_URL,k as FEEDBACK_API_URL,c as I18N_DIR_NAME,d as JWT_SECRET_KEY,S as L10N_DIR_NAME,P as LINK_ORIGINAL_ATTR_NAME,f as LLMS_TXT_FILE_NAME,s_ as MARKDOC_PARTIALS_DATA_KEY,T_ as MARKDOC_PARTIALS_DEPS_KEY,G as MCP_ACCESS_TOKEN_TTL_SEC,K as MCP_REFRESH_SESSION_LIFETIME_SEC,e as ORG_ID,n as ORG_SLUG,e_ as OTEL_EXCLUDED_ROUTES,R_ as OTEL_TRACES_DEV_URL,J as PAGE_COUNT_OUTPUT_FILE_NAME,o as PROJECT_ID,R as PROJECT_SLUG,a as PUBLIC_API_DEFINITIONS_FOLDER,x as PUBLIC_ASSETS_FOLDER,M as PUBLIC_STATIC_FOLDER,Y as RESERVED_DIR_NAMES,N as RUNTIME_RESOURCES_DIR,h as SCORECARD_OUTPUT_FILE_NAME,V as SEMANTIC_SEARCH_API_URL,A as SERVER_EDITOR_APP_URL,W as SITEMAP_FILE_NAME,T as SSR_RENDER_MODE,__ as SSR_WORKERS_MAX,Q as SSR_WORKERS_MIN,H as STYLES_FILE,o_ as TELEMETRY_ENABLED,m as VERSIONED_FOLDER_REGEXP_MATCH,l as VERSION_SEPARATOR,y as X_REDOCLY_CACHE_CONTROL_HEADER,Z as X_REDOCLY_CACHE_CONTROL_PUBLIC_HTML};
1
+ import{ServerRoutes as r}from"../../constants/api.js";import{USER_THEME_ALIAS as L}from"../../constants/common.js";import{envConfig as _}from"../config/env-config.js";const N="runtime",x="/assets",a="/_spec",U="/_bundle",M="static",P="redocly:::linkOriginal",l="@",i="default",m=new RegExp("((?!@i18n\\/|@l10n\\/)\\/?@.*?(?=\\/))","g"),F="@redocly/theme/plugin.js",H="styles.css",d=["@theme/plugin.js","@theme/plugin.cjs","@theme/plugin.mjs"],u=_.JWT_SECRET_KEY||"test_secret_key",G=3600,K=1440*60,c="@i18n",S="@l10n",Y=[L,c,S],B="https://redocly.com/auth/aud",g="catalog.json",h="scorecard.json",f="page-count.json",k="sitemap.xml",J="llms.txt",o=_.PROJECT_ID,e=_.PROJECT_SLUG,R=_.ORGANIZATION_ID,s=_.ORG_SLUG,t=_.BH_API_URL,n=t&&s&&e?`${t}/orgs/${s}/projects/${e}`:void 0,E=t&&R&&o?`${t}/orgs/${R}/projects/${o}`:void 0,p=t?`${t}/entitlements/.well-known/jwks`:"https://app.cloud.redocly.com/api/entitlements/.well-known/jwks",W=_.ENTITLEMENTS_JWKS_CDN_URL||p,$=n?`${n}/feedback`:"",j=E?`${E}/ai-agentic-conversations`:"",w=E?`${E}/ai-assistant-escalations`:"",V=E?`${E}/ai-assistant-feedback`:"",X=E?`${E}/semantic-search`:"",v="no-store",y=365*24*60*60,b="X-Redocly-Cache-Control",Z="no-cache",q="public, s-maxage=86400, max-age=10, must-revalidate",z=_.AUTH_URL,Q="Developer Documentation",T=_.REDOCLY_SSR_RENDER_MODE==="worker"?"worker":"main",__=T==="main"?0:_.REDOCLY_SSR_WORKERS_MIN!=null?Number(_.REDOCLY_SSR_WORKERS_MIN):2,E_=T==="main"?1:_.REDOCLY_SSR_WORKERS_MAX!=null?Number(_.REDOCLY_SSR_WORKERS_MAX):5,t_="-----BEGIN CERTIFICATE-----",o_="-----END CERTIFICATE-----",e_=!_.isDevelopMode&&_.REDOCLY_TELEMETRY!=="off"||_.REDOCLY_TELEMETRY==="on",R_="http://localhost:4318/v1/traces",s_=[r.HEALTH],A=_.SERVER_EDITOR_APP_URL||"http://127.0.0.1:3000",n_=_.REDOCLY_CORS_ORIGINS?_.REDOCLY_CORS_ORIGINS.split(",").map(I=>I.trim()).filter(Boolean).concat(A):[A],T_="markdown/partials",A_="markdown/partials-deps";export{w as AI_ASSISTANT_ESCALATIONS_API_URL,V as AI_ASSISTANT_FEEDBACK_API_URL,n_ as ALLOWED_CORS_ORIGINS,B as ALTERNATIVE_AUD_CLAIM_NAME,j as ASK_AI_API_URL,z as AUTH_URL,t_ as BEGIN_CERTIFICATE_DELIMITER,n as BH_API_PROJECTS_URL,E as BH_API_PROJECTS_URL_V2,v as CACHE_CONTROL_NO_STORE_HEADER_VALUE,Z as CACHE_CONTROL_PUBLIC_HTML,g as CATALOG_OUTPUT_FILE_NAME,y as DEFAULT_IMMUTABLE_CACHE_MAX_AGE,d as DEFAULT_PROJECT_PLUGIN_PATHS,F as DEFAULT_THEME_PLUGIN_PATH,Q as DEFAULT_TITLE,i as DEFAULT_VERSION_NAME,a as DEPRECATED_PUBLIC_API_DEFINITIONS_FOLDER,o_ as END_CERTIFICATE_DELIMITER,W as ENTITLEMENTS_JWKS_URL,$ as FEEDBACK_API_URL,c as I18N_DIR_NAME,u as JWT_SECRET_KEY,S as L10N_DIR_NAME,P as LINK_ORIGINAL_ATTR_NAME,J as LLMS_TXT_FILE_NAME,T_ as MARKDOC_PARTIALS_DATA_KEY,A_ as MARKDOC_PARTIALS_DEPS_KEY,G as MCP_ACCESS_TOKEN_TTL_SEC,K as MCP_REFRESH_SESSION_LIFETIME_SEC,R as ORG_ID,s as ORG_SLUG,s_ as OTEL_EXCLUDED_ROUTES,R_ as OTEL_TRACES_DEV_URL,f as PAGE_COUNT_OUTPUT_FILE_NAME,o as PROJECT_ID,e as PROJECT_SLUG,U as PUBLIC_API_DEFINITIONS_FOLDER,x as PUBLIC_ASSETS_FOLDER,M as PUBLIC_STATIC_FOLDER,Y as RESERVED_DIR_NAMES,N as RUNTIME_RESOURCES_DIR,h as SCORECARD_OUTPUT_FILE_NAME,X as SEMANTIC_SEARCH_API_URL,A as SERVER_EDITOR_APP_URL,k as SITEMAP_FILE_NAME,T as SSR_RENDER_MODE,E_ as SSR_WORKERS_MAX,__ as SSR_WORKERS_MIN,H as STYLES_FILE,e_ as TELEMETRY_ENABLED,m as VERSIONED_FOLDER_REGEXP_MATCH,l as VERSION_SEPARATOR,b as X_REDOCLY_CACHE_CONTROL_HEADER,q as X_REDOCLY_CACHE_CONTROL_PUBLIC_HTML};
@@ -0,0 +1,36 @@
1
+ export { AUDIT_LOG_PREFIX } from './constants.js';
2
+ export declare const AUDIT_FIELD_MAX_CHARS = 4096;
3
+ export type AuditField = string | number | boolean | undefined;
4
+ export declare function auditSha256(value: string): string;
5
+ /**
6
+ * Cuts at the last whitespace inside the budget rather than mid-token, so a secret is never split
7
+ * in half before the patterns get a look at it — half a token matches nothing and would survive.
8
+ */
9
+ export declare function truncateAuditField(value: string): string;
10
+ /**
11
+ * Redact an input payload (code, request body, tool arguments) down to something loggable.
12
+ * Truncation runs first so the patterns only ever see whole tokens, and so the 14 regexes run
13
+ * over at most `AUDIT_FIELD_MAX_CHARS` instead of a multi-megabyte body.
14
+ */
15
+ export declare function auditInput(value: string, injectedSecretValues?: readonly string[]): string;
16
+ /**
17
+ * Outputs are never recorded — an upstream response or tool result can hold credentials nobody
18
+ * can pattern-match. The digest still lets a replayed run be compared against the original.
19
+ */
20
+ export declare function auditOutputDigest(value: string): {
21
+ sha256: string;
22
+ bytes: number;
23
+ };
24
+ /**
25
+ * Header values never reach the log. Redacting a fixed list of sensitive names would not be
26
+ * enough — sandbox code picks its own header names, so any allow-list leaks the creative ones.
27
+ */
28
+ export declare function auditHeaderNames(headers: Record<string, string> | undefined): string;
29
+ /** Full URL with its query string, credential-shaped parameter values replaced. */
30
+ export declare function auditUrl(rawUrl: string, injectedSecretValues?: readonly string[]): string;
31
+ /**
32
+ * Values are JSON-encoded so a multi-line payload (submitted code, an assembled prompt) stays on
33
+ * one log line, which is what makes one record one Grafana row.
34
+ */
35
+ export declare function logAuditRecord(action: string, fields: Record<string, AuditField>): void;
36
+ //# sourceMappingURL=audit-log.d.ts.map
@@ -0,0 +1 @@
1
+ import{createHash as a}from"node:crypto";import{logger as c}from"../../../tools/notifiers/logger.js";import{AUDIT_LOG_PREFIX as u}from"./constants.js";import{redactSecretsFromString as f}from"../gateway-mcp/fetch/redact.js";import{redactSecretShapes as p}from"./secret-patterns.js";import{AUDIT_LOG_PREFIX as I}from"./constants.js";const s=4096,d="redacted",m=new Set(["access_token","api-key","api_key","apikey","auth","code","id_token","key","password","refresh_token","secret","session","sig","signature","token","x-amz-credential","x-amz-security-token","x-amz-signature"]);function h(t){return a("sha256").update(t,"utf8").digest("hex")}function g(t){if(t.length<=s)return t;const e=t.slice(0,s),r=e.search(/\s\S*$/),n=r>0?e.slice(0,r):e;return`${n}\u2026[truncated: kept ${n.length} of ${t.length} chars]`}function i(t,e=[]){return p(f(g(t),e))}function S(t){return{sha256:h(t),bytes:Buffer.byteLength(t,"utf8")}}function $(t){return Object.keys(t??{}).map(e=>e.toLowerCase()).sort().join(",")}function E(t,e=[]){let r;try{r=new URL(t)}catch{return i(t,e)}for(const n of[...r.searchParams.keys()])m.has(n.toLowerCase())&&r.searchParams.set(n,d);return r.username="",r.password="",i(r.href,e)}function R(t,e){const r=Object.entries(e).filter(([,n])=>n!==void 0&&n!=="").map(([n,o])=>typeof o=="string"?`${n}=${JSON.stringify(o)}`:`${n}=${String(o)}`).join(" ");try{c.infoPreSanitized(`${u} ${t} ${r}`)}catch{}}export{s as AUDIT_FIELD_MAX_CHARS,I as AUDIT_LOG_PREFIX,$ as auditHeaderNames,i as auditInput,S as auditOutputDigest,h as auditSha256,E as auditUrl,R as logAuditRecord,g as truncateAuditField};
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Single token in front of every audit record so the whole class can be filtered in Grafana — or
3
+ * dropped in the log collector — with one rule.
4
+ */
5
+ export declare const AUDIT_LOG_PREFIX = "[mcp-audit]";
6
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ const t="[mcp-audit]";export{t as AUDIT_LOG_PREFIX};
@@ -0,0 +1,2 @@
1
+ export declare function redactSecretShapes(text: string): string;
2
+ //# sourceMappingURL=secret-patterns.d.ts.map
@@ -0,0 +1 @@
1
+ const n=[{name:"aws_access_key_id",re:/\bAKIA[0-9A-Z]{16}\b/g},{name:"aws_temp_key",re:/\bASIA[0-9A-Z]{16}\b/g},{name:"github_token",re:/\bgh[pousr]_[A-Za-z0-9_]{20,255}\b/g},{name:"github_fine_grained",re:/\bgithub_pat_[A-Za-z0-9_]{60,255}\b/g},{name:"jwt",re:/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g},{name:"pem_private_key",re:/-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/g},{name:"google_api_key",re:/\bAIza[0-9A-Za-z_-]{35}\b/g},{name:"slack_token",re:/\bxox[abprs]-[A-Za-z0-9-]{10,}\b/g},{name:"bearer_token",re:/(?<=[Aa]uthorization\s*[:=]\s*["']?[Bb]earer\s+)[A-Za-z0-9._\-+/=]{20,}/g},{name:"anthropic_key",re:/\bsk-ant-[A-Za-z0-9_-]{20,}\b/g},{name:"openai_key",re:/\bsk-(?:proj-|svcacct-)?[A-Za-z0-9_-]{20,}\b/g},{name:"stripe_key",re:/\b(?:sk|rk|pk)_(?:live|test)_[A-Za-z0-9]{20,}\b/g},{name:"sendgrid_key",re:/\bSG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}\b/g},{name:"basic_auth_url",re:/\b([a-z][a-z0-9+\-.]*:\/\/)[^\s/:@]+:[^\s/@]+@/gi}];function t(e){if(!e)return e;let a=e;for(const{name:r,re:_}of n)a=a.replace(_,(A,b)=>r==="basic_auth_url"?`${b??""}[REDACTED:${r}]@`:`[REDACTED:${r}]`);return a}export{t as redactSecretShapes};
@@ -0,0 +1,12 @@
1
+ import type { McpToolSchema } from '../../../types';
2
+ import type { SandboxCapability } from './capabilities/types.js';
3
+ /** One line of the tool list: the sandbox-facing tool name plus a one-line purpose. */
4
+ export type ToolListEntry = {
5
+ name: string;
6
+ purpose?: string;
7
+ };
8
+ /** Maps MCP tool schemas and active capabilities to tool-list entries (sandbox names). */
9
+ export declare function toToolListEntries(tools: McpToolSchema[], capabilities: SandboxCapability[]): ToolListEntry[];
10
+ /** Renders one `- name — purpose` line per entry within `maxChars`. */
11
+ export declare function buildToolList(entries: ToolListEntry[], maxChars: number): string;
12
+ //# sourceMappingURL=build-tool-list.d.ts.map
@@ -0,0 +1,2 @@
1
+ import{sanitizeToolName as s}from"./build-tool-type-declarations.js";const l=60;function a(e,t){return[...e.map(n=>({name:s(n.name),purpose:n.description})),...t.map(n=>({name:s(n.id),purpose:n.purpose}))]}function $(e,t){if(e.length===0||t<=0)return"";const o=e.map(({name:r,purpose:f})=>{const i=(f??"").replace(/\s+/g," ").trim();if(!i)return`- ${r}`;const m=i.length>l?`${i.slice(0,l-1)}\u2026`:i;return`- ${r} \u2014 ${m}`}).join(`
2
+ `);if(o.length<=t)return o;const u=e.map(r=>r.name),p=u.join(", ");return p.length<=t?p:h(u,t)}function h(e,t){const n=[];for(const o of e){if([...n,o].join(", ").length+c(e.length-n.length-1).length>t)break;n.push(o)}return n.length>0?`${n.join(", ")}${c(e.length-n.length)}`:""}function c(e){return e>0?` \u2026 (+${e} more)`:""}export{$ as buildToolList,a as toToolListEntries};
@@ -1,4 +1,4 @@
1
1
  import{getCallableApis as a,isFetchCapable as s}from"../../../gateway-mcp/fetch-capability.js";import{FETCH_TOOL_NAME as i}from"../../constants.js";const l=["```ts","/**"," * Call a documented API. Only the callable APIs' documented hosts are reachable."," * Responses cap at 1 MiB and redirects are not followed."," */","declare function fetch(url: string, init?: {"," method?: string;"," /** `content-type` is set automatically for URLSearchParams/FormData unless you set one. */"," headers?: Record<string, string>;"," /** string \xB7 URLSearchParams (\u2192 application/x-www-form-urlencoded) \xB7 FormData (\u2192 multipart/form-data) */"," body?: string | URLSearchParams | FormData;","}): Promise<{"," status: number;"," statusText: string;"," ok: boolean;"," headers: { get(name: string): string | null };"," json(): Promise<unknown>;"," text(): Promise<string>;","}>;","```"].join(`
2
2
  `),c=["Example - List the write endpoints of an API and call one of them with `fetch`:","","```ts","const { items } = await tools.listApis({ filter: 'billing' });","const { endpoints } = await tools.getEndpoints({ name: items[0].name });","const ep = endpoints.find((e) => e.method === 'POST' && e.path.includes('invoices'));","const info = await tools.getEndpointInfo({ name: items[0].name, path: ep.path, method: ep.method });","const url = (info.servers?.[0]?.url ?? '') + ep.path;","try {"," const res = await fetch(url, { method: 'POST', body: JSON.stringify({ /* fields from info */ }) });"," return res.ok ? await res.json() : { status: res.status, error: await res.text() };","} catch (error) {"," return { error: error instanceof Error ? error.message : String(error), endpointId: info };","}","```"].join(`
3
- `);function d(r){const t=new Map;for(const{name:e,version:n}of r){if(!e)continue;const o=t.get(e)??[];n&&o.push(n),t.set(e,o)}return[...t.entries()].map(([e,n])=>n.length>1?`${e} (${n.map(o=>`v${o}`).join(", ")})`:e).join(", ")}const f={id:i,isActive:s,annotations:{readOnlyHint:!1,destructiveHint:!0,openWorldHint:!0},renderSection(r){const t=d(a(r));return["## `fetch` \u2014 call the documented APIs","",l,"","Always call `getEndpointInfo` before `fetch` for the exact URL, required parameters, and body shape. Never guess.","",`Callable APIs (their documented hosts are reachable via \`fetch\`): ${t}`,"",c].join(`
4
- `)}};export{l as FETCH_DECLARATION,f as fetchCapability};
3
+ `);function d(r){const t=new Map;for(const{name:e,version:n}of r){if(!e)continue;const o=t.get(e)??[];n&&o.push(n),t.set(e,o)}return[...t.entries()].map(([e,n])=>n.length>1?`${e} (${n.map(o=>`v${o}`).join(", ")})`:e).join(", ")}const h={id:i,purpose:"Call the documented APIs over HTTP",isActive:s,annotations:{readOnlyHint:!1,destructiveHint:!0,openWorldHint:!0},renderSection(r){const t=d(a(r));return["## `fetch` \u2014 call the documented APIs","",l,"","Always call `getEndpointInfo` before `fetch` for the exact URL, required parameters, and body shape. Never guess.","",`Callable APIs (their documented hosts are reachable via \`fetch\`): ${t}`,"",c].join(`
4
+ `)}};export{l as FETCH_DECLARATION,h as fetchCapability};
@@ -3,6 +3,7 @@ import type { McpToolContext } from '../../types.js';
3
3
  /** A capability is a per-request-gated global the code-mode sandbox exposes beyond the schema-driven `tools.*` surface. */
4
4
  export type SandboxCapability = {
5
5
  id: string;
6
+ purpose: string;
6
7
  /** Whether this capability is live for THIS request (RBAC/config/flag-dependent). */
7
8
  isActive(context: McpToolContext): boolean;
8
9
  /** How this capability changes `execute`'s MCP annotations when active. */
@@ -1,7 +1,7 @@
1
- import{buildToolTypeDeclarations as i}from"../build-tool-type-declarations.js";import{EXECUTE_BASE_ANNOTATIONS as c}from"../tools/execute-schema.js";import{EXECUTE_ARGS_NOTE as p,USAGE_NOTES as s}from"../prompts.js";import{ALL_CAPABILITIES as u}from"./registry.js";function T(o){return u.filter(t=>t.isActive(o))}function m(o){return o.reduce((t,e)=>({...t,...e.annotations}),{...c})}function l(o,t,e){const n=[];o.length>0&&n.push(`## Sandbox tools
1
+ import{buildToolTypeDeclarations as i}from"../build-tool-type-declarations.js";import{EXECUTE_BASE_ANNOTATIONS as E}from"../tools/execute-schema.js";import{EXECUTE_ARGS_NOTE as p,PLAIN_EXECUTE_EXAMPLE as s,USAGE_NOTES as u}from"../prompts.js";import{FETCH_TOOL_NAME as c}from"../constants.js";import{ALL_CAPABILITIES as A}from"./registry.js";function S(o){return A.filter(n=>n.isActive(o))}function l(o){return o.reduce((n,e)=>({...n,...e.annotations}),{...E})}function N(o,n,e){const t=[];o.length>0&&t.push(`## Sandbox tools
2
2
 
3
3
  ${i(o)}
4
4
 
5
- ${s}`),n.push(p);for(const r of t)n.push(r.renderSection(e));return n.join(`
5
+ ${u}`),t.push(p),o.length>0&&!n.some(r=>r.id===c)&&t.push(s);for(const r of n)t.push(r.renderSection(e));return t.join(`
6
6
 
7
- `)}export{m as composeExecuteAnnotations,T as getActiveCapabilities,l as renderDescribeToolsOutput};
7
+ `)}export{l as composeExecuteAnnotations,S as getActiveCapabilities,N as renderDescribeToolsOutput};
@@ -0,0 +1,17 @@
1
+ import type { CodeAudit } from './code-audit.js';
2
+ type CodeExecutionAuditRecord = {
3
+ audit: CodeAudit;
4
+ code: string;
5
+ description: string;
6
+ sessionHandle?: string;
7
+ toolNames: readonly string[];
8
+ fetchBudget?: number;
9
+ fetchCapable: boolean;
10
+ };
11
+ /**
12
+ * Full sandbox input for one code mode `execute`, keyed by the same `code_sha256` the
13
+ * `mcpServer.codeExecuted` telemetry event carries, so a Grafana row resolves to this line.
14
+ */
15
+ export declare function logCodeExecutionAudit({ audit, code, description, sessionHandle, toolNames, fetchBudget, fetchCapable, }: CodeExecutionAuditRecord): void;
16
+ export {};
17
+ //# sourceMappingURL=code-execution-audit.d.ts.map
@@ -0,0 +1 @@
1
+ import{auditInput as e,logAuditRecord as l}from"../audit/audit-log.js";function h({audit:o,code:t,description:c,sessionHandle:n,toolNames:d,fetchBudget:i,fetchCapable:s}){l("Record code execution input",{code_sha256:o.sha256,code_length:o.length,flags:o.flags,session_handle:n,description:e(c),tools:[...d].sort().join(","),fetch_capable:s,fetch_budget:i,code:e(t)})}export{h as logCodeExecutionAudit};
@@ -1,6 +1,9 @@
1
+ import { type ToolListEntry } from './build-tool-list.js';
2
+ export declare const DESCRIPTION_CHAR_LIMIT = 2048;
1
3
  export declare const EXECUTE_ARGS_NOTE = "Both `execute` arguments are required: `code` (the JavaScript to run; return a value to receive it) and `description` (a short human-readable summary of what the code does).";
2
- export declare function buildExecuteDescription(hasFetch: boolean): string;
4
+ export declare function buildExecuteDescription(hasFetch: boolean, toolList?: ToolListEntry[]): string;
3
5
  export declare const DESCRIBE_TOOLS_DESCRIPTION: string;
4
- export declare function buildCodeModeInstructions(hasFetch: boolean): string;
5
- export declare const USAGE_NOTES = "Types named `OpenApi\u2026` are the standard OpenAPI Specification objects.";
6
+ export declare function buildCodeModeInstructions(hasFetch: boolean, toolList?: ToolListEntry[]): string;
7
+ export declare const PLAIN_EXECUTE_EXAMPLE: string;
8
+ export declare const USAGE_NOTES: string;
6
9
  //# sourceMappingURL=prompts.d.ts.map
@@ -1,4 +1,7 @@
1
- const t="Both `execute` arguments are required: `code` (the JavaScript to run; return a value to receive it) and `description` (a short human-readable summary of what the code does).";function o(e){return["Run JavaScript in a sandbox against the documentation tools on the global `tools` object.",e?"The only extra globals are `tools` and `fetch` - there is no `console`, `setTimeout`, `require`, or Node.js API; all other I/O goes through `tools`.":"The only extra global is `tools` - there is no `fetch`, `console`, `setTimeout`, `require`, or Node.js API; all I/O goes through `tools`.","","Workflow - minimize round-trips; one `describe-tools` call plus one `execute` call covers most requests:","1. Analyze the request and decide which sandbox tools it needs (every tool name is listed in the `describe-tools` input schema / description).","2. Call `describe-tools` with those names to get their exact TypeScript signatures - inputs AND result shapes. NEVER call `execute` with guessed method names, parameter names, enum values, or result field names: read fields off the declared return types, not from assumptions.","3. Batch everything into a SINGLE script: chain tool calls, pass one call's output into the next, process results in plain JavaScript, and `return` only what the request needs.","",...e?["Example - list the Billing API endpoints and call one of them:","","```ts","const { items } = await tools.listApis({ filter: 'billing' });","const { endpoints } = await tools.getEndpoints({ name: items[0].name });","const ep = endpoints.find((e) => e.method === 'POST' && e.path.includes('invoices'));","const info = await tools.getEndpointInfo({ name: items[0].name, path: ep.path, method: ep.method });","const url = (info.servers?.[0]?.url ?? '') + ep.path;","try {"," const res = await fetch(url, { method: 'POST', body: JSON.stringify({ /* fields from info */ }) });"," return res.ok ? await res.json() : { status: res.status, error: await res.text() };","} catch (error) {"," return { error: error instanceof Error ? error.message : String(error), endpointId: info };","}","```"]:["Example - list the write endpoints of an API:","","```ts","const { items } = await tools.listApis({ filter: 'billing' });","const { endpoints } = await tools.getEndpoints({ name: items[0].name });","const writes = endpoints.filter((e) => e.method !== 'GET');","return { api: items[0].name, writes: writes.map((e) => `${e.method} ${e.path}`) };","```"]].join(`
2
- `)}const s=["Get the exact TypeScript signatures of the sandbox tools available to `execute`.","Analyze the user request first, then pass `tools` with only the tool names the request needs (every name is listed in this input schema / description); omit `tools` only when you cannot tell which tools apply.","Always call this before your first `execute` and never guess signatures. Request every tool the task may need in ONE call instead of calling this repeatedly."].join(`
3
- `);function n(e){return["This server exposes documentation tools (API discovery, endpoint details, security schemes, full-text search) through two tools:","- `describe-tools` returns the exact TypeScript signatures of the sandbox tools.","- `execute` runs JavaScript that calls those tools on the global `tools` object.","Workflow: analyze the request, call `describe-tools` once with only the tool names it needs, then batch everything into a single `execute` script. Minimize round-trips and never guess tool signatures.",e?"The sandbox also has a global `fetch` that can call the documented APIs (allow-listed hosts only). Include `fetch` in your `describe-tools` call to get its contract and the list of callable APIs.":""].join(`
4
- `)}const i="Types named `OpenApi\u2026` are the standard OpenAPI Specification objects.";export{s as DESCRIBE_TOOLS_DESCRIPTION,t as EXECUTE_ARGS_NOTE,i as USAGE_NOTES,n as buildCodeModeInstructions,o as buildExecuteDescription};
1
+ import{buildToolList as i}from"./build-tool-list.js";const l=2048,c="Both `execute` arguments are required: `code` (the JavaScript to run; return a value to receive it) and `description` (a short human-readable summary of what the code does).",n="Available tools (exact signatures come from `describe-tools`):";function a(e,t){if(t.length===0)return e;const o=l-e.length-n.length-3,s=i(t,o);return s?[e,"",n,s].join(`
2
+ `):e}function h(e,t=[]){const o=["Run JavaScript in a sandbox against the documentation tools on the global `tools` object.",e?"The only extra globals are `tools` and `fetch` - there is no `console`, `setTimeout`, `require`, or Node.js API; all other I/O goes through `tools`.":"The only extra global is `tools` - there is no `fetch`, `console`, `setTimeout`, `require`, or Node.js API; all I/O goes through `tools`.","","Workflow - minimize round-trips; one `describe-tools` call plus one `execute` call covers most requests:","1. Analyze the request and decide which sandbox tools it needs.","2. Call `describe-tools` with those names to get their exact TypeScript signatures - inputs, result shapes, and a worked example. NEVER call `execute` with guessed method names, parameter names, enum values, or result field names: read fields off the declared return types, not from assumptions.","3. Batch everything into a SINGLE script: chain tool calls, pass one call's output into the next, process results in plain JavaScript, and `return` only what the request needs."].join(`
3
+ `);return a(o,t)}const d=["Get the exact TypeScript signatures of the sandbox tools available to `execute`.","Analyze the user request first, then pass `tools` with only the tool names the request needs (every name is listed in this input schema / description); omit `tools` only when you cannot tell which tools apply.","Always call this before your first `execute` and never guess signatures. Request every tool the task may need in ONE call instead of calling this repeatedly."].join(`
4
+ `);function u(e,t=[]){const o=["This server exposes documentation tools (API discovery, endpoint details, security schemes, full-text search) through two tools:","- `describe-tools` returns the exact TypeScript signatures of the sandbox tools.","- `execute` runs JavaScript that calls those tools on the global `tools` object.","Workflow: analyze the request, call `describe-tools` once with only the tool names it needs, then batch everything into a single `execute` script. Minimize round-trips and never guess tool signatures.",...e?["The sandbox also has a global `fetch` that can call the documented APIs (allow-listed hosts only). Include `fetch` in your `describe-tools` call to get its contract and the list of callable APIs."]:[]].join(`
5
+ `);return a(o,t)}const p=["Example - list the write endpoints of an API:","","```ts","const { items } = await tools.listApis({ filter: 'billing' });","const { endpoints } = await tools.getEndpoints({ name: items[0].name });","const writes = endpoints.filter((e) => e.method !== 'GET');","return { api: items[0].name, writes: writes.map((e) => `${e.method} ${e.path}`) };","```"].join(`
6
+ `),m=["Types named `OpenApi\u2026` are the standard OpenAPI Specification objects.","Payload samples live on a media type object (`requestBody.content[mediaType]`, `responses[code].content[mediaType]`): `.example` holds a single value, `.examples[name].value` a named map, and some descriptions only set `.schema.example`. Read each before concluding a sample is absent."].join(`
7
+ `);export{d as DESCRIBE_TOOLS_DESCRIPTION,l as DESCRIPTION_CHAR_LIMIT,c as EXECUTE_ARGS_NOTE,p as PLAIN_EXECUTE_EXAMPLE,m as USAGE_NOTES,u as buildCodeModeInstructions,h as buildExecuteDescription};
@@ -1 +1 @@
1
- import{renderDescribeToolsOutput as w}from"../capabilities/index.js";import{sanitizeToolName as o}from"../build-tool-type-declarations.js";function g(i,l){const n=[...new Set([...i.map(s=>o(s.name)),...l.map(s=>o(s.id))])];return{type:"object",required:[],additionalProperties:!1,properties:{tools:{type:"array",description:`Tool names to describe: ${n.join(", ")}. Omit to describe everything.`,items:{type:"string"},nullable:!0}}}}function v(i,l,n,s){const r=new Map(i.map(t=>[o(t.name),t])),c=new Map(n.map(t=>[o(t.id),t]));return async t=>{const a=[...new Set((t.tools??[]).map(o))],p=a.length===0,m=a.filter(e=>!r.has(e)&&!c.has(e));if(m.length>0){const e=[...r.keys(),...c.keys()];throw new Error(`Unknown tool(s): ${m.join(", ")}. Valid tools: ${e.join(", ")}.`)}const u=p?n:n.filter(e=>a.includes(o(e.id))),d=p?i:a.filter(e=>r.has(e)).map(e=>r.get(e)),y=await Promise.all(d.map(e=>l(e.name))),f=d.filter((e,b)=>y[b]);return{content:[{type:"text",text:w(f,u,s)}]}}}export{v as describeTools,g as describeToolsSchema};
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 +1 @@
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 O,EXECUTE_TOOL_NAME as A}from"../constants.js";import{sanitizeToolName as C}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 M}from"../code-audit.js";import{reportCodeExecuted as v}from"../code-execution-telemetry.js";const y=async(t,e,r)=>{const o=await N(e,r),n=[],i=E(e)?b(e,s=>n.push(s)):void 0;let c=0;const u=i?s=>(c+=1,i(s)):void 0,d=M(t.code),m=Date.now(),{result:f,error:a}=await p(t.code,{setupContext:x(o,u)});return v({audit:d,fetchCount:c,outcome:a===void 0?"ok":"error",durationMs:Date.now()-m}),T({content:[{type:"text",text:JSON.stringify({result:f,error:a})}],isError:a!==void 0},n)};async function N(t,e){const r={};for(const o of Object.keys(l))O.has(o)||!await h({toolName:o,context:t}).catch(()=>!1)||(r[C(o)]=i=>w(o,i,t,e));return r}var B={[A]:y};export{B as default};
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,4 @@
1
+ export declare class UnknownToolError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ //# sourceMappingURL=tool-errors.d.ts.map
@@ -0,0 +1 @@
1
+ class n extends Error{constructor(r){super(r),this.name="UnknownToolError"}}export{n as UnknownToolError};
@@ -1 +1 @@
1
- import{resolveParameters as f,resolveRequestBody as y,resolveResponses as h}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(n,i)=>{const{path:o,method:s}=n;let t;try{t=await E(n,i)}catch(l){return{content:[{type:"text",text:l.message}],isError:!0}}const p=o.startsWith("/")?o:`/${o}`,{title:a=""}=t.info||{},r=t.paths?.[p],c=s.toLowerCase();if(!r)return{content:[{type:"text",text:"Endpoint not found"}],isError:!0};const e=r[c];if(!x(e)||!v(e,"docs"))return{content:[{type:"text",text:"Endpoint not found"}],isError:!0};const d=r?.parameters||[],m=e.parameters||[],u={...e,parameters:f({pathParams:d,opParams:m,definition:t}),requestBody:y(e.requestBody,t),responses:h(e.responses,t)};return{content:[{type:"text",text:JSON.stringify({api:a,version:t.info?.version||"",servers:t.servers||[],endpoint:{path:o,method:s.toUpperCase(),...u},globalSecurity:t.security||[],securitySchemes:t.components?.securitySchemes||{}},null,2)}]}};var B={"get-endpoint-info":g};export{B as default};
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
- function $(t,o,n){if(!o)return;const s=o.toLowerCase(),r=Object.values(t).filter(({name:i})=>i.toLowerCase()===s);if(r.length===0)return;const e=n?r.filter(i=>i.version===n):r;if(e.length===0)return;if(e.length===1)return e[0];if(new Set(e.map(({version:i})=>i||"")).size>1){const i=e.map(({version:a})=>a||"(no version)").join(", ");throw new Error(`Multiple versions of "${o}" found: ${i}. Specify a version to disambiguate.`)}const f=e.map(({relativePath:i})=>`"${i||"(unknown path)"}"`).join(", "),u=e[0].version?` at version "${e[0].version}"`:"";throw new Error(`Multiple APIs are titled "${o}"${u}. Pass the "filePath" parameter to select one of: ${f}.`)}function v(t,o){return Object.values(t).filter(({name:n})=>n.toLowerCase().includes(o.toLowerCase()))}function d(t,o){if(!o||!t?.startsWith("#/"))return;const n=t.slice(2).split("/");let s=o;for(const r of n)if(typeof s=="object"&&s!==null&&r in s)s=s[r];else return;return s}function l({schema:t,definition:o,visitedRefs:n=new Set,maxDepth:s=50,currentDepth:r=0}){if(!t)return;if(r>=s)return{type:"object",description:`Maximum resolution depth exceeded (${s})`};if("$ref"in t&&t.$ref){if(n.has(t.$ref))return{type:"object",description:`Circular reference detected: ${t.$ref}`,"x-circular-ref":t.$ref};const f=new Set(n);f.add(t.$ref);const u=d(t.$ref,o);return u?l({schema:u,definition:o,visitedRefs:f,maxDepth:s,currentDepth:r+1}):{type:"object",description:`Could not resolve reference: ${t.$ref}`,"x-unresolved-ref":t.$ref}}const e={...t};if(e.properties){const f={};for(const[u,i]of Object.entries(e.properties)){const a=l({schema:i,definition:o,visitedRefs:new Set(n),maxDepth:s,currentDepth:r+1});f[u]=a||i}e.properties=f}if(e.items&&typeof e.items=="object")if(Array.isArray(e.items))e.items=e.items.map(f=>l({schema:f,definition:o,visitedRefs:new Set(n),maxDepth:s,currentDepth:r+1})||f);else{const f=l({schema:e.items,definition:o,visitedRefs:new Set(n),maxDepth:s,currentDepth:r+1});f&&(e.items=f)}return["allOf","anyOf","oneOf"].forEach(f=>{const u=e[f];Array.isArray(u)&&(e[f]=u.reduce((i,a)=>{const p=l({schema:a,definition:o,visitedRefs:new Set(n),maxDepth:s,currentDepth:r+1});return p&&i.push(p),i},[]))}),e}function h({pathParams:t,opParams:o,definition:n}){return[...t,...o].reduce((s,r)=>{if(!r)return s;if("$ref"in r&&r.$ref){const e=d(r.$ref,n);return e&&s.push({...e,schema:"schema"in e&&e.schema?l({schema:e.schema,definition:n}):void 0}),s}return s.push({...r,schema:"schema"in r&&r.schema?l({schema:r.schema,definition:n}):void 0}),s},[])}function m(t,o){const n={...t};if(t.example!==void 0&&(n.example=t.example),!t.examples)return n;const s={};for(const[r,e]of Object.entries(t.examples))if("$ref"in e){const c=d(e.$ref,o);c&&(s[r]=c)}else s[r]=e;return n.examples=s,n}function w(t,o){if(!t)return;const n="$ref"in t&&t.$ref?d(t.$ref,o):t;if(!n)return;const s={...n};if("content"in n&&n.content){const r={};for(const[e,c]of Object.entries(n.content))r[e]={...m(c,o),schema:c.schema?l({schema:c.schema,definition:o}):void 0};s.content=r}return s}function x(t,o){return Object.entries(t).reduce((n,[s,r])=>{const e="$ref"in r&&r.$ref?d(r.$ref,o):r;if(!e)return n;const c={...e};if("content"in e&&e.content){const f={};for(const[u,i]of Object.entries(e.content))f[u]={...m(i,o),schema:i.schema?l({schema:i.schema,definition:o}):void 0};c.content=f}return n[s]=c,n},{})}export{v as filterApiDescriptionsByName,$ as findApiDescriptionByNameAndVersion,h as resolveParameters,d as resolveRef,w as resolveRequestBody,x as resolveResponses,l as resolveSchemaRefs};
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};
@@ -0,0 +1,24 @@
1
+ import type { GatewayFetchOutcome } from './fetch-telemetry.js';
2
+ type GatewayFetchAuditRecord = {
3
+ rawUrl: string;
4
+ method: string;
5
+ host: string;
6
+ outcome: GatewayFetchOutcome;
7
+ durationMs: number;
8
+ injectedSecretValues: readonly string[];
9
+ path?: string;
10
+ api?: string;
11
+ status?: number;
12
+ requestHeaders?: Record<string, string>;
13
+ requestBody?: string;
14
+ responseHeaders?: Record<string, string>;
15
+ responseBody?: string;
16
+ };
17
+ /**
18
+ * One outbound gateway fetch. `host`, `method` and `path` repeat the `mcpServer.gatewayFetch`
19
+ * telemetry event verbatim so a Grafana row joins onto this line; `url_sha256` identifies the
20
+ * request itself, since the telemetry event's `path` is deliberately normalized.
21
+ */
22
+ export declare function logGatewayFetchAudit({ rawUrl, method, host, outcome, durationMs, injectedSecretValues, path, api, status, requestHeaders, requestBody, responseHeaders, responseBody, }: GatewayFetchAuditRecord): void;
23
+ export {};
24
+ //# sourceMappingURL=fetch-audit.d.ts.map
@@ -0,0 +1 @@
1
+ import{auditHeaderNames as u,auditInput as g,auditOutputDigest as c,auditSha256 as b,auditUrl as l,logAuditRecord as y}from"../../audit/audit-log.js";function D({rawUrl:e,method:n,host:i,outcome:r,durationMs:o,injectedSecretValues:t,path:_,api:h,status:f,requestHeaders:p,requestBody:d,responseHeaders:m,responseBody:a}){const s=a===void 0?void 0:c(a);y("Record gateway fetch",{url_sha256:b(e),host:i,method:n,path:_,api:h,outcome:r,status:f,duration_ms:o,url:l(e,t),request_header_names:u(p),request_body:d===void 0?void 0:g(d,t),response_header_names:u(m),response_sha256:s?.sha256,response_bytes:s?.bytes})}export{D as logGatewayFetchAudit};
@@ -1 +1 @@
1
- import{Agent as P}from"undici";import{envConfig as W}from"../../../../config/env-config.js";import{logger as R}from"../../../../tools/notifiers/logger.js";import{assertUrlAllowed as H}from"./ssrf-guard.js";import{createSsrfSafeLookup as I}from"./ssrf-safe-lookup.js";import{SsrfBlockedError as C}from"./ssrf-blocklist.js";import{GatewayFetchBlockedError as $,GatewayFetchSystemError as J,GatewayFetchUserError as L}from"./fetch-errors.js";import{redactHeaders as x,redactSecretsFromString as K}from"./redact.js";import{normalizeFetchPath as V}from"./normalize-path.js";import{reportGatewayFetch as O}from"./fetch-telemetry.js";const G="x-forwarded-for",X=3e4,Y=1024*1024,z=new Set([401,403,407]);let T;function Q(){if(!T){const t=W.isDevelopMode;t&&R.warn("Gateway MCP SSRF guard: loopback upstreams are ALLOWED (develop mode only, never in runtime)."),T=new P({connect:{lookup:I({allowLoopback:t})}})}return T}function ge({allowedHosts:t,apis:e=[],authorization:r,onAuthRequired:n,fetchImpl:c=fetch,dispatcher:u=Q(),timeoutMs:i=X,maxResponseBytes:h=Y,maxFetchesPerExec:f,clientIp:m}){const F=[...t,...e.flatMap(o=>o.matchers)],N=[r,...e.map(o=>o.secret?.value)].filter(o=>!!o),k=e.map(o=>o.secret?.header).filter(o=>!!o);let v=0;return async o=>{const g=o.method??"GET",D=Date.now();let U,S,b,E={},_,A;try{if(f!==void 0){const l=Math.max(0,f);if(v+=1,v>l)throw new L(`Outbound fetch budget exceeded (max ${l} fetches per execute); split the work across calls`)}const d=H(o.url,F);U=d.hostname,S=V(d.pathname);const y=e.filter(l=>l.matchers.some(j=>j.test(d.hostname))),a=y.length===1?y[0]:void 0;b=a?.slug,E=ae(o.headers,a?.secret,r,m);const w=new AbortController;A=setTimeout(()=>w.abort(),i);const s=await c(d,{method:g,headers:E,body:o.body,redirect:"manual",signal:w.signal,dispatcher:u});_=s.status,z.has(s.status)&&a&&n&&n({api:a.slug,name:a.name,status:s.status});const M=await ne(s,h);let p=se(s.headers);if(te(s.status)){p=x(p,k);const{location:l}=p;l&&!re(l,d,F)&&delete p.location}return O({host:d.hostname,method:g,outcome:"success",durationMs:Date.now()-D,path:S,api:b,status:s.status,bytes:Buffer.byteLength(M,"utf8")}),{status:s.status,statusText:s.statusText,ok:s.ok,headers:p,bodyText:M}}catch(d){const{outcome:y,userSafe:a}=q(d),w=U??(a instanceof $?a.host:void 0)??ee(o.url)??"invalid-url";throw O({host:w,method:g,outcome:y,durationMs:Date.now()-D,path:S,api:b,status:_}),a||(R.error(`Gateway MCP fetch failed [${g} ${w}]: ${K(oe(d),N)} headers=${JSON.stringify(x(E,k))}`),new J("Upstream fetch failed due to an internal error"))}finally{A!==void 0&&clearTimeout(A)}}}function Z(t){let e=t;for(let r=0;r<5&&e instanceof Error;r++){if(e instanceof L||e instanceof C)return e;e=e.cause}}function q(t){const e=Z(t);return{outcome:e instanceof $||e instanceof C?"blocked":e?"user_error":"system_error",userSafe:e}}function ee(t){try{return new URL(t).hostname||void 0}catch{return}}function te(t){return t>=300&&t<400}function re(t,e,r){try{return!!H(new URL(t,e).href,r)}catch{return!1}}function oe(t){if(!(t instanceof Error))return String(t);const e=t.cause;return e instanceof Error?`${t.message}: ${e.message}`:t.message}async function ne(t,e){const r=Number(t.headers.get("content-length"));if(Number.isFinite(r)&&r>e)throw B(e,r);if(!t.body)return"";const n=t.body.getReader(),c=new TextDecoder;let u=0,i="";try{for(;;){const{done:h,value:f}=await n.read();if(h)break;if(u+=f.byteLength,u>e)throw B(e,u);i+=c.decode(f,{stream:!0})}return i+c.decode()}finally{await n.cancel().catch(()=>{})}}function B(t,e){return new L(`Response is too large (over ${t} bytes, got ${e}); request less data`)}function ae(t,e,r,n){const c={},u=new Set;for(const[h,f]of Object.entries(t??{})){const m=h.toLowerCase();m==="host"||m==="content-length"||m!==G&&(u.add(m),c[h]=f)}n&&(c[G]=n);const i=e??(r?{header:"Authorization",value:r}:void 0);return i&&!u.has(i.header.toLowerCase())&&(c[i.header]=i.value),c}function se(t){const e={};return t.forEach((r,n)=>{e[n.toLowerCase()]=r}),e}export{ge as buildFetchBridge,Q as getDefaultAgent,ne as readCappedBody};
1
+ import{Agent as W}from"undici";import{envConfig as I}from"../../../../config/env-config.js";import{logger as R}from"../../../../tools/notifiers/logger.js";import{assertUrlAllowed as B}from"./ssrf-guard.js";import{createSsrfSafeLookup as J}from"./ssrf-safe-lookup.js";import{SsrfBlockedError as C}from"./ssrf-blocklist.js";import{GatewayFetchBlockedError as $,GatewayFetchSystemError as K,GatewayFetchUserError as U}from"./fetch-errors.js";import{redactHeaders as x,redactSecretsFromString as V}from"./redact.js";import{normalizeFetchPath as X}from"./normalize-path.js";import{reportGatewayFetch as G}from"./fetch-telemetry.js";import{logGatewayFetchAudit as O}from"./fetch-audit.js";const N="x-forwarded-for",Y=3e4,z=1024*1024,Q=new Set([401,403,407]);let M;function Z(){if(!M){const t=I.isDevelopMode;t&&R.warn("Gateway MCP SSRF guard: loopback upstreams are ALLOWED (develop mode only, never in runtime)."),M=new W({connect:{lookup:J({allowLoopback:t})}})}return M}function be({allowedHosts:t,apis:e=[],authorization:o,onAuthRequired:s,fetchImpl:i=fetch,dispatcher:u=Z(),timeoutMs:d=Y,maxResponseBytes:h=z,maxFetchesPerExec:f,clientIp:m}){const k=[...t,...e.flatMap(r=>r.matchers)],L=[o,...e.map(r=>r.secret?.value)].filter(r=>!!r),v=e.map(r=>r.secret?.header).filter(r=>!!r);let H=0;return async r=>{const w=r.method??"GET",A=Date.now();let _,g,b,S={},T,D;try{if(f!==void 0){const l=Math.max(0,f);if(H+=1,H>l)throw new U(`Outbound fetch budget exceeded (max ${l} fetches per execute); split the work across calls`)}const n=B(r.url,k);_=n.hostname,g=X(n.pathname);const E=e.filter(l=>l.matchers.some(P=>P.test(n.hostname))),c=E.length===1?E[0]:void 0;b=c?.slug,S=se(r.headers,c?.secret,o,m);const p=new AbortController;D=setTimeout(()=>p.abort(),d);const a=await i(n,{method:w,headers:S,body:r.body,redirect:"manual",signal:p.signal,dispatcher:u});T=a.status,Q.has(a.status)&&c&&s&&s({api:c.slug,name:c.name,status:a.status});const F=await ae(a,h);let y=ce(a.headers);if(re(a.status)){y=x(y,v);const{location:l}=y;l&&!oe(l,n,k)&&delete y.location}return G({host:n.hostname,method:w,outcome:"success",durationMs:Date.now()-A,path:g,api:b,status:a.status,bytes:Buffer.byteLength(F,"utf8")}),O({rawUrl:n.href,method:w,host:n.hostname,outcome:"success",durationMs:Date.now()-A,injectedSecretValues:L,path:g,api:b,status:a.status,requestHeaders:S,requestBody:r.body,responseHeaders:y,responseBody:F}),{status:a.status,statusText:a.statusText,ok:a.ok,headers:y,bodyText:F}}catch(n){const{outcome:E,userSafe:c}=ee(n),p=_??(c instanceof $?c.host:void 0)??te(r.url)??"invalid-url";throw G({host:p,method:w,outcome:E,durationMs:Date.now()-A,path:g,api:b,status:T}),O({rawUrl:r.url,method:w,host:p,outcome:E,durationMs:Date.now()-A,injectedSecretValues:L,path:g,api:b,status:T,requestHeaders:S,requestBody:r.body}),c||(R.error(`Gateway MCP fetch failed [${w} ${p}]: ${V(ne(n),L)} headers=${JSON.stringify(x(S,v))}`),new K("Upstream fetch failed due to an internal error"))}finally{D!==void 0&&clearTimeout(D)}}}function q(t){let e=t;for(let o=0;o<5&&e instanceof Error;o++){if(e instanceof U||e instanceof C)return e;e=e.cause}}function ee(t){const e=q(t);return{outcome:e instanceof $||e instanceof C?"blocked":e?"user_error":"system_error",userSafe:e}}function te(t){try{return new URL(t).hostname||void 0}catch{return}}function re(t){return t>=300&&t<400}function oe(t,e,o){try{return!!B(new URL(t,e).href,o)}catch{return!1}}function ne(t){if(!(t instanceof Error))return String(t);const e=t.cause;return e instanceof Error?`${t.message}: ${e.message}`:t.message}async function ae(t,e){const o=Number(t.headers.get("content-length"));if(Number.isFinite(o)&&o>e)throw j(e,o);if(!t.body)return"";const s=t.body.getReader(),i=new TextDecoder;let u=0,d="";try{for(;;){const{done:h,value:f}=await s.read();if(h)break;if(u+=f.byteLength,u>e)throw j(e,u);d+=i.decode(f,{stream:!0})}return d+i.decode()}finally{await s.cancel().catch(()=>{})}}function j(t,e){return new U(`Response is too large (over ${t} bytes, got ${e}); request less data`)}function se(t,e,o,s){const i={},u=new Set;for(const[h,f]of Object.entries(t??{})){const m=h.toLowerCase();m==="host"||m==="content-length"||m!==N&&(u.add(m),i[h]=f)}s&&(i[N]=s);const d=e??(o?{header:"Authorization",value:o}:void 0);return d&&!u.has(d.header.toLowerCase())&&(i[d.header]=d.value),i}function ce(t){const e={};return t.forEach((o,s)=>{e[s.toLowerCase()]=o}),e}export{be as buildFetchBridge,Z as getDefaultAgent,ae as readCappedBody};
@@ -1 +1 @@
1
- import{fromJsonSchema as C}from"@modelcontextprotocol/server";import{logger as A}from"../../../tools/notifiers/logger.js";import{mcpToolWorkers as b,MCP_TOOL_IS_AVAILABLE_KEY as g}from"../../../workers/mcp-tool-worker-pool.js";import{CREATE_ANONYMOUS_SESSION_TOOL_NAME as M}from"../constants.js";import{isAnonymousSessionToolAvailable as T}from"../docs-mcp/tools/core/create-anonymous-session.js";import{isManageApiCredentialsToolAvailable as v,manageApiCredentials as L,manageApiCredentialsSchema as D}from"../codemode/tools/manage-credentials.js";import{EXECUTE_TOOL_SCHEMA as y}from"../codemode/tools/execute-schema.js";import{EXECUTE_TOOL_NAME as h,DESCRIBE_TOOLS_TOOL_NAME as I,CODE_MODE_TOOL_NAMES as N,FETCH_TOOL_NAME as H}from"../codemode/constants.js";import{buildCodeModeInstructions as k,buildExecuteDescription as w,DESCRIBE_TOOLS_DESCRIPTION as x}from"../codemode/prompts.js";import{composeExecuteAnnotations as F,getActiveCapabilities as R}from"../codemode/capabilities/index.js";import{describeToolsSchema as P,describeTools as B}from"../codemode/tools/describe-tools.js";import{mcpJsonSchemaValidator as U}from"../utils/json-schema-validator.js";import{connectMcpServer as z,createMcpServer as J,createToolDispatch as V,extractSerializableContext as X}from"./mcp-server.js";function Y(s){const t=y.schema;if(T(s))return t;const{sessionHandle:i,...c}=t.properties;return{...t,properties:c}}function $(s,t){const i=t.schema;if(T(s))return i;const{sessionHandle:c,...l}=i.properties;return{...i,properties:l}}async function se({name:s,tools:t,context:i,skills:c=[],getKv:l}){const m=i.mcpMode==="code",p=m?R(i):[],u=p.some(e=>e.id===H),O=m&&v(i),{server:r,transport:_}=J(s,m?k(u):void 0),a=e=>C(e,U),d=X(i),E={getClientCapabilities:()=>r.server.getClientCapabilities(),getKv:l},f=V(d,E),S=async e=>{try{return await b.exec(g,[{toolName:e,context:d}],{timeout:1e4})}catch(o){return A.error(`Failed to check MCP tool availability for "${e}": ${o instanceof Error?o.message:String(o)}`),!1}};if(m){const e=t.filter(n=>!N.has(n.name));r.registerTool(h,{description:w(u),inputSchema:a(Y(i)),annotations:F(p)},f(h)),r.registerTool(I,{description:x,inputSchema:a(P(e,p))},B(e,S,p,i));const o=t.find(n=>n.name===M);if(o&&T(i)&&r.registerTool(o.name,{description:o.description,inputSchema:a(o.schema),annotations:o.annotations??{title:o.name}},f(o.name)),O){const n=D(i);r.registerTool(n.name,{description:n.description,inputSchema:a($(i,n)),annotations:n.annotations??{title:n.name}},L(d,E))}}else await Promise.all(t.map(async e=>{const o=r.registerTool(e.name,{description:e.description,inputSchema:a(e.schema),annotations:e.annotations||{title:e.name}},f(e.name));await S(e.name)||o.disable()}));for(const e of c)r.registerResource(e.slug,e.uri,{title:e.name,description:e.description,mimeType:"text/markdown"},async()=>({contents:[{uri:e.uri,mimeType:"text/markdown",text:e.content}]}));return z(r,_)}export{se as createDocsMcpServer};
1
+ import{fromJsonSchema as A}from"@modelcontextprotocol/server";import{logger as b}from"../../../tools/notifiers/logger.js";import{mcpToolWorkers as g,MCP_TOOL_IS_AVAILABLE_KEY as M}from"../../../workers/mcp-tool-worker-pool.js";import{CREATE_ANONYMOUS_SESSION_TOOL_NAME as L}from"../constants.js";import{isAnonymousSessionToolAvailable as T}from"../docs-mcp/tools/core/create-anonymous-session.js";import{isManageApiCredentialsToolAvailable as v,manageApiCredentials as D,manageApiCredentialsSchema as y}from"../codemode/tools/manage-credentials.js";import{EXECUTE_TOOL_SCHEMA as I}from"../codemode/tools/execute-schema.js";import{EXECUTE_TOOL_NAME as O,DESCRIBE_TOOLS_TOOL_NAME as N,CODE_MODE_TOOL_NAMES as H,FETCH_TOOL_NAME as k}from"../codemode/constants.js";import{buildCodeModeInstructions as w,buildExecuteDescription as x,DESCRIBE_TOOLS_DESCRIPTION as F}from"../codemode/prompts.js";import{toToolListEntries as R}from"../codemode/build-tool-list.js";import{composeExecuteAnnotations as P,getActiveCapabilities as B}from"../codemode/capabilities/index.js";import{describeToolsSchema as U,describeTools as z}from"../codemode/tools/describe-tools.js";import{mcpJsonSchemaValidator as J}from"../utils/json-schema-validator.js";import{connectMcpServer as V,createMcpServer as X,createToolDispatch as Y,extractSerializableContext as $}from"./mcp-server.js";function K(r){const t=I.schema;if(T(r))return t;const{sessionHandle:i,...m}=t.properties;return{...t,properties:m}}function W(r,t){const i=t.schema;if(T(r))return i;const{sessionHandle:m,...p}=i.properties;return{...i,properties:p}}async function me({name:r,tools:t,context:i,skills:m=[],getKv:p}){const s=i.mcpMode==="code",a=s?B(i):[],E=a.some(e=>e.id===k),l=s?t.filter(e=>!H.has(e.name)):[],u=R(l,a),_=s&&v(i),{server:n,transport:C}=X(r,s?w(E,u):void 0),c=e=>A(e,J),d=$(i),S={getClientCapabilities:()=>n.server.getClientCapabilities(),getKv:p},f=Y(d,S),h=async e=>{try{return await g.exec(M,[{toolName:e,context:d}],{timeout:1e4})}catch(o){return b.error(`Failed to check MCP tool availability for "${e}": ${o instanceof Error?o.message:String(o)}`),!1}};if(s){n.registerTool(O,{description:x(E,u),inputSchema:c(K(i)),annotations:P(a)},f(O)),n.registerTool(N,{description:F,inputSchema:c(U(l,a))},z(l,h,a,i));const e=t.find(o=>o.name===L);if(e&&T(i)&&n.registerTool(e.name,{description:e.description,inputSchema:c(e.schema),annotations:e.annotations??{title:e.name}},f(e.name)),_){const o=y(i);n.registerTool(o.name,{description:o.description,inputSchema:c(W(i,o)),annotations:o.annotations??{title:o.name}},D(d,S))}}else await Promise.all(t.map(async e=>{const o=n.registerTool(e.name,{description:e.description,inputSchema:c(e.schema),annotations:e.annotations||{title:e.name}},f(e.name));await h(e.name)||o.disable()}));for(const e of m)n.registerResource(e.slug,e.uri,{title:e.name,description:e.description,mimeType:"text/markdown"},async()=>({contents:[{uri:e.uri,mimeType:"text/markdown",text:e.content}]}));return V(n,C)}export{me as createDocsMcpServer};
@@ -1 +1 @@
1
- import{telemetry as m}from"../../../telemetry/index.js";import{mcpToolHandlers as d}from"../../../../client/mcp-tool-handlers-entry.js";import{McpServerType as f}from"../constants.js";import{EXECUTE_TOOL_NAME as y}from"../codemode/constants.js";import{reportMcpToolCalled as x,reportMcpToolError as p}from"./mcp-tool-telemetry.js";function w(e){return e.content.map(o=>o.type==="text"?o.text:"")}async function _(e){m.initialize();const{toolName:o,args:c,context:i,extra:r}=e,s=i?.mcpMode??"tools",n=performance.now();try{const t=d[o];if(!t)throw new Error(`Unknown MCP tool: ${o}`);const{default:h}=await t(),l=h[o];if(!l)throw new Error(`MCP tool module does not export a handler for "${o}". Expected \`export default { '${o}': handler }\`.`);const a=await(typeof l=="function"?l:l.execute)(c,i,r);if(!E(a))throw new Error(`MCP tool "${o}" returned an invalid result.`);const u=Math.round(performance.now()-n);if(a.isError)p({tool:o,message:w(a).join(" "),mode:s,serverType:f.Docs,durationMs:u,errorClass:o===y?"sandbox_error":"tool_error"});else{const M=w(a).join("").length;x({tool:o,mode:s,serverType:f.Docs,durationMs:u,outputLength:M})}return a}catch(t){throw p({tool:o,message:t instanceof Error?t.message:String(t),mode:s,serverType:f.Docs,durationMs:Math.round(performance.now()-n),stack:t instanceof Error&&t.stack||"",errorClass:"server_error"}),t}}function E(e){return!e||typeof e!="object"?!1:Array.isArray(e.content)}async function k(e){m.initialize();const{toolName:o,context:c}=e,i=performance.now();try{const r=d[o];if(!r)return!1;const{default:s}=await r(),n=s[o];return n?typeof n=="object"&&typeof n.isAvailable=="function"?await n.isAvailable(c):!0:!1}catch(r){throw p({tool:o,message:"Failed to check if MCP tool is available: "+(r instanceof Error?r.message:String(r)),mode:c?.mcpMode??"tools",serverType:f.Docs,durationMs:Math.round(performance.now()-i),stack:r instanceof Error&&r.stack||"",errorClass:"server_error"}),r}}export{_ as executeMcpTool,k as isMcpToolAvailable};
1
+ import{telemetry as h}from"../../../telemetry/index.js";import{mcpToolHandlers as y}from"../../../../client/mcp-tool-handlers-entry.js";import{McpServerType as c}from"../constants.js";import{EXECUTE_TOOL_NAME as T}from"../codemode/constants.js";import{logMcpToolCallAudit as M,reportMcpToolCalled as C,reportMcpToolError as u}from"./mcp-tool-telemetry.js";function E(e){return e.content.map(o=>o.type==="text"?o.text:"")}async function j(e){h.initialize();const{toolName:o,args:a,context:l,extra:r}=e,n=l?.mcpMode??"tools",s=performance.now();try{const t=y[o];if(!t)throw new Error(`Unknown MCP tool: ${o}`);const{default:x}=await t(),i=x[o];if(!i)throw new Error(`MCP tool module does not export a handler for "${o}". Expected \`export default { '${o}': handler }\`.`);const f=await(typeof i=="function"?i:i.execute)(a,l,r);if(!v(f))throw new Error(`MCP tool "${o}" returned an invalid result.`);const m=Math.round(performance.now()-s),p=f.isError?o===T?"sandbox_error":"tool_error":void 0,d=E(f),w=d.join("");return M({tool:o,mode:n,serverType:c.Docs,args:a,result:w,errorClass:p}),p?u({tool:o,message:d.join(" "),mode:n,serverType:c.Docs,durationMs:m,errorClass:p}):C({tool:o,mode:n,serverType:c.Docs,durationMs:m,outputLength:w.length}),f}catch(t){throw M({tool:o,mode:n,serverType:c.Docs,args:a,errorClass:"server_error"}),u({tool:o,message:t instanceof Error?t.message:String(t),mode:n,serverType:c.Docs,durationMs:Math.round(performance.now()-s),stack:t instanceof Error&&t.stack||"",errorClass:"server_error"}),t}}function v(e){return!e||typeof e!="object"?!1:Array.isArray(e.content)}async function P(e){h.initialize();const{toolName:o,context:a}=e,l=performance.now();try{const r=y[o];if(!r)return!1;const{default:n}=await r(),s=n[o];return s?typeof s=="object"&&typeof s.isAvailable=="function"?await s.isAvailable(a):!0:!1}catch(r){throw u({tool:o,message:"Failed to check if MCP tool is available: "+(r instanceof Error?r.message:String(r)),mode:a?.mcpMode??"tools",serverType:c.Docs,durationMs:Math.round(performance.now()-l),stack:r instanceof Error&&r.stack||"",errorClass:"server_error"}),r}}export{j as executeMcpTool,P as isMcpToolAvailable};
@@ -15,6 +15,17 @@ export type McpErrorClass = 'tool_error' | 'sandbox_error' | 'server_error';
15
15
  export declare function reportMcpToolCalled({ tool, mode, serverType, durationMs, outputLength, }: McpToolEvent & {
16
16
  outputLength: number;
17
17
  }): void;
18
+ /**
19
+ * Arguments and result identity for one MCP tool call. `tool` and `mode` repeat the
20
+ * `mcpServer.toolCalled` telemetry event so a Grafana row joins onto this line; `args_sha256`
21
+ * identifies the invocation. The result body itself is never recorded — a tool can return
22
+ * credentials no pattern will recognise — so only its digest lands here.
23
+ */
24
+ export declare function logMcpToolCallAudit({ tool, mode, serverType, args, result, errorClass, }: Omit<McpToolEvent, 'durationMs'> & {
25
+ args: unknown;
26
+ result?: string;
27
+ errorClass?: McpErrorClass;
28
+ }): void;
18
29
  export declare function reportMcpToolError({ tool, mode, serverType, durationMs, message, stack, errorClass, }: McpToolEvent & {
19
30
  message: string;
20
31
  stack?: string;
@@ -1 +1 @@
1
- import{telemetry as p}from"../../../telemetry/index.js";import{serverAttributes as l}from"../utils/telemetry-attributes.js";function m({tool:r,mode:o,serverType:e,durationMs:t,outputLength:s}){p.sendMcpToolCalledMessage([{...l(e),tool:r,mode:o,output_length:s,duration_ms:t}])}function u({tool:r,mode:o,serverType:e,durationMs:t,message:s,stack:n="",errorClass:a}){p.sendMcpErrorMessage([{...l(e),tool:r,message:s,stack:n,error_class:a,mode:o,duration_ms:t}])}export{m as reportMcpToolCalled,u as reportMcpToolError};
1
+ import{telemetry as u}from"../../../telemetry/index.js";import{serverAttributes as l}from"../utils/telemetry-attributes.js";import{EXECUTE_TOOL_NAME as c}from"../codemode/constants.js";import{auditInput as d,auditOutputDigest as p,auditSha256 as f,logAuditRecord as g}from"../audit/audit-log.js";function E({tool:r,mode:e,serverType:o,durationMs:s,outputLength:t}){u.sendMcpToolCalledMessage([{...l(o),tool:r,mode:e,output_length:t,duration_ms:s}])}function T({tool:r,mode:e,serverType:o,args:s,result:t,errorClass:n}){if(r===c)return;const i=m(s),a=t===void 0?void 0:p(t);g("Record MCP tool call",{tool:r,mode:e,server_type:o,error_class:n,args_sha256:f(i),args:d(i),result_sha256:a?.sha256,result_bytes:a?.bytes})}function m(r){try{return JSON.stringify(r)??String(r)}catch{return"\xAB[unserializable]\xBB"}}function A({tool:r,mode:e,serverType:o,durationMs:s,message:t,stack:n="",errorClass:i}){u.sendMcpErrorMessage([{...l(o),tool:r,message:t,stack:n,error_class:i,mode:e,duration_ms:s}])}export{T as logMcpToolCallAudit,E as reportMcpToolCalled,A as reportMcpToolError};
@@ -6,6 +6,7 @@ type LogData = {
6
6
  duration?: number;
7
7
  context?: LoggerContext;
8
8
  scope?: ErrorScope;
9
+ preSanitized?: boolean;
9
10
  };
10
11
  export declare enum LogLevel {
11
12
  INFO = "INFO",
@@ -1,3 +1,3 @@
1
- import*as g from"node:path";import{envConfig as R}from"../../config/env-config.js";import{maskEmail as E}from"./helpers/privacy/mask-email.js";import{maskSubject as T}from"./helpers/privacy/mask-subject.js";import{removePii as C}from"./helpers/privacy/remove-pii.js";import{removePiiFromUrl as $}from"./helpers/privacy/remove-pii-from-url.js";import{red as x,green as A,bold as j,blue as p,gray as i,yellow as P,cyan as w}from"./helpers/colors.js";var n;(function(e){e.INFO="INFO",e.WARN="WARN",e.ERROR="ERROR",e.SUCCESS="SUCCESS",e.VERBOSE="VERBOSE",e.HTTP="HTTP"})(n||(n={}));const b={[n.VERBOSE]:0,[n.HTTP]:1,[n.INFO]:2,[n.SUCCESS]:2,[n.WARN]:3,[n.ERROR]:4};function W(e,s){return b[e]>=b[s]}function L(e){if(!e)return;const s=e.toUpperCase();if(s in n)return s}const F={[n.INFO]:p,[n.SUCCESS]:A,[n.WARN]:P,[n.ERROR]:x,[n.HTTP]:w,[n.VERBOSE]:i},I={"%ap":e=>p(g.resolve(e)),"%rp":e=>p(g.relative(process.cwd(),e)),"%s":e=>e?.toString(),"%c":e=>p(e)},v={format(e){return R.REDOCLY_LOG_FORMAT==="JSON"?M(e):H(e)},interpolate(e,...s){const a=Object.keys(I).map(t=>`(${t})`).join("|"),m=new RegExp(a,"g");let o,u=e;for(;(o=m.exec(e))!==null;){o.index===m.lastIndex&&m.lastIndex++;const t=s.shift();if(t===void 0)break;const r=o[0];u=u.replace(r,I[r](t))}return u}};var z=v;const H=e=>{const{level:s,message:a,duration:m,context:o}=e,u=R.isRuntimeMode,t=[F[s](j(`[${s.toLowerCase()}]`))];if(u&&t.push(i(`time="${N()}"`)),o){const{email:r,ipAddress:f,subject:l,method:d,pathname:c,statusCode:S,teams:h,apiFunction:O}=o;O&&t.push(p(`fn="${O}"`)),S&&t.push(U(S)),e.level===n.HTTP&&(d&&t.push(i(`method="${d}"`)),c&&t.push(i(`path="${$(c)}"`))),f&&t.push(i(`ip="${f}"`)),r&&t.push(i(`email="${E(r)}"`)),l&&t.push(i(`sub="${T(l)}"`)),h?.length&&t.push(i(`teams="${h.join(", ")}"`))}if(a){const r=u?`msg="${String(C(a)).replaceAll?.('"','\\"')}"`:String(a);t.push(r)}return e.level===n.HTTP&&o?.userAgent&&t.push(i(`agent="${o.userAgent}"`)),m&&t.push(y(m)),t.join(" ")+`
2
- `},M=({context:e,message:s,...a})=>{const{method:m,pathname:o,statusCode:u,userAgent:t,subject:r,email:f,teams:l,apiFunction:d}=e||{},c={...a,email:f&&E(f),subject:r&&T(r),teams:l,apiFunction:d,...a.level===n.HTTP?{method:m,pathname:String($(o)),statusCode:u,userAgent:t}:{message:String(C(s))}};return R.isRuntimeMode&&(c.time=N()),JSON.stringify(c,["time","level","scope","message","duration","method","pathname","statusCode","userAgent","subject","ipAddress","email","apiFunction"])+`
3
- `},U=e=>(e%500<100?x:e%400<100?P:A)(`status="${e}"`),N=()=>{let e=new Date().getTimezoneOffset()*6e4;return new Date(Date.now()-e).toISOString().slice(0,-1)},y=e=>i(`dur="${Math.round(e)}ms"`);export{n as LogLevel,z as default,L as parseLogLevel,W as shouldLog};
1
+ import*as E from"node:path";import{envConfig as R}from"../../config/env-config.js";import{maskEmail as T}from"./helpers/privacy/mask-email.js";import{maskSubject as C}from"./helpers/privacy/mask-subject.js";import{removePii as w}from"./helpers/privacy/remove-pii.js";import{removePiiFromUrl as $}from"./helpers/privacy/remove-pii-from-url.js";import{red as x,green as A,bold as F,blue as l,gray as i,yellow as b,cyan as v}from"./helpers/colors.js";function P(e,r){return r?e:w(e)}var n;(function(e){e.INFO="INFO",e.WARN="WARN",e.ERROR="ERROR",e.SUCCESS="SUCCESS",e.VERBOSE="VERBOSE",e.HTTP="HTTP"})(n||(n={}));const I={[n.VERBOSE]:0,[n.HTTP]:1,[n.INFO]:2,[n.SUCCESS]:2,[n.WARN]:3,[n.ERROR]:4};function L(e,r){return I[e]>=I[r]}function G(e){if(!e)return;const r=e.toUpperCase();if(r in n)return r}const H={[n.INFO]:l,[n.SUCCESS]:A,[n.WARN]:b,[n.ERROR]:x,[n.HTTP]:v,[n.VERBOSE]:i},N={"%ap":e=>l(E.resolve(e)),"%rp":e=>l(E.relative(process.cwd(),e)),"%s":e=>e?.toString(),"%c":e=>l(e)},M={format(e){return R.REDOCLY_LOG_FORMAT==="JSON"?y(e):U(e)},interpolate(e,...r){const u=Object.keys(N).map(a=>`(${a})`).join("|"),o=new RegExp(u,"g");let s,m=e;for(;(s=o.exec(e))!==null;){s.index===o.lastIndex&&o.lastIndex++;const a=r.shift();if(a===void 0)break;const t=s[0];m=m.replace(t,N[t](a))}return m}};var Y=M;const U=e=>{const{level:r,message:u,duration:o,context:s,preSanitized:m}=e,a=R.isRuntimeMode,t=[H[r](F(`[${r.toLowerCase()}]`))];if(a&&t.push(i(`time="${j()}"`)),s){const{email:c,ipAddress:f,subject:d,method:S,pathname:p,statusCode:h,teams:g,apiFunction:O}=s;O&&t.push(l(`fn="${O}"`)),h&&t.push(D(h)),e.level===n.HTTP&&(S&&t.push(i(`method="${S}"`)),p&&t.push(i(`path="${$(p)}"`))),f&&t.push(i(`ip="${f}"`)),c&&t.push(i(`email="${T(c)}"`)),d&&t.push(i(`sub="${C(d)}"`)),g?.length&&t.push(i(`teams="${g.join(", ")}"`))}if(u){const c=a?`msg="${String(P(u,m)).replaceAll?.('"','\\"')}"`:String(u);t.push(c)}return e.level===n.HTTP&&s?.userAgent&&t.push(i(`agent="${s.userAgent}"`)),o&&t.push(V(o)),t.join(" ")+`
2
+ `},y=({context:e,message:r,preSanitized:u,...o})=>{const{method:s,pathname:m,statusCode:a,userAgent:t,subject:c,email:f,teams:d,apiFunction:S}=e||{},p={...o,email:f&&T(f),subject:c&&C(c),teams:d,apiFunction:S,...o.level===n.HTTP?{method:s,pathname:String($(m)),statusCode:a,userAgent:t}:{message:String(P(String(r??""),u))}};return R.isRuntimeMode&&(p.time=j()),JSON.stringify(p,["time","level","scope","message","duration","method","pathname","statusCode","userAgent","subject","ipAddress","email","apiFunction"])+`
3
+ `},D=e=>(e%500<100?x:e%400<100?b:A)(`status="${e}"`),j=()=>{let e=new Date().getTimezoneOffset()*6e4;return new Date(Date.now()-e).toISOString().slice(0,-1)},V=e=>i(`dur="${Math.round(e)}ms"`);export{n as LogLevel,Y as default,G as parseLogLevel,L as shouldLog};
@@ -22,6 +22,7 @@ export declare class Logger {
22
22
  constructor({ context, allowInteractive, minLogLevel }?: LoggerOptions);
23
23
  shouldLog(level: LogLevel): boolean;
24
24
  info(message: string, ...args: unknown[]): void;
25
+ infoPreSanitized(message: string): void;
25
26
  infoTime(timingId: symbol | string, message: string, ...args: unknown[]): {
26
27
  message: string;
27
28
  timeMs: number;
@@ -1,2 +1,2 @@
1
- import{telemetry as c}from"../../telemetry/index.js";import{envConfig as l}from"../../config/env-config.js";import h,{LogLevel as r,shouldLog as m,parseLogLevel as u}from"./formatter.js";import{isVirtualFile as f}from"../../fs/utils/isVirtualFile.js";import{TerminalManager as g}from"./terminal-manager.js";class p{#e;#l;#i;#n;#r=new Map;#o=new Map;constructor({context:t,allowInteractive:e,minLogLevel:i}={}){this.#e=t,this.#l=l.isProductionEnv,this.#i=new g(e),this.#n=i??u(l.REDOCLY_LOG_LEVEL)??(l.isDevelopMode?r.INFO:r.HTTP)}shouldLog(t){return m(t,this.#n)}info(t,...e){this.#t({level:r.INFO,message:t,args:e})}infoTime(t,e,...i){return this.#s(r.INFO,e,t,...i)}success(t,...e){this.#t({level:r.SUCCESS,message:t,args:e})}logInFooter(t,e,...i){const o=h.interpolate(e,...i)+`
2
- `;this.isInteractive()||this.#i.isFooterChanged(t,o)&&this.#t({level:r.INFO,message:e,args:i}),this.#i.updateFooter(t,o)}successTime(t,e,...i){return this.#s(r.SUCCESS,e,t,...i)}warn(t,...e){this.#t({level:r.WARN,message:t,args:e})}warnProd(t,...e){this.#l?this.warn(t,...e):this.verbose(t,...e)}error(t,...e){this.#t({level:r.ERROR,message:t,args:e})}contentError(t,...e){this.#t({level:r.ERROR,message:t,scope:"content",args:e})}verbose(t,...e){this.#t({level:r.VERBOSE,message:t,args:e})}verboseTime(t,e,...i){return this.#s(r.VERBOSE,e,t,...i)}httpTime(t){return this.#s(r.HTTP,"",t)}startTiming(t){const e=t||Symbol();this.#r.set(e,performance.now());const i=setTimeout(()=>{this.#r.delete(e),this.#o.delete(e)},500*1e3);return this.#o.set(e,i),e}updateContext(t){this.#e={...this.#e,...t}}clearAllTimeouts(){for(const t of this.#o.values())clearTimeout(t);this.#o.clear()}isInteractive(){return this.#i.isInteractive()}warnForRealFile(t,e,i,...o){f(e,i)||this.warn(t,e,...o)}#s(t,e,i,...o){const n=this.#r.get(i);if(!n)return;const s=Math.round(performance.now()-n);return this.#r.delete(i),this.#t({level:t,message:e,duration:s,args:o}),{message:e,timeMs:s}}#t({level:t,message:e,duration:i,scope:o,args:n}){if(!m(t,this.#n))return;let s=e&&h.interpolate(e,...n);s&&i!=null&&t!==r.VERBOSE&&c.sendTimingPerformedMessage([{id:"timingId",object:"timing",uri:"urn:redocly:realm:api:timing:timingId",timeMs:i,message:s}]);const a={level:t,message:s,duration:i,scope:o,context:this.#e};process.stderr.write(h.format(a))}}const R=new p({allowInteractive:!0});export{p as Logger,R as logger};
1
+ import{telemetry as u}from"../../telemetry/index.js";import{envConfig as h}from"../../config/env-config.js";import m,{LogLevel as r,shouldLog as a,parseLogLevel as f}from"./formatter.js";import{isVirtualFile as g}from"../../fs/utils/isVirtualFile.js";import{TerminalManager as d}from"./terminal-manager.js";class p{#e;#l;#i;#n;#r=new Map;#o=new Map;constructor({context:t,allowInteractive:e,minLogLevel:i}={}){this.#e=t,this.#l=h.isProductionEnv,this.#i=new d(e),this.#n=i??f(h.REDOCLY_LOG_LEVEL)??(h.isDevelopMode?r.INFO:r.HTTP)}shouldLog(t){return a(t,this.#n)}info(t,...e){this.#t({level:r.INFO,message:t,args:e})}infoPreSanitized(t){this.#t({level:r.INFO,message:t,args:[],preSanitized:!0})}infoTime(t,e,...i){return this.#s(r.INFO,e,t,...i)}success(t,...e){this.#t({level:r.SUCCESS,message:t,args:e})}logInFooter(t,e,...i){const o=m.interpolate(e,...i)+`
2
+ `;this.isInteractive()||this.#i.isFooterChanged(t,o)&&this.#t({level:r.INFO,message:e,args:i}),this.#i.updateFooter(t,o)}successTime(t,e,...i){return this.#s(r.SUCCESS,e,t,...i)}warn(t,...e){this.#t({level:r.WARN,message:t,args:e})}warnProd(t,...e){this.#l?this.warn(t,...e):this.verbose(t,...e)}error(t,...e){this.#t({level:r.ERROR,message:t,args:e})}contentError(t,...e){this.#t({level:r.ERROR,message:t,scope:"content",args:e})}verbose(t,...e){this.#t({level:r.VERBOSE,message:t,args:e})}verboseTime(t,e,...i){return this.#s(r.VERBOSE,e,t,...i)}httpTime(t){return this.#s(r.HTTP,"",t)}startTiming(t){const e=t||Symbol();this.#r.set(e,performance.now());const i=setTimeout(()=>{this.#r.delete(e),this.#o.delete(e)},500*1e3);return this.#o.set(e,i),e}updateContext(t){this.#e={...this.#e,...t}}clearAllTimeouts(){for(const t of this.#o.values())clearTimeout(t);this.#o.clear()}isInteractive(){return this.#i.isInteractive()}warnForRealFile(t,e,i,...o){g(e,i)||this.warn(t,e,...o)}#s(t,e,i,...o){const s=this.#r.get(i);if(!s)return;const n=Math.round(performance.now()-s);return this.#r.delete(i),this.#t({level:t,message:e,duration:n,args:o}),{message:e,timeMs:n}}#t({level:t,message:e,duration:i,scope:o,args:s,preSanitized:n}){if(!a(t,this.#n))return;let l=e&&m.interpolate(e,...s);l&&i!=null&&t!==r.VERBOSE&&u.sendTimingPerformedMessage([{id:"timingId",object:"timing",uri:"urn:redocly:realm:api:timing:timingId",timeMs:i,message:l}]);const c={level:t,message:l,duration:i,scope:o,context:this.#e,preSanitized:n};process.stderr.write(m.format(c))}}const O=new p({allowInteractive:!0});export{p as Logger,O as logger};
@@ -7,4 +7,9 @@ export declare function askAiHandler(store: Store): Handler;
7
7
  * only by portal users who can use AI search.
8
8
  */
9
9
  export declare function aiAssistantEscalationsHandler(store: Store): Handler;
10
+ /**
11
+ * Message ratings (thumbs up/down) from the embedded assistant. Proxied and gated exactly like
12
+ * `/_ask-ai`: feedback is reachable only by portal users who can use AI search.
13
+ */
14
+ export declare function aiAssistantFeedbackHandler(store: Store): Handler;
10
15
  //# sourceMappingURL=ask-ai.d.ts.map
@@ -1 +1 @@
1
- import{RbacFeatures as a}from"../../../constants/common.js";import{ASK_AI_API_URL as c,AI_ASSISTANT_ESCALATIONS_API_URL as u}from"../../constants/common.js";import{fetchWithClient as p}from"../../utils/fetch-with-client.js";import{canAccessFeature as A}from"../../utils/rbac.js";import{handleUnauthorizedApiRequest as h}from"../utils.js";import{isAiSearchEnabled as f}from"../../plugins/search/utils.js";function l(e,t){const o=t.getConfig();return f(o)?A(a.AI_SEARCH,e.get("auth"),o.access?.rbac,o.access?.requiresLogin)?null:h(e):e.newResponse(null,403,{})}function d(e){const t=e.get("auth"),o={"Content-Type":"application/json"},n=[];t.claims?.authCookie&&n.push(`authorization=${t.claims.authCookie}`),t.idpAccessToken&&n.push(`accessToken=${t.idpAccessToken}`),n.length>0&&(o.Cookie=n.join("; "));const s=e.req.header("Authorization");return s&&(o.Authorization=s),o}function i({store:e,apiUrl:t,successContentType:o="application/json"}){return async n=>{if(!t)return n.newResponse(null,404);const s=l(n,e);if(s)return s;const r=await p(t,{method:"POST",body:JSON.stringify(await n.req.json()),headers:d(n)});return n.newResponse(r.body,r.status,{"Content-Type":r.ok?o:"application/json"})}}function y(e){return i({store:e,apiUrl:c,successContentType:"text/event-stream"})}function R(e){return i({store:e,apiUrl:u})}export{R as aiAssistantEscalationsHandler,y as askAiHandler};
1
+ import{RbacFeatures as c}from"../../../constants/common.js";import{ASK_AI_API_URL as u,AI_ASSISTANT_ESCALATIONS_API_URL as A,AI_ASSISTANT_FEEDBACK_API_URL as p}from"../../constants/common.js";import{fetchWithClient as f}from"../../utils/fetch-with-client.js";import{canAccessFeature as h}from"../../utils/rbac.js";import{handleUnauthorizedApiRequest as d}from"../utils.js";import{isAiSearchEnabled as l}from"../../plugins/search/utils.js";function m(e,n){const r=n.getConfig();return l(r)?h(c.AI_SEARCH,e.get("auth"),r.access?.rbac,r.access?.requiresLogin)?null:d(e):e.newResponse(null,403,{})}function S(e){const n=e.get("auth"),r={"Content-Type":"application/json"},t=[];n.claims?.authCookie&&t.push(`authorization=${n.claims.authCookie}`),n.idpAccessToken&&t.push(`accessToken=${n.idpAccessToken}`),t.length>0&&(r.Cookie=t.join("; "));const i=e.req.header("Authorization");return i&&(r.Authorization=i),r}const g="x-redocly-ai-assistant-origin";function _(e){const n=e.req.header("Origin");if(n)return n;const r=e.req.header("Referer");if(r)try{return new URL(r).origin}catch{}return new URL(e.req.url).origin}function s({store:e,apiUrl:n,successContentType:r="application/json"}){return async t=>{if(!n)return t.newResponse(null,404);const i=m(t,e);if(i)return i;const a=S(t);a[g]=_(t);const o=await f(n,{method:"POST",body:JSON.stringify(await t.req.json()),headers:a});return t.newResponse(o.body,o.status,{"Content-Type":o.ok?r:"application/json"})}}function E(e){return s({store:e,apiUrl:u,successContentType:"text/event-stream"})}function b(e){return s({store:e,apiUrl:A})}function w(e){return s({store:e,apiUrl:p})}export{b as aiAssistantEscalationsHandler,w as aiAssistantFeedbackHandler,E as askAiHandler};
@@ -1 +1 @@
1
- import{serveStatic as R}from"hono/serve-static";import{withPathPrefix as i,withoutPathPrefix as c}from"@redocly/theme/core/utils";import{ServerRoutes as l}from"../../../constants/api.js";import{PUBLIC_STATIC_FOLDER as H}from"../../constants/common.js";import{envConfig as L}from"../../config/env-config.js";import{authMiddleware as C}from"../middleware/authMiddleware.js";import{ensureSearchData as g}from"../middleware/ensureSearchData.js";import{dynamicMiddleware as I}from"../middleware/dynamic-middleware/dynamic-middleware.js";import{installRoutes as h}from"../../plugins/dev-onboarding/api/routes/index.js";import{installSkillsRoutes as D}from"./skills/skills-routes.js";import{authorizeHandler as P,oidcCallbackHandler as N,logoutHandler as s,postLogoutHandler as w,idpLoginHandler as M,redoclyLoginCallbackHandler as v,samlCallbackHandler as B,redoclyTokenLoginHandler as G,inviteHandler as F}from"./auth.js";import{appDataHandler as U}from"./app-data.js";import{searchFacetsHandler as y,searchHandler as Y}from"./search.js";import{dynamicRouteHandler as k}from"./dynamic-route.js";import{pageDataHandler as K,sharedPageDataHandler as b}from"./page-data.js";import{pathPrefixRedirectHandler as V}from"./path-prefix-redirect.js";import{getRoutesByLineHandler as f,resolvePathHandler as r,resolvePathsHandler as x,resolveSlugHandler as E}from"./resolve-route.js";import{feedbackHandler as q}from"./feedback.js";import{loggerMiddleware as X}from"../middleware/loggerMiddleware.js";import{responseHeadersMiddleware as $}from"../middleware/responseHeadersMiddleware.js";import{idleTimeoutMiddleware as j}from"../middleware/idleTimeoutMiddleware.js";import{otelTracesHandler as u}from"./otel/otel.js";import{healthCheckHandler as z}from"./health.js";import{askAiHandler as W,aiAssistantEscalationsHandler as Z}from"./ask-ai.js";import{semanticSearchHandler as J}from"./semantic-search.js";import{replayOauth2RedirectCallbackHandler as Q}from"./replay-oauth2-redirect.js";import{corsProxyHandler as O}from"./cors-proxy.js";import{corsMiddleware as m}from"../middleware/corsMiddleware.js";import{installApiRoutes as aa}from"./api-routes/api-routes.js";import{cookieMiddleware as ea}from"../middleware/cookieMiddleware.js";import{staticContentHandler as ia}from"../routes/static-content.js";import{infoHandler as _}from"./info.js";import{catalogHandler as la}from"./catalog/catalog.js";import{catalogRelationsHandler as ta}from"./catalog/catalog-relations.js";import{bffCatalogHandler as ma}from"./catalog/bff-catalog.js";import{bffCatalogRevisionsHandler as oa}from"./catalog/bff-catalog-revisions.js";import{bffCatalogRelatedEntitiesHandler as da}from"./catalog/bff-catalog-related-entities.js";import{catalogAuthMiddleware as t}from"../middleware/catalogAuthMiddleware.js";import{telemetryMiddleware as na}from"../middleware/telemetry-middleware.js";import{errorHandler as pa}from"./error.js";import{installMcpRoutes as Aa}from"./mcp-routes/mcp-routes.js";import{jsonValidatorMiddleware as T}from"../middleware/jsonValidatorMiddleware.js";import{searchFacetsRequestSchema as sa}from"../../../api-schemas/search-facets.js";import{searchRequestSchema as fa}from"../../../api-schemas/search.js";function te(a,e,o){const{resolveRouteData:n,readStaticAsset:p}=o;a.use("*",j()),a.use("*",ea()),a.use("*",I(e)),a.use("*",C(e)),a.use("*",X()),a.use("*",$(e)),a.use("*",na()),a.use(i("*"),R({root:`./${H}`,getContent:(d,S)=>ia(d,S,e,p),rewriteRequestPath:d=>c(d)})),a.use(i(l.FEEDBACK),m({allowMethods:["POST"]})),a.use(i(l.ASK_AI),m({allowMethods:["POST"]})),a.use(i(l.AI_ASSISTANT_ESCALATIONS),m({allowMethods:["POST"]})),a.use(i(l.SEMANTIC_SEARCH),m({allowMethods:["POST"]})),a.use("*",ra(e));const A=g(e);a.use(i(l.INFO),_()),L.NEW_CATALOG_ENABLED&&(a.use(i(l.CATALOG_ENTITIES),t({serverOutDir:e.serverOutDir}),la(e)),a.use(i(l.CATALOG_ENTITIES_RELATIONS),t({serverOutDir:e.serverOutDir}),ta(e)),a.get(i(l.BFF_CATALOG_ENTITIES),t({serverOutDir:e.serverOutDir,protectReadMethods:!1}),ma(e)),a.get(i(l.BFF_CATALOG_RELATED_ENTITIES),t({serverOutDir:e.serverOutDir,protectReadMethods:!1}),da(e)),a.get(i(l.BFF_CATALOG_REVISIONS),t({serverOutDir:e.serverOutDir,protectReadMethods:!1}),oa(e))),a.get(i(l.SHARED_PAGE_DATA),b(e)),a.get(i(l.PAGE_DATA),K(e,n)),a.get(i(l.APP_DATA),U(e)),a.post(i(l.SEARCH),A,T(fa),Y(e)),a.post(i(l.SEARCH_FACETS),A,T(sa),y(e)),a.post(i(l.AUTHORIZATION),P),a.post(i(l.LOGOUT),s(e)),a.get(i(l.LOGOUT),s(e)),a.get(i(l.POST_LOGOUT),w(e)),a.get(i(l.OIDC_CALLBACK),N(e)),a.get(i(l.REDOCLY_TOKEN_LOGIN),G(e)),a.get(i(l.REDOCLY_LOGIN_CALLBACK),v()),a.get(i(l.IDP_LOGIN),M(e)),a.post(i(l.SAML_CALLBACK),B(e)),a.get(i(l.INVITE),F(e)),a.get(i(l.HEALTH),z),Aa(a,e),h(a,e),D(a,e),a.all(i(l.CORS_PROXY),O(e)),a.all(i(`${l.CORS_PROXY}/*`),O(e)),aa(a,e),a.post(i(l.FEEDBACK),q(e)),a.post(i(l.RESOLVE_ROUTE_BY_PATH),r(e)),a.post(i(l.RESOLVE_ROUTES_BY_PATHS),x(e)),a.post(i(l.RESOLVE_ROUTE_BY_SLUG),E(e)),a.post(i(l.ASK_AI),W(e)),a.post(i(l.AI_ASSISTANT_ESCALATIONS),Z(e)),a.post(i(l.SEMANTIC_SEARCH),J(e)),a.get(i(l.GET_ROUTES_BY_LINE),f(e)),a.post(i(l.OTEL_TRACES),u),a.get(i(l.REPLAY_OAUTH2_CALLBACK),Q),a.all(i("/*"),k(e,n,p)),a.get("*",V),a.onError(pa)}function ra(a){return async(e,o)=>{await a.waitForPluginsLifecycle(),await o()}}function me(a,e){a.get(i(l.INFO),_()),a.post(i(l.RESOLVE_ROUTE_BY_PATH),r(e)),a.post(i(l.RESOLVE_ROUTE_BY_SLUG),E(e)),a.get(i(l.GET_ROUTES_BY_LINE),f(e))}export{me as installDevRoutes,te as installProdRoutes,ra as waitForPluginsLifecycle};
1
+ import{serveStatic as R}from"hono/serve-static";import{withPathPrefix as i,withoutPathPrefix as c}from"@redocly/theme/core/utils";import{ServerRoutes as l}from"../../../constants/api.js";import{PUBLIC_STATIC_FOLDER as H}from"../../constants/common.js";import{envConfig as L}from"../../config/env-config.js";import{authMiddleware as C}from"../middleware/authMiddleware.js";import{ensureSearchData as g}from"../middleware/ensureSearchData.js";import{dynamicMiddleware as I}from"../middleware/dynamic-middleware/dynamic-middleware.js";import{installRoutes as h}from"../../plugins/dev-onboarding/api/routes/index.js";import{installSkillsRoutes as D}from"./skills/skills-routes.js";import{authorizeHandler as P,oidcCallbackHandler as N,logoutHandler as s,postLogoutHandler as w,idpLoginHandler as M,redoclyLoginCallbackHandler as B,samlCallbackHandler as v,redoclyTokenLoginHandler as F,inviteHandler as G}from"./auth.js";import{appDataHandler as U}from"./app-data.js";import{searchFacetsHandler as y,searchHandler as K}from"./search.js";import{dynamicRouteHandler as Y}from"./dynamic-route.js";import{pageDataHandler as k,sharedPageDataHandler as b}from"./page-data.js";import{pathPrefixRedirectHandler as V}from"./path-prefix-redirect.js";import{getRoutesByLineHandler as E,resolvePathHandler as T,resolvePathsHandler as x,resolveSlugHandler as _}from"./resolve-route.js";import{feedbackHandler as q}from"./feedback.js";import{loggerMiddleware as X}from"../middleware/loggerMiddleware.js";import{responseHeadersMiddleware as $}from"../middleware/responseHeadersMiddleware.js";import{idleTimeoutMiddleware as j}from"../middleware/idleTimeoutMiddleware.js";import{otelTracesHandler as z}from"./otel/otel.js";import{healthCheckHandler as W}from"./health.js";import{askAiHandler as Z,aiAssistantEscalationsHandler as u,aiAssistantFeedbackHandler as J}from"./ask-ai.js";import{semanticSearchHandler as Q}from"./semantic-search.js";import{replayOauth2RedirectCallbackHandler as aa}from"./replay-oauth2-redirect.js";import{corsProxyHandler as f}from"./cors-proxy.js";import{corsMiddleware as t}from"../middleware/corsMiddleware.js";import{installApiRoutes as ea}from"./api-routes/api-routes.js";import{cookieMiddleware as ia}from"../middleware/cookieMiddleware.js";import{staticContentHandler as la}from"../routes/static-content.js";import{infoHandler as S}from"./info.js";import{catalogHandler as ta}from"./catalog/catalog.js";import{catalogRelationsHandler as ma}from"./catalog/catalog-relations.js";import{bffCatalogHandler as da}from"./catalog/bff-catalog.js";import{bffCatalogRevisionsHandler as oa}from"./catalog/bff-catalog-revisions.js";import{bffCatalogRelatedEntitiesHandler as na}from"./catalog/bff-catalog-related-entities.js";import{catalogAuthMiddleware as m}from"../middleware/catalogAuthMiddleware.js";import{telemetryMiddleware as Aa}from"../middleware/telemetry-middleware.js";import{errorHandler as pa}from"./error.js";import{installMcpRoutes as sa}from"./mcp-routes/mcp-routes.js";import{jsonValidatorMiddleware as O}from"../middleware/jsonValidatorMiddleware.js";import{searchFacetsRequestSchema as Ea}from"../../../api-schemas/search-facets.js";import{searchRequestSchema as Ta}from"../../../api-schemas/search.js";function me(a,e,d){const{resolveRouteData:n,readStaticAsset:A}=d;a.use("*",j()),a.use("*",ia()),a.use("*",I(e)),a.use("*",C(e)),a.use("*",X()),a.use("*",$(e)),a.use("*",Aa()),a.use(i("*"),R({root:`./${H}`,getContent:(o,r)=>la(o,r,e,A),rewriteRequestPath:o=>c(o)})),a.use(i(l.FEEDBACK),t({allowMethods:["POST"]})),a.use(i(l.ASK_AI),t({allowMethods:["POST"]})),a.use(i(l.AI_ASSISTANT_ESCALATIONS),t({allowMethods:["POST"]})),a.use(i(l.AI_ASSISTANT_FEEDBACK),t({allowMethods:["POST"]})),a.use(i(l.SEMANTIC_SEARCH),t({allowMethods:["POST"]})),a.use("*",_a(e));const p=g(e);a.use(i(l.INFO),S()),L.NEW_CATALOG_ENABLED&&(a.use(i(l.CATALOG_ENTITIES),m({serverOutDir:e.serverOutDir}),ta(e)),a.use(i(l.CATALOG_ENTITIES_RELATIONS),m({serverOutDir:e.serverOutDir}),ma(e)),a.get(i(l.BFF_CATALOG_ENTITIES),m({serverOutDir:e.serverOutDir,protectReadMethods:!1}),da(e)),a.get(i(l.BFF_CATALOG_RELATED_ENTITIES),m({serverOutDir:e.serverOutDir,protectReadMethods:!1}),na(e)),a.get(i(l.BFF_CATALOG_REVISIONS),m({serverOutDir:e.serverOutDir,protectReadMethods:!1}),oa(e))),a.get(i(l.SHARED_PAGE_DATA),b(e)),a.get(i(l.PAGE_DATA),k(e,n)),a.get(i(l.APP_DATA),U(e)),a.post(i(l.SEARCH),p,O(Ta),K(e)),a.post(i(l.SEARCH_FACETS),p,O(Ea),y(e)),a.post(i(l.AUTHORIZATION),P),a.post(i(l.LOGOUT),s(e)),a.get(i(l.LOGOUT),s(e)),a.get(i(l.POST_LOGOUT),w(e)),a.get(i(l.OIDC_CALLBACK),N(e)),a.get(i(l.REDOCLY_TOKEN_LOGIN),F(e)),a.get(i(l.REDOCLY_LOGIN_CALLBACK),B()),a.get(i(l.IDP_LOGIN),M(e)),a.post(i(l.SAML_CALLBACK),v(e)),a.get(i(l.INVITE),G(e)),a.get(i(l.HEALTH),W),sa(a,e),h(a,e),D(a,e),a.all(i(l.CORS_PROXY),f(e)),a.all(i(`${l.CORS_PROXY}/*`),f(e)),ea(a,e),a.post(i(l.FEEDBACK),q(e)),a.post(i(l.RESOLVE_ROUTE_BY_PATH),T(e)),a.post(i(l.RESOLVE_ROUTES_BY_PATHS),x(e)),a.post(i(l.RESOLVE_ROUTE_BY_SLUG),_(e)),a.post(i(l.ASK_AI),Z(e)),a.post(i(l.AI_ASSISTANT_ESCALATIONS),u(e)),a.post(i(l.AI_ASSISTANT_FEEDBACK),J(e)),a.post(i(l.SEMANTIC_SEARCH),Q(e)),a.get(i(l.GET_ROUTES_BY_LINE),E(e)),a.post(i(l.OTEL_TRACES),z),a.get(i(l.REPLAY_OAUTH2_CALLBACK),aa),a.all(i("/*"),Y(e,n,A)),a.get("*",V),a.onError(pa)}function _a(a){return async(e,d)=>{await a.waitForPluginsLifecycle(),await d()}}function de(a,e){a.get(i(l.INFO),S()),a.post(i(l.RESOLVE_ROUTE_BY_PATH),T(e)),a.post(i(l.RESOLVE_ROUTE_BY_SLUG),_(e)),a.get(i(l.GET_ROUTES_BY_LINE),E(e))}export{de as installDevRoutes,me as installProdRoutes,_a as waitForPluginsLifecycle};
@@ -1 +1 @@
1
- import{withPathPrefix as S}from"@redocly/theme/core/utils";import{DEFAULT_DOCS_MCP_SERVER_NAME as h,MCP_PROTOCOL_VERSION as f}from"../../../plugins/mcp/constants.js";import{RESERVED_ROUTES as O}from"../../../plugins/get-reserved-routes.js";import{isMcpCodeModeEnabled as A}from"../../../plugins/mcp/utils/mode.js";import{CODE_MODE_TOOL_NAMES as T,DESCRIBE_TOOLS_TOOL_NAME as b,FETCH_TOOL_NAME as M}from"../../../plugins/mcp/codemode/constants.js";import{EXECUTE_TOOL_SCHEMA as R}from"../../../plugins/mcp/codemode/tools/execute-schema.js";import{describeToolsSchema as g}from"../../../plugins/mcp/codemode/tools/describe-tools.js";import{buildCodeModeInstructions as v,buildExecuteDescription as I,DESCRIBE_TOOLS_DESCRIPTION as L}from"../../../plugins/mcp/codemode/prompts.js";import{composeExecuteAnnotations as D}from"../../../plugins/mcp/codemode/capabilities/index.js";import{ALL_CAPABILITIES as P}from"../../../plugins/mcp/codemode/capabilities/registry.js";import{isGatewayCapabilityEnabled as y}from"../../../plugins/mcp/gateway-mcp/eligibility.js";import{shouldHandleMcpAuth as V}from"../../../plugins/mcp/auth/auth-handlers.js";import{resolvePublicBaseUrl as H}from"../../../web-server/utils/resolve-public-base-url.js";import{telemetryTraceStep as N}from"../../../telemetry/helpers/trace-step.js";import{MCP_AUTH_SCHEMES as U}from"./constants.js";const x="https://static.modelcontextprotocol.io/schemas/mcp-server-card/v1.json",w="1.0";function oe(r){const i=A(),u=new Date().toISOString().slice(0,10);return async c=>await N("com.redocly.mcpServerCard.served",async e=>{e?.setAttribute("id","mcpServerCardId"),e?.setAttribute("object","mcpServerCard"),e?.setAttribute("uri","urn:redocly:realm:api:mcpServerCard:mcpServerCardId");const o=r.getConfig(),m=o.mcp?.docs?.name||h,a=r.getMcpTools().filter(t=>!T.has(t.name)),s=y(o.mcp)?[...P]:[],d=s.some(t=>t.id===M),n=(i?[{...R,description:I(d),annotations:D(s)},{name:b,description:L,schema:g(a,s)}]:a).map(({name:t,description:E,schema:_,annotations:p})=>({name:t,description:E,inputSchema:_,...p?{annotations:p}:{}})),C=o.skills?.hide===!0?[]:r.getSkills(),l={$schema:x,version:w,protocolVersion:f,serverInfo:{name:m,title:m,version:u},description:o.seo?.description,documentationUrl:H(r,c),transport:{type:"streamable-http",endpoint:S(O.mcp)},capabilities:{logging:{},tools:{listChanged:!0},...C.length>0?{resources:{listChanged:!0},completions:{}}:{}},authentication:V(!!o.access?.requiresLogin,o.access?.rbac)?{required:!0,schemes:U}:void 0,instructions:i?v(d):void 0,tools:n.length>0?n:["dynamic"]};return e?.setAttribute("mode",i?"code":"tools"),e?.setAttribute("toolsCount",n.length),e?.setAttribute("authRequired",l.authentication!==void 0),c.json(l,200,{"Access-Control-Allow-Origin":"*","Cache-Control":"public, max-age=3600"})})}export{oe as mcpServerCardHandler};
1
+ import{withPathPrefix as f}from"@redocly/theme/core/utils";import{DEFAULT_DOCS_MCP_SERVER_NAME as h,MCP_PROTOCOL_VERSION as O}from"../../../plugins/mcp/constants.js";import{RESERVED_ROUTES as A}from"../../../plugins/get-reserved-routes.js";import{isMcpCodeModeEnabled as T}from"../../../plugins/mcp/utils/mode.js";import{CODE_MODE_TOOL_NAMES as b,DESCRIBE_TOOLS_TOOL_NAME as M,FETCH_TOOL_NAME as R}from"../../../plugins/mcp/codemode/constants.js";import{EXECUTE_TOOL_SCHEMA as g}from"../../../plugins/mcp/codemode/tools/execute-schema.js";import{describeToolsSchema as v}from"../../../plugins/mcp/codemode/tools/describe-tools.js";import{buildCodeModeInstructions as L,buildExecuteDescription as I,DESCRIBE_TOOLS_DESCRIPTION as D}from"../../../plugins/mcp/codemode/prompts.js";import{toToolListEntries as P}from"../../../plugins/mcp/codemode/build-tool-list.js";import{composeExecuteAnnotations as y}from"../../../plugins/mcp/codemode/capabilities/index.js";import{ALL_CAPABILITIES as V}from"../../../plugins/mcp/codemode/capabilities/registry.js";import{isGatewayCapabilityEnabled as H}from"../../../plugins/mcp/gateway-mcp/eligibility.js";import{shouldHandleMcpAuth as N}from"../../../plugins/mcp/auth/auth-handlers.js";import{resolvePublicBaseUrl as U}from"../../../web-server/utils/resolve-public-base-url.js";import{telemetryTraceStep as x}from"../../../telemetry/helpers/trace-step.js";import{MCP_AUTH_SCHEMES as w}from"./constants.js";const B="https://static.modelcontextprotocol.io/schemas/mcp-server-card/v1.json",j="1.0";function ie(r){const s=T(),E=new Date().toISOString().slice(0,10);return async m=>await x("com.redocly.mcpServerCard.served",async e=>{e?.setAttribute("id","mcpServerCardId"),e?.setAttribute("object","mcpServerCard"),e?.setAttribute("uri","urn:redocly:realm:api:mcpServerCard:mcpServerCardId");const o=r.getConfig(),a=o.mcp?.docs?.name||h,n=r.getMcpTools().filter(t=>!b.has(t.name)),i=H(o.mcp)?[...V]:[],d=i.some(t=>t.id===R),l=P(n,i),c=(s?[{...g,description:I(d,l),annotations:y(i)},{name:M,description:D,schema:v(n,i)}]:n).map(({name:t,description:_,schema:S,annotations:u})=>({name:t,description:_,inputSchema:S,...u?{annotations:u}:{}})),C=o.skills?.hide===!0?[]:r.getSkills(),p={$schema:B,version:j,protocolVersion:O,serverInfo:{name:a,title:a,version:E},description:o.seo?.description,documentationUrl:U(r,m),transport:{type:"streamable-http",endpoint:f(A.mcp)},capabilities:{logging:{},tools:{listChanged:!0},...C.length>0?{resources:{listChanged:!0},completions:{}}:{}},authentication:N(!!o.access?.requiresLogin,o.access?.rbac)?{required:!0,schemes:w}:void 0,instructions:s?L(d,l):void 0,tools:c.length>0?c:["dynamic"]};return e?.setAttribute("mode",s?"code":"tools"),e?.setAttribute("toolsCount",c.length),e?.setAttribute("authRequired",p.authentication!==void 0),m.json(p,200,{"Access-Control-Allow-Origin":"*","Cache-Control":"public, max-age=3600"})})}export{ie as mcpServerCardHandler};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/realm",
3
- "version": "0.136.0-next.13",
3
+ "version": "0.136.0-next.14",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "bin": {
@@ -54,15 +54,15 @@
54
54
  "fflate": "0.7.4",
55
55
  "flexsearch": "0.7.43",
56
56
  "graphql": "16.12.0",
57
- "hono": "4.12.34",
57
+ "hono": "4.13.1",
58
58
  "htmlparser2": "8.0.2",
59
59
  "i18next": "22.4.15",
60
60
  "is-glob": "4.0.3",
61
- "js-yaml": "4.3.0",
61
+ "js-yaml": "4.3.1",
62
62
  "lru-cache": "11.1.0",
63
63
  "minimatch": "10.2.4",
64
64
  "mri": "1.2.0",
65
- "nanoid": "5.0.9",
65
+ "nanoid": "5.1.16",
66
66
  "nprogress": "0.2.0",
67
67
  "openapi-sampler": "^1.7.2",
68
68
  "os-browserify": "0.3.0",
@@ -93,15 +93,15 @@
93
93
  "xpath": "0.0.34",
94
94
  "yaml-ast-parser": "0.0.43",
95
95
  "zod": "^3.25.76",
96
- "@redocly/api-docs": "0.1.0-next.5",
97
- "@redocly/asyncapi-docs": "1.13.0-next.11",
96
+ "@redocly/api-docs": "0.1.0-next.6",
97
+ "@redocly/asyncapi-docs": "1.13.0-next.12",
98
98
  "@redocly/config": "0.53.1",
99
- "@redocly/graphql-docs": "1.13.0-next.11",
100
- "@redocly/openapi-docs": "3.24.0-next.11",
99
+ "@redocly/graphql-docs": "1.13.0-next.12",
100
+ "@redocly/openapi-docs": "3.24.0-next.12",
101
101
  "@redocly/portal-legacy-ui": "0.19.0-next.0",
102
- "@redocly/realm-asyncapi-sdk": "0.14.0-next.3",
103
- "@redocly/theme": "0.68.0-next.8",
104
- "@redocly/portal-plugin-mock-server": "0.21.0-next.11"
102
+ "@redocly/portal-plugin-mock-server": "0.21.0-next.12",
103
+ "@redocly/realm-asyncapi-sdk": "0.14.0-next.4",
104
+ "@redocly/theme": "0.68.0-next.8"
105
105
  },
106
106
  "peerDependencies": {
107
107
  "react": "^19.2.7",