fix(mcp-server): add missing jose + @fastify/cors deps; use ServiceError subclasses for 401/403; remove swagger option that crashed startup; add AbortSignal.timeout to platformFetch

This commit is contained in:
saravanakumardb1 2026-03-05 12:06:54 -08:00
parent eb5c3b1c66
commit 9a298d50ef
5 changed files with 76 additions and 11 deletions

66
pnpm-lock.yaml generated
View File

@ -92,6 +92,9 @@ importers:
'@bytelyst/datastore':
specifier: workspace:*
version: link:../../packages/datastore
'@bytelyst/design-tokens':
specifier: workspace:*
version: link:../../packages/design-tokens
'@bytelyst/errors':
specifier: workspace:*
version: link:../../packages/errors
@ -347,6 +350,24 @@ importers:
specifier: '>=4.0.0'
version: 4.9.1(@azure/core-client@1.10.1)
packages/dashboard-components:
devDependencies:
'@types/react':
specifier: ^19.0.0
version: 19.2.14
'@types/react-dom':
specifier: ^19.0.0
version: 19.2.3(@types/react@19.2.14)
react:
specifier: ^19.0.0
version: 19.2.4
react-dom:
specifier: ^19.0.0
version: 19.2.4(react@19.2.4)
typescript:
specifier: ^5.7.3
version: 5.9.3
packages/datastore:
dependencies:
'@azure/cosmos':
@ -627,6 +648,43 @@ importers:
specifier: ^3.0.5
version: 3.2.4(@types/debug@4.1.12)(@types/node@22.19.11)(happy-dom@18.0.1)(jiti@2.6.1)(jsdom@28.0.0(@noble/hashes@1.8.0))(lightningcss@1.31.1)(msw@2.12.10(@types/node@22.19.11)(typescript@5.9.3))(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)
services/mcp-server:
dependencies:
'@bytelyst/config':
specifier: workspace:*
version: link:../../packages/config
'@bytelyst/errors':
specifier: workspace:*
version: link:../../packages/errors
'@bytelyst/fastify-core':
specifier: workspace:*
version: link:../../packages/fastify-core
'@fastify/cors':
specifier: ^10.0.2
version: 10.1.0
fastify:
specifier: ^5.2.1
version: 5.7.4
jose:
specifier: ^5.9.6
version: 5.10.0
zod:
specifier: ^3.24.2
version: 3.25.76
devDependencies:
'@types/node':
specifier: ^22.12.0
version: 22.19.11
tsx:
specifier: ^4.19.2
version: 4.21.0
typescript:
specifier: ^5.7.3
version: 5.9.3
vitest:
specifier: ^3.0.5
version: 3.2.4(@types/debug@4.1.12)(@types/node@22.19.11)(happy-dom@18.0.1)(jiti@2.6.1)(jsdom@28.0.0(@noble/hashes@1.8.0))(lightningcss@1.31.1)(msw@2.12.10(@types/node@22.19.11)(typescript@5.9.3))(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)
services/monitoring:
devDependencies:
'@bytelyst/monitoring':
@ -9465,6 +9523,12 @@ packages:
}
hasBin: true
jose@5.10.0:
resolution:
{
integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==,
}
jose@6.1.3:
resolution:
{
@ -20412,6 +20476,8 @@ snapshots:
jiti@2.6.1: {}
jose@5.10.0: {}
jose@6.1.3: {}
js-tokens@10.0.0: {}

View File

@ -16,7 +16,9 @@
"@bytelyst/config": "workspace:*",
"@bytelyst/errors": "workspace:*",
"@bytelyst/fastify-core": "workspace:*",
"@fastify/cors": "^10.0.2",
"fastify": "^5.2.1",
"jose": "^5.9.6",
"zod": "^3.24.2"
},
"devDependencies": {

View File

@ -1,4 +1,5 @@
import { jwtVerify } from 'jose';
import { UnauthorizedError, ForbiddenError } from '@bytelyst/errors';
import { config } from './config.js';
export type Role = 'viewer' | 'admin' | 'super_admin';
@ -38,8 +39,7 @@ export async function parseJwt(req: FastifyRequest): Promise<void> {
}
export function requireAuth(req: AuthRequest): JwtPayload {
if (!req.jwtPayload?.sub)
throw Object.assign(new Error('Authentication required'), { statusCode: 401 });
if (!req.jwtPayload?.sub) throw new UnauthorizedError('Authentication required');
return req.jwtPayload as JwtPayload;
}
@ -48,8 +48,6 @@ export function requireRole(req: AuthRequest, minRole: Role): JwtPayload {
const order: Role[] = ['viewer', 'admin', 'super_admin'];
const userLevel = order.indexOf(payload.role ?? 'viewer');
const requiredLevel = order.indexOf(minRole);
if (userLevel < requiredLevel) {
throw Object.assign(new Error(`Role '${minRole}' required`), { statusCode: 403 });
}
if (userLevel < requiredLevel) throw new ForbiddenError(`Role '${minRole}' required`);
return payload;
}

View File

@ -21,7 +21,11 @@ async function platformFetch<T>(
...(opts.requestId ? { 'x-request-id': opts.requestId } : {}),
...(opts.productId ? { 'x-product-id': opts.productId } : {}),
};
const res = await fetch(url, { ...init, headers: { ...headers, ...(init.headers ?? {}) } });
const res = await fetch(url, {
...init,
headers: { ...headers, ...(init.headers ?? {}) },
signal: AbortSignal.timeout(10_000),
});
if (!res.ok) {
const body = await res.text().catch(() => '');
throw new Error(`platform-service ${init.method ?? 'GET'} ${path}${res.status}: ${body}`);

View File

@ -28,11 +28,6 @@ const app = await createServiceApp({
description:
'ByteLyst MCP Server — platform.telemetry.*, platform.diagnostics.*, extraction.*, support.*',
corsOrigin: config.CORS_ORIGIN,
swagger: {
title: 'ByteLyst MCP Server',
description: 'MCP tool execution endpoint for the ByteLyst platform',
port: config.PORT,
},
});
// Parse JWT on every request (best-effort)