@redocly/realm 0.137.0-next.2 → 0.137.0-next.4

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 (31) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/dist/cli/stats/collectors/openapi/index.js +1 -1
  3. package/dist/client/utils/catalog/get-next-page-param.d.ts +1 -1
  4. package/dist/client/utils/catalog/get-next-page-param.js +1 -1
  5. package/dist/compiled/svgo/svgo-node.js +32 -32
  6. package/dist/server/plugins/api-docs/search/search-indexer.js +1 -1
  7. package/dist/server/plugins/mcp/codemode/tools/manage-credentials.js +2 -2
  8. package/dist/server/plugins/mcp/gateway-mcp/elicitation.d.ts +2 -1
  9. package/dist/server/plugins/mcp/gateway-mcp/elicitation.js +4 -4
  10. package/dist/server/plugins/mcp/gateway-mcp/secret-store.d.ts +8 -0
  11. package/dist/server/plugins/mcp/gateway-mcp/upstream-auth-scheme.d.ts +2 -0
  12. package/dist/server/plugins/mcp/gateway-mcp/upstream-auth-scheme.js +2 -2
  13. package/dist/server/plugins/mcp/handlers/mcp-credentials-handler.js +1 -144
  14. package/dist/server/plugins/mcp/handlers/mcp-credentials-page.d.ts +6 -0
  15. package/dist/server/plugins/mcp/handlers/mcp-credentials-page.js +217 -0
  16. package/dist/server/plugins/mcp/index.js +1 -1
  17. package/dist/server/plugins/mcp/servers/mcp-server.js +3 -3
  18. package/dist/server/plugins/mcp/utils/find-gated-tool-names.d.ts +13 -0
  19. package/dist/server/plugins/mcp/utils/find-gated-tool-names.js +1 -0
  20. package/dist/server/sandbox/constants/sandbox.d.ts +4 -1
  21. package/dist/server/sandbox/constants/sandbox.js +1 -1
  22. package/dist/server/sandbox/sandbox.js +2 -2
  23. package/dist/server/store.d.ts +1 -0
  24. package/dist/server/store.js +1 -1
  25. package/dist/server/types/plugins/common.d.ts +3 -0
  26. package/dist/server/web-server/routes/ask-ai.d.ts +1 -0
  27. package/dist/server/web-server/routes/ask-ai.js +1 -1
  28. package/dist/server/web-server/routes/auth.js +1 -1
  29. package/dist/server/web-server/routes/index.js +1 -1
  30. package/dist/server/web-server/routes/mcp-routes/mcp-server-card.js +1 -1
  31. package/package.json +9 -9
@@ -1 +1 @@
1
- import{fileURLToPath as q}from"node:url";import{dirname as I,join as p}from"node:path";import{logger as l}from"../../tools/notifiers/logger.js";import{reporter as G}from"../../tools/notifiers/reporter.js";import{isDefined as N}from"../../../utils/guards/is-defined.js";import{bundledOpenApiDocumentsToMcpOpenApi as H,bundledGraphqlDocumentsToMcpOpenApi as U,buildGraphqlDescriptions as j,collectIgnoredRoutes as x,filterIgnoredApiDescriptions as _,getCleanedUpApiDescriptions as F,isMcpInRedirects as B}from"./utils.js";import{getMcpMode as V,isMcpElicitationEnabled as J}from"./utils/mode.js";import{getDefaultToolSchemas as W}from"./docs-mcp/tool-schemas.js";import{EXECUTE_TOOL_SCHEMA as X}from"./codemode/tools/execute-schema.js";import{isGatewayCapabilityEnabled as $,isGatewayEligible as k}from"./gateway-mcp/eligibility.js";import{resolveGatewayFetchLimits as z}from"./gateway-mcp/rate-limit/limits.js";import{telemetryTraceStep as K}from"../../../cli/telemetry/helpers/trace-step.js";import{DEFAULT_DOCS_MCP_SERVER_NAME as v,MCP_NOT_ALLOWED_TEMPLATE_ID as Q}from"./constants.js";const y="mcp-docs-server-handler",Y="/mcp",A="mcp-credentials-handler",Z="/mcp/credentials",m=I(q(import.meta.url));async function ee(){let o=!1;return{id:"mcp",async processContent(t,r){o||await K("com.redocly.build.plugin.mcp",async a=>{const n=H(await t.loadApiDocuments(r)),f=U(await t.loadApiDocuments(r)),s=await r.getConfig(),{mcp:i,redirects:d}=s;if(a?.setAttribute("config",JSON.stringify(i||{})),!(i?.hide||i?.docs?.hide)&&(B(d)&&await G.panicOnBuildContentError('The reserved route "/mcp" cannot be used in redirect configuration. Please remove any redirects involving "/mcp" from your redocly.yaml config.'),t.createTemplate(Q,"@redocly/theme/layouts/McpNotAllowed"),!i?.docs?.hide)){l.info("Configuring MCP servers...");const u=p(m,"./handlers/docs-mcp-handler.js"),c=i?.docs?.ignore||[],h=F(_(n,c)),g=!!s?.access?.requiresLogin,D=s?.access?.rbac||{},R=$(i),E=new Map;for(const e of h){const L=e.definition.info?.title||"",w=e.definition.info?.version||"";E.set(e.relativePath,{name:L,description:typeof e.definition.info?.description=="string"?e.definition.info.description:"",version:w,servers:e.definition.servers||[],relativePath:e.relativePath,...R&&k(e.definition.info,e.relativePath,D)?{fetchEligible:!0}:{}})}const M=j(_(f,c)),b=Object.values(s.products||{}).map(e=>e?.name).filter(N),S=s.l10n?.locales?.map(e=>e.code)??r.fs.localeFolders,C=V(),P=J(),{budgetPerExec:T}=z(),O=W({products:b,locales:S,requiresLogin:g,rbac:D,hasGraphqlApis:M.length>0,elicitationEnabled:P});t.addMcpTools(p(m,"./docs-mcp/tools/index.js"),O),C==="code"&&t.addMcpTools(p(m,"./codemode/tools/execute.js"),[X]),await te({actions:t,handlerPath:u,apiDescriptionsMap:Object.fromEntries(E),graphqlDescriptions:M,mcpConfig:i,mcpMode:C,elicitationEnabled:P,gatewayFetchBudgetPerExec:T}),o=!0}})},async afterRoutesCreated(t,r){const a=await r.getConfig(),{mcp:n}=a;t.setGlobalData({mcpData:{docs:{enabled:!n?.hide&&!n?.docs?.hide,name:n?.docs?.name||v}}})}}}var ge=ee;async function te({actions:o,handlerPath:t,apiDescriptionsMap:r,graphqlDescriptions:a,mcpConfig:n,mcpMode:f,elicitationEnabled:s,gatewayFetchBudgetPerExec:i}){if(o.createRequestHandler(y,t),o.addApiRoute({slug:Y,requestHandlerId:y,httpMethod:"all",getStaticData:async(d,u)=>{const c=x(u.getAllRoutes(),n?.docs?.ignore||[]);return c.length>0&&l.verbose(`mcp.docs.ignore: hiding ${c.length} page routes from MCP search`),{props:{config:{apiDescriptionsMap:r,graphqlDescriptions:a,mcpDocsServerName:n?.docs?.name||v,mcpMode:f,elicitationEnabled:s,gatewayFetchBudgetPerExec:i,searchIgnoredRoutes:c},serverOutDir:o.serverOutDir,tools:o.getMcpTools().map(({importPath:h,...g})=>g)}}}}),l.info("Registered Docs MCP Server endpoint at /mcp"),s){const d=p(m,"./handlers/mcp-credentials-handler.js");o.createRequestHandler(A,d),o.addApiRoute({slug:Z,requestHandlerId:A,httpMethod:"all",getStaticData:async()=>({props:{}})}),l.info("Registered MCP credentials endpoint at /mcp/credentials")}}export{A as MCP_CREDENTIALS_HANDLER_ID,y as MCP_DOCS_SERVER_HANDLER_ID,ge as default,ee as mcpServerPlugin};
1
+ import{fileURLToPath as G}from"node:url";import{dirname as q,join as p}from"node:path";import{logger as l}from"../../tools/notifiers/logger.js";import{reporter as I}from"../../tools/notifiers/reporter.js";import{isDefined as N}from"../../../utils/guards/is-defined.js";import{bundledOpenApiDocumentsToMcpOpenApi as H,bundledGraphqlDocumentsToMcpOpenApi as U,buildGraphqlDescriptions as j,collectIgnoredRoutes as x,filterIgnoredApiDescriptions as _,getCleanedUpApiDescriptions as F,isMcpInRedirects as B}from"./utils.js";import{getMcpMode as V,isMcpElicitationEnabled as k}from"./utils/mode.js";import{findGatedToolNames as J}from"./utils/find-gated-tool-names.js";import{getDefaultToolSchemas as W}from"./docs-mcp/tool-schemas.js";import{EXECUTE_TOOL_SCHEMA as X}from"./codemode/tools/execute-schema.js";import{isGatewayCapabilityEnabled as $,isGatewayEligible as z}from"./gateway-mcp/eligibility.js";import{resolveGatewayFetchLimits as K}from"./gateway-mcp/rate-limit/limits.js";import{telemetryTraceStep as Q}from"../../../cli/telemetry/helpers/trace-step.js";import{DEFAULT_DOCS_MCP_SERVER_NAME as v,MCP_NOT_ALLOWED_TEMPLATE_ID as Y}from"./constants.js";const y="mcp-docs-server-handler",Z="/mcp",A="mcp-credentials-handler",ee="/mcp/credentials",m=q(G(import.meta.url));async function te(){let o=!1;return{id:"mcp",async processContent(t,r){o||await Q("com.redocly.build.plugin.mcp",async a=>{const n=H(await t.loadApiDocuments(r)),f=U(await t.loadApiDocuments(r)),s=await r.getConfig(),{mcp:i,redirects:d}=s;if(a?.setAttribute("config",JSON.stringify(i||{})),!(i?.hide||i?.docs?.hide)&&(B(d)&&await I.panicOnBuildContentError('The reserved route "/mcp" cannot be used in redirect configuration. Please remove any redirects involving "/mcp" from your redocly.yaml config.'),t.createTemplate(Y,"@redocly/theme/layouts/McpNotAllowed"),!i?.docs?.hide)){l.info("Configuring MCP servers...");const g=p(m,"./handlers/docs-mcp-handler.js"),c=i?.docs?.ignore||[],h=F(_(n,c)),u=!!s?.access?.requiresLogin,D=s?.access?.rbac||{},R=$(i),M=new Map;for(const e of h){const L=e.definition.info?.title||"",w=e.definition.info?.version||"";M.set(e.relativePath,{name:L,description:typeof e.definition.info?.description=="string"?e.definition.info.description:"",version:w,servers:e.definition.servers||[],relativePath:e.relativePath,...R&&z(e.definition.info,e.relativePath,D)?{fetchEligible:!0}:{}})}const E=j(_(f,c)),T=Object.values(s.products||{}).map(e=>e?.name).filter(N),b=s.l10n?.locales?.map(e=>e.code)??r.fs.localeFolders,C=V(),P=k(),{budgetPerExec:S}=K(),O=W({products:T,locales:b,requiresLogin:u,rbac:D,hasGraphqlApis:E.length>0,elicitationEnabled:P});t.addMcpTools(p(m,"./docs-mcp/tools/index.js"),O),C==="code"&&t.addMcpTools(p(m,"./codemode/tools/execute.js"),[X]),await oe({actions:t,handlerPath:g,apiDescriptionsMap:Object.fromEntries(M),graphqlDescriptions:E,mcpConfig:i,mcpMode:C,elicitationEnabled:P,gatewayFetchBudgetPerExec:S}),o=!0}})},async afterRoutesCreated(t,r){const a=await r.getConfig(),{mcp:n}=a;t.markGatedMcpTools(await J(t.getMcpTools())),t.setGlobalData({mcpData:{docs:{enabled:!n?.hide&&!n?.docs?.hide,name:n?.docs?.name||v}}})}}}var De=te;async function oe({actions:o,handlerPath:t,apiDescriptionsMap:r,graphqlDescriptions:a,mcpConfig:n,mcpMode:f,elicitationEnabled:s,gatewayFetchBudgetPerExec:i}){if(o.createRequestHandler(y,t),o.addApiRoute({slug:Z,requestHandlerId:y,httpMethod:"all",getStaticData:async(d,g)=>{const c=x(g.getAllRoutes(),n?.docs?.ignore||[]);return c.length>0&&l.verbose(`mcp.docs.ignore: hiding ${c.length} page routes from MCP search`),{props:{config:{apiDescriptionsMap:r,graphqlDescriptions:a,mcpDocsServerName:n?.docs?.name||v,mcpMode:f,elicitationEnabled:s,gatewayFetchBudgetPerExec:i,searchIgnoredRoutes:c},serverOutDir:o.serverOutDir,tools:o.getMcpTools().map(({importPath:h,...u})=>u)}}}}),l.info("Registered Docs MCP Server endpoint at /mcp"),s){const d=p(m,"./handlers/mcp-credentials-handler.js");o.createRequestHandler(A,d),o.addApiRoute({slug:ee,requestHandlerId:A,httpMethod:"all",getStaticData:async()=>({props:{}})}),l.info("Registered MCP credentials endpoint at /mcp/credentials")}}export{A as MCP_CREDENTIALS_HANDLER_ID,y as MCP_DOCS_SERVER_HANDLER_ID,De as default,te as mcpServerPlugin};
@@ -1,5 +1,5 @@
1
- import{McpServer as L,WebStandardStreamableHTTPServerTransport as j}from"@modelcontextprotocol/server";import{logger as S}from"../../../tools/notifiers/logger.js";import{mcpToolWorkers as D,MCP_TOOL_WORKER_KEY as K}from"../../../workers/mcp-tool-worker-pool.js";import{SSE_KEEPALIVE_INTERVAL_MS as x}from"../constants.js";import{secretStore as F}from"../gateway-mcp/secret-store.js";import{readAuthRequiredMeta as H,stripAuthRequiredMeta as M,upstreamAuthKey as b}from"../gateway-mcp/upstream-auth.js";import{buildCredentialsToolHint as P,buildSchemeChoiceHint as v,buildUpstreamCredentialFallback as T,buildUpstreamCredentialInputRequired as $,isCredentialElicitationDeclined as N,schemeLabelOf as B}from"../gateway-mcp/elicitation.js";import{DEFAULT_SCHEME_NAME as q,resolveUpstreamApiAuth as G,schemeChoicesOf as W}from"../gateway-mcp/upstream-auth-scheme.js";import{MISSING_SESSION_HANDLE_MESSAGE as V,resolveSecretSubject as Y}from"../gateway-mcp/subject.js";const J=6e4,E=new Map;function Q(e,t){const r=E.get(e);if(r)return r;const n=t().finally(()=>{E.delete(e)});return E.set(e,n),n}function fe(e,t){const r=new L({name:e,version:new Date().toISOString().slice(0,10)},{capabilities:{logging:{}},...t?{instructions:t}:{}}),n=new j({keepAliveMs:x,sessionIdGenerator:void 0});return{server:r,transport:n}}async function he(e,t){return await e.connect(t),{server:e,transport:t,cleanup:async()=>{t.close()}}}function ge(e,t){return r=>async(n,s)=>{S.info(`MCP tool called: ${r}`);const i=e.elicitationEnabled??!1,a=s.mcpReq.envelope!==void 0,u=i?Y(e.user,n.sessionHandle):{kind:"anonymous"},o=u.kind==="resolved"?u.subject:void 0;if(u.kind==="invalid")return{content:[{type:"text",text:u.message}],isError:!0};const p=async()=>{if(!(!i||!o||!t?.getKv))try{const m=await t.getKv();return await F.getSecretsForSubject(m,o)}catch(m){S.warn(`gateway mcp secret store error, continuing unauthenticated: ${String(m)}`);return}};let c;const l=async()=>{c=await p();const m={toolName:r,args:n,context:{...e,upstreamSecrets:c},extra:ie(s)};return await D.exec(K,[m],{timeout:J})};let d=await l();const f=H(d);if(f.length>0&&i&&!o)return M({...d,content:[{type:"text",text:V}],isError:!0});if(f.length>0&&i&&o&&t?.getKv){S.info(`gateway mcp credentials prompt: era=${a?"2026-07-28":"2025"} clientElicitation=${Z(t)}`);const m=await t.getKv(),h=await z(e,f),k=f.map(U=>ee(U,h)),y=te(c),g=a?await re(e,s,k,o,m,y,h,t):await ne(e,k,o,m,y,h);switch(g.kind){case"inputRequired":return g.result;case"retry":d=await l();break;case"prependText":d={...d,content:[{type:"text",text:g.text},...d.content],isError:!0};break;case"none":break}}return M(d)}}function X(e){return!!e?.getClientCapabilities()?.elicitation?.url}function Z(e){const t=e?.getClientCapabilities()?.elicitation;if(!t)return"none";const r=[t.form?"form":void 0,t.url?"url":void 0].filter(Boolean);return r.length>0?r.join("+"):"none"}async function z(e,t){const r=new Map;for(const n of new Set(t.map(s=>s.api)))r.set(n,await G(e,n));return r}function ee(e,t){const r=t.get(e.api);if(!r)return{...e,scheme:e.scheme??q};const n=e.scheme&&r.schemes[e.scheme]?e.scheme:r.defaultScheme;return{...e,scheme:n}}function te(e){const t=new Set;for(const[r,n]of Object.entries(e??{}))for(const s of Object.keys(n))t.add(b({api:r,scheme:s}));return t}function I(e,t){return e.status===401&&!t.has(b(e))}function R(e,t){const r=t.get(e.api);return r&&e.scheme?B(r.schemes,e.scheme):void 0}function A(e,t){const r=t.get(e.api);return!r||!e.schemes?.length?[]:W(r,e.schemes)}function _(e,t){return A(e,t).length>1}function w(e,t){return e.map(r=>P(r,R(r,t))).join(`
1
+ import{McpServer as L,WebStandardStreamableHTTPServerTransport as j}from"@modelcontextprotocol/server";import{logger as S}from"../../../tools/notifiers/logger.js";import{mcpToolWorkers as D,MCP_TOOL_WORKER_KEY as K}from"../../../workers/mcp-tool-worker-pool.js";import{SSE_KEEPALIVE_INTERVAL_MS as x}from"../constants.js";import{secretStore as F}from"../gateway-mcp/secret-store.js";import{readAuthRequiredMeta as H,stripAuthRequiredMeta as M,upstreamAuthKey as b}from"../gateway-mcp/upstream-auth.js";import{buildCredentialsToolHint as P,buildSchemeChoiceHint as v,buildUpstreamCredentialFallback as T,buildUpstreamCredentialInputRequired as $,isCredentialElicitationDeclined as N,schemeLabelOf as B}from"../gateway-mcp/elicitation.js";import{DEFAULT_SCHEME_NAME as q,resolveUpstreamApiAuth as G,schemeChoicesOf as W}from"../gateway-mcp/upstream-auth-scheme.js";import{MISSING_SESSION_HANDLE_MESSAGE as V,resolveSecretSubject as Y}from"../gateway-mcp/subject.js";const J=6e4,E=new Map;function Q(e,t){const r=E.get(e);if(r)return r;const n=t().finally(()=>{E.delete(e)});return E.set(e,n),n}function fe(e,t){const r=new L({name:e,version:new Date().toISOString().slice(0,10)},{capabilities:{logging:{}},...t?{instructions:t}:{}}),n=new j({keepAliveMs:x,sessionIdGenerator:void 0});return{server:r,transport:n}}async function he(e,t){return await e.connect(t),{server:e,transport:t,cleanup:async()=>{t.close()}}}function ge(e,t){return r=>async(n,s)=>{S.info(`MCP tool called: ${r}`);const i=e.elicitationEnabled??!1,c=s.mcpReq.envelope!==void 0,o=i?Y(e.user,n.sessionHandle):{kind:"anonymous"},a=o.kind==="resolved"?o.subject:void 0;if(o.kind==="invalid")return{content:[{type:"text",text:o.message}],isError:!0};const p=async()=>{if(!(!i||!a||!t?.getKv))try{const m=await t.getKv();return await F.getSecretsForSubject(m,a)}catch(m){S.warn(`gateway mcp secret store error, continuing unauthenticated: ${String(m)}`);return}};let u;const l=async()=>{u=await p();const m={toolName:r,args:n,context:{...e,upstreamSecrets:u},extra:ie(s)};return await D.exec(K,[m],{timeout:J})};let d=await l();const f=H(d);if(f.length>0&&i&&!a)return M({...d,content:[{type:"text",text:V}],isError:!0});if(f.length>0&&i&&a&&t?.getKv){S.info(`gateway mcp credentials prompt: era=${c?"2026-07-28":"2025"} clientElicitation=${Z(t)}`);const m=await t.getKv(),h=await z(e,f),k=f.map(U=>ee(U,h)),y=te(u),g=c?await re(e,s,k,a,m,y,h,t):await ne(e,k,a,m,y,h);switch(g.kind){case"inputRequired":return g.result;case"retry":d=await l();break;case"prependText":d={...d,content:[{type:"text",text:g.text},...d.content],isError:!0};break;case"none":break}}return M(d)}}function X(e){return!!e?.getClientCapabilities()?.elicitation?.url}function Z(e){const t=e?.getClientCapabilities()?.elicitation;if(!t)return"none";const r=[t.form?"form":void 0,t.url?"url":void 0].filter(Boolean);return r.length>0?r.join("+"):"none"}async function z(e,t){const r=new Map;for(const n of new Set(t.map(s=>s.api)))r.set(n,await G(e,n));return r}function ee(e,t){const r=t.get(e.api);if(!r)return{...e,scheme:e.scheme??q};const n=e.scheme&&r.schemes[e.scheme]?e.scheme:r.defaultScheme;return{...e,scheme:n}}function te(e){const t=new Set;for(const[r,n]of Object.entries(e??{}))for(const s of Object.keys(n))t.add(b({api:r,scheme:s}));return t}function A(e,t){return e.status===401&&!t.has(b(e))}function I(e,t){const r=t.get(e.api);return r&&e.scheme?B(r.schemes,e.scheme):void 0}function R(e,t){const r=t.get(e.api);return!r||!e.schemes?.length?[]:W(r,e.schemes)}function _(e,t){return R(e,t).length>1}function w(e,t){return e.map(r=>P(r,I(r,t))).join(`
2
2
 
3
- `)}function C(e,t,r){const n=e.map(s=>v(s,A(s,r)));return t.length>0&&n.push(w(t,r)),n.join(`
3
+ `)}function C(e,t,r){const n=e.map(s=>v(s,R(s,r)));return t.length>0&&n.push(w(t,r)),n.join(`
4
4
 
5
- `)}function O(e,t,r,n,s){const[i,...a]=t,u=s.get(i.api),o=i.scheme??u?.defaultScheme??q,p=R(i,s);return{subject:r,baseUrl:e.baseUrl??"",target:{api:i.api,name:i.name,scheme:o,...p?{schemeLabel:p}:{}},auth:u?.schemes[o]??{kind:"raw"},kv:n,hasExistingSecret:!1,reason:{kind:"authFailure",status:i.status,otherApiNames:a.map(c=>c.name)}}}async function re(e,t,r,n,s,i,a,u){if(N(t.mcpReq.inputResponses))return{kind:"none"};const o=r.filter(l=>I(l,i));if(o.length===0)return{kind:"prependText",text:w(r,a)};const p=o.filter(l=>_(l,a));if(p.length>0){const l=r.filter(d=>!p.includes(d));return{kind:"prependText",text:C(p,l,a)}}const c=O(e,o,n,s,a);return X(u)?{kind:"inputRequired",result:await $(c)}:{kind:"prependText",text:await T(c)}}async function ne(e,t,r,n,s,i){const a=t.filter(c=>I(c,s));if(a.length===0)return{kind:"prependText",text:w(t,i)};const u=a.filter(c=>_(c,i));if(u.length>0){const c=t.filter(l=>!u.includes(l));return{kind:"prependText",text:C(u,c,i)}}const o=O(e,a,r,n,i);return{kind:"prependText",text:await Q(`${r}:${b(a[0])}`,()=>T(o))}}function ie(e){return{sessionId:e.sessionId,authInfo:e.http?.authInfo,requestId:e.mcpReq.id,_meta:e.mcpReq._meta}}function Se(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,mcpMode:e.mcpMode,elicitationEnabled:e.elicitationEnabled,gatewayFetchBudgetPerExec:e.gatewayFetchBudgetPerExec,clientIp:e.clientIp,upstreamSecrets:e.upstreamSecrets,searchIgnoredRoutes:e.searchIgnoredRoutes}}export{he as connectMcpServer,fe as createMcpServer,ge as createToolDispatch,Se as extractSerializableContext};
5
+ `)}function O(e,t,r,n,s){const[i,...c]=t,o=s.get(i.api),a=i.scheme??o?.defaultScheme??q,p=I(i,s);return{subject:r,baseUrl:e.baseUrl??"",target:{api:i.api,name:i.name,scheme:a,...p?{schemeLabel:p}:{}},auth:o?.schemes[a]??{kind:"raw"},...o?{apiAuth:o}:{},kv:n,hasExistingSecret:!1,reason:{kind:"authFailure",status:i.status,otherApiNames:c.map(u=>u.name)}}}async function re(e,t,r,n,s,i,c,o){if(N(t.mcpReq.inputResponses))return{kind:"none"};const a=r.filter(l=>A(l,i));if(a.length===0)return{kind:"prependText",text:w(r,c)};const p=a.filter(l=>_(l,c));if(p.length>0){const l=r.filter(d=>!p.includes(d));return{kind:"prependText",text:C(p,l,c)}}const u=O(e,a,n,s,c);return X(o)?{kind:"inputRequired",result:await $(u)}:{kind:"prependText",text:await T(u)}}async function ne(e,t,r,n,s,i){const c=t.filter(u=>A(u,s));if(c.length===0)return{kind:"prependText",text:w(t,i)};const o=c.filter(u=>_(u,i));if(o.length>0){const u=t.filter(l=>!o.includes(l));return{kind:"prependText",text:C(o,u,i)}}const a=O(e,c,r,n,i);return{kind:"prependText",text:await Q(`${r}:${b(c[0])}`,()=>T(a))}}function ie(e){return{sessionId:e.sessionId,authInfo:e.http?.authInfo,requestId:e.mcpReq.id,_meta:e.mcpReq._meta}}function Se(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,mcpMode:e.mcpMode,elicitationEnabled:e.elicitationEnabled,gatewayFetchBudgetPerExec:e.gatewayFetchBudgetPerExec,clientIp:e.clientIp,upstreamSecrets:e.upstreamSecrets,searchIgnoredRoutes:e.searchIgnoredRoutes}}export{he as connectMcpServer,fe as createMcpServer,ge as createToolDispatch,Se as extractSerializableContext};
@@ -0,0 +1,13 @@
1
+ import type { McpToolRegistration } from '../../../types';
2
+ import type { McpToolHandler, McpToolHandlerExtended } from '../types.js';
3
+ type ToolHandlers = Record<string, McpToolHandler | McpToolHandlerExtended>;
4
+ type ToolModuleLoader = (importPath: string) => Promise<{
5
+ default: ToolHandlers;
6
+ }>;
7
+ /**
8
+ * Names of the tools whose handler declares an `isAvailable` predicate — the ones the live
9
+ * server enables per caller. Includes every tool of a handler module that fails to load.
10
+ */
11
+ export declare function findGatedToolNames(registrations: McpToolRegistration[], load?: ToolModuleLoader): Promise<string[]>;
12
+ export {};
13
+ //# sourceMappingURL=find-gated-tool-names.d.ts.map
@@ -0,0 +1 @@
1
+ const r=t=>import(t);async function f(t,o=r){const n=[];for(const{name:a,importPath:i}of t){const e=await c(i,o);(!e||s(e[a]))&&n.push(a)}return n}async function c(t,o){try{const{default:n}=await o(t);return n}catch{return}}function s(t){return typeof t=="object"&&typeof t.isAvailable=="function"}export{f as findGatedToolNames};
@@ -1,6 +1,9 @@
1
1
  export declare const DEFAULT_TIMEOUT_MS = 10000;
2
- export declare const MEMORY_LIMIT_BYTES: number;
3
2
  export declare const DEFAULT_MAX_LIFETIME_MS: number;
4
3
  export declare const MAX_STACK_SIZE_BYTES: number;
5
4
  export declare const MAX_RESULT_BYTES: number;
5
+ export declare const MAX_ALLOCATION_BYTES: number;
6
+ export declare const MAX_TOTAL_MEMORY_BYTES: number;
7
+ export declare const INITIAL_TOTAL_MEMORY_BYTES: number;
8
+ export declare const WASM_PAGE_BYTES: number;
6
9
  //# sourceMappingURL=sandbox.d.ts.map
@@ -1 +1 @@
1
- const _=1e4,E=67108864,T=6e5,o=262144,t=1048576;export{T as DEFAULT_MAX_LIFETIME_MS,_ as DEFAULT_TIMEOUT_MS,t as MAX_RESULT_BYTES,o as MAX_STACK_SIZE_BYTES,E as MEMORY_LIMIT_BYTES};
1
+ const _=1e4,T=6e5,o=262144,t=1048576,E=67108864,A=134217728,M=16777216,S=65536;export{T as DEFAULT_MAX_LIFETIME_MS,_ as DEFAULT_TIMEOUT_MS,M as INITIAL_TOTAL_MEMORY_BYTES,E as MAX_ALLOCATION_BYTES,t as MAX_RESULT_BYTES,o as MAX_STACK_SIZE_BYTES,A as MAX_TOTAL_MEMORY_BYTES,S as WASM_PAGE_BYTES};
@@ -1,3 +1,3 @@
1
- import p from"@jitl/quickjs-singlefile-mjs-release-sync";import{newQuickJSWASMModuleFromVariant as S}from"quickjs-emscripten-core";import{createExecutionBudget as E}from"./utils/execution-budget.js";import{DEFAULT_MAX_LIFETIME_MS as g,DEFAULT_TIMEOUT_MS as T,MAX_RESULT_BYTES as _,MAX_STACK_SIZE_BYTES as y,MEMORY_LIMIT_BYTES as h}from"./constants/sandbox.js";let d;function x(){return d??=S(p)}async function R(t,c={}){const{timeoutMs:l=T,maxLifetimeMs:m=g,setupContext:f}=c,e=(await x()).newContext();let n,s,r;try{e.runtime.setMemoryLimit(h),e.runtime.setMaxStackSize(y),n=E(e,l,m),s=f?.(e,n)??void 0,r=e.unwrapResult(e.evalCode(`(async () => {
1
+ import _ from"@jitl/quickjs-singlefile-mjs-release-sync";import{newQuickJSWASMModuleFromVariant as E,newVariant as S}from"quickjs-emscripten-core";import{createExecutionBudget as A}from"./utils/execution-budget.js";import{DEFAULT_MAX_LIFETIME_MS as p,DEFAULT_TIMEOUT_MS as y,INITIAL_TOTAL_MEMORY_BYTES as g,MAX_ALLOCATION_BYTES as w,MAX_RESULT_BYTES as x,MAX_STACK_SIZE_BYTES as I,MAX_TOTAL_MEMORY_BYTES as L,WASM_PAGE_BYTES as d}from"./constants/sandbox.js";let l;function h(){return l??=E(S(_,{wasmMemory:new WebAssembly.Memory({initial:g/d,maximum:L/d})}))}async function k(t,c={}){const{timeoutMs:m=y,maxLifetimeMs:M=p,setupContext:f}=c,e=(await h()).newContext();let n,s,r;try{e.runtime.setMemoryLimit(w),e.runtime.setMaxStackSize(I),n=A(e,m,M),s=f?.(e,n)??void 0,r=e.unwrapResult(e.evalCode(`(async () => {
2
2
  ${t}
3
- })()`));const i=e.resolvePromise(r);e.runtime.executePendingJobs();const M=await Promise.race([i,n.aborted]),u=e.unwrapResult(M),o=e.dump(u);if(u.dispose(),o===void 0)return{error:"The code returned undefined. Check that a top-level `return` yields a defined value: return the value directly (not `return;` or `return undefined`), read an existing field (a wrong field name is undefined), and do not wrap the logic in a function or IIFE whose return value is discarded."};const a=JSON.stringify(o??null).length;return a>_?{error:`Result is too large (${a} bytes); return less data`}:{result:o}}catch(i){return{error:v(i)}}finally{n?.dispose(),r?.dispose();try{s?.()}catch{}try{e.dispose()}catch{d=void 0}}}function v(t){return t instanceof Error?t.message:String(t)}export{R as runInSandbox};
3
+ })()`));const i=e.resolvePromise(r);e.runtime.executePendingJobs();const T=await Promise.race([i,n.aborted]),u=e.unwrapResult(T),o=e.dump(u);if(u.dispose(),o===void 0)return{error:"The code returned undefined. Check that a top-level `return` yields a defined value: return the value directly (not `return;` or `return undefined`), read an existing field (a wrong field name is undefined), and do not wrap the logic in a function or IIFE whose return value is discarded."};const a=JSON.stringify(o??null).length;return a>x?{error:`Result is too large (${a} bytes); return less data`}:{result:o}}catch(i){return{error:b(i)}}finally{n?.dispose(),r?.dispose();try{s?.()}catch{}try{e.dispose()}catch{l=void 0}}}function b(t){return t instanceof Error?t.message:String(t)}export{k as runInSandbox};
@@ -125,6 +125,7 @@ export declare class Store {
125
125
  addApiRoute: (route: ApiRoute) => void;
126
126
  addMcpTools: (importPath: string, tools: McpToolSchema[]) => void;
127
127
  getMcpTools: () => McpToolRegistration[];
128
+ markGatedMcpTools: (toolNames: string[]) => void;
128
129
  addSkills: (skills: Skill[]) => void;
129
130
  getSkills: () => Skill[];
130
131
  addMiddleware: (middleware: MiddlewareDetails) => void;
@@ -1 +1 @@
1
- import C from"@markdoc/markdoc";import{getPathnameForLocale as A}from"@redocly/theme/core/utils";import{DEFAULT_LOCALE_PLACEHOLDER as g}from"../constants/common.js";import{DEFAULT_TITLE as T}from"./constants/common.js";import{GATED_MARKDOC_TAGS as R}from"./constants/entitlements.js";import{MARKDOC_PARTIALS_DATA_KEY as m,MARKDOC_PARTIALS_DEPS_KEY as O}from"./constants/common.js";import{isObject as M}from"../utils/guards/is-object.js";import{mapObject as k}from"../utils/object/map-object.js";import{getValueDeep as D}from"../utils/object/get-value-deep.js";import{removeTrailingSlash as L}from"../utils/url/remove-trailing-slash.js";import{normalizeRouteSlug as h}from"../utils/path/normalize-route-slug.js";import{isLocalLink as y}from"../utils/path/is-local-link.js";import{reporter as S}from"./tools/notifiers/reporter.js";import{logger as u}from"./tools/notifiers/logger.js";import{sha1 as H}from"./utils/crypto/sha1.js";import{envConfig as _,setEnv as B}from"./config/env-config.js";import{KvService as F}from"./persistence/kv/services/kv-service.js";import{SandboxContext as G}from"./utils/sandbox-context.js";import{writeSharedData as I}from"./utils/index.js";import{renderComponents as N}from"./ssr/render.js";import{readStaticData as V,writeStaticData as q}from"./utils/static-data.js";import{parseAndResolveMarkdoc as j}from"./plugins/markdown/compiler.js";import{getMarkdocOptions as U}from"./plugins/markdown/markdoc/markdoc-options.js";import{EntitlementsProvider as w}from"./entitlements/entitlements-provider.js";import{isL10nPath as K}from"./fs/utils/is-l10n-path.js";import{resolveGlobMapValue as x}from"./utils/globs.js";import{replaceEnvVariablesDeep as J}from"./utils/envs/replace-env-variables-deep.js";import{findRedirect as W}from"./utils/redirects/find-redirect.js";import{followRedirectChain as $}from"./utils/redirects/follow-redirect-chain.js";import{addWildcardRedirectToTree as z}from"./utils/redirects/add-wildcard-redirect-to-tree.js";import{telemetryTraceStep as Y}from"../cli/telemetry/helpers/trace-step.js";const v={routesBySlug:"map",apiRoutes:"object",middleware:"object",routesByFsPath:"map",routesSharedData:"map",globalData:"object",config:"object",ssr:"object",searchFacets:"map",routesPartials:"map",mcpToolHandlers:"map",skills:"map"},E="PLAN_GATES",Z=["OAUTH_CLIENT_ID","OAUTH_CLIENT_SECRET","ORGANIZATION_SLUG","ORGANIZATION_ID","ORG_ID"],Me="userDefinedApiFunctions";class P{routesBySlug=new Map;replacedEnvVars={};unsetEnvVars=new Set;lifecycleContext;newRoutes=[];#t={};routesByFsPath=new Map;apiRoutes=[];middleware=[];routesSharedData=new Map;sharedDataDeps=new Map;sharedDataMarkdocComponents=new Map;routesDynamicComponents=new Map;routesPartials=new Map;ssr={preBodyTags:[],postBodyTags:[],headTags:[]};searchFacets=new Map;searchEngine;templates=new Map;browserPlugins=new Set;apiRoutesRequestHandlers=new Map;mcpToolHandlers=new Map;skills=new Map;serverPropsGetters=new Map;pagePropsGetters=new Map;listeners=new Map;globalData={};#o=void 0;config={configFilePath:"",redirects:{},wildcardRedirectsTree:{},access:{rbac:{},requiresLogin:!1},directoryPermissions:{},devLogin:!1,ssoDirect:{}};#i;serverMode;serverOutDir;outdir;buildRevision=0;hasSitemap=!1;compilationErrors=[];#n;userCodeReady;#s=Promise.resolve();#c;#l=Promise.resolve();#d;#u=Promise.resolve();#h;#e=!1;#r=0;#f=0;#a=new Map;constructor({outdir:e,contentDir:t,serverMode:s=!1,serverOutDir:a}){this.#i=t,this.outdir=e,this.serverMode=s,this.serverOutDir=a,G.register({serverOutDir:a,readRoots:[e,process.cwd()]}),this.userCodeReady=new Promise(r=>{this.#n=r})}on(e,t){const s=this.listeners.get(e);s?s.add(t):this.listeners.set(e,new Set([t]))}queueEvent=(e,t,...s)=>{this.#a.set(e+String(t),[e,t,...s])};runListeners=(e,t,...s)=>{for(const a of this.listeners.get(e)||new Set)t?a(t,...s):a(...s)};startPluginsRun(){this.clear(),this.#s=new Promise(e=>{this.#c=e})}waitForPluginsLifecycle(){return Promise.all([this.#s,this.#l,this.#u])}waitForPluginsRun(){return this.#s}finishPluginsRun(){this.#c?.();for(const e of this.#a.values())this.runListeners(...e);this.#a.clear()}startEsbuildRun(){this.#l=new Promise(e=>{this.#d=e})}finishEsbuildRun(){this.#d?.()}markRequestHandlersEntryOutdated(){this.#r++,!this.#e&&(this.#e=!0,this.#u=new Promise(e=>{this.#h=e}))}noteRequestHandlersEntryBuildStart(){this.#f=this.#r}markRequestHandlersEntryUpdated(){this.#e&&(this.#r>this.#f||(this.#e=!1,this.#h?.()))}get contentDir(){if(this.serverMode)throw new Error("contentDir should not be used in server mode");return this.#i}markUserCodeReady(){this.#n?.(!0)}async reloadMarkdocOptions(){await Y("com.redocly.build.reloadMarkdocOptions",async()=>{const e=w.instance(),t=await U(this.serverOutDir),s=Object.fromEntries(Object.entries(t.tags).filter(([a])=>R[a]!=null?e.canAccessFeature(R[a]):!0));this.#o={...t,tags:s}})}get markdocOptions(){return{...this.#o,partials:this.getGlobalConfig(m),themeConfig:this.config.markdown}}setGlobalData=e=>{const t=this.globalData,s={...this.globalData,...e};this.globalData=s,JSON.stringify(s)!==JSON.stringify(t)&&this.queueEvent("global-data-updated",void 0,s)};getGlobalData=()=>this.globalData;getKv=async()=>F.getInstance({baseDbDir:this.serverOutDir});parseMarkdoc=async({input:e,context:t,deps:s,resource:a})=>{const{data:{info:r,ast:o},compoundHash:l}=await j(e,this.markdocOptions,{actions:this,context:t},a);for(const i of r.sharedDataDeps||[]){for(const n of s?.routeSlugs||[])this.addRouteSharedData(n,i,i);for(const n of s?.sharedDataIds||[]){const c=this.sharedDataDeps.get(n)||new Set;c.add(i),this.sharedDataDeps.set(n,c)}}for(const i of r.dynamicMarkdocComponents||[]){for(const n of s?.routeSlugs||[]){const c=this.routesDynamicComponents.get(n)||new Set;c.add(i),this.routesDynamicComponents.set(n,c)}for(const n of s?.sharedDataIds||[]){const c=this.sharedDataMarkdocComponents.get(n)||new Set;c.add(i),this.sharedDataMarkdocComponents.set(n,c)}}if(s?.routeSlugs&&r.partials?.length)for(const i of s.routeSlugs){const n=this.routesPartials.get(i)||[];for(const c of r.partials)n.includes(c)||n.push(c);this.routesPartials.set(i,n)}return{info:r,ast:o,compoundHash:l}};async loadApiDocuments(e){return(await e.cache.load(".","api-docs")).data}setSearchEngine(e){this.searchEngine=e}setSearchFacets=e=>{this.searchFacets=e};setGlobalConfig=e=>{const t=Object.keys(e);for(const o of t)for(const l in this.replacedEnvVars)if(l===o||l.startsWith(`${o}:`)){const i=l.split(":"),{error:n,value:c}=D(e,i);(n||c!==this.replacedEnvVars[l].replaced)&&delete this.replacedEnvVars[l]}const{resolvedObj:s,unsetEnvVars:a,replacedValues:r}=J(e);for(const o of a)this.unsetEnvVars.add(o);Object.assign(this.replacedEnvVars,r),Object.assign(this.config,s)};getConfig=()=>this.config;getGlobalConfig=e=>this.config[e];getSearchFacets=()=>this.searchFacets;addRedirect=(e,t,s={})=>{if(!w.instance().canAccessFeature("redirects")&&e!=="/")return;this.config.redirects||(this.config.redirects={});const o=h(e).toLowerCase();this.config.redirects[o]=t;const{trackOriginalSource:l=!0}=s,i=this.getGlobalConfig("originalRedirectSources");l&&Array.isArray(i)&&!i.includes(o)&&i.push(o),o.endsWith("*")&&z(this.config.wildcardRedirectsTree,o)};getRedirect=e=>{const t=h(e).toLowerCase(),s=W(t,this.config.redirects,this.config.wildcardRedirectsTree);if(!s)return null;if(y(s.to)){const a=h(s.to).toLowerCase();if(!a.endsWith("*")&&$(a,[t],this.config.redirects,this.config.wildcardRedirectsTree).type==="cycle")return null}return{to:s.to,type:s.type}};createSharedData=async(e,t,s)=>{if(s&&this.#t[e]===s)return e;const a=JSON.stringify(t),r=s??H(a);return this.#t[e]===r||(this.#t[e]=r,await I(e,a,this.outdir),this.queueEvent("shared-data-updated",e)),e};addRouteSharedData=(e,t,s)=>{const a=L(e),r=this.routesSharedData.get(a)||{};r[t]=s,this.routesSharedData.set(a,r),u.verbose(`Adding shared data to ${e}, ${t}, ${s}`)};getRouteSharedDataByFsPath=e=>{const t=this.routesByFsPath.get(e);return t?this.routesSharedData.get(t)||{}:{}};getPartialsForRoute=e=>{const t=this.getGlobalConfig(m)||{},s=this.routesPartials.get(e);if(!s||s.length===0)return{};const a=this.getGlobalConfig(O)||{},r=new Set(s),o=Array.from(s);for(let i=0;i<o.length;i++){const n=o[i],c=a[n]?.partials??[];for(const d of c)r.has(d)||(r.add(d),o.push(d))}const l={};for(const i of r)t[i]&&(l[i]=t[i]);return l};addRoute=e=>{const s={...x(e.fsPath,this.config.metadataGlobs),...e.metadata||{}};this.newRoutes.push({...e,metadata:s}),u.verbose("Created route %s",e.slug)};addRouteSharedDataToAllLocales=(e,t,s)=>{const a=[g,...this.lifecycleContext?.fs.localeFolders||[]].map(r=>({code:r,name:r}));for(const r of a){const o=A(e,g,r.code,a);this.addRouteSharedData(o,t,s)}};addApiRoute=e=>{this.apiRoutes.push(e),u.verbose("Created API route %s",e.slug)};addMcpTools=(e,t)=>{for(const s of t)this.mcpToolHandlers.set(s.name,{...s,importPath:e}),u.verbose("Created MCP tool %s",s.name)};getMcpTools=()=>Array.from(this.mcpToolHandlers.values());addSkills=e=>{for(const t of e)this.skills.set(t.slug,t),u.verbose("Discovered skill %s",t.slug)};getSkills=()=>Array.from(this.skills.values());addMiddleware=e=>{this.middleware.push(e),u.verbose("Created middleware %s",e.id)};setResourceResponseHeaders=(e,t)=>{this.config.responseHeaders||(this.config.responseHeaders={});const s=new Set(t.map(o=>o.name.toLowerCase())),r=[...(this.config.responseHeaders[e]??[]).filter(o=>!s.has(o.name.toLowerCase())),...t];this.config.responseHeaders[e]=r};getRouteByFsPath=e=>{const t=this.routesByFsPath.get(e);return t?this.getRouteBySlug(t):void 0};getRouteBySlug=(e,t={})=>{const{followRedirect:s=!0}=t,a=this.getRedirect(e);return s&&a?this.routesBySlug.get(h(a.to)):this.routesBySlug.get(e)};hasRouteOrRedirectBySlug=e=>{if(this.routesBySlug.has(e))return!0;const t=this.getRedirect(e);if(!t)return!1;if(!y(t.to))return!0;const s=h(t.to);return this.routesBySlug.has(s)};getRoutesByTemplateId=e=>this.newRoutes.filter(t=>t.templateId===e);getAllRoutesForLocale=(e=g)=>{const t=Array.from(this.routesBySlug.values()),s=e.toLowerCase();return t.filter(a=>e===g?!K(a.fsPath):a.slug.startsWith(`/${s}`))};getAllRoutes=()=>Array.from(this.routesBySlug.values());getAllApiRoutes=()=>this.apiRoutes;getAllMiddleware=()=>this.middleware;getTemplate=e=>this.templates.get(e);getRequestHandler=e=>this.apiRoutesRequestHandlers.get(e);createTemplate=(e,t)=>(this.templates.set(e,t),e);addBrowserPlugin=e=>{this.browserPlugins.add(e)};createRequestHandler=(e,t)=>(this.apiRoutesRequestHandlers.set(e,t),e);clearRequestHandlersByPrefix=e=>{for(const t of this.apiRoutesRequestHandlers.keys())t.startsWith(e)&&this.apiRoutesRequestHandlers.delete(t)};registerServerPropsGetter=(e,t)=>(this.serverPropsGetters.set(e,t),e);registerPagePropsGetter=(e,t)=>{this.pagePropsGetters.set(e,t)};async writeRouteStaticData(e,t){const s=await this.resolveRouteStaticData(e,t,!1);s&&q(e.slug,s,this.outdir)}async resolveRouteStaticData(e,t,s){if(this.serverMode)return V(e.slug,this.outdir);const a={...this,contentDir:this.contentDir,parseMarkdoc:({input:c,context:d,resource:f})=>this.parseMarkdoc({input:c,context:d,deps:{routeSlugs:[e.slug]},resource:f})},r=await e.getStaticData?.(e,a)||{},o=new Set(this.routesDynamicComponents.get(e.slug)),l=this.routesSharedData.get(e.slug)||{};for(const c of Object.values(l)){const d=this.sharedDataMarkdocComponents.get(c);d&&d.forEach(p=>o.add(p));const f=this.sharedDataDeps.get(c);f&&f.forEach(p=>this.addRouteSharedData(e.slug,p,p))}const i=this.getGlobalConfig("seo"),n=r?.frontmatter||{};return{...r,frontmatter:{...n,seo:{...n?.seo,title:n?.seo?.title||await e.getNavText?.()}},props:{...r.props,dynamicMarkdocComponents:Array.from(o),metadata:{...r?.props?.metadata,...e.metadata},seo:{title:T,...i,...r.props?.seo},compilationErrors:this.compilationErrors},lastModified:s||!e.fsPath?null:await this.lifecycleContext?.fs.getLastModified(e.fsPath)}}addSsrComponents(e,t){if(!e?.length)return;const s=typeof e[0]=="string"?e.join(""):N(e);s&&(t==="head"?this.ssr.headTags.push(s):t==="preBody"?this.ssr.preBodyTags.push(s):this.ssr.postBodyTags.push(s))}clear=()=>{this.routesByFsPath.clear(),this.templates.clear(),this.newRoutes=[],this.routesBySlug.clear(),this.apiRoutes=[],this.middleware=[],this.routesSharedData.clear(),this.sharedDataDeps.clear(),this.sharedDataMarkdocComponents.clear(),this.routesDynamicComponents.clear(),this.routesPartials.clear(),this.skills.clear(),this.config.redirects={},this.config.wildcardRedirectsTree={},this.config.directoryPermissions={},this.ssr={preBodyTags:[],postBodyTags:[],headTags:[]}};async toJson(){const e=[];for(const[s,a]of Object.entries(v))switch(a){case"map":const r=Array.from(this[s].entries());e.push([s,r]);break;case"object":s==="config"&&e.push([s,await this.getConfigWithEnvPlaceholders()]),e.push([s,this[s]]);break;default:throw new Error("Invalid format")}const t=Object.fromEntries(e);return t[E]=_.PLAN_GATES,t}static fromJson(e,t){const s=new P(t);for(const[r,o]of Object.entries(v))switch(o){case"map":s[r]=new Map(e[r]);break;case"object":if(r==="config"){s.setGlobalConfig(e[r]);break}s[r]=e[r];break;default:throw new Error("Invalid format")}s.config[m]=Q(s.config[m]||{});const a=e[E];return a&&B("PLAN_GATES",a),s}async getConfigWithEnvPlaceholders(){const e=JSON.parse(JSON.stringify(this.config));for(const t in this.replacedEnvVars){const{original:s}=this.replacedEnvVars[t],a=t.split(":"),r=a.pop(),{error:o,value:l}=D(e,a);if(o||!M(l)&&!Array.isArray(l)){await S.panicOnBuild(`Failed to replace env var with env name for ${t}`);continue}l[r]=s}return e}async reportUnsetEnvVars(){if(this.unsetEnvVars.size===0)return;const e=Array.from(this.unsetEnvVars).filter(s=>!Z.includes(s));if(e.length===0)return;const t=`Failed to resolve config. The following environment variables are not set: ${e.join(", ")}`;await S.panicOnBuildContentError(t)}}function Q(b){return k(b,e=>C.Ast.fromJSON(JSON.stringify(e)))}export{P as Store,Me as USER_DEFINED_API_FUNCTIONS_COUNTER_KEY};
1
+ import C from"@markdoc/markdoc";import{getPathnameForLocale as A}from"@redocly/theme/core/utils";import{DEFAULT_LOCALE_PLACEHOLDER as g}from"../constants/common.js";import{DEFAULT_TITLE as T}from"./constants/common.js";import{GATED_MARKDOC_TAGS as R}from"./constants/entitlements.js";import{MARKDOC_PARTIALS_DATA_KEY as m,MARKDOC_PARTIALS_DEPS_KEY as O}from"./constants/common.js";import{isObject as M}from"../utils/guards/is-object.js";import{mapObject as k}from"../utils/object/map-object.js";import{getValueDeep as y}from"../utils/object/get-value-deep.js";import{removeTrailingSlash as L}from"../utils/url/remove-trailing-slash.js";import{normalizeRouteSlug as h}from"../utils/path/normalize-route-slug.js";import{isLocalLink as D}from"../utils/path/is-local-link.js";import{reporter as S}from"./tools/notifiers/reporter.js";import{logger as u}from"./tools/notifiers/logger.js";import{sha1 as H}from"./utils/crypto/sha1.js";import{envConfig as G,setEnv as _}from"./config/env-config.js";import{KvService as B}from"./persistence/kv/services/kv-service.js";import{SandboxContext as F}from"./utils/sandbox-context.js";import{writeSharedData as I}from"./utils/index.js";import{renderComponents as N}from"./ssr/render.js";import{readStaticData as V,writeStaticData as q}from"./utils/static-data.js";import{parseAndResolveMarkdoc as j}from"./plugins/markdown/compiler.js";import{getMarkdocOptions as U}from"./plugins/markdown/markdoc/markdoc-options.js";import{EntitlementsProvider as w}from"./entitlements/entitlements-provider.js";import{isL10nPath as K}from"./fs/utils/is-l10n-path.js";import{resolveGlobMapValue as x}from"./utils/globs.js";import{replaceEnvVariablesDeep as J}from"./utils/envs/replace-env-variables-deep.js";import{findRedirect as W}from"./utils/redirects/find-redirect.js";import{followRedirectChain as $}from"./utils/redirects/follow-redirect-chain.js";import{addWildcardRedirectToTree as z}from"./utils/redirects/add-wildcard-redirect-to-tree.js";import{telemetryTraceStep as Y}from"../cli/telemetry/helpers/trace-step.js";const v={routesBySlug:"map",apiRoutes:"object",middleware:"object",routesByFsPath:"map",routesSharedData:"map",globalData:"object",config:"object",ssr:"object",searchFacets:"map",routesPartials:"map",mcpToolHandlers:"map",skills:"map"},E="PLAN_GATES",Z=["OAUTH_CLIENT_ID","OAUTH_CLIENT_SECRET","ORGANIZATION_SLUG","ORGANIZATION_ID","ORG_ID"],Me="userDefinedApiFunctions";class P{routesBySlug=new Map;replacedEnvVars={};unsetEnvVars=new Set;lifecycleContext;newRoutes=[];#t={};routesByFsPath=new Map;apiRoutes=[];middleware=[];routesSharedData=new Map;sharedDataDeps=new Map;sharedDataMarkdocComponents=new Map;routesDynamicComponents=new Map;routesPartials=new Map;ssr={preBodyTags:[],postBodyTags:[],headTags:[]};searchFacets=new Map;searchEngine;templates=new Map;browserPlugins=new Set;apiRoutesRequestHandlers=new Map;mcpToolHandlers=new Map;skills=new Map;serverPropsGetters=new Map;pagePropsGetters=new Map;listeners=new Map;globalData={};#o=void 0;config={configFilePath:"",redirects:{},wildcardRedirectsTree:{},access:{rbac:{},requiresLogin:!1},directoryPermissions:{},devLogin:!1,ssoDirect:{}};#i;serverMode;serverOutDir;outdir;buildRevision=0;hasSitemap=!1;compilationErrors=[];#n;userCodeReady;#s=Promise.resolve();#c;#l=Promise.resolve();#d;#u=Promise.resolve();#h;#e=!1;#r=0;#f=0;#a=new Map;constructor({outdir:e,contentDir:t,serverMode:s=!1,serverOutDir:a}){this.#i=t,this.outdir=e,this.serverMode=s,this.serverOutDir=a,F.register({serverOutDir:a,readRoots:[e,process.cwd()]}),this.userCodeReady=new Promise(r=>{this.#n=r})}on(e,t){const s=this.listeners.get(e);s?s.add(t):this.listeners.set(e,new Set([t]))}queueEvent=(e,t,...s)=>{this.#a.set(e+String(t),[e,t,...s])};runListeners=(e,t,...s)=>{for(const a of this.listeners.get(e)||new Set)t?a(t,...s):a(...s)};startPluginsRun(){this.clear(),this.#s=new Promise(e=>{this.#c=e})}waitForPluginsLifecycle(){return Promise.all([this.#s,this.#l,this.#u])}waitForPluginsRun(){return this.#s}finishPluginsRun(){this.#c?.();for(const e of this.#a.values())this.runListeners(...e);this.#a.clear()}startEsbuildRun(){this.#l=new Promise(e=>{this.#d=e})}finishEsbuildRun(){this.#d?.()}markRequestHandlersEntryOutdated(){this.#r++,!this.#e&&(this.#e=!0,this.#u=new Promise(e=>{this.#h=e}))}noteRequestHandlersEntryBuildStart(){this.#f=this.#r}markRequestHandlersEntryUpdated(){this.#e&&(this.#r>this.#f||(this.#e=!1,this.#h?.()))}get contentDir(){if(this.serverMode)throw new Error("contentDir should not be used in server mode");return this.#i}markUserCodeReady(){this.#n?.(!0)}async reloadMarkdocOptions(){await Y("com.redocly.build.reloadMarkdocOptions",async()=>{const e=w.instance(),t=await U(this.serverOutDir),s=Object.fromEntries(Object.entries(t.tags).filter(([a])=>R[a]!=null?e.canAccessFeature(R[a]):!0));this.#o={...t,tags:s}})}get markdocOptions(){return{...this.#o,partials:this.getGlobalConfig(m),themeConfig:this.config.markdown}}setGlobalData=e=>{const t=this.globalData,s={...this.globalData,...e};this.globalData=s,JSON.stringify(s)!==JSON.stringify(t)&&this.queueEvent("global-data-updated",void 0,s)};getGlobalData=()=>this.globalData;getKv=async()=>B.getInstance({baseDbDir:this.serverOutDir});parseMarkdoc=async({input:e,context:t,deps:s,resource:a})=>{const{data:{info:r,ast:o},compoundHash:l}=await j(e,this.markdocOptions,{actions:this,context:t},a);for(const i of r.sharedDataDeps||[]){for(const n of s?.routeSlugs||[])this.addRouteSharedData(n,i,i);for(const n of s?.sharedDataIds||[]){const c=this.sharedDataDeps.get(n)||new Set;c.add(i),this.sharedDataDeps.set(n,c)}}for(const i of r.dynamicMarkdocComponents||[]){for(const n of s?.routeSlugs||[]){const c=this.routesDynamicComponents.get(n)||new Set;c.add(i),this.routesDynamicComponents.set(n,c)}for(const n of s?.sharedDataIds||[]){const c=this.sharedDataMarkdocComponents.get(n)||new Set;c.add(i),this.sharedDataMarkdocComponents.set(n,c)}}if(s?.routeSlugs&&r.partials?.length)for(const i of s.routeSlugs){const n=this.routesPartials.get(i)||[];for(const c of r.partials)n.includes(c)||n.push(c);this.routesPartials.set(i,n)}return{info:r,ast:o,compoundHash:l}};async loadApiDocuments(e){return(await e.cache.load(".","api-docs")).data}setSearchEngine(e){this.searchEngine=e}setSearchFacets=e=>{this.searchFacets=e};setGlobalConfig=e=>{const t=Object.keys(e);for(const o of t)for(const l in this.replacedEnvVars)if(l===o||l.startsWith(`${o}:`)){const i=l.split(":"),{error:n,value:c}=y(e,i);(n||c!==this.replacedEnvVars[l].replaced)&&delete this.replacedEnvVars[l]}const{resolvedObj:s,unsetEnvVars:a,replacedValues:r}=J(e);for(const o of a)this.unsetEnvVars.add(o);Object.assign(this.replacedEnvVars,r),Object.assign(this.config,s)};getConfig=()=>this.config;getGlobalConfig=e=>this.config[e];getSearchFacets=()=>this.searchFacets;addRedirect=(e,t,s={})=>{if(!w.instance().canAccessFeature("redirects")&&e!=="/")return;this.config.redirects||(this.config.redirects={});const o=h(e).toLowerCase();this.config.redirects[o]=t;const{trackOriginalSource:l=!0}=s,i=this.getGlobalConfig("originalRedirectSources");l&&Array.isArray(i)&&!i.includes(o)&&i.push(o),o.endsWith("*")&&z(this.config.wildcardRedirectsTree,o)};getRedirect=e=>{const t=h(e).toLowerCase(),s=W(t,this.config.redirects,this.config.wildcardRedirectsTree);if(!s)return null;if(D(s.to)){const a=h(s.to).toLowerCase();if(!a.endsWith("*")&&$(a,[t],this.config.redirects,this.config.wildcardRedirectsTree).type==="cycle")return null}return{to:s.to,type:s.type}};createSharedData=async(e,t,s)=>{if(s&&this.#t[e]===s)return e;const a=JSON.stringify(t),r=s??H(a);return this.#t[e]===r||(this.#t[e]=r,await I(e,a,this.outdir),this.queueEvent("shared-data-updated",e)),e};addRouteSharedData=(e,t,s)=>{const a=L(e),r=this.routesSharedData.get(a)||{};r[t]=s,this.routesSharedData.set(a,r),u.verbose(`Adding shared data to ${e}, ${t}, ${s}`)};getRouteSharedDataByFsPath=e=>{const t=this.routesByFsPath.get(e);return t?this.routesSharedData.get(t)||{}:{}};getPartialsForRoute=e=>{const t=this.getGlobalConfig(m)||{},s=this.routesPartials.get(e);if(!s||s.length===0)return{};const a=this.getGlobalConfig(O)||{},r=new Set(s),o=Array.from(s);for(let i=0;i<o.length;i++){const n=o[i],c=a[n]?.partials??[];for(const d of c)r.has(d)||(r.add(d),o.push(d))}const l={};for(const i of r)t[i]&&(l[i]=t[i]);return l};addRoute=e=>{const s={...x(e.fsPath,this.config.metadataGlobs),...e.metadata||{}};this.newRoutes.push({...e,metadata:s}),u.verbose("Created route %s",e.slug)};addRouteSharedDataToAllLocales=(e,t,s)=>{const a=[g,...this.lifecycleContext?.fs.localeFolders||[]].map(r=>({code:r,name:r}));for(const r of a){const o=A(e,g,r.code,a);this.addRouteSharedData(o,t,s)}};addApiRoute=e=>{this.apiRoutes.push(e),u.verbose("Created API route %s",e.slug)};addMcpTools=(e,t)=>{for(const s of t)this.mcpToolHandlers.set(s.name,{...s,importPath:e}),u.verbose("Created MCP tool %s",s.name)};getMcpTools=()=>Array.from(this.mcpToolHandlers.values());markGatedMcpTools=e=>{for(const t of e){const s=this.mcpToolHandlers.get(t);s&&this.mcpToolHandlers.set(t,{...s,hasAvailabilityGate:!0})}};addSkills=e=>{for(const t of e)this.skills.set(t.slug,t),u.verbose("Discovered skill %s",t.slug)};getSkills=()=>Array.from(this.skills.values());addMiddleware=e=>{this.middleware.push(e),u.verbose("Created middleware %s",e.id)};setResourceResponseHeaders=(e,t)=>{this.config.responseHeaders||(this.config.responseHeaders={});const s=new Set(t.map(o=>o.name.toLowerCase())),r=[...(this.config.responseHeaders[e]??[]).filter(o=>!s.has(o.name.toLowerCase())),...t];this.config.responseHeaders[e]=r};getRouteByFsPath=e=>{const t=this.routesByFsPath.get(e);return t?this.getRouteBySlug(t):void 0};getRouteBySlug=(e,t={})=>{const{followRedirect:s=!0}=t,a=this.getRedirect(e);return s&&a?this.routesBySlug.get(h(a.to)):this.routesBySlug.get(e)};hasRouteOrRedirectBySlug=e=>{if(this.routesBySlug.has(e))return!0;const t=this.getRedirect(e);if(!t)return!1;if(!D(t.to))return!0;const s=h(t.to);return this.routesBySlug.has(s)};getRoutesByTemplateId=e=>this.newRoutes.filter(t=>t.templateId===e);getAllRoutesForLocale=(e=g)=>{const t=Array.from(this.routesBySlug.values()),s=e.toLowerCase();return t.filter(a=>e===g?!K(a.fsPath):a.slug.startsWith(`/${s}`))};getAllRoutes=()=>Array.from(this.routesBySlug.values());getAllApiRoutes=()=>this.apiRoutes;getAllMiddleware=()=>this.middleware;getTemplate=e=>this.templates.get(e);getRequestHandler=e=>this.apiRoutesRequestHandlers.get(e);createTemplate=(e,t)=>(this.templates.set(e,t),e);addBrowserPlugin=e=>{this.browserPlugins.add(e)};createRequestHandler=(e,t)=>(this.apiRoutesRequestHandlers.set(e,t),e);clearRequestHandlersByPrefix=e=>{for(const t of this.apiRoutesRequestHandlers.keys())t.startsWith(e)&&this.apiRoutesRequestHandlers.delete(t)};registerServerPropsGetter=(e,t)=>(this.serverPropsGetters.set(e,t),e);registerPagePropsGetter=(e,t)=>{this.pagePropsGetters.set(e,t)};async writeRouteStaticData(e,t){const s=await this.resolveRouteStaticData(e,t,!1);s&&q(e.slug,s,this.outdir)}async resolveRouteStaticData(e,t,s){if(this.serverMode)return V(e.slug,this.outdir);const a={...this,contentDir:this.contentDir,parseMarkdoc:({input:c,context:d,resource:f})=>this.parseMarkdoc({input:c,context:d,deps:{routeSlugs:[e.slug]},resource:f})},r=await e.getStaticData?.(e,a)||{},o=new Set(this.routesDynamicComponents.get(e.slug)),l=this.routesSharedData.get(e.slug)||{};for(const c of Object.values(l)){const d=this.sharedDataMarkdocComponents.get(c);d&&d.forEach(p=>o.add(p));const f=this.sharedDataDeps.get(c);f&&f.forEach(p=>this.addRouteSharedData(e.slug,p,p))}const i=this.getGlobalConfig("seo"),n=r?.frontmatter||{};return{...r,frontmatter:{...n,seo:{...n?.seo,title:n?.seo?.title||await e.getNavText?.()}},props:{...r.props,dynamicMarkdocComponents:Array.from(o),metadata:{...r?.props?.metadata,...e.metadata},seo:{title:T,...i,...r.props?.seo},compilationErrors:this.compilationErrors},lastModified:s||!e.fsPath?null:await this.lifecycleContext?.fs.getLastModified(e.fsPath)}}addSsrComponents(e,t){if(!e?.length)return;const s=typeof e[0]=="string"?e.join(""):N(e);s&&(t==="head"?this.ssr.headTags.push(s):t==="preBody"?this.ssr.preBodyTags.push(s):this.ssr.postBodyTags.push(s))}clear=()=>{this.routesByFsPath.clear(),this.templates.clear(),this.newRoutes=[],this.routesBySlug.clear(),this.apiRoutes=[],this.middleware=[],this.routesSharedData.clear(),this.sharedDataDeps.clear(),this.sharedDataMarkdocComponents.clear(),this.routesDynamicComponents.clear(),this.routesPartials.clear(),this.skills.clear(),this.config.redirects={},this.config.wildcardRedirectsTree={},this.config.directoryPermissions={},this.ssr={preBodyTags:[],postBodyTags:[],headTags:[]}};async toJson(){const e=[];for(const[s,a]of Object.entries(v))switch(a){case"map":const r=Array.from(this[s].entries());e.push([s,r]);break;case"object":s==="config"&&e.push([s,await this.getConfigWithEnvPlaceholders()]),e.push([s,this[s]]);break;default:throw new Error("Invalid format")}const t=Object.fromEntries(e);return t[E]=G.PLAN_GATES,t}static fromJson(e,t){const s=new P(t);for(const[r,o]of Object.entries(v))switch(o){case"map":s[r]=new Map(e[r]);break;case"object":if(r==="config"){s.setGlobalConfig(e[r]);break}s[r]=e[r];break;default:throw new Error("Invalid format")}s.config[m]=Q(s.config[m]||{});const a=e[E];return a&&_("PLAN_GATES",a),s}async getConfigWithEnvPlaceholders(){const e=JSON.parse(JSON.stringify(this.config));for(const t in this.replacedEnvVars){const{original:s}=this.replacedEnvVars[t],a=t.split(":"),r=a.pop(),{error:o,value:l}=y(e,a);if(o||!M(l)&&!Array.isArray(l)){await S.panicOnBuild(`Failed to replace env var with env name for ${t}`);continue}l[r]=s}return e}async reportUnsetEnvVars(){if(this.unsetEnvVars.size===0)return;const e=Array.from(this.unsetEnvVars).filter(s=>!Z.includes(s));if(e.length===0)return;const t=`Failed to resolve config. The following environment variables are not set: ${e.join(", ")}`;await S.panicOnBuildContentError(t)}}function Q(b){return k(b,e=>C.Ast.fromJSON(JSON.stringify(e)))}export{P as Store,Me as USER_DEFINED_API_FUNCTIONS_COUNTER_KEY};
@@ -130,6 +130,7 @@ export type McpToolSchema = {
130
130
  };
131
131
  export type McpToolRegistration = McpToolSchema & {
132
132
  importPath: string;
133
+ hasAvailabilityGate?: boolean;
133
134
  };
134
135
  export type MiddlewareDetails = {
135
136
  id: string;
@@ -208,6 +209,8 @@ export type AfterRoutesCreatedActions = {
208
209
  addApiRoute: (route: ApiRoute) => void;
209
210
  addSkills: (skills: Skill[]) => void;
210
211
  getSkills: () => Skill[];
212
+ getMcpTools: () => McpToolRegistration[];
213
+ markGatedMcpTools: (toolNames: string[]) => void;
211
214
  loadApiDocuments(context: LifecycleContext): Promise<BundledApiDefinition[]>;
212
215
  getRouteSharedDataByFsPath: (routeFsPath: string) => Record<string, string> | undefined;
213
216
  getKv: () => Promise<KvService>;
@@ -1,5 +1,6 @@
1
1
  import type { Handler } from 'hono';
2
2
  import type { Store } from '../../store';
3
+ export declare const AI_ASSISTANT_IDENTITY_STATUS_HEADER = "x-redocly-ai-assistant-identity-status";
3
4
  export declare function askAiHandler(store: Store): Handler;
4
5
  /**
5
6
  * Ticket submissions from the embedded assistant. Proxied for the portal's own org/project so the
@@ -1 +1 @@
1
- import{RbacFeatures as c}from"../../../constants/common.js";import{ASK_AI_API_URL as u,AI_ASSISTANT_ESCALATIONS_API_URL as A,AI_ASSISTANT_FEEDBACK_API_URL as p}from"../../constants/common.js";import{fetchWithClient as f}from"../../utils/fetch-with-client.js";import{canAccessFeature as h}from"../../utils/rbac.js";import{handleUnauthorizedApiRequest as d}from"../utils.js";import{isAiSearchEnabled as l}from"../../plugins/search/utils.js";function m(e,n){const r=n.getConfig();return l(r)?h(c.AI_SEARCH,e.get("auth"),r.access?.rbac,r.access?.requiresLogin)?null:d(e):e.newResponse(null,403,{})}function S(e){const n=e.get("auth"),r={"Content-Type":"application/json"},t=[];n.claims?.authCookie&&t.push(`authorization=${n.claims.authCookie}`),n.idpAccessToken&&t.push(`accessToken=${n.idpAccessToken}`),t.length>0&&(r.Cookie=t.join("; "));const i=e.req.header("Authorization");return i&&(r.Authorization=i),r}const g="x-redocly-ai-assistant-origin";function _(e){const n=e.req.header("Origin");if(n)return n;const r=e.req.header("Referer");if(r)try{return new URL(r).origin}catch{}return new URL(e.req.url).origin}function s({store:e,apiUrl:n,successContentType:r="application/json"}){return async t=>{if(!n)return t.newResponse(null,404);const i=m(t,e);if(i)return i;const a=S(t);a[g]=_(t);const o=await f(n,{method:"POST",body:JSON.stringify(await t.req.json()),headers:a});return t.newResponse(o.body,o.status,{"Content-Type":o.ok?r:"application/json"})}}function E(e){return s({store:e,apiUrl:u,successContentType:"text/event-stream"})}function b(e){return s({store:e,apiUrl:A})}function w(e){return s({store:e,apiUrl:p})}export{b as aiAssistantEscalationsHandler,w as aiAssistantFeedbackHandler,E as askAiHandler};
1
+ import{RbacFeatures as d}from"../../../constants/common.js";import{ASK_AI_API_URL as h,AI_ASSISTANT_ESCALATIONS_API_URL as p,AI_ASSISTANT_FEEDBACK_API_URL as f}from"../../constants/common.js";import{fetchWithClient as l}from"../../utils/fetch-with-client.js";import{canAccessFeature as S}from"../../utils/rbac.js";import{handleUnauthorizedApiRequest as T}from"../utils.js";import{isAiSearchEnabled as I}from"../../plugins/search/utils.js";function _(e,t){const n=t.getConfig();return I(n)?S(d.AI_SEARCH,e.get("auth"),n.access?.rbac,n.access?.requiresLogin)?null:T(e):e.newResponse(null,403,{})}function g(e){const t=e.get("auth"),n={"Content-Type":"application/json"},r=[];t.claims?.authCookie&&r.push(`authorization=${t.claims.authCookie}`),t.idpAccessToken&&r.push(`accessToken=${t.idpAccessToken}`),r.length>0&&(n.Cookie=r.join("; "));const i=e.req.header("Authorization");i&&(n.Authorization=i);const s=e.req.header(u);return s&&(n[u]=s),n}const m="x-redocly-ai-assistant-origin",u="x-redocly-ai-assistant-identity",A="x-redocly-ai-assistant-identity-status";function y(e){const t=e.req.header("Origin");if(t)return t;const n=e.req.header("Referer");if(n)try{return new URL(n).origin}catch{}return new URL(e.req.url).origin}function a({store:e,apiUrl:t,successContentType:n="application/json"}){return async r=>{if(!t)return r.newResponse(null,404);const i=_(r,e);if(i)return i;const s=g(r);s[m]=y(r);const o=await l(t,{method:"POST",body:JSON.stringify(await r.req.json()),headers:s}),c=o.headers.get(A);return r.newResponse(o.body,o.status,{"Content-Type":o.ok?n:"application/json",...c?{[A]:c}:{}})}}function U(e){return a({store:e,apiUrl:h,successContentType:"text/event-stream"})}function b(e){return a({store:e,apiUrl:p})}function q(e){return a({store:e,apiUrl:f})}export{A as AI_ASSISTANT_IDENTITY_STATUS_HEADER,b as aiAssistantEscalationsHandler,q as aiAssistantFeedbackHandler,U as askAiHandler};
@@ -1 +1 @@
1
- import{setCookie as L,deleteCookie as U}from"hono/cookie";import{AuthProviderType as Q,DEFAULT_TEAM_CLAIM_NAME as x}from"@redocly/config";import{withPathPrefix as O,getPathPrefix as C}from"@redocly/theme/core/utils";import{compareURIs as ee}from"../../../utils/url/compare-uris.js";import{ensureArray as N}from"../../../utils/array/ensure-array.js";import{ALTERNATIVE_AUD_CLAIM_NAME as H,JWT_SECRET_KEY as v,ORG_SLUG as re,ORG_ID as oe}from"../../constants/common.js";import{AUTH_SEGMENT as ne,ServerRoutes as P}from"../../../constants/api.js";import{AuthCookieNames as _,DEFAULT_COOKIE_EXPIRATION as B}from"../../../constants/common.js";import{sanitizeRedirectPathname as K}from"../../../utils/url/sanitize-redirect-pathname.js";import{telemetry as T}from"../../telemetry/index.js";import{envConfig as j}from"../../config/env-config.js";import{fetchWithClient as te}from"../../utils/fetch-with-client.js";import{clearAuthCookies as z,clearPostLogoutCookie as $,hasPostLogoutCookie as se,setPostLogoutCookie as G}from"../../utils/cookie.js";import{getAuthProviderLoginParams as ie,isOidcProviderConfig as b,isSaml2ProviderConfig as ae,oidcExchangeCodeForToken as de,buildLoginUrl as ce,decodeSamlResponse as le,extractUserClaims as ue,parseSamlResponse as pe,parseOidcState as ge,verifySAMLResponse as fe,getUsernameFromPayload as me,buildOidcLogoutUrl as he,getOidcMetadata as J,getRedoclyTokenPayload as Re,isRedoclySso as _e,rewritePreviewAuthRedirectUri as Ie,parsePreviewBranch as Z,buildOidcLoginUrl as Ce,createMcpSessionResource as D}from"../auth.js";import*as k from"../jwt/jwt.js";import{AlgorithmTypes as E}from"../jwt/types.js";import{handleErrorPageRender as Ae}from"../utils.js";import{encodeBase64URL as we}from"../jwt/encode.js";import{resolveUiLocalesForIdpLogin as Le}from"./helpers/resolve-ui-locales-for-idp-login.js";async function Ke(s){if(j.isProductionEnv)return s.newResponse(null,404,{});const{password:e,...r}=await s.req.json(),a=await k.sign({...r,name:r.username||r.email||"Unknown"},v,E.HS256);return L(s,_.AUTHORIZATION,a,{path:C()||"/",httpOnly:!0,secure:!0,sameSite:"none"}),$(s),s.newResponse(null,200,{})}function je(){return async s=>{const e=s.get("logger"),r=encodeURIComponent(s.req.query("message")||"");e.error(`Login error: ${r}`);const a=`${P.LOGIN}/?error=${encodeURIComponent(r)}`;return s.newResponse(null,301,{Location:a})}}function V(s){if(!s||!s.includes(P.MCP_CALLBACK))return null;try{const e=s.split("/"),r=e[e.length-1];if(r){const a=Buffer.from(r,"base64url").toString("utf-8");return JSON.parse(a).mcpSessionId||null}}catch{}return null}function ze(s){return async e=>{const r=e.get("logger"),a=s.getConfig().ssoDirect,o=ge(e.req.query("state")),m=o.idpId,t=o.source==="mcp"||o.redirectTo&&typeof o.redirectTo=="string"&&o.redirectTo.includes(P.MCP_CALLBACK),l=t?V(typeof o.redirectTo=="string"?o.redirectTo:void 0):null,i=a?.[m];if(!b(i))return r.error("OIDC login error: missing OIDC provider config"),e.text("Forbidden",403);const d=await J(m,i);if(a&&!d.token_endpoint){const p="Invalid OIDC configuration: token_endpoint is required";return r.error(`OIDC login error: ${p}`),e.text(p,500)}try{const p=d.token_endpoint,u=e.req.query("code"),h=e.req.query("error");if(h)return t&&T.sendMcpAuthorizationFailedMessage([{...D(l),error:`OIDC error: ${h}`,error_details:e.req.query("error_description")||null}]),Ae(e,s,{slug:"/"},403,"403OIDC");if(!u){const I="Code is expected but not present";return r.error(`OIDC login error: ${I}`),t&&T.sendMcpAuthorizationFailedMessage([{...D(l),error:I,error_details:null}]),new Response(`Forbidden: ${I}`,{status:403})}const w=typeof o.redirectUri=="string"?o.redirectUri:new URL(O(P.OIDC_CALLBACK),e.req.url).toString(),R=e.get("cookies")?.code_verifier,c=await de(p,u,w,i,{...i.tokenRequestCustomParams,...R?{code_verifier:R}:{}});if(c.error)return r.error(`Error from OIDC provider: "${c.error}"`),t&&T.sendMcpAuthorizationFailedMessage([{...D(l),error:`Token exchange error: ${c.error}`,error_details:c.error_description||null}]),e.text(`Forbidden: ${c.error_description||c.error}`,403);if(!c?.id_token){const I="No id_token, please, add openid to scopes";return r.error(`OIDC login error: ${I}`),t&&T.sendMcpAuthorizationFailedMessage([{...D(l),error:I,error_details:null}]),new Response(`Forbidden: ${I}`,{status:403})}const{payload:f,header:M}=k.decode(c.id_token),n=M.alg===E.RS256;if(i.audience?.length&&![...N(f.aud||[]),...N(f[H]||[])].some(y=>i.audience?.includes(y))){const y="No valid audience found in id_token";return r.error(`OIDC login error: ${y}`),t&&T.sendMcpAuthorizationFailedMessage([{...D(l),error:y,error_details:null}]),new Response(`Forbidden: ${y}`)}const g=n?c.id_token:await k.sign({...f,idpId:m},v,E.HS256);me(f)||r.warn("To display your username, the required 'email' or 'full_profile' scope must be added to the identity provider configuration");const S=i?.tokenExpirationTime?Date.now()+i.tokenExpirationTime*1e3:f.exp*1e3||Date.now()+B*1e3;if(i.introspectEndpoint){const I=await fetch(i.introspectEndpoint,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({access_token:c.access_token})});if(I.ok){const q=(await I.json()).ext?.federatedIdentity;q&&(L(e,_.FEDERATED_ACCESS_TOKEN,q.access_token||"",{path:C()||"/",httpOnly:!1,expires:new Date(S)}),L(e,_.FEDERATED_ID_TOKEN,q.id_token||"",{path:C()||"/",httpOnly:!1,expires:new Date(S)}))}else r.warn(`OIDC introspect error: ${I.statusText}`)}if(L(e,_.AUTHORIZATION,g,{path:C()||"/",httpOnly:!0,expires:new Date(S)}),g!==c.id_token&&L(e,_.IDP_ID_TOKEN,c.id_token||"",{path:C()||"/",httpOnly:!0,expires:new Date(S)}),L(e,_.IDP_ACCESS_TOKEN,c.access_token||"",{path:C()||"/",httpOnly:!0,expires:new Date(S)}),c.refresh_token?L(e,_.IDP_REFRESH_TOKEN,c.refresh_token,{path:C()||"/",httpOnly:!0,expires:new Date(S)}):U(e,_.IDP_REFRESH_TOKEN,{path:C()||"/"}),U(e,"code_verifier",{path:C()||"/"}),$(e),t&&o.redirectTo&&typeof o.redirectTo=="string"&&o.redirectTo.includes(P.MCP_CALLBACK)){const y=`${e.req.url.split("?")[0].replace(P.OIDC_CALLBACK,"")}${o.redirectTo}`;return e.newResponse(null,302,{Location:y})}const W=typeof o.redirectTo=="string"?o.redirectTo:void 0;let X=K(new URL(W||"/",e.req.url).pathname);const Y=e.newResponse(null,302,{Location:X});return r.updateContext({email:f.email,subject:f.sub}),r.info("OIDC login successful"),Y}catch(p){const u=p instanceof Error?p.message:String(p),h=p instanceof Error?p.stack:String(p);if(r.error(`OIDC login error: ${u}`),t&&T.sendMcpAuthorizationFailedMessage([{...D(l),error:u,error_details:h}]),p.error==="access_denied")return r.info("Access denied"),e.text("Forbidden",403)}const A="Something went wrong";return r.error(`OIDC login error: ${A}`),t&&T.sendMcpAuthorizationFailedMessage([{...D(l),error:A,error_details:null}]),e.text(A,500)}}function Ge(s){return async e=>{const r=e.get("logger"),o=e.get("auth").claims?.idpId,t=s.getConfig().ssoDirect?.[o];if(e.req.method==="POST")return b(t)||(z(e),G(e)),r.info("Logout successful"),e.newResponse(null,200,{});let l;if(b(t)){const i=(await J(o,t)).end_session_endpoint;if(i){const d=new URL(e.req.url),A=e.req.header("x-forwarded-proto")||d.protocol.slice(0,-1)||"https",p=e.req.header("x-forwarded-host")||d.host,u=`${A}://${p}`,h=Z(u),w=h?we(JSON.stringify({branch:Z(u)})):void 0,R=h?`${Ie(u)}/${ne}/logout`:`${u}${O(P.POST_LOGOUT)}`;l=he(i,R,e.get("cookies")?.[_.IDP_ID_TOKEN]||e.get("cookies")?.[_.AUTHORIZATION]||"",w)}}return r.info("Logout successful"),z(e),G(e),e.newResponse(null,302,{Location:l||O("/")})}}function Je(s){return async e=>{const r=s.getConfig().access?.logoutReturnUrl,a=r||O("/");return e.newResponse(null,302,{Location:a})}}function Ze(s){return async e=>{const r=e.get("logger"),a=e.req.param("code"),o=j.BH_API_URL,m=(t,l,i)=>t&&l?`${t} ${l.charAt(0)}`:i;try{if(!o)throw new Error("BH_API_URL is not set");const t=s.getConfig().ssoDirect;if(!t||!Object.keys(t).length)return r.warn("Invite no sso configured to handle"),e.redirect(O("/"));const l=await te(`${o}/public-user-invites/${a}`);if(!l.ok)return l.status===404?(r.warn(`Invite ${a} not found redirect to homepage`),e.redirect(O("/"))):(r.error("Invite error",await l.text()),e.redirect(O("/")));const i=await l.json(),d=new URL(O("/invite"),e.req.url);return d.searchParams.set("code",a),d.searchParams.set("org",i.organization.name),d.searchParams.set("invitedBy",m(i.invitedBy.firstName,i.invitedBy.lastName,i.invitedBy.name)),e.newResponse(null,302,{Location:d.toString()})}catch(t){return r.error("Error processing invite",{error:t,inviteCode:a}),e.text(t.message||"Failed to process invite",400)}}}function Ve(s){return async e=>{const r=e.get("logger"),a=s.getConfig().ssoDirect,o=new URL(e.req.url),m=e.req.query("inviteCode"),t=e.req.header("x-forwarded-proto")||o.protocol.slice(0,-1)||"https",l=e.req.header("x-forwarded-host")||o.host,i=`${t}://${l}`;let d=o.searchParams.get("idpId");const A=o.searchParams.get("redirectTo"),p=Object.keys(a||{})[0];d=d||p;const u=o.searchParams.get("mcp_redirect_uri"),h=!!u;if(!a?.[d]){const g="Invalid idpId";if(r.error(`IdP login error: ${g}`),h){const F=V(A||void 0);T.sendMcpAuthorizationFailedMessage([{...D(F),error:g,error_details:null}])}return e.text(`Forbidden: ${g}`,403)}const R=Le({localePrefixParam:o.searchParams.get("localePrefix"),l10n:s.getGlobalData()?.l10n}),c=d&&a?await ie(d,a[d]):void 0,f={};for(const g of Object.keys(c?.extraParams||{}))f[g]=o.searchParams.get(g)||c?.extraParams?.[g]||void 0;se(e)&&(f.prompt="login",f.post_logout="1");let M,n={};if(h&&u&&c&&c.type===Q.OIDC){r.info(`Building MCP OAuth login URL with redirect_uri: ${u}`);const g=Ce("",{...c,extraParams:f},A,m,{redirectUriOverride:u,sourceOverride:"mcp",branchOverride:void 0,uiLocales:R});M=g.loginUrl,n=g.cookies||{}}else if(c){const g=ce({...c,extraParams:f},i,A,m,R);M=g.loginUrl,n=g.cookies||{}}return Object.keys(n).forEach(g=>{L(e,g,n[g].value,n[g].options)}),r.info(`IdP login initiated for ID '${d}'`),e.newResponse(null,302,{Location:M||new URL(e.req.url).pathname})}}function We(s){return async e=>{const r=e.get("logger"),a=await e.req.formData(),o=a.get("SAMLResponse"),m=a.get("RelayState");if(typeof o!="string"||typeof m!="string"){const n="SAMLResponse is required";return r.error(`SAML2 login error: ${n}`),e.text(`Bad request: ${n}`,400)}const t=le(o),{success:l,uid:i,nameFormat:d,attrs:A,issuerId:p,expiresAt:u}=pe(t),{idpId:h,redirectTo:w}=JSON.parse(m);if(!l){const n="SAML2 assertion is not successful";return r.error(`SAML2 login error: ${n}`),e.text(`Permission denied: ${n}`,401)}if(!u||Math.ceil(Date.now()/1e3)>=u){const n="SAML2 Token Expired";return r.error(`SAML2 login error: ${n}`),e.text(n,401)}const R=s.getConfig().ssoDirect?.[h];if(!R||!ae(R)){const n="Cannot find valid IdP";return r.error(`SAML2 login error: ${n}`),e.text(`Permission denied: ${n}`,401)}if(!(R.issuerId&&p&&ee(R.issuerId,p))){const n="IssuerID is misconfigured or untrusted assertions issuer received";return r.error(`SAML2 login error: ${n}`),e.text(`Permission denied: ${n}`,401)}if(!await fe(t,R.x509PublicCert)){const n="SAMLResponse signature invalid";return r.error(`SAML2 login error: ${n}`),e.text(n,401)}const f=ue(i,d,A,R.teamsAttributeName??x);if(!f.sub){const n="The provider did not return a valid user identity.";return r.error(`SAML2 login error: ${n}`),e.text(n,400)}if(!f.email){const n="The provider did not return a valid user email.";return r.error(`SAML2 login error: ${n}`),e.text(n,400)}const M=await k.sign({...f,idpId:h},v,E.HS256);return L(e,_.AUTHORIZATION,M,{path:C()||"/",httpOnly:!0,expires:new Date(u*1e3)}),U(e,_.IDP_REFRESH_TOKEN,{path:C()||"/"}),$(e),r.updateContext({email:f.email,subject:f.sub}),r.info("SAML2 login successful"),e.newResponse(null,302,{Location:w||"/"})}}function Xe(s){return async e=>{const r=e.get("logger"),a=new URL(e.req.query("redirectTo")||"/",e.req.url),o=O(K(a.pathname)),m=s.getConfig().ssoDirect,t=Object.entries(m||{}).find(([,w])=>b(w)&&_e(w));if(!(m&&t))return e.newResponse(null,302,{Location:o});const i=e.req.query("token"),d=i&&await Re(i);if(!d)return e.newResponse(null,302,{Location:o});if(!N(d[H]||[]).some(w=>w===re||w===oe))return e.newResponse(null,302,{Location:o});const u=await k.sign({...d,idpId:t?.at(0)},v,E.HS256),h=Date.now()+B*1e3;return L(e,_.AUTHORIZATION,u,{path:C()||"/",httpOnly:!0,expires:new Date(h),sameSite:"None",secure:!0}),U(e,_.IDP_REFRESH_TOKEN,{path:C()||"/"}),$(e),r.info("Token login successful"),e.newResponse(null,302,{Location:o})}}export{Ke as authorizeHandler,Ve as idpLoginHandler,Ze as inviteHandler,Ge as logoutHandler,ze as oidcCallbackHandler,Je as postLogoutHandler,je as redoclyLoginCallbackHandler,Xe as redoclyTokenLoginHandler,We as samlCallbackHandler};
1
+ import{setCookie as O,deleteCookie as U}from"hono/cookie";import{AuthProviderType as Q,DEFAULT_TEAM_CLAIM_NAME as x}from"@redocly/config";import{withPathPrefix as L,getPathPrefix as C}from"@redocly/theme/core/utils";import{compareURIs as ee}from"../../../utils/url/compare-uris.js";import{ensureArray as N}from"../../../utils/array/ensure-array.js";import{ALTERNATIVE_AUD_CLAIM_NAME as H,JWT_SECRET_KEY as v,ORG_SLUG as re,ORG_ID as oe}from"../../constants/common.js";import{AUTH_SEGMENT as ne,ServerRoutes as P}from"../../../constants/api.js";import{AuthCookieNames as R,DEFAULT_COOKIE_EXPIRATION as B}from"../../../constants/common.js";import{sanitizeRedirectPathname as K}from"../../../utils/url/sanitize-redirect-pathname.js";import{telemetry as D}from"../../telemetry/index.js";import{envConfig as j}from"../../config/env-config.js";import{fetchWithClient as te}from"../../utils/fetch-with-client.js";import{clearAuthCookies as z,clearPostLogoutCookie as $,hasPostLogoutCookie as se,setPostLogoutCookie as G}from"../../utils/cookie.js";import{getAuthProviderLoginParams as ie,isOidcProviderConfig as q,isSaml2ProviderConfig as ae,oidcExchangeCodeForToken as de,buildLoginUrl as ce,decodeSamlResponse as le,extractUserClaims as ue,parseSamlResponse as pe,parseOidcState as ge,verifySAMLResponse as fe,getUsernameFromPayload as me,buildOidcLogoutUrl as he,getOidcMetadata as J,getRedoclyTokenPayload as Ie,isRedoclySso as Re,rewritePreviewAuthRedirectUri as _e,parsePreviewBranch as Z,buildOidcLoginUrl as Ce,createMcpSessionResource as T}from"../auth.js";import*as k from"../jwt/jwt.js";import{AlgorithmTypes as E}from"../jwt/types.js";import{handleErrorPageRender as Ae}from"../utils.js";import{encodeBase64URL as we}from"../jwt/encode.js";import{resolveUiLocalesForIdpLogin as Oe}from"./helpers/resolve-ui-locales-for-idp-login.js";async function Ke(s){if(j.isProductionEnv)return s.newResponse(null,404,{});const{password:e,...r}=await s.req.json(),a=await k.sign({...r,name:r.username||r.email||"Unknown"},v,E.HS256);return O(s,R.AUTHORIZATION,a,{path:C()||"/",httpOnly:!0,secure:!0,sameSite:"none"}),$(s),s.newResponse(null,200,{})}function je(){return async s=>{const e=s.get("logger"),r=encodeURIComponent(s.req.query("message")||"");e.error(`Login error: ${r}`);const a=`${P.LOGIN}/?error=${encodeURIComponent(r)}`;return s.newResponse(null,301,{Location:a})}}function V(s){if(!s||!s.includes(P.MCP_CALLBACK))return null;try{const e=s.split("/"),r=e[e.length-1];if(r){const a=Buffer.from(r,"base64url").toString("utf-8");return JSON.parse(a).mcpSessionId||null}}catch{}return null}function ze(s){return async e=>{const r=e.get("logger"),a=s.getConfig().ssoDirect;let o;try{o=ge(e.req.query("state"))}catch{return r.warn("OIDC login error: Invalid OAuth2 state"),e.text("Bad request: Invalid OAuth2 state",400)}const m=o.idpId,t=o.source==="mcp"||o.redirectTo&&typeof o.redirectTo=="string"&&o.redirectTo.includes(P.MCP_CALLBACK),l=t?V(typeof o.redirectTo=="string"?o.redirectTo:void 0):null,i=a?.[m];if(!q(i))return r.error("OIDC login error: missing OIDC provider config"),e.text("Forbidden",403);const d=await J(m,i);if(a&&!d.token_endpoint){const p="Invalid OIDC configuration: token_endpoint is required";return r.error(`OIDC login error: ${p}`),e.text(p,500)}try{const p=d.token_endpoint,u=e.req.query("code"),h=e.req.query("error");if(h)return t&&D.sendMcpAuthorizationFailedMessage([{...T(l),error:`OIDC error: ${h}`,error_details:e.req.query("error_description")||null}]),Ae(e,s,{slug:"/"},403,"403OIDC");if(!u){const _="Code is expected but not present";return r.error(`OIDC login error: ${_}`),t&&D.sendMcpAuthorizationFailedMessage([{...T(l),error:_,error_details:null}]),new Response(`Forbidden: ${_}`,{status:403})}const w=typeof o.redirectUri=="string"?o.redirectUri:new URL(L(P.OIDC_CALLBACK),e.req.url).toString(),I=e.get("cookies")?.code_verifier,c=await de(p,u,w,i,{...i.tokenRequestCustomParams,...I?{code_verifier:I}:{}});if(c.error)return r.error(`Error from OIDC provider: "${c.error}"`),t&&D.sendMcpAuthorizationFailedMessage([{...T(l),error:`Token exchange error: ${c.error}`,error_details:c.error_description||null}]),e.text(`Forbidden: ${c.error_description||c.error}`,403);if(!c?.id_token){const _="No id_token, please, add openid to scopes";return r.error(`OIDC login error: ${_}`),t&&D.sendMcpAuthorizationFailedMessage([{...T(l),error:_,error_details:null}]),new Response(`Forbidden: ${_}`,{status:403})}const{payload:f,header:M}=k.decode(c.id_token),n=M.alg===E.RS256;if(i.audience?.length&&![...N(f.aud||[]),...N(f[H]||[])].some(y=>i.audience?.includes(y))){const y="No valid audience found in id_token";return r.error(`OIDC login error: ${y}`),t&&D.sendMcpAuthorizationFailedMessage([{...T(l),error:y,error_details:null}]),new Response(`Forbidden: ${y}`)}const g=n?c.id_token:await k.sign({...f,idpId:m},v,E.HS256);me(f)||r.warn("To display your username, the required 'email' or 'full_profile' scope must be added to the identity provider configuration");const S=i?.tokenExpirationTime?Date.now()+i.tokenExpirationTime*1e3:f.exp*1e3||Date.now()+B*1e3;if(i.introspectEndpoint){const _=await fetch(i.introspectEndpoint,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({access_token:c.access_token})});if(_.ok){const b=(await _.json()).ext?.federatedIdentity;b&&(O(e,R.FEDERATED_ACCESS_TOKEN,b.access_token||"",{path:C()||"/",httpOnly:!1,expires:new Date(S)}),O(e,R.FEDERATED_ID_TOKEN,b.id_token||"",{path:C()||"/",httpOnly:!1,expires:new Date(S)}))}else r.warn(`OIDC introspect error: ${_.statusText}`)}if(O(e,R.AUTHORIZATION,g,{path:C()||"/",httpOnly:!0,expires:new Date(S)}),g!==c.id_token&&O(e,R.IDP_ID_TOKEN,c.id_token||"",{path:C()||"/",httpOnly:!0,expires:new Date(S)}),O(e,R.IDP_ACCESS_TOKEN,c.access_token||"",{path:C()||"/",httpOnly:!0,expires:new Date(S)}),c.refresh_token?O(e,R.IDP_REFRESH_TOKEN,c.refresh_token,{path:C()||"/",httpOnly:!0,expires:new Date(S)}):U(e,R.IDP_REFRESH_TOKEN,{path:C()||"/"}),U(e,"code_verifier",{path:C()||"/"}),$(e),t&&o.redirectTo&&typeof o.redirectTo=="string"&&o.redirectTo.includes(P.MCP_CALLBACK)){const y=`${e.req.url.split("?")[0].replace(P.OIDC_CALLBACK,"")}${o.redirectTo}`;return e.newResponse(null,302,{Location:y})}const W=typeof o.redirectTo=="string"?o.redirectTo:void 0;let X=K(new URL(W||"/",e.req.url).pathname);const Y=e.newResponse(null,302,{Location:X});return r.updateContext({email:f.email,subject:f.sub}),r.info("OIDC login successful"),Y}catch(p){const u=p instanceof Error?p.message:String(p),h=p instanceof Error?p.stack:String(p);if(r.error(`OIDC login error: ${u}`),t&&D.sendMcpAuthorizationFailedMessage([{...T(l),error:u,error_details:h}]),p.error==="access_denied")return r.info("Access denied"),e.text("Forbidden",403)}const A="Something went wrong";return r.error(`OIDC login error: ${A}`),t&&D.sendMcpAuthorizationFailedMessage([{...T(l),error:A,error_details:null}]),e.text(A,500)}}function Ge(s){return async e=>{const r=e.get("logger"),o=e.get("auth").claims?.idpId,t=s.getConfig().ssoDirect?.[o];if(e.req.method==="POST")return q(t)||(z(e),G(e)),r.info("Logout successful"),e.newResponse(null,200,{});let l;if(q(t)){const i=(await J(o,t)).end_session_endpoint;if(i){const d=new URL(e.req.url),A=e.req.header("x-forwarded-proto")||d.protocol.slice(0,-1)||"https",p=e.req.header("x-forwarded-host")||d.host,u=`${A}://${p}`,h=Z(u),w=h?we(JSON.stringify({branch:Z(u)})):void 0,I=h?`${_e(u)}/${ne}/logout`:`${u}${L(P.POST_LOGOUT)}`;l=he(i,I,e.get("cookies")?.[R.IDP_ID_TOKEN]||e.get("cookies")?.[R.AUTHORIZATION]||"",w)}}return r.info("Logout successful"),z(e),G(e),e.newResponse(null,302,{Location:l||L("/")})}}function Je(s){return async e=>{const r=s.getConfig().access?.logoutReturnUrl,a=r||L("/");return e.newResponse(null,302,{Location:a})}}function Ze(s){return async e=>{const r=e.get("logger"),a=e.req.param("code"),o=j.BH_API_URL,m=(t,l,i)=>t&&l?`${t} ${l.charAt(0)}`:i;try{if(!o)throw new Error("BH_API_URL is not set");const t=s.getConfig().ssoDirect;if(!t||!Object.keys(t).length)return r.warn("Invite no sso configured to handle"),e.redirect(L("/"));const l=await te(`${o}/public-user-invites/${a}`);if(!l.ok)return l.status===404?(r.warn(`Invite ${a} not found redirect to homepage`),e.redirect(L("/"))):(r.error("Invite error",await l.text()),e.redirect(L("/")));const i=await l.json(),d=new URL(L("/invite"),e.req.url);return d.searchParams.set("code",a),d.searchParams.set("org",i.organization.name),d.searchParams.set("invitedBy",m(i.invitedBy.firstName,i.invitedBy.lastName,i.invitedBy.name)),e.newResponse(null,302,{Location:d.toString()})}catch(t){return r.error("Error processing invite",{error:t,inviteCode:a}),e.text(t.message||"Failed to process invite",400)}}}function Ve(s){return async e=>{const r=e.get("logger"),a=s.getConfig().ssoDirect,o=new URL(e.req.url),m=e.req.query("inviteCode"),t=e.req.header("x-forwarded-proto")||o.protocol.slice(0,-1)||"https",l=e.req.header("x-forwarded-host")||o.host,i=`${t}://${l}`;let d=o.searchParams.get("idpId");const A=o.searchParams.get("redirectTo"),p=Object.keys(a||{})[0];d=d||p;const u=o.searchParams.get("mcp_redirect_uri"),h=!!u;if(!a?.[d]){const g="Invalid idpId";if(r.error(`IdP login error: ${g}`),h){const F=V(A||void 0);D.sendMcpAuthorizationFailedMessage([{...T(F),error:g,error_details:null}])}return e.text(`Forbidden: ${g}`,403)}const I=Oe({localePrefixParam:o.searchParams.get("localePrefix"),l10n:s.getGlobalData()?.l10n}),c=d&&a?await ie(d,a[d]):void 0,f={};for(const g of Object.keys(c?.extraParams||{}))f[g]=o.searchParams.get(g)||c?.extraParams?.[g]||void 0;se(e)&&(f.prompt="login",f.post_logout="1");let M,n={};if(h&&u&&c&&c.type===Q.OIDC){r.info(`Building MCP OAuth login URL with redirect_uri: ${u}`);const g=Ce("",{...c,extraParams:f},A,m,{redirectUriOverride:u,sourceOverride:"mcp",branchOverride:void 0,uiLocales:I});M=g.loginUrl,n=g.cookies||{}}else if(c){const g=ce({...c,extraParams:f},i,A,m,I);M=g.loginUrl,n=g.cookies||{}}return Object.keys(n).forEach(g=>{O(e,g,n[g].value,n[g].options)}),r.info(`IdP login initiated for ID '${d}'`),e.newResponse(null,302,{Location:M||new URL(e.req.url).pathname})}}function We(s){return async e=>{const r=e.get("logger"),a=await e.req.formData(),o=a.get("SAMLResponse"),m=a.get("RelayState");if(typeof o!="string"||typeof m!="string"){const n="SAMLResponse is required";return r.error(`SAML2 login error: ${n}`),e.text(`Bad request: ${n}`,400)}const t=le(o),{success:l,uid:i,nameFormat:d,attrs:A,issuerId:p,expiresAt:u}=pe(t),{idpId:h,redirectTo:w}=JSON.parse(m);if(!l){const n="SAML2 assertion is not successful";return r.error(`SAML2 login error: ${n}`),e.text(`Permission denied: ${n}`,401)}if(!u||Math.ceil(Date.now()/1e3)>=u){const n="SAML2 Token Expired";return r.error(`SAML2 login error: ${n}`),e.text(n,401)}const I=s.getConfig().ssoDirect?.[h];if(!I||!ae(I)){const n="Cannot find valid IdP";return r.error(`SAML2 login error: ${n}`),e.text(`Permission denied: ${n}`,401)}if(!(I.issuerId&&p&&ee(I.issuerId,p))){const n="IssuerID is misconfigured or untrusted assertions issuer received";return r.error(`SAML2 login error: ${n}`),e.text(`Permission denied: ${n}`,401)}if(!await fe(t,I.x509PublicCert)){const n="SAMLResponse signature invalid";return r.error(`SAML2 login error: ${n}`),e.text(n,401)}const f=ue(i,d,A,I.teamsAttributeName??x);if(!f.sub){const n="The provider did not return a valid user identity.";return r.error(`SAML2 login error: ${n}`),e.text(n,400)}if(!f.email){const n="The provider did not return a valid user email.";return r.error(`SAML2 login error: ${n}`),e.text(n,400)}const M=await k.sign({...f,idpId:h},v,E.HS256);return O(e,R.AUTHORIZATION,M,{path:C()||"/",httpOnly:!0,expires:new Date(u*1e3)}),U(e,R.IDP_REFRESH_TOKEN,{path:C()||"/"}),$(e),r.updateContext({email:f.email,subject:f.sub}),r.info("SAML2 login successful"),e.newResponse(null,302,{Location:w||"/"})}}function Xe(s){return async e=>{const r=e.get("logger"),a=new URL(e.req.query("redirectTo")||"/",e.req.url),o=L(K(a.pathname)),m=s.getConfig().ssoDirect,t=Object.entries(m||{}).find(([,w])=>q(w)&&Re(w));if(!(m&&t))return e.newResponse(null,302,{Location:o});const i=e.req.query("token"),d=i&&await Ie(i);if(!d)return e.newResponse(null,302,{Location:o});if(!N(d[H]||[]).some(w=>w===re||w===oe))return e.newResponse(null,302,{Location:o});const u=await k.sign({...d,idpId:t?.at(0)},v,E.HS256),h=Date.now()+B*1e3;return O(e,R.AUTHORIZATION,u,{path:C()||"/",httpOnly:!0,expires:new Date(h),sameSite:"None",secure:!0}),U(e,R.IDP_REFRESH_TOKEN,{path:C()||"/"}),$(e),r.info("Token login successful"),e.newResponse(null,302,{Location:o})}}export{Ke as authorizeHandler,Ve as idpLoginHandler,Ze as inviteHandler,Ge as logoutHandler,ze as oidcCallbackHandler,Je as postLogoutHandler,je as redoclyLoginCallbackHandler,Xe as redoclyTokenLoginHandler,We as samlCallbackHandler};
@@ -1 +1 @@
1
- import{serveStatic as R}from"hono/serve-static";import{withPathPrefix as i,withoutPathPrefix as c}from"@redocly/theme/core/utils";import{ServerRoutes as l}from"../../../constants/api.js";import{PUBLIC_STATIC_FOLDER as H}from"../../constants/common.js";import{envConfig as L}from"../../config/env-config.js";import{authMiddleware as C}from"../middleware/authMiddleware.js";import{ensureSearchData as g}from"../middleware/ensureSearchData.js";import{dynamicMiddleware as I}from"../middleware/dynamic-middleware/dynamic-middleware.js";import{installRoutes as h}from"../../plugins/dev-onboarding/api/routes/index.js";import{installSkillsRoutes as D}from"./skills/skills-routes.js";import{authorizeHandler as P,oidcCallbackHandler as N,logoutHandler as s,postLogoutHandler as w,idpLoginHandler as M,redoclyLoginCallbackHandler as B,samlCallbackHandler as v,redoclyTokenLoginHandler as F,inviteHandler as G}from"./auth.js";import{appDataHandler as U}from"./app-data.js";import{searchFacetsHandler as y,searchHandler as K}from"./search.js";import{dynamicRouteHandler as Y}from"./dynamic-route.js";import{pageDataHandler as k,sharedPageDataHandler as b}from"./page-data.js";import{pathPrefixRedirectHandler as V}from"./path-prefix-redirect.js";import{getRoutesByLineHandler as E,resolvePathHandler as T,resolvePathsHandler as x,resolveSlugHandler as _}from"./resolve-route.js";import{feedbackHandler as q}from"./feedback.js";import{loggerMiddleware as X}from"../middleware/loggerMiddleware.js";import{responseHeadersMiddleware as $}from"../middleware/responseHeadersMiddleware.js";import{idleTimeoutMiddleware as j}from"../middleware/idleTimeoutMiddleware.js";import{otelTracesHandler as z}from"./otel/otel.js";import{healthCheckHandler as W}from"./health.js";import{askAiHandler as Z,aiAssistantEscalationsHandler as u,aiAssistantFeedbackHandler as J}from"./ask-ai.js";import{semanticSearchHandler as Q}from"./semantic-search.js";import{replayOauth2RedirectCallbackHandler as aa}from"./replay-oauth2-redirect.js";import{corsProxyHandler as f}from"./cors-proxy.js";import{corsMiddleware as t}from"../middleware/corsMiddleware.js";import{installApiRoutes as ea}from"./api-routes/api-routes.js";import{cookieMiddleware as ia}from"../middleware/cookieMiddleware.js";import{staticContentHandler as la}from"../routes/static-content.js";import{infoHandler as S}from"./info.js";import{catalogHandler as ta}from"./catalog/catalog.js";import{catalogRelationsHandler as ma}from"./catalog/catalog-relations.js";import{bffCatalogHandler as da}from"./catalog/bff-catalog.js";import{bffCatalogRevisionsHandler as oa}from"./catalog/bff-catalog-revisions.js";import{bffCatalogRelatedEntitiesHandler as na}from"./catalog/bff-catalog-related-entities.js";import{catalogAuthMiddleware as m}from"../middleware/catalogAuthMiddleware.js";import{telemetryMiddleware as Aa}from"../middleware/telemetry-middleware.js";import{errorHandler as pa}from"./error.js";import{installMcpRoutes as sa}from"./mcp-routes/mcp-routes.js";import{jsonValidatorMiddleware as O}from"../middleware/jsonValidatorMiddleware.js";import{searchFacetsRequestSchema as Ea}from"../../../api-schemas/search-facets.js";import{searchRequestSchema as Ta}from"../../../api-schemas/search.js";function me(a,e,d){const{resolveRouteData:n,readStaticAsset:A}=d;a.use("*",j()),a.use("*",ia()),a.use("*",I(e)),a.use("*",C(e)),a.use("*",X()),a.use("*",$(e)),a.use("*",Aa()),a.use(i("*"),R({root:`./${H}`,getContent:(o,r)=>la(o,r,e,A),rewriteRequestPath:o=>c(o)})),a.use(i(l.FEEDBACK),t({allowMethods:["POST"]})),a.use(i(l.ASK_AI),t({allowMethods:["POST"]})),a.use(i(l.AI_ASSISTANT_ESCALATIONS),t({allowMethods:["POST"]})),a.use(i(l.AI_ASSISTANT_FEEDBACK),t({allowMethods:["POST"]})),a.use(i(l.SEMANTIC_SEARCH),t({allowMethods:["POST"]})),a.use("*",_a(e));const p=g(e);a.use(i(l.INFO),S()),L.NEW_CATALOG_ENABLED&&(a.use(i(l.CATALOG_ENTITIES),m({serverOutDir:e.serverOutDir}),ta(e)),a.use(i(l.CATALOG_ENTITIES_RELATIONS),m({serverOutDir:e.serverOutDir}),ma(e)),a.get(i(l.BFF_CATALOG_ENTITIES),m({serverOutDir:e.serverOutDir,protectReadMethods:!1}),da(e)),a.get(i(l.BFF_CATALOG_RELATED_ENTITIES),m({serverOutDir:e.serverOutDir,protectReadMethods:!1}),na(e)),a.get(i(l.BFF_CATALOG_REVISIONS),m({serverOutDir:e.serverOutDir,protectReadMethods:!1}),oa(e))),a.get(i(l.SHARED_PAGE_DATA),b(e)),a.get(i(l.PAGE_DATA),k(e,n)),a.get(i(l.APP_DATA),U(e)),a.post(i(l.SEARCH),p,O(Ta),K(e)),a.post(i(l.SEARCH_FACETS),p,O(Ea),y(e)),a.post(i(l.AUTHORIZATION),P),a.post(i(l.LOGOUT),s(e)),a.get(i(l.LOGOUT),s(e)),a.get(i(l.POST_LOGOUT),w(e)),a.get(i(l.OIDC_CALLBACK),N(e)),a.get(i(l.REDOCLY_TOKEN_LOGIN),F(e)),a.get(i(l.REDOCLY_LOGIN_CALLBACK),B()),a.get(i(l.IDP_LOGIN),M(e)),a.post(i(l.SAML_CALLBACK),v(e)),a.get(i(l.INVITE),G(e)),a.get(i(l.HEALTH),W),sa(a,e),h(a,e),D(a,e),a.all(i(l.CORS_PROXY),f(e)),a.all(i(`${l.CORS_PROXY}/*`),f(e)),ea(a,e),a.post(i(l.FEEDBACK),q(e)),a.post(i(l.RESOLVE_ROUTE_BY_PATH),T(e)),a.post(i(l.RESOLVE_ROUTES_BY_PATHS),x(e)),a.post(i(l.RESOLVE_ROUTE_BY_SLUG),_(e)),a.post(i(l.ASK_AI),Z(e)),a.post(i(l.AI_ASSISTANT_ESCALATIONS),u(e)),a.post(i(l.AI_ASSISTANT_FEEDBACK),J(e)),a.post(i(l.SEMANTIC_SEARCH),Q(e)),a.get(i(l.GET_ROUTES_BY_LINE),E(e)),a.post(i(l.OTEL_TRACES),z),a.get(i(l.REPLAY_OAUTH2_CALLBACK),aa),a.all(i("/*"),Y(e,n,A)),a.get("*",V),a.onError(pa)}function _a(a){return async(e,d)=>{await a.waitForPluginsLifecycle(),await d()}}function de(a,e){a.get(i(l.INFO),S()),a.post(i(l.RESOLVE_ROUTE_BY_PATH),T(e)),a.post(i(l.RESOLVE_ROUTE_BY_SLUG),_(e)),a.get(i(l.GET_ROUTES_BY_LINE),E(e))}export{de as installDevRoutes,me as installProdRoutes,_a as waitForPluginsLifecycle};
1
+ import{serveStatic as R}from"hono/serve-static";import{withPathPrefix as i,withoutPathPrefix as H}from"@redocly/theme/core/utils";import{ServerRoutes as l}from"../../../constants/api.js";import{PUBLIC_STATIC_FOLDER as c}from"../../constants/common.js";import{envConfig as L}from"../../config/env-config.js";import{authMiddleware as C}from"../middleware/authMiddleware.js";import{ensureSearchData as I}from"../middleware/ensureSearchData.js";import{dynamicMiddleware as g}from"../middleware/dynamic-middleware/dynamic-middleware.js";import{installRoutes as D}from"../../plugins/dev-onboarding/api/routes/index.js";import{installSkillsRoutes as h}from"./skills/skills-routes.js";import{authorizeHandler as P,oidcCallbackHandler as N,logoutHandler as s,postLogoutHandler as w,idpLoginHandler as M,redoclyLoginCallbackHandler as B,samlCallbackHandler as v,redoclyTokenLoginHandler as F,inviteHandler as G}from"./auth.js";import{appDataHandler as U}from"./app-data.js";import{searchFacetsHandler as Y,searchHandler as y}from"./search.js";import{dynamicRouteHandler as K}from"./dynamic-route.js";import{pageDataHandler as k,sharedPageDataHandler as b}from"./page-data.js";import{pathPrefixRedirectHandler as x}from"./path-prefix-redirect.js";import{getRoutesByLineHandler as T,resolvePathHandler as E,resolvePathsHandler as V,resolveSlugHandler as _}from"./resolve-route.js";import{feedbackHandler as q}from"./feedback.js";import{loggerMiddleware as X}from"../middleware/loggerMiddleware.js";import{responseHeadersMiddleware as $}from"../middleware/responseHeadersMiddleware.js";import{idleTimeoutMiddleware as j}from"../middleware/idleTimeoutMiddleware.js";import{otelTracesHandler as z}from"./otel/otel.js";import{healthCheckHandler as W}from"./health.js";import{askAiHandler as Z,aiAssistantEscalationsHandler as u,aiAssistantFeedbackHandler as J,AI_ASSISTANT_IDENTITY_STATUS_HEADER as Q}from"./ask-ai.js";import{semanticSearchHandler as aa}from"./semantic-search.js";import{replayOauth2RedirectCallbackHandler as ea}from"./replay-oauth2-redirect.js";import{corsProxyHandler as S}from"./cors-proxy.js";import{corsMiddleware as t}from"../middleware/corsMiddleware.js";import{installApiRoutes as ia}from"./api-routes/api-routes.js";import{cookieMiddleware as la}from"../middleware/cookieMiddleware.js";import{staticContentHandler as ta}from"../routes/static-content.js";import{infoHandler as f}from"./info.js";import{catalogHandler as ma}from"./catalog/catalog.js";import{catalogRelationsHandler as da}from"./catalog/catalog-relations.js";import{bffCatalogHandler as Aa}from"./catalog/bff-catalog.js";import{bffCatalogRevisionsHandler as oa}from"./catalog/bff-catalog-revisions.js";import{bffCatalogRelatedEntitiesHandler as na}from"./catalog/bff-catalog-related-entities.js";import{catalogAuthMiddleware as m}from"../middleware/catalogAuthMiddleware.js";import{telemetryMiddleware as pa}from"../middleware/telemetry-middleware.js";import{errorHandler as sa}from"./error.js";import{installMcpRoutes as Ta}from"./mcp-routes/mcp-routes.js";import{jsonValidatorMiddleware as O}from"../middleware/jsonValidatorMiddleware.js";import{searchFacetsRequestSchema as Ea}from"../../../api-schemas/search-facets.js";import{searchRequestSchema as _a}from"../../../api-schemas/search.js";function de(a,e,d){const{resolveRouteData:o,readStaticAsset:n}=d;a.use("*",j()),a.use("*",la()),a.use("*",g(e)),a.use("*",C(e)),a.use("*",X()),a.use("*",$(e)),a.use("*",pa()),a.use(i("*"),R({root:`./${c}`,getContent:(A,r)=>ta(A,r,e,n),rewriteRequestPath:A=>H(A)})),a.use(i(l.FEEDBACK),t({allowMethods:["POST"]})),a.use(i(l.ASK_AI),t({allowMethods:["POST"],exposeHeaders:[Q]})),a.use(i(l.AI_ASSISTANT_ESCALATIONS),t({allowMethods:["POST"]})),a.use(i(l.AI_ASSISTANT_FEEDBACK),t({allowMethods:["POST"]})),a.use(i(l.SEMANTIC_SEARCH),t({allowMethods:["POST"]})),a.use("*",Sa(e));const p=I(e);a.use(i(l.INFO),f()),L.NEW_CATALOG_ENABLED&&(a.use(i(l.CATALOG_ENTITIES),m({serverOutDir:e.serverOutDir}),ma(e)),a.use(i(l.CATALOG_ENTITIES_RELATIONS),m({serverOutDir:e.serverOutDir}),da(e)),a.get(i(l.BFF_CATALOG_ENTITIES),m({serverOutDir:e.serverOutDir,protectReadMethods:!1}),Aa(e)),a.get(i(l.BFF_CATALOG_RELATED_ENTITIES),m({serverOutDir:e.serverOutDir,protectReadMethods:!1}),na(e)),a.get(i(l.BFF_CATALOG_REVISIONS),m({serverOutDir:e.serverOutDir,protectReadMethods:!1}),oa(e))),a.get(i(l.SHARED_PAGE_DATA),b(e)),a.get(i(l.PAGE_DATA),k(e,o)),a.get(i(l.APP_DATA),U(e)),a.post(i(l.SEARCH),p,O(_a),y(e)),a.post(i(l.SEARCH_FACETS),p,O(Ea),Y(e)),a.post(i(l.AUTHORIZATION),P),a.post(i(l.LOGOUT),s(e)),a.get(i(l.LOGOUT),s(e)),a.get(i(l.POST_LOGOUT),w(e)),a.get(i(l.OIDC_CALLBACK),N(e)),a.get(i(l.REDOCLY_TOKEN_LOGIN),F(e)),a.get(i(l.REDOCLY_LOGIN_CALLBACK),B()),a.get(i(l.IDP_LOGIN),M(e)),a.post(i(l.SAML_CALLBACK),v(e)),a.get(i(l.INVITE),G(e)),a.get(i(l.HEALTH),W),Ta(a,e),D(a,e),h(a,e),a.all(i(l.CORS_PROXY),S(e)),a.all(i(`${l.CORS_PROXY}/*`),S(e)),ia(a,e),a.post(i(l.FEEDBACK),q(e)),a.post(i(l.RESOLVE_ROUTE_BY_PATH),E(e)),a.post(i(l.RESOLVE_ROUTES_BY_PATHS),V(e)),a.post(i(l.RESOLVE_ROUTE_BY_SLUG),_(e)),a.post(i(l.ASK_AI),Z(e)),a.post(i(l.AI_ASSISTANT_ESCALATIONS),u(e)),a.post(i(l.AI_ASSISTANT_FEEDBACK),J(e)),a.post(i(l.SEMANTIC_SEARCH),aa(e)),a.get(i(l.GET_ROUTES_BY_LINE),T(e)),a.post(i(l.OTEL_TRACES),z),a.get(i(l.REPLAY_OAUTH2_CALLBACK),ea),a.all(i("/*"),K(e,o,n)),a.get("*",x),a.onError(sa)}function Sa(a){return async(e,d)=>{await a.waitForPluginsLifecycle(),await d()}}function Ae(a,e){a.get(i(l.INFO),f()),a.post(i(l.RESOLVE_ROUTE_BY_PATH),E(e)),a.post(i(l.RESOLVE_ROUTE_BY_SLUG),_(e)),a.get(i(l.GET_ROUTES_BY_LINE),T(e))}export{Ae as installDevRoutes,de as installProdRoutes,Sa as waitForPluginsLifecycle};
@@ -1 +1 @@
1
- import{withPathPrefix as f}from"@redocly/theme/core/utils";import{DEFAULT_DOCS_MCP_SERVER_NAME as h,MCP_PROTOCOL_VERSION as O}from"../../../plugins/mcp/constants.js";import{RESERVED_ROUTES as A}from"../../../plugins/get-reserved-routes.js";import{isMcpCodeModeEnabled as T}from"../../../plugins/mcp/utils/mode.js";import{CODE_MODE_TOOL_NAMES as b,DESCRIBE_TOOLS_TOOL_NAME as M,FETCH_TOOL_NAME as R}from"../../../plugins/mcp/codemode/constants.js";import{EXECUTE_TOOL_SCHEMA as g}from"../../../plugins/mcp/codemode/tools/execute-schema.js";import{describeToolsSchema as v}from"../../../plugins/mcp/codemode/tools/describe-tools.js";import{buildCodeModeInstructions as L,buildExecuteDescription as I,DESCRIBE_TOOLS_DESCRIPTION as D}from"../../../plugins/mcp/codemode/prompts.js";import{toToolListEntries as P}from"../../../plugins/mcp/codemode/build-tool-list.js";import{composeExecuteAnnotations as y}from"../../../plugins/mcp/codemode/capabilities/index.js";import{ALL_CAPABILITIES as V}from"../../../plugins/mcp/codemode/capabilities/registry.js";import{isGatewayCapabilityEnabled as H}from"../../../plugins/mcp/gateway-mcp/eligibility.js";import{shouldHandleMcpAuth as N}from"../../../plugins/mcp/auth/auth-handlers.js";import{resolvePublicBaseUrl as U}from"../../../web-server/utils/resolve-public-base-url.js";import{telemetryTraceStep as x}from"../../../telemetry/helpers/trace-step.js";import{MCP_AUTH_SCHEMES as w}from"./constants.js";const B="https://static.modelcontextprotocol.io/schemas/mcp-server-card/v1.json",j="1.0";function ie(r){const s=T(),E=new Date().toISOString().slice(0,10);return async m=>await x("com.redocly.mcpServerCard.served",async e=>{e?.setAttribute("id","mcpServerCardId"),e?.setAttribute("object","mcpServerCard"),e?.setAttribute("uri","urn:redocly:realm:api:mcpServerCard:mcpServerCardId");const o=r.getConfig(),a=o.mcp?.docs?.name||h,n=r.getMcpTools().filter(t=>!b.has(t.name)),i=H(o.mcp)?[...V]:[],d=i.some(t=>t.id===R),l=P(n,i),c=(s?[{...g,description:I(d,l),annotations:y(i)},{name:M,description:D,schema:v(n,i)}]:n).map(({name:t,description:_,schema:S,annotations:u})=>({name:t,description:_,inputSchema:S,...u?{annotations:u}:{}})),C=o.skills?.hide===!0?[]:r.getSkills(),p={$schema:B,version:j,protocolVersion:O,serverInfo:{name:a,title:a,version:E},description:o.seo?.description,documentationUrl:U(r,m),transport:{type:"streamable-http",endpoint:f(A.mcp)},capabilities:{logging:{},tools:{listChanged:!0},...C.length>0?{resources:{listChanged:!0},completions:{}}:{}},authentication:N(!!o.access?.requiresLogin,o.access?.rbac)?{required:!0,schemes:w}:void 0,instructions:s?L(d,l):void 0,tools:c.length>0?c:["dynamic"]};return e?.setAttribute("mode",s?"code":"tools"),e?.setAttribute("toolsCount",c.length),e?.setAttribute("authRequired",p.authentication!==void 0),m.json(p,200,{"Access-Control-Allow-Origin":"*","Cache-Control":"public, max-age=3600"})})}export{ie as mcpServerCardHandler};
1
+ import{withPathPrefix as S}from"@redocly/theme/core/utils";import{DEFAULT_DOCS_MCP_SERVER_NAME as f,MCP_PROTOCOL_VERSION as A}from"../../../plugins/mcp/constants.js";import{RESERVED_ROUTES as O}from"../../../plugins/get-reserved-routes.js";import{isMcpCodeModeEnabled as T}from"../../../plugins/mcp/utils/mode.js";import{CODE_MODE_TOOL_NAMES as b,DESCRIBE_TOOLS_TOOL_NAME as M,FETCH_TOOL_NAME as R}from"../../../plugins/mcp/codemode/constants.js";import{EXECUTE_TOOL_SCHEMA as g}from"../../../plugins/mcp/codemode/tools/execute-schema.js";import{describeToolsSchema as v}from"../../../plugins/mcp/codemode/tools/describe-tools.js";import{buildCodeModeInstructions as L,buildExecuteDescription as I,DESCRIBE_TOOLS_DESCRIPTION as D}from"../../../plugins/mcp/codemode/prompts.js";import{toToolListEntries as P}from"../../../plugins/mcp/codemode/build-tool-list.js";import{composeExecuteAnnotations as y}from"../../../plugins/mcp/codemode/capabilities/index.js";import{ALL_CAPABILITIES as V}from"../../../plugins/mcp/codemode/capabilities/registry.js";import{isGatewayCapabilityEnabled as H}from"../../../plugins/mcp/gateway-mcp/eligibility.js";import{shouldHandleMcpAuth as N}from"../../../plugins/mcp/auth/auth-handlers.js";import{resolvePublicBaseUrl as U}from"../../../web-server/utils/resolve-public-base-url.js";import{telemetryTraceStep as x}from"../../../telemetry/helpers/trace-step.js";import{MCP_AUTH_SCHEMES as w}from"./constants.js";const B="https://static.modelcontextprotocol.io/schemas/mcp-server-card/v1.json",j="1.0";function ie(r){const s=T(),E=new Date().toISOString().slice(0,10);return async m=>await x("com.redocly.mcpServerCard.served",async e=>{e?.setAttribute("id","mcpServerCardId"),e?.setAttribute("object","mcpServerCard"),e?.setAttribute("uri","urn:redocly:realm:api:mcpServerCard:mcpServerCardId");const o=r.getConfig(),a=o.mcp?.docs?.name||f,n=r.getMcpTools().filter(t=>!b.has(t.name)&&!t.hasAvailabilityGate),i=H(o.mcp)?[...V]:[],l=i.some(t=>t.id===R),d=P(n,i),c=(s?[{...g,description:I(l,d),annotations:y(i)},{name:M,description:D,schema:v(n,i)}]:n).map(({name:t,description:_,schema:h,annotations:u})=>({name:t,description:_,inputSchema:h,...u?{annotations:u}:{}})),C=o.skills?.hide===!0?[]:r.getSkills(),p={$schema:B,version:j,protocolVersion:A,serverInfo:{name:a,title:a,version:E},description:o.seo?.description,documentationUrl:U(r,m),transport:{type:"streamable-http",endpoint:S(O.mcp)},capabilities:{logging:{},tools:{listChanged:!0},...C.length>0?{resources:{listChanged:!0},completions:{}}:{}},authentication:N(!!o.access?.requiresLogin,o.access?.rbac)?{required:!0,schemes:w}:void 0,instructions:s?L(l,d):void 0,tools:c.length>0?c:["dynamic"]};return e?.setAttribute("mode",s?"code":"tools"),e?.setAttribute("toolsCount",c.length),e?.setAttribute("authRequired",p.authentication!==void 0),m.json(p,200,{"Access-Control-Allow-Origin":"*","Cache-Control":"public, max-age=3600"})})}export{ie as mcpServerCardHandler};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/realm",
3
- "version": "0.137.0-next.2",
3
+ "version": "0.137.0-next.4",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "bin": {
@@ -33,7 +33,7 @@
33
33
  "@opentelemetry/sdk-trace-web": "2.8.0",
34
34
  "@opentelemetry/semantic-conventions": "1.40.0",
35
35
  "@redocly/ajv": "8.18.0",
36
- "@redocly/openapi-core": "2.45.0",
36
+ "@redocly/openapi-core": "2.47.0",
37
37
  "@shikijs/transformers": "3.21.0",
38
38
  "@tanstack/react-query": "5.62.3",
39
39
  "@tanstack/react-table": "8.21.3",
@@ -93,15 +93,15 @@
93
93
  "xpath": "0.0.34",
94
94
  "yaml-ast-parser": "0.0.43",
95
95
  "zod": "^3.25.76",
96
- "@redocly/api-docs": "0.2.0-next.2",
97
- "@redocly/asyncapi-docs": "1.14.0-next.2",
96
+ "@redocly/api-docs": "0.2.0-next.4",
97
+ "@redocly/asyncapi-docs": "1.14.0-next.4",
98
98
  "@redocly/config": "0.53.1",
99
- "@redocly/graphql-docs": "1.14.0-next.2",
100
- "@redocly/openapi-docs": "3.25.0-next.2",
99
+ "@redocly/graphql-docs": "1.14.0-next.4",
100
+ "@redocly/openapi-docs": "3.25.0-next.4",
101
101
  "@redocly/portal-legacy-ui": "0.19.0",
102
- "@redocly/portal-plugin-mock-server": "0.22.0-next.2",
103
- "@redocly/realm-asyncapi-sdk": "0.15.0-next.1",
104
- "@redocly/theme": "0.69.0-next.2"
102
+ "@redocly/portal-plugin-mock-server": "0.22.0-next.4",
103
+ "@redocly/realm-asyncapi-sdk": "0.15.0-next.2",
104
+ "@redocly/theme": "0.69.0-next.4"
105
105
  },
106
106
  "peerDependencies": {
107
107
  "react": "^19.2.7",