@dnb/eufemia
Version:
DNB Eufemia Design System UI Library
120 lines • 3.55 kB
JavaScript
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