diff --git a/workspaces/x2a/app-config.yaml b/workspaces/x2a/app-config.yaml
index f8c487426a..1425d8bdf2 100644
--- a/workspaces/x2a/app-config.yaml
+++ b/workspaces/x2a/app-config.yaml
@@ -22,7 +22,9 @@ backend:
# Content-Security-Policy directives follow the Helmet format: https://helmetjs.github.io/#reference
# Default Helmet Content-Security-Policy values can be removed by setting the key to false
cors:
- origin: http://localhost:3000
+ origin:
+ - http://localhost:3000
+ - http://localhost:6274
methods: [GET, HEAD, PATCH, POST, PUT, DELETE]
credentials: true
# This is for local development only, it is not recommended to use this in production
@@ -30,6 +32,10 @@ backend:
database:
client: better-sqlite3
connection: ':memory:'
+ actions:
+ pluginSources:
+ - 'catalog'
+ - 'x2a'
# workingDirectory: /tmp # Use this to configure a working directory for the scaffolder, defaults to the OS temp-dir
proxy:
@@ -72,6 +78,9 @@ integrations:
- host: bitbucket.org
auth:
+ experimentalDynamicClientRegistration:
+ # enable the feature
+ enabled: true
# see https://backstage.io/docs/auth/ to learn about auth providers
environment: development
providers:
diff --git a/workspaces/x2a/examples/example-rbac-policy.csv b/workspaces/x2a/examples/example-rbac-policy.csv
index dec1388150..7cccb20125 100644
--- a/workspaces/x2a/examples/example-rbac-policy.csv
+++ b/workspaces/x2a/examples/example-rbac-policy.csv
@@ -20,6 +20,7 @@ g, user:development/guest, role:default/x2aUser
g, group:default/x2a-admin-group, role:default/x2aAdmin
g, user:default/elai-shalev, role:default/x2aAdmin
+g, user:default/eloycoto, role:default/x2aAdmin
########################################################
# Catalog and Scaffolder templates permissions
@@ -47,4 +48,4 @@ p, role:default/x2aUser, scaffolder.template.parameter.read, read, allow
p, role:default/x2aUser, scaffolder.template.step.read, read, allow
p, role:default/x2aUser, scaffolder.task.create, create, allow
p, role:default/x2aUser, scaffolder.task.cancel, cancel, allow
-p, role:default/x2aUser, scaffolder.task.read, read, allow
\ No newline at end of file
+p, role:default/x2aUser, scaffolder.task.read, read, allow
diff --git a/workspaces/x2a/examples/org.yaml b/workspaces/x2a/examples/org.yaml
index 7885622d8b..4ed5b5b79d 100644
--- a/workspaces/x2a/examples/org.yaml
+++ b/workspaces/x2a/examples/org.yaml
@@ -48,3 +48,13 @@ spec:
displayName: Elai Shalev
email: foo@bar.com
memberOf: []
+---
+apiVersion: backstage.io/v1alpha1
+kind: User
+metadata:
+ name: eloycoto
+spec:
+ profile:
+ displayName: Eloy Coto
+ email: foo@bar.com
+ memberOf: []
diff --git a/workspaces/x2a/packages/app/src/App.tsx b/workspaces/x2a/packages/app/src/App.tsx
index 6c82e9d9e8..3a8ee3fded 100644
--- a/workspaces/x2a/packages/app/src/App.tsx
+++ b/workspaces/x2a/packages/app/src/App.tsx
@@ -61,6 +61,7 @@ import {
githubAuthApiRef,
gitlabAuthApiRef,
} from '@backstage/core-plugin-api';
+import { ConsentPage } from './components/oauth2/ConsentPage';
const app = createApp({
apis,
@@ -152,6 +153,7 @@ const routes = (
} />
} />
} />
+ } />
);
diff --git a/workspaces/x2a/packages/app/src/components/oauth2/ConsentPage.tsx b/workspaces/x2a/packages/app/src/components/oauth2/ConsentPage.tsx
new file mode 100644
index 0000000000..7774166355
--- /dev/null
+++ b/workspaces/x2a/packages/app/src/components/oauth2/ConsentPage.tsx
@@ -0,0 +1,249 @@
+/*
+ * Copyright Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { useCallback, useEffect, useState } from 'react';
+import { useParams } from 'react-router-dom';
+import {
+ useApi,
+ configApiRef,
+ fetchApiRef,
+ discoveryApiRef,
+} from '@backstage/core-plugin-api';
+import {
+ Page,
+ Header,
+ Content,
+ Progress,
+ ResponseErrorPanel,
+} from '@backstage/core-components';
+import {
+ Card,
+ CardContent,
+ CardActions,
+ Button,
+ Typography,
+ Divider,
+ Box,
+} from '@material-ui/core';
+import { makeStyles } from '@material-ui/core/styles';
+
+interface Session {
+ id: string;
+ clientName?: string;
+ clientId: string;
+ redirectUri: string;
+ scopes?: string[];
+}
+
+type ConsentState =
+ | { status: 'loading' }
+ | { status: 'error'; error: string }
+ | { status: 'loaded'; session: Session }
+ | { status: 'submitting'; session: Session; action: 'approve' | 'reject' }
+ | { status: 'completed'; action: 'approve' | 'reject' };
+
+const useStyles = makeStyles(theme => ({
+ card: {
+ maxWidth: 600,
+ margin: `${theme.spacing(4)}px auto`,
+ },
+ appHeader: {
+ display: 'flex',
+ alignItems: 'center',
+ gap: theme.spacing(2),
+ marginBottom: theme.spacing(2),
+ },
+ callbackUrl: {
+ fontFamily: 'monospace',
+ fontSize: '0.85rem',
+ backgroundColor: theme.palette.background.default,
+ padding: theme.spacing(1),
+ borderRadius: theme.shape.borderRadius,
+ wordBreak: 'break-all',
+ marginTop: theme.spacing(1),
+ },
+ actions: {
+ display: 'flex',
+ justifyContent: 'flex-end',
+ gap: theme.spacing(1),
+ padding: theme.spacing(2),
+ },
+}));
+
+const getHeaderTitle = (state: ConsentState): string => {
+ if (state.status === 'completed' && state.action === 'approve') {
+ return 'Authorization Approved';
+ }
+ if (state.status === 'completed') {
+ return 'Authorization Denied';
+ }
+ return 'Authorization Request';
+};
+
+export const ConsentPage = () => {
+ const { sessionId } = useParams<{ sessionId: string }>();
+ const classes = useStyles();
+ const configApi = useApi(configApiRef);
+ const fetchApi = useApi(fetchApiRef);
+ const discoveryApi = useApi(discoveryApiRef);
+ const appTitle = configApi.getOptionalString('app.title') ?? 'Backstage';
+
+ const [state, setState] = useState({ status: 'loading' });
+
+ useEffect(() => {
+ if (!sessionId) {
+ setState({ status: 'error', error: 'No session ID provided' });
+ return undefined;
+ }
+
+ let cancelled = false;
+ (async () => {
+ try {
+ const baseUrl = await discoveryApi.getBaseUrl('auth');
+ const response = await fetchApi.fetch(
+ `${baseUrl}/v1/sessions/${sessionId}`,
+ );
+
+ if (cancelled) return;
+ if (!response.ok) {
+ const text = await response.text();
+ setState({ status: 'error', error: text || response.statusText });
+ return;
+ }
+
+ const session: Session = await response.json();
+ setState({ status: 'loaded', session });
+ } catch (e: unknown) {
+ if (cancelled) return;
+ const message = e instanceof Error ? e.message : String(e);
+ setState({ status: 'error', error: message });
+ }
+ })();
+
+ return () => {
+ cancelled = true;
+ };
+ }, [sessionId, discoveryApi, fetchApi]);
+
+ const handleAction = useCallback(
+ async (action: 'approve' | 'reject') => {
+ if (state.status !== 'loaded') return;
+
+ setState({ status: 'submitting', session: state.session, action });
+
+ try {
+ const baseUrl = await discoveryApi.getBaseUrl('auth');
+ const response = await fetchApi.fetch(
+ `${baseUrl}/v1/sessions/${sessionId}/${action}`,
+ { method: 'POST', headers: { 'Content-Type': 'application/json' } },
+ );
+
+ if (!response.ok) {
+ const text = await response.text();
+ setState({ status: 'loaded', session: state.session });
+ throw new Error(text || response.statusText);
+ }
+
+ const result = await response.json();
+ setState({ status: 'completed', action });
+
+ if (result.redirectUrl) {
+ window.location.href = result.redirectUrl;
+ }
+ } catch (e: unknown) {
+ const message = e instanceof Error ? e.message : String(e);
+ setState({ status: 'error', error: message });
+ }
+ },
+ [state, sessionId, discoveryApi, fetchApi],
+ );
+
+ const headerTitle = getHeaderTitle(state);
+
+ return (
+
+
+
+ {state.status === 'loading' && (
+
+
+
+ )}
+
+ {state.status === 'error' && (
+
+ )}
+
+ {state.status === 'completed' && (
+
+ {state.action === 'approve'
+ ? `You have successfully authorized the application to access your ${appTitle} account. Redirecting...`
+ : `You have denied the application access to your ${appTitle} account.`}
+
+ )}
+
+ {(state.status === 'loaded' || state.status === 'submitting') && (
+
+
+
+
+
+ {state.session.clientName ?? state.session.clientId}
+
+
+ wants to access your {appTitle} account
+
+
+
+
+
+
+ This will grant the application a token to access {appTitle}{' '}
+ on your behalf. Only authorize applications you trust.
+
+
+ {state.session.redirectUri}
+
+
+
+
+
+
+
+
+ )}
+
+
+ );
+};
diff --git a/workspaces/x2a/packages/backend/package.json b/workspaces/x2a/packages/backend/package.json
index afe32e48a7..414fa6ad0b 100644
--- a/workspaces/x2a/packages/backend/package.json
+++ b/workspaces/x2a/packages/backend/package.json
@@ -35,6 +35,7 @@
"@backstage/plugin-catalog-backend-module-logs": "^0.1.16",
"@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "^0.2.14",
"@backstage/plugin-kubernetes-backend": "^0.20.4",
+ "@backstage/plugin-mcp-actions-backend": "^0.1.9",
"@backstage/plugin-notifications-backend": "^0.6.0",
"@backstage/plugin-permission-backend": "^0.7.8",
"@backstage/plugin-permission-common": "^0.9.3",
diff --git a/workspaces/x2a/packages/backend/src/index.ts b/workspaces/x2a/packages/backend/src/index.ts
index a70ce4a899..192cbf664e 100644
--- a/workspaces/x2a/packages/backend/src/index.ts
+++ b/workspaces/x2a/packages/backend/src/index.ts
@@ -84,6 +84,9 @@ backend.add(import('@backstage/plugin-kubernetes-backend'));
backend.add(import('@backstage/plugin-notifications-backend'));
backend.add(import('@backstage/plugin-signals-backend'));
+// mcp plugin
+backend.add(import('@backstage/plugin-mcp-actions-backend'));
+
backend.add(import('@red-hat-developer-hub/backstage-plugin-x2a-backend'));
backend.add(
diff --git a/workspaces/x2a/plugins/x2a-backend/src/mcp/projects.ts b/workspaces/x2a/plugins/x2a-backend/src/mcp/projects.ts
new file mode 100644
index 0000000000..5d86dddff4
--- /dev/null
+++ b/workspaces/x2a/plugins/x2a-backend/src/mcp/projects.ts
@@ -0,0 +1,89 @@
+/*
+ * Copyright Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import type {
+ BackstageCredentials,
+ BackstageUserPrincipal,
+ PermissionsService,
+} from '@backstage/backend-plugin-api';
+import type { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';
+import type { CatalogService } from '@backstage/plugin-catalog-node';
+import type { x2aDatabaseServiceRef } from '../services/X2ADatabaseService';
+import { listProjects } from '../router/listProjects';
+
+interface ProjectActionsOptions {
+ actionsRegistry: ActionsRegistryService;
+ permissionsSvc: PermissionsService;
+ catalog: CatalogService;
+ x2aDatabase: typeof x2aDatabaseServiceRef.T;
+}
+
+export function registerProjectActions({
+ actionsRegistry,
+ permissionsSvc,
+ catalog,
+ x2aDatabase,
+}: ProjectActionsOptions): void {
+ actionsRegistry.register({
+ name: 'x2a:project:list',
+ title: 'List x2a Projects',
+ description:
+ 'List conversion projects with optional pagination and sorting.',
+ attributes: { readOnly: true },
+ schema: {
+ input: z =>
+ z.object({
+ page: z.number().optional().describe('Page number (0-indexed)'),
+ pageSize: z
+ .number()
+ .optional()
+ .describe('Number of results per page'),
+ order: z.enum(['asc', 'desc']).optional().describe('Sort order'),
+ sort: z
+ .enum([
+ 'createdAt',
+ 'name',
+ 'abbreviation',
+ 'status',
+ 'description',
+ 'createdBy',
+ ])
+ .optional()
+ .describe('Field to sort by'),
+ }),
+ output: z =>
+ z.object({
+ totalCount: z.number().describe('Total number of projects'),
+ projects: z
+ .string()
+ .describe('JSON-serialized array of project objects'),
+ }),
+ },
+ action: async ({ input, credentials, logger }) => {
+ logger.info('Running x2a:project:list MCP action');
+ const response = await listProjects(
+ input,
+ { permissionsSvc, catalog, x2aDatabase },
+ credentials as BackstageCredentials,
+ );
+ return {
+ output: {
+ totalCount: response.totalCount ?? 0,
+ projects: JSON.stringify(response.items ?? []),
+ },
+ };
+ },
+ });
+}
diff --git a/workspaces/x2a/plugins/x2a-backend/src/plugin.ts b/workspaces/x2a/plugins/x2a-backend/src/plugin.ts
index 5630f59e89..6d6ab4763f 100644
--- a/workspaces/x2a/plugins/x2a-backend/src/plugin.ts
+++ b/workspaces/x2a/plugins/x2a-backend/src/plugin.ts
@@ -17,11 +17,13 @@ import {
coreServices,
createBackendPlugin,
} from '@backstage/backend-plugin-api';
+import { actionsRegistryServiceRef } from '@backstage/backend-plugin-api/alpha';
import { catalogServiceRef } from '@backstage/plugin-catalog-node';
import { x2aDatabaseServiceRef } from './services/X2ADatabaseService';
import { createRouter } from './router';
import { migrate } from './services/dbMigrate';
import { kubeServiceRef } from './services/KubeService';
+import { registerProjectActions } from './mcp/projects';
/**
* x2APlugin backend plugin
@@ -43,6 +45,7 @@ export const x2APlugin = createBackendPlugin({
config: coreServices.rootConfig,
x2aDatabase: x2aDatabaseServiceRef,
kubeService: kubeServiceRef,
+ actionsRegistry: actionsRegistryServiceRef,
},
async init({
httpRouter,
@@ -55,6 +58,7 @@ export const x2APlugin = createBackendPlugin({
database,
kubeService,
config,
+ actionsRegistry,
}) {
await migrate(database);
@@ -75,6 +79,13 @@ export const x2APlugin = createBackendPlugin({
config,
}),
);
+
+ registerProjectActions({
+ actionsRegistry,
+ permissionsSvc,
+ catalog,
+ x2aDatabase,
+ });
},
});
},
diff --git a/workspaces/x2a/plugins/x2a-backend/src/router/listProjects.ts b/workspaces/x2a/plugins/x2a-backend/src/router/listProjects.ts
new file mode 100644
index 0000000000..d6334a66c8
--- /dev/null
+++ b/workspaces/x2a/plugins/x2a-backend/src/router/listProjects.ts
@@ -0,0 +1,68 @@
+/*
+ * Copyright Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type {
+ BackstageCredentials,
+ BackstageUserPrincipal,
+} from '@backstage/backend-plugin-api';
+import { AuthorizeResult } from '@backstage/plugin-permission-common';
+import { NotAllowedError } from '@backstage/errors';
+import {
+ x2aAdminViewPermission,
+ x2aUserPermission,
+} from '@red-hat-developer-hub/backstage-plugin-x2a-common';
+
+import type { RouterDeps } from './types';
+import { getGroupsOfUser, getUserRef } from './common';
+import type { ProjectsGet } from '../schema/openapi';
+
+export async function listProjects(
+ query: ProjectsGet['query'],
+ deps: Pick,
+ credentials: BackstageCredentials,
+): Promise {
+ const { permissionsSvc, catalog, x2aDatabase } = deps;
+
+ const [userResult, adminViewResult] = await Promise.all([
+ permissionsSvc.authorize([{ permission: x2aUserPermission }], {
+ credentials,
+ }),
+ permissionsSvc.authorize([{ permission: x2aAdminViewPermission }], {
+ credentials,
+ }),
+ ]);
+
+ const isX2AUser = userResult[0]?.result === AuthorizeResult.ALLOW;
+ const canViewAll = adminViewResult[0]?.result === AuthorizeResult.ALLOW;
+
+ if (!isX2AUser && !canViewAll) {
+ throw new NotAllowedError('The user is not allowed to read projects.');
+ }
+
+ const userRef = getUserRef(credentials);
+ const groupsOfUser = await getGroupsOfUser(userRef, {
+ catalog,
+ credentials,
+ });
+
+ const { projects, totalCount } = await x2aDatabase.listProjects(query, {
+ credentials,
+ canViewAll,
+ groupsOfUser,
+ });
+
+ return { totalCount, items: projects };
+}
diff --git a/workspaces/x2a/plugins/x2a-backend/src/router/projects.ts b/workspaces/x2a/plugins/x2a-backend/src/router/projects.ts
index 9ec647abfb..a8aa68792e 100644
--- a/workspaces/x2a/plugins/x2a-backend/src/router/projects.ts
+++ b/workspaces/x2a/plugins/x2a-backend/src/router/projects.ts
@@ -31,9 +31,9 @@ import {
getUserRef,
reconcileJobStatus,
useEnforceProjectPermissions,
- useEnforceX2APermissions,
} from './common';
import { ProjectsGet, ProjectsPost } from '../schema/openapi';
+import { listProjects } from './listProjects';
export function registerProjectRoutes(
router: express.Router,
@@ -54,12 +54,7 @@ export function registerProjectRoutes(
const endpoint = 'GET /projects';
logger.info(`${endpoint} request received`);
- const { canViewAll } = await useEnforceX2APermissions({
- req,
- readOnly: true,
- permissionsSvc,
- httpAuth,
- });
+ const credentials = await httpAuth.credentials(req, { allow: ['user'] });
// parse request query
const projectsGetRequestSchema = z.object({
@@ -90,24 +85,11 @@ export function registerProjectRoutes(
logger.info(`${endpoint} request received: query=${JSON.stringify(query)}`);
- // list projects
- const credentials = await httpAuth.credentials(req, { allow: ['user'] });
- const userRef = getUserRef(credentials);
- const groupsOfUser = await getGroupsOfUser(userRef, {
- catalog,
- credentials,
- });
-
- const { projects, totalCount } = await x2aDatabase.listProjects(query, {
+ const response = await listProjects(
+ query,
+ { permissionsSvc, catalog, x2aDatabase },
credentials,
- canViewAll,
- groupsOfUser,
- });
-
- const response: ProjectsGet['response'] = {
- totalCount,
- items: projects,
- };
+ );
res.json(response);
});
diff --git a/workspaces/x2a/yarn.lock b/workspaces/x2a/yarn.lock
index fa068e333c..7625d15809 100644
--- a/workspaces/x2a/yarn.lock
+++ b/workspaces/x2a/yarn.lock
@@ -3316,6 +3316,29 @@ __metadata:
languageName: node
linkType: hard
+"@backstage/plugin-catalog-node@npm:^2.0.0":
+ version: 2.0.0
+ resolution: "@backstage/plugin-catalog-node@npm:2.0.0"
+ dependencies:
+ "@backstage/backend-plugin-api": "npm:^1.7.0"
+ "@backstage/catalog-client": "npm:^1.13.0"
+ "@backstage/catalog-model": "npm:^1.7.6"
+ "@backstage/errors": "npm:^1.2.7"
+ "@backstage/plugin-catalog-common": "npm:^1.1.8"
+ "@backstage/plugin-permission-common": "npm:^0.9.6"
+ "@backstage/plugin-permission-node": "npm:^0.10.10"
+ "@backstage/types": "npm:^1.2.2"
+ lodash: "npm:^4.17.21"
+ yaml: "npm:^2.0.0"
+ peerDependencies:
+ "@backstage/backend-test-utils": ^1.11.0
+ peerDependenciesMeta:
+ "@backstage/backend-test-utils":
+ optional: true
+ checksum: 10c0/dd418541e19f234987258d3688c98847d9c5d1256aa1ab1c5df49715c4a5c44e76f87eea9d802b2dc2a219e94681ecf76c2c379862e1f96ffa9fd84af2dcd6be
+ languageName: node
+ linkType: hard
+
"@backstage/plugin-catalog-react@npm:^1.20.0, @backstage/plugin-catalog-react@npm:^1.20.1, @backstage/plugin-catalog-react@npm:^1.21.0, @backstage/plugin-catalog-react@npm:^1.21.5":
version: 1.21.6
resolution: "@backstage/plugin-catalog-react@npm:1.21.6"
@@ -3593,6 +3616,24 @@ __metadata:
languageName: node
linkType: hard
+"@backstage/plugin-mcp-actions-backend@npm:^0.1.9":
+ version: 0.1.9
+ resolution: "@backstage/plugin-mcp-actions-backend@npm:0.1.9"
+ dependencies:
+ "@backstage/backend-plugin-api": "npm:^1.7.0"
+ "@backstage/catalog-client": "npm:^1.13.0"
+ "@backstage/errors": "npm:^1.2.7"
+ "@backstage/plugin-catalog-node": "npm:^2.0.0"
+ "@backstage/types": "npm:^1.2.2"
+ "@cfworker/json-schema": "npm:^4.1.1"
+ "@modelcontextprotocol/sdk": "npm:^1.25.2"
+ express: "npm:^4.22.0"
+ express-promise-router: "npm:^4.1.0"
+ zod: "npm:^3.25.76"
+ checksum: 10c0/9afdc1e59e576e3dbac5f4249625c8ae6664a2badad4357b7f8d2c2ed1a7b6f7c49b1cc4dd5bd083d4c0691ad941b4ad161e15535fec4a67fac687db4dbafdea
+ languageName: node
+ linkType: hard
+
"@backstage/plugin-notifications-backend@npm:^0.6.0":
version: 0.6.1
resolution: "@backstage/plugin-notifications-backend@npm:0.6.1"
@@ -3736,21 +3777,21 @@ __metadata:
languageName: node
linkType: hard
-"@backstage/plugin-permission-node@npm:^0.10.3, @backstage/plugin-permission-node@npm:^0.10.6, @backstage/plugin-permission-node@npm:^0.10.7, @backstage/plugin-permission-node@npm:^0.10.9":
- version: 0.10.9
- resolution: "@backstage/plugin-permission-node@npm:0.10.9"
+"@backstage/plugin-permission-node@npm:^0.10.10, @backstage/plugin-permission-node@npm:^0.10.3, @backstage/plugin-permission-node@npm:^0.10.6, @backstage/plugin-permission-node@npm:^0.10.7, @backstage/plugin-permission-node@npm:^0.10.9":
+ version: 0.10.10
+ resolution: "@backstage/plugin-permission-node@npm:0.10.10"
dependencies:
- "@backstage/backend-plugin-api": "npm:^1.6.2"
+ "@backstage/backend-plugin-api": "npm:^1.7.0"
"@backstage/config": "npm:^1.3.6"
"@backstage/errors": "npm:^1.2.7"
- "@backstage/plugin-auth-node": "npm:^0.6.12"
- "@backstage/plugin-permission-common": "npm:^0.9.5"
+ "@backstage/plugin-auth-node": "npm:^0.6.13"
+ "@backstage/plugin-permission-common": "npm:^0.9.6"
"@types/express": "npm:^4.17.6"
express: "npm:^4.22.0"
express-promise-router: "npm:^4.1.0"
zod: "npm:^3.25.76"
zod-to-json-schema: "npm:^3.25.1"
- checksum: 10c0/c1c0b8245c8877b2720849676c0dfeb3452caeb52ec2f9b1ef122013cf36f58dbce7ea249bb68204aea83aa757435b5f6d730c434e737478b11b0d38191ed20f
+ checksum: 10c0/132ba02e69621fc6e162fd3acd8f320bc67f2d6c0958af4f5386195aed822ea7bba2342ff7fc47df834d8a6aa8bcacee89cfb54bd8b9a0cb8029aba1a4d468da
languageName: node
linkType: hard
@@ -4980,6 +5021,13 @@ __metadata:
languageName: node
linkType: hard
+"@cfworker/json-schema@npm:^4.1.1":
+ version: 4.1.1
+ resolution: "@cfworker/json-schema@npm:4.1.1"
+ checksum: 10c0/b5253486d346b7de6feec9c73954f612b11019dacb9023d710a5666df2f5fc145dd88b6b913c88726c6d97e2e258a515fa2cab177f58b18da6bac3738cbc4739
+ languageName: node
+ linkType: hard
+
"@changesets/apply-release-plan@npm:^7.0.14":
version: 7.0.14
resolution: "@changesets/apply-release-plan@npm:7.0.14"
@@ -6591,6 +6639,15 @@ __metadata:
languageName: node
linkType: hard
+"@hono/node-server@npm:^1.19.9":
+ version: 1.19.11
+ resolution: "@hono/node-server@npm:1.19.11"
+ peerDependencies:
+ hono: ^4
+ checksum: 10c0/34b1c29c249c5cd95469980b5c359370f3cbab49b3603f324a4afbf895d68b8d5485c71f1887769eabeb3499276c49e7102084234b4feb3853edb748aaa85f50
+ languageName: node
+ linkType: hard
+
"@httptoolkit/httpolyglot@npm:^2.2.1":
version: 2.2.2
resolution: "@httptoolkit/httpolyglot@npm:2.2.2"
@@ -7919,6 +7976,39 @@ __metadata:
languageName: node
linkType: hard
+"@modelcontextprotocol/sdk@npm:^1.25.2":
+ version: 1.27.1
+ resolution: "@modelcontextprotocol/sdk@npm:1.27.1"
+ dependencies:
+ "@hono/node-server": "npm:^1.19.9"
+ ajv: "npm:^8.17.1"
+ ajv-formats: "npm:^3.0.1"
+ content-type: "npm:^1.0.5"
+ cors: "npm:^2.8.5"
+ cross-spawn: "npm:^7.0.5"
+ eventsource: "npm:^3.0.2"
+ eventsource-parser: "npm:^3.0.0"
+ express: "npm:^5.2.1"
+ express-rate-limit: "npm:^8.2.1"
+ hono: "npm:^4.11.4"
+ jose: "npm:^6.1.3"
+ json-schema-typed: "npm:^8.0.2"
+ pkce-challenge: "npm:^5.0.0"
+ raw-body: "npm:^3.0.0"
+ zod: "npm:^3.25 || ^4.0"
+ zod-to-json-schema: "npm:^3.25.1"
+ peerDependencies:
+ "@cfworker/json-schema": ^4.1.1
+ zod: ^3.25 || ^4.0
+ peerDependenciesMeta:
+ "@cfworker/json-schema":
+ optional: true
+ zod:
+ optional: false
+ checksum: 10c0/1b8ad87093c9e43174c7d65864b3d826a8dd050d5c32248f5da49fd72c51b556ebd702e3e49a7f1cc7fa25717a3f7fcee22ed89edd5bd3d8f4e1f8ca499b365e
+ languageName: node
+ linkType: hard
+
"@module-federation/bridge-react-webpack-plugin@npm:0.9.1":
version: 0.9.1
resolution: "@module-federation/bridge-react-webpack-plugin@npm:0.9.1"
@@ -16158,6 +16248,16 @@ __metadata:
languageName: node
linkType: hard
+"accepts@npm:^2.0.0":
+ version: 2.0.0
+ resolution: "accepts@npm:2.0.0"
+ dependencies:
+ mime-types: "npm:^3.0.0"
+ negotiator: "npm:^1.0.0"
+ checksum: 10c0/98374742097e140891546076215f90c32644feacf652db48412329de4c2a529178a81aa500fbb13dd3e6cbf6e68d829037b123ac037fc9a08bcec4b87b358eef
+ languageName: node
+ linkType: hard
+
"acorn-globals@npm:^7.0.0":
version: 7.0.1
resolution: "acorn-globals@npm:7.0.1"
@@ -17188,6 +17288,7 @@ __metadata:
"@backstage/plugin-catalog-backend-module-logs": "npm:^0.1.16"
"@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "npm:^0.2.14"
"@backstage/plugin-kubernetes-backend": "npm:^0.20.4"
+ "@backstage/plugin-mcp-actions-backend": "npm:^0.1.9"
"@backstage/plugin-notifications-backend": "npm:^0.6.0"
"@backstage/plugin-permission-backend": "npm:^0.7.8"
"@backstage/plugin-permission-common": "npm:^0.9.3"
@@ -17525,6 +17626,23 @@ __metadata:
languageName: node
linkType: hard
+"body-parser@npm:^2.2.1":
+ version: 2.2.2
+ resolution: "body-parser@npm:2.2.2"
+ dependencies:
+ bytes: "npm:^3.1.2"
+ content-type: "npm:^1.0.5"
+ debug: "npm:^4.4.3"
+ http-errors: "npm:^2.0.0"
+ iconv-lite: "npm:^0.7.0"
+ on-finished: "npm:^2.4.1"
+ qs: "npm:^6.14.1"
+ raw-body: "npm:^3.0.1"
+ type-is: "npm:^2.0.1"
+ checksum: 10c0/95a830a003b38654b75166ca765358aa92ee3d561bf0e41d6ccdde0e1a0c9783cab6b90b20eb635d23172c010b59d3563a137a738e74da4ba714463510d05137
+ languageName: node
+ linkType: hard
+
"bonjour-service@npm:^1.2.1":
version: 1.3.0
resolution: "bonjour-service@npm:1.3.0"
@@ -17850,7 +17968,7 @@ __metadata:
languageName: node
linkType: hard
-"bytes@npm:3.1.2, bytes@npm:~3.1.2":
+"bytes@npm:3.1.2, bytes@npm:^3.1.2, bytes@npm:~3.1.2":
version: 3.1.2
resolution: "bytes@npm:3.1.2"
checksum: 10c0/76d1c43cbd602794ad8ad2ae94095cddeb1de78c5dddaa7005c51af10b0176c69971a6d88e805a90c2b6550d76636e43c40d8427a808b8645ede885de4a0358e
@@ -18789,6 +18907,13 @@ __metadata:
languageName: node
linkType: hard
+"content-disposition@npm:^1.0.0":
+ version: 1.0.1
+ resolution: "content-disposition@npm:1.0.1"
+ checksum: 10c0/bd7ff1fe8d2542d3a2b9a29428cc3591f6ac27bb5595bba2c69664408a68f9538b14cbd92479796ea835b317a09a527c8c7209c4200381dedb0c34d3b658849e
+ languageName: node
+ linkType: hard
+
"content-disposition@npm:~0.5.2, content-disposition@npm:~0.5.4":
version: 0.5.4
resolution: "content-disposition@npm:0.5.4"
@@ -18836,6 +18961,13 @@ __metadata:
languageName: node
linkType: hard
+"cookie-signature@npm:^1.2.1":
+ version: 1.2.2
+ resolution: "cookie-signature@npm:1.2.2"
+ checksum: 10c0/54e05df1a293b3ce81589b27dddc445f462f6fa6812147c033350cd3561a42bc14481674e05ed14c7bd0ce1e8bb3dc0e40851bad75415733711294ddce0b7bc6
+ languageName: node
+ linkType: hard
+
"cookie-signature@npm:~1.0.6, cookie-signature@npm:~1.0.7":
version: 1.0.7
resolution: "cookie-signature@npm:1.0.7"
@@ -18843,7 +18975,7 @@ __metadata:
languageName: node
linkType: hard
-"cookie@npm:0.7.2, cookie@npm:^0.7.0, cookie@npm:~0.7.1, cookie@npm:~0.7.2":
+"cookie@npm:0.7.2, cookie@npm:^0.7.0, cookie@npm:^0.7.1, cookie@npm:~0.7.1, cookie@npm:~0.7.2":
version: 0.7.2
resolution: "cookie@npm:0.7.2"
checksum: 10c0/9596e8ccdbf1a3a88ae02cf5ee80c1c50959423e1022e4e60b91dd87c622af1da309253d8abdb258fb5e3eacb4f08e579dc58b4897b8087574eee0fd35dfa5d2
@@ -20494,7 +20626,7 @@ __metadata:
languageName: node
linkType: hard
-"encodeurl@npm:~2.0.0":
+"encodeurl@npm:^2.0.0, encodeurl@npm:~2.0.0":
version: 2.0.0
resolution: "encodeurl@npm:2.0.0"
checksum: 10c0/5d317306acb13e6590e28e27924c754163946a2480de11865c991a3a7eed4315cd3fba378b543ca145829569eefe9b899f3d84bb09870f675ae60bc924b01ceb
@@ -21307,7 +21439,7 @@ __metadata:
languageName: node
linkType: hard
-"etag@npm:~1.8.1":
+"etag@npm:^1.8.1, etag@npm:~1.8.1":
version: 1.8.1
resolution: "etag@npm:1.8.1"
checksum: 10c0/12be11ef62fb9817314d790089a0a49fae4e1b50594135dcb8076312b7d7e470884b5100d249b28c18581b7fd52f8b485689ffae22a11ed9ec17377a33a08f84
@@ -21351,6 +21483,22 @@ __metadata:
languageName: node
linkType: hard
+"eventsource-parser@npm:^3.0.0, eventsource-parser@npm:^3.0.1":
+ version: 3.0.6
+ resolution: "eventsource-parser@npm:3.0.6"
+ checksum: 10c0/70b8ccec7dac767ef2eca43f355e0979e70415701691382a042a2df8d6a68da6c2fca35363669821f3da876d29c02abe9b232964637c1b6635c940df05ada78a
+ languageName: node
+ linkType: hard
+
+"eventsource@npm:^3.0.2":
+ version: 3.0.7
+ resolution: "eventsource@npm:3.0.7"
+ dependencies:
+ eventsource-parser: "npm:^3.0.1"
+ checksum: 10c0/c48a73c38f300e33e9f11375d4ee969f25cbb0519608a12378a38068055ae8b55b6e0e8a49c3f91c784068434efe1d9f01eb49b6315b04b0da9157879ce2f67d
+ languageName: node
+ linkType: hard
+
"evp_bytestokey@npm:^1.0.0, evp_bytestokey@npm:^1.0.3":
version: 1.0.3
resolution: "evp_bytestokey@npm:1.0.3"
@@ -21472,6 +21620,17 @@ __metadata:
languageName: node
linkType: hard
+"express-rate-limit@npm:^8.2.1":
+ version: 8.3.1
+ resolution: "express-rate-limit@npm:8.3.1"
+ dependencies:
+ ip-address: "npm:10.1.0"
+ peerDependencies:
+ express: ">= 4.11"
+ checksum: 10c0/e3229938457cec617460c54ef4e90c8c254facc884729325d20ea35e3838bd273e4c611fc1f4a76f6d14c411e30d31b15e88eb4be87408615ff0aacc142511a2
+ languageName: node
+ linkType: hard
+
"express-session@npm:^1.17.1":
version: 1.19.0
resolution: "express-session@npm:1.19.0"
@@ -21527,6 +21686,42 @@ __metadata:
languageName: node
linkType: hard
+"express@npm:^5.2.1":
+ version: 5.2.1
+ resolution: "express@npm:5.2.1"
+ dependencies:
+ accepts: "npm:^2.0.0"
+ body-parser: "npm:^2.2.1"
+ content-disposition: "npm:^1.0.0"
+ content-type: "npm:^1.0.5"
+ cookie: "npm:^0.7.1"
+ cookie-signature: "npm:^1.2.1"
+ debug: "npm:^4.4.0"
+ depd: "npm:^2.0.0"
+ encodeurl: "npm:^2.0.0"
+ escape-html: "npm:^1.0.3"
+ etag: "npm:^1.8.1"
+ finalhandler: "npm:^2.1.0"
+ fresh: "npm:^2.0.0"
+ http-errors: "npm:^2.0.0"
+ merge-descriptors: "npm:^2.0.0"
+ mime-types: "npm:^3.0.0"
+ on-finished: "npm:^2.4.1"
+ once: "npm:^1.4.0"
+ parseurl: "npm:^1.3.3"
+ proxy-addr: "npm:^2.0.7"
+ qs: "npm:^6.14.0"
+ range-parser: "npm:^1.2.1"
+ router: "npm:^2.2.0"
+ send: "npm:^1.1.0"
+ serve-static: "npm:^2.2.0"
+ statuses: "npm:^2.0.1"
+ type-is: "npm:^2.0.1"
+ vary: "npm:^1.1.2"
+ checksum: 10c0/45e8c841ad188a41402ddcd1294901e861ee0819f632fb494f2ed344ef9c43315d294d443fb48d594e6586a3b779785120f43321417adaef8567316a55072949
+ languageName: node
+ linkType: hard
+
"extend@npm:3.0.2, extend@npm:^3.0.0, extend@npm:^3.0.2":
version: 3.0.2
resolution: "extend@npm:3.0.2"
@@ -21838,6 +22033,20 @@ __metadata:
languageName: node
linkType: hard
+"finalhandler@npm:^2.1.0":
+ version: 2.1.1
+ resolution: "finalhandler@npm:2.1.1"
+ dependencies:
+ debug: "npm:^4.4.0"
+ encodeurl: "npm:^2.0.0"
+ escape-html: "npm:^1.0.3"
+ on-finished: "npm:^2.4.1"
+ parseurl: "npm:^1.3.3"
+ statuses: "npm:^2.0.1"
+ checksum: 10c0/6bd664e21b7b2e79efcaace7d1a427169f61cce048fae68eb56290e6934e676b78e55d89f5998c5508871345bc59a61f47002dc505dc7288be68cceac1b701e2
+ languageName: node
+ linkType: hard
+
"finalhandler@npm:~1.3.1":
version: 1.3.2
resolution: "finalhandler@npm:1.3.2"
@@ -22138,6 +22347,13 @@ __metadata:
languageName: node
linkType: hard
+"fresh@npm:^2.0.0":
+ version: 2.0.0
+ resolution: "fresh@npm:2.0.0"
+ checksum: 10c0/0557548194cb9a809a435bf92bcfbc20c89e8b5eb38861b73ced36750437251e39a111fc3a18b98531be9dd91fe1411e4969f229dc579ec0251ce6c5d4900bbc
+ languageName: node
+ linkType: hard
+
"fresh@npm:~0.5.2":
version: 0.5.2
resolution: "fresh@npm:0.5.2"
@@ -23272,6 +23488,13 @@ __metadata:
languageName: node
linkType: hard
+"hono@npm:^4.11.4":
+ version: 4.12.8
+ resolution: "hono@npm:4.12.8"
+ checksum: 10c0/10b8a5012e362824d97d0f11895da6e7ba3195320399684f07b3743801986647aeb8395f189a8fefbe8d4567f943c23dc0ba73834ce9a848640a5d37d50f29b6
+ languageName: node
+ linkType: hard
+
"hookified@npm:^1.10.0":
version: 1.15.0
resolution: "hookified@npm:1.15.0"
@@ -23442,7 +23665,7 @@ __metadata:
languageName: node
linkType: hard
-"http-errors@npm:~2.0.0, http-errors@npm:~2.0.1":
+"http-errors@npm:^2.0.0, http-errors@npm:^2.0.1, http-errors@npm:~2.0.0, http-errors@npm:~2.0.1":
version: 2.0.1
resolution: "http-errors@npm:2.0.1"
dependencies:
@@ -23627,7 +23850,7 @@ __metadata:
languageName: node
linkType: hard
-"iconv-lite@npm:^0.7.0, iconv-lite@npm:^0.7.2":
+"iconv-lite@npm:^0.7.0, iconv-lite@npm:^0.7.2, iconv-lite@npm:~0.7.0":
version: 0.7.2
resolution: "iconv-lite@npm:0.7.2"
dependencies:
@@ -23937,7 +24160,7 @@ __metadata:
languageName: node
linkType: hard
-"ip-address@npm:^10.0.1":
+"ip-address@npm:10.1.0, ip-address@npm:^10.0.1":
version: 10.1.0
resolution: "ip-address@npm:10.1.0"
checksum: 10c0/0103516cfa93f6433b3bd7333fa876eb21263912329bfa47010af5e16934eeeff86f3d2ae700a3744a137839ddfad62b900c7a445607884a49b5d1e32a3d7566
@@ -25287,10 +25510,10 @@ __metadata:
languageName: node
linkType: hard
-"jose@npm:^6.1.0":
- version: 6.1.3
- resolution: "jose@npm:6.1.3"
- checksum: 10c0/b9577b4a7a5e84131011c23823db9f5951eae3ba796771a6a2401ae5dd50daf71104febc8ded9c38146aa5ebe94a92ac09c725e699e613ef26949b9f5a8bc30f
+"jose@npm:^6.1.0, jose@npm:^6.1.3":
+ version: 6.2.1
+ resolution: "jose@npm:6.2.1"
+ checksum: 10c0/456822e00a2ee6b1470fabadd694b237af64b9977aa8a47844aae2841298daefd79bb04ff328cbc3aa3c886761aa72b33bc5c1ad797b31b8368e77a89d09b779
languageName: node
linkType: hard
@@ -25552,6 +25775,13 @@ __metadata:
languageName: node
linkType: hard
+"json-schema-typed@npm:^8.0.2":
+ version: 8.0.2
+ resolution: "json-schema-typed@npm:8.0.2"
+ checksum: 10c0/89f5e2fb1495483b705c027203c07277ee6bf2665165ad25a9cb55de5af7f72570326d13d32565180781e4083ad5c9688102f222baed7b353c2f39c1e02b0428
+ languageName: node
+ linkType: hard
+
"json-schema@npm:^0.4.0":
version: 0.4.0
resolution: "json-schema@npm:0.4.0"
@@ -27027,6 +27257,13 @@ __metadata:
languageName: node
linkType: hard
+"merge-descriptors@npm:^2.0.0":
+ version: 2.0.0
+ resolution: "merge-descriptors@npm:2.0.0"
+ checksum: 10c0/95389b7ced3f9b36fbdcf32eb946dc3dd1774c2fdf164609e55b18d03aa499b12bd3aae3a76c1c7185b96279e9803525550d3eb292b5224866060a288f335cb3
+ languageName: node
+ linkType: hard
+
"merge-stream@npm:^2.0.0":
version: 2.0.0
resolution: "merge-stream@npm:2.0.0"
@@ -27434,7 +27671,7 @@ __metadata:
languageName: node
linkType: hard
-"mime-types@npm:^3.0.1":
+"mime-types@npm:^3.0.0, mime-types@npm:^3.0.1, mime-types@npm:^3.0.2":
version: 3.0.2
resolution: "mime-types@npm:3.0.2"
dependencies:
@@ -29261,7 +29498,7 @@ __metadata:
languageName: node
linkType: hard
-"parseurl@npm:^1.3.2, parseurl@npm:~1.3.3":
+"parseurl@npm:^1.3.2, parseurl@npm:^1.3.3, parseurl@npm:~1.3.3":
version: 1.3.3
resolution: "parseurl@npm:1.3.3"
checksum: 10c0/90dd4760d6f6174adb9f20cf0965ae12e23879b5f5464f38e92fce8073354341e4b3b76fa3d878351efe7d01e617121955284cfd002ab087fba1a0726ec0b4f5
@@ -29692,6 +29929,13 @@ __metadata:
languageName: node
linkType: hard
+"pkce-challenge@npm:^5.0.0":
+ version: 5.0.1
+ resolution: "pkce-challenge@npm:5.0.1"
+ checksum: 10c0/207f4cb976682f27e8324eb49cf71937c98fbb8341a0b8f6142bc6f664825b30e049a54a21b5c034e823ee3c3d412f10d74bd21de78e17452a6a496c2991f57c
+ languageName: node
+ linkType: hard
+
"pkg-dir@npm:^4.2.0":
version: 4.2.0
resolution: "pkg-dir@npm:4.2.0"
@@ -30508,7 +30752,7 @@ __metadata:
languageName: node
linkType: hard
-"proxy-addr@npm:~2.0.7":
+"proxy-addr@npm:^2.0.7, proxy-addr@npm:~2.0.7":
version: 2.0.7
resolution: "proxy-addr@npm:2.0.7"
dependencies:
@@ -30602,7 +30846,16 @@ __metadata:
languageName: node
linkType: hard
-"qs@npm:^6.10.1, qs@npm:^6.10.3, qs@npm:^6.11.0, qs@npm:^6.11.2, qs@npm:^6.12.2, qs@npm:^6.12.3, qs@npm:^6.14.1, qs@npm:^6.9.4, qs@npm:~6.14.0":
+"qs@npm:^6.10.1, qs@npm:^6.10.3, qs@npm:^6.11.0, qs@npm:^6.11.2, qs@npm:^6.12.2, qs@npm:^6.12.3, qs@npm:^6.14.0, qs@npm:^6.14.1, qs@npm:^6.9.4":
+ version: 6.15.0
+ resolution: "qs@npm:6.15.0"
+ dependencies:
+ side-channel: "npm:^1.1.0"
+ checksum: 10c0/ff341078a78a991d8a48b4524d52949211447b4b1ad907f489cac0770cbc346a28e47304455c0320e5fb000f8762d64b03331e3b71865f663bf351bcba8cdb4b
+ languageName: node
+ linkType: hard
+
+"qs@npm:~6.14.0":
version: 6.14.1
resolution: "qs@npm:6.14.1"
dependencies:
@@ -30771,6 +31024,18 @@ __metadata:
languageName: node
linkType: hard
+"raw-body@npm:^3.0.0, raw-body@npm:^3.0.1":
+ version: 3.0.2
+ resolution: "raw-body@npm:3.0.2"
+ dependencies:
+ bytes: "npm:~3.1.2"
+ http-errors: "npm:~2.0.1"
+ iconv-lite: "npm:~0.7.0"
+ unpipe: "npm:~1.0.0"
+ checksum: 10c0/d266678d08e1e7abea62c0ce5864344e980fa81c64f6b481e9842c5beaed2cdcf975f658a3ccd67ad35fc919c1f6664ccc106067801850286a6cbe101de89f29
+ languageName: node
+ linkType: hard
+
"raw-loader@npm:^4.0.2":
version: 4.0.2
resolution: "raw-loader@npm:4.0.2"
@@ -32307,6 +32572,19 @@ __metadata:
languageName: node
linkType: hard
+"router@npm:^2.2.0":
+ version: 2.2.0
+ resolution: "router@npm:2.2.0"
+ dependencies:
+ debug: "npm:^4.4.0"
+ depd: "npm:^2.0.0"
+ is-promise: "npm:^4.0.0"
+ parseurl: "npm:^1.3.3"
+ path-to-regexp: "npm:^8.0.0"
+ checksum: 10c0/3279de7450c8eae2f6e095e9edacbdeec0abb5cb7249c6e719faa0db2dba43574b4fff5892d9220631c9abaff52dd3cad648cfea2aaace845e1a071915ac8867
+ languageName: node
+ linkType: hard
+
"rtl-css-js@npm:^1.16.1":
version: 1.16.1
resolution: "rtl-css-js@npm:1.16.1"
@@ -32586,6 +32864,25 @@ __metadata:
languageName: node
linkType: hard
+"send@npm:^1.1.0, send@npm:^1.2.0":
+ version: 1.2.1
+ resolution: "send@npm:1.2.1"
+ dependencies:
+ debug: "npm:^4.4.3"
+ encodeurl: "npm:^2.0.0"
+ escape-html: "npm:^1.0.3"
+ etag: "npm:^1.8.1"
+ fresh: "npm:^2.0.0"
+ http-errors: "npm:^2.0.1"
+ mime-types: "npm:^3.0.2"
+ ms: "npm:^2.1.3"
+ on-finished: "npm:^2.4.1"
+ range-parser: "npm:^1.2.1"
+ statuses: "npm:^2.0.2"
+ checksum: 10c0/fbbbbdc902a913d65605274be23f3d604065cfc3ee3d78bf9fc8af1dc9fc82667c50d3d657f5e601ac657bac9b396b50ee97bd29cd55436320cf1cddebdcec72
+ languageName: node
+ linkType: hard
+
"send@npm:~0.19.0, send@npm:~0.19.1":
version: 0.19.2
resolution: "send@npm:0.19.2"
@@ -32647,6 +32944,18 @@ __metadata:
languageName: node
linkType: hard
+"serve-static@npm:^2.2.0":
+ version: 2.2.1
+ resolution: "serve-static@npm:2.2.1"
+ dependencies:
+ encodeurl: "npm:^2.0.0"
+ escape-html: "npm:^1.0.3"
+ parseurl: "npm:^1.3.3"
+ send: "npm:^1.2.0"
+ checksum: 10c0/37986096e8572e2dfaad35a3925fa8da0c0969f8814fd7788e84d4d388bc068cf0c06d1658509788e55bed942a6b6d040a8a267fa92bb9ffb1179f8bacde5fd7
+ languageName: node
+ linkType: hard
+
"serve-static@npm:~1.16.2":
version: 1.16.3
resolution: "serve-static@npm:1.16.3"
@@ -33260,7 +33569,7 @@ __metadata:
languageName: node
linkType: hard
-"statuses@npm:~2.0.1, statuses@npm:~2.0.2":
+"statuses@npm:^2.0.1, statuses@npm:^2.0.2, statuses@npm:~2.0.1, statuses@npm:~2.0.2":
version: 2.0.2
resolution: "statuses@npm:2.0.2"
checksum: 10c0/a9947d98ad60d01f6b26727570f3bcceb6c8fa789da64fe6889908fe2e294d57503b14bf2b5af7605c2d36647259e856635cd4c49eab41667658ec9d0080ec3f
@@ -34735,6 +35044,17 @@ __metadata:
languageName: node
linkType: hard
+"type-is@npm:^2.0.1":
+ version: 2.0.1
+ resolution: "type-is@npm:2.0.1"
+ dependencies:
+ content-type: "npm:^1.0.5"
+ media-typer: "npm:^1.1.0"
+ mime-types: "npm:^3.0.0"
+ checksum: 10c0/7f7ec0a060b16880bdad36824ab37c26019454b67d73e8a465ed5a3587440fbe158bc765f0da68344498235c877e7dbbb1600beccc94628ed05599d667951b99
+ languageName: node
+ linkType: hard
+
"typed-array-buffer@npm:^1.0.3":
version: 1.0.3
resolution: "typed-array-buffer@npm:1.0.3"
@@ -36433,7 +36753,7 @@ __metadata:
languageName: node
linkType: hard
-"zod@npm:^4.1.11":
+"zod@npm:^3.25 || ^4.0, zod@npm:^4.1.11":
version: 4.3.6
resolution: "zod@npm:4.3.6"
checksum: 10c0/860d25a81ab41d33aa25f8d0d07b091a04acb426e605f396227a796e9e800c44723ed96d0f53a512b57be3d1520f45bf69c0cb3b378a232a00787a2609625307