@redocly/redoc 0.137.0-next.4 → 0.137.0-next.6

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 (108) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/dist/api-schemas/otel-traces.d.ts +1359 -0
  3. package/dist/api-schemas/otel-traces.js +1 -0
  4. package/dist/cli/stats/collectors/asyncapi/index.d.ts +3 -0
  5. package/dist/cli/stats/collectors/asyncapi/index.js +1 -0
  6. package/dist/cli/stats/collectors/extension-sampling.d.ts +44 -0
  7. package/dist/cli/stats/collectors/extension-sampling.js +1 -0
  8. package/dist/cli/stats/collectors/openapi/index.js +1 -1
  9. package/dist/cli/stats/collectors/prepare-stats-walk.d.ts +10 -0
  10. package/dist/cli/stats/collectors/prepare-stats-walk.js +1 -0
  11. package/dist/cli/stats/collectors/run-stats-collector.d.ts +24 -0
  12. package/dist/cli/stats/collectors/run-stats-collector.js +1 -0
  13. package/dist/cli/stats/index.js +1 -1
  14. package/dist/client/app/hooks/useMcpData.d.ts +1 -0
  15. package/dist/client/app/hooks/useMcpData.js +1 -1
  16. package/dist/client/app/markdoc/custom-components/openapi/json-schema.js +1 -1
  17. package/dist/client/app/markdoc/custom-components/openapi/openapi-code-sample.js +1 -1
  18. package/dist/client/app/markdoc/custom-components/openapi/openapi-example.js +1 -1
  19. package/dist/client/app/markdoc/custom-components/openapi/openapi-response-sample.js +1 -1
  20. package/dist/client/app/markdoc/custom-components/openapi/use-markdown-adapter.d.ts +45 -0
  21. package/dist/client/app/markdoc/custom-components/openapi/use-markdown-adapter.js +1 -0
  22. package/dist/client/providers/hooks.js +1 -1
  23. package/dist/client/providers/post-message/PostMessageProvider.js +1 -1
  24. package/dist/constants/l10n/langs/ar.js +1 -1
  25. package/dist/constants/l10n/langs/de.js +1 -1
  26. package/dist/constants/l10n/langs/en.js +1 -1
  27. package/dist/constants/l10n/langs/es.js +1 -1
  28. package/dist/constants/l10n/langs/fr.js +1 -1
  29. package/dist/constants/l10n/langs/hi.js +1 -1
  30. package/dist/constants/l10n/langs/it.js +1 -1
  31. package/dist/constants/l10n/langs/ja.js +1 -1
  32. package/dist/constants/l10n/langs/ko.js +1 -1
  33. package/dist/constants/l10n/langs/pl.js +1 -1
  34. package/dist/constants/l10n/langs/pt-BR.js +1 -1
  35. package/dist/constants/l10n/langs/pt.js +1 -1
  36. package/dist/constants/l10n/langs/ru.js +1 -1
  37. package/dist/constants/l10n/langs/uk.js +1 -1
  38. package/dist/constants/l10n/langs/zh.js +1 -1
  39. package/dist/server/config/env-config.d.ts +2 -0
  40. package/dist/server/config/env-schema.d.ts +5 -0
  41. package/dist/server/config/env-schemas/search.d.ts +3 -0
  42. package/dist/server/config/env-schemas/search.js +1 -1
  43. package/dist/server/constants/mcp.d.ts +5 -0
  44. package/dist/server/constants/mcp.js +1 -0
  45. package/dist/server/constants/plugins/search.d.ts +0 -2
  46. package/dist/server/constants/plugins/search.js +1 -1
  47. package/dist/server/constants/rbac.d.ts +3 -0
  48. package/dist/server/constants/rbac.js +1 -0
  49. package/dist/server/plugins/api-docs/search/get-ai-search-documents.js +9 -9
  50. package/dist/server/plugins/api-docs/search/search-indexer.js +1 -1
  51. package/dist/server/plugins/markdown/utils/sanitize-malformed-md-content.js +2 -2
  52. package/dist/server/plugins/mcp/auth/auth-handlers.d.ts +1 -2
  53. package/dist/server/plugins/mcp/auth/auth-handlers.js +1 -1
  54. package/dist/server/plugins/mcp/codemode/tools/describe-tools.d.ts +1 -1
  55. package/dist/server/plugins/mcp/codemode/tools/describe-tools.js +1 -1
  56. package/dist/server/plugins/mcp/codemode/tools/execute-schema.js +1 -1
  57. package/dist/server/plugins/mcp/codemode/tools/manage-credentials.js +2 -2
  58. package/dist/server/plugins/mcp/constants.d.ts +0 -1
  59. package/dist/server/plugins/mcp/constants.js +1 -1
  60. package/dist/server/plugins/mcp/docs-mcp/tool-schemas.js +1 -1
  61. package/dist/server/plugins/mcp/gateway-mcp/elicitation.d.ts +3 -2
  62. package/dist/server/plugins/mcp/gateway-mcp/elicitation.js +5 -4
  63. package/dist/server/plugins/mcp/gateway-mcp/eligibility.js +1 -1
  64. package/dist/server/plugins/mcp/gateway-mcp/fetch/fetch-audit.d.ts +2 -1
  65. package/dist/server/plugins/mcp/gateway-mcp/fetch/fetch-audit.js +1 -1
  66. package/dist/server/plugins/mcp/gateway-mcp/fetch/fetch-bridge.js +1 -1
  67. package/dist/server/plugins/mcp/gateway-mcp/fetch/upstream-detail.d.ts +7 -0
  68. package/dist/server/plugins/mcp/gateway-mcp/fetch/upstream-detail.js +1 -0
  69. package/dist/server/plugins/mcp/gateway-mcp/upstream-auth.d.ts +6 -0
  70. package/dist/server/plugins/mcp/gateway-mcp/upstream-auth.js +1 -1
  71. package/dist/server/plugins/mcp/handlers/docs-mcp-handler.js +1 -1
  72. package/dist/server/plugins/mcp/handlers/handle-mcp-request.js +1 -1
  73. package/dist/server/plugins/mcp/handlers/mcp-credentials-handler.js +1 -1
  74. package/dist/server/plugins/mcp/handlers/mcp-credentials-page.d.ts +6 -0
  75. package/dist/server/plugins/mcp/handlers/mcp-credentials-page.js +50 -42
  76. package/dist/server/plugins/mcp/index.d.ts +0 -2
  77. package/dist/server/plugins/mcp/index.js +1 -1
  78. package/dist/server/plugins/mcp/servers/docs-server.js +1 -1
  79. package/dist/server/plugins/mcp/servers/mcp-server.js +3 -3
  80. package/dist/server/plugins/mcp/types.d.ts +1 -0
  81. package/dist/server/plugins/mcp/utils/tool-registration.d.ts +17 -0
  82. package/dist/server/plugins/mcp/utils/tool-registration.js +1 -0
  83. package/dist/server/plugins/openapi-docs/search/get-ai-search-documents.js +5 -5
  84. package/dist/server/plugins/search/engines/typesense/index.js +1 -1
  85. package/dist/server/plugins/search/index.js +1 -1
  86. package/dist/server/plugins/search/utils.d.ts +2 -1
  87. package/dist/server/plugins/search/utils.js +2 -2
  88. package/dist/server/types/plugins/common.d.ts +1 -0
  89. package/dist/server/utils/rbac/can-anonymous-access-mcp-feature.d.ts +3 -0
  90. package/dist/server/utils/rbac/can-anonymous-access-mcp-feature.js +1 -0
  91. package/dist/server/utils/rbac/should-handle-mcp-auth.d.ts +2 -0
  92. package/dist/server/utils/rbac/should-handle-mcp-auth.js +1 -0
  93. package/dist/server/utils/rbac.js +1 -1
  94. package/dist/server/web-server/auth.js +3 -3
  95. package/dist/server/web-server/handle-api-route-request.js +1 -1
  96. package/dist/server/web-server/routes/error.js +1 -1
  97. package/dist/server/web-server/routes/helpers/resolve-ui-locales-for-idp-login.js +1 -1
  98. package/dist/server/web-server/routes/index.js +1 -1
  99. package/dist/server/web-server/routes/mcp-routes/agent-card.js +1 -1
  100. package/dist/server/web-server/routes/mcp-routes/mcp-server-card.js +1 -1
  101. package/dist/server/web-server/routes/otel/otel.js +1 -1
  102. package/dist/server/web-server/routes/page-data.js +1 -1
  103. package/dist/server/web-server/utils/prepare-list-response.d.ts +0 -4
  104. package/dist/server/web-server/utils/prepare-list-response.js +1 -1
  105. package/dist/server/web-server/utils.d.ts +1 -1
  106. package/dist/server/web-server/utils.js +1 -1
  107. package/dist/types/global-data.d.ts +1 -0
  108. package/package.json +9 -9
@@ -1,4 +1,3 @@
1
- import type { SearchDocument } from '../../types';
2
1
  export declare const SEARCH_INDEX_FIELDS: string[];
3
2
  export declare const SEARCH_GROUP_NAME_DOCUMENTATION = "Documentation";
4
3
  export declare const SEARCH_GROUP_NAME_API = "API Reference";
@@ -12,7 +11,6 @@ export declare const AI_INDEX_EXPORT_FOLDER = "ai-search";
12
11
  export declare const AI_SEARCH_CHUNK_SIZE = 50;
13
12
  export declare const AI_SEARCH_DOCUMENT_CHUNK_SIZE = 250;
14
13
  export declare const SEARCH_DOCUMENT_METADATA_KEY: unique symbol;
15
- export declare const BASE_SEARCH_DOCUMENT: SearchDocument;
16
14
  export declare const DEFAULT_AI_SEARCH_ENABLED = true;
17
15
  export declare const DISABLE_DEEP_LINK_IF_FIELDS_EXIST: string[];
18
16
  //# sourceMappingURL=search.d.ts.map
@@ -1 +1 @@
1
- import{DEFAULT_ANONYMOUS_VISITOR_TEAM as e,DEFAULT_AUTHENTICATED_TEAM as t}from"../../../constants/common.js";const _=["title","text","path","httpPath","parameters[]:name","parameters[]:description","parameters[]:place","parameters[]:path"],A="Documentation",o="API Reference",r="search",p=100,T=10,a=25,n=100,s=150,c="ai-search",I=50,S=250,C=Symbol("metadata"),R={id:"",url:"",title:"",text:"",security:[],parameters:[{place:"",mediaType:void 0,name:"",description:"",path:[],type:"object",required:!1,example:void 0,enum:void 0}],rbacTeams:[e,t]},D=!0,H=["title"];export{c as AI_INDEX_EXPORT_FOLDER,I as AI_SEARCH_CHUNK_SIZE,S as AI_SEARCH_DOCUMENT_CHUNK_SIZE,R as BASE_SEARCH_DOCUMENT,D as DEFAULT_AI_SEARCH_ENABLED,H as DISABLE_DEEP_LINK_IF_FIELDS_EXIST,s as HIGHLIGHTED_TEXT_MAX_LENGTH,r as SEARCH_DATA_EXPORT_FOLDER,C as SEARCH_DOCUMENT_METADATA_KEY,T as SEARCH_GROUP_LIMIT,o as SEARCH_GROUP_NAME_API,A as SEARCH_GROUP_NAME_DOCUMENTATION,_ as SEARCH_INDEX_FIELDS,p as SEARCH_LIMIT,a as SEARCH_MAX_FACET_VALUES,n as SEARCH_MAX_INMEMORY_DOCUMENTS_COUNT};
1
+ const t=["title","text","path","httpPath","parameters[]:name","parameters[]:description","parameters[]:place","parameters[]:path"],_="Documentation",E="API Reference",e="search",o=100,A=10,r=25,p=100,I=150,s="ai-search",c=50,R=250,S=Symbol("metadata"),a=!0,n=["title"];export{s as AI_INDEX_EXPORT_FOLDER,c as AI_SEARCH_CHUNK_SIZE,R as AI_SEARCH_DOCUMENT_CHUNK_SIZE,a as DEFAULT_AI_SEARCH_ENABLED,n as DISABLE_DEEP_LINK_IF_FIELDS_EXIST,I as HIGHLIGHTED_TEXT_MAX_LENGTH,e as SEARCH_DATA_EXPORT_FOLDER,S as SEARCH_DOCUMENT_METADATA_KEY,A as SEARCH_GROUP_LIMIT,E as SEARCH_GROUP_NAME_API,_ as SEARCH_GROUP_NAME_DOCUMENTATION,t as SEARCH_INDEX_FIELDS,o as SEARCH_LIMIT,r as SEARCH_MAX_FACET_VALUES,p as SEARCH_MAX_INMEMORY_DOCUMENTS_COUNT};
@@ -0,0 +1,3 @@
1
+ import type { AuthDetails } from '../types';
2
+ export declare const ANONYMOUS_AUTH_DETAILS: AuthDetails;
3
+ //# sourceMappingURL=rbac.d.ts.map
@@ -0,0 +1 @@
1
+ import{DEFAULT_ANONYMOUS_VISITOR_TEAM as t}from"../../constants/common.js";const A={isAuthenticated:!1,teams:[t],claims:{email:"",authCookie:""}};export{A as ANONYMOUS_AUTH_DETAILS};
@@ -1,5 +1,5 @@
1
- import P from"node:path";import{REDOCLY_TEAMS_RBAC as O}from"@redocly/config";import{nodeTypes as h,contentType as g}from"@redocly/api-docs";import{normalizeApiVersion as sn}from"../utils/normalize-api-version.js";import{replaceFileExtension as v}from"../utils/replace-file-extension.js";import{isResourcePubliclyAccessible as cn,canDownloadApiDefinition as ln,extractTeamsFromScopeItems as fn}from"../../../utils/rbac.js";import{getLlmsTxtMdPathBySlug as J}from"../../../utils/llmstxt/get-llms-txt-md-path-by-slug.js";import{PUBLIC_API_DEFINITIONS_FOLDER as Q}from"../../../constants/common.js";import{DEFAULT_ANONYMOUS_VISITOR_TEAM as an,GRAPHQL_SPEC_SLUG as pn}from"../../../../constants/common.js";import{toMarkdown as N}from"../../markdown/search/to-markdown.js";import{createRenderTagFn as un}from"../../markdown/search/create-render-tag-fn.js";import{getLocaleFromRelativePath as dn}from"../../../fs/utils/get-locale-from-relative-path.js";import{getBuildTranslateContext as hn}from"../../../utils/l10n/get-build-translate-context.js";import{llmsTxtLink as w}from"../../search/llmstxt/index.js";import{formatDocumentMetadata as mn}from"../../search/utils.js";import{extractDocumentSearchFacets as $n}from"./search-facets.js";import{ApiDocsSearchIndexer as gn}from"./search-indexer.js";function A(t,r){return r==="openapi"&&"openapi"in t}function Y(t,r){return r==="asyncapi"&&"asyncapi"in t}function Jn({items:t,metadata:r,store:c,document:n,specType:o,excludeFromSearch:i,basePath:e,getSearchFacets:l,markdocTags:s}){return async(f,a,u,d)=>{if(i)return;const m={...d.markdocOptions?.tags,...s},{locale:X,translate:Z}=await hn(f.slug,d.getGlobalData?.().l10n),C=un(m,X,Z),x=await u.getConfig(),R=dn(f.fsPath),M=new gn(e,c?.schemaStore,n),j=y=>{for(const p of y)M.addItem(p),p.items?.length&&j(p.items)};j(t);const nn=M.getResult(),V=new Map;for(const y of nn)V.set(y.url,y);const L=typeof r.title=="string"?r.title:void 0,_=sn(r.version),E=$n(f.metadata||{},L,_,l),I=[],S=[],F=yn(r,n,o),q=bn(o,r,f.fsPath,x),tn=Sn(t,x,C),en=F+q+`
2
- `+tn,on=F+q;S.push({title:L||"API Overview",description:typeof r.description=="string"?r.description:void 0,content:en,slug:e,fsPath:f.fsPath,includeInLLMsTxt:!0}),I.push({title:L||"API Overview",content:on,url:e,fsPath:f.fsPath,locale:R,product:f.product?.name,facets:E});const rn=n&&A(n,o)?n.security:void 0,D=y=>{for(const p of y){if(!p.content||!p.link){p.items?.length&&D(p.items);continue}const k=V.get(p.link),G=p.content.contentType,z=p[O],W=z?fn(z):void 0,B=W?.length?W:void 0;if(G===g.ITEM){const $=Pn(p,n,o)??H(p,k,o,_,rn,C);if($){const b=p.label||"";S.push({title:b,description:void 0,content:$,slug:p.link,fsPath:f.fsPath,includeInLLMsTxt:!1}),I.push({title:b,content:$,url:p.link,fsPath:f.fsPath,locale:R,product:f.product?.name,facets:E,rbacTeams:B})}}else if(G===g.GROUP){const $=On(p,x,o,C);if($){const b=p.label||"";S.push({title:b,description:k?Ln(k.text):void 0,content:$.llmstxt,slug:p.link,fsPath:f.fsPath,includeInLLMsTxt:!1}),I.push({title:b,content:$.aiSearch,url:p.link,fsPath:f.fsPath,locale:R,product:f.product?.name,facets:E,rbacTeams:B})}}p.items?.length&&D(p.items)}};return D(t),{async getLLMsTxts(){return S},async getSearchDocuments(){return I}}}}function yn(t,r,c){const n=typeof t.title=="string"?t.title:"",o=typeof t.description=="string"?t.description:"",i=typeof t.summary=="string"?t.summary:"",e=typeof t.version=="string"?t.version:"";let l=n?`# ${n}
1
+ import P from"node:path";import{REDOCLY_TEAMS_RBAC as O}from"@redocly/config";import{nodeTypes as h,contentType as g}from"@redocly/api-docs";import{normalizeApiVersion as sn}from"../utils/normalize-api-version.js";import{replaceFileExtension as v}from"../utils/replace-file-extension.js";import{isResourcePubliclyAccessible as cn,canDownloadApiDefinition as ln,extractTeamsFromScopeItems as fn}from"../../../utils/rbac.js";import{getLlmsTxtMdPathBySlug as J}from"../../../utils/llmstxt/get-llms-txt-md-path-by-slug.js";import{PUBLIC_API_DEFINITIONS_FOLDER as Q}from"../../../constants/common.js";import{GRAPHQL_SPEC_SLUG as an}from"../../../../constants/common.js";import{ANONYMOUS_AUTH_DETAILS as pn}from"../../../constants/rbac.js";import{toMarkdown as N}from"../../markdown/search/to-markdown.js";import{createRenderTagFn as un}from"../../markdown/search/create-render-tag-fn.js";import{getLocaleFromRelativePath as dn}from"../../../fs/utils/get-locale-from-relative-path.js";import{getBuildTranslateContext as hn}from"../../../utils/l10n/get-build-translate-context.js";import{llmsTxtLink as w}from"../../search/llmstxt/index.js";import{formatDocumentMetadata as mn}from"../../search/utils.js";import{extractDocumentSearchFacets as $n}from"./search-facets.js";import{ApiDocsSearchIndexer as gn}from"./search-indexer.js";function A(t,r){return r==="openapi"&&"openapi"in t}function H(t,r){return r==="asyncapi"&&"asyncapi"in t}function Qn({items:t,metadata:r,store:c,document:n,specType:o,excludeFromSearch:i,basePath:e,getSearchFacets:l,markdocTags:s}){return async(f,a,u,d)=>{if(i)return;const m={...d.markdocOptions?.tags,...s},{locale:X,translate:Z}=await hn(f.slug,d.getGlobalData?.().l10n),C=un(m,X,Z),x=await u.getConfig(),L=dn(f.fsPath),j=new gn(e,c?.schemaStore,n),M=y=>{for(const p of y)j.addItem(p),p.items?.length&&M(p.items)};M(t);const nn=j.getResult(),V=new Map;for(const y of nn)V.set(y.url,y);const R=typeof r.title=="string"?r.title:void 0,_=sn(r.version),D=$n(f.metadata||{},R,_,l),I=[],S=[],q=yn(r,n,o),F=bn(o,r,f.fsPath,x),tn=Sn(t,x,C),en=q+F+`
2
+ `+tn,on=q+F;S.push({title:R||"API Overview",description:typeof r.description=="string"?r.description:void 0,content:en,slug:e,fsPath:f.fsPath,includeInLLMsTxt:!0}),I.push({title:R||"API Overview",content:on,url:e,fsPath:f.fsPath,locale:L,product:f.product?.name,facets:D});const rn=n&&A(n,o)?n.security:void 0,E=y=>{for(const p of y){if(!p.content||!p.link){p.items?.length&&E(p.items);continue}const k=V.get(p.link),G=p.content.contentType,z=p[O],W=z?fn(z):void 0,B=W?.length?W:void 0;if(G===g.ITEM){const $=Pn(p,n,o)??Y(p,k,o,_,rn,C);if($){const b=p.label||"";S.push({title:b,description:void 0,content:$,slug:p.link,fsPath:f.fsPath,includeInLLMsTxt:!1}),I.push({title:b,content:$,url:p.link,fsPath:f.fsPath,locale:L,product:f.product?.name,facets:D,rbacTeams:B})}}else if(G===g.GROUP){const $=On(p,x,o,C);if($){const b=p.label||"";S.push({title:b,description:k?Rn(k.text):void 0,content:$.llmstxt,slug:p.link,fsPath:f.fsPath,includeInLLMsTxt:!1}),I.push({title:b,content:$.aiSearch,url:p.link,fsPath:f.fsPath,locale:L,product:f.product?.name,facets:D,rbacTeams:B})}}p.items?.length&&E(p.items)}};return E(t),{async getLLMsTxts(){return S},async getSearchDocuments(){return I}}}}function yn(t,r,c){const n=typeof t.title=="string"?t.title:"",o=typeof t.description=="string"?t.description:"",i=typeof t.summary=="string"?t.summary:"",e=typeof t.version=="string"?t.version:"";let l=n?`# ${n}
3
3
 
4
4
  `:"";if(l+=o?`${o}
5
5
 
@@ -10,9 +10,9 @@ import P from"node:path";import{REDOCLY_TEAMS_RBAC as O}from"@redocly/config";im
10
10
  `);const f=s["x-metadata"],a=mn(f);a&&(l+=`
11
11
  Metadata:
12
12
  ${a}
13
- `)}else if(Y(r,c)){const{info:s}=r;s?.license?.name&&(l+=`License: ${s.license.name}
13
+ `)}else if(H(r,c)){const{info:s}=r;s?.license?.name&&(l+=`License: ${s.license.name}
14
14
  `)}}return l+=`
15
- `,l+=An(r,c),l+=In(r,c),l}function bn(t,r,c,n){if(!c)return"";let o,i,e;if(t==="openapi")o=P.posix.join(Q,v(c,".yaml")),i="## Download OpenAPI description",e="OpenAPI definition";else if(t==="asyncapi")o=P.posix.join(Q,v(c,".yaml")),i="## Download AsyncAPI description",e="AsyncAPI definition";else if(t==="graphql"){const f=P.posix.extname(c)||".graphql";o=P.posix.join(pn,v(c,f)),i="## Download GraphQL schema",e="GraphQL schema"}else return"";if(!ln(o,n.access?.rbac??{},n.access?.requiresLogin??!1,{isAuthenticated:!1,teams:[an]}))return"";const s=typeof r.title=="string"&&r.title||e;return`${i}
15
+ `,l+=An(r,c),l+=In(r,c),l}function bn(t,r,c,n){if(!c)return"";let o,i,e;if(t==="openapi")o=P.posix.join(Q,v(c,".yaml")),i="## Download OpenAPI description",e="OpenAPI definition";else if(t==="asyncapi")o=P.posix.join(Q,v(c,".yaml")),i="## Download AsyncAPI description",e="AsyncAPI definition";else if(t==="graphql"){const f=P.posix.extname(c)||".graphql";o=P.posix.join(an,v(c,f)),i="## Download GraphQL schema",e="GraphQL schema"}else return"";if(!ln(o,n.access?.rbac??{},n.access?.requiresLogin??!1,pn))return"";const s=typeof r.title=="string"&&r.title||e;return`${i}
16
16
 
17
17
  - ${w({title:s,description:void 0,slug:o})}`}function An(t,r){if(!t)return"";if(A(t,r)){const{servers:c}=t;if(!c?.length)return"";let n=`## Servers
18
18
 
@@ -26,7 +26,7 @@ ${o.url}
26
26
  `,e.default&&(n+=` Default: ${JSON.stringify(e.default)}
27
27
  `),e.enum&&(n+=` Enum: ${e.enum.map(l=>JSON.stringify(l)).join(", ")}
28
28
  `);n+=`
29
- `}return n}if(Y(t,r)){const{servers:c}=t;if(!c||!Object.keys(c).length)return"";let n=`## Servers
29
+ `}return n}if(H(t,r)){const{servers:c}=t;if(!c||!Object.keys(c).length)return"";let n=`## Servers
30
30
 
31
31
  `;for(const[o,i]of Object.entries(c))n+=`### ${o}
32
32
 
@@ -55,7 +55,7 @@ ${o.url}
55
55
  `}n+=`
56
56
  `}return n}function Sn(t,r,c){let n="";const o=(i,e)=>{for(const l of i)if(l.link){if(l.content?.contentType===g.GROUP)n+=`${e} ${l.label||""}
57
57
 
58
- `,l.items?.length&&o(l.items,e+"#");else if(l.content?.contentType===g.ITEM){if(!U(l,r))continue;const s=l,f=s.httpVerb,a=s.httpPath,u=f&&a?`${f.toUpperCase()} ${a}`:l.label||"",d=l.content?T(l.content.children,c):void 0;n+=` - ${w({title:u,description:d?d.slice(0,200):void 0,slug:J(l.link)})}`}}};return o(t,"##"),n}function H(t,r,c,n,o,i){if(!t.content)return;const e=t,l=t.label||"",s=T(t.content.children,i),f=t.content.meta?.deprecated;let a=`# ${l}${f?" (deprecated)":""}
58
+ `,l.items?.length&&o(l.items,e+"#");else if(l.content?.contentType===g.ITEM){if(!U(l,r))continue;const s=l,f=s.httpVerb,a=s.httpPath,u=f&&a?`${f.toUpperCase()} ${a}`:l.label||"",d=l.content?T(l.content.children,c):void 0;n+=` - ${w({title:u,description:d?d.slice(0,200):void 0,slug:J(l.link)})}`}}};return o(t,"##"),n}function Y(t,r,c,n,o,i){if(!t.content)return;const e=t,l=t.label||"",s=T(t.content.children,i),f=t.content.meta?.deprecated;let a=`# ${l}${f?" (deprecated)":""}
59
59
 
60
60
  `;if(s&&(a+=`${s}
61
61
 
@@ -85,7 +85,7 @@ ${JSON.stringify(o.outputSchema,null,2)}
85
85
 
86
86
  `;if(i&&(e+=`${i}
87
87
 
88
- `),c==="graphql"){let s="";const f=a=>{for(const u of a??[])if(!(!u.content||!U(u,r)))if(u.content.contentType===g.ITEM){const d=H(u,void 0,c,void 0,void 0,n);d&&(s+=`${d}
88
+ `),c==="graphql"){let s="";const f=a=>{for(const u of a??[])if(!(!u.content||!U(u,r)))if(u.content.contentType===g.ITEM){const d=Y(u,void 0,c,void 0,void 0,n);d&&(s+=`${d}
89
89
 
90
90
  ---
91
91
 
@@ -97,9 +97,9 @@ ${JSON.stringify(o.outputSchema,null,2)}
97
97
 
98
98
  ${i.join(`
99
99
  `)}`}).join(`
100
- `)}function xn(t){let r="";const c=n=>{for(const o of n)if(o.nodeType===h.ITEM){const i=o;i.graphqlSchema&&(r+=Rn(i.graphqlSchema,i.variant))}else(o.nodeType===h.CONTAINER||o.nodeType===h.OVERVIEW_SECTION_WRAPPER)&&c(o.children)};return c(t),r}function Rn(t,r){let n=`
100
+ `)}function xn(t){let r="";const c=n=>{for(const o of n)if(o.nodeType===h.ITEM){const i=o;i.graphqlSchema&&(r+=Ln(i.graphqlSchema,i.variant))}else(o.nodeType===h.CONTAINER||o.nodeType===h.OVERVIEW_SECTION_WRAPPER)&&c(o.children)};return c(t),r}function Ln(t,r){let n=`
101
101
  ## ${K(r==="return-type"?"Return type":r==="graphql-args"||r==="arguments"?"Arguments":r==="graphql-fields"?"Fields":r)}:
102
102
 
103
103
  `;const o=i=>{const e=i.description?N(Array.isArray(i.description)?i.description:[i.description]).trim():"";return` - \`${i.name}\` (${i.type})${e?`: ${e}`:""}
104
104
  `};if(!Array.isArray(t)){const i=t.description?N(Array.isArray(t.description)?t.description:[t.description]).trim():"";n+=` Type: ${t.type}`,i&&(n+=` - ${i}`),n+=`
105
- `;for(const e of t.fields??[])n+=o(e);return n}if(!t.length)return"";for(const i of t)n+=o(i);return n}function T(t,r){const c=[],n=i=>{for(const e of i)if(e.nodeType===h.MARKDOC){const l=e.content,s=Array.isArray(l)?l:[l],f=N(s,{renderTag:r}).trim();f&&c.push(f)}else(e.nodeType===h.CONTAINER||e.nodeType===h.OVERVIEW_SECTION_WRAPPER)&&n(e.children)};return n(t),c.join(" ").trim()||void 0}function Ln(t){return(Array.isArray(t)?t.join(" "):t)||void 0}function K(t){return t.charAt(0).toUpperCase()+t.slice(1)}function U(t,r){return r.access?.requiresLogin?!0:cn({slug:t.link||"",[O]:t[O]},r)}export{Jn as getAiDocumentsStore};
105
+ `;for(const e of t.fields??[])n+=o(e);return n}if(!t.length)return"";for(const i of t)n+=o(i);return n}function T(t,r){const c=[],n=i=>{for(const e of i)if(e.nodeType===h.MARKDOC){const l=e.content,s=Array.isArray(l)?l:[l],f=N(s,{renderTag:r}).trim();f&&c.push(f)}else(e.nodeType===h.CONTAINER||e.nodeType===h.OVERVIEW_SECTION_WRAPPER)&&n(e.children)};return n(t),c.join(" ").trim()||void 0}function Rn(t){return(Array.isArray(t)?t.join(" "):t)||void 0}function K(t){return t.charAt(0).toUpperCase()+t.slice(1)}function U(t,r){return r.access?.requiresLogin?!0:cn({slug:t.link||"",[O]:t[O]},r)}export{Qn as getAiDocumentsStore};
@@ -1 +1 @@
1
- import{REDOCLY_TEAMS_RBAC as p}from"@redocly/config";import{extractFullText as W,nodeTypes as g,contentType as P,schemaKind as j}from"@redocly/api-docs";import{toMarkdown as H}from"../../markdown/search/to-markdown.js";const L=10,_=200,C="message payload",D="message headers";function E(h){if(typeof h=="string")return h;const e=Array.isArray(h)?h:[h];return H(e).trim()}function N(h){return h.length>_?`${h.slice(0,_)}\u2026`:h}class B{#c=[];#i;#s;#o;#f;constructor(e,t,n){this.#i=e,this.#s=t,this.#o=n}addItem(e){if(!(!e.link||!e.content))try{switch(e.content.contentType){case P.OVERVIEW:this.#a(e,this.#i);break;case P.GROUP:case P.ITEM:this.#a(e,e.link);break}}catch(t){console.error(`Cannot add item to api-docs search indexer [${e.label}, ${e.link}]:`,t.message)}}#a(e,t){if(!e.content)return;const n=t.startsWith(this.#i+"/")?t.slice(this.#i.length+1):t.split("/").slice(-2).join("/"),i=e.content.children,o=this.#p(i),f=this.#y(i,n),r=e,s={id:t,url:t,title:e.label??"",text:o,path:[],deprecated:e.content.meta?.deprecated,..."httpVerb"in e&&e.httpVerb?{httpMethod:e.httpVerb}:{},..."httpPath"in e&&e.httpPath?{httpPath:e.httpPath}:{},..."badges"in e&&Array.isArray(e.badges)&&e.badges.length?{badges:e.badges}:{},..."isAdditionalOperation"in e&&e.isAdditionalOperation?{isAdditionalOperation:!0}:{},[p]:r[p],...f.length&&{parameters:f}};this.#c.push(s)}#p(e){const t=[],n=i=>{for(const o of i)if(o.nodeType===g.MARKDOC){const f=o;if(this.#h(f.content))continue;const r=W(f.content);r&&t.push(r)}else(o.nodeType===g.CONTAINER||o.nodeType===g.OVERVIEW_SECTION_WRAPPER)&&n(o.children)};return n(e),t.join(" ").trim()}#h(e){let t=!1,n=!1;const i=f=>{f.type==="code"&&f.attributes?.content?t=!0:f.type==="text"&&f.attributes?.content&&(n=!0);for(const r of f.children??[])i(r)},o=Array.isArray(e)?e:[e];for(const f of o)i(f);return t&&!n}#y(e,t){const n={},i=new Set,o=f=>{for(const r of f)if(r.nodeType===g.ITEM){const s=r;if(s[p])continue;if(s.parameters?.length)for(const a of s.parameters){if(a[p])continue;const d=this.#O(s.variant),c=this.#$(s.variant),u=a.description?E(a.description):"",l=this.#A(a.schemaId),$={name:a.name,description:u,place:d,mediaType:void 0,type:l,required:a.required??!1,deepLink:this.#d(t,"request",c,void 0,void 0,[a.name]),example:void 0,enum:void 0};n[this.#n($)]=$}this.#g(s,n,i,t)}else if(r.nodeType===g.CONTAINER){const s=r;if(s[p])continue;o(s.children)}else r.nodeType===g.OVERVIEW_SECTION_WRAPPER&&o(r.children)};return o(e),Object.values(n)}#n(e){return[...Array.isArray(e.path)?e.path:[],e.name.toString()].join(".")+e.description+e.place}#d(e,t,n,i,o,f){if(!e)return"";const r=f.join("/"),s=[`t=${t}`];return n&&s.push(`in=${n}`),o&&s.push(`c=${o}`),i&&s.push(`ct=${i}`),r&&s.push(`path=${r}`),`/${e}#${e}/${s.join("&")}`.toLowerCase()}#m(e,t,n,i){if(!e)return"";let o="messages";t&&(o+=`&m=${t}`),o+=`&t=${n}`;const f=i.join("/");return f&&(o+=`&path=${f}`),`/${e}#${e}/${o}`.toLowerCase()}#$(e){switch(e){case"query":return"query";case"path":return"path";case"headers":return"header";case"cookies":return"cookie";default:return}}#O(e){switch(e){case"query":return"query parameters";case"path":return"path parameters";case"headers":return"header parameters";case"cookies":return"cookie parameters";case"parameters":return"channel parameters";default:return e?`${e} parameters`:"parameters"}}#A(e){const t=this.#s?.[e];if(!t||t.kind!==j.JSON_SCHEMA)return"unknown";const n=t.data,i=this.#l(n);return this.#u(i)}#u(e){return Array.isArray(e.type)?e.type.join(" | "):e.type?e.type:e.properties||e.additionalProperties?"object":e.items?"array":e.enum?"string":"any"}#g(e,t,n,i){const o=this.#E(e);if(e.graphqlSchema&&Array.isArray(e.graphqlSchema)){this.#S(e.graphqlSchema,o,t);return}if(!this.#s||!this.#o)return;const f=o.startsWith("response");if(e.mediaTypeSchemas){const r=Object.entries(e.mediaTypeSchemas),s=r.find(([,a])=>!a[p]);if(s){const[a,d]=s;d.schemaId&&this.#r(d.schemaId,o,a,r.length>1,f,t,[],n,i)}}else e.schemaId&&this.#r(e.schemaId,o,void 0,!1,f,t,[],n,i);if(e.responses)for(const r of e.responses){if(r[p])continue;const s=`response ${r.code} fields`,a=new Set;if(r.mediaTypeContent){const d=Object.entries(r.mediaTypeContent),c=d.find(([,u])=>!u[p]);if(c){const[u,l]=c;l.schemaId&&this.#r(l.schemaId,s,u,d.length>1,!0,t,[],a,i)}}else r.schemaId&&this.#r(r.schemaId,s,r.mediaType,!1,!0,t,[],a,i)}if(e.messages)for(const r of e.messages){if(r[p])continue;const s=r.description?E(r.description):"",a=r.summary??"",d={name:r.name,description:(s+a).trim(),place:"message",mediaType:void 0,type:"unknown",required:!1,example:void 0,enum:void 0};t[this.#n(d)]=d,this.#f=r.name;const c=new Set;r.schemaId&&this.#r(r.schemaId,C,r.contentType,!1,!1,t,[],c,i),r.headerSchemaId&&this.#r(r.headerSchemaId,D,void 0,!1,!1,t,[],c,i),this.#f=void 0}}#E(e){switch(e.variant){case"body":return"request fields";case"responses":return"response fields";case"graphql-fields":return"fields";case"graphql-args":return"arguments";case"return-type":return"return type fields";default:return e.variant??"fields"}}#S(e,t,n){for(const i of e){const o=i.description?E(i.description):"",f={name:i.name,description:o,place:t,mediaType:void 0,path:[],type:i.type??"unknown",required:!1,example:void 0,enum:void 0};if(n[this.#n(f)]=f,i.args?.length)for(const r of i.args){const s=r.description?E(r.description):"",a={name:r.name,description:s,place:"arguments",mediaType:void 0,path:[i.name],type:r.type??"unknown",required:!1,example:void 0,enum:void 0};n[this.#n(a)]=a}}}#r(e,t,n,i,o,f,r,s,a=""){const d=this.#s?.[e];if(!d||d.kind!==j.JSON_SCHEMA||d[p])return;const c=d.data,u=i?n:void 0;this.#t(c,t,n,u,o,f,r,s,0,a)}#I(e){const t=e.match(/^response (\d+)/);return t?t[1]:void 0}#l(e){if(e.allOf){const t={};for(const n of e.allOf){const i=n.$ref?this.#e(n.$ref):n;if(i){const o=t.properties,f=t.required;Object.assign(t,i),(o||i.properties)&&(t.properties={...o||{},...i.properties||{}}),(f||i.required)&&(t.required=[...f||[],...i.required||[]])}}return delete t.allOf,e.description&&(t.description=e.description),t}if(e.$ref){const t=this.#e(e.$ref);if(t){const n={...t};return e.description&&(n.description=e.description),n}}return e}#e(e){if(!e.startsWith("#/"))return;const t=e.slice(2).split("/");let n=this.#o;for(const i of t){if(n==null||typeof n!="object")return;n=n[i]}return n!=null&&typeof n=="object"?n:void 0}#t(e,t,n,i,o,f,r,s,a,d=""){if(!(a>L||e[p])){if(e.$ref){if(s.has(e.$ref))return;s.add(e.$ref);const c=this.#e(e.$ref);c&&this.#t(c,t,n,i,o,f,r,s,a,d);return}if(e.allOf){const c={};for(const u of e.allOf){let l=u;if(u.$ref){if(s.has(u.$ref))continue;s.add(u.$ref),l=this.#e(u.$ref)}if(l){const $=c.properties,A=c.required;Object.assign(c,l),($||l.properties)&&(c.properties={...$||{},...l.properties||{}}),(A||l.required)&&(c.required=[...A||[],...l.required||[]])}}delete c.allOf,this.#t(c,t,n,i,o,f,r,s,a,d);return}if(e.discriminator&&e.properties&&e.oneOf){this.#t({...e,oneOf:void 0,discriminator:void 0},t,n,i,o,f,r,s,a,d);return}if(e.oneOf||e.anyOf){for(const c of e.oneOf||e.anyOf||[]){if(c.$ref){if(s.has(c.$ref))continue;s.add(c.$ref)}const u=c.$ref?this.#e(c.$ref):c;u&&this.#t(u,t,n,i,o,f,r,s,a+1,d)}return}if(e.properties&&this.#b(e,t,n,i,o,f,r,s,a,d),e.items&&typeof e.items=="object"&&!Array.isArray(e.items)){if(e.items.$ref){if(s.has(e.items.$ref))return;s.add(e.items.$ref)}const c=e.items.$ref?this.#e(e.items.$ref):e.items;c&&this.#t(c,t,n,i,o,f,r,s,a+1,d)}}}#b(e,t,n,i,o,f,r,s,a,d){if(!e.properties)return;const c=e.required||[],u=this.#I(t),l=o?"response":"request",$=t===C?"payload":t===D?"headers":void 0;for(const[A,O]of Object.entries(e.properties)){if(O[p])continue;if(O.$ref){if(s.has(O.$ref))continue;s.add(O.$ref)}const m=O.$ref?this.#e(O.$ref):O;if(!m)continue;let x=!1;if(m.allOf){let b=!1;for(const q of m.allOf)q.$ref?s.has(q.$ref)||(s.add(q.$ref),b=!0):b=!0;x=!b}const y=this.#l(m);if(m[p]||y[p]||y.readOnly&&!o||y.writeOnly&&o||m.additionalProperties!==void 0&&!m.properties)continue;const T=m.description??y.description,R=T?E(T):"",S=m.example??y.example,V=S!==void 0?N(typeof S=="string"?S:JSON.stringify(S)):void 0,v=Array.isArray(y.enum)&&y.enum.length?y.enum:void 0,I=[...r,A],w={name:A,description:R,place:t,mediaType:n,path:r,type:this.#u(y),required:c.includes(A),deepLink:$?this.#m(d,this.#f,$,I):this.#d(d,l,void 0,i,u,I),example:V,enum:v},k=this.#n(w);f[k]==null&&(f[k]=w,x||this.#t(y,t,n,i,o,f,I,s,a+1,d))}}getResult(){return this.#c}}export{B as ApiDocsSearchIndexer};
1
+ import{REDOCLY_TEAMS_RBAC as f}from"@redocly/config";import{extractFullText as w,nodeTypes as h,contentType as A,schemaKind as g}from"@redocly/api-docs";import{toMarkdown as C}from"../../markdown/search/to-markdown.js";const R=10,k=200,I="message payload",P="message headers";function l(u){if(typeof u=="string")return u;const e=Array.isArray(u)?u:[u];return C(e).trim()}function x(u){return u.length>k?`${u.slice(0,k)}\u2026`:u}class L{#a=[];#r;#i;#s;#o;constructor(e,t,n){this.#r=e,this.#i=t,this.#s=n}addItem(e){if(!(!e.link||!e.content))try{switch(e.content.contentType){case A.OVERVIEW:this.#d(e,this.#r);break;case A.GROUP:case A.ITEM:this.#d(e,e.link);break}}catch(t){console.error(`Cannot add item to api-docs search indexer [${e.label}, ${e.link}]:`,t.message)}}#d(e,t){if(!e.content)return;const n=t.startsWith(this.#r+"/")?t.slice(this.#r.length+1):t.split("/").slice(-2).join("/"),r=e.content.children,i=this.#l(r),s=this.#y(r,n),o=e,a={id:t,url:t,title:e.label??"",text:i,path:[],deprecated:e.content.meta?.deprecated,..."httpVerb"in e&&e.httpVerb?{httpMethod:e.httpVerb}:{},..."httpPath"in e&&e.httpPath?{httpPath:e.httpPath}:{},..."badges"in e&&Array.isArray(e.badges)&&e.badges.length?{badges:e.badges}:{},..."isAdditionalOperation"in e&&e.isAdditionalOperation?{isAdditionalOperation:!0}:{},[f]:o[f],...s.length&&{parameters:s}};this.#a.push(a)}#l(e){const t=[],n=r=>{for(const i of r)if(i.nodeType===h.MARKDOC){const s=i;if(this.#h(s.content))continue;const o=w(s.content);o&&t.push(o)}else(i.nodeType===h.CONTAINER||i.nodeType===h.OVERVIEW_SECTION_WRAPPER)&&n(i.children)};return n(e),t.join(" ").trim()}#h(e){let t=!1,n=!1;const r=s=>{s.type==="code"&&s.attributes?.content?t=!0:s.type==="text"&&s.attributes?.content&&(n=!0);for(const o of s.children??[])r(o)},i=Array.isArray(e)?e:[e];for(const s of i)r(s);return t&&!n}#y(e,t){const n={},r=new Set,i=s=>{for(const o of s)if(o.nodeType===h.ITEM){const a=o;if(a[f])continue;this.#m(a,n,t),this.#E(a,n,r,t)}else if(o.nodeType===h.CONTAINER){const a=o;if(a[f])continue;i(a.children)}else o.nodeType===h.OVERVIEW_SECTION_WRAPPER&&i(o.children)};return i(e),Object.values(n)}#m(e,t,n){if(e.parameters?.length)for(const r of e.parameters){if(r[f])continue;const i=this.#A(e.variant),s=this.#O(e.variant),o=r.description?l(r.description):"",a=this.#T(r.schemaId),d={name:r.name,description:o,place:i,mediaType:void 0,type:a,required:r.required??!1,deepLink:this.#c(n,"request",s,void 0,void 0,[r.name]),example:void 0,enum:void 0};t[this.#t(d)]=d}}#t(e){return[...Array.isArray(e.path)?e.path:[],e.name.toString()].join(".")+e.description+e.place}#c(e,t,n,r,i,s){if(!e)return"";const o=s.join("/"),a=[`t=${t}`];return n&&a.push(`in=${n}`),i&&a.push(`c=${i}`),r&&a.push(`ct=${r}`),o&&a.push(`path=${o}`),`/${e}#${e}/${a.join("&")}`.toLowerCase()}#$(e,t){return e?`/${e}#${e}/response&c=${t}`.toLowerCase():""}#S(e,t,n,r){if(!e)return"";let i="messages";t&&(i+=`&m=${t}`),i+=`&t=${n}`;const s=r.join("/");return s&&(i+=`&path=${s}`),`/${e}#${e}/${i}`.toLowerCase()}#O(e){switch(e){case"query":return"query";case"path":return"path";case"headers":return"header";case"cookies":return"cookie";default:return}}#A(e){switch(e){case"query":return"query parameters";case"path":return"path parameters";case"headers":return"header parameters";case"cookies":return"cookie parameters";case"parameters":return"channel parameters";default:return e?`${e} parameters`:"parameters"}}#T(e){const t=this.#i?.[e];if(!t||t.kind!==g.JSON_SCHEMA)return"unknown";const n=t.data,r=this.#u(n);return this.#f(r)}#f(e){return Array.isArray(e.type)?e.type.join(" | "):e.type?e.type:e.properties||e.additionalProperties?"object":e.items?"array":e.enum?"string":"any"}#E(e,t,n,r){const i=this.#P(e);if(e.graphqlSchema&&Array.isArray(e.graphqlSchema)){this.#q(e.graphqlSchema,i,t);return}const s=!!this.#i&&!!this.#s;s&&this.#b(e,i,t,n,r);for(const o of e.responses??[])o[f]||(this.#g(o,t,r),s&&this.#k(o,t,r));if(s)for(const o of e.messages??[])o[f]||this.#I(o,t,r)}#b(e,t,n,r,i){const s=t.startsWith("response");if(e.mediaTypeSchemas){const o=Object.entries(e.mediaTypeSchemas),a=o.find(([,d])=>!d[f]);if(a){const[d,c]=a;c.schemaId&&this.#n(c.schemaId,t,d,o.length>1,s,n,[],r,i)}}else e.schemaId&&this.#n(e.schemaId,t,void 0,!1,s,n,[],r,i)}#g(e,t,n){const r=e.description?l(e.description):"",s=[e.summary?l(e.summary):"",r].filter(Boolean).join(" ").trim();if(!s)return;const o={name:e.code,description:s,place:`response ${e.code}`,mediaType:void 0,type:"unknown",required:!1,deepLink:this.#$(n,e.code),example:void 0,enum:void 0};t[this.#t(o)]=o}#k(e,t,n){const r=`response ${e.code} fields`,i=new Set;if(e.mediaTypeContent){const s=Object.entries(e.mediaTypeContent),o=s.find(([,a])=>!a[f]);if(o){const[a,d]=o;d.schemaId&&this.#n(d.schemaId,r,a,s.length>1,!0,t,[],i,n)}}else e.schemaId&&this.#n(e.schemaId,r,e.mediaType,!1,!0,t,[],i,n)}#I(e,t,n){const r=e.description?l(e.description):"",i=e.summary??"",s={name:e.name,description:(r+i).trim(),place:"message",mediaType:void 0,type:"unknown",required:!1,example:void 0,enum:void 0};t[this.#t(s)]=s,this.#o=e.name;const o=new Set;e.schemaId&&this.#n(e.schemaId,I,e.contentType,!1,!1,t,[],o,n),e.headerSchemaId&&this.#n(e.headerSchemaId,P,void 0,!1,!1,t,[],o,n),this.#o=void 0}#P(e){switch(e.variant){case"body":return"request fields";case"responses":return"response fields";case"graphql-fields":return"fields";case"graphql-args":return"arguments";case"return-type":return"return type fields";default:return e.variant??"fields"}}#q(e,t,n){for(const r of e){const i=r.description?l(r.description):"",s={name:r.name,description:i,place:t,mediaType:void 0,path:[],type:r.type??"unknown",required:!1,example:void 0,enum:void 0};if(n[this.#t(s)]=s,r.args?.length)for(const o of r.args){const a=o.description?l(o.description):"",d={name:o.name,description:a,place:"arguments",mediaType:void 0,path:[r.name],type:o.type??"unknown",required:!1,example:void 0,enum:void 0};n[this.#t(d)]=d}}}#n(e,t,n,r,i,s,o,a,d=""){const c=this.#i?.[e];if(!c||c.kind!==g.JSON_SCHEMA||c[f])return;const p=c.data,y=r?n:void 0;this.#p(p,t,n,y,i,s,o,a,0,d)}#w(e){const t=e.match(/^response (\d+)/);return t?t[1]:void 0}#u(e){if(e.allOf){const t={};for(const n of e.allOf){const r=n.$ref?this.#e(n.$ref):n;if(r){const i=t.properties,s=t.required;Object.assign(t,r),(i||r.properties)&&(t.properties={...i||{},...r.properties||{}}),(s||r.required)&&(t.required=[...s||[],...r.required||[]])}}return delete t.allOf,e.description&&(t.description=e.description),t}if(e.$ref){const t=this.#e(e.$ref);if(t){const n={...t};return e.description&&(n.description=e.description),n}}return e}#e(e){if(!e.startsWith("#/"))return;const t=e.slice(2).split("/");let n=this.#s;for(const r of t){if(n==null||typeof n!="object")return;n=n[r]}return n!=null&&typeof n=="object"?n:void 0}#p(e,t,n,r,i,s,o,a,d,c=""){if(d>R||e[f])return;const p=(y,$,S)=>this.#p(y,t,n,r,i,s,$,a,S,c);if(e.$ref){this.#C(e.$ref,p,o,d,a);return}if(e.allOf){p(this.#R(e,a),o,d);return}if(e.discriminator&&e.properties&&e.oneOf){p({...e,oneOf:void 0,discriminator:void 0},o,d);return}if(e.oneOf||e.anyOf){this.#x(e,p,o,d,a);return}e.properties&&this.#j(e,p,{place:t,mediaType:n,deepLinkContentType:r,isResponse:i,paramsMap:s,path:o,visited:a,depth:d,slug:c}),this.#M(e,p,o,d,a)}#C(e,t,n,r,i){if(i.has(e))return;i.add(e);const s=this.#e(e);s&&t(s,n,r)}#R(e,t){const n={};for(const r of e.allOf??[]){let i=r;if(r.$ref){if(t.has(r.$ref))continue;t.add(r.$ref),i=this.#e(r.$ref)}if(i){const s=n.properties,o=n.required;Object.assign(n,i),(s||i.properties)&&(n.properties={...s||{},...i.properties||{}}),(o||i.required)&&(n.required=[...o||[],...i.required||[]])}}return delete n.allOf,n}#x(e,t,n,r,i){for(const s of e.oneOf||e.anyOf||[]){if(s.$ref){if(i.has(s.$ref))continue;i.add(s.$ref)}const o=s.$ref?this.#e(s.$ref):s;o&&t(o,n,r+1)}}#j(e,t,n){const{isResponse:r,paramsMap:i,path:s,visited:o}=n,a=e.required||[],d=this.#w(n.place),c=r?"response":"request",p=n.place===I?"payload":n.place===P?"headers":void 0,y={requiredList:a,responseCode:d,type:c,asyncSubsection:p,...n};for(const[$,S]of Object.entries(e.properties??{})){if(S[f])continue;const m=this.#D(S,o);if(!m)continue;const q=this.#_(m,o),O=this.#u(m);if(!this.#L(m,O,r))continue;const T=[...s,$],E=this.#V($,m,O,T,y),b=this.#t(E);i[b]==null&&(i[b]=E,q||t(O,T,n.depth+1))}}#D(e,t){if(!e.$ref)return e;if(!t.has(e.$ref))return t.add(e.$ref),this.#e(e.$ref)}#_(e,t){if(!e.allOf)return!1;let n=!1;for(const r of e.allOf)r.$ref?t.has(r.$ref)||(t.add(r.$ref),n=!0):n=!0;return!n}#L(e,t,n){return!(e[f]||t[f]||t.readOnly&&!n||t.writeOnly&&n||e.additionalProperties!==void 0&&!e.properties)}#V(e,t,n,r,i){const s=t.description??n.description,o=s?l(s):"",a=t.example??n.example,d=a!==void 0?x(typeof a=="string"?a:JSON.stringify(a)):void 0,c=Array.isArray(n.enum)&&n.enum.length?n.enum:void 0;return{name:e,description:o,place:i.place,mediaType:i.mediaType,path:i.path,type:this.#f(n),required:i.requiredList.includes(e),deepLink:i.asyncSubsection?this.#S(i.slug,this.#o,i.asyncSubsection,r):this.#c(i.slug,i.type,void 0,i.deepLinkContentType,i.responseCode,r),example:d,enum:c}}#M(e,t,n,r,i){if(!e.items||typeof e.items!="object"||Array.isArray(e.items))return;if(e.items.$ref){if(i.has(e.items.$ref))return;i.add(e.items.$ref)}const s=e.items.$ref?this.#e(e.items.$ref):e.items;s&&t(s,n,r+1)}getResult(){return this.#a}}export{L as ApiDocsSearchIndexer};
@@ -1,2 +1,2 @@
1
- function l(r){return r.replace(/\[([^\]]*<[^>]*>[^\]]*)\]\(([^)]+)\)/g,(s,e,t)=>`[${e.replace(/</g,"&lt;").replace(/>/g,"&gt;")}](${t})`).replace(/({%\s*(\w+)(?:\s+[^%]*)?%})(\s*)({%\s*\/\2\s*%})/g,(s,e,t,n,a)=>`${e}
2
- ${a}`)}export{l as sanitizeMalformedMdContent};
1
+ function c(n){return n.replace(/\[([^\]]*<[^>]*>[^\]]*)\]\(([^)]+)\)/g,(r,t,s)=>`[${t.replace(/(`+)[^`]+\1|[<>]/g,e=>e==="<"?"&lt;":e===">"?"&gt;":e)}](${s})`).replace(/({%\s*(\w+)(?:\s+[^%]*)?%})(\s*)({%\s*\/\2\s*%})/g,(r,t,s,l,e)=>`${t}
2
+ ${e}`)}export{c as sanitizeMalformedMdContent};
@@ -1,6 +1,5 @@
1
1
  import type { ApiFunctionsContext, ApiFunctionsUser } from '@redocly/config';
2
2
  import type { Store } from '../../../store.js';
3
- export declare function shouldHandleMcpAuth(requiresLogin: boolean, rbacConfig?: Record<string, unknown>): boolean;
4
3
  export declare function isMcpContentProtected(store: Store): boolean;
5
4
  export declare function handleMcpAuth(request: Request, context: ApiFunctionsContext): Promise<{
6
5
  isAuthenticated: boolean;
@@ -8,7 +7,7 @@ export declare function handleMcpAuth(request: Request, context: ApiFunctionsCon
8
7
  currentUser?: ApiFunctionsUser;
9
8
  accessToken?: string;
10
9
  }>;
11
- export declare function constructUnauthorizedResponse(baseUrl: string): Response;
10
+ export declare function constructUnauthorizedResponse(baseUrl: string, publicMcpUrl?: string): Response;
12
11
  export declare function constructForbiddenResponse(): Response;
13
12
  export declare function constructInvalidTokenResponse(baseUrl: string): Response;
14
13
  //# sourceMappingURL=auth-handlers.d.ts.map
@@ -1 +1 @@
1
- import{extractTokenFromAuthHeader as p}from"../../../plugins/mcp/utils/jwt.js";import{SUPPORTED_MCP_SCOPES as h}from"../constants.js";import{getUserParamsFromCookies as A}from"../../../web-server/auth.js";import{ServerRoutes as u}from"../../../../constants/api.js";import{DEFAULT_ANONYMOUS_VISITOR_TEAM as d,RBAC_ALL_OTHER_TEAMS as f,RbacFeatures as m}from"../../../../constants/common.js";import{withPathPrefix as T}from"@redocly/theme/core/utils";import{checkTokenAudience as R,isTokenAudienceEnforced as O,toAudienceList as k}from"./token-audience.js";import{reportMcpTokenAudienceMismatch as _}from"./token-audience-telemetry.js";function s(e){return!e||typeof e!="object"||Object.keys(e).length===0?!1:!(e[d]&&e[d]!=="none"||e[f]&&e[f]!=="none")}function C(e,t){if(!t||Object.keys(t).length===0)return e;const r=t.content;if(r&&Object.keys(r).length>0&&Object.values(r).some(s))return!0;const o=t.teamFoldersBaseRoles;if(s(o))return!0;const i=t.features;return s(i?.[m.MCP])?!0:e}function x(e){const t=e.getConfig().access;return C(!!t?.requiresLogin,t?.rbac||{})}async function v(e,t){const r=e.headers.get("Authorization");if(!r)return{isAuthenticated:!1};const o=p(r),i=t?.config?.ssoDirect||{},n=o?await A(i,{authorization:o}):{};if(!(o&&n&&n.isAuthenticated))return{isAuthenticated:!1,isTokenValid:!1};const a=n.aud;if(R(a)==="mismatch"){const c=O();if(_({audience:k(a).join(","),enforced:c}),c)return{isAuthenticated:!1,isTokenValid:!1}}return{isAuthenticated:!0,isTokenValid:!0,currentUser:{teams:n.teams||[],email:n.email||"",claims:n,isAuthenticated:!0,idpAccessToken:n.idpAccessToken||void 0,idpId:n.idpId||void 0},accessToken:o}}function l(e,t){const r=`${e}${u.MCP_OAUTH_PROTECTED_RESOURCE}${T("/mcp")}`,o=`${e}${u.MCP_OAUTH_AUTHORIZATION_SERVER}`,i=t?[`error="${t.code}"`,`error_description="${t.description}"`]:[];return i.push(`resource_metadata="${r}"`,`scope="${h.join(" ")}"`,`authorization_uri="${o}"`),`Bearer ${i.join(", ")}`}function w(e){return new Response(JSON.stringify({error:"unauthorized",message:"Authentication required"}),{status:401,headers:{"Content-Type":"application/json","WWW-Authenticate":l(e),"Access-Control-Allow-Origin":"*"}})}function F(){return new Response(JSON.stringify({error:"forbidden",message:"You do not have access to this MCP server"}),{status:403,headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}})}function H(e){return new Response(JSON.stringify({error:"invalid_token",message:"Invalid or expired token"}),{status:401,headers:{"Content-Type":"application/json","WWW-Authenticate":l(e,{code:"invalid_token",description:"Invalid or expired token"}),"Access-Control-Allow-Origin":"*"}})}export{F as constructForbiddenResponse,H as constructInvalidTokenResponse,w as constructUnauthorizedResponse,v as handleMcpAuth,x as isMcpContentProtected,C as shouldHandleMcpAuth};
1
+ import{extractTokenFromAuthHeader as d}from"../../../plugins/mcp/utils/jwt.js";import{SUPPORTED_MCP_SCOPES as p}from"../constants.js";import{getUserParamsFromCookies as h}from"../../../web-server/auth.js";import{ServerRoutes as c}from"../../../../constants/api.js";import{withPathPrefix as l}from"@redocly/theme/core/utils";import{shouldHandleMcpAuth as f}from"../../../utils/rbac/should-handle-mcp-auth.js";import{checkTokenAudience as m,isTokenAudienceEnforced as A,toAudienceList as T}from"./token-audience.js";import{reportMcpTokenAudienceMismatch as k}from"./token-audience-telemetry.js";function x(t){const e=t.getConfig().access;return f(!!e?.requiresLogin,e?.rbac||{})}async function E(t,e){const o=t.headers.get("Authorization");if(!o)return{isAuthenticated:!1};const i=d(o),r=e?.config?.ssoDirect||{},n=i?await h(r,{authorization:i}):{};if(!(i&&n&&n.isAuthenticated))return{isAuthenticated:!1,isTokenValid:!1};const s=n.aud;if(m(s)==="mismatch"){const a=A();if(k({audience:T(s).join(","),enforced:a}),a)return{isAuthenticated:!1,isTokenValid:!1}}return{isAuthenticated:!0,isTokenValid:!0,currentUser:{teams:n.teams||[],email:n.email||"",claims:n,isAuthenticated:!0,idpAccessToken:n.idpAccessToken||void 0,idpId:n.idpId||void 0},accessToken:i}}function u(t,e){const o=`${t}${c.MCP_OAUTH_PROTECTED_RESOURCE}${l("/mcp")}`,i=`${t}${c.MCP_OAUTH_AUTHORIZATION_SERVER}`,r=e?[`error="${e.code}"`,`error_description="${e.description}"`]:[];return r.push(`resource_metadata="${o}"`,`scope="${p.join(" ")}"`,`authorization_uri="${i}"`),`Bearer ${r.join(", ")}`}function S(t,e){const o=e?`Authentication required. A public endpoint that serves anonymous-accessible content is available at ${e}.`:"Authentication required";return new Response(JSON.stringify({error:"unauthorized",message:o}),{status:401,headers:{"Content-Type":"application/json","WWW-Authenticate":u(t),"Access-Control-Allow-Origin":"*"}})}function w(){return new Response(JSON.stringify({error:"forbidden",message:"You do not have access to this MCP server"}),{status:403,headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}})}function y(t){return new Response(JSON.stringify({error:"invalid_token",message:"Invalid or expired token"}),{status:401,headers:{"Content-Type":"application/json","WWW-Authenticate":u(t,{code:"invalid_token",description:"Invalid or expired token"}),"Access-Control-Allow-Origin":"*"}})}export{w as constructForbiddenResponse,y as constructInvalidTokenResponse,S as constructUnauthorizedResponse,E as handleMcpAuth,x as isMcpContentProtected};
@@ -1,6 +1,6 @@
1
1
  import type { McpToolSchema } from '../../../../types';
2
2
  import type { McpToolCallback, McpToolContext } from '../../types.js';
3
3
  import { type SandboxCapability } from '../capabilities/index.js';
4
- export declare function describeToolsSchema(tools: McpToolSchema[], capabilities: SandboxCapability[]): Record<string, unknown>;
4
+ export declare function describeToolsSchema(tools: McpToolSchema[], capabilities: SandboxCapability[]): McpToolSchema;
5
5
  export declare function describeTools(tools: McpToolSchema[], isToolAvailable: (toolName: string) => Promise<boolean>, capabilities: SandboxCapability[], context: McpToolContext): McpToolCallback;
6
6
  //# sourceMappingURL=describe-tools.d.ts.map
@@ -1 +1 @@
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
+ import{renderDescribeToolsOutput as S}from"../capabilities/index.js";import{McpServerType as y}from"../../constants.js";import{reportMcpToolCalled as v,reportMcpToolError as O}from"../../workers/mcp-tool-telemetry.js";import{DESCRIBE_TOOLS_TOOL_NAME as p}from"../constants.js";import{DESCRIBE_TOOLS_DESCRIPTION as k}from"../prompts.js";import{sanitizeToolName as s}from"../build-tool-type-declarations.js";import{UnknownToolError as w}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{name:p,title:"Describe tools",description:k,schema:{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 P(i,l,r,n){const c=new Map(i.map(t=>[s(t.name),t])),m=new Map(r.map(t=>[s(t.id),t]));return async t=>{const d=performance.now(),f=n.mcpMode??"code";try{const o=[...new Set((t.tools??[]).map(s))],a=o.length===0,u=o.filter(e=>!c.has(e)&&!m.has(e));if(u.length>0){const e=[...c.keys(),...m.keys()];throw new w(`Unknown tool(s): ${u.join(", ")}. Valid tools: ${e.join(", ")}.`)}const g=a?r:r.filter(e=>o.includes(s(e.id))),T=a?i:o.filter(e=>c.has(e)).map(e=>c.get(e)),M=await Promise.all(T.map(e=>l(e.name))),E=T.filter((e,b)=>M[b]),h=S(E,g,n);return v({tool:p,mode:f,serverType:y.Docs,durationMs:Math.round(performance.now()-d),outputLength:h.length}),{content:[{type:"text",text:h}]}}catch(o){const a=o instanceof w;throw O({tool:p,mode:f,serverType:y.Docs,durationMs:Math.round(performance.now()-d),message:o instanceof Error?o.message:String(o),stack:!a&&o instanceof Error&&o.stack||"",errorClass:a?"tool_error":"server_error"}),o}}}export{P as describeTools,x as describeToolsSchema};
@@ -1 +1 @@
1
- import{EXECUTE_TOOL_NAME as e}from"../constants.js";import{buildExecuteDescription as t}from"../prompts.js";const n={title:"Execute code",readOnlyHint:!0,openWorldHint:!1},r={name:e,description:t(!1),annotations:n,schema:{type:"object",required:["code","description"],additionalProperties:!1,properties:{code:{type:"string",description:"JavaScript to run in the sandbox. Return a value to receive it as the result.",minLength:1},description:{type:"string",description:"A short, human-readable summary of what this code does.",minLength:1},sessionHandle:{type:"string",description:"Handle returned by createAnonymousSession. Required when an API needs credentials. This parameter is only offered when createAnonymousSession is available, so pass it whenever you have called that tool.",minLength:1}}}};export{n as EXECUTE_BASE_ANNOTATIONS,r as EXECUTE_TOOL_SCHEMA};
1
+ import{EXECUTE_TOOL_NAME as e}from"../constants.js";import{buildExecuteDescription as t}from"../prompts.js";const n={readOnlyHint:!0,openWorldHint:!1},r={name:e,title:"Execute code",description:t(!1),annotations:n,schema:{type:"object",required:["code","description"],additionalProperties:!1,properties:{code:{type:"string",description:"JavaScript to run in the sandbox. Return a value to receive it as the result.",minLength:1},description:{type:"string",description:"A short, human-readable summary of what this code does.",minLength:1},sessionHandle:{type:"string",description:"Handle returned by createAnonymousSession. Required when an API needs credentials. This parameter is only offered when createAnonymousSession is available, so pass it whenever you have called that tool.",minLength:1}}}};export{n as EXECUTE_BASE_ANNOTATIONS,r as EXECUTE_TOOL_SCHEMA};
@@ -1,2 +1,2 @@
1
- import{MANAGE_API_CREDENTIALS_TOOL_NAME as v}from"../../constants.js";import{buildUpstreamCredentialFallback as y,buildUpstreamCredentialInputRequired as b,schemeLabelOf as P}from"../../gateway-mcp/elicitation.js";import{getCallableApis as m,isFetchCapable as S}from"../../gateway-mcp/fetch-capability.js";import{secretStore as I}from"../../gateway-mcp/secret-store.js";import{MISSING_SESSION_HANDLE_MESSAGE as w,resolveSecretSubject as k}from"../../gateway-mcp/subject.js";import{resolveUpstreamApiAuth as j}from"../../gateway-mcp/upstream-auth-scheme.js";function H(e){return!!e.elicitationEnabled&&S(e)}function U(e){const t=m(e).map(n=>n.relativePath);return{name:v,description:["Get a secure link where the user sets, replaces, or removes the credentials this server sends to a documented API.","Call it when the user wants to change or clear an API key, or when a tool result tells you the stored credentials were rejected.","The link covers every credential the API accepts, so scheme is optional - pass it only when a tool result names the scheme that was rejected.","It returns a link for the user to open - never ask for the secret in the conversation and never pass it as an argument."].join(`
2
- `),annotations:{title:"Manage API credentials",readOnlyHint:!1,openWorldHint:!1},schema:{type:"object",required:["api"],additionalProperties:!1,properties:{api:{type:"string",description:`The API to manage credentials for.${t.length>0?` One of: ${t.join(", ")}.`:""}`,minLength:1,...t.length>0?{enum:t}:{}},scheme:{type:"string",description:"Name of the security scheme to manage the credential for, as named in the API description (a tool result that reports a rejected credential names it). Omit it to let the link cover every scheme the API accepts.",minLength:1},sessionHandle:{type:"string",description:"Handle returned by createAnonymousSession. Required while the caller is anonymous, so the credentials are stored against the same subject the API calls use.",minLength:1}}}}}function _(e,c){return async t=>{const n=k(e.user,typeof t.sessionHandle=="string"?t.sessionHandle:void 0);if(n.kind==="invalid")return l(n.message);if(n.kind==="anonymous")return l(w);const{subject:u}=n,h=typeof t.api=="string"?t.api:"",s=m(e).find(o=>o.relativePath===h||o.name===h);if(!s){const o=m(e).map(g=>g.relativePath).join(", ");return l(`Unknown api "${h}". Credentials can only be managed for the APIs this server can call: ${o||"none"}.`)}const r=await j(e,s.relativePath),a=typeof t.scheme=="string"?t.scheme:void 0,A=Object.keys(r.schemes);if(a&&!r.schemes[a])return l(`Unknown scheme "${a}" for api "${s.relativePath}". The security schemes this server can store credentials for are: ${A.join(", ")}.`);const i=a??r.defaultScheme,p=P(r.schemes,i),d=await c.getKv(),f={subject:u,baseUrl:e.baseUrl??"",target:{api:s.relativePath,name:s.name,scheme:i,...p?{schemeLabel:p}:{}},auth:r.schemes[i],apiAuth:r,kv:d,hasExistingSecret:await I.hasSecret(d,u,s.relativePath,i),reason:{kind:"requested"}};return c.getClientCapabilities()?.elicitation?.url?await b(f):{content:[{type:"text",text:await y(f)}],isError:!1}}}function l(e){return{content:[{type:"text",text:e}],isError:!0}}export{H as isManageApiCredentialsToolAvailable,_ as manageApiCredentials,U as manageApiCredentialsSchema};
1
+ import{MANAGE_API_CREDENTIALS_TOOL_NAME as v}from"../../constants.js";import{buildUpstreamCredentialFallback as y,buildUpstreamCredentialInputRequired as b,schemeLabelOf as P}from"../../gateway-mcp/elicitation.js";import{getCallableApis as m,isFetchCapable as S}from"../../gateway-mcp/fetch-capability.js";import{secretStore as I}from"../../gateway-mcp/secret-store.js";import{MISSING_SESSION_HANDLE_MESSAGE as w,resolveSecretSubject as k}from"../../gateway-mcp/subject.js";import{resolveUpstreamApiAuth as j}from"../../gateway-mcp/upstream-auth-scheme.js";function H(e){return!!e.elicitationEnabled&&S(e)}function U(e){const t=m(e).map(n=>n.relativePath);return{name:v,title:"Manage API credentials",description:["Get a secure link where the user sets, replaces, or removes the credentials this server sends to a documented API.","Call it when the user wants to change or clear an API key, or when a tool result tells you the stored credentials were rejected.","The link covers every credential the API accepts, so scheme is optional - pass it only when a tool result names the scheme that was rejected.","It returns a link for the user to open - never ask for the secret in the conversation and never pass it as an argument."].join(`
2
+ `),annotations:{readOnlyHint:!1,openWorldHint:!1},schema:{type:"object",required:["api"],additionalProperties:!1,properties:{api:{type:"string",description:`The API to manage credentials for.${t.length>0?` One of: ${t.join(", ")}.`:""}`,minLength:1,...t.length>0?{enum:t}:{}},scheme:{type:"string",description:"Name of the security scheme to manage the credential for, as named in the API description (a tool result that reports a rejected credential names it). Omit it to let the link cover every scheme the API accepts.",minLength:1},sessionHandle:{type:"string",description:"Handle returned by createAnonymousSession. Required while the caller is anonymous, so the credentials are stored against the same subject the API calls use.",minLength:1}}}}}function _(e,c){return async t=>{const n=k(e.user,typeof t.sessionHandle=="string"?t.sessionHandle:void 0);if(n.kind==="invalid")return l(n.message);if(n.kind==="anonymous")return l(w);const{subject:u}=n,h=typeof t.api=="string"?t.api:"",s=m(e).find(o=>o.relativePath===h||o.name===h);if(!s){const o=m(e).map(g=>g.relativePath).join(", ");return l(`Unknown api "${h}". Credentials can only be managed for the APIs this server can call: ${o||"none"}.`)}const r=await j(e,s.relativePath),a=typeof t.scheme=="string"?t.scheme:void 0,A=Object.keys(r.schemes);if(a&&!r.schemes[a])return l(`Unknown scheme "${a}" for api "${s.relativePath}". The security schemes this server can store credentials for are: ${A.join(", ")}.`);const i=a??r.defaultScheme,p=P(r.schemes,i),d=await c.getKv(),f={subject:u,baseUrl:e.baseUrl??"",target:{api:s.relativePath,name:s.name,scheme:i,...p?{schemeLabel:p}:{}},auth:r.schemes[i],apiAuth:r,kv:d,hasExistingSecret:await I.hasSecret(d,u,s.relativePath,i),reason:{kind:"requested"}};return c.getClientCapabilities()?.elicitation?.url?await b(f):{content:[{type:"text",text:await y(f)}],isError:!1}}}function l(e){return{content:[{type:"text",text:e}],isError:!0}}export{H as isManageApiCredentialsToolAvailable,_ as manageApiCredentials,U as manageApiCredentialsSchema};
@@ -12,7 +12,6 @@ export declare const MCP_PROTOCOL_VERSION = "2025-11-25";
12
12
  export declare const DEFAULT_CONNECTION_TIMEOUT_MS = 30000;
13
13
  export declare const SSE_KEEPALIVE_INTERVAL_MS = 15000;
14
14
  export declare const MAX_DOCUMENTS_PER_CATEGORY = 3;
15
- export declare const MCP_NOT_ALLOWED_TEMPLATE_ID = "mcp-not-allowed";
16
15
  export declare const DEFAULT_DOCS_MCP_SERVER_NAME = "Docs MCP server";
17
16
  export declare const SUPPORTED_MCP_SCOPES: readonly ['openid', 'profile', 'email', 'offline_access'];
18
17
  export declare const CREATE_ANONYMOUS_SESSION_TOOL_NAME = "createAnonymousSession";
@@ -1 +1 @@
1
- import{LATEST_PROTOCOL_VERSION as o}from"@modelcontextprotocol/server";const E={Docs:"docs"},r={InternalError:-32603,ServerError:-32e3,InvalidRequest:-32600,MethodNotFound:-32601,InvalidParams:-32602},_=o,t=3e4,O=15e3,n=3,s="mcp-not-allowed",S="Docs MCP server",T=["openid","profile","email","offline_access"],c="createAnonymousSession",A="manageApiCredentials";export{c as CREATE_ANONYMOUS_SESSION_TOOL_NAME,t as DEFAULT_CONNECTION_TIMEOUT_MS,S as DEFAULT_DOCS_MCP_SERVER_NAME,A as MANAGE_API_CREDENTIALS_TOOL_NAME,n as MAX_DOCUMENTS_PER_CATEGORY,s as MCP_NOT_ALLOWED_TEMPLATE_ID,_ as MCP_PROTOCOL_VERSION,r as McpErrorCodes,E as McpServerType,O as SSE_KEEPALIVE_INTERVAL_MS,T as SUPPORTED_MCP_SCOPES};
1
+ import{LATEST_PROTOCOL_VERSION as o}from"@modelcontextprotocol/server";const r={Docs:"docs"},E={InternalError:-32603,ServerError:-32e3,InvalidRequest:-32600,MethodNotFound:-32601,InvalidParams:-32602},_=o,t=3e4,s=15e3,O=3,n="Docs MCP server",S=["openid","profile","email","offline_access"],c="createAnonymousSession",A="manageApiCredentials";export{c as CREATE_ANONYMOUS_SESSION_TOOL_NAME,t as DEFAULT_CONNECTION_TIMEOUT_MS,n as DEFAULT_DOCS_MCP_SERVER_NAME,A as MANAGE_API_CREDENTIALS_TOOL_NAME,O as MAX_DOCUMENTS_PER_CATEGORY,_ as MCP_PROTOCOL_VERSION,E as McpErrorCodes,r as McpServerType,s as SSE_KEEPALIVE_INTERVAL_MS,S as SUPPORTED_MCP_SCOPES};
@@ -1 +1 @@
1
- import{shouldHandleMcpAuth as d}from"../auth/auth-handlers.js";import{CREATE_ANONYMOUS_SESSION_TOOL_NAME as m}from"../constants.js";import{GRAPHQL_TYPE_KINDS as o,GRAPHQL_LIST_SCOPES as h}from"./tools/graphql/constants.js";import{OPERATION_METHODS as p}from"./tools/openapi/constants.js";const i={type:"string",nullable:!0},n={limit:{type:"number"},total:{type:"number"},page:{type:"number"},totalPages:{type:"number"}},r={codemodeType:"OpenApiServer[]"},s={codemodeType:"OpenApiSecurityRequirement[]"},l={codemodeType:"Record<string, OpenApiSecurityScheme>"},y={codemodeType:"OpenApiDocument"},f={codemodeType:"OpenApiOperation & { path: string; method: string }"},g={type:"object",additionalProperties:!1,required:[],properties:{filter:{type:"string",description:"Matches part of an API name, or of a description when no name matches",minLength:1,nullable:!0},page:{type:"number",description:"Page number",minimum:1,default:1,nullable:!0},limit:{type:"number",description:"Number of APIs per page. Default is 300",minimum:1,default:300,nullable:!0}}},a={type:"object",required:["name"],additionalProperties:!1,properties:{name:{type:"string",description:"API name, or a unique part of it",minLength:1},version:{type:"string",description:"API version",nullable:!0},filePath:{type:"string",description:"API definition file path",nullable:!0}}},b={type:"object",required:["name","path","method"],additionalProperties:!1,properties:{name:{type:"string",description:"API name, or a unique part of it",minLength:1},path:{type:"string",description:"Endpoint path (e.g. /api/v1/users)",minLength:1},method:{type:"string",description:"HTTP method (GET, POST, PUT, DELETE, etc.)",enum:[...p.map(e=>e.toUpperCase()),...p],minLength:1},version:{type:"string",description:"API version (required when multiple APIs share the same name but differ by version)",nullable:!0},filePath:{type:"string",description:"API definition file path",nullable:!0}}},S={type:"object",required:[],additionalProperties:!1,properties:{}},v={type:"object",required:[],additionalProperties:!1,properties:{}};function I(e){const t=[{name:"list-apis",description:"Lists available APIs with their context and purpose",schema:g,annotations:{title:"List APIs",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{items:{type:"array",items:{type:"object",properties:{name:{type:"string"},description:i,version:i,servers:r,filePath:{type:"string",description:"API definition file path"}}}},...n}}},{name:"get-endpoints",description:"Get all endpoints for a specific API",schema:a,annotations:{title:"Get Endpoints",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{api:{type:"string"},version:{type:"string"},servers:r,endpoints:{type:"array",items:{type:"object",properties:{path:{type:"string"},method:{type:"string"},summary:i,description:i,security:s}}}}}},{name:"get-endpoint-info",description:"Get comprehensive information about specific endpoint including parameters, security, and examples",schema:b,annotations:{title:"Get Endpoint Info",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{api:{type:"string"},version:{type:"string"},servers:r,endpoint:f,globalSecurity:s,securitySchemes:l}}},{name:"get-security-schemes",description:"Get the security schemes for a specific API",schema:a,annotations:{title:"Get Security Schemes",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{name:i,version:i,securitySchemes:l,security:s}}},{name:"get-full-api-description",description:"Get the complete OpenAPI description",schema:a,annotations:{title:"Get Full API Description",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{api:{type:"string"},version:{type:"string"},definition:y}}},q(e.products,e.locales)];return d(e.requiresLogin,e.rbac)&&t.push({name:"whoami",description:"Get information about the currently authenticated user",schema:S,annotations:{title:"Who Am I",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{email:{type:"string"},name:{type:"string"},subject:{type:"string"},clientId:{type:"string"},scope:{type:"string"},issuedAt:{type:"string"},expiresAt:{type:"string"}}}}),e.hasGraphqlApis&&t.push(...T()),e.elicitationEnabled&&t.push({name:m,description:"Create an anonymous session handle for storing upstream API credentials. Pass the returned sessionHandle to execute when an API needs credentials and the caller is not signed in. Treat it as a secret and reuse it across calls.",schema:v,annotations:{title:"Create anonymous session",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},outputSchema:{type:"object",required:["sessionHandle"],properties:{sessionHandle:{type:"string"}}}}),t}const H={type:"object",required:[],additionalProperties:!1,properties:{filter:{type:"string",description:"GraphQL schema name (or part of it)",minLength:1,nullable:!0},page:{type:"number",description:"Page number",minimum:1,default:1,nullable:!0},limit:{type:"number",description:"Number of results per page. Default is 300",minimum:1,default:300,nullable:!0}}},P={type:"object",required:["name"],additionalProperties:!1,properties:{name:{type:"string",description:"GraphQL schema name",minLength:1},kind:{type:"string",description:'Optional: return only a single category of schema members. With scope "fields", restricts to the fields of that parent category ("objects", "interfaces", or "inputs" only).',enum:[...o],nullable:!0},scope:{type:"string",description:'Optional: what each list item is. "members" (default) lists top-level types and operations. "fields" flattens every field of every object/interface/input type into "ParentType.fieldName" entries with their descriptions \u2014 combine with `filter` to search nested fields anywhere in the schema.',enum:[...h],nullable:!0},filter:{type:"string",description:'Optional: only members whose name or description contains this substring, case-insensitive (e.g. "custom tags")',minLength:1,nullable:!0},referencesType:{type:"string",description:'Optional: only members that reference this type directly or transitively (via return, field, argument, union-member or input-field types). Finds cross-cutting members that never name it \u2014 e.g. referencesType "Book" surfaces a search query returning a union that includes Book.',minLength:1,nullable:!0},page:{type:"number",description:"Page number",minimum:1,default:1,nullable:!0},limit:{type:"number",description:"Number of results per page. Default is 300",minimum:1,default:300,nullable:!0}}},A={type:"object",required:["name","type"],additionalProperties:!1,properties:{name:{type:"string",description:"GraphQL schema name",minLength:1},type:{type:"string",description:"Name of the type, operation, or directive",minLength:1},includeReferencedTypes:{type:"boolean",description:"Also include the SDL of every type referenced transitively. Gives the whole subgraph at once, but can be large for highly-connected types.",default:!1,nullable:!0}}},L={type:"object",required:["name"],additionalProperties:!1,properties:{name:{type:"string",description:"GraphQL schema name",minLength:1}}};function T(){return[{name:"list-graphql-apis",description:"Lists available GraphQL schemas with their context and purpose",schema:H,annotations:{title:"List GraphQL APIs",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{items:{type:"array",items:{type:"object",properties:{name:{type:"string"},description:i}}},...n}}},{name:"get-graphql-type-list",description:'List all operations and types in a GraphQL schema as a flat, paginated list. Each item is tagged with its kind (query, object, enum, \u2026) and carries a one-line description; operations also include their return type and argument signature \u2014 use it to decide which members to fetch in detail. Narrow the result with `kind` (one category), `filter` (case-insensitive substring of the member name or description, e.g. "book"), or `referencesType` (only members that use a given type, directly or transitively) to explore a domain cheaply instead of pulling the full schema. To search nested fields \u2014 not just type names \u2014 set scope "fields": it flattens every field of every object/interface/input type into "ParentType.fieldName" entries with their descriptions, so `filter` finds things like an input field whose docstring mentions your keyword.',schema:P,annotations:{title:"Get GraphQL Type List",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{name:{type:"string"},items:{type:"array",items:{type:"object",properties:{kind:{type:"string",enum:o},name:{type:"string"},description:i,type:{type:"string"},args:{type:"string"},directives:{type:"string"}}}},...n}}},{name:"get-graphql-type",description:'Get the SDL for a single GraphQL type, operation (query/mutation/subscription field), or directive. Without includeReferencedTypes, the SDL is followed by a one-level manifest of every directly referenced type\'s field names (e.g. "input ActionOperationInput { addTags, removeTags }"), so you can see what nested types contain before fetching them. Set includeReferencedTypes to instead pull in the full SDL of every type it references transitively \u2014 the whole structural subgraph in one call, ideal for complete exploration of an entity (e.g. Book and all the types it touches). That traversal only follows what the target depends on; to go the other direction and find the members that use a type, call get-graphql-type-list with `referencesType`.',schema:A,annotations:{title:"Get GraphQL Type",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"string"}},{name:"get-graphql-schema",description:"Get the complete GraphQL schema as SDL \u2014 the whole type graph in one call. Returns everything, so it can be very large and token-expensive for big schemas; prefer get-graphql-type-list (filter/paginate) plus targeted get-graphql-type calls, and reach for this only for small schemas or when you truly need the full SDL.",schema:L,annotations:{title:"Get GraphQL Schema",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"string"}}]}function q(e,t){const c=e&&e.length>0,u=t&&t.length>0;return{name:"search",description:"Search across the documentation to fetch relevant content for a given query",schema:{type:"object",required:["query"],additionalProperties:!1,properties:{query:{type:"string",description:"Search query. Should be a single word or that phrase that is presented in a documentation.",minLength:1},...c?{product:{type:"string",description:"Optional product name to filter search results by specific product.",enum:e,nullable:!0}}:{},...u?{locale:{type:"string",description:"Optional locale code to filter search results by documentation language (e.g. 'en', 'ja'). Use the locale matching the language of the user's request. Defaults to the project's default locale.",enum:t,nullable:!0}}:{}}},annotations:{title:"Search Documentation",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},outputSchema:{type:"string"}}}export{I as getDefaultToolSchemas};
1
+ import{shouldHandleMcpAuth as d}from"../../../utils/rbac/should-handle-mcp-auth.js";import{CREATE_ANONYMOUS_SESSION_TOOL_NAME as m}from"../constants.js";import{GRAPHQL_TYPE_KINDS as o,GRAPHQL_LIST_SCOPES as h}from"./tools/graphql/constants.js";import{OPERATION_METHODS as p}from"./tools/openapi/constants.js";const i={type:"string",nullable:!0},n={limit:{type:"number"},total:{type:"number"},page:{type:"number"},totalPages:{type:"number"}},r={codemodeType:"OpenApiServer[]"},s={codemodeType:"OpenApiSecurityRequirement[]"},l={codemodeType:"Record<string, OpenApiSecurityScheme>"},y={codemodeType:"OpenApiDocument"},f={codemodeType:"OpenApiOperation & { path: string; method: string }"},g={type:"object",additionalProperties:!1,required:[],properties:{filter:{type:"string",description:"Matches part of an API name, or of a description when no name matches",minLength:1,nullable:!0},page:{type:"number",description:"Page number",minimum:1,default:1,nullable:!0},limit:{type:"number",description:"Number of APIs per page. Default is 300",minimum:1,default:300,nullable:!0}}},a={type:"object",required:["name"],additionalProperties:!1,properties:{name:{type:"string",description:"API name, or a unique part of it",minLength:1},version:{type:"string",description:"API version",nullable:!0},filePath:{type:"string",description:"API definition file path",nullable:!0}}},b={type:"object",required:["name","path","method"],additionalProperties:!1,properties:{name:{type:"string",description:"API name, or a unique part of it",minLength:1},path:{type:"string",description:"Endpoint path (e.g. /api/v1/users)",minLength:1},method:{type:"string",description:"HTTP method (GET, POST, PUT, DELETE, etc.)",enum:[...p.map(e=>e.toUpperCase()),...p],minLength:1},version:{type:"string",description:"API version (required when multiple APIs share the same name but differ by version)",nullable:!0},filePath:{type:"string",description:"API definition file path",nullable:!0}}},v={type:"object",required:[],additionalProperties:!1,properties:{}},S={type:"object",required:[],additionalProperties:!1,properties:{}};function I(e){const t=[{name:"list-apis",title:"List APIs",description:"Lists available APIs with their context and purpose",schema:g,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{items:{type:"array",items:{type:"object",properties:{name:{type:"string"},description:i,version:i,servers:r,filePath:{type:"string",description:"API definition file path"}}}},...n}}},{name:"get-endpoints",title:"Get endpoints",description:"Get all endpoints for a specific API",schema:a,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{api:{type:"string"},version:{type:"string"},servers:r,endpoints:{type:"array",items:{type:"object",properties:{path:{type:"string"},method:{type:"string"},summary:i,description:i,security:s}}}}}},{name:"get-endpoint-info",title:"Get endpoint info",description:"Get comprehensive information about specific endpoint including parameters, security, and examples",schema:b,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{api:{type:"string"},version:{type:"string"},servers:r,endpoint:f,globalSecurity:s,securitySchemes:l}}},{name:"get-security-schemes",title:"Get security schemes",description:"Get the security schemes for a specific API",schema:a,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{name:i,version:i,securitySchemes:l,security:s}}},{name:"get-full-api-description",title:"Get full API description",description:"Get the complete OpenAPI description",schema:a,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{api:{type:"string"},version:{type:"string"},definition:y}}},G(e.products,e.locales)];return d(e.requiresLogin,e.rbac)&&t.push({name:"whoami",title:"Who am I",description:"Get information about the currently authenticated user",schema:v,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{email:{type:"string"},name:{type:"string"},subject:{type:"string"},clientId:{type:"string"},scope:{type:"string"},issuedAt:{type:"string"},expiresAt:{type:"string"}}}}),e.hasGraphqlApis&&t.push(...q()),e.elicitationEnabled&&t.push({name:m,title:"Create anonymous session",description:"Create an anonymous session handle for storing upstream API credentials. Pass the returned sessionHandle to execute when an API needs credentials and the caller is not signed in. Treat it as a secret and reuse it across calls.",schema:S,annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},outputSchema:{type:"object",required:["sessionHandle"],properties:{sessionHandle:{type:"string"}}}}),t}const H={type:"object",required:[],additionalProperties:!1,properties:{filter:{type:"string",description:"GraphQL schema name (or part of it)",minLength:1,nullable:!0},page:{type:"number",description:"Page number",minimum:1,default:1,nullable:!0},limit:{type:"number",description:"Number of results per page. Default is 300",minimum:1,default:300,nullable:!0}}},P={type:"object",required:["name"],additionalProperties:!1,properties:{name:{type:"string",description:"GraphQL schema name",minLength:1},kind:{type:"string",description:'Optional: return only a single category of schema members. With scope "fields", restricts to the fields of that parent category ("objects", "interfaces", or "inputs" only).',enum:[...o],nullable:!0},scope:{type:"string",description:'Optional: what each list item is. "members" (default) lists top-level types and operations. "fields" flattens every field of every object/interface/input type into "ParentType.fieldName" entries with their descriptions \u2014 combine with `filter` to search nested fields anywhere in the schema.',enum:[...h],nullable:!0},filter:{type:"string",description:'Optional: only members whose name or description contains this substring, case-insensitive (e.g. "custom tags")',minLength:1,nullable:!0},referencesType:{type:"string",description:'Optional: only members that reference this type directly or transitively (via return, field, argument, union-member or input-field types). Finds cross-cutting members that never name it \u2014 e.g. referencesType "Book" surfaces a search query returning a union that includes Book.',minLength:1,nullable:!0},page:{type:"number",description:"Page number",minimum:1,default:1,nullable:!0},limit:{type:"number",description:"Number of results per page. Default is 300",minimum:1,default:300,nullable:!0}}},A={type:"object",required:["name","type"],additionalProperties:!1,properties:{name:{type:"string",description:"GraphQL schema name",minLength:1},type:{type:"string",description:"Name of the type, operation, or directive",minLength:1},includeReferencedTypes:{type:"boolean",description:"Also include the SDL of every type referenced transitively. Gives the whole subgraph at once, but can be large for highly-connected types.",default:!1,nullable:!0}}},L={type:"object",required:["name"],additionalProperties:!1,properties:{name:{type:"string",description:"GraphQL schema name",minLength:1}}};function q(){return[{name:"list-graphql-apis",title:"List GraphQL APIs",description:"Lists available GraphQL schemas with their context and purpose",schema:H,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{items:{type:"array",items:{type:"object",properties:{name:{type:"string"},description:i}}},...n}}},{name:"get-graphql-type-list",title:"Get GraphQL type list",description:'List all operations and types in a GraphQL schema as a flat, paginated list. Each item is tagged with its kind (query, object, enum, \u2026) and carries a one-line description; operations also include their return type and argument signature \u2014 use it to decide which members to fetch in detail. Narrow the result with `kind` (one category), `filter` (case-insensitive substring of the member name or description, e.g. "book"), or `referencesType` (only members that use a given type, directly or transitively) to explore a domain cheaply instead of pulling the full schema. To search nested fields \u2014 not just type names \u2014 set scope "fields": it flattens every field of every object/interface/input type into "ParentType.fieldName" entries with their descriptions, so `filter` finds things like an input field whose docstring mentions your keyword.',schema:P,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{name:{type:"string"},items:{type:"array",items:{type:"object",properties:{kind:{type:"string",enum:o},name:{type:"string"},description:i,type:{type:"string"},args:{type:"string"},directives:{type:"string"}}}},...n}}},{name:"get-graphql-type",title:"Get GraphQL type",description:'Get the SDL for a single GraphQL type, operation (query/mutation/subscription field), or directive. Without includeReferencedTypes, the SDL is followed by a one-level manifest of every directly referenced type\'s field names (e.g. "input ActionOperationInput { addTags, removeTags }"), so you can see what nested types contain before fetching them. Set includeReferencedTypes to instead pull in the full SDL of every type it references transitively \u2014 the whole structural subgraph in one call, ideal for complete exploration of an entity (e.g. Book and all the types it touches). That traversal only follows what the target depends on; to go the other direction and find the members that use a type, call get-graphql-type-list with `referencesType`.',schema:A,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"string"}},{name:"get-graphql-schema",title:"Get GraphQL schema",description:"Get the complete GraphQL schema as SDL \u2014 the whole type graph in one call. Returns everything, so it can be very large and token-expensive for big schemas; prefer get-graphql-type-list (filter/paginate) plus targeted get-graphql-type calls, and reach for this only for small schemas or when you truly need the full SDL.",schema:L,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"string"}}]}function G(e,t){const c=e&&e.length>0,u=t&&t.length>0;return{name:"search",title:"Search documentation",description:"Search across the documentation to fetch relevant content for a given query",schema:{type:"object",required:["query"],additionalProperties:!1,properties:{query:{type:"string",description:"Search query. Should be a single word or that phrase that is presented in a documentation.",minLength:1},...c?{product:{type:"string",description:"Optional product name to filter search results by specific product.",enum:e,nullable:!0}}:{},...u?{locale:{type:"string",description:"Optional locale code to filter search results by documentation language (e.g. 'en', 'ja'). Use the locale matching the language of the user's request. Defaults to the project's default locale.",enum:t,nullable:!0}}:{}}},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},outputSchema:{type:"string"}}}export{I as getDefaultToolSchemas};
@@ -16,6 +16,7 @@ export type CredentialTarget = {
16
16
  export type CredentialElicitationReason = {
17
17
  kind: 'authFailure';
18
18
  status: number;
19
+ detail?: string;
19
20
  otherApiNames?: string[];
20
21
  } | {
21
22
  kind: 'requested';
@@ -43,8 +44,8 @@ export declare function buildUpstreamCredentialInputRequired(request: Credential
43
44
  export declare function buildUpstreamCredentialFallback(request: CredentialElicitationRequest): Promise<string>;
44
45
  /**
45
46
  * Text for an authorization failure we deliberately do NOT prompt for: a stored secret the
46
- * upstream rejected, or a 403/407 that is more likely a permissions problem than a missing key.
47
- * Minting another link here is what produced the credentials-link loop.
47
+ * upstream rejected, a 401 that never carried a stored credential, or a 403/407 that is more
48
+ * likely a permissions problem than a missing key.
48
49
  */
49
50
  export declare function buildCredentialsToolHint(event: UpstreamAuthRequiredEvent, schemeLabel?: string): string;
50
51
  /**
@@ -1,4 +1,5 @@
1
- import{inputRequired as h,inputResponse as T}from"@modelcontextprotocol/server";import{MANAGE_API_CREDENTIALS_TOOL_NAME as l}from"../constants.js";import{secretStore as d,ELICITATION_TTL_MS as w}from"./secret-store.js";import{operationsForScheme as y,orderedSchemesOf as I}from"./upstream-auth-scheme.js";import{ANONYMOUS_SUBJECT_PREFIX as u}from"./subject.js";const m="credentials";function N(e){const t=T(e,m);return t.kind==="elicit"&&(t.action==="decline"||t.action==="cancel")}function j(e,t){return Object.keys(e).length>1?t:void 0}async function x(e){const{url:t}=await p(e);return h({inputRequests:{[m]:h.elicitUrl({message:P(e.target,e.hasExistingSecret),url:t})}})}async function R(e){const{url:t}=await p(e);return S(e,t,A(e.subject))}function U(e,t){const r=e.status===401?`The stored credentials for "${e.name}" were rejected (HTTP 401).`:`The "${e.name}" API rejected the request with HTTP ${e.status}.`,n=t?`api: "${e.api}" and scheme: "${t}"`:`api: "${e.api}"`;return[r,"",`Do not retry the same call. Call \`${l}\` with ${n} to get a link the user has to open in a browser to replace or remove the stored credentials, then retry once they confirm they saved them.`].join(`
2
- `)}function F(e,t){const r=t.map(({name:n,label:o,description:a})=>`- ${n} \u2014 ${o}${a?`: ${a}`:""}`);return[`The "${e.name}" API rejected the request with HTTP ${e.status} and this operation accepts more than one kind of credential:`,"",...r,"","Ask the user which one of these they have, and wait for the answer. Never guess and never reuse a value meant for another kind.",`Then call \`${l}\` with api: "${e.api}" and scheme set to the name they picked to get a link where they enter it.`].join(`
3
- `)}async function p({kv:e,subject:t,baseUrl:r,target:n,auth:o,apiAuth:a,hasExistingSecret:i}){const s=a?await b(e,t,n.api,a):[],c=await d.createElicitation(e,{subject:t,api:n.api,name:n.name,scheme:n.scheme,...n.schemeLabel?{schemeLabel:n.schemeLabel}:{},auth:o,hasExistingSecret:i,...s.length>1?{schemes:s}:{}});return{token:c,url:E(r,c)}}async function b(e,t,r,n){const a=(await d.getSecretsForSubject(e,t))[r]??{};return I(n.schemes).map(i=>{const s=y(n,i),c=n.descriptions?.[i];return{name:i,...c?{label:c}:{},auth:n.schemes[i],hasExistingSecret:!!a[i],...s.length>0?{operations:s}:{}}})}function A(e){return e.startsWith(u)?e.slice(u.length):void 0}function P(e,t){const r=`The link works for ${f()} minutes.`,n=g(e);return t?`Open this page in a browser to replace or remove the stored credentials for ${n}. They go straight to the API and never enter the chat. ${r}`:`${k(n)} needs credentials. Open this page in a browser to paste them - they go straight to the API and never enter the chat. ${r}`}function g({name:e,schemeLabel:t}){return t?`the "${t}" scheme of the "${e}" API`:`the "${e}" API`}function k(e){return e.charAt(0).toUpperCase()+e.slice(1)}function f(){return Math.round(w/6e4)}function E(e,t){if(!e)return`/mcp/credentials?token=${t}`;try{const r=new URL("/mcp/credentials",e);return r.searchParams.set("token",t),r.toString()}catch{return`${e}/mcp/credentials?token=${t}`}}function S({target:e,reason:t,hasExistingSecret:r},n,o){const i=[`**Stop and show the user this link. They have to open it in a browser to ${r?`replace or remove the stored credentials for the "${e.name}" API`:`add credentials for the "${e.name}" API`} - nothing continues until they do.**`,"",n,"",`The link is valid ${f()} minutes and opens a form on this site. Credentials go straight to the API from there: never ask for them in the conversation and never pass them as a tool argument.`,"",t.kind==="authFailure"?`The upstream API "${e.name}" returned ${t.status} and needs credentials.`:r?`Credentials are already stored for the "${e.name}" API; the link lets the user replace or remove them.`:`The "${e.name}" API needs credentials.`,...e.schemeLabel?[`The operation authenticates with its "${e.schemeLabel}" security scheme.`]:[],o?`Once the user confirms they saved them, call this tool again with sessionHandle: "${o}" - the credentials are stored against that handle, applied server-side, and never appear in the conversation.`:"Once the user confirms they saved them, call this tool again - the credentials are applied server-side and never appear in the conversation."],s=t.kind==="authFailure"?t.otherApiNames??[]:[];if(s.length>0){const c=s.map($=>`"${$}"`).join(", ");i.push("",`The remaining APIs (${c}) will prompt for credentials on subsequent calls.`)}return i.join(`
4
- `)}export{m as CREDENTIALS_INPUT_REQUEST_KEY,U as buildCredentialsToolHint,F as buildSchemeChoiceHint,R as buildUpstreamCredentialFallback,x as buildUpstreamCredentialInputRequired,N as isCredentialElicitationDeclined,j as schemeLabelOf};
1
+ import{inputRequired as h,inputResponse as w}from"@modelcontextprotocol/server";import{MANAGE_API_CREDENTIALS_TOOL_NAME as l}from"../constants.js";import{secretStore as d,ELICITATION_TTL_MS as y}from"./secret-store.js";import{operationsForScheme as I,orderedSchemesOf as A}from"./upstream-auth-scheme.js";import{ANONYMOUS_SUBJECT_PREFIX as u}from"./subject.js";const m="credentials";function R(e){const t=w(e,m);return t.kind==="elicit"&&(t.action==="decline"||t.action==="cancel")}function x(e,t){return Object.keys(e).length>1?t:void 0}async function F(e){const{url:t}=await f(e);return h({inputRequests:{[m]:h.elicitUrl({message:b(e),url:t})}})}async function L(e){const{url:t}=await f(e);return C(e,t,P(e.subject))}function U(e,t){const r=p(e);if(e.status===401&&!e.sentScheme){const[a,o]=e.callerHeader?[`Your code set the "${e.callerHeader}" header itself, so the stored credential was not sent.`,"Remove that header from the request and retry. Do not replace the stored credentials."]:["No stored credential was sent with this request, so this result says nothing about the stored credentials.","Do not replace them. Check whether this operation needs a different kind of credential or a different route."];return[`The "${e.name}" API returned ${r}. ${a}`,"",o].join(`
2
+ `)}const n=e.status===401?`The "${e.name}" API rejected the stored ${t?`"${t}" `:""}credential (${r}).`:`The "${e.name}" API rejected the request with ${r}.`,i=t?`api: "${e.api}" and scheme: "${t}"`:`api: "${e.api}"`;return[n,"",`Do not retry the same call. Call \`${l}\` with ${i} to get a link the user has to open in a browser to replace or remove the stored credentials, then retry once they confirm they saved them.`].join(`
3
+ `)}function p({status:e,detail:t}){return t?`HTTP ${e}: ${t}`:`HTTP ${e}`}function H(e,t){const r=t.map(({name:n,label:i,description:a})=>`- ${n} \u2014 ${i}${a?`: ${a}`:""}`);return[`The "${e.name}" API rejected the request with ${p(e)} and this operation accepts more than one kind of credential:`,"",...r,"","Ask the user which one of these they have, and wait for the answer. Never guess and never reuse a value meant for another kind.",`Then call \`${l}\` with api: "${e.api}" and scheme set to the name they picked to get a link where they enter it.`].join(`
4
+ `)}async function f({kv:e,subject:t,baseUrl:r,target:n,auth:i,apiAuth:a,hasExistingSecret:o}){const s=a?await k(e,t,n.api,a):[],c=await d.createElicitation(e,{subject:t,api:n.api,name:n.name,scheme:n.scheme,...n.schemeLabel?{schemeLabel:n.schemeLabel}:{},auth:i,hasExistingSecret:o,...s.length>1?{schemes:s}:{}});return{token:c,url:E(r,c)}}async function k(e,t,r,n){const a=(await d.getSecretsForSubject(e,t))[r]??{};return A(n.schemes).map(o=>{const s=I(n,o),c=n.descriptions?.[o];return{name:o,...c?{label:c}:{},auth:n.schemes[o],hasExistingSecret:!!a[o],...s.length>0?{operations:s}:{}}})}function P(e){return e.startsWith(u)?e.slice(u.length):void 0}function b({target:e,hasExistingSecret:t,reason:r}){const n=`The link works for ${$()} minutes.`,i=S(e),a=r.kind==="authFailure"&&r.detail?` The API answered: ${r.detail}.`:"";return t?`Open this page in a browser to replace or remove the stored credentials for ${i}. They go straight to the API and never enter the chat. ${n}`:`${g(i)} needs credentials.${a} Open this page in a browser to paste them - they go straight to the API and never enter the chat. ${n}`}function S({name:e,schemeLabel:t}){return t?`the "${t}" scheme of the "${e}" API`:`the "${e}" API`}function g(e){return e.charAt(0).toUpperCase()+e.slice(1)}function $(){return Math.round(y/6e4)}function E(e,t){if(!e)return`/mcp/credentials?token=${t}`;try{const r=new URL("/mcp/credentials",e);return r.searchParams.set("token",t),r.toString()}catch{return`${e}/mcp/credentials?token=${t}`}}function C({target:e,reason:t,hasExistingSecret:r},n,i){const o=[`**Stop and show the user this link. They have to open it in a browser to ${r?`replace or remove the stored credentials for the "${e.name}" API`:`add credentials for the "${e.name}" API`} - nothing continues until they do.**`,"",n,"",`The link is valid ${$()} minutes and opens a form on this site. Credentials go straight to the API from there: never ask for them in the conversation and never pass them as a tool argument.`,"",t.kind==="authFailure"?`The upstream API "${e.name}" returned ${t.status}${t.detail?` (${t.detail})`:""} and needs credentials.`:r?`Credentials are already stored for the "${e.name}" API; the link lets the user replace or remove them.`:`The "${e.name}" API needs credentials.`,...e.schemeLabel?[`The operation authenticates with its "${e.schemeLabel}" security scheme.`]:[],i?`Once the user confirms they saved them, call this tool again with sessionHandle: "${i}" - the credentials are stored against that handle, applied server-side, and never appear in the conversation.`:"Once the user confirms they saved them, call this tool again - the credentials are applied server-side and never appear in the conversation."],s=t.kind==="authFailure"?t.otherApiNames??[]:[];if(s.length>0){const c=s.map(T=>`"${T}"`).join(", ");o.push("",`The remaining APIs (${c}) will prompt for credentials on subsequent calls.`)}return o.join(`
5
+ `)}export{m as CREDENTIALS_INPUT_REQUEST_KEY,U as buildCredentialsToolHint,H as buildSchemeChoiceHint,L as buildUpstreamCredentialFallback,F as buildUpstreamCredentialInputRequired,R as isCredentialElicitationDeclined,x as schemeLabelOf};
@@ -1 +1 @@
1
- import{join as e}from"node:path";import{PUBLIC_API_DEFINITIONS_FOLDER as s}from"../../../constants/common.js";import{DEFAULT_ANONYMOUS_VISITOR_TEAM as a}from"../../../../constants/common.js";import{canDownloadApiDefinition as n}from"../../../utils/rbac.js";import{MCP_PROTOCOL_VERSION as p}from"../constants.js";import{isApiIncludedInDocsMcp as c}from"../utils.js";import{isMcpGatewayEnabled as l}from"../utils/mode.js";import{isDefinitionHidden as m,hasGatewayConfig as f}from"../utils/xmcp-utils.js";function u(o){return l()&&!o?.hide&&!o?.docs?.hide}function d(o,i,r){return m(o,"gateway")?!1:n(e(s,i),r,!1,{isAuthenticated:!1,teams:[a]})||f(o)}function D(o,i,r,t){return u(t)&&!o?.["x-mcp"]?.servers?.length&&d(o?.info,i,r)&&c(i,o,t)}function M(o){return{protocolVersion:p,capabilities:{tools:{}},servers:[{url:o}],tools:[],resources:[],prompts:[]}}export{M as buildGatewayXMcp,u as isGatewayCapabilityEnabled,d as isGatewayEligible,D as shouldAdvertiseGatewayXMcp};
1
+ import{join as e}from"node:path";import{PUBLIC_API_DEFINITIONS_FOLDER as s}from"../../../constants/common.js";import{ANONYMOUS_AUTH_DETAILS as n}from"../../../constants/rbac.js";import{canDownloadApiDefinition as a}from"../../../utils/rbac.js";import{MCP_PROTOCOL_VERSION as p}from"../constants.js";import{isApiIncludedInDocsMcp as c}from"../utils.js";import{isMcpGatewayEnabled as l}from"../utils/mode.js";import{isDefinitionHidden as m,hasGatewayConfig as f}from"../utils/xmcp-utils.js";function u(o){return l()&&!o?.hide&&!o?.docs?.hide}function d(o,r,i){return m(o,"gateway")?!1:a(e(s,r),i,!1,n)||f(o)}function _(o,r,i,t){return u(t)&&!o?.["x-mcp"]?.servers?.length&&d(o?.info,r,i)&&c(r,o,t)}function G(o){return{protocolVersion:p,capabilities:{tools:{}},servers:[{url:o}],tools:[],resources:[],prompts:[]}}export{G as buildGatewayXMcp,u as isGatewayCapabilityEnabled,d as isGatewayEligible,_ as shouldAdvertiseGatewayXMcp};
@@ -9,6 +9,7 @@ type GatewayFetchAuditRecord = {
9
9
  path?: string;
10
10
  api?: string;
11
11
  status?: number;
12
+ injectedScheme?: string;
12
13
  requestHeaders?: Record<string, string>;
13
14
  requestBody?: string;
14
15
  responseHeaders?: Record<string, string>;
@@ -19,6 +20,6 @@ type GatewayFetchAuditRecord = {
19
20
  * telemetry event verbatim so a Grafana row joins onto this line; `url_sha256` identifies the
20
21
  * request itself, since the telemetry event's `path` is deliberately normalized.
21
22
  */
22
- export declare function logGatewayFetchAudit({ rawUrl, method, host, outcome, durationMs, injectedSecretValues, path, api, status, requestHeaders, requestBody, responseHeaders, responseBody, }: GatewayFetchAuditRecord): void;
23
+ export declare function logGatewayFetchAudit({ rawUrl, method, host, outcome, durationMs, injectedSecretValues, path, api, status, injectedScheme, requestHeaders, requestBody, responseHeaders, responseBody, }: GatewayFetchAuditRecord): void;
23
24
  export {};
24
25
  //# sourceMappingURL=fetch-audit.d.ts.map
@@ -1 +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
+ import{auditHeaderNames as n,auditInput as g,auditOutputDigest as b,auditSha256 as l,auditUrl as y,logAuditRecord as A}from"../../audit/audit-log.js";function R({rawUrl:e,method:u,host:i,outcome:r,durationMs:o,injectedSecretValues:t,path:_,api:h,status:f,injectedScheme:m,requestHeaders:p,requestBody:d,responseHeaders:c,responseBody:s}){const a=s===void 0?void 0:b(s);A("Record gateway fetch",{url_sha256:l(e),host:i,method:u,path:_,api:h,outcome:r,status:f,injected_scheme:m,duration_ms:o,url:y(e,t),request_header_names:n(p),request_body:d===void 0?void 0:g(d,t),response_header_names:n(c),response_sha256:a?.sha256,response_bytes:a?.bytes})}export{R as logGatewayFetchAudit};
@@ -1 +1 @@
1
- import{Agent as V}from"undici";import{envConfig as X}from"../../../../config/env-config.js";import{logger as $}from"../../../../tools/notifiers/logger.js";import{assertUrlAllowed as x}from"./ssrf-guard.js";import{createSsrfSafeLookup as Y}from"./ssrf-safe-lookup.js";import{SsrfBlockedError as B}from"./ssrf-blocklist.js";import{GatewayFetchBlockedError as G,GatewayFetchSystemError as z,GatewayFetchUserError as M}from"./fetch-errors.js";import{redactHeaders as j,redactSecretsFromString as Q}from"./redact.js";import{normalizeFetchPath as Z}from"./normalize-path.js";import{reportGatewayFetch as N}from"./fetch-telemetry.js";import{logGatewayFetchAudit as P}from"./fetch-audit.js";const W="x-forwarded-for",q=3e4,ee=1024*1024,te=new Set([401,403,407]);let U;function re(){if(!U){const e=X.isDevelopMode;e&&$.warn("Gateway MCP SSRF guard: loopback upstreams are ALLOWED (develop mode only, never in runtime)."),U=new V({connect:{lookup:Y({allowLoopback:e})}})}return U}function ke({allowedHosts:e,apis:t=[],authorization:r,onAuthRequired:o,fetchImpl:a=fetch,dispatcher:c=re(),timeoutMs:f=q,maxResponseBytes:i=ee,maxFetchesPerExec:w,clientIp:l}){const L=[...e,...t.flatMap(n=>n.matchers)],h=t.flatMap(n=>Object.values(n.secrets??{})),k=[r,...h.map(n=>n.value)].filter(n=>!!n),_=h.map(n=>n.header);let C=0;return async n=>{const p=n.method??"GET",v=Date.now();let R,y,b,A={},T,D;try{if(w!==void 0){const S=Math.max(0,w);if(C+=1,C>S)throw new M(`Outbound fetch budget exceeded (max ${S} fetches per execute); split the work across calls`)}const d=x(n.url,L);R=d.hostname,y=Z(d.pathname);const E=t.filter(S=>S.matchers.some(K=>K.test(d.hostname))),s=E.length===1?E[0]:void 0;b=s?.slug;const m=s?ie(s,d.pathname,p):void 0,H=s&&m?fe(s,m):void 0,J=H?s?.secrets?.[H]:void 0;A=he({callerHeaders:n.headers,secret:J,authorization:r,clientIp:l,credentialHeaders:s?.credentialHeaders});const O=new AbortController;D=setTimeout(()=>O.abort(),f);const u=await a(d,{method:p,headers:A,body:n.body,redirect:"manual",signal:O.signal,dispatcher:c});T=u.status,te.has(u.status)&&s&&o&&o({api:s.slug,name:s.name,status:u.status,...H?{scheme:H}:{},...m?.length?{schemes:[...m]}:{}});const F=await ue(u,i);let g=me(u.headers);if(ae(u.status)){g=j(g,_);const{location:S}=g;S&&!ce(S,d,L)&&delete g.location}return N({host:d.hostname,method:p,outcome:"success",durationMs:Date.now()-v,path:y,api:b,status:u.status,bytes:Buffer.byteLength(F,"utf8")}),P({rawUrl:d.href,method:p,host:d.hostname,outcome:"success",durationMs:Date.now()-v,injectedSecretValues:k,path:y,api:b,status:u.status,requestHeaders:A,requestBody:n.body,responseHeaders:g,responseBody:F}),{status:u.status,statusText:u.statusText,ok:u.ok,headers:g,bodyText:F}}catch(d){const{outcome:E,userSafe:s}=ne(d),m=R??(s instanceof G?s.host:void 0)??se(n.url)??"invalid-url";throw N({host:m,method:p,outcome:E,durationMs:Date.now()-v,path:y,api:b,status:T}),P({rawUrl:n.url,method:p,host:m,outcome:E,durationMs:Date.now()-v,injectedSecretValues:k,path:y,api:b,status:T,requestHeaders:A,requestBody:n.body}),s||($.error(`Gateway MCP fetch failed [${p} ${m}]: ${Q(de(d),k)} headers=${JSON.stringify(j(A,_))}`),new z("Upstream fetch failed due to an internal error"))}finally{D!==void 0&&clearTimeout(D)}}}function oe(e){let t=e;for(let r=0;r<5&&t instanceof Error;r++){if(t instanceof M||t instanceof B)return t;t=t.cause}}function ne(e){const t=oe(e);return{outcome:t instanceof G||t instanceof B?"blocked":t?"user_error":"system_error",userSafe:t}}function se(e){try{return new URL(e).hostname||void 0}catch{return}}function ae(e){return e>=300&&e<400}function ce(e,t,r){try{return!!x(new URL(e,t).href,r)}catch{return!1}}function de(e){if(!(e instanceof Error))return String(e);const t=e.cause;return t instanceof Error?`${e.message}: ${t.message}`:e.message}async function ue(e,t){const r=Number(e.headers.get("content-length"));if(Number.isFinite(r)&&r>t)throw I(t,r);if(!e.body)return"";const o=e.body.getReader(),a=new TextDecoder;let c=0,f="";try{for(;;){const{done:i,value:w}=await o.read();if(i)break;if(c+=w.byteLength,c>t)throw I(t,c);f+=a.decode(w,{stream:!0})}return f+a.decode()}finally{await o.cancel().catch(()=>{})}}function I(e,t){return new M(`Response is too large (over ${e} bytes, got ${t}); request less data`)}function ie(e,t,r){const o=e.routes?.find(a=>a.method===r&&a.pattern.test(t));return o?o.schemes:le(e)}function fe(e,t){const r=t.filter(o=>e.secrets?.[o]);return r.length===0?t[0]:r.reduce((o,a)=>{const c=e.secrets?.[o]?.savedAt??0;return(e.secrets?.[a]?.savedAt??0)>c?a:o})}function le(e){const t=Object.keys(e.secrets??{});if(t.length===0)return e.defaultScheme?[e.defaultScheme]:[];const r=(e.schemePriority??[]).filter(o=>t.includes(o));return[...r,...t.filter(o=>!r.includes(o))]}function he({callerHeaders:e,secret:t,authorization:r,clientIp:o,credentialHeaders:a}){const c={},f=new Set,i=t??(r?{header:"Authorization",value:r}:void 0),w=i?new Set((a??[]).map(l=>l.toLowerCase()).filter(l=>l!==i.header.toLowerCase())):new Set;for(const[l,L]of Object.entries(e??{})){const h=l.toLowerCase();h==="host"||h==="content-length"||h!==W&&(w.has(h)||(f.add(h),c[l]=L))}return o&&(c[W]=o),i&&!f.has(i.header.toLowerCase())&&(c[i.header]=i.value),c}function me(e){const t={};return e.forEach((r,o)=>{t[o.toLowerCase()]=r}),t}export{ke as buildFetchBridge,re as getDefaultAgent,ue as readCappedBody};
1
+ import{Agent as Y}from"undici";import{envConfig as z}from"../../../../config/env-config.js";import{logger as B}from"../../../../tools/notifiers/logger.js";import{assertUrlAllowed as G}from"./ssrf-guard.js";import{createSsrfSafeLookup as Q}from"./ssrf-safe-lookup.js";import{SsrfBlockedError as N}from"./ssrf-blocklist.js";import{GatewayFetchBlockedError as P,GatewayFetchSystemError as Z,GatewayFetchUserError as _}from"./fetch-errors.js";import{redactHeaders as W,redactSecretsFromString as q}from"./redact.js";import{extractUpstreamDetail as ee}from"./upstream-detail.js";import{normalizeFetchPath as te}from"./normalize-path.js";import{reportGatewayFetch as I}from"./fetch-telemetry.js";import{logGatewayFetchAudit as J}from"./fetch-audit.js";const K="x-forwarded-for",re=3e4,oe=1024*1024,ne=new Set([401,403,407]);let C;function se(){if(!C){const e=z.isDevelopMode;e&&B.warn("Gateway MCP SSRF guard: loopback upstreams are ALLOWED (develop mode only, never in runtime)."),C=new Y({connect:{lookup:Q({allowLoopback:e})}})}return C}function Fe({allowedHosts:e,apis:t=[],authorization:r,onAuthRequired:o,fetchImpl:c=fetch,dispatcher:a=se(),timeoutMs:m=re,maxResponseBytes:u=oe,maxFetchesPerExec:p,clientIp:k}){const h=[...e,...t.flatMap(n=>n.matchers)],D=t.flatMap(n=>Object.values(n.secrets??{})),l=[r,...D.map(n=>n.value)].filter(n=>!!n),R=D.map(n=>n.header);let O=0;return async n=>{const S=n.method??"GET",T=Date.now();let j,g,b,A={},E,F,M;try{if(p!==void 0){const f=Math.max(0,p);if(O+=1,O>f)throw new _(`Outbound fetch budget exceeded (max ${f} fetches per execute); split the work across calls`)}const d=G(n.url,h);j=d.hostname,g=te(d.pathname);const H=t.filter(f=>f.matchers.some(X=>X.test(d.hostname))),s=H.length===1?H[0]:void 0;b=s?.slug;const w=s?he(s,d.pathname,S):void 0,L=s&&w?me(s,w):void 0,x=L?s?.secrets?.[L]:void 0,U=pe({callerHeaders:n.headers,secret:x,authorization:r,clientIp:k,credentialHeaders:s?.credentialHeaders});A=U.headers,E=x&&!U.callerHeader?L:void 0;const $=new AbortController;M=setTimeout(()=>$.abort(),m);const i=await c(d,{method:S,headers:A,body:n.body,redirect:"manual",signal:$.signal,dispatcher:a});F=i.status;let v="";try{v=await fe(i,u)}finally{if(ne.has(i.status)&&s&&o){const f=ee(v,i.headers.get("content-type"),l);o({api:s.slug,name:s.name,status:i.status,...L?{scheme:L}:{},...w?.length?{schemes:[...w]}:{},...E?{sentScheme:E}:{},...U.callerHeader?{callerHeader:U.callerHeader}:{},...f?{detail:f}:{}})}}let y=Se(i.headers);if(ie(i.status)){y=W(y,R);const{location:f}=y;f&&!ue(f,d,h)&&delete y.location}return I({host:d.hostname,method:S,outcome:"success",durationMs:Date.now()-T,path:g,api:b,status:i.status,bytes:Buffer.byteLength(v,"utf8")}),J({rawUrl:d.href,method:S,host:d.hostname,outcome:"success",durationMs:Date.now()-T,injectedSecretValues:l,path:g,api:b,status:i.status,injectedScheme:E,requestHeaders:A,requestBody:n.body,responseHeaders:y,responseBody:v}),{status:i.status,statusText:i.statusText,ok:i.ok,headers:y,bodyText:v}}catch(d){const{outcome:H,userSafe:s}=ce(d),w=j??(s instanceof P?s.host:void 0)??de(n.url)??"invalid-url";throw I({host:w,method:S,outcome:H,durationMs:Date.now()-T,path:g,api:b,status:F}),J({rawUrl:n.url,method:S,host:w,outcome:H,durationMs:Date.now()-T,injectedSecretValues:l,path:g,api:b,status:F,injectedScheme:E,requestHeaders:A,requestBody:n.body}),s||(B.error(`Gateway MCP fetch failed [${S} ${w}]: ${q(le(d),l)} headers=${JSON.stringify(W(A,R))}`),new Z("Upstream fetch failed due to an internal error"))}finally{M!==void 0&&clearTimeout(M)}}}function ae(e){let t=e;for(let r=0;r<5&&t instanceof Error;r++){if(t instanceof _||t instanceof N)return t;t=t.cause}}function ce(e){const t=ae(e);return{outcome:t instanceof P||t instanceof N?"blocked":t?"user_error":"system_error",userSafe:t}}function de(e){try{return new URL(e).hostname||void 0}catch{return}}function ie(e){return e>=300&&e<400}function ue(e,t,r){try{return!!G(new URL(e,t).href,r)}catch{return!1}}function le(e){if(!(e instanceof Error))return String(e);const t=e.cause;return t instanceof Error?`${e.message}: ${t.message}`:e.message}async function fe(e,t){const r=Number(e.headers.get("content-length"));if(Number.isFinite(r)&&r>t)throw V(t,r);if(!e.body)return"";const o=e.body.getReader(),c=new TextDecoder;let a=0,m="";try{for(;;){const{done:u,value:p}=await o.read();if(u)break;if(a+=p.byteLength,a>t)throw V(t,a);m+=c.decode(p,{stream:!0})}return m+c.decode()}finally{await o.cancel().catch(()=>{})}}function V(e,t){return new _(`Response is too large (over ${e} bytes, got ${t}); request less data`)}function he(e,t,r){const o=e.routes?.find(c=>c.method===r&&c.pattern.test(t));return o?o.schemes:we(e)}function me(e,t){const r=t.filter(o=>e.secrets?.[o]);return r.length===0?t[0]:r.reduce((o,c)=>{const a=e.secrets?.[o]?.savedAt??0;return(e.secrets?.[c]?.savedAt??0)>a?c:o})}function we(e){const t=Object.keys(e.secrets??{});if(t.length===0)return e.defaultScheme?[e.defaultScheme]:[];const r=(e.schemePriority??[]).filter(o=>t.includes(o));return[...r,...t.filter(o=>!r.includes(o))]}function pe({callerHeaders:e,secret:t,authorization:r,clientIp:o,credentialHeaders:c}){const a={},m=new Set,u=t??(r?{header:"Authorization",value:r}:void 0),p=u?new Set((c??[]).map(h=>h.toLowerCase()).filter(h=>h!==u.header.toLowerCase())):new Set;for(const[h,D]of Object.entries(e??{})){const l=h.toLowerCase();l==="host"||l==="content-length"||l!==K&&(p.has(l)||(m.add(l),a[h]=D))}if(o&&(a[K]=o),!u)return{headers:a};const k=u.header.toLowerCase();return m.has(k)?{headers:a,callerHeader:k}:(a[u.header]=u.value,{headers:a})}function Se(e){const t={};return e.forEach((r,o)=>{t[o.toLowerCase()]=r}),t}export{Fe as buildFetchBridge,se as getDefaultAgent,fe as readCappedBody};
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The upstream's own explanation of an auth failure: `title` and `detail` of a `problem+json`
3
+ * body, a `message`/`error` string of a plain JSON body, or a short text body. Whitespace is
4
+ * collapsed, injected secrets are redacted, and the result is capped so it fits in a hint.
5
+ */
6
+ export declare function extractUpstreamDetail(bodyText: string, contentType: string | null, injectedSecretValues: readonly string[]): string | undefined;
7
+ //# sourceMappingURL=upstream-detail.d.ts.map
@@ -0,0 +1 @@
1
+ import{redactSecretsFromString as c}from"./redact.js";const o=200;function l(t,e,r){const i=f(e)?u(t):t;if(!i)return;const n=c(i.replace(/\s+/g," ").trim(),r);if(n)return n.length>o?`${n.slice(0,o-1)}\u2026`:n}function f(t){return/[/+]json\b/i.test(t??"")}function u(t){let e;try{e=JSON.parse(t)}catch{return}if(typeof e!="object"||e===null||Array.isArray(e))return;const r=[e.title,e.detail].filter(s);return r.length>0?r.join(": "):[e.message,e.error].find(s)}function s(t){return typeof t=="string"&&t.trim()!==""}export{l as extractUpstreamDetail};
@@ -6,6 +6,12 @@ export type UpstreamAuthRequiredEvent = {
6
6
  status: number;
7
7
  scheme?: string;
8
8
  schemes?: string[];
9
+ /** Scheme whose stored credential the bridge actually sent; absent when nothing was injected. */
10
+ sentScheme?: string;
11
+ /** Lower-cased credential header the sandbox code set itself, which suppressed the injection. */
12
+ callerHeader?: string;
13
+ /** The upstream's own explanation of the failure, taken from the response body. */
14
+ detail?: string;
9
15
  };
10
16
  export declare function upstreamAuthKey(event: {
11
17
  api: string;
@@ -1 +1 @@
1
- const i="com.redocly/upstream-auth-required";function u(e){return`${e.api}\0${e.scheme??""}`}function c(e,n){const t=new Map;for(const a of n){const s=u(a),o=t.get(s);(!o||o.status!==401&&a.status===401)&&t.set(s,a)}const r=[...t.values()];return r.length===0?e:{...e,_meta:{...e._meta,[i]:r}}}function p(e){const n=e._meta?.[i];return Array.isArray(n)?n.filter(t=>typeof t?.api=="string"&&typeof t?.name=="string"&&typeof t?.status=="number").map(t=>{const r=Array.isArray(t.schemes)?t.schemes.filter(o=>typeof o=="string"):[],{schemes:a,...s}=t;return r.length>0?{...s,schemes:r}:s}):[]}function m(e){if(!e._meta||!(i in e._meta))return e;const{[i]:n,...t}=e._meta,r={...e};return Object.keys(t).length>0?r._meta=t:delete r._meta,r}export{i as UPSTREAM_AUTH_REQUIRED_META,p as readAuthRequiredMeta,m as stripAuthRequiredMeta,u as upstreamAuthKey,c as withAuthRequiredMeta};
1
+ const i="com.redocly/upstream-auth-required";function m(e){return`${e.api}\0${e.scheme??""}`}function h(e,n){const t=new Map;for(const o of n){const s=m(o),a=t.get(s);(!a||u(o)>u(a))&&t.set(s,o)}const r=[...t.values()];return r.length===0?e:{...e,_meta:{...e._meta,[i]:r}}}function u(e){return e.status!==401?0:e.sentScheme?2:1}function y(e){const n=e._meta?.[i];return Array.isArray(n)?n.filter(t=>typeof t?.api=="string"&&typeof t?.name=="string"&&typeof t?.status=="number").map(t=>{const r=Array.isArray(t.schemes)?t.schemes.filter(f=>typeof f=="string"):[],{schemes:o,sentScheme:s,callerHeader:a,detail:c,...p}=t;return{...p,...r.length>0?{schemes:r}:{},...typeof s=="string"?{sentScheme:s}:{},...typeof a=="string"?{callerHeader:a}:{},...typeof c=="string"?{detail:c}:{}}}):[]}function d(e){if(!e._meta||!(i in e._meta))return e;const{[i]:n,...t}=e._meta,r={...e};return Object.keys(t).length>0?r._meta=t:delete r._meta,r}export{i as UPSTREAM_AUTH_REQUIRED_META,y as readAuthRequiredMeta,d as stripAuthRequiredMeta,m as upstreamAuthKey,h as withAuthRequiredMeta};