UNPKG

@dnb/eufemia

Version:

DNB Eufemia Design System UI Library

120 lines 3.55 kB
import _URL from "core-js-pure/stable/url/index.js"; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js'; import { SERVER_INFO, createDocsTools, registerDocsTools, validateDocsSource } from "../mcp-docs-server.js"; import { createBundledDocsSource } from "../docs-source.js"; import docsBundle from './docs.bundle.json'; const docsSource = createBundledDocsSource(docsBundle, { label: 'worker:docs.bundle.json' }); let validatedOnce = false; async function ensureValidated() { if (!validatedOnce) { await validateDocsSource(docsSource); validatedOnce = true; } } function authResponse() { return new Response(JSON.stringify({ jsonrpc: '2.0', error: { code: -32001, message: 'Unauthorized' }, id: null }), { status: 401, headers: { 'content-type': 'application/json', 'www-authenticate': 'Bearer realm="eufemia-mcp"' } }); } async function safeEqual(a, b) { const encoder = new TextEncoder(); const keyData = encoder.encode(a); const key = await crypto.subtle.importKey('raw', keyData, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign', 'verify']); const sig = await crypto.subtle.sign('HMAC', key, encoder.encode(a)); return crypto.subtle.verify('HMAC', key, sig, encoder.encode(b)); } async function checkAuth(request, env) { var _request$headers$get; const token = env.MCP_AUTH_TOKEN; if (!token) { return null; } const header = (_request$headers$get = request.headers.get('authorization')) !== null && _request$headers$get !== void 0 ? _request$headers$get : ''; const expected = `Bearer ${token}`; if (header.length === expected.length && (await safeEqual(header, expected))) { return null; } return authResponse(); } async function handleMcp(request, env) { const unauthorized = await checkAuth(request, env); if (unauthorized) { return unauthorized; } const tools = createDocsTools({ source: docsSource }); const server = new McpServer(SERVER_INFO); registerDocsTools(server, tools); const transport = new WebStandardStreamableHTTPServerTransport({ sessionIdGenerator: undefined, enableJsonResponse: true }); await server.connect(transport); try { return await transport.handleRequest(request); } finally { try { await transport.close(); } catch {} } } function handleHealth() { return new Response(JSON.stringify({ ok: true, name: SERVER_INFO.name, version: SERVER_INFO.version, transports: ['streamable-http'], runtime: 'cloudflare-worker' }), { status: 200, headers: { 'content-type': 'application/json' } }); } const handler = { async fetch(request, env) { const url = new _URL(request.url); if (url.pathname === '/healthz' && request.method === 'GET') { return handleHealth(); } if (url.pathname === '/mcp') { try { await ensureValidated(); } catch (e) { return new Response(`Eufemia MCP misconfigured: ${e.message}`, { status: 500 }); } if (request.method !== 'POST' && request.method !== 'GET' && request.method !== 'DELETE') { return new Response('Method Not Allowed', { status: 405 }); } return handleMcp(request, env); } return new Response('Not Found', { status: 404 }); } }; export default handler; //# sourceMappingURL=index.js.map