@redocly/realm 0.135.0 → 0.136.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/cli/build/analytics/collect-analytics.js +1 -1
  3. package/dist/cli/build/analytics/collectors/get-skills-usage.d.ts +3 -0
  4. package/dist/cli/build/analytics/collectors/get-skills-usage.js +1 -0
  5. package/dist/client/templates/openapi-docs/helpers.js +1 -1
  6. package/dist/constants/common.d.ts +8 -0
  7. package/dist/constants/common.js +1 -1
  8. package/dist/server/config/env-config.d.ts +2 -0
  9. package/dist/server/config/env-schema.d.ts +5 -0
  10. package/dist/server/config/env-schemas/feature-flags.d.ts +4 -0
  11. package/dist/server/config/env-schemas/feature-flags.js +1 -1
  12. package/dist/server/constants/plugins/search.d.ts +1 -2
  13. package/dist/server/constants/plugins/search.js +1 -1
  14. package/dist/server/esbuild/plugins/on-rebuild.js +1 -1
  15. package/dist/server/fs/fs.js +1 -1
  16. package/dist/server/plugins/catalog-entities/database/repositories/entities/entities-write-repository.js +1 -1
  17. package/dist/server/plugins/catalog-entities/extensions/extractors/api-description/arazzo-entities-extractor.js +1 -1
  18. package/dist/server/plugins/catalog-entities/extensions/extractors/api-description/asyncapi-entities-extractor.js +1 -1
  19. package/dist/server/plugins/catalog-entities/extensions/extractors/api-description/graphql-entities-extractor.js +2 -2
  20. package/dist/server/plugins/catalog-entities/extensions/extractors/api-description/openapi-entities-extractor.js +1 -1
  21. package/dist/server/plugins/config-parser/loaders/redocly-config-loader.js +1 -1
  22. package/dist/server/plugins/lifecycle.js +2 -2
  23. package/dist/server/plugins/mcp/{docs-mcp/codemode → codemode}/build-tool-type-declarations.d.ts +1 -1
  24. package/dist/server/plugins/mcp/codemode/capabilities/fetch/index.d.ts +4 -0
  25. package/dist/server/plugins/mcp/codemode/capabilities/fetch/index.js +4 -0
  26. package/dist/server/plugins/mcp/codemode/capabilities/index.d.ts +3 -0
  27. package/dist/server/plugins/mcp/codemode/capabilities/index.js +1 -0
  28. package/dist/server/plugins/mcp/codemode/capabilities/registry.d.ts +4 -0
  29. package/dist/server/plugins/mcp/codemode/capabilities/registry.js +1 -0
  30. package/dist/server/plugins/mcp/codemode/capabilities/types.d.ts +13 -0
  31. package/dist/server/plugins/mcp/codemode/capabilities/utils.d.ts +13 -0
  32. package/dist/server/plugins/mcp/codemode/capabilities/utils.js +7 -0
  33. package/dist/server/plugins/mcp/codemode/constants.d.ts +5 -0
  34. package/dist/server/plugins/mcp/codemode/constants.js +1 -0
  35. package/dist/server/plugins/mcp/codemode/invoke-tool.d.ts +4 -0
  36. package/dist/server/plugins/mcp/codemode/invoke-tool.js +1 -0
  37. package/dist/server/plugins/mcp/codemode/prompts.d.ts +6 -0
  38. package/dist/server/plugins/mcp/codemode/prompts.js +4 -0
  39. package/dist/server/plugins/mcp/codemode/sandbox/sandbox-polyfills.d.ts +3 -0
  40. package/dist/server/plugins/mcp/codemode/sandbox/sandbox-polyfills.js +100 -0
  41. package/dist/server/plugins/mcp/{docs-mcp/codemode → codemode/sandbox}/sandbox.d.ts +1 -1
  42. package/dist/server/plugins/mcp/codemode/sandbox/sandbox.js +15 -0
  43. package/dist/server/plugins/mcp/codemode/tools/describe-tools.d.ts +7 -0
  44. package/dist/server/plugins/mcp/codemode/tools/describe-tools.js +1 -0
  45. package/dist/server/plugins/mcp/codemode/tools/execute-schema.d.ts +5 -0
  46. package/dist/server/plugins/mcp/codemode/tools/execute-schema.js +1 -0
  47. package/dist/server/plugins/mcp/codemode/tools/execute.js +1 -0
  48. package/dist/server/plugins/mcp/codemode/types.d.ts +24 -0
  49. package/dist/server/plugins/mcp/codemode/types.js +0 -0
  50. package/dist/server/plugins/mcp/constants.d.ts +2 -0
  51. package/dist/server/plugins/mcp/constants.js +1 -1
  52. package/dist/server/plugins/mcp/docs-mcp/tool-schemas.d.ts +0 -4
  53. package/dist/server/plugins/mcp/docs-mcp/tool-schemas.js +1 -1
  54. package/dist/server/plugins/mcp/docs-mcp/tools/openapi/list-apis.js +1 -1
  55. package/dist/server/plugins/mcp/docs-mcp/utils.js +1 -1
  56. package/dist/server/plugins/mcp/gateway-mcp/eligibility.d.ts +30 -0
  57. package/dist/server/plugins/mcp/gateway-mcp/eligibility.js +1 -0
  58. package/dist/server/plugins/mcp/gateway-mcp/fetch/allowed-hosts.d.ts +13 -0
  59. package/dist/server/plugins/mcp/gateway-mcp/fetch/allowed-hosts.js +1 -0
  60. package/dist/server/plugins/mcp/gateway-mcp/fetch/fetch-bridge.d.ts +12 -0
  61. package/dist/server/plugins/mcp/gateway-mcp/fetch/fetch-bridge.js +1 -0
  62. package/dist/server/plugins/mcp/gateway-mcp/fetch/ssrf-guard.d.ts +9 -0
  63. package/dist/server/plugins/mcp/gateway-mcp/fetch/ssrf-guard.js +1 -0
  64. package/dist/server/plugins/mcp/gateway-mcp/fetch-capability.d.ts +6 -0
  65. package/dist/server/plugins/mcp/gateway-mcp/fetch-capability.js +1 -0
  66. package/dist/server/plugins/mcp/handlers/docs-mcp-handler.js +1 -1
  67. package/dist/server/plugins/mcp/handlers/errors.d.ts +3 -21
  68. package/dist/server/plugins/mcp/handlers/errors.js +1 -1
  69. package/dist/server/plugins/mcp/handlers/handle-mcp-request.js +1 -1
  70. package/dist/server/plugins/mcp/index.js +1 -1
  71. package/dist/server/plugins/mcp/servers/docs-server.d.ts +5 -10
  72. package/dist/server/plugins/mcp/servers/docs-server.js +1 -3
  73. package/dist/server/plugins/mcp/servers/mcp-server.d.ts +18 -0
  74. package/dist/server/plugins/mcp/servers/mcp-server.js +1 -0
  75. package/dist/server/plugins/mcp/types.d.ts +23 -8
  76. package/dist/server/plugins/mcp/utils/mode.d.ts +7 -0
  77. package/dist/server/plugins/mcp/utils/mode.js +1 -0
  78. package/dist/server/plugins/mcp/utils/xmcp-utils.d.ts +12 -60
  79. package/dist/server/plugins/mcp/utils/xmcp-utils.js +1 -1
  80. package/dist/server/plugins/mcp/utils.d.ts +8 -0
  81. package/dist/server/plugins/mcp/utils.js +1 -1
  82. package/dist/server/plugins/mcp/workers/execute-mcp-tool.js +1 -1
  83. package/dist/server/plugins/mcp/workers/mcp-tool-telemetry.d.ts +15 -3
  84. package/dist/server/plugins/mcp/workers/mcp-tool-telemetry.js +1 -1
  85. package/dist/server/plugins/openapi-docs/index.js +1 -1
  86. package/dist/server/plugins/search/engines/search-engine.d.ts +2 -1
  87. package/dist/server/plugins/search/index.js +1 -1
  88. package/dist/server/plugins/skills/discover-skills.d.ts +17 -0
  89. package/dist/server/plugins/skills/discover-skills.js +1 -0
  90. package/dist/server/plugins/skills/index.d.ts +3 -0
  91. package/dist/server/plugins/skills/index.js +1 -0
  92. package/dist/server/plugins/skills/utils/parse-skill.d.ts +18 -0
  93. package/dist/server/plugins/skills/utils/parse-skill.js +1 -0
  94. package/dist/server/plugins/sso/index.js +1 -1
  95. package/dist/server/store.d.ts +4 -0
  96. package/dist/server/store.js +1 -1
  97. package/dist/server/types/plugins/common.d.ts +3 -0
  98. package/dist/server/types/plugins/search.d.ts +0 -1
  99. package/dist/server/types/plugins/skills.d.ts +16 -0
  100. package/dist/server/types/plugins/skills.js +0 -0
  101. package/dist/server/utils/cookie.d.ts +3 -0
  102. package/dist/server/utils/cookie.js +1 -1
  103. package/dist/server/utils/skills/can-access-skill.d.ts +9 -0
  104. package/dist/server/utils/skills/can-access-skill.js +1 -0
  105. package/dist/server/utils/skills/get-accessible-skills.d.ts +5 -0
  106. package/dist/server/utils/skills/get-accessible-skills.js +1 -0
  107. package/dist/server/utils/skills/get-skill-url.d.ts +2 -0
  108. package/dist/server/utils/skills/get-skill-url.js +1 -0
  109. package/dist/server/web-server/auth.js +4 -4
  110. package/dist/server/web-server/middleware/corsMiddleware.d.ts +1 -0
  111. package/dist/server/web-server/middleware/corsMiddleware.js +1 -1
  112. package/dist/server/web-server/middleware/ensureSearchData.js +1 -1
  113. package/dist/server/web-server/routes/auth.js +1 -1
  114. package/dist/server/web-server/routes/catalog/helpers/create-entity-schema.d.ts +305 -256
  115. package/dist/server/web-server/routes/catalog/helpers/create-entity-update-schema.d.ts +46 -39
  116. package/dist/server/web-server/routes/health.js +1 -1
  117. package/dist/server/web-server/routes/index.js +1 -1
  118. package/dist/server/web-server/routes/mcp-routes/a2a.d.ts +10 -0
  119. package/dist/server/web-server/routes/mcp-routes/a2a.js +1 -0
  120. package/dist/server/web-server/routes/mcp-routes/agent-card.d.ts +4 -0
  121. package/dist/server/web-server/routes/mcp-routes/agent-card.js +1 -0
  122. package/dist/server/web-server/routes/mcp-routes/constants.d.ts +14 -0
  123. package/dist/server/web-server/routes/mcp-routes/constants.js +1 -0
  124. package/dist/server/web-server/routes/mcp-routes/mcp-routes.d.ts +1 -1
  125. package/dist/server/web-server/routes/mcp-routes/mcp-routes.js +1 -1
  126. package/dist/server/web-server/routes/mcp-routes/mcp-server-card.d.ts +4 -0
  127. package/dist/server/web-server/routes/mcp-routes/mcp-server-card.js +1 -0
  128. package/dist/server/web-server/routes/mcp-routes/types.d.ts +34 -0
  129. package/dist/server/web-server/routes/mcp-routes/types.js +0 -0
  130. package/dist/server/web-server/routes/mcp-routes/utils/build-agent-card.d.ts +10 -0
  131. package/dist/server/web-server/routes/mcp-routes/utils/build-agent-card.js +1 -0
  132. package/dist/server/web-server/routes/page-data.js +1 -1
  133. package/dist/server/web-server/routes/skills/build-skills-index.d.ts +4 -0
  134. package/dist/server/web-server/routes/skills/build-skills-index.js +1 -0
  135. package/dist/server/web-server/routes/skills/constants.d.ts +4 -0
  136. package/dist/server/web-server/routes/skills/constants.js +1 -0
  137. package/dist/server/web-server/routes/skills/skill-markdown.d.ts +4 -0
  138. package/dist/server/web-server/routes/skills/skill-markdown.js +1 -0
  139. package/dist/server/web-server/routes/skills/skills-index.d.ts +4 -0
  140. package/dist/server/web-server/routes/skills/skills-index.js +1 -0
  141. package/dist/server/web-server/routes/skills/skills-routes.d.ts +4 -0
  142. package/dist/server/web-server/routes/skills/skills-routes.js +1 -0
  143. package/dist/server/web-server/routes/skills/types.d.ts +12 -0
  144. package/dist/server/web-server/routes/skills/types.js +0 -0
  145. package/dist/server/web-server/utils/resolve-public-base-url.d.ts +4 -0
  146. package/dist/server/web-server/utils/resolve-public-base-url.js +1 -0
  147. package/dist/server/web-server/utils.js +1 -1
  148. package/dist/server/workers/api-routes-worker-pool.js +1 -1
  149. package/dist/server/workers/mcp-tool-worker-pool.js +1 -1
  150. package/dist/server/workers/scorecards-worker-pool.js +1 -1
  151. package/dist/server/workers/worker-pool.d.ts +1 -0
  152. package/dist/server/workers/worker-pool.js +1 -1
  153. package/package.json +9 -9
  154. package/dist/server/plugins/mcp/docs-mcp/codemode/prompts.d.ts +0 -5
  155. package/dist/server/plugins/mcp/docs-mcp/codemode/prompts.js +0 -4
  156. package/dist/server/plugins/mcp/docs-mcp/codemode/sandbox.js +0 -7
  157. package/dist/server/plugins/mcp/docs-mcp/codemode/types.d.ts +0 -9
  158. package/dist/server/plugins/mcp/docs-mcp/tools/execute.js +0 -1
  159. package/dist/server/plugins/mcp/mode.d.ts +0 -9
  160. package/dist/server/plugins/mcp/mode.js +0 -1
  161. /package/dist/server/plugins/mcp/{docs-mcp/codemode → codemode}/build-tool-type-declarations.js +0 -0
  162. /package/dist/server/plugins/mcp/{docs-mcp/codemode → codemode/capabilities}/types.js +0 -0
  163. /package/dist/server/plugins/mcp/{docs-mcp → codemode}/tools/execute.d.ts +0 -0
@@ -1 +1 @@
1
- import{telemetry as c}from"../../../telemetry/index.js";import{McpErrorCodes as o,McpServerType as i}from"../constants.js";import{getMcpMode as d}from"../mode.js";class n extends Error{code;data;requestId;constructor(r,t,s,a){super(t),this.name="McpError",this.code=r,this.data=s,this.requestId=a}createErrorResponse(){const r={jsonrpc:"2.0",error:{code:this.code,message:this.message,...this.data?{data:this.data}:{}},id:this.requestId??null},t=u(this.code);return new Response(JSON.stringify(r),{status:t,headers:{"Content-Type":"application/json"}})}}function u(e){switch(e){case o.InvalidRequest:case o.InvalidParams:return 400;case o.MethodNotFound:return 404;case o.ServerError:return 405;case o.InternalError:default:return 500}}function h(e){return new n(o.ServerError,"Method not allowed",void 0,e).createErrorResponse()}function p(e,r=i.Docs,t){const s=e instanceof Error?e.message:String(e),a=e instanceof Error?e.stack:void 0;return c.sendMcpErrorMessage([{id:r,object:"mcpServer",uri:`urn:redocly:realm:mcp:server:${r}`,server_type:r,message:s,stack:a||"",mode:d()}]),new n(o.InternalError,"Internal server error mcp",s,t).createErrorResponse()}function v(e="Invalid request",r){return new n(o.InvalidRequest,e,void 0,r).createErrorResponse()}function I(e="Invalid parameters",r){return new n(o.InvalidParams,e,void 0,r).createErrorResponse()}async function M(e,r,t){try{return await e()}catch(s){return c.sendMcpErrorMessage([{id:r,object:"mcpServer",uri:`urn:redocly:realm:mcp:server:${r}`,server_type:r,message:s?.message||"",stack:s?.stack||"",mode:d()}]),t&&t(),p(s,r)}}export{n as McpError,p as createInternalServerError,I as createInvalidParamsError,v as createInvalidRequestError,h as createMethodNotAllowedError,M as withErrorHandling};
1
+ import{telemetry as d}from"../../../telemetry/index.js";import{McpErrorCodes as o,McpServerType as s}from"../constants.js";import{getMcpMode as i}from"../utils/mode.js";class n extends Error{code;data;requestId;constructor(e,t,a,c){super(t),this.name="McpError",this.code=e,this.data=a,this.requestId=c}createErrorResponse(){const e={jsonrpc:"2.0",error:{code:this.code,message:this.message,...this.data?{data:this.data}:{}},id:this.requestId??null},t=p(this.code);return new Response(JSON.stringify(e),{status:t,headers:{"Content-Type":"application/json"}})}}function p(r){switch(r){case o.InvalidRequest:case o.InvalidParams:return 400;case o.MethodNotFound:return 404;case o.ServerError:return 405;case o.InternalError:default:return 500}}function v(r){return new n(o.ServerError,"Method not allowed",void 0,r).createErrorResponse()}function u(r,e){const t=r instanceof Error?r.message:String(r),a=r instanceof Error?r.stack:void 0;return d.sendMcpErrorMessage([{id:s.Docs,object:"mcpServer",uri:`urn:redocly:realm:mcp:server:${s.Docs}`,server_type:s.Docs,message:t,stack:a||"",mode:i()}]),new n(o.InternalError,"Internal server error mcp",t,e).createErrorResponse()}function f(r="Invalid request",e){return new n(o.InvalidRequest,r,void 0,e).createErrorResponse()}function h(r="Invalid parameters",e){return new n(o.InvalidParams,r,void 0,e).createErrorResponse()}async function I(r,e){try{return await r()}catch(t){return d.sendMcpErrorMessage([{id:s.Docs,object:"mcpServer",uri:`urn:redocly:realm:mcp:server:${s.Docs}`,server_type:s.Docs,message:t?.message||"",stack:t?.stack||"",mode:i()}]),e&&e(),u(t)}}export{n as McpError,u as createInternalServerError,h as createInvalidParamsError,f as createInvalidRequestError,v as createMethodNotAllowedError,I as withErrorHandling};
@@ -1 +1 @@
1
- import{withPathPrefix as h}from"@redocly/theme/core/utils";import{importApiRoutesHandler as g}from"../../../api-routes/import-api-routes-handlers.js";import{enhanceContext as A}from"../../../api-routes/helpers/enhance-context.js";import{telemetry as T}from"../../../telemetry/index.js";import{KvService as D}from"../../../persistence/kv/services/kv-service.js";import{envConfig as o}from"../../../config/env-config.js";import{acceptsHtml as R}from"../../../utils/ai-agent-detection.js";import{handleErrorPageRender as E}from"../../../web-server/utils.js";import{MCP_NOT_ALLOWED_TEMPLATE_ID as c}from"../constants.js";async function b(n,t,e){const i=t.req.raw;if(i.method==="GET"&&R(i.headers.get("accept"))&&e.templates.has(c))return E(t,e,{slug:n.slug},405,c);const r=t.get("auth"),s=await e.resolveRouteStaticData(n)||{},m=h(n.slug),{requestHandlers:p}=await g(e.serverOutDir),l=p[n.requestHandlerId],u=(await l()).default,d=async()=>await D.getInstance({baseDbDir:e.serverOutDir,sqldRemoteDatabaseUrl:o.SQLD_REMOTE_DATABASE_URL,sqldRemoteDatabaseAuthToken:o.SQLD_REMOTE_DATABASE_AUTH_TOKEN}),f=A({honoCtx:t,ctx:{user:{teams:r.teams,email:r.claims.email,claims:r.claims,idpAccessToken:r.idpAccessToken,idpId:r.claims.idpId,isAuthenticated:r.isAuthenticated},config:e.config,outdir:e.outdir,baseUrl:new URL(t.req.url).origin},telemetry:T,getKv:d}),a=await u(t.req.raw,f,{...s,props:{...s.props,routeSlug:m,outdir:e.outdir}});return a instanceof Response?a:typeof a=="string"?new Response(a,{headers:{"Content-Type":"text/plain"}}):new Response(JSON.stringify(a),{headers:{"Content-Type":"application/json"}})}export{b as handleMcpRequest};
1
+ import{withPathPrefix as f}from"@redocly/theme/core/utils";import{importApiRoutesHandler as g}from"../../../api-routes/import-api-routes-handlers.js";import{enhanceContext as h}from"../../../api-routes/helpers/enhance-context.js";import{telemetry as D}from"../../../telemetry/index.js";import{KvService as A}from"../../../persistence/kv/services/kv-service.js";import{envConfig as o}from"../../../config/env-config.js";import{acceptsHtml as T}from"../../../utils/ai-agent-detection.js";import{handleErrorPageRender as R}from"../../../web-server/utils.js";import{MCP_NOT_ALLOWED_TEMPLATE_ID as E}from"../constants.js";import{resolvePublicBaseUrl as w}from"../../../web-server/utils/resolve-public-base-url.js";async function C(i,t,e){const s=t.req.raw;if(s.method==="GET"&&T(s.headers.get("accept"))&&e.globalData.mcpData?.docs?.enabled)return R(t,e,{slug:i.slug},405,E);const r=t.get("auth"),n=await e.resolveRouteStaticData(i)||{},l=f(i.slug),{requestHandlers:c}=await g(e.serverOutDir),m=c[i.requestHandlerId],p=(await m()).default,u=async()=>await A.getInstance({baseDbDir:e.serverOutDir,sqldRemoteDatabaseUrl:o.SQLD_REMOTE_DATABASE_URL,sqldRemoteDatabaseAuthToken:o.SQLD_REMOTE_DATABASE_AUTH_TOKEN}),d=h({honoCtx:t,ctx:{user:{teams:r.teams,email:r.claims.email,claims:r.claims,idpAccessToken:r.idpAccessToken,idpId:r.claims.idpId,isAuthenticated:r.isAuthenticated},config:e.config,outdir:e.outdir,baseUrl:new URL(t.req.url).origin},telemetry:D,getKv:u}),a=await p(t.req.raw,d,{...n,props:{...n.props,routeSlug:l,outdir:e.outdir,skills:e.config.skills?.hide===!0?[]:e.getSkills(),skillsBaseUrl:w(e,t)}});return a instanceof Response?a:typeof a=="string"?new Response(a,{headers:{"Content-Type":"text/plain"}}):new Response(JSON.stringify(a),{headers:{"Content-Type":"application/json"}})}export{C as handleMcpRequest};
@@ -1 +1 @@
1
- import{fileURLToPath as b}from"node:url";import{dirname as A,join as a}from"node:path";import{logger as g}from"../../tools/notifiers/logger.js";import{reporter as O}from"../../tools/notifiers/reporter.js";import{isDefined as _}from"../../../utils/guards/is-defined.js";import{buildGraphqlDescriptions as w,filterIgnoredApiDescriptions as h,getCleanedUpApiDescriptions as R,isMcpInRedirects as q}from"./utils.js";import{isMcpCodeModeEnabled as L}from"./mode.js";import{getDefaultToolSchemas as j,EXECUTE_TOOL_SCHEMA as N}from"./docs-mcp/tool-schemas.js";import{telemetryTraceStep as G}from"../../../cli/telemetry/helpers/trace-step.js";import{MCP_NOT_ALLOWED_TEMPLATE_ID as H}from"./constants.js";const D="mcp-docs-server-handler",p=A(b(import.meta.url));async function I(){let i=!1;return{id:"mcp",async processContent(e,r){i||await G("build.plugin.mcp",async c=>{const t=await e.loadOpenApiDefinitions(r),d=await e.loadGraphqlDefinitions(r),s=await r.getConfig(),{mcp:n,redirects:v}=s;if(c?.setAttribute("config",JSON.stringify(n||{})),!(n?.hide||n?.docs?.hide)&&(q(v)&&await O.panicOnBuildContentError('The reserved route "/mcp" cannot be used in redirect configuration. Please remove any redirects involving "/mcp" from your redocly.yaml config.'),e.createTemplate(H,"@redocly/theme/layouts/McpNotAllowed"),!n?.docs?.hide)){g.info("Configuring MCP servers...");const M=a(p,"./handlers/docs-mcp-handler.js"),C=R(h(t,n?.docs?.ignore||[])),l=new Map;for(const o of C){const f=o.definition.info?.title||"",u=o.definition.info?.version||"";l.set(`${f.toLowerCase()}@${u}`,{name:f,description:o.definition.info?.description||"",version:u,servers:o.definition.servers||[],relativePath:o.relativePath})}const m=w(h(d,n?.docs?.ignore||[])),P=Object.values(s.products||{}).map(o=>o?.name).filter(_),y=s.l10n?.locales?.map(o=>o.code)??r.fs.localeFolders,E=!!s?.access?.requiresLogin,S=s?.access?.rbac||{},T=j({products:P,locales:y,requiresLogin:E,rbac:S,hasGraphqlApis:m.length>0});e.addMcpTools(a(p,"./docs-mcp/tools/index.js"),T),L()&&e.addMcpTools(a(p,"./docs-mcp/tools/execute.js"),[N]),await U({actions:e,handlerPath:M,apiDescriptionsMap:Object.fromEntries(l),graphqlDescriptions:m,mcpConfig:n}),i=!0}})},async afterRoutesCreated(e,r){const c=await r.getConfig(),{mcp:t}=c;e.setGlobalData({mcpData:{docs:{enabled:!t?.hide&&!t?.docs?.hide,name:t?.docs?.name}}})}}}var K=I;async function U({actions:i,handlerPath:e,apiDescriptionsMap:r,graphqlDescriptions:c,mcpConfig:t}){i.createRequestHandler(D,e),i.addApiRoute({slug:"/mcp",requestHandlerId:D,httpMethod:"all",getStaticData:async()=>({props:{config:{apiDescriptionsMap:r,graphqlDescriptions:c,mcpDocsServerName:t?.docs?.name||"Docs MCP Server"},serverOutDir:i.serverOutDir,tools:i.getMcpTools().map(({importPath:d,...s})=>s)}})}),g.info("Registered Docs MCP Server endpoint at /mcp")}export{D as MCP_DOCS_SERVER_HANDLER_ID,K as default,I as mcpServerPlugin};
1
+ import{fileURLToPath as O}from"node:url";import{dirname as T,join as c}from"node:path";import{logger as g}from"../../tools/notifiers/logger.js";import{reporter as A}from"../../tools/notifiers/reporter.js";import{isDefined as R}from"../../../utils/guards/is-defined.js";import{buildGraphqlDescriptions as w,filterIgnoredApiDescriptions as u,getCleanedUpApiDescriptions as L,isMcpInRedirects as q}from"./utils.js";import{isMcpCodeModeEnabled as G}from"./utils/mode.js";import{getDefaultToolSchemas as N}from"./docs-mcp/tool-schemas.js";import{EXECUTE_TOOL_SCHEMA as j}from"./codemode/tools/execute-schema.js";import{isGatewayCapabilityEnabled as U,isGatewayEligible as H}from"./gateway-mcp/eligibility.js";import{telemetryTraceStep as I}from"../../../cli/telemetry/helpers/trace-step.js";import{DEFAULT_DOCS_MCP_SERVER_NAME as F,MCP_NOT_ALLOWED_TEMPLATE_ID as x}from"./constants.js";const h="mcp-docs-server-handler",V="/mcp",p=T(O(import.meta.url));async function B(){let r=!1;return{id:"mcp",async processContent(i,o){r||await I("build.plugin.mcp",async a=>{const s=await i.loadOpenApiDefinitions(o),l=await i.loadGraphqlDefinitions(o),n=await o.getConfig(),{mcp:t,redirects:D}=n;if(a?.setAttribute("config",JSON.stringify(t||{})),!(t?.hide||t?.docs?.hide)&&(q(D)&&await A.panicOnBuildContentError('The reserved route "/mcp" cannot be used in redirect configuration. Please remove any redirects involving "/mcp" from your redocly.yaml config.'),i.createTemplate(x,"@redocly/theme/layouts/McpNotAllowed"),!t?.docs?.hide)){g.info("Configuring MCP servers...");const C=c(p,"./handlers/docs-mcp-handler.js"),E=L(u(s,t?.docs?.ignore||[])),M=!!n?.access?.requiresLogin,d=n?.access?.rbac||{},v=U(t),m=new Map;for(const e of E){const b=e.definition.info?.title||"",S=e.definition.info?.version||"";m.set(e.relativePath,{name:b,description:e.definition.info?.description||"",version:S,servers:e.definition.servers||[],relativePath:e.relativePath,...v&&H(e.definition.info,e.relativePath,d)?{fetchEligible:!0}:{}})}const f=w(u(l,t?.docs?.ignore||[])),y=Object.values(n.products||{}).map(e=>e?.name).filter(R),P=n.l10n?.locales?.map(e=>e.code)??o.fs.localeFolders,_=N({products:y,locales:P,requiresLogin:M,rbac:d,hasGraphqlApis:f.length>0});i.addMcpTools(c(p,"./docs-mcp/tools/index.js"),_),G()&&i.addMcpTools(c(p,"./codemode/tools/execute.js"),[j]),await J({actions:i,handlerPath:C,apiDescriptionsMap:Object.fromEntries(m),graphqlDescriptions:f,mcpConfig:t}),r=!0}})},async afterRoutesCreated(i,o){const a=await o.getConfig(),{mcp:s}=a;i.setGlobalData({mcpData:{docs:{enabled:!s?.hide&&!s?.docs?.hide,name:s?.docs?.name}}})}}}var te=B;async function J({actions:r,handlerPath:i,apiDescriptionsMap:o,graphqlDescriptions:a,mcpConfig:s}){r.createRequestHandler(h,i),r.addApiRoute({slug:V,requestHandlerId:h,httpMethod:"all",getStaticData:async()=>({props:{config:{apiDescriptionsMap:o,graphqlDescriptions:a,mcpDocsServerName:s?.docs?.name||F},serverOutDir:r.serverOutDir,tools:r.getMcpTools().map(({importPath:l,...n})=>n)}})}),g.info("Registered Docs MCP Server endpoint at /mcp")}export{h as MCP_DOCS_SERVER_HANDLER_ID,te as default,B as mcpServerPlugin};
@@ -1,15 +1,10 @@
1
- import { McpServer } from '@redocly/mcp-typescript-sdk/server/mcp.js';
2
- import { StreamableHTTPServerTransport } from '@redocly/mcp-typescript-sdk/server/streamableHttp.js';
3
1
  import type { McpToolSchema } from '../../../types';
4
- import type { McpToolContext } from '../types.js';
5
- export type DocsMcpServerInstance = {
6
- server: McpServer;
7
- transport: StreamableHTTPServerTransport;
8
- cleanup: () => Promise<void>;
9
- };
10
- export declare function createDocsMcpServer({ name, tools, context }: {
2
+ import type { McpToolContext, SkillMcpResource } from '../types.js';
3
+ import { type McpServerInstance } from './mcp-server.js';
4
+ export declare function createDocsMcpServer({ name, tools, context, skills }: {
11
5
  name: string;
12
6
  tools: McpToolSchema[];
13
7
  context: McpToolContext;
14
- }): Promise<DocsMcpServerInstance>;
8
+ skills?: SkillMcpResource[];
9
+ }): Promise<McpServerInstance>;
15
10
  //# sourceMappingURL=docs-server.d.ts.map
@@ -1,3 +1 @@
1
- import{McpServer as _}from"@redocly/mcp-typescript-sdk/server/mcp.js";import{StreamableHTTPServerTransport as S}from"@redocly/mcp-typescript-sdk/server/streamableHttp.js";import{logger as p}from"../../../tools/notifiers/logger.js";import{mcpToolWorkers as u,MCP_TOOL_WORKER_KEY as b,MCP_TOOL_IS_AVAILABLE_KEY as y}from"../../../workers/mcp-tool-worker-pool.js";import{EXECUTE_TOOL_NAME as d,EXECUTE_TOOL_SCHEMA as f,DESCRIBE_TOOLS_TOOL_NAME as I,CODE_MODE_TOOL_NAMES as C}from"../docs-mcp/tool-schemas.js";import{buildToolTypeDeclarations as M,sanitizeToolName as E}from"../docs-mcp/codemode/build-tool-type-declarations.js";import{CODE_MODE_INSTRUCTIONS as D,DESCRIBE_TOOLS_DESCRIPTION as g,USAGE_NOTES as h}from"../docs-mcp/codemode/prompts.js";import{isMcpCodeModeEnabled as T}from"../mode.js";async function B({name:e,tools:o,context:n}){const a=new _({name:e,version:new Date().toISOString().slice(0,10)},{capabilities:{logging:{}},instructions:T()?D:void 0}),s=new S({sessionIdGenerator:void 0}),c=L(n),l=r=>async(t,m)=>{p.info(`MCP tool called: ${r}`);const O={toolName:r,args:t,context:c,extra:w(m)};return await u.exec(b,[O],{timeout:6e4})},i=async r=>{try{return await u.exec(y,[{toolName:r,context:c}],{timeout:1e4})}catch(t){return p.error(`Failed to check MCP tool availability for "${r}": ${t instanceof Error?t.message:String(t)}`),!1}};if(T()){const r=o.filter(t=>!C.has(t.name));a.tool(d,f.description,f.schema,l(d)),a.tool(I,g,q(r),v(r,i))}else await Promise.all(o.map(async r=>{const t=r.annotations||{title:r.name},m=a.tool(r.name,r.description,r.schema,t,l(r.name));await i(r.name)||m.disable()}));return await a.connect(s),{server:a,transport:s,cleanup:async()=>{s.close()}}}function q(e){const o=[...new Set(e.map(n=>E(n.name)))];return{type:"object",required:[],additionalProperties:!1,properties:{tools:{type:"array",description:"Tool names to describe. Omit to describe every tool.",items:{type:"string",enum:o},nullable:!0}}}}function v(e,o){return async n=>{const a=n.tools,s=a?.length?e.filter(i=>a.includes(E(i.name))):e,c=await Promise.all(s.map(i=>o(i.name))),l=s.filter((i,r)=>c[r]);return{content:[{type:"text",text:`${M(l)}
2
-
3
- ${h}`}]}}}function w(e){return{sessionId:e.sessionId,authInfo:e.authInfo,requestId:e.requestId,requestInfo:e.requestInfo,_meta:e._meta}}function L(e){return{user:e.user,config:e.config,outdir:e.outdir,baseUrl:e.baseUrl,params:e.params,query:e.query,cookies:e.cookies,apiDescriptionsMap:e.apiDescriptionsMap,graphqlDescriptions:e.graphqlDescriptions,products:e.products,accessToken:e.accessToken,metadata:e.metadata}}export{B as createDocsMcpServer};
1
+ import{logger as O}from"../../../tools/notifiers/logger.js";import{mcpToolWorkers as _,MCP_TOOL_IS_AVAILABLE_KEY as u}from"../../../workers/mcp-tool-worker-pool.js";import{EXECUTE_TOOL_SCHEMA as C}from"../codemode/tools/execute-schema.js";import{EXECUTE_TOOL_NAME as p,DESCRIBE_TOOLS_TOOL_NAME as M,CODE_MODE_TOOL_NAMES as b,FETCH_TOOL_NAME as S}from"../codemode/constants.js";import{buildCodeModeInstructions as A,buildExecuteDescription as h,DESCRIBE_TOOLS_DESCRIPTION as x}from"../codemode/prompts.js";import{composeExecuteAnnotations as L,getActiveCapabilities as D}from"../codemode/capabilities/index.js";import{describeToolsSchema as v,describeTools as y}from"../codemode/tools/describe-tools.js";import{isMcpCodeModeEnabled as g}from"../utils/mode.js";import{connectMcpServer as I,createMcpServer as k,createToolDispatch as w,extractSerializableContext as N}from"./mcp-server.js";async function K({name:E,tools:a,context:i,skills:d=[]}){const n=g(),r=n?D(i):[],s=r.some(e=>e.id===S),{server:o,transport:f}=k(E,n?A(s):void 0),c=N(i),m=w(c),l=async e=>{try{return await _.exec(u,[{toolName:e,context:c}],{timeout:1e4})}catch(t){return O.error(`Failed to check MCP tool availability for "${e}": ${t instanceof Error?t.message:String(t)}`),!1}};if(n){const e=a.filter(t=>!b.has(t.name));o.tool(p,h(s),C.schema,L(r),m(p)),o.tool(M,x,v(e,r),y(e,l,r,i))}else await Promise.all(a.map(async e=>{const t=e.annotations||{title:e.name},T=o.tool(e.name,e.description,e.schema,t,m(e.name));await l(e.name)||T.disable()}));for(const e of d)o.registerResource(e.slug,e.uri,{title:e.name,description:e.description,mimeType:"text/markdown"},async()=>({contents:[{uri:e.uri,mimeType:"text/markdown",text:e.content}]}));return I(o,f)}export{K as createDocsMcpServer};
@@ -0,0 +1,18 @@
1
+ import { McpServer } from '@redocly/mcp-typescript-sdk/server/mcp.js';
2
+ import { StreamableHTTPServerTransport } from '@redocly/mcp-typescript-sdk/server/streamableHttp.js';
3
+ import type { ToolCallback } from '@redocly/mcp-typescript-sdk/server/mcp.js';
4
+ import type { McpToolContext } from '../types.js';
5
+ export type McpServerInstance = {
6
+ server: McpServer;
7
+ transport: StreamableHTTPServerTransport;
8
+ cleanup: () => Promise<void>;
9
+ };
10
+ export declare function createMcpServer(name: string, instructions?: string): {
11
+ server: McpServer;
12
+ transport: StreamableHTTPServerTransport;
13
+ };
14
+ export declare function connectMcpServer(server: McpServer, transport: StreamableHTTPServerTransport): Promise<McpServerInstance>;
15
+ /** Build the tool callback that ships a tool call to the worker pool. */
16
+ export declare function createToolDispatch(serializableContext: McpToolContext): (toolName: string) => ToolCallback<Record<string, unknown>>;
17
+ export declare function extractSerializableContext(context: McpToolContext): McpToolContext;
18
+ //# sourceMappingURL=mcp-server.d.ts.map
@@ -0,0 +1 @@
1
+ import{McpServer as i}from"@redocly/mcp-typescript-sdk/server/mcp.js";import{StreamableHTTPServerTransport as t}from"@redocly/mcp-typescript-sdk/server/streamableHttp.js";import{logger as n}from"../../../tools/notifiers/logger.js";import{mcpToolWorkers as c,MCP_TOOL_WORKER_KEY as u}from"../../../workers/mcp-tool-worker-pool.js";const p=6e4;function T(e,r){const o=new i({name:e,version:new Date().toISOString().slice(0,10)},{capabilities:{logging:{}},...r?{instructions:r}:{}}),a=new t({sessionIdGenerator:void 0});return{server:o,transport:a}}async function g(e,r){return await e.connect(r),{server:e,transport:r,cleanup:async()=>{r.close()}}}function S(e){return r=>async(o,a)=>{n.info(`MCP tool called: ${r}`);const s={toolName:r,args:o,context:e,extra:l(a)};return await c.exec(u,[s],{timeout:p})}}function l(e){return{sessionId:e.sessionId,authInfo:e.authInfo,requestId:e.requestId,requestInfo:e.requestInfo,_meta:e._meta}}function q(e){return{user:e.user,config:e.config,outdir:e.outdir,baseUrl:e.baseUrl,params:e.params,query:e.query,cookies:e.cookies,apiDescriptionsMap:e.apiDescriptionsMap,graphqlDescriptions:e.graphqlDescriptions,products:e.products,accessToken:e.accessToken,metadata:e.metadata,upstreamAuthorization:e.upstreamAuthorization}}export{g as connectMcpServer,T as createMcpServer,S as createToolDispatch,q as extractSerializableContext};
@@ -1,10 +1,11 @@
1
- import type { OpenAPIPath } from '@redocly/openapi-docs/src/types';
1
+ import type { OpenAPIOperation, OpenAPIPath } from '@redocly/openapi-docs/src/types';
2
2
  import type { ApiFunctionsContext, ApiFunctionsContextMethods, PageStaticData } from '@redocly/config';
3
3
  import type { CallToolResult, RequestId, RequestInfo, RequestMeta } from '@redocly/mcp-typescript-sdk/types.js';
4
4
  import type { AuthInfo } from '@redocly/mcp-typescript-sdk/server/types.js';
5
5
  import type { OpenAPIServer } from '@redocly/openapi-docs/lib/types/open-api.js';
6
6
  import type { McpErrorCodes, McpServerType } from './constants';
7
7
  import type { McpToolSchema } from '../../types';
8
+ import type { Skill } from '../../types/plugins/skills.js';
8
9
  type ObjectValues<T> = T[keyof T];
9
10
  export type McpServerType = ObjectValues<typeof McpServerType>;
10
11
  export type McpErrorCodeTypes = ObjectValues<typeof McpErrorCodes>;
@@ -14,6 +15,7 @@ export type ApiDescriptionInfo = {
14
15
  description?: string;
15
16
  version?: string;
16
17
  servers?: OpenAPIServer[];
18
+ fetchEligible?: boolean;
17
19
  };
18
20
  export type GraphqlDescriptionInfo = {
19
21
  name: string;
@@ -30,8 +32,17 @@ export type McpDocsStaticData = PageStaticData & {
30
32
  config: McpDocsStaticDataPropsConfig;
31
33
  serverOutDir?: string;
32
34
  tools?: McpToolSchema[];
35
+ skills?: Skill[];
36
+ skillsBaseUrl?: string;
33
37
  };
34
38
  };
39
+ export type SkillMcpResource = {
40
+ slug: string;
41
+ name: string;
42
+ description: string;
43
+ content: string;
44
+ uri: string;
45
+ };
35
46
  export type McpPath = {
36
47
  'x-mcp'?: XMcpConfig;
37
48
  } & OpenAPIPath;
@@ -39,10 +50,13 @@ export type XMcpConfig = {
39
50
  docs?: {
40
51
  hide?: boolean;
41
52
  };
53
+ gateway?: {
54
+ hide?: boolean;
55
+ };
42
56
  };
43
- export type McpEndpoint = {
57
+ export type XMcpSection = keyof XMcpConfig;
58
+ export type McpEndpoint = OpenAPIOperation & {
44
59
  'x-mcp'?: XMcpConfig;
45
- [key: string]: any;
46
60
  };
47
61
  export type McpMode = 'tools' | 'code';
48
62
  export type McpPluginConfig = {
@@ -52,10 +66,10 @@ export type McpPluginConfig = {
52
66
  readonly name: string;
53
67
  readonly ignore: readonly string[];
54
68
  };
55
- };
56
- export type DocsServerDependencies = {
57
- apiDescriptionsMap: Record<string, ApiDescriptionInfo>;
58
- readonly name: string;
69
+ readonly gateway?: {
70
+ readonly hide: boolean;
71
+ readonly allowedHosts?: readonly string[];
72
+ };
59
73
  };
60
74
  export type McpUserInfo = {
61
75
  email?: string;
@@ -66,7 +80,7 @@ export type McpUserInfo = {
66
80
  scope?: string;
67
81
  iat?: number;
68
82
  exp?: number;
69
- [key: string]: any;
83
+ [key: string]: unknown;
70
84
  };
71
85
  export type SemanticSearchResult = {
72
86
  title: string;
@@ -87,6 +101,7 @@ export type McpToolContext = Omit<ApiFunctionsContext, 'telemetry' | 'getKv' | k
87
101
  products?: string[];
88
102
  accessToken?: string;
89
103
  metadata?: Record<string, unknown>;
104
+ upstreamAuthorization?: string;
90
105
  };
91
106
  export type McpToolHandler<TArgs extends Record<string, unknown> = Record<string, unknown>> = (args: TArgs, context: McpToolContext, extra: McpToolExtra) => Promise<McpToolWorkerResponse>;
92
107
  export type McpToolIsAvailable = (context: McpToolContext) => boolean | Promise<boolean>;
@@ -0,0 +1,7 @@
1
+ import type { McpMode } from '../types.js';
2
+ /** Codemode - `execute` sandbox and `describeTools`. */
3
+ export declare function isMcpCodeModeEnabled(): boolean;
4
+ /** The gateway capability — a guarded `fetch` in the `execute` sandbox. Requires codemode to be on. */
5
+ export declare function isMcpGatewayEnabled(): boolean;
6
+ export declare function getMcpMode(): McpMode;
7
+ //# sourceMappingURL=mode.d.ts.map
@@ -0,0 +1 @@
1
+ import{envConfig as e}from"../../../config/env-config.js";function o(){return e.REDOCLY_EXP_MCP_CODE_MODE_ENABLED??!1}function n(){return(e.REDOCLY_EXP_MCP_GATEWAY_SERVER_ENABLED??!1)&&o()}function t(){return o()?"code":"tools"}export{t as getMcpMode,o as isMcpCodeModeEnabled,n as isMcpGatewayEnabled};
@@ -1,62 +1,14 @@
1
1
  import type { OpenAPIDefinition } from '@redocly/openapi-docs';
2
- import type { XMcpConfig, McpEndpoint, McpServerType } from '../types.js';
3
- /**
4
- * Extracts the x-mcp configuration from an MCP endpoint.
5
- *
6
- * This function safely retrieves the x-mcp extension property from an endpoint object.
7
- * The x-mcp property contains server-specific configuration for MCP (Model Context Protocol) endpoints.
8
- *
9
- * @param endpoint - The MCP endpoint object to extract configuration from.
10
- * @returns The x-mcp configuration object or undefined if not present.
11
- *
12
- * @example
13
- * getXMcpConfig({ 'x-mcp': { gateway: { hide: true } } }); // { gateway: { hide: true } }
14
- * getXMcpConfig({}); // undefined
15
- */
16
- export declare function getXMcpConfig(endpoint: McpEndpoint): XMcpConfig | undefined;
17
- /**
18
- * Checks if an endpoint should be hidden for a specific server type.
19
- *
20
- * This function determines whether an MCP endpoint should be hidden based on the
21
- * x-mcp configuration and the target server type (gateway or catalog).
22
- *
23
- * @param endpoint - The MCP endpoint to check.
24
- * @param serverType - The server type to check visibility for ('gateway' or 'catalog').
25
- * @returns `true` if the endpoint should be hidden, `false` otherwise.
26
- *
27
- * @example
28
- * isEndpointHidden({ 'x-mcp': { docs: { hide: false } } }, 'docs'); // false
29
- */
30
- export declare function isEndpointHidden(endpoint: McpEndpoint, serverType: McpServerType): boolean;
31
- /**
32
- * Checks endpoint visibility and removes x-mcp configuration from output.
33
- *
34
- * This function first checks if an endpoint should be hidden for the given server type.
35
- * If the endpoint is accessible, it removes the x-mcp configuration to clean up the output.
36
- * This is typically used to prepare endpoints for public API documentation.
37
- *
38
- * @param endpoint - The MCP endpoint to process.
39
- * @param serverType - The server type to check visibility for.
40
- * @returns `true` if the endpoint is accessible and processed, `false` if hidden.
41
- *
42
- * @example
43
- * checkEndpointAndDeleteXMcp(endpoint, 'docs');
44
- * // Returns true and removes x-mcp from endpoint if accessible
45
- */
46
- export declare function checkEndpointAndDeleteXMcp(endpoint: McpEndpoint, serverType: McpServerType): boolean;
47
- /**
48
- * Checks if an OpenAPI definition should be hidden based on x-mcp configuration.
49
- *
50
- * This function examines the info section of an OpenAPI definition for x-mcp configuration
51
- * and determines if the definition should be hidden for the specified server type.
52
- * If no server type is provided, it checks if the definition is hidden for any server type.
53
- *
54
- * @param info - The OpenAPI info object to check.
55
- * @param serverType - Optional server type to check ('docs' or 'gateway').
56
- * @returns `true` if the definition should be hidden, `false` otherwise.
57
- *
58
- * @example
59
- * isDefinitionHidden({ 'x-mcp': { docs: { hide: false } } }); // false
60
- */
61
- export declare function isDefinitionHidden(info: OpenAPIDefinition['info'], serverType?: McpServerType): boolean;
2
+ import type { XMcpConfig, XMcpSection } from '../types.js';
3
+ /**
4
+ * Returns false when the endpoint is hidden for `section`; otherwise strips x-mcp from the
5
+ * output and returns true.
6
+ */
7
+ export declare function checkEndpointAndDeleteXMcp(endpoint: {
8
+ 'x-mcp'?: XMcpConfig;
9
+ }, section: XMcpSection): boolean;
10
+ /** Whether the definition's info-level x-mcp hides it for `section` (defaults to `docs`). */
11
+ export declare function isDefinitionHidden(info?: OpenAPIDefinition['info'], section?: XMcpSection): boolean;
12
+ /** Whether the API explicitly opts into the gateway via `x-mcp.gateway`. */
13
+ export declare function hasGatewayConfig(info: OpenAPIDefinition['info'] | undefined): boolean;
62
14
  //# sourceMappingURL=xmcp-utils.d.ts.map
@@ -1 +1 @@
1
- function r(e){return e?.["x-mcp"]}function i(e,t){return!!r(e)?.[t]?.hide}function c(e,t){return i(e,t)?!1:(e&&delete e["x-mcp"],!0)}function o(e,t){if(!e)return!1;const n=e["x-mcp"];return!n||typeof n!="object"?!1:t?n[t]?.hide===!0:n.docs?.hide}export{c as checkEndpointAndDeleteXMcp,r as getXMcpConfig,o as isDefinitionHidden,i as isEndpointHidden};
1
+ function c(e,t){return e?.["x-mcp"]?.[t]?.hide?!1:(e&&delete e["x-mcp"],!0)}function i(e,t){if(!e)return!1;const n=e["x-mcp"];return!n||typeof n!="object"?!1:t?n[t]?.hide===!0:n.docs?.hide===!0}function o(e){if(!e)return!1;const t=e["x-mcp"];return!!(t&&typeof t=="object"&&t.gateway)}export{c as checkEndpointAndDeleteXMcp,o as hasGatewayConfig,i as isDefinitionHidden};
@@ -60,4 +60,12 @@ export declare function filterIgnoredApiDescriptions<T extends {
60
60
  export declare function isMcpInRedirects(redirects?: {
61
61
  [key: string]: RedirectConfig;
62
62
  }): boolean;
63
+ /** Whether an API definition is part of the Docs MCP catalog (not a localized copy, has public paths, not ignored via mcp.docs.ignore). */
64
+ export declare function isApiIncludedInDocsMcp(relativePath: string, definition: {
65
+ paths?: Record<string, unknown>;
66
+ } | undefined, mcpConfig: {
67
+ docs?: {
68
+ ignore?: readonly string[];
69
+ };
70
+ } | undefined): boolean;
63
71
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- import p,{join as c}from"node:path";import D from"picomatch";import{PUBLIC_API_DEFINITIONS_FOLDER as f,L10N_DIR_NAME as a,I18N_DIR_NAME as u}from"../../constants/common.js";import{RESERVED_ROUTES as l}from"../../plugins/get-reserved-routes.js";import{canDownloadApiDefinition as h}from"../../utils/rbac.js";function o(r){if(typeof r!="object"||r===null)return r;if(Array.isArray(r))return r.map(o);const i={};for(const t in r)if(Object.hasOwn(r,t)){if(t.toString().startsWith("x-parsed-"))continue;i[t]=o(r[t])}return i}function R(r){return r.reduce((i,t)=>(t.relativePath.startsWith(a)||t.relativePath.startsWith(u)||t.definition?.paths&&Object.keys(t.definition.paths).length>0&&i.push({...t,definition:o(t.definition)}),i),[])}function x(r,i,t,e){const n={};for(const[m,s]of Object.entries(r))h(c(f,s.relativePath),t,e,i)&&(n[m]=s);return n}function E(r,i,t,e){return r.filter(n=>h(c(f,n.relativePath),t,e,i))}function P(r){return r.filter(({relativePath:i})=>!i.startsWith(a)&&!i.startsWith(u)).map(({relativePath:i,settings:t})=>({name:t.info?.title||p.basename(i,p.extname(i)),description:t.info?.description,relativePath:i}))}function _(r,i){return r.filter(({relativePath:t})=>!D(i||[])(t))}function g(r={}){for(const[i,t]of Object.entries(r))if(i===l.mcp||(typeof t=="string"?t:t.to)===l.mcp)return!0;return!1}export{P as buildGraphqlDescriptions,x as filterApiDescriptionsByRbac,E as filterGraphqlDescriptionsByRbac,_ as filterIgnoredApiDescriptions,R as getCleanedUpApiDescriptions,g as isMcpInRedirects};
1
+ import f,{join as a}from"node:path";import u from"picomatch";import{PUBLIC_API_DEFINITIONS_FOLDER as h,L10N_DIR_NAME as o,I18N_DIR_NAME as s}from"../../constants/common.js";import{RESERVED_ROUTES as l}from"../../plugins/get-reserved-routes.js";import{canDownloadApiDefinition as m}from"../../utils/rbac.js";function p(r){if(typeof r!="object"||r===null)return r;if(Array.isArray(r))return r.map(p);const i={};for(const t in r)if(Object.hasOwn(r,t)){if(t.toString().startsWith("x-parsed-"))continue;i[t]=p(r[t])}return i}function x(r){return r.reduce((i,t)=>(t.relativePath.startsWith(o)||t.relativePath.startsWith(s)||t.definition?.paths&&Object.keys(t.definition.paths).length>0&&i.push({...t,definition:p(t.definition)}),i),[])}function R(r,i,t,e){const n={};for(const[D,c]of Object.entries(r))m(a(h,c.relativePath),t,e,i)&&(n[D]=c);return n}function g(r,i,t,e){return r.filter(n=>m(a(h,n.relativePath),t,e,i))}function E(r){return r.filter(({relativePath:i})=>!i.startsWith(o)&&!i.startsWith(s)).map(({relativePath:i,settings:t})=>({name:t.info?.title||f.basename(i,f.extname(i)),description:t.info?.description,relativePath:i}))}function _(r,i){return r.filter(({relativePath:t})=>!u(i||[])(t))}function W(r={}){for(const[i,t]of Object.entries(r))if(i===l.mcp||(typeof t=="string"?t:t.to)===l.mcp)return!0;return!1}function b(r,i,t){return r.startsWith(o)||r.startsWith(s)||!i?.paths||Object.keys(i.paths).length===0?!1:!u(t?.docs?.ignore||[])(r)}export{E as buildGraphqlDescriptions,R as filterApiDescriptionsByRbac,g as filterGraphqlDescriptionsByRbac,_ as filterIgnoredApiDescriptions,x as getCleanedUpApiDescriptions,b as isApiIncludedInDocsMcp,W as isMcpInRedirects};
@@ -1 +1 @@
1
- import{telemetry as u}from"../../../telemetry/index.js";import{mcpToolHandlers as p}from"../../../../client/mcp-tool-handlers-entry.js";import{getMcpMode as m}from"../mode.js";import{reportMcpToolCalled as w,reportMcpToolError as f}from"./mcp-tool-telemetry.js";async function b(n){u.initialize();const{toolName:t,args:c,context:o,extra:l}=n,r=m();try{const e=p[t];if(!e)throw new Error(`Unknown MCP tool: ${t}`);const{default:d}=await e(),i=d[t];if(!i)throw new Error(`MCP tool module does not export a handler for "${t}". Expected \`export default { '${t}': handler }\`.`);const a=await(typeof i=="function"?i:i.execute)(c,o,l);if(!x(a))throw new Error(`MCP tool "${t}" returned an invalid result.`);if(a.isError)f(t,a.content.map(({text:s})=>s).join(" "),r);else{const s=a.content.map(({text:h})=>h).join("").length;w(t,r,s)}return a}catch(e){throw f(t,e instanceof Error?e.message:String(e),r,e instanceof Error&&e.stack||""),e}}function x(n){return!n||typeof n!="object"?!1:Array.isArray(n.content)}async function T(n){u.initialize();const{toolName:t,context:c}=n;try{const o=p[t];if(!o)return!1;const{default:l}=await o(),r=l[t];return r?typeof r=="object"&&typeof r.isAvailable=="function"?await r.isAvailable(c):!0:!1}catch(o){throw f(t,"Failed to check if MCP tool is available: "+(o instanceof Error?o.message:String(o)),m(),o instanceof Error&&o.stack||""),o}}export{b as executeMcpTool,T as isMcpToolAvailable};
1
+ import{telemetry as d}from"../../../telemetry/index.js";import{mcpToolHandlers as w}from"../../../../client/mcp-tool-handlers-entry.js";import{getMcpMode as h}from"../utils/mode.js";import{McpServerType as i}from"../constants.js";import{reportMcpToolCalled as x,reportMcpToolError as u}from"./mcp-tool-telemetry.js";async function A(r){d.initialize();const{toolName:o,args:l,context:f,extra:t}=r,a=h(),n=performance.now();try{const e=w[o];if(!e)throw new Error(`Unknown MCP tool: ${o}`);const{default:M}=await e(),c=M[o];if(!c)throw new Error(`MCP tool module does not export a handler for "${o}". Expected \`export default { '${o}': handler }\`.`);const s=await(typeof c=="function"?c:c.execute)(l,f,t);if(!g(s))throw new Error(`MCP tool "${o}" returned an invalid result.`);const m=Math.round(performance.now()-n);if(s.isError)u({tool:o,message:s.content.map(({text:p})=>p).join(" "),mode:a,serverType:i.Docs,durationMs:m});else{const p=s.content.map(({text:y})=>y).join("").length;x({tool:o,mode:a,serverType:i.Docs,durationMs:m,outputLength:p})}return s}catch(e){throw u({tool:o,message:e instanceof Error?e.message:String(e),mode:a,serverType:i.Docs,durationMs:Math.round(performance.now()-n),stack:e instanceof Error&&e.stack||""}),e}}function g(r){return!r||typeof r!="object"?!1:Array.isArray(r.content)}async function j(r){d.initialize();const{toolName:o,context:l}=r,f=performance.now();try{const t=w[o];if(!t)return!1;const{default:a}=await t(),n=a[o];return n?typeof n=="object"&&typeof n.isAvailable=="function"?await n.isAvailable(l):!0:!1}catch(t){throw u({tool:o,message:"Failed to check if MCP tool is available: "+(t instanceof Error?t.message:String(t)),mode:h(),serverType:i.Docs,durationMs:Math.round(performance.now()-f),stack:t instanceof Error&&t.stack||""}),t}}export{A as executeMcpTool,j as isMcpToolAvailable};
@@ -1,4 +1,16 @@
1
- import type { McpMode } from '../types.js';
2
- export declare function reportMcpToolCalled(tool: string, mode: McpMode, outputLength: number): void;
3
- export declare function reportMcpToolError(tool: string, message: string, mode: McpMode, stack?: string): void;
1
+ import type { McpMode, McpServerType } from '../types.js';
2
+ type McpToolEvent = {
3
+ tool: string;
4
+ mode: McpMode;
5
+ serverType: McpServerType;
6
+ durationMs: number;
7
+ };
8
+ export declare function reportMcpToolCalled({ tool, mode, serverType, durationMs, outputLength }: McpToolEvent & {
9
+ outputLength: number;
10
+ }): void;
11
+ export declare function reportMcpToolError({ tool, mode, serverType, durationMs, message, stack }: McpToolEvent & {
12
+ message: string;
13
+ stack?: string;
14
+ }): void;
15
+ export {};
4
16
  //# sourceMappingURL=mcp-tool-telemetry.d.ts.map
@@ -1 +1 @@
1
- import{telemetry as p}from"../../../telemetry/index.js";import{McpServerType as t}from"../constants.js";const c={id:t.Docs,object:"mcpServer",uri:`urn:redocly:realm:mcp:server:${t.Docs}`,server_type:t.Docs};function m(e,r,o){p.sendMcpToolCalledMessage([{...c,tool:e,mode:r,output_length:o}])}function i(e,r,o,s=""){p.sendMcpErrorMessage([{...c,tool:e,message:r,stack:s,mode:o}])}export{m as reportMcpToolCalled,i as reportMcpToolError};
1
+ import{telemetry as c}from"../../../telemetry/index.js";function l(r){return{id:r,object:"mcpServer",uri:`urn:redocly:realm:mcp:server:${r}`,server_type:r}}function s({tool:r,mode:e,serverType:o,durationMs:t,outputLength:n}){c.sendMcpToolCalledMessage([{...l(o),tool:r,mode:e,output_length:n,duration_ms:t}])}function i({tool:r,mode:e,serverType:o,durationMs:t,message:n,stack:p=""}){c.sendMcpErrorMessage([{...l(o),tool:r,message:n,stack:p,mode:e,duration_ms:t}])}export{s as reportMcpToolCalled,i as reportMcpToolError};
@@ -1 +1 @@
1
- import{simplifyAstStructure as Z}from"@redocly/openapi-docs";import{REDOCLY_ROUTE_RBAC as A,REDOCLY_TEAMS_RBAC as S}from"@redocly/config";import{OPENAPI_DOCS_TEMPLATE_ID as H,PUBLIC_RBAC_SCOPE_ITEM as $}from"../../../constants/common.js";import{DEPRECATED_PUBLIC_API_DEFINITIONS_FOLDER as ee,PUBLIC_API_DEFINITIONS_FOLDER as te}from"../../constants/common.js";import{OPENAPI_SHARED_DATA_PREFIX as j}from"../../constants/plugins/openapi-docs.js";import{envConfig as G}from"../../config/env-config.js";import{isRbacScopeItems as oe}from"../../utils/rbac/is-rbac-scope-items.js";import{getRouteSlugsForPath as ae}from"../utils.js";import{searchResolver as re}from"./search/search-resolver.js";import{convertOpenAPIDocs2Sidebar as se,shouldAddRoute as ne}from"./utils.js";import{getTemplatePath as U}from"./get-template-path.js";import{storeDefinitionBundles as ie}from"./store-definition-bundles.js";import{definitionLoader as pe,definitionsLoader as ce}from"./load-definition.js";import{getAiDocumentsStore as de}from"./search/get-ai-search-documents.js";import{fromCurrentDir as le}from"../../utils/paths.js";import{telemetryTraceStep as fe}from"../../../cli/telemetry/helpers/trace-step.js";import{getApiConfigByPath as ue}from"../get-api-config.js";const B="openapi-spec-download";async function Te(){let L=[],E={},M=new Set,f={};return{id:"openapi",requiredEntitlements:["openapi"],loaders:{"load-oas-docs":ce,"load-oas":pe},processContent:async(e,c)=>{await fe("build.plugin.openapi_docs",async u=>{e.createRequestHandler(B,le(import.meta.url,"./spec-download.api.js")),e.addApiRoute({slug:te+"/*",requestHandlerId:B,httpMethod:"all",[S]:$,getStaticData:async()=>({props:{}})}),e.addApiRoute({slug:ee+"/*",requestHandlerId:B,httpMethod:"all",[S]:$,getStaticData:async()=>({props:{}})});const _=e.createTemplate(H,U("../../../client/templates/openapi-docs/template.js")),a=e.registerServerPropsGetter(H,U("./get-server-props.js")),o=await c.getConfig();u?.setAttribute("config",JSON.stringify(o.openapi||{})),E={},f={};const p=await e.loadOpenApiDefinitions(c);L=p.map(({markdocChunks:r,relativePath:m,customOutputRelativeFile:i,isVirtual:n,realRelativePath:g})=>({chunks:r,relativePath:m,realRelativePath:g,isVirtual:i!=null||n})),ie(p,e.outdir);for(const r of p||[]){const{definition:m,config:i,relativePath:n,customOutputRelativeFile:g,contentItems:C,flatItems:D,parser:d,options:I,rawOptions:V,hash:q}=r,b=g||n,s=[],k={},{definition:Y}=d||{},{info:l}=Y||{},y=l?.["x-metadata"],v=!!i.openapi?.excludeFromSearch||!!i.theme?.openapi?.excludeFromSearch||!!o.openapi?.excludeFromSearch||!!o.theme?.openapi?.excludeFromSearch,x={title:l?.title,description:l?.description,summary:l?.summary,...i.metadata,...y},h={untagged:[],tagged:new Map};for(const t of D){const{id:O,href:P,operationDefinition:F}=t;if(F){const{tags:R}=F;if(R)for(const w of R)h.tagged.has(w)||h.tagged.set(w,[]),h.tagged.get(w)?.push(t);else h.untagged.push(t);G.isDevelopMode&&(k[`#${F.pointer}`]=t.href)}if(!ne({item:t}))continue;const T=t,W=T.type==="section"&&!!T.infoDefinition,X=P.split("#")[0]+"/",z=t?.operationDefinition?.[S],K=oe(i.access?.rbac)?i.access.rbac:void 0;s.push({excludeFromSearch:v,slugSuffix:X,fsPath:b,metadata:{subType:"openapi-operation"},httpVerb:t?.httpVerb||"",path:n,templateId:_,[S]:z||K,getAiDocumentsStore:de({parser:d,options:I,info:l,tagOperations:h,openapiContentItem:T,metadata:x,relativePath:n,getSearchFacets:e.getSearchFacets,includeInLLMsTxt:W,excludeFromSearch:v}),getStaticData:async R=>({props:{dynamicMarkdocComponents:["openapi"],baseSlug:R.baseSlug,seo:t["x-metadata"]?.seo||{title:t.name,description:t.description},itemId:O,disableAutoScroll:!0}})})}if(s[0]={...s[0],metadata:{type:"openapi",...x},hasClientRoutes:!0,getSidebar:(t,O)=>{const P=[];return se({contentItems:C,sidebarItems:P,routeSlug:t.slug,navItem:O}),P},getNavText:()=>l?.title,getSearchDocuments:re(d,I,D,e.getSearchFacets,e.setSearchFacets,v)},y?.apiId&&!r.isVirtual){const t=ue(r.config.apis,r.realRelativePath);f[y.apiId]={[S]:t.rbac,[A]:{fsPath:b}}}const J=s[0];s[0]=s[s.length-1],s[s.length-1]=J;for(const t of s)e.addRoute({...t,serverPropsGetterIds:[a]});const Q=G.isDevelopMode?n:void 0,N=`${j}${n}`;E[N]={fsPath:b,definition:m,options:V,sourcePath:Q,routesMapping:k,hash:q};for(const t of s)e.addRoute({...t,sharedData:[{id:N,key:"openAPIDocsStore"}],serverPropsGetterIds:[a]})}})},afterRoutesCreated:async(e,c)=>{const u=new Set;for(const{chunks:a,relativePath:o,isVirtual:p,realRelativePath:r}of L){const m=ae(e.getAllRoutes(),o),i=(await c.cache.load(r,"load-oas")).compoundHash;await c.cache.load(o,{loader:async function(){for(const{node:g,markdown:C,key:D,relativePath:d}of a){const{ast:I}=await e.parseMarkdoc({input:{content:C,relativePath:d,isVirtual:p},context:c,deps:{sharedDataIds:[`${j}${d}`],routeSlugs:m}});g[`x-parsed-md-${D}`]={result:Z(I)}}},name:"openapi-markdoc-inline-parser"},[i]);for(const{pointer:n}of a)u.add(n)}const _=M.difference(u);for(const a of _)c.cache.delete(a);M=u;for(const[a,o]of Object.entries(E))await e.createSharedData(a,{...o,baseSlug:e.getRouteByFsPath(o.fsPath)?.baseSlug},o.hash);Object.entries(f).forEach(([a,o])=>{const p=o[A]?.fsPath;if(!p)return;const r=e.getRouteByFsPath(p)?.baseSlug;f[a]={...o,[A]:{...o[A],slug:r},slug:r}}),e.setGlobalData({apiProducts:f})}}}export{Te as openAPIDocsPlugin};
1
+ import{simplifyAstStructure as Z}from"@redocly/openapi-docs";import{REDOCLY_ROUTE_RBAC as y,REDOCLY_TEAMS_RBAC as D}from"@redocly/config";import{OPENAPI_DOCS_TEMPLATE_ID as j,PUBLIC_RBAC_SCOPE_ITEM as U}from"../../../constants/common.js";import{DEPRECATED_PUBLIC_API_DEFINITIONS_FOLDER as ee,PUBLIC_API_DEFINITIONS_FOLDER as te}from"../../constants/common.js";import{OPENAPI_SHARED_DATA_PREFIX as L}from"../../constants/plugins/openapi-docs.js";import{envConfig as V}from"../../config/env-config.js";import{isRbacScopeItems as oe}from"../../utils/rbac/is-rbac-scope-items.js";import{getRouteSlugsForPath as ae}from"../utils.js";import{searchResolver as re}from"./search/search-resolver.js";import{convertOpenAPIDocs2Sidebar as ie,shouldAddRoute as se}from"./utils.js";import{getTemplatePath as q}from"./get-template-path.js";import{storeDefinitionBundles as ne}from"./store-definition-bundles.js";import{definitionLoader as pe,definitionsLoader as ce}from"./load-definition.js";import{getAiDocumentsStore as de}from"./search/get-ai-search-documents.js";import{fromCurrentDir as le}from"../../utils/paths.js";import{telemetryTraceStep as fe}from"../../../cli/telemetry/helpers/trace-step.js";import{getApiConfigByPath as ue}from"../get-api-config.js";import{shouldAdvertiseGatewayXMcp as me,buildGatewayXMcp as ge}from"../mcp/gateway-mcp/eligibility.js";const x="openapi-spec-download";async function Be(){let k=[],C={},N=new Set,d={};return{id:"openapi",requiredEntitlements:["openapi"],loaders:{"load-oas-docs":ce,"load-oas":pe},processContent:async(e,n)=>{await fe("build.plugin.openapi_docs",async l=>{e.createRequestHandler(x,le(import.meta.url,"./spec-download.api.js")),e.addApiRoute({slug:te+"/*",requestHandlerId:x,httpMethod:"all",[D]:U,getStaticData:async()=>({props:{}})}),e.addApiRoute({slug:ee+"/*",requestHandlerId:x,httpMethod:"all",[D]:U,getStaticData:async()=>({props:{}})});const E=e.createTemplate(j,q("../../../client/templates/openapi-docs/template.js")),f=e.registerServerPropsGetter(j,q("./get-server-props.js")),u=await n.getConfig();l?.setAttribute("config",JSON.stringify(u.openapi||{})),C={},d={};const p=await e.loadOpenApiDefinitions(n);k=p.map(({markdocChunks:a,relativePath:o,customOutputRelativeFile:r,isVirtual:i,realRelativePath:m})=>({chunks:a,relativePath:o,realRelativePath:m,isVirtual:r!=null||i})),ne(p,e.outdir);for(const a of p||[]){const{definition:o,config:r,relativePath:i,customOutputRelativeFile:m,contentItems:_,flatItems:g,parser:h,options:I,rawOptions:v,hash:R}=a,S=m||i,s=[],G={},{definition:X}=h||{},{info:c}=X||{},O=c?.["x-metadata"],w=!!r.openapi?.excludeFromSearch||!!r.theme?.openapi?.excludeFromSearch||!!u.openapi?.excludeFromSearch||!!u.theme?.openapi?.excludeFromSearch,H={title:c?.title,description:c?.description,summary:c?.summary,...r.metadata,...O},P={untagged:[],tagged:new Map};for(const t of g){const{id:F,href:A,operationDefinition:T}=t;if(T){const{tags:b}=T;if(b)for(const B of b)P.tagged.has(B)||P.tagged.set(B,[]),P.tagged.get(B)?.push(t);else P.untagged.push(t);V.isDevelopMode&&(G[`#${T.pointer}`]=t.href)}if(!se({item:t}))continue;const M=t,Q=M.type==="section"&&!!M.infoDefinition,W=A.split("#")[0]+"/",z=t?.operationDefinition?.[D],K=oe(r.access?.rbac)?r.access.rbac:void 0;s.push({excludeFromSearch:w,slugSuffix:W,fsPath:S,metadata:{subType:"openapi-operation"},httpVerb:t?.httpVerb||"",path:i,templateId:E,[D]:z||K,getAiDocumentsStore:de({parser:h,options:I,info:c,tagOperations:P,openapiContentItem:M,metadata:H,relativePath:i,getSearchFacets:e.getSearchFacets,includeInLLMsTxt:Q,excludeFromSearch:w}),getStaticData:async b=>({props:{dynamicMarkdocComponents:["openapi"],baseSlug:b.baseSlug,seo:t["x-metadata"]?.seo||{title:t.name,description:t.description},itemId:F,disableAutoScroll:!0}})})}if(s[0]={...s[0],metadata:{type:"openapi",...H},hasClientRoutes:!0,getSidebar:(t,F)=>{const A=[];return ie({contentItems:_,sidebarItems:A,routeSlug:t.slug,navItem:F}),A},getNavText:()=>c?.title,getSearchDocuments:re(h,I,g,e.getSearchFacets,e.setSearchFacets,w)},O?.apiId&&!a.isVirtual){const t=ue(a.config.apis,a.realRelativePath);d[O.apiId]={[D]:t.rbac,[y]:{fsPath:S}}}const Y=s[0];s[0]=s[s.length-1],s[s.length-1]=Y;for(const t of s)e.addRoute({...t,serverPropsGetterIds:[f]});const J=V.isDevelopMode?i:void 0,$=`${L}${i}`;C[$]={fsPath:S,definition:o,options:v,sourcePath:J,routesMapping:G,hash:R};for(const t of s)e.addRoute({...t,sharedData:[{id:$,key:"openAPIDocsStore"}],serverPropsGetterIds:[f]})}})},afterRoutesCreated:async(e,n)=>{const l=new Set;for(const{chunks:a,relativePath:o,isVirtual:r,realRelativePath:i}of k){const m=ae(e.getAllRoutes(),o),_=(await n.cache.load(i,"load-oas")).compoundHash;await n.cache.load(o,{loader:async function(){for(const{node:h,markdown:I,key:v,relativePath:R}of a){const{ast:S}=await e.parseMarkdoc({input:{content:I,relativePath:R,isVirtual:r},context:n,deps:{sharedDataIds:[`${L}${R}`],routeSlugs:m}});h[`x-parsed-md-${v}`]={result:Z(S)}}},name:"openapi-markdoc-inline-parser"},[_]);for(const{pointer:g}of a)l.add(g)}const E=N.difference(l);for(const a of E)n.cache.delete(a);N=l;const f=e.getConfig(),u=f.access?.rbac??{},p=n.withPathPrefix("/mcp");for(const[a,o]of Object.entries(C)){const r=e.getRouteByFsPath(o.fsPath)?.baseSlug,i=r&&me(o.definition,a.slice(L.length),u,f.mcp)?{...o.definition,"x-mcp":{...ge(p),...o.definition["x-mcp"],servers:[{url:p}]}}:o.definition;await e.createSharedData(a,{...o,definition:i,baseSlug:r},o.hash)}Object.entries(d).forEach(([a,o])=>{const r=o[y]?.fsPath;if(!r)return;const i=e.getRouteByFsPath(r)?.baseSlug;d[a]={...o,[y]:{...o[y],slug:i},slug:i}}),e.setGlobalData({apiProducts:d})}}}export{Be as openAPIDocsPlugin};
@@ -1,5 +1,6 @@
1
1
  import type { SearchFacet } from '@redocly/theme/core/types';
2
- import type { SearchEngineType, AfterRoutesCreatedActions, AddDocumentsParams, SearchDocument, AuthDetails } from '../../../types';
2
+ import type { SearchEngineType } from '@redocly/config';
3
+ import type { AfterRoutesCreatedActions, AddDocumentsParams, SearchDocument, AuthDetails } from '../../../types';
3
4
  import type { FacetCountsRequestParams, GlobalData, SearchRequestParams, SearchResponse } from '../../../../types/index.js';
4
5
  export declare class SearchEngine {
5
6
  #private;
@@ -1 +1 @@
1
- import{REDOCLY_TEAMS_RBAC as p}from"@redocly/config";import{DEFAULT_SEARCH_ENGINE as s}from"../../constants/plugins/search.js";import{envConfig as c}from"../../config/env-config.js";import{logger as E}from"../../tools/notifiers/logger.js";import{reporter as u}from"../../tools/notifiers/reporter.js";import{prepareSearchDocuments as S}from"./documents/search-documents.js";import{SearchEngine as b}from"./engines/search-engine.js";import{prepareSemanticDocuments as D}from"./ai-indexer/prepare-semantic-documents.js";import{isAiSearchEnabled as y,shouldGenerateEmbeddings as A}from"./utils.js";import{telemetryTraceStep as C}from"../../../cli/telemetry/helpers/trace-step.js";import{EntitlementsProvider as R}from"../../entitlements/entitlements-provider.js";async function B(){let e=!0;return{async afterRoutesCreated(t,i){(c.SEARCH_DEV_REINIT||e)&&await _(t,i),e=!1},id:"search"}}async function _(e,t){await C("build.plugin.search",async i=>{const r=e.getConfig();if(i?.setAttribute("config",JSON.stringify(r.search||{})),r.search?.hide){E.info("Search is disabled in the config, skipping");return}const o=r.search?.engine||s,m=c.isDevelopMode?c.SEARCH_DEV_DEBUG?o:s:o,{l10n:l}=e.getGlobalData(),d=y(r),n=r.access?.rbac?.features?.aiSearch,f=A(r);let a;try{a=new b(m,l)}catch(g){await u.panic(g);return}await a.initIndexSchema(e.getSearchFacets()),e.setSearchEngine(a),e.setGlobalData({searchFeatures:{advanced:{enabled:m!==s&&!r.search?.filters?.hide},ai:{enabled:d,[p]:n&&Object.keys(n).length?n:void 0}}}),await S(t.fs,e,a);const h=R.instance().canAccessFeature("seo")===!0&&r.seo?.llmstxt?.hide!==!0;await D(e,t,{embeddingsEnabled:f,llmstxtEnabled:h})})}export{B as searchPlugin};
1
+ import{DEFAULT_SEARCH_ENGINE as s,REDOCLY_TEAMS_RBAC as E}from"@redocly/config";import{envConfig as c}from"../../config/env-config.js";import{logger as p}from"../../tools/notifiers/logger.js";import{reporter as u}from"../../tools/notifiers/reporter.js";import{prepareSearchDocuments as S}from"./documents/search-documents.js";import{SearchEngine as b}from"./engines/search-engine.js";import{prepareSemanticDocuments as D}from"./ai-indexer/prepare-semantic-documents.js";import{isAiSearchEnabled as y,shouldGenerateEmbeddings as A}from"./utils.js";import{telemetryTraceStep as C}from"../../../cli/telemetry/helpers/trace-step.js";import{EntitlementsProvider as R}from"../../entitlements/entitlements-provider.js";async function k(){let e=!0;return{async afterRoutesCreated(t,n){(c.SEARCH_DEV_REINIT||e)&&await _(t,n),e=!1},id:"search"}}async function _(e,t){await C("build.plugin.search",async n=>{const r=e.getConfig();if(n?.setAttribute("config",JSON.stringify(r.search||{})),r.search?.hide){p.info("Search is disabled in the config, skipping");return}const o=r.search?.engine||s,m=c.isDevelopMode?c.SEARCH_DEV_DEBUG?o:s:o,{l10n:l}=e.getGlobalData(),d=y(r),i=r.access?.rbac?.features?.aiSearch,f=A(r);let a;try{a=new b(m,l)}catch(g){await u.panic(g);return}await a.initIndexSchema(e.getSearchFacets()),e.setSearchEngine(a),e.setGlobalData({searchFeatures:{advanced:{enabled:m!==s&&!r.search?.filters?.hide},ai:{enabled:d,[E]:i&&Object.keys(i).length?i:void 0}}}),await S(t.fs,e,a);const h=R.instance().canAccessFeature("seo")===!0&&r.seo?.llmstxt?.hide!==!0;await D(e,t,{embeddingsEnabled:f,llmstxtEnabled:h})})}export{k as searchPlugin};
@@ -0,0 +1,17 @@
1
+ import type { Skill } from '../../types/plugins/skills.js';
2
+ type DiscoverSkillsInput = {
3
+ cwd: string;
4
+ excludeFiles?: string[];
5
+ };
6
+ /**
7
+ * Produce the full, unfiltered skill set: one skill per `@skills/<folder>/SKILL.md`
8
+ * plus an optional root `SKILL.md`. Slugs must be unique: the root skill wins over a
9
+ * colliding `@skills` entry.
10
+ *
11
+ * Skill filenames in folders are matched case-insensitively against directory listings,
12
+ * so behavior is identical on case-sensitive and case-insensitive filesystems — the
13
+ * lowercase served path is a routing concern, not an authoring rule.
14
+ */
15
+ export declare function discoverSkills(input: DiscoverSkillsInput): Promise<Skill[]>;
16
+ export {};
17
+ //# sourceMappingURL=discover-skills.d.ts.map
@@ -0,0 +1 @@
1
+ import k from"picomatch";import{readdir as w,readFile as F}from"fs/promises";import p from"node:path";import{USER_SKILLS_ALIAS as h,USER_SKILL_FILENAME as u}from"../../../constants/common.js";import{logger as S}from"../../tools/notifiers/logger.js";import{slug as y}from"../../../utils/slugger.js";import{envConfig as L}from"../../config/env-config.js";import{parseSkill as j}from"./utils/parse-skill.js";const E="project-docs";async function M(t){const{cwd:s,excludeFiles:e=[]}=t,o=L.PROJECT_SLUG||E,l=e.map(r=>k(r)),c=new Map,d=await g(p.join(s,u)),i=d!==void 0?[{slug:o,fsPath:u,content:d}]:[],a=await _({cwd:s,baseSegments:[h]});for(const r of[...a,...i]){if(l.some(m=>m(r.fsPath)))continue;const n=j(r);if("error"in n){S.warn(n.error);continue}const f=c.get(n.slug);f!==void 0&&S.warn(`Skill "${n.slug}" from "${f.fsPath}" is replaced by "${n.fsPath}".`),c.set(n.slug,n)}return Array.from(c.values())}async function _(t){const{cwd:s,baseSegments:e}=t,o=p.join(s,...e),l=[],c=new Map,d=await P(o);for(const i of d){const a=y(i,{replaceDots:!0}),r=c.get(a);if(r!==void 0){S.warn(`Skipping skill folder "${[...e,i].join("/")}": its slug "${a}" collides with the folder "${r}" after slugification.`);continue}const n=await I(p.join(o,i)),f=$(n,[...e,i].join("/"));if(f===void 0)continue;const m=await g(p.join(o,i,f));m!==void 0&&(c.set(a,i),l.push({slug:a,fsPath:[...e,i,f].join("/"),content:m}))}return l}function $(t,s){const e=t.filter(l=>l.toLowerCase()===u.toLowerCase()),o=e.includes(u)?u:e[0];return e.length>1&&S.warn(`Multiple skill files in "${s}" differ only by case; using "${o}".`),o}async function g(t){try{return await F(t,"utf-8")}catch{return}}async function P(t){try{return(await w(t,{withFileTypes:!0})).filter(e=>e.isDirectory()).map(e=>e.name).sort()}catch{return[]}}async function I(t){try{return(await w(t,{withFileTypes:!0})).filter(e=>e.isFile()).map(e=>e.name).sort()}catch{return[]}}export{M as discoverSkills};
@@ -0,0 +1,3 @@
1
+ import type { LifecyclePluginInstance } from '../../types';
2
+ export declare function skillsPlugin(): Promise<LifecyclePluginInstance>;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ import{discoverSkills as r}from"./discover-skills.js";async function o(){return{id:"skills",async afterRoutesCreated(s,l){const e=s.getConfig();if(e.skills?.hide===!0)return;const i=await r({cwd:l.fs.cwd,excludeFiles:e.skills?.excludeFiles??[]});s.addSkills(i),l.logger.verbose(`Discovered ${i.length} skill(s)`)}}}export{o as skillsPlugin};
@@ -0,0 +1,18 @@
1
+ import type { Skill } from '../../../types/plugins/skills.js';
2
+ type ParseSkillInput = {
3
+ slug: string;
4
+ fsPath: string;
5
+ content: string;
6
+ };
7
+ type ParseSkillError = {
8
+ error: string;
9
+ };
10
+ /**
11
+ * @param input - the skill slug, its project-relative path, and the raw file content
12
+ * @returns the parsed `Skill` with a `sha256:<hex>` digest over the raw content,
13
+ * or a `{ error }` object when the frontmatter is invalid or the required
14
+ * `name`/`description` fields are missing
15
+ */
16
+ export declare function parseSkill(input: ParseSkillInput): Skill | ParseSkillError;
17
+ export {};
18
+ //# sourceMappingURL=parse-skill.d.ts.map
@@ -0,0 +1 @@
1
+ import{extractFrontmatter as p}from"../../markdown/extract-frontmatter.js";import{shaHex as d}from"../../../utils/crypto/sha-hex.js";function u(s){const{slug:a,fsPath:t,content:e}=s,{frontmatter:r,frontmatterError:i}=p(e);if(i)return{error:`Invalid frontmatter in ${t}: ${i}`};const n=typeof r.name=="string"?r.name.trim():"",o=typeof r.description=="string"?r.description.trim():"";if(!n)return{error:`Skill ${t} is missing required frontmatter "name"`};if(!o)return{error:`Skill ${t} is missing required frontmatter "description"`};const c=r.rbac&&typeof r.rbac=="object"&&!Array.isArray(r.rbac)?r.rbac:void 0,f=Array.isArray(r.tags)?r.tags.filter(m=>typeof m=="string"):[];return{slug:a,name:n,description:o,content:e,digest:`sha256:${d(e)}`,fsPath:t,rbac:c,tags:f}}export{u as parseSkill};
@@ -1 +1 @@
1
- import{DEFAULT_SSO_IDP_TITLE as A}from"../../../constants/common.js";import{envConfig as d}from"../../config/env-config.js";import{telemetryTraceStep as I}from"../../../cli/telemetry/helpers/trace-step.js";import{logger as C}from"../../tools/notifiers/logger.js";const O="https://auth.cloud.redocly.com/oidc/.well-known/openid-configuration",_="https://auth.cloud.redocly.com/api/sso/oidc/introspect";async function S(T){return{id:"sso",async processContent(i){await I("build.plugin.sso",async f=>{const o=i.getConfig(),s=o.access;if(f?.setAttribute("config",`{"ssoDirect": ${JSON.stringify(o.ssoDirect||{})}}, {"sso": ${JSON.stringify(s?.sso||{})}}, {"idps": ${JSON.stringify(s?.idps||{})}}`),o.ssoDirect&&typeof o.ssoDirect=="object"&&Object.keys(o.ssoDirect).length!==0)return;const y=!!s?.sso&&(Array.isArray(s.sso)?s.sso.length>0:!0),r=!!s?.idps&&(Array.isArray(s.idps)?s.idps.length>0:!0);if(s?.sso&&Array.isArray(s.sso)&&!s.sso.length&&!r)return;const g=!!(s?.rbac&&typeof s.rbac=="object"&&Object.keys(s.rbac).length!==0),u=s?.requiresLogin;if(!g&&!u)return;y&&r&&C.error("access.sso and access.idps both set \u2014 should have been rejected by config validation",{sso:s.sso,idps:s.idps});let c=O,a=d.REDOCLY_OAUTH_USE_INTROSPECT?_:"";const e=s?.residency;if(e){const l=e.endsWith("/")?e.slice(0,-1):e;c=`${l.replace("app.","auth.")}/oidc/.well-known/openid-configuration`,a=d.REDOCLY_OAUTH_USE_INTROSPECT?`${l}/api/sso/oidc/introspect`:""}let n="AUTO";s?.sso&&(Array.isArray(s.sso)?n=s.sso.join(","):n=s.sso);let t="";s?.idps&&(Array.isArray(s.idps)?t=s.idps.join(","):t=s.idps);const p={login_hint:"{{ process.env.ORGANIZATION_ID }}",login_type:n,prompt:"login"};t&&(p.login_idps=t);const m={oidc:{title:A,type:"OIDC",configurationUrl:c,clientId:"{{ process.env.OAUTH_CLIENT_ID }}",clientSecret:"{{ process.env.OAUTH_CLIENT_SECRET }}",teamsClaimName:"https://redocly.com/sso/teams",scopes:["openid"],authorizationRequestCustomParams:p,audience:"{{ process.env.ORGANIZATION_ID }}",introspectEndpoint:a}};i.setGlobalConfig({ssoDirect:m})})},async afterRoutesCreated(i){}}}export{S as ssoPlugin};
1
+ import{DEFAULT_SSO_IDP_TITLE as A}from"../../../constants/common.js";import{envConfig as d}from"../../config/env-config.js";import{telemetryTraceStep as I}from"../../../cli/telemetry/helpers/trace-step.js";import{logger as C}from"../../tools/notifiers/logger.js";const O="https://auth.cloud.redocly.com/oidc/.well-known/openid-configuration",_="https://auth.cloud.redocly.com/api/sso/oidc/introspect";async function S(T){return{id:"sso",async processContent(i){await I("build.plugin.sso",async f=>{const o=i.getConfig(),s=o.access;if(f?.setAttribute("config",`{"ssoDirect": ${JSON.stringify(o.ssoDirect||{})}}, {"sso": ${JSON.stringify(s?.sso||{})}}, {"idps": ${JSON.stringify(s?.idps||{})}}`),o.ssoDirect&&typeof o.ssoDirect=="object"&&Object.keys(o.ssoDirect).length!==0)return;const y=!!s?.sso&&(Array.isArray(s.sso)?s.sso.length>0:!0),r=!!s?.idps&&(Array.isArray(s.idps)?s.idps.length>0:!0);if(s?.sso&&Array.isArray(s.sso)&&!s.sso.length&&!r)return;const g=!!(s?.rbac&&typeof s.rbac=="object"&&Object.keys(s.rbac).length!==0),u=s?.requiresLogin;if(!g&&!u)return;y&&r&&C.error("access.sso and access.idps both set \u2014 should have been rejected by config validation",{sso:s.sso,idps:s.idps});let c=O,a=d.REDOCLY_OAUTH_USE_INTROSPECT?_:"";const e=s?.residency;if(e){const l=e.endsWith("/")?e.slice(0,-1):e;c=`${l.replace("app.","auth.")}/oidc/.well-known/openid-configuration`,a=d.REDOCLY_OAUTH_USE_INTROSPECT?`${l}/api/sso/oidc/introspect`:""}let n="AUTO";s?.sso&&(Array.isArray(s.sso)?n=s.sso.join(","):n=s.sso);let t="";s?.idps&&(Array.isArray(s.idps)?t=s.idps.join(","):t=s.idps);const p={login_hint:"{{ process.env.ORGANIZATION_ID }}",login_type:n};t&&(p.login_idps=t);const m={oidc:{title:A,type:"OIDC",configurationUrl:c,clientId:"{{ process.env.OAUTH_CLIENT_ID }}",clientSecret:"{{ process.env.OAUTH_CLIENT_SECRET }}",teamsClaimName:"https://redocly.com/sso/teams",scopes:["openid"],authorizationRequestCustomParams:p,audience:"{{ process.env.ORGANIZATION_ID }}",introspectEndpoint:a}};i.setGlobalConfig({ssoDirect:m})})},async afterRoutesCreated(i){}}}export{S as ssoPlugin};
@@ -2,6 +2,7 @@ import type { Node } from '@markdoc/markdoc';
2
2
  import type { JSX } from 'react';
3
3
  import type { CommonError, GlobalData } from '../types/index.js';
4
4
  import type { MiddlewareDetails, PageRouteInit, PageRouteDetails, RouteDetails, LifecycleContext, ApiRoute, ParseMarkdocOpts, WildcardRedirectsTree, RedirectOptions, McpToolRegistration, McpToolSchema, MarkdocOptions } from './types';
5
+ import type { Skill } from './types/plugins/skills.js';
5
6
  import type { RedoclyConfig, RedirectConfig, CompilationError, PageStaticData } from '@redocly/config';
6
7
  import type { SearchFacet } from '@redocly/theme/core/types';
7
8
  import type { BundledDefinition as OpenApiBundledDefinition } from './plugins/openapi-docs/load-definition.js';
@@ -55,6 +56,7 @@ export declare class Store {
55
56
  browserPlugins: Set<string>;
56
57
  apiRoutesRequestHandlers: Map<string, string>;
57
58
  mcpToolHandlers: Map<string, McpToolRegistration>;
59
+ skills: Map<string, Skill>;
58
60
  serverPropsGetters: Map<string, string>;
59
61
  pagePropsGetters: Map<string, string>;
60
62
  listeners: Map<string, Set<(...args: any[]) => void>>;
@@ -127,6 +129,8 @@ export declare class Store {
127
129
  addApiRoute: (route: ApiRoute) => void;
128
130
  addMcpTools: (importPath: string, tools: McpToolSchema[]) => void;
129
131
  getMcpTools: () => McpToolRegistration[];
132
+ addSkills: (skills: Skill[]) => void;
133
+ getSkills: () => Skill[];
130
134
  addMiddleware: (middleware: MiddlewareDetails) => void;
131
135
  setResourceResponseHeaders: (resourcePath: string, headers: NonNullable<RedoclyConfig['responseHeaders']>[string]) => void;
132
136
  getRouteByFsPath: (relativePath: string) => PageRouteDetails | undefined;