From efa2c3f30d88bc03ae146c37c5c9bc2a1c772dc0 Mon Sep 17 00:00:00 2001 From: Marco Sadjadi Date: Tue, 19 May 2026 00:27:22 +0200 Subject: [PATCH] feat(runner-template): MCP Streamable HTTP + OAuth 2.1 resource server template --- apps/runner-template/.dockerignore | 4 ++++ apps/runner-template/Dockerfile | 15 +++++++++++++++ apps/runner-template/package.json | 19 +++++++++++++++++++ apps/runner-template/src/server.ts | 18 ++++++++++++++++++ apps/runner-template/tsconfig.json | 15 +++++++++++++++ 5 files changed, 71 insertions(+) create mode 100644 apps/runner-template/.dockerignore create mode 100644 apps/runner-template/Dockerfile create mode 100644 apps/runner-template/package.json create mode 100644 apps/runner-template/src/server.ts create mode 100644 apps/runner-template/tsconfig.json diff --git a/apps/runner-template/.dockerignore b/apps/runner-template/.dockerignore new file mode 100644 index 0000000..1375393 --- /dev/null +++ b/apps/runner-template/.dockerignore @@ -0,0 +1,4 @@ +node_modules +dist +.turbo +*.tsbuildinfo diff --git a/apps/runner-template/Dockerfile b/apps/runner-template/Dockerfile new file mode 100644 index 0000000..ee9d823 --- /dev/null +++ b/apps/runner-template/Dockerfile @@ -0,0 +1,15 @@ +FROM node:20-alpine AS deps +WORKDIR /app +COPY package.json ./ +RUN npm install --omit=dev --no-audit --no-fund && npm install --no-save tsx@4.19.2 typescript@5.7.2 + +FROM node:20-alpine AS runtime +WORKDIR /app +ENV NODE_ENV=production +COPY --from=deps /app/node_modules ./node_modules +COPY package.json tsconfig.json ./ +COPY src ./src +EXPOSE 3000 +HEALTHCHECK --interval=15s --timeout=3s --start-period=10s --retries=3 \ + CMD wget -qO- http://localhost:3000/health || exit 1 +CMD ["npx", "tsx", "src/server.ts"] diff --git a/apps/runner-template/package.json b/apps/runner-template/package.json new file mode 100644 index 0000000..cc77388 --- /dev/null +++ b/apps/runner-template/package.json @@ -0,0 +1,19 @@ +{ + "name": "bmm-runner", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "start": "tsx src/server.ts" + }, + "dependencies": { + "@modelcontextprotocol/sdk": "1.0.4", + "fastify": "5.2.0", + "jose": "5.9.6", + "zod": "3.23.8" + }, + "devDependencies": { + "tsx": "4.19.2", + "typescript": "5.7.2" + } +} diff --git a/apps/runner-template/src/server.ts b/apps/runner-template/src/server.ts new file mode 100644 index 0000000..b1f4baa --- /dev/null +++ b/apps/runner-template/src/server.ts @@ -0,0 +1,18 @@ +// Placeholder. The generator overwrites this file with rendered code. +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; +import Fastify from 'fastify'; +import { randomUUID } from 'node:crypto'; + +const PORT = Number.parseInt(process.env.PORT ?? '3000', 10); +const server = new McpServer({ name: 'placeholder', version: '0.0.0' }); +const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID() }); + +const app = Fastify({ logger: { level: 'info' } }); +app.get('/health', async () => ({ ok: true })); +app.all('/mcp', async (req, reply) => { + await transport.handleRequest(req.raw, reply.raw, req.body); +}); + +await server.connect(transport); +await app.listen({ port: PORT, host: '0.0.0.0' }); diff --git a/apps/runner-template/tsconfig.json b/apps/runner-template/tsconfig.json new file mode 100644 index 0000000..711a22c --- /dev/null +++ b/apps/runner-template/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "esModuleInterop": true, + "resolveJsonModule": true, + "isolatedModules": true, + "skipLibCheck": true, + "strict": true, + "noUncheckedIndexedAccess": true, + "verbatimModuleSyntax": false + }, + "include": ["src/**/*"] +}