From 9a298d50efee4b95d8ef13e832b41b7958b2bece Mon Sep 17 00:00:00 2001 From: saravanakumardb1 Date: Thu, 5 Mar 2026 12:06:54 -0800 Subject: [PATCH] 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 --- pnpm-lock.yaml | 66 +++++++++++++++++++ services/mcp-server/package.json | 2 + services/mcp-server/src/lib/auth.ts | 8 +-- .../mcp-server/src/lib/platform-client.ts | 6 +- services/mcp-server/src/server.ts | 5 -- 5 files changed, 76 insertions(+), 11 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e6bcaaac..e2b2994f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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: {} diff --git a/services/mcp-server/package.json b/services/mcp-server/package.json index a1856510..83f5172d 100644 --- a/services/mcp-server/package.json +++ b/services/mcp-server/package.json @@ -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": { diff --git a/services/mcp-server/src/lib/auth.ts b/services/mcp-server/src/lib/auth.ts index 40a63346..e81ad395 100644 --- a/services/mcp-server/src/lib/auth.ts +++ b/services/mcp-server/src/lib/auth.ts @@ -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 { } 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; } diff --git a/services/mcp-server/src/lib/platform-client.ts b/services/mcp-server/src/lib/platform-client.ts index 6a6fb8c3..2578f1b1 100644 --- a/services/mcp-server/src/lib/platform-client.ts +++ b/services/mcp-server/src/lib/platform-client.ts @@ -21,7 +21,11 @@ async function platformFetch( ...(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}`); diff --git a/services/mcp-server/src/server.ts b/services/mcp-server/src/server.ts index 21202ffb..bc9882bd 100644 --- a/services/mcp-server/src/server.ts +++ b/services/mcp-server/src/server.ts @@ -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)