@redocly/realm 0.136.0-next.10 → 0.136.0-next.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/dist/client/App.js +1 -1
- package/dist/client/ScriptLoader.js +1 -1
- package/dist/client/TestProvider.js +1 -1
- package/dist/client/app/Link.js +1 -1
- package/dist/client/app/Sidebar/useBreadcrumbs.js +1 -1
- package/dist/client/app/Sidebar/useSidebarItems.js +1 -1
- package/dist/client/app/Sidebar/utils.d.ts +1 -1
- package/dist/client/app/hooks/catalog/useCatalogClassic.js +1 -1
- package/dist/client/app/hooks/catalog/useCatalogFilter.js +1 -1
- package/dist/client/app/hooks/catalog/useCatalogSearch.js +1 -1
- package/dist/client/app/hooks/catalog/useCatalogSort.js +1 -1
- package/dist/client/app/hooks/products/useCurrentProduct.js +1 -1
- package/dist/client/app/hooks/useAutoScroll.js +1 -1
- package/dist/client/app/hooks/useBanner.js +1 -1
- package/dist/client/app/hooks/useL10nConfig.js +1 -1
- package/dist/client/app/hooks/useLoginUrl.js +1 -1
- package/dist/client/app/hooks/usePageTimeTracker.js +1 -1
- package/dist/client/app/hooks/useRouteChangeTracker.js +1 -1
- package/dist/client/app/markdoc/custom-components/openapi/replay-openapi.js +1 -1
- package/dist/client/app/pages/DevLogin/DevLogin.js +1 -1
- package/dist/client/app/seo/hooks/useHreflangLinks.js +1 -1
- package/dist/client/app/useMobileMenu.js +1 -1
- package/dist/client/app/usePreloadHistory.d.ts +1 -1
- package/dist/client/app/usePreloadHistory.js +1 -1
- package/dist/client/app/utils/loadAndNavigate.d.ts +1 -1
- package/dist/client/browser-entry.js +2 -2
- package/dist/client/providers/hooks.d.ts +1 -1
- package/dist/client/providers/hooks.js +1 -1
- package/dist/client/providers/page-data/hooks.js +1 -1
- package/dist/client/providers/post-message/PostMessageProvider.js +1 -1
- package/dist/constants/api.d.ts +1 -0
- package/dist/constants/api.js +1 -1
- package/dist/server/constants/common.d.ts +1 -0
- package/dist/server/constants/common.js +1 -1
- package/dist/server/esbuild/esbuild.js +1 -1
- package/dist/server/esbuild/plugins/themes-resolver.js +4 -2
- package/dist/server/plugins/dev-onboarding/template/App.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/AppsList.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/ApiKeyItem.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/ApiLogDetails.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/ApiLogs.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/CreateApiKey.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/DeleteAppDialog.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/EditApis.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/EditAppDescriptionDialog.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/EditAppDialog.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/EditCallbackUrlDialog.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/RevokeAPIKey.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/RollAPIKey.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/UpdateApiKey.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/index.js +1 -1
- package/dist/server/plugins/mcp/codemode/constants.js +1 -1
- package/dist/server/plugins/mcp/codemode/tools/manage-credentials.d.ts +19 -0
- package/dist/server/plugins/mcp/codemode/tools/manage-credentials.js +2 -0
- package/dist/server/plugins/mcp/constants.d.ts +1 -0
- package/dist/server/plugins/mcp/constants.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/elicitation.d.ts +35 -15
- package/dist/server/plugins/mcp/gateway-mcp/elicitation.js +3 -2
- package/dist/server/plugins/mcp/gateway-mcp/fetch/fetch-bridge.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/secret-store.d.ts +3 -0
- package/dist/server/plugins/mcp/gateway-mcp/secret-store.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/upstream-auth.d.ts +1 -0
- package/dist/server/plugins/mcp/gateway-mcp/upstream-auth.js +1 -1
- package/dist/server/plugins/mcp/handlers/mcp-credentials-handler.js +43 -24
- package/dist/server/plugins/mcp/servers/docs-server.js +1 -1
- package/dist/server/plugins/mcp/servers/mcp-server.js +3 -1
- package/dist/server/plugins/scorecard-classic/template/ApiDetailsPage.js +1 -1
- package/dist/server/plugins/scorecard-classic/template/RuleDetailsPage.js +1 -1
- package/dist/server/plugins/scorecard-classic/template/index.js +1 -1
- package/dist/server/plugins/scorecard-classic/template/utils.d.ts +1 -1
- package/dist/server/plugins/scorecard-classic/template/views.js +1 -1
- package/dist/server/ssr/render.js +1 -1
- package/dist/server/types/plugin-hooks.d.ts +1 -1
- package/dist/server/web-server/routes/ask-ai.d.ts +6 -0
- package/dist/server/web-server/routes/ask-ai.js +1 -1
- package/dist/server/web-server/routes/index.js +1 -1
- package/package.json +15 -15
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import*as e from"react";import o from"styled-components";import{useParams as g}from"react-router
|
|
1
|
+
import*as e from"react";import o from"styled-components";import{useParams as g}from"react-router";import{useMutation as E,useQueryClient as y}from"@tanstack/react-query";import{Flex as s}from"@redocly/portal-legacy-ui";import{Button as x}from"@redocly/theme/components/Button/Button";import{H3 as h}from"@redocly/theme/components/Typography/H3";import{Admonition as v}from"@redocly/theme/markdoc/components/Admonition/Admonition";import k from"./ApiKeyOverview";import{ApiKeyProductsStatus as w}from"./ApiKeyProductStatus";import{UpdateKey as K}from"./UpdateApiKey";import{Dialog as L}from"./Dialog";import{ButtonWithLoading as D}from"./ButtonWithLoading";function S(m){const p=y(),{credential:t}=m,{id:r}=g(),[c,a]=e.useState(!1),[u,n]=e.useState(!1),{isPending:i,error:l,mutate:d}=E({mutationKey:["DELETE_CRED"+t.id],mutationFn:()=>fetch(`/api/api-keys/apps/${r}/credentials/${t.id}`,{method:"DELETE"}),onSuccess:()=>{p.invalidateQueries({queryKey:["APP:"+r]}),n(!1)}}),C=()=>{d()},f=()=>{a(!0)};return e.createElement(e.Fragment,null,e.createElement(A,null,e.createElement(k,{apiKey:t}),e.createElement(w,{apiProductsStatuses:t.apiProductsStatus,secret:t.clientSecret,id:t.id})),e.createElement(K,{credential:t,open:c,onClose:()=>a(!1),onOpen:f}),e.createElement(L,{open:u,onOpen:()=>n(!0),onClose:()=>n(!1)},e.createElement(e.Fragment,null,e.createElement(h,null,"Revoke key"),e.createElement("div",null,"Do you really want to revoke this key?"),l&&e.createElement(v,{type:"danger"},l.message),e.createElement(O,null,e.createElement(x,{onClick:()=>n(!1)},"Cancel"),e.createElement(D,{variant:"primary",onClick:C,disabled:i,loading:i},"Revoke key")))))}const A=o(s)`
|
|
2
2
|
flex-wrap: wrap;
|
|
3
3
|
gap: 32px;
|
|
4
4
|
`,W=o.span`
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import*as e from"react";import{useQuery as b}from"@tanstack/react-query";import{useParams as g}from"react-router
|
|
1
|
+
import*as e from"react";import{useQuery as b}from"@tanstack/react-query";import{useParams as g}from"react-router";import t from"styled-components";import{CodeBlock as f}from"@redocly/theme/components/CodeBlock/CodeBlock";import{fetch as v}from"../../../../../client/app/utils";import y from"./LogStatusCode";import{getReadableDate as h}from"./getReadableDate";import{Headers as E}from"./Headers";function x(m){const{logId:a}=m,[s,p]=e.useState(0),{id:u}=g(),{data:r}=b({refetchOnWindowFocus:!1,queryKey:["API-LOGS-"+a],queryFn:()=>v(`/api/api-keys/apps/${u}/logs/${a}`).then(n=>n.json())}),c=[{name:"Request",content:r?.request},{name:"Response",content:r?.response}];if(!a||!r)return e.createElement(d,null);const o=c[s].content;return e.createElement(d,null,e.createElement(w,null,e.createElement(l,null,r.path)),e.createElement(k,null,e.createElement("span",null,"Status"),e.createElement("span",null,e.createElement(y,{statusCode:r.statusCode.toString()})),e.createElement("span",null,"ID"),e.createElement("span",null,r.id),e.createElement("span",null,"Time"),e.createElement("span",null,h(r.timestamp,{hour:"numeric",minute:"numeric",second:"numeric"})),e.createElement("span",null,"IP Address"),e.createElement("span",null,r.ipAddress)),e.createElement(C,null,e.createElement(T,null,c.map(({name:n},i)=>e.createElement(S,{key:n,className:s===i?"active":"",onClick:()=>p(i)},n))),e.createElement(z,null,e.createElement(l,null,"Headers"),Object.keys(o?.headers||{}).length?e.createElement(E,{headers:o?.headers||{}}):e.createElement("span",null,"No headers"))),o?.body&&e.createElement(e.Fragment,null,e.createElement(l,null,"Body"),e.createElement(A,{lang:"json",source:o.body,withLineNumbers:!0})))}const d=t.div`
|
|
2
2
|
display: flex;
|
|
3
3
|
flex-grow: 1;
|
|
4
4
|
flex-direction: column;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import*as a from"react";import v from"react-dom";import{useQuery as y}from"@tanstack/react-query";import{useParams as E}from"react-router
|
|
1
|
+
import*as a from"react";import v from"react-dom";import{useQuery as y}from"@tanstack/react-query";import{useParams as E}from"react-router";import i from"styled-components";import{fetch as D}from"../../../../../client/app/utils";import L from"../../../../../client/app/CircularProgress";import{TAB_ACTIONS_PORTAL_ID as F}from"../App";import A from"./ApiLogDetails";import x from"./ApiLogItem";import S from"./ApiLogsActions";import{getReadableDate as p}from"./getReadableDate";function C(){const[o,n]=a.useState(void 0),[e,c]=a.useState({}),s=a.useMemo(()=>R(e),[e]),{id:d}=E(),l=t=>{n(t.id)},u=()=>{const t=new URLSearchParams;return t.append("page","1"),e.api?.length&&e.api.forEach(r=>t.append("api",r.id)),e.method?.length&&e.method?.forEach(r=>t.append("method",r.id)),e.status?.length&&e.status?.forEach(r=>t.append("statusCode",r.id)),e.date?.type==="predefined"&&(e.date.fromDateOffset&&t.append("fromDate",(Date.now()-e.date.fromDateOffset).toString()),e.date.toDateOffset&&t.append("toDate",(Date.now()-e.date.toDateOffset).toString())),e.date?.type==="custom"&&(t.append("fromDate",e.date.fromDate.toString()),t.append("toDate",e.date.toDate.toString())),t.toString()},{data:m,isLoading:f}=y({queryKey:["API-LOGS-"+d,{activeFilters:e}],queryFn:()=>D(`/api/api-keys/apps/${d}/logs?${u()}`).then(t=>t.json()).then(t=>{const{data:r,total:h}=t;return h&&n(r[0].id),t}),refetchOnWindowFocus:!1}),g=a.useCallback(t=>c(t),[]);return a.createElement(a.Fragment,null,a.createElement(k,null,a.createElement(S,{onFiltersChange:g,activeFilters:e})),s.length>0?a.createElement(P,null,s.map((t,r)=>a.createElement(O,{key:r},t))):null,f?a.createElement(L,null):m?.data.length?a.createElement(I,null,a.createElement(b,null,m?.data.map(t=>a.createElement(x,{key:t.id,log:t,active:t.id===o,onClick:l}))),a.createElement(A,{logId:o})):a.createElement("p",null,"There are no logs yet."))}const I=i.div`
|
|
2
2
|
display: flex;
|
|
3
3
|
`,b=i.div`
|
|
4
4
|
display: flex;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as e from"react";import{useMutation as C,useQueryClient as v}from"@tanstack/react-query";import{useParams as E}from"react-router
|
|
1
|
+
import*as e from"react";import{useMutation as C,useQueryClient as v}from"@tanstack/react-query";import{useParams as E}from"react-router";import{Button as m}from"@redocly/theme/components/Button/Button";import{H3 as h}from"@redocly/theme/components/Typography/H3";import{Admonition as A}from"@redocly/theme/markdoc/components/Admonition/Admonition";import{useTranslate as P}from"../../../../../client/app/hooks";import{Dialog as S}from"./Dialog";import D from"./ApisList";import{ButtonWithLoading as O}from"./ButtonWithLoading";import{ButtonsContainer as b}from"./RollAPIKey";import{DialogRow as w,DialogRowTitle as B,DialogRowContent as I,DialogBodyWrapper as R}from"./DialogStyledComponents";function T({initialEnabledApis:r}){const p=v(),[u,o]=e.useState(!1),{translate:t}=P(),{id:i}=E(),{isPending:l,error:s,mutateAsync:y}=C({mutationFn:()=>fetch(`/api/api-keys/apps/${i}/credentials`,{method:"POST",body:JSON.stringify({apiProductIds:d})}).then(n=>n.json()),onSuccess:()=>{p.invalidateQueries({queryKey:["APP:"+i]})}}),c=()=>o(!0),a=()=>o(!1),g=async()=>{await y(),a()},[d,f]=e.useState(r),k=n=>{f(n||[])};return e.createElement(e.Fragment,null,e.createElement(m,{size:"large",variant:"primary",onClick:c},t("dev.app.key.create","Create key")),e.createElement(S,{open:u,onOpen:c,onClose:a},e.createElement(R,null,e.createElement(h,{"data-translation-key":"dev.create.key.dialog.title"},t("dev.create.key.dialog.title","Create key")),e.createElement(w,null,e.createElement(B,{"data-translation-key":"dev.edit.apis.dialog.selectedAPIs"},t("dev.edit.apis.dialog.selectedAPIs","Selected APIs")),e.createElement(I,null,e.createElement(D,{initialEnabledApis:r,onChange:k}))),s?e.createElement(A,{type:"danger"}," ",s.message," "):null,e.createElement(b,null,e.createElement(m,{onClick:a,"data-translation-key":"dev.create.key.dialog.cancel",variant:"text",size:"large"},t("dev.create.key.dialog.cancel","Cancel")),e.createElement(O,{variant:"primary",onClick:g,disabled:l||d.length<1,loading:l,"data-translation-key":"dev.create.key.dialog.create"},t("dev.create.key.dialog.create","Create key"))))))}export{T as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as e from"react";import{useMutation as f,useQueryClient as y}from"@tanstack/react-query";import{useParams as g}from"react-router
|
|
1
|
+
import*as e from"react";import{useMutation as f,useQueryClient as y}from"@tanstack/react-query";import{useParams as g}from"react-router";import{Button as E}from"@redocly/theme/components/Button/Button";import{H2 as v}from"@redocly/theme/components/Typography/H2";import{Admonition as h}from"@redocly/theme/markdoc/components/Admonition/Admonition";import{usePreloadHistory as D}from"../../../../../client/app/usePreloadHistory";import{useTranslate as C}from"../../../../../client/app/hooks";import{Dialog as k}from"./Dialog";import{ButtonWithLoading as P}from"./ButtonWithLoading";import{ButtonsContainer as A}from"./RollAPIKey";const Q=e.forwardRef((w,p)=>{const d=D(),{id:a}=g(),{translate:t}=C(),s=y(),[m,o]=e.useState(!1),n=()=>o(!0),l=()=>o(!1);e.useImperativeHandle(p,()=>({open:n}));const c=async()=>{await u(),d.push("/apps/")},{isPending:r,error:i,mutateAsync:u}=f({mutationKey:["DELETE_APP"+a],mutationFn:()=>fetch(`/api/api-keys/apps/${a}`,{method:"DELETE"}),onSuccess:()=>{s.invalidateQueries({queryKey:["APPS"]})}});return e.createElement(k,{open:m,onOpen:n,onClose:l},e.createElement(e.Fragment,null,e.createElement(v,{"data-translation-key":"dev.delete.app.dialog.title"},t("dev.delete.app.dialog.title","Delete app")),e.createElement("div",{"data-translation-key":"dev.delete.app.dialog.confirmation"},t("dev.delete.app.dialog.confirmation","Do you really want to delete this app?")),i&&e.createElement(h,{type:"danger"},i.message),e.createElement(A,null,e.createElement(E,{onClick:l,"data-translation-key":"dev.delete.app.dialog.cancel",variant:"text",size:"large"},t("dev.delete.app.dialog.cancel","Cancel")),e.createElement(P,{variant:"primary",onClick:c,disabled:r,loading:r,"data-translation-key":"dev.delete.app.dialog.delete"},t("dev.delete.app.dialog.delete","Delete app")))))});export{Q as DeleteAppDialog};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as e from"react";import{useMutation as h,useQueryClient as k}from"@tanstack/react-query";import{useParams as I}from"react-router
|
|
1
|
+
import*as e from"react";import{useMutation as h,useQueryClient as k}from"@tanstack/react-query";import{useParams as I}from"react-router";import{Button as S}from"@redocly/theme/components/Button/Button";import{H2 as b}from"@redocly/theme/components/Typography/H2";import{Admonition as D}from"@redocly/theme/markdoc/components/Admonition/Admonition";import{useTranslate as w}from"../../../../../client/app/hooks";import{Dialog as B}from"./Dialog";import K from"./ApisList";import{ButtonWithLoading as R}from"./ButtonWithLoading";import{ApiKeySecret as T}from"./ApiKeySecret";import{DialogRow as p,DialogRowContent as c,DialogRowTitle as u,DialogBodyWrapper as q}from"./DialogStyledComponents";import{ButtonsContainer as x}from"./RollAPIKey";function L(g){const{initialEnabledApis:n,secret:y,open:E,onClose:a,id:o}=g,{translate:t}=w(),f=k(),{id:l}=I(),{isPending:r,error:s,mutateAsync:v}=h({mutationFn:()=>fetch(`/api/api-keys/apps/${l}/credentials/${o}`,{method:"PUT",body:JSON.stringify({apiProductIds:[...d]})}).then(i=>i.json()),onSuccess:()=>{f.invalidateQueries({queryKey:["APP:"+l]})}}),A=async()=>{await v(),a()},[d,C]=e.useState(n),P=i=>{C(i||[])},m=o||y;return e.createElement(B,{open:E,onClose:a},e.createElement(q,null,e.createElement(b,{"data-translation-key":"dev.edit.apis.dialog.title"},t("dev.edit.apis.dialog.title","Edit APIs")),e.createElement(p,null,e.createElement(u,{"data-translation-key":"dev.edit.apis.dialog.apiKey"},t("dev.edit.apis.dialog.apiKey","API key")),e.createElement(c,null,m?e.createElement(T,{secret:m}):null)),e.createElement(p,null,e.createElement(u,{"data-translation-key":"dev.edit.apis.dialog.selectedAPIs"},t("dev.edit.apis.dialog.selectedAPIs","Selected APIs")),e.createElement(c,null,e.createElement(K,{initialEnabledApis:n,onChange:P}))),s?e.createElement(D,{type:"danger"}," ",s.message," "):null,e.createElement(x,null,e.createElement(S,{onClick:a,"data-translation-key":"dev.edit.apis.dialog.cancel",variant:"text",size:"large"},t("dev.edit.apis.dialog.cancel","Cancel")),e.createElement(R,{variant:"primary",onClick:A,disabled:r||d.length<1,loading:r,"data-translation-key":"dev.edit.apis.dialog.save"},t("dev.edit.apis.dialog.save","Save")))))}export{L as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as e from"react";import{useMutation as h,useQueryClient as P}from"@tanstack/react-query";import{useParams as D}from"react-router
|
|
1
|
+
import*as e from"react";import{useMutation as h,useQueryClient as P}from"@tanstack/react-query";import{useParams as D}from"react-router";import{Button as k}from"@redocly/theme/components/Button/Button";import{H2 as A}from"@redocly/theme/components/Typography/H2";import{Admonition as S}from"@redocly/theme/markdoc/components/Admonition/Admonition";import{useTranslate as T}from"../../../../../client/app/hooks";import{Dialog as w}from"./Dialog";import{ButtonWithLoading as I}from"./ButtonWithLoading";import{ButtonsContainer as K}from"./RollAPIKey";import{DialogInputMultiline as O,DialogRow as R,DialogRowContent as x}from"./DialogStyledComponents";import{MAX_DESCRIPTION_LENGTH as p,CharacterCounter as B}from"./CharacterCounter";const G=u=>{const{id:o}=D(),{initialDescription:g,open:y,onOpen:f,onClose:s}=u,t=g||"",{translate:a}=T(),[n,i]=e.useState(t);e.useEffect(()=>{i(t)},[t]);const c=async()=>{await C(),s()},E=l=>{l.key==="Enter"&&!r&&c()},d=()=>{s(),i(t)},v=P(),{mutateAsync:C,isPending:r,error:m}=h({mutationKey:["UPDATE_APP"+o],mutationFn:()=>fetch(`/api/api-keys/apps/${o}`,{method:"PUT",body:JSON.stringify({description:n})}),onSettled:()=>{v.invalidateQueries({queryKey:["APP:"+o]}),i(n)}});return e.createElement(w,{open:y,onClose:d,onOpen:f},e.createElement(e.Fragment,null,e.createElement(A,{"data-translation-key":"dev.edit.description.dialog.title"},a("dev.edit.description.dialog.title","Change description")),e.createElement(R,null,e.createElement(x,null,e.createElement(O,{maxLength:p,value:n,onChange:l=>i(l.target.value),onKeyPress:E,rows:6,cols:60}))),e.createElement(B,null,n.length,"/",p),m&&e.createElement(S,{type:"danger"},m.message," "),e.createElement(K,null,e.createElement(k,{onClick:d,"data-translation-key":"dev.edit.description.dialog.cancel",variant:"text",size:"large"},a("dev.edit.description.dialog.cancel","Cancel")),e.createElement(I,{variant:"primary",loading:r,onClick:c,disabled:r,"data-translation-key":"dev.edit.description.dialog.save"},a("dev.edit.description.dialog.save","Save changes")))))};export{G as EditAppDescriptionDialog};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as e from"react";import{useMutation as A,useQueryClient as D}from"@tanstack/react-query";import{useParams as S}from"react-router
|
|
1
|
+
import*as e from"react";import{useMutation as A,useQueryClient as D}from"@tanstack/react-query";import{useParams as S}from"react-router";import{Button as N}from"@redocly/theme/components/Button/Button";import{H2 as w}from"@redocly/theme/components/Typography/H2";import{Admonition as I}from"@redocly/theme/markdoc/components/Admonition/Admonition";import{useTranslate as K}from"../../../../../client/app/hooks";import{Dialog as O}from"./Dialog";import{ButtonWithLoading as R}from"./ButtonWithLoading";import{ButtonsContainer as B}from"./RollAPIKey";import{DialogInput as T,DialogRow as $,DialogRowContent as b}from"./DialogStyledComponents";const M=e.forwardRef((g,f)=>{const{id:o}=S(),{appName:y,appDisplayName:v}=g,t=v||y||"",[a,n]=e.useState(t),[E,m]=e.useState(!1),h=s=>n(s.target.value),{translate:i}=K(),p=async()=>{await k(),l()},C=s=>{s.key==="Enter"&&!r&&u&&p()},d=()=>{m(!0)};e.useImperativeHandle(f,()=>({open:d}));const l=()=>{m(!1),n(t)};e.useEffect(()=>{n(t)},[t]);const P=D(),{mutateAsync:k,isPending:r,error:c}=A({mutationKey:["UPDATE_APP"+o],mutationFn:()=>fetch(`/api/api-keys/apps/${o}`,{method:"PUT",body:JSON.stringify({name:a})}),onSettled:()=>{P.invalidateQueries({queryKey:["APPS","APP:"+o]}),n(a)}}),u=/^(?!\s*$).{1,100}$/gi.test(a);return e.createElement(O,{open:E,onClose:l,onOpen:d},e.createElement(e.Fragment,null,e.createElement(w,{"data-translation-key":"dev.edit.app.dialog.title"},i("dev.edit.app.dialog.title","Change display name")),e.createElement($,null,e.createElement(b,null,e.createElement(T,{value:a,onChange:h,onKeyPress:C}))),c&&e.createElement(I,{type:"danger"},c.message," "),e.createElement(B,null,e.createElement(N,{onClick:l,"data-translation-key":"dev.edit.app.dialog.cancel",variant:"text",size:"large"},i("dev.edit.app.dialog.cancel","Cancel")),e.createElement(R,{variant:"primary",loading:r,onClick:p,disabled:r||!u,"data-translation-key":"dev.edit.app.dialog.save"},i("dev.edit.app.dialog.save","Save changes")))))});export{M as EditAppDialog};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as e from"react";import{useMutation as b,useQueryClient as U}from"@tanstack/react-query";import{useParams as C}from"react-router
|
|
1
|
+
import*as e from"react";import{useMutation as b,useQueryClient as U}from"@tanstack/react-query";import{useParams as C}from"react-router";import{Button as E}from"@redocly/theme/components/Button/Button";import{H2 as w}from"@redocly/theme/components/Typography/H2";import{Admonition as P}from"@redocly/theme/markdoc/components/Admonition/Admonition";import{useTranslate as D}from"../../../../../client/app/hooks";import{Dialog as R}from"./Dialog";import{ButtonWithLoading as S}from"./ButtonWithLoading";import{ButtonsContainer as A}from"./RollAPIKey";import{DialogHelperText as T,DialogInputMultiline as x,DialogRow as K,DialogRowContent as j}from"./DialogStyledComponents";const z=p=>{const{id:o}=C(),{initialCallbackUrl:u,open:g,onOpen:k,onClose:s}=p,l=u||"",{translate:a}=D(),[r,n]=e.useState(l);e.useEffect(()=>{n(l)},[l]);const c=async()=>{await f(),s()},y=t=>{t.key==="Enter"&&!i&&c()},d=()=>{s(),n(l)},h=U(),{mutateAsync:f,isPending:i,error:m}=b({mutationKey:["UPDATE_APP"+o],mutationFn:async()=>{const t=await fetch(`/api/api-keys/apps/${o}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({callbackUrl:r})});if(!t.ok){const v=await t.json().catch(()=>null);throw new Error(v?.error||`Request failed with status ${t.status}`)}return t.json()},onSettled:()=>{h.invalidateQueries({queryKey:["APP:"+o]}),n(r)}});return e.createElement(R,{open:g,onClose:d,onOpen:k},e.createElement(e.Fragment,null,e.createElement(w,{"data-translation-key":"dev.edit.callbackUrls.dialog.title"},a("dev.edit.callbackUrls.dialog.title","Change callback URLs")),e.createElement(K,null,e.createElement(j,null,e.createElement(x,{value:r,onChange:t=>n(t.target.value),onKeyPress:y,placeholder:a("dev.edit.callbackUrls.dialog.placeholder","https://example.com/callback"),rows:3}),e.createElement(T,{"data-translation-key":"dev.edit.callbackUrls.dialog.hint"},a("dev.edit.callbackUrls.dialog.hint","Separate multiple URLs with a comma")))),m&&e.createElement(P,{type:"danger"},m.message," "),e.createElement(A,null,e.createElement(E,{onClick:d,"data-translation-key":"dev.edit.callbackUrls.dialog.cancel",variant:"text",size:"large"},a("dev.edit.callbackUrls.dialog.cancel","Cancel")),e.createElement(S,{variant:"primary",loading:i,onClick:c,disabled:i,"data-translation-key":"dev.edit.callbackUrls.dialog.save"},a("dev.edit.callbackUrls.dialog.save","Save changes")))))};export{z as EditCallbackUrlDialog};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import*as e from"react";import r from"styled-components";import{useMutation as h,useQueryClient as x}from"@tanstack/react-query";import{useParams as A}from"react-router
|
|
1
|
+
import*as e from"react";import r from"styled-components";import{useMutation as h,useQueryClient as x}from"@tanstack/react-query";import{useParams as A}from"react-router";import{Button as b}from"@redocly/theme/components/Button/Button";import{H2 as I}from"@redocly/theme/components/Typography/H2";import{ErrorIcon as P}from"@redocly/theme/icons/ErrorIcon/ErrorIcon";import{Admonition as C}from"@redocly/theme/markdoc/components/Admonition/Admonition";import{useTranslate as R}from"../../../../../client/app/hooks";import{Dialog as K}from"./Dialog";import{ButtonWithLoading as w}from"./ButtonWithLoading";import{Separator as z}from"./ApiKeys";import{getReadableDate as S}from"./AppOverview";const s=5;function _({apiKey:{expiresAt:t,clientId:n,clientSecret:o,id:v}}){const{id:i}=A(),k=x(),[y,d]=e.useState(!1),{translate:a}=R(),g=Number(t)>0?S(t):"Never",u=n||o,p=()=>d(!0),l=()=>d(!1),f=async()=>{await E()},{isPending:m,error:c,mutateAsync:E}=h({mutationKey:["REVOKE_API_KEY"+i],mutationFn:()=>fetch(`/api/api-keys/apps/${i}/credentials/${v}`,{method:"DELETE",body:JSON.stringify({clientId:n,clientSecret:o})}),onSuccess:()=>{k.invalidateQueries({queryKey:["APP:"+i]}),l()}});return e.createElement(e.Fragment,null,e.createElement(D,{onClick:p},e.createElement("span",{"data-translation-key":"dev.app.key.revoke"},a("dev.app.key.revoke","Revoke API key")),e.createElement(P,{size:"24px",color:"#161616"})),e.createElement(K,{open:y,onOpen:p,onClose:l},e.createElement(F,null,e.createElement(e.Fragment,null,e.createElement(I,{"data-translation-key":"dev.revoke.key.dialog.title"},a("dev.revoke.key.dialog.title","Revoke API key")),e.createElement(O,null,e.createElement("span",{"data-translation-key":"dev.revoke.key.dialog.apiKey"},a("dev.revoke.key.dialog.apiKey","API key")),e.createElement("span",null,B(u||"")),e.createElement("span",{"data-translation-key":"dev.revoke.key.dialog.expires"},a("dev.revoke.key.dialog.expires","Expires")),e.createElement("span",null,g)),e.createElement(z,null),e.createElement("div",{"data-translation-key":"dev.revoke.key.dialog.confirmation"},a("dev.revoke.key.dialog.confirmation","Are you sure you want to revoke this API key?")),c&&e.createElement(C,{type:"danger"},c.message),e.createElement($,null,e.createElement(b,{onClick:l,"data-translation-key":"dev.revoke.key.dialog.cancel",variant:"text",size:"large"},a("dev.revoke.key.dialog.cancel","Cancel")),e.createElement(w,{variant:"primary",onClick:f,disabled:m,loading:m,"data-translation-key":"dev.revoke.key.dialog.revoke"},a("dev.revoke.key.dialog.revoke","Revoke API key")))))))}function B(t){if(t.length>s*2){const n=t.slice(0,s),o=t.slice(-s);return`${n} . . . ${o}`}return t}const O=r.div`
|
|
2
2
|
display: grid;
|
|
3
3
|
grid-template-columns: 1fr 1fr;
|
|
4
4
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import*as e from"react";import o from"styled-components";import{useMutation as E,useQueryClient as x}from"@tanstack/react-query";import{useParams as A}from"react-router
|
|
1
|
+
import*as e from"react";import o from"styled-components";import{useMutation as E,useQueryClient as x}from"@tanstack/react-query";import{useParams as A}from"react-router";import{Button as b}from"@redocly/theme/components/Button/Button";import{H2 as P}from"@redocly/theme/components/Typography/H2";import{RenewIcon as I}from"@redocly/theme/icons/RenewIcon/RenewIcon";import{Admonition as R}from"@redocly/theme/markdoc/components/Admonition/Admonition";import{useTranslate as C}from"../../../../../client/app/l10n/hooks";import{Dialog as w}from"./Dialog";import{ButtonWithLoading as K}from"./ButtonWithLoading";import{Separator as z}from"./ApiKeys";import{getReadableDate as S}from"./AppOverview";const s=5;function _({apiKey:{expiresAt:t,clientId:n,clientSecret:l,id:y}}){const{id:r}=A(),{translate:a}=C(),g=x(),[u,d]=e.useState(!1),f=Number(t)>0?S(t):"Never",v=n||l,p=()=>d(!0),i=()=>d(!1),k=async()=>{await h()},{isPending:m,error:c,mutateAsync:h}=E({mutationKey:["ROLL_API_KEY"+r],mutationFn:()=>fetch(`/api/api-keys/apps/${r}/credentials/${y}`,{method:"PUT",body:JSON.stringify({clientId:n,clientSecret:l})}),onSuccess:()=>{g.invalidateQueries({queryKey:["APP:"+r]}),i()}});return e.createElement(e.Fragment,null,e.createElement($,{onClick:p},e.createElement("span",{"data-translation-key":"dev.app.key.roll"},a("dev.app.key.roll","Roll API key")),e.createElement(I,{size:"20px",color:"#161616"})),e.createElement(w,{open:u,onOpen:p,onClose:i},e.createElement(F,null,e.createElement(e.Fragment,null,e.createElement(P,{"data-translation-key":"dev.roll.key.dialog.title"},a("dev.roll.key.dialog.title","Roll API key")),e.createElement(O,null,e.createElement("span",{"data-translation-key":"dev.roll.key.dialog.apiKey"},a("dev.roll.key.dialog.apiKey","API key")),e.createElement("span",null,B(v||"")),e.createElement("span",{"data-translation-key":"dev.roll.key.dialog.expires"},a("dev.roll.key.dialog.expires","Expires")),e.createElement("span",null,f)),e.createElement(z,null),e.createElement("div",{"data-translation-key":"dev.roll.key.dialog.confirmation"},a("dev.roll.key.dialog.confirmation","Are you sure you want to roll this API key?")),c&&e.createElement(R,{type:"danger"},c.message),e.createElement(L,null,e.createElement(b,{onClick:i,"data-translation-key":"dev.roll.key.dialog.cancel",variant:"text",size:"large"},a("dev.roll.key.dialog.cancel","Cancel")),e.createElement(K,{variant:"primary",onClick:k,disabled:m,loading:m,"data-translation-key":"dev.roll.key.dialog.roll"},a("dev.roll.key.dialog.roll","Roll API key")))))))}function B(t){if(t.length>s*2){const n=t.slice(0,s),l=t.slice(-s);return`${n} . . . ${l}`}return t}const O=o.div`
|
|
2
2
|
display: grid;
|
|
3
3
|
grid-template-columns: 1fr 1fr;
|
|
4
4
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as e from"react";import{useMutation as E,useQueryClient as A}from"@tanstack/react-query";import{useParams as C}from"react-router
|
|
1
|
+
import*as e from"react";import{useMutation as E,useQueryClient as A}from"@tanstack/react-query";import{useParams as C}from"react-router";import{Button as h}from"@redocly/theme/components/Button/Button";import{H3 as v}from"@redocly/theme/components/Typography/H3";import{Admonition as P}from"@redocly/theme/markdoc/components/Admonition/Admonition";import{useTranslate as S}from"../../../../../client/app/l10n/hooks";import{ButtonWithLoading as b}from"./ButtonWithLoading";import{Dialog as U}from"./Dialog";import I from"./ApisList";import{ButtonsWrapper as T}from"./ApiKeyItem";function W(p){const m=A(),{credential:a,open:c,onClose:n,onOpen:u}=p,{translate:i}=S(),{id:o}=C(),r=e.useMemo(()=>a.apiProductsStatus?.map(t=>t.apiId||t.id),[a]),[l,y]=e.useState(r),{isPending:d,error:s,mutateAsync:g}=E({mutationKey:["MUTATE_CRED"+o],mutationFn:()=>fetch(`/api/api-keys/apps/${o}/credentials/${a.id}`,{method:"PUT",body:JSON.stringify({apiProductIds:l,clientId:a.clientId,clientSecret:a.clientSecret})}).then(t=>t.json()),onSuccess:()=>{m.invalidateQueries({queryKey:["APP:"+o]})}}),f=async()=>{await g(),n()},k=t=>{y(t||[])};return e.createElement(e.Fragment,null,e.createElement(U,{open:c,onClose:n,onOpen:u},e.createElement(e.Fragment,null,e.createElement(v,{"data-translation-key":"dev.update.key.dialog.title"},i("dev.update.key.dialog.title","Update key")),e.createElement(I,{initialEnabledApis:r,onChange:k}),s?e.createElement(P,{type:"danger"}," ",s.message," "):null,e.createElement(T,null,e.createElement(h,{onClick:n,"data-translation-key":"dev.update.key.dialog.cancel"},i("dev.update.key.dialog.cancel","Cancel")),e.createElement(b,{variant:"primary",onClick:f,disabled:d||l.length<1,loading:d,"data-translation-key":"dev.update.key.dialog.update"},i("dev.update.key.dialog.update","Update key"))))))}export{W as UpdateKey};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as e from"react";import{Navigate as r,Route as t,Routes as n}from"react-router
|
|
1
|
+
import*as e from"react";import{Navigate as r,Route as t,Routes as n}from"react-router";import{QueryClientProvider as l,QueryClient as o}from"@tanstack/react-query";import m from"./AppsList";import a from"./App";const i=new o({defaultOptions:{queries:{refetchOnWindowFocus:!1}}});function p(u){return e.createElement(l,{client:i},e.createElement(n,null,e.createElement(t,{path:"/",element:e.createElement(m,null)},e.createElement(t,{path:"/:id",element:e.createElement(a,null)})),e.createElement(t,{path:"*",element:e.createElement(r,{to:"..",replace:!0})})))}export{p as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{CREATE_ANONYMOUS_SESSION_TOOL_NAME as _}from"../constants.js";const
|
|
1
|
+
import{CREATE_ANONYMOUS_SESSION_TOOL_NAME as _,MANAGE_API_CREDENTIALS_TOOL_NAME as E}from"../constants.js";const O="execute",t="describe-tools",e="fetch",A=new Set([O,t,_,E]),N="tools",T="__fetchBridge",L="fetch";export{A as CODE_MODE_TOOL_NAMES,t as DESCRIBE_TOOLS_TOOL_NAME,O as EXECUTE_TOOL_NAME,L as FETCH_BRIDGE_FUNCTION_NAME,T as FETCH_BRIDGE_GLOBAL_NAME,e as FETCH_TOOL_NAME,N as TOOLS_BRIDGE_GLOBAL_NAME};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { CallToolResult, InputRequiredResult } from '@modelcontextprotocol/server';
|
|
2
|
+
import type { McpToolSchema } from '../../../../types';
|
|
3
|
+
import type { McpToolContext } from '../../types.js';
|
|
4
|
+
import type { KvService } from '@redocly/config';
|
|
5
|
+
export type ManageApiCredentialsDeps = {
|
|
6
|
+
getClientCapabilities: () => {
|
|
7
|
+
elicitation?: {
|
|
8
|
+
url?: unknown;
|
|
9
|
+
};
|
|
10
|
+
} | undefined;
|
|
11
|
+
getKv: () => Promise<KvService>;
|
|
12
|
+
};
|
|
13
|
+
export declare function isManageApiCredentialsToolAvailable(context: McpToolContext): boolean;
|
|
14
|
+
export declare function manageApiCredentialsSchema(context: McpToolContext): McpToolSchema;
|
|
15
|
+
export declare function manageApiCredentials(context: McpToolContext, deps: ManageApiCredentialsDeps): (args: {
|
|
16
|
+
api?: unknown;
|
|
17
|
+
sessionHandle?: unknown;
|
|
18
|
+
}) => Promise<CallToolResult | InputRequiredResult>;
|
|
19
|
+
//# sourceMappingURL=manage-credentials.d.ts.map
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{MANAGE_API_CREDENTIALS_TOOL_NAME as m}from"../../constants.js";import{buildUpstreamCredentialFallback as h,buildUpstreamCredentialInputRequired as f}from"../../gateway-mcp/elicitation.js";import{getCallableApis as o,isFetchCapable as g}from"../../gateway-mcp/fetch-capability.js";import{secretStore as A}from"../../gateway-mcp/secret-store.js";import{MISSING_SESSION_HANDLE_MESSAGE as v,resolveSecretSubject as b}from"../../gateway-mcp/subject.js";import{resolveUpstreamAuthScheme as y}from"../../gateway-mcp/upstream-auth-scheme.js";function k(e){return!!e.elicitationEnabled&&g(e)}function j(e){const t=o(e).map(n=>n.relativePath);return{name:m,description:["Get a secure link where the user sets, replaces, or removes the credentials this server sends to a documented API.","Call it when the user wants to change or clear an API key, or when a tool result tells you the stored credentials were rejected.","It returns a link for the user to open - never ask for the secret in the conversation and never pass it as an argument."].join(`
|
|
2
|
+
`),annotations:{title:"Manage API credentials",readOnlyHint:!1,openWorldHint:!1},schema:{type:"object",required:["api"],additionalProperties:!1,properties:{api:{type:"string",description:`The API to manage credentials for.${t.length>0?` One of: ${t.join(", ")}.`:""}`,minLength:1,...t.length>0?{enum:t}:{}},sessionHandle:{type:"string",description:"Handle returned by createAnonymousSession. Required while the caller is anonymous, so the credentials are stored against the same subject the API calls use.",minLength:1}}}}}function H(e,i){return async t=>{const n=b(e.user,typeof t.sessionHandle=="string"?t.sessionHandle:void 0);if(n.kind==="invalid")return l(n.message);if(n.kind==="anonymous")return l(v);const{subject:u}=n,s=typeof t.api=="string"?t.api:"",r=o(e).find(a=>a.relativePath===s||a.name===s);if(!r){const a=o(e).map(d=>d.relativePath).join(", ");return l(`Unknown api "${s}". Credentials can only be managed for the APIs this server can call: ${a||"none"}.`)}const c=await i.getKv(),p={subject:u,baseUrl:e.baseUrl??"",target:{api:r.relativePath,name:r.name},auth:await y(e,r.relativePath),kv:c,hasExistingSecret:await A.hasSecret(c,u,r.relativePath),reason:{kind:"requested"}};return i.getClientCapabilities()?.elicitation?.url?await f(p):{content:[{type:"text",text:await h(p)}],isError:!1}}}function l(e){return{content:[{type:"text",text:e}],isError:!0}}export{k as isManageApiCredentialsToolAvailable,H as manageApiCredentials,j as manageApiCredentialsSchema};
|
|
@@ -15,4 +15,5 @@ export declare const MAX_DOCUMENTS_PER_CATEGORY = 3;
|
|
|
15
15
|
export declare const MCP_NOT_ALLOWED_TEMPLATE_ID = "mcp-not-allowed";
|
|
16
16
|
export declare const DEFAULT_DOCS_MCP_SERVER_NAME = "Docs MCP server";
|
|
17
17
|
export declare const CREATE_ANONYMOUS_SESSION_TOOL_NAME = "createAnonymousSession";
|
|
18
|
+
export declare const MANAGE_API_CREDENTIALS_TOOL_NAME = "manageApiCredentials";
|
|
18
19
|
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{LATEST_PROTOCOL_VERSION as o}from"@modelcontextprotocol/server";const
|
|
1
|
+
import{LATEST_PROTOCOL_VERSION as o}from"@modelcontextprotocol/server";const r={Docs:"docs"},e={InternalError:-32603,ServerError:-32e3,InvalidRequest:-32600,MethodNotFound:-32601,InvalidParams:-32602},_=o,t=3e4,O=15e3,n=3,s="mcp-not-allowed",A="Docs MCP server",T="createAnonymousSession",M="manageApiCredentials";export{T as CREATE_ANONYMOUS_SESSION_TOOL_NAME,t as DEFAULT_CONNECTION_TIMEOUT_MS,A as DEFAULT_DOCS_MCP_SERVER_NAME,M as MANAGE_API_CREDENTIALS_TOOL_NAME,n as MAX_DOCUMENTS_PER_CATEGORY,s as MCP_NOT_ALLOWED_TEMPLATE_ID,_ as MCP_PROTOCOL_VERSION,e as McpErrorCodes,r as McpServerType,O as SSE_KEEPALIVE_INTERVAL_MS};
|
|
@@ -3,26 +3,46 @@ import type { UpstreamAuthRequiredEvent } from './upstream-auth.js';
|
|
|
3
3
|
import type { UpstreamAuthScheme } from './upstream-auth-scheme.js';
|
|
4
4
|
import type { KvService } from '@redocly/config';
|
|
5
5
|
export declare const CREDENTIALS_INPUT_REQUEST_KEY = "credentials";
|
|
6
|
+
export type CredentialTarget = {
|
|
7
|
+
api: string;
|
|
8
|
+
name: string;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Why the prompt is being raised. `authFailure` is the automatic path behind an upstream 401;
|
|
12
|
+
* `requested` is the model calling the credentials tool on the user's behalf.
|
|
13
|
+
*/
|
|
14
|
+
export type CredentialElicitationReason = {
|
|
15
|
+
kind: 'authFailure';
|
|
16
|
+
status: number;
|
|
17
|
+
otherApiNames?: string[];
|
|
18
|
+
} | {
|
|
19
|
+
kind: 'requested';
|
|
20
|
+
};
|
|
21
|
+
type CredentialElicitationRequest = {
|
|
22
|
+
subject: string;
|
|
23
|
+
baseUrl: string;
|
|
24
|
+
target: CredentialTarget;
|
|
25
|
+
auth: UpstreamAuthScheme;
|
|
26
|
+
kv: KvService;
|
|
27
|
+
hasExistingSecret: boolean;
|
|
28
|
+
reason: CredentialElicitationReason;
|
|
29
|
+
};
|
|
6
30
|
export declare function isCredentialElicitationDeclined(inputResponses: Record<string, unknown> | undefined): boolean;
|
|
31
|
+
export declare function targetOf(event: UpstreamAuthRequiredEvent): CredentialTarget;
|
|
7
32
|
/**
|
|
8
33
|
* The 2026-07-28-era credentials prompt: the tool call returns an input-required result the
|
|
9
34
|
* client fulfils and retries, with no push `elicitation/create` and no session behind it.
|
|
10
35
|
* Nothing has to survive the round trip - the secrets are keyed by `subject`, which is
|
|
11
36
|
* recomputed from the caller's identity on every request.
|
|
12
37
|
*/
|
|
13
|
-
export declare function buildUpstreamCredentialInputRequired(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
export declare function
|
|
22
|
-
|
|
23
|
-
baseUrl: string;
|
|
24
|
-
events: UpstreamAuthRequiredEvent[];
|
|
25
|
-
auth: UpstreamAuthScheme;
|
|
26
|
-
kv: KvService;
|
|
27
|
-
}): Promise<string>;
|
|
38
|
+
export declare function buildUpstreamCredentialInputRequired(request: CredentialElicitationRequest): Promise<InputRequiredResult>;
|
|
39
|
+
/** Fallback text for a client without URL-elicitation support. */
|
|
40
|
+
export declare function buildUpstreamCredentialFallback(request: CredentialElicitationRequest): Promise<string>;
|
|
41
|
+
/**
|
|
42
|
+
* Text for an authorization failure we deliberately do NOT prompt for: a stored secret the
|
|
43
|
+
* upstream rejected, or a 403/407 that is more likely a permissions problem than a missing key.
|
|
44
|
+
* Minting another link here is what produced the credentials-link loop.
|
|
45
|
+
*/
|
|
46
|
+
export declare function buildCredentialsToolHint(event: UpstreamAuthRequiredEvent): string;
|
|
47
|
+
export {};
|
|
28
48
|
//# sourceMappingURL=elicitation.d.ts.map
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import{inputRequired as s,inputResponse as p}from"@modelcontextprotocol/server";import{secretStore as
|
|
2
|
-
`)}
|
|
1
|
+
import{inputRequired as s,inputResponse as p}from"@modelcontextprotocol/server";import{MANAGE_API_CREDENTIALS_TOOL_NAME as m}from"../constants.js";import{secretStore as T,ELICITATION_TTL_MS as f}from"./secret-store.js";import{ANONYMOUS_SUBJECT_PREFIX as l}from"./subject.js";const c="credentials";function C(e){const t=p(e,c);return t.kind==="elicit"&&(t.action==="decline"||t.action==="cancel")}function _(e){return{api:e.api,name:e.name}}async function y(e){const{url:t}=await d(e);return s({inputRequests:{[c]:s.elicitUrl({message:A(e.target.name,e.hasExistingSecret),url:t})}})}async function b(e){const{url:t}=await d(e);return I(e,t,$(e.subject))}function O(e){return[e.status===401?`The stored credentials for "${e.name}" were rejected (HTTP 401).`:`The "${e.name}" API rejected the request with HTTP ${e.status}.`,"",`Do not retry the same call. Call \`${m}\` with api: "${e.api}" to get a link where the user can replace or remove the stored credentials, then retry once they confirm.`].join(`
|
|
2
|
+
`)}async function d({kv:e,subject:t,baseUrl:n,target:i,auth:a,hasExistingSecret:o}){const r=await T.createElicitation(e,{subject:t,api:i.api,name:i.name,auth:a,hasExistingSecret:o});return{token:r,url:E(n,r)}}function $(e){return e.startsWith(l)?e.slice(l.length):void 0}function A(e,t){return t?`Open the link to replace or remove the stored credentials for the "${e}" API. They never pass through the chat.`:`The "${e}" API needs credentials. Open the link to add them. They never pass through the chat.`}function E(e,t){if(!e)return`/mcp/credentials?token=${t}`;try{const n=new URL("/mcp/credentials",e);return n.searchParams.set("token",t),n.toString()}catch{return`${e}/mcp/credentials?token=${t}`}}function I({target:e,reason:t,hasExistingSecret:n},i,a){const o=[t.kind==="authFailure"?`The upstream API "${e.name}" returned ${t.status} and needs credentials.`:n?`Credentials are already stored for the "${e.name}" API; the link below lets the user replace or remove them.`:`The "${e.name}" API needs credentials.`,"",`**Action required: show the user the link below and ask them to open it in a browser (valid ${Math.round(f/6e4)} minutes):**`,"",i,"",a?`Then call this tool again with sessionHandle: "${a}" - the credentials are stored against that handle, applied server-side, and never appear in the conversation.`:"Then call this tool again - the credentials are applied server-side and never appear in the conversation."],r=t.kind==="authFailure"?t.otherApiNames??[]:[];if(r.length>0){const h=r.map(u=>`"${u}"`).join(", ");o.push("",`The remaining APIs (${h}) will prompt for credentials on subsequent calls.`)}return o.join(`
|
|
3
|
+
`)}export{c as CREDENTIALS_INPUT_REQUEST_KEY,O as buildCredentialsToolHint,b as buildUpstreamCredentialFallback,y as buildUpstreamCredentialInputRequired,C as isCredentialElicitationDeclined,_ as targetOf};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Agent as P}from"undici";import{envConfig as W}from"../../../../config/env-config.js";import{logger as
|
|
1
|
+
import{Agent as P}from"undici";import{envConfig as W}from"../../../../config/env-config.js";import{logger as R}from"../../../../tools/notifiers/logger.js";import{assertUrlAllowed as H}from"./ssrf-guard.js";import{createSsrfSafeLookup as I}from"./ssrf-safe-lookup.js";import{SsrfBlockedError as C}from"./ssrf-blocklist.js";import{GatewayFetchBlockedError as $,GatewayFetchSystemError as J,GatewayFetchUserError as L}from"./fetch-errors.js";import{redactHeaders as x,redactSecretsFromString as K}from"./redact.js";import{normalizeFetchPath as V}from"./normalize-path.js";import{reportGatewayFetch as O}from"./fetch-telemetry.js";const G="x-forwarded-for",X=3e4,Y=1024*1024,z=new Set([401,403,407]);let T;function Q(){if(!T){const t=W.isDevelopMode;t&&R.warn("Gateway MCP SSRF guard: loopback upstreams are ALLOWED (develop mode only, never in runtime)."),T=new P({connect:{lookup:I({allowLoopback:t})}})}return T}function ge({allowedHosts:t,apis:e=[],authorization:r,onAuthRequired:n,fetchImpl:c=fetch,dispatcher:u=Q(),timeoutMs:i=X,maxResponseBytes:h=Y,maxFetchesPerExec:f,clientIp:m}){const F=[...t,...e.flatMap(o=>o.matchers)],N=[r,...e.map(o=>o.secret?.value)].filter(o=>!!o),k=e.map(o=>o.secret?.header).filter(o=>!!o);let v=0;return async o=>{const g=o.method??"GET",D=Date.now();let U,S,b,E={},_,A;try{if(f!==void 0){const l=Math.max(0,f);if(v+=1,v>l)throw new L(`Outbound fetch budget exceeded (max ${l} fetches per execute); split the work across calls`)}const d=H(o.url,F);U=d.hostname,S=V(d.pathname);const y=e.filter(l=>l.matchers.some(j=>j.test(d.hostname))),a=y.length===1?y[0]:void 0;b=a?.slug,E=ae(o.headers,a?.secret,r,m);const w=new AbortController;A=setTimeout(()=>w.abort(),i);const s=await c(d,{method:g,headers:E,body:o.body,redirect:"manual",signal:w.signal,dispatcher:u});_=s.status,z.has(s.status)&&a&&n&&n({api:a.slug,name:a.name,status:s.status});const M=await ne(s,h);let p=se(s.headers);if(te(s.status)){p=x(p,k);const{location:l}=p;l&&!re(l,d,F)&&delete p.location}return O({host:d.hostname,method:g,outcome:"success",durationMs:Date.now()-D,path:S,api:b,status:s.status,bytes:Buffer.byteLength(M,"utf8")}),{status:s.status,statusText:s.statusText,ok:s.ok,headers:p,bodyText:M}}catch(d){const{outcome:y,userSafe:a}=q(d),w=U??(a instanceof $?a.host:void 0)??ee(o.url)??"invalid-url";throw O({host:w,method:g,outcome:y,durationMs:Date.now()-D,path:S,api:b,status:_}),a||(R.error(`Gateway MCP fetch failed [${g} ${w}]: ${K(oe(d),N)} headers=${JSON.stringify(x(E,k))}`),new J("Upstream fetch failed due to an internal error"))}finally{A!==void 0&&clearTimeout(A)}}}function Z(t){let e=t;for(let r=0;r<5&&e instanceof Error;r++){if(e instanceof L||e instanceof C)return e;e=e.cause}}function q(t){const e=Z(t);return{outcome:e instanceof $||e instanceof C?"blocked":e?"user_error":"system_error",userSafe:e}}function ee(t){try{return new URL(t).hostname||void 0}catch{return}}function te(t){return t>=300&&t<400}function re(t,e,r){try{return!!H(new URL(t,e).href,r)}catch{return!1}}function oe(t){if(!(t instanceof Error))return String(t);const e=t.cause;return e instanceof Error?`${t.message}: ${e.message}`:t.message}async function ne(t,e){const r=Number(t.headers.get("content-length"));if(Number.isFinite(r)&&r>e)throw B(e,r);if(!t.body)return"";const n=t.body.getReader(),c=new TextDecoder;let u=0,i="";try{for(;;){const{done:h,value:f}=await n.read();if(h)break;if(u+=f.byteLength,u>e)throw B(e,u);i+=c.decode(f,{stream:!0})}return i+c.decode()}finally{await n.cancel().catch(()=>{})}}function B(t,e){return new L(`Response is too large (over ${t} bytes, got ${e}); request less data`)}function ae(t,e,r,n){const c={},u=new Set;for(const[h,f]of Object.entries(t??{})){const m=h.toLowerCase();m==="host"||m==="content-length"||m!==G&&(u.add(m),c[h]=f)}n&&(c[G]=n);const i=e??(r?{header:"Authorization",value:r}:void 0);return i&&!u.has(i.header.toLowerCase())&&(c[i.header]=i.value),c}function se(t){const e={};return t.forEach((r,n)=>{e[n.toLowerCase()]=r}),e}export{ge as buildFetchBridge,Q as getDefaultAgent,ne as readCappedBody};
|
|
@@ -10,11 +10,14 @@ export type ElicitationTarget = {
|
|
|
10
10
|
api: string;
|
|
11
11
|
name: string;
|
|
12
12
|
auth: UpstreamAuthScheme;
|
|
13
|
+
hasExistingSecret?: boolean;
|
|
13
14
|
};
|
|
14
15
|
export declare class SecretStore {
|
|
15
16
|
#private;
|
|
16
17
|
setSecret(kv: KvService, subject: string, api: string, secret: StoredSecret): Promise<void>;
|
|
17
18
|
getSecretsForSubject(kv: KvService, subject: string): Promise<Record<string, StoredSecret>>;
|
|
19
|
+
hasSecret(kv: KvService, subject: string, api: string): Promise<boolean>;
|
|
20
|
+
deleteSecret(kv: KvService, subject: string, api: string): Promise<void>;
|
|
18
21
|
deleteSubject(kv: KvService, subject: string): Promise<void>;
|
|
19
22
|
createElicitation(kv: KvService, target: ElicitationTarget): Promise<string>;
|
|
20
23
|
peekElicitation(kv: KvService, token: string): Promise<ElicitationTarget | undefined>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createCipheriv as o,createDecipheriv as f,createHash as u,hkdfSync as a,randomBytes as h,randomUUID as
|
|
1
|
+
import{createCipheriv as o,createDecipheriv as f,createHash as u,hkdfSync as a,randomBytes as h,randomUUID as l}from"node:crypto";import{JWT_SECRET_KEY as c}from"../../../constants/common.js";import{secretTtlSeconds as m}from"./subject.js";const p=900*1e3,d="mcp-upstream-secrets",y="mcp-elicitation",S="mcp-elicitation";class g{async setSecret(t,e,r,n){const i={api:r,...this.#c(this.#a(e),n,r)};await t.set(this.#t(e,r),i,{ttlInSeconds:m(e)})}async getSecretsForSubject(t,e){const r=this.#a(e),{items:n}=await t.list({prefix:this.#n(e)}),i={};for(const s of n)if(s.value)try{i[s.value.api]=this.#i(r,s.value,s.value.api)}catch{continue}return i}async hasSecret(t,e,r){return!!await t.get(this.#t(e,r))}async deleteSecret(t,e,r){await t.delete(this.#t(e,r))}async deleteSubject(t,e){const{items:r}=await t.list({prefix:this.#n(e)}),n=r.flatMap(i=>i.value?[this.#t(e,i.value.api)]:[]);n.length!==0&&await t.transaction(async i=>{for(const s of n)await i.delete(s)})}async createElicitation(t,e){const r=l();return await t.set(this.#e(r),this.#c(this.#r(),e),{ttlInSeconds:p/1e3}),r}async peekElicitation(t,e){const r=await t.get(this.#e(e));if(r)try{return this.#i(this.#r(),r)}catch{return}}async consumeElicitation(t,e){const r=this.#e(e),n=await t.transaction(async i=>{const s=await i.get(r);return s&&await i.delete(r),s});if(n)try{return this.#i(this.#r(),n)}catch{return}}#n(t){return["mcp","secret",this.#s(t)]}#t(t,e){return["mcp","secret",this.#s(t),e]}#e(t){return["mcp","elicitation",t]}#s(t){return u("sha256").update(t).digest("hex")}#a(t){return Buffer.from(a("sha256",c,t,d,32))}#r(){return Buffer.from(a("sha256",c,S,y,32))}#c(t,e,r){const n=h(12),i=o("aes-256-gcm",t,n);r&&i.setAAD(Buffer.from(r,"utf8"));const s=Buffer.concat([i.update(JSON.stringify(e),"utf8"),i.final()]);return{iv:n.toString("base64"),tag:i.getAuthTag().toString("base64"),data:s.toString("base64")}}#i(t,e,r){const n=f("aes-256-gcm",t,Buffer.from(e.iv,"base64"));r&&n.setAAD(Buffer.from(r,"utf8")),n.setAuthTag(Buffer.from(e.tag,"base64"));const i=Buffer.concat([n.update(Buffer.from(e.data,"base64")),n.final()]).toString("utf8");return JSON.parse(i)}}const B=new g;export{p as ELICITATION_TTL_MS,g as SecretStore,B as secretStore};
|
|
@@ -3,6 +3,7 @@ export declare const UPSTREAM_AUTH_REQUIRED_META = "com.redocly/upstream-auth-re
|
|
|
3
3
|
export type UpstreamAuthRequiredEvent = {
|
|
4
4
|
api: string;
|
|
5
5
|
name: string;
|
|
6
|
+
status: number;
|
|
6
7
|
};
|
|
7
8
|
export declare function withAuthRequiredMeta(result: CallToolResult, events: UpstreamAuthRequiredEvent[]): CallToolResult;
|
|
8
9
|
export declare function readAuthRequiredMeta(result: CallToolResult): UpstreamAuthRequiredEvent[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
1
|
+
const i="com.redocly/upstream-auth-required";function u(t,n){const e=new Map;for(const a of n){const o=e.get(a.api);(!o||o.status!==401&&a.status===401)&&e.set(a.api,a)}const r=[...e.values()];return r.length===0?t:{...t,_meta:{...t._meta,[i]:r}}}function p(t){const n=t._meta?.[i];return Array.isArray(n)?n.filter(e=>typeof e?.api=="string"&&typeof e?.name=="string"&&typeof e?.status=="number"):[]}function s(t){if(!t._meta||!(i in t._meta))return t;const{[i]:n,...e}=t._meta,r={...t};return Object.keys(e).length>0?r._meta=e:delete r._meta,r}export{i as UPSTREAM_AUTH_REQUIRED_META,p as readAuthRequiredMeta,s as stripAuthRequiredMeta,u as withAuthRequiredMeta};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{secretStore as
|
|
1
|
+
import{secretStore as s}from"../gateway-mcp/secret-store.js";import{buildAllowedHostMatchers as C}from"../gateway-mcp/fetch/allowed-hosts.js";import{assertUrlAllowed as P}from"../gateway-mcp/fetch/ssrf-guard.js";import{readCappedBody as I,getDefaultAgent as E}from"../gateway-mcp/fetch/fetch-bridge.js";const p={"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-store","X-Frame-Options":"DENY","Referrer-Policy":"no-referrer","Content-Security-Policy":"default-src 'none'; style-src 'unsafe-inline'; form-action 'self'"},_=3e4,T=64*1024;let k=fetch;function Y(e){k=e}const R=async(e,t)=>{const n=await t.getKv();if(e.method==="GET"){const o=new URL(e.url).searchParams.get("token")??"",r=await s.peekElicitation(n,o);return r?new Response(q(r,o),{status:200,headers:p}):u()}if(e.method==="POST"){const o=await e.formData(),r=String(o.get("token")??""),m=String(o.get("mode")??"token");return m==="client-credentials"?O(n,o,r):m==="clear"?F(n,r):z(n,o,r)}return new Response("Method Not Allowed",{status:405,headers:{Allow:"GET, POST"}})};async function z(e,t,n){const o=String(t.get("secret")??"");if(!o)return u();const r=await s.consumeElicitation(e,n);return r?(await s.setSecret(e,r.subject,r.api,B(r.auth,o)),new Response(x(r.name),{status:200,headers:p})):u()}async function F(e,t){const n=await s.consumeElicitation(e,t);return n?(await s.deleteSecret(e,n.subject,n.api),new Response(D(n.name),{status:200,headers:p})):u()}async function O(e,t,n){const o=String(t.get("client_id")??"").trim(),r=String(t.get("client_secret")??"").trim();if(!o||!r)return u();const m=await s.peekElicitation(e,n);if(!m||m.auth.kind!=="oauth2ClientCredentials")return u();const i=await s.consumeElicitation(e,n);if(!i||i.auth.kind!=="oauth2ClientCredentials")return u();const b=i.auth;try{const A=C([{url:b.tokenUrl}]),$=P(b.tokenUrl,A),v=new AbortController,S=setTimeout(()=>v.abort(),_);let y;try{y=await k($,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"client_credentials",client_id:o,client_secret:r,...b.scopes.length>0?{scope:b.scopes.join(" ")}:{}}),redirect:"manual",signal:v.signal,dispatcher:E()})}finally{clearTimeout(S)}if(!y.ok)return f(i.name);let w;try{w=await I(y,T)}catch{return f(i.name)}let h;try{h=JSON.parse(w)}catch{return f(i.name)}const g=typeof h=="object"&&h!==null&&"access_token"in h?h.access_token:void 0;return typeof g!="string"||!g?f(i.name):(await s.setSecret(e,i.subject,i.api,{header:"Authorization",value:`Bearer ${g}`}),new Response(x(i.name),{status:200,headers:p}))}catch{return f(i.name)}}function B(e,t){return e.kind==="apiKey"?{header:e.header,value:t}:e.kind==="bearer"||e.kind==="oauth2ClientCredentials"?{header:"Authorization",value:j(t)}:{header:"Authorization",value:t}}function j(e){const t=e.trim();return t.toLowerCase().startsWith("bearer")?t:`Bearer ${t}`}function a(e){return e.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""")}function c(e){return`<!doctype html>
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
@@ -20,6 +20,9 @@ input:focus{outline:none;border-color:#4f46e5;box-shadow:0 0 0 3px rgba(79,70,22
|
|
|
20
20
|
button{margin-top:1.25rem;padding:.625rem 1.5rem;font-size:1rem;font-weight:600;color:#fff;background:#4f46e5;border:none;border-radius:.5rem;cursor:pointer}
|
|
21
21
|
button:hover{background:#4338ca}
|
|
22
22
|
button:focus-visible{outline:2px solid #4f46e5;outline-offset:2px}
|
|
23
|
+
button.danger{background:#b91c1c}
|
|
24
|
+
button.danger:hover{background:#991b1b}
|
|
25
|
+
button.danger:focus-visible{outline-color:#b91c1c}
|
|
23
26
|
code{background:#f3f4f6;padding:.125rem .375rem;border-radius:.25rem;font-size:.85em}
|
|
24
27
|
footer{margin-top:1.5rem;padding-top:1rem;border-top:1px solid #e5e7eb;font-size:.75rem;color:#9ca3af;text-align:center}
|
|
25
28
|
@media (prefers-color-scheme:dark){
|
|
@@ -34,34 +37,45 @@ footer{border-top-color:#1f2937;color:#6b7280}
|
|
|
34
37
|
</style>
|
|
35
38
|
</head>
|
|
36
39
|
<body><div class="card">${e}</div></body>
|
|
37
|
-
</html>`}function
|
|
38
|
-
|
|
40
|
+
</html>`}function d(e){return`<h1>\u{1F512} ${a(e)}</h1>`}function l(){return"<footer>Secured by Redocly · credentials never enter the AI conversation</footer>"}function q(e,t){const n=e.hasExistingSecret?H(t):"";switch(e.auth.kind){case"bearer":return N(e.name,t,n);case"apiKey":return U(e.name,t,e.auth.header,n);case"oauth2ClientCredentials":return K(e.name,t,e.auth.scopes,n);default:return M(e.name,t,n)}}function H(e){return`
|
|
41
|
+
<div class="section">
|
|
42
|
+
<h2>Remove stored credentials</h2>
|
|
43
|
+
<p class="hint">This API already has credentials stored. Save above to replace them, or remove them to make calls anonymous again.</p>
|
|
44
|
+
<form method="post" action="/mcp/credentials" autocomplete="off">
|
|
45
|
+
<input type="hidden" name="token" value="${a(e)}">
|
|
46
|
+
<input type="hidden" name="mode" value="clear">
|
|
47
|
+
<button class="danger" type="submit">Remove credentials</button>
|
|
48
|
+
</form>
|
|
49
|
+
</div>`}function N(e,t,n){return c(`
|
|
50
|
+
${d(`Credentials for ${e}`)}
|
|
39
51
|
<p class="hint">Stored encrypted for you only (24 hours for anonymous callers, 30 days when signed in) and sent to the API as the <code>Authorization</code> header. It never passes through the AI conversation.</p>
|
|
40
52
|
<form method="post" action="/mcp/credentials" autocomplete="off">
|
|
41
|
-
<input type="hidden" name="token" value="${
|
|
53
|
+
<input type="hidden" name="token" value="${a(t)}">
|
|
42
54
|
<input type="hidden" name="mode" value="token">
|
|
43
55
|
<label for="secret">API token</label>
|
|
44
56
|
<input type="password" id="secret" name="secret" placeholder="your API token" required autofocus>
|
|
45
57
|
<p class="hint">Paste the raw token or one already prefixed with <code>Bearer</code> — we add the prefix automatically if it is missing.</p>
|
|
46
58
|
<button type="submit">Save credentials</button>
|
|
47
59
|
</form>
|
|
48
|
-
${
|
|
49
|
-
${
|
|
50
|
-
|
|
60
|
+
${n}
|
|
61
|
+
${l()}`)}function U(e,t,n,o){return c(`
|
|
62
|
+
${d(`Credentials for ${e}`)}
|
|
63
|
+
<p class="hint">Stored encrypted for you only (24 hours for anonymous callers, 30 days when signed in) and sent to the API as the <code>${a(n)}</code> header. It never passes through the AI conversation.</p>
|
|
51
64
|
<form method="post" action="/mcp/credentials" autocomplete="off">
|
|
52
|
-
<input type="hidden" name="token" value="${
|
|
65
|
+
<input type="hidden" name="token" value="${a(t)}">
|
|
53
66
|
<input type="hidden" name="mode" value="token">
|
|
54
|
-
<label for="secret">${
|
|
67
|
+
<label for="secret">${a(n)} value</label>
|
|
55
68
|
<input type="password" id="secret" name="secret" placeholder="value" required autofocus>
|
|
56
69
|
<button type="submit">Save credentials</button>
|
|
57
70
|
</form>
|
|
58
|
-
${
|
|
59
|
-
${
|
|
71
|
+
${o}
|
|
72
|
+
${l()}`)}function K(e,t,n,o){const r=n.length>0?n.map(a).join(", "):"default scopes";return c(`
|
|
73
|
+
${d(`Credentials for ${e}`)}
|
|
60
74
|
<p class="hint">Paste a token you already have, or let us exchange client credentials for one via OAuth2 client_credentials.</p>
|
|
61
75
|
<div class="section">
|
|
62
76
|
<h2>I have a token</h2>
|
|
63
77
|
<form method="post" action="/mcp/credentials" autocomplete="off">
|
|
64
|
-
<input type="hidden" name="token" value="${
|
|
78
|
+
<input type="hidden" name="token" value="${a(t)}">
|
|
65
79
|
<input type="hidden" name="mode" value="token">
|
|
66
80
|
<label for="secret">API token</label>
|
|
67
81
|
<input type="password" id="secret" name="secret" placeholder="your API token" required autofocus>
|
|
@@ -71,9 +85,9 @@ ${c(`Credentials for ${e}`)}
|
|
|
71
85
|
</div>
|
|
72
86
|
<div class="section">
|
|
73
87
|
<h2>Exchange client credentials</h2>
|
|
74
|
-
<p class="hint">We request a token from the API on your behalf. Scopes: ${
|
|
88
|
+
<p class="hint">We request a token from the API on your behalf. Scopes: ${r}.</p>
|
|
75
89
|
<form method="post" action="/mcp/credentials" autocomplete="off">
|
|
76
|
-
<input type="hidden" name="token" value="${
|
|
90
|
+
<input type="hidden" name="token" value="${a(t)}">
|
|
77
91
|
<input type="hidden" name="mode" value="client-credentials">
|
|
78
92
|
<label for="client_id">Client ID</label>
|
|
79
93
|
<input type="text" id="client_id" name="client_id" required>
|
|
@@ -82,20 +96,25 @@ ${c(`Credentials for ${e}`)}
|
|
|
82
96
|
<button type="submit">Exchange & save</button>
|
|
83
97
|
</form>
|
|
84
98
|
</div>
|
|
85
|
-
${
|
|
86
|
-
${
|
|
99
|
+
${o}
|
|
100
|
+
${l()}`)}function M(e,t,n){return c(`
|
|
101
|
+
${d(`Credentials for ${e}`)}
|
|
87
102
|
<p class="hint">The value is stored encrypted for you only (24 hours for anonymous callers, 30 days when signed in) and is sent to the API as the <code>Authorization</code> header. It never passes through the AI conversation.</p>
|
|
88
103
|
<form method="post" action="/mcp/credentials" autocomplete="off">
|
|
89
|
-
<input type="hidden" name="token" value="${
|
|
104
|
+
<input type="hidden" name="token" value="${a(t)}">
|
|
90
105
|
<input type="hidden" name="mode" value="token">
|
|
91
106
|
<label for="secret">Authorization header value</label>
|
|
92
107
|
<input type="password" id="secret" name="secret" placeholder="Bearer <your token>" required autofocus>
|
|
93
108
|
<button type="submit">Save credentials</button>
|
|
94
109
|
</form>
|
|
95
|
-
${
|
|
96
|
-
${
|
|
97
|
-
|
|
98
|
-
${
|
|
99
|
-
${
|
|
100
|
-
|
|
101
|
-
|
|
110
|
+
${n}
|
|
111
|
+
${l()}`)}function D(e){return c(`
|
|
112
|
+
${d("Credentials removed")}
|
|
113
|
+
<p>The stored credentials for ${a(e)} are gone. Calls to that API are anonymous again until you add new ones.</p>
|
|
114
|
+
${l()}`)}function x(e){return c(`
|
|
115
|
+
${d("Credentials saved")}
|
|
116
|
+
<p>Credentials for ${a(e)} are set for this session. Return to your AI client and continue - confirm the elicitation prompt if it is still open.</p>
|
|
117
|
+
${l()}`)}function f(e){return new Response(c(`
|
|
118
|
+
${d("Exchange failed")}
|
|
119
|
+
<p>We could not exchange your client credentials for a token for ${a(e)}. This link has been used up - go back to your AI client and request a new link, then try again.</p>
|
|
120
|
+
${l()}`),{status:502,headers:p})}function u(){return new Response(c(`${d("Link expired")}<p>This credentials link is invalid or has expired. Retry the request in your AI client to get a fresh link.</p>${l()}`),{status:404,headers:p})}var J=R;export{J as default,Y as setCredentialsFetchImpl};
|