buildmymcpserver/BuildMyMCPServer_MASTER_PROMPT.md

741 lines
34 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 🎯 BUILD: BuildMyMCPServer — Production-Ready SaaS
> **Master Prompt für Claude Code**
> Mission: Baue eine komplette, deploy-ready SaaS-Plattform die aus einem natürlichsprachigen Prompt einen vollständig gehosteten, OAuth-2.1-geschützten MCP-Server (Model Context Protocol) erzeugt, deployed und verwaltet. Endkunden konfigurieren ihn in Claude Desktop, Cursor oder ChatGPT mit einem Klick.
---
## 1. PROJECT IDENTITY & POSITIONING
**Produktname:** BuildMyMCPServer
**Domain:** buildmymcpserver.com (bzw. die spätere gewählte Variante)
**Tagline:** *"Describe your tool. We host the server. AI uses it."*
**Untertitel:** *"From prompt to production MCP server in 60 seconds. OAuth 2.1, Streamable HTTP, ready for Claude, Cursor & ChatGPT."*
**Zielgruppe primär:** B2B Mid-Market — Operations Leads, Solo-Founder, Tech-Teams in KMUs, Agenturen die für Kunden MCP-Server bauen, interne Tooling-Teams in Enterprise.
**Pricing-Anker (im UI sichtbar):**
- **Hobby:** €0 — 1 Server, 100k Tool-Calls/Mo, geteilte Infra, BMM-Subdomain
- **Pro:** €49/Mo — 5 Server, 1M Calls, Custom Domain, Priority Build
- **Team:** €149/Mo — 25 Server, 10M Calls, RBAC, Audit-Logs, SLA 99.9%
- **Enterprise:** ab €499/Mo — Unlimited, BYOC, SSO/SAML, dedicated Cluster
**Brand-Voice:** Technisch, präzise, ohne Marketing-BS. "It's infrastructure, not magic." Dunkel & monochrom mit einem präzisen Akzent. Kein lila Gradient, kein "AI sparkle". Inspiration: Linear, Vercel, Resend, Railway. **Native-feel, kein generisches AI-Wrapper-UI.**
---
## 2. TECH-STACK (fest, nicht verhandelbar)
### Monorepo (Turborepo + pnpm)
```
buildmymcpserver/
├── apps/
│ ├── web/ # Next.js 15 (App Router) — Marketing + Dashboard
│ ├── api/ # Fastify Backend (Control Plane)
│ ├── generator/ # Generation-Worker (BullMQ-Consumer)
│ └── runner-template/ # Template für gehostete MCP-Server-Container
├── packages/
│ ├── db/ # Drizzle ORM + PostgreSQL Schema
│ ├── mcp-templates/ # Code-Templates für generierte Server
│ ├── ui/ # Shared shadcn/ui components
│ ├── auth/ # Better-Auth wrapper
│ └── types/ # Shared TypeScript types (Zod)
├── infra/
│ ├── docker/ # Dockerfiles
│ ├── coolify/ # Coolify-Konfigurationen
│ └── traefik/ # Reverse-Proxy-Config
└── turbo.json
```
### Stack-Entscheidungen (final)
**Frontend Web:**
- Next.js 15 (App Router, RSC, Server Actions)
- TypeScript (strict mode)
- Tailwind CSS v4
- shadcn/ui (registry-mode, nur was wir brauchen)
- Lucide-React (icons)
- Framer Motion (sparingly — nur für state-transitions)
- next-themes (dark default, light optional)
**Backend:**
- Fastify (statt Express — schneller, besseres TS)
- Drizzle ORM
- PostgreSQL 16
- Redis 7 (BullMQ-Queue + Sessions)
- Better-Auth (Auth-Layer — User-Auth NICHT MCP-OAuth)
- Stripe (Subscriptions + Metered-Billing)
**MCP-Generierung:**
- Anthropic Claude API (claude-opus-4-7 für Generation, claude-haiku-4-5 für Fixes)
- `@modelcontextprotocol/sdk` v1.x (TypeScript) als Basis-Library für generierte Server
- Zod für Tool-Input-Validation
- esbuild für Build-Pipeline
**Hosting / Runtime:**
- Coolify (auf Hetzner Dedicated AX52) — Container-Orchestrator
- Docker (jeder Kunden-MCP-Server = eigener Container)
- Traefik (Reverse-Proxy + automatic SSL via Let's Encrypt + per-customer subdomains)
- Cloudflare (DNS + DDoS + Cache vor Web)
**Transport für generierte MCP-Server:**
- **Streamable HTTP** (Stand 2025-11-25 spec) — der einzige Standard heute
- KEIN SSE (deprecated seit 2025-06-18)
- Stateless-ready (Sessions in Redis falls nötig, default stateless)
- OAuth 2.1 + PKCE + Dynamic Client Registration + Resource Indicators (RFC 8707)
- `.well-known/oauth-protected-resource` Discovery Endpoint
- `.well-known/oauth-authorization-server` (OAuth Authorization Server Metadata, RFC 8414)
**Observability:**
- OpenTelemetry (OTel Collector → Grafana/Loki/Tempo Self-hosted)
- BetterStack für Uptime-Pings
- Sentry für Frontend + Backend Errors
**CI/CD:**
- GitHub Actions
- Docker-Build → Push zu GHCR
- Coolify Webhook für Auto-Deploy
---
## 3. SYSTEM-ARCHITEKTUR (so funktioniert das Ganze)
```
┌─────────────────────────────────────────────────────────────┐
│ END USER │
│ (uses Claude Desktop / Cursor / ChatGPT to call MCP tools) │
└──────────────────────────┬──────────────────────────────────┘
│ Streamable HTTP + OAuth 2.1
┌─────────────────────────────────────────────────────────────┐
│ TRAEFIK (per-customer routing) │
│ {slug}.mcp.buildmymcpserver.com → container │
└──────────────────────────┬──────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ KUNDEN-MCP-SERVER (Docker Container, 1 pro Server) │
│ - Generierter Code aus Template + Claude-Output │
│ - @modelcontextprotocol/sdk │
│ - OAuth 2.1 Resource Server │
│ - Connects to: Customer's APIs, DBs, etc. │
└──────────────────────────┬──────────────────────────────────┘
│ (logs, metrics)
┌─────────────────────────────────────────────────────────────┐
│ CONTROL PLANE (Fastify API) │
│ - User-Auth (Better-Auth) │
│ - Server-CRUD │
│ - Generation-Job-Queue (BullMQ) │
│ - Container-Orchestrierung (Coolify API) │
│ - Billing (Stripe webhooks) │
│ - Tool-Call-Metering (für Usage-based-Billing) │
└──────────────────────────┬──────────────────────────────────┘
┌────────────┼────────────┐
▼ ▼ ▼
┌────────┐ ┌──────────┐ ┌──────────┐
│Postgres│ │ Redis │ │ Coolify │
└────────┘ └──────────┘ └──────────┘
┌─────────────────────────────────────────────────────────────┐
│ CUSTOMER (Marco's User) — uses Web Dashboard │
│ Next.js 15 Dashboard: │
│ - Prompt to build a new MCP server │
│ - Watch generation stream in real-time │
│ - Manage existing servers (logs, restart, edit) │
│ - Install instructions for Claude/Cursor/ChatGPT │
└─────────────────────────────────────────────────────────────┘
```
**Kritischer Flow — "Prompt to Live Server":**
1. User loggt sich ins Dashboard ein, klickt "New MCP Server"
2. Gibt Prompt ein: *"Create an MCP server that queries my Postgres DB at db.example.com with read-only access to the users and orders tables"*
3. UI fragt strukturiert nach: API endpoints, secrets needed, scopes, tool names
4. Backend pusht Job in BullMQ Queue
5. Generator-Worker:
- Lädt MCP-Template
- Ruft Claude API mit System-Prompt + User-Spezifikation
- Parst generierte Tools/Resources/Prompts (Zod-validated)
- Schreibt fertigen TypeScript-Code
- Baut Docker-Image via `docker buildx`
- Pusht zu interner Registry
- Triggert Coolify-Deployment auf `{slug}.mcp.buildmymcpserver.com`
- Streamt Status (queued → generating → building → deploying → live) via Server-Sent-Events ans Dashboard
6. User sieht Live-URL + Copy-Paste-Snippets für:
- **Claude Desktop:** `claude_desktop_config.json` Eintrag
- **Cursor:** `mcp.json` Eintrag
- **ChatGPT Custom Connector:** URL + OAuth-Setup
7. Erster Tool-Call vom End-User → Traefik → Container → Tool → Response → Metric in Redis → später aggregiert in Postgres für Billing
---
## 4. DATABASE SCHEMA (Drizzle, vollständig)
```typescript
// packages/db/schema.ts
import { pgTable, uuid, varchar, text, timestamp, jsonb, boolean, integer, bigint, index, pgEnum } from 'drizzle-orm/pg-core';
export const planEnum = pgEnum('plan', ['hobby', 'pro', 'team', 'enterprise']);
export const serverStatusEnum = pgEnum('server_status', ['draft', 'queued', 'generating', 'building', 'deploying', 'live', 'failed', 'paused']);
export const buildStatusEnum = pgEnum('build_status', ['queued', 'running', 'success', 'failed', 'cancelled']);
// Tenant/Org-Modell — auch wenn Single-User, baust du es Org-fähig (kein Refactor später)
export const organizations = pgTable('organizations', {
id: uuid('id').defaultRandom().primaryKey(),
slug: varchar('slug', { length: 64 }).notNull().unique(),
name: varchar('name', { length: 128 }).notNull(),
plan: planEnum('plan').default('hobby').notNull(),
stripeCustomerId: varchar('stripe_customer_id', { length: 128 }),
stripeSubscriptionId: varchar('stripe_subscription_id', { length: 128 }),
monthlyCallQuota: bigint('monthly_call_quota', { mode: 'number' }).default(100_000),
callsThisPeriod: bigint('calls_this_period', { mode: 'number' }).default(0),
periodStartsAt: timestamp('period_starts_at').defaultNow(),
createdAt: timestamp('created_at').defaultNow().notNull(),
});
export const users = pgTable('users', {
id: uuid('id').defaultRandom().primaryKey(),
email: varchar('email', { length: 255 }).notNull().unique(),
name: varchar('name', { length: 128 }),
avatarUrl: text('avatar_url'),
createdAt: timestamp('created_at').defaultNow().notNull(),
});
export const memberships = pgTable('memberships', {
id: uuid('id').defaultRandom().primaryKey(),
orgId: uuid('org_id').references(() => organizations.id, { onDelete: 'cascade' }).notNull(),
userId: uuid('user_id').references(() => users.id, { onDelete: 'cascade' }).notNull(),
role: varchar('role', { length: 32 }).default('owner').notNull(), // owner | admin | member | viewer
createdAt: timestamp('created_at').defaultNow().notNull(),
});
export const mcpServers = pgTable('mcp_servers', {
id: uuid('id').defaultRandom().primaryKey(),
orgId: uuid('org_id').references(() => organizations.id, { onDelete: 'cascade' }).notNull(),
slug: varchar('slug', { length: 64 }).notNull(), // becomes subdomain
name: varchar('name', { length: 128 }).notNull(),
description: text('description'),
status: serverStatusEnum('status').default('draft').notNull(),
currentVersion: integer('current_version').default(0),
containerId: varchar('container_id', { length: 128 }), // Coolify ref
publicUrl: text('public_url'), // https://{slug}.mcp.buildmymcpserver.com
toolsSchema: jsonb('tools_schema'), // generated tools metadata
oauthEnabled: boolean('oauth_enabled').default(true),
createdAt: timestamp('created_at').defaultNow().notNull(),
updatedAt: timestamp('updated_at').defaultNow().notNull(),
}, (table) => ({
orgSlugIdx: index('idx_servers_org_slug').on(table.orgId, table.slug),
}));
export const builds = pgTable('builds', {
id: uuid('id').defaultRandom().primaryKey(),
serverId: uuid('server_id').references(() => mcpServers.id, { onDelete: 'cascade' }).notNull(),
version: integer('version').notNull(),
prompt: text('prompt').notNull(),
generatedCode: text('generated_code'),
status: buildStatusEnum('status').default('queued').notNull(),
errorMessage: text('error_message'),
startedAt: timestamp('started_at'),
finishedAt: timestamp('finished_at'),
createdAt: timestamp('created_at').defaultNow().notNull(),
});
export const secrets = pgTable('secrets', {
id: uuid('id').defaultRandom().primaryKey(),
serverId: uuid('server_id').references(() => mcpServers.id, { onDelete: 'cascade' }).notNull(),
key: varchar('key', { length: 128 }).notNull(),
encryptedValue: text('encrypted_value').notNull(), // AES-256-GCM, key in env
createdAt: timestamp('created_at').defaultNow().notNull(),
});
// Per-customer OAuth clients (für Dynamic Client Registration)
export const oauthClients = pgTable('oauth_clients', {
id: uuid('id').defaultRandom().primaryKey(),
serverId: uuid('server_id').references(() => mcpServers.id, { onDelete: 'cascade' }).notNull(),
clientId: varchar('client_id', { length: 128 }).notNull().unique(),
clientSecretHash: text('client_secret_hash'), // bcrypt
redirectUris: jsonb('redirect_uris').notNull(), // string[]
metadata: jsonb('metadata'), // RFC 7591 client metadata
createdAt: timestamp('created_at').defaultNow().notNull(),
});
export const oauthTokens = pgTable('oauth_tokens', {
id: uuid('id').defaultRandom().primaryKey(),
clientId: uuid('client_id').references(() => oauthClients.id, { onDelete: 'cascade' }).notNull(),
accessTokenHash: text('access_token_hash').notNull(),
refreshTokenHash: text('refresh_token_hash'),
scope: text('scope'),
expiresAt: timestamp('expires_at').notNull(),
createdAt: timestamp('created_at').defaultNow().notNull(),
});
export const toolCallMetrics = pgTable('tool_call_metrics', {
id: uuid('id').defaultRandom().primaryKey(),
serverId: uuid('server_id').references(() => mcpServers.id).notNull(),
toolName: varchar('tool_name', { length: 128 }).notNull(),
durationMs: integer('duration_ms'),
success: boolean('success').notNull(),
errorCode: varchar('error_code', { length: 64 }),
timestamp: timestamp('timestamp').defaultNow().notNull(),
}, (table) => ({
serverTimeIdx: index('idx_metrics_server_time').on(table.serverId, table.timestamp),
}));
export const auditLog = pgTable('audit_log', {
id: uuid('id').defaultRandom().primaryKey(),
orgId: uuid('org_id').references(() => organizations.id),
userId: uuid('user_id').references(() => users.id),
action: varchar('action', { length: 128 }).notNull(),
resourceType: varchar('resource_type', { length: 64 }),
resourceId: varchar('resource_id', { length: 128 }),
metadata: jsonb('metadata'),
ipAddress: varchar('ip_address', { length: 64 }),
createdAt: timestamp('created_at').defaultNow().notNull(),
});
```
---
## 5. UI/UX — DAS MUSS NATIVE-FEEL HABEN
### Design-Prinzipien
1. **Monochrom mit Akzent.** Background `#0A0A0B` (fast schwarz aber nicht). Foreground `#FAFAFA`. Border `#1F1F22`. Akzentfarbe: ein präzises **Electric Indigo `#6366F1`** sparingly für CTAs und Status-Live. Kein Lila-Gradient.
2. **Geometric Sans + Mono.** `Geist` für UI, `Geist Mono` für Code/IDs/URLs.
3. **Tight Density.** Padding 12-16px nicht 24-32px. Compact-feeling wie Linear/Vercel-Dashboard.
4. **Reduced motion.** Nur Skeleton-Loads und State-Changes animieren (200ms ease-out). Keine "hero-spinners".
5. **Code als Hero.** Jede Page hat irgendwo einen Code-Block (Copy-Button immer rechts oben).
6. **Status-Pills.** `live` = green dot pulsing, `building` = amber spinner, `failed` = red, `draft` = grey.
### Pages (komplett)
**Marketing (apps/web/app):**
1. **`/` (Landing):**
- Hero: Großes Heading *"Describe your tool. We host the server."*, Subline, ein Code-Block der einen Prompt zeigt + Output-Snippet (animiertes Typing-Demo, nicht Video, in HTML).
- "How it works" — 3 Steps mit jeweils einem kleinen Screenshot/Code-Block.
- "Works with" — Logos: Claude, Cursor, ChatGPT, VS Code Copilot, Continue.dev.
- "Examples" — Grid mit 6 Use-Cases (Postgres, Salesforce, Notion, GitHub, Stripe, Custom REST).
- Pricing (4 Tiers, transparent, kein "Contact us" außer Enterprise).
- FAQ (12 Fragen: Was ist MCP, Brauche ich OAuth, Welche AI-Tools, Kann ich self-hosten, Was passiert wenn ich kündige, Custom Domain, etc.).
- Footer: Status, Docs, GitHub (wenn open-source-Teile), Security/SOC2-roadmap, Privacy, Terms.
2. **`/docs/*`** — MDX-basiert, schmaler 280px Sidebar, max-w-prose, Search via Pagefind. Bereiche: Quickstart, Concepts (Tools, Resources, Prompts, OAuth), Recipes, API-Reference, Self-Hosting-Guide.
3. **`/changelog`** — MDX timeline.
4. **`/pricing`** — wenn aus Landing rausgezogen werden soll.
**Dashboard (apps/web/app/(dashboard)):**
5. **`/dashboard`** — Übersicht. Cards: Servers (Anzahl + Status), Calls this period (Progress-Bar vs Quota), Recent Builds, Quick-Action "New Server".
6. **`/servers`** — Liste aller MCP-Server. Tabelle mit Name, Status, Calls (24h), Last Build, Actions. Filter + Search.
7. **`/servers/new`** — DER WIZARD. Drei-Step:
- Step 1: Prompt-Eingabe (Textarea mit Beispiel-Prompts als Chips drumherum: *"Postgres reader"*, *"Notion search"*, *"Custom REST API"*).
- Step 2: Strukturierte Confirmation. Claude hat parsed, zeigt: Tool-Liste, Required Secrets (Input-Felder, encrypted save), Optional Scopes.
- Step 3: Deploy. Live-Stream der Build-Logs (Server-Sent Events von der API). Bei Success: Live-URL + 3 Tabs (Claude Desktop, Cursor, ChatGPT) mit copy-ready Config-Snippets.
8. **`/servers/[id]`** — Server-Detail mit Tabs:
- **Overview:** Status, URL, OAuth-Status, Quick-Install-Configs
- **Tools:** Liste der exposed Tools mit Schemas
- **Logs:** Streaming-Logs (Server-Sent Events, last 1000 lines)
- **Metrics:** Charts (Calls/h, Latency P50/P95/P99, Error-Rate) — Recharts
- **Secrets:** Manage env-vars (verschlüsselt)
- **Iterate:** Neuer Prompt um Server zu erweitern (= neuer Build, neue Version)
- **Settings:** Rename, Custom Domain (Pro+), Pause, Delete
9. **`/settings`** — Org/User-Settings, Billing (Stripe Customer Portal embed), API Keys (für CLI), Team Members (Team+).
10. **`/audit`** — Audit Log (Team+).
### Komponenten-Library
Use shadcn/ui registry, install only: `button`, `card`, `dialog`, `dropdown-menu`, `input`, `label`, `select`, `separator`, `sheet`, `table`, `tabs`, `toast`, `tooltip`, `badge`, `skeleton`, `progress`, `command` (für CMD+K). Custom: `<CodeBlock>` mit Shiki highlighting + Copy-Button, `<StatusPill>` mit Animations, `<StreamingLogs>`.
---
## 6. MCP-GENERIERUNGS-PIPELINE (das Kernstück)
### Template (apps/runner-template/src/server.ts)
Ein parametrisiertes Template das die Generation füllt:
```typescript
// THIS IS A TEMPLATE — values in {{ }} get replaced by generator
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { z } from "zod";
import Fastify from "fastify";
import { createRemoteJWKSet, jwtVerify } from "jose";
const server = new McpServer({
name: "{{SERVER_NAME}}",
version: "{{SERVER_VERSION}}",
});
// {{TOOL_REGISTRATIONS}}
// Generated tools go here, e.g.:
// server.tool("query_users", "...", { id: z.string() }, async ({ id }) => { ... });
// {{RESOURCE_REGISTRATIONS}}
// {{PROMPT_REGISTRATIONS}}
const app = Fastify({ logger: { level: 'info' } });
// OAuth 2.1 Protected Resource Metadata (RFC 9728)
app.get('/.well-known/oauth-protected-resource', async () => ({
resource: process.env.PUBLIC_URL,
authorization_servers: [`${process.env.PUBLIC_URL}/oauth`],
bearer_methods_supported: ['header'],
resource_documentation: `${process.env.PUBLIC_URL}/docs`,
scopes_supported: {{SCOPES_JSON}},
}));
// OAuth Authorization Server Metadata (RFC 8414)
app.get('/.well-known/oauth-authorization-server', async () => ({
issuer: `${process.env.PUBLIC_URL}/oauth`,
authorization_endpoint: `${process.env.PUBLIC_URL}/oauth/authorize`,
token_endpoint: `${process.env.PUBLIC_URL}/oauth/token`,
registration_endpoint: `${process.env.PUBLIC_URL}/oauth/register`,
response_types_supported: ['code'],
grant_types_supported: ['authorization_code', 'refresh_token'],
code_challenge_methods_supported: ['S256'],
token_endpoint_auth_methods_supported: ['client_secret_basic', 'client_secret_post', 'none'],
}));
// OAuth endpoints (delegated to control-plane via internal RPC, or local for stateless)
// {{OAUTH_HANDLERS}}
// MCP Streamable HTTP endpoint
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined, // stateless mode
});
app.all('/mcp', async (request, reply) => {
// Validate bearer token (OAuth 2.1 Resource Server)
const auth = request.headers.authorization;
if (!auth?.startsWith('Bearer ')) {
return reply
.code(401)
.header('WWW-Authenticate', `Bearer resource_metadata="${process.env.PUBLIC_URL}/.well-known/oauth-protected-resource"`)
.send({ error: 'unauthorized' });
}
const token = auth.slice(7);
try {
const JWKS = createRemoteJWKSet(new URL(`${process.env.CONTROL_PLANE_URL}/oauth/jwks`));
const { payload } = await jwtVerify(token, JWKS, {
issuer: `${process.env.PUBLIC_URL}/oauth`,
audience: process.env.PUBLIC_URL,
});
// RFC 8707 Resource Indicators check
if (payload.aud !== process.env.PUBLIC_URL) {
return reply.code(403).send({ error: 'invalid_audience' });
}
} catch (e) {
return reply.code(401).send({ error: 'invalid_token' });
}
// Forward to MCP transport
await transport.handleRequest(request.raw, reply.raw, request.body);
});
await server.connect(transport);
await app.listen({ port: parseInt(process.env.PORT || '3000'), host: '0.0.0.0' });
```
### Generator-Worker Logic
```typescript
// apps/generator/src/worker.ts (Pseudocode-Skelett)
import { Worker } from 'bullmq';
import Anthropic from '@anthropic-ai/sdk';
const SYSTEM_PROMPT = `You are an expert TypeScript engineer generating production MCP servers.
Output ONLY a JSON object with this exact schema:
{
"tools": [
{
"name": "snake_case_name",
"description": "Clear description for the AI client",
"inputSchema": { /* JSON Schema, strict types */ },
"implementation": "async ({ args }) => { /* full TypeScript body */ }"
}
],
"resources": [...],
"prompts": [...],
"requiredSecrets": ["API_KEY", "DATABASE_URL"],
"scopes": ["read:users", "write:orders"],
"dependencies": { "pg": "^8.13.0" }
}
Rules:
- Use Zod for parameter schemas, NOT JSON Schema directly
- All implementations MUST be async, MUST handle errors, MUST return MCP content blocks
- NEVER use eval, exec, fs writes outside /tmp
- NEVER hardcode secrets — use process.env.{SECRET_NAME}
- Validate all inputs with Zod before use
- Return errors as { content: [{ type: "text", text: "Error: ..." }], isError: true }
- For databases: use parameterized queries ONLY
- For external APIs: implement retry with exponential backoff
- Each tool must be idempotent OR clearly state it is destructive in description
`;
new Worker('build', async (job) => {
const { serverId, prompt, orgId } = job.data;
// 1. Generate with Claude
const anthropic = new Anthropic();
const response = await anthropic.messages.create({
model: 'claude-opus-4-7',
max_tokens: 8192,
system: SYSTEM_PROMPT,
messages: [{ role: 'user', content: prompt }],
});
// 2. Parse + Validate output (Zod schema for the generator output itself)
const spec = parseAndValidate(response.content[0].text);
// 3. Inject into template
const code = renderTemplate(spec);
// 4. Run static checks (tsc --noEmit on generated code, lint, basic sandbox-test)
await runStaticChecks(code);
// 5. Build Docker image
const imageTag = `mcp-${serverId}:v${version}`;
await dockerBuild(code, imageTag, spec.dependencies);
// 6. Deploy via Coolify API
await coolify.deploy({
image: imageTag,
subdomain: `${serverSlug}.mcp.buildmymcpserver.com`,
env: secrets,
healthcheck: '/health',
});
// 7. Update DB, emit SSE to dashboard
await db.update(mcpServers).set({ status: 'live', publicUrl: '...' }).where(...);
await redis.publish(`build:${serverId}`, JSON.stringify({ status: 'live' }));
}, { connection: redis });
```
### Sandboxing & Sicherheit (NON-NEGOTIABLE)
- Container laufen mit `--read-only` Filesystem (außer `/tmp` mit Quota)
- `--cap-drop=ALL`, `--security-opt=no-new-privileges`
- CPU-Limit 0.5 cores, Memory 512MB pro Container default
- Network-Egress nur zu whitelisted Domains (definiert via Tool-Spec)
- Keine SSH, kein Shell in den Containern
- Secrets via Docker Secrets / Coolify Env-Vars (verschlüsselt at rest mit AES-256-GCM in Postgres, KMS-Key in Hetzner Vault oder Doppler)
- Rate-Limit pro Tool-Call (default 100/min/IP, configurable)
- Prompt-Injection-Defense: Generierte Tool-Descriptions werden auf gefährliche Patterns gescannt (z.B. "ignore previous instructions" → reject build)
---
## 7. PFLICHT-FEATURES FÜR PRODUCTION
### MUST-HAVE (MVP-Cut)
- [ ] Auth (Email-Magic-Link + GitHub-OAuth via Better-Auth)
- [ ] Org-Management mit RBAC
- [ ] Server-CRUD
- [ ] Prompt-to-Server Wizard mit Live-Build-Stream
- [ ] Secret-Management (encrypted)
- [ ] OAuth-2.1-Server-Implementation für jeden generierten MCP
- [ ] Streamable HTTP Transport in jedem Generated Server
- [ ] Stripe-Billing (Pro/Team-Subs + Metered Overage)
- [ ] Tool-Call-Metering + Quota-Enforcement
- [ ] Server Detail mit Logs/Metrics
- [ ] Install-Snippets für Claude Desktop, Cursor, ChatGPT
- [ ] Marketing-Landing
- [ ] Docs-Site (10+ Pages)
- [ ] Status-Page
- [ ] Audit-Log
- [ ] Health-Checks + Auto-Restart bei Failure
- [ ] Backups (DB + Server-Configs daily zu Backblaze B2)
### SHOULD-HAVE (Phase 2, nach erstem zahlenden Kunden)
- [ ] CLI (`buildmymcp init`, `deploy`, `logs`)
- [ ] GitHub-Repo-Sync (Server-Code in Customer's Repo pushen für Transparenz/Audit)
- [ ] Template-Marketplace (Pre-Built MCP-Server für gängige Tools)
- [ ] Custom-Domain per Server (CNAME-Validation)
- [ ] Team-Member-Invites
- [ ] Webhooks für Build-Events
- [ ] SSO/SAML für Enterprise
### NICE-TO-HAVE (Phase 3)
- [ ] BYOC (Bring Your Own Cloud — Deploy in Customer's AWS/GCP/Azure)
- [ ] MCP-Registry-Publishing (Servers public listen)
- [ ] AI-Powered Test-Generation (Tests für jeden generierten Tool)
- [ ] Multi-Region Deployment
---
## 8. DEPLOYMENT (production-ready ab Tag 1)
### Hetzner-Setup
- **1× AX52 Dedicated** (€59/Mo): 16 Cores / 64GB RAM — running Coolify + Postgres + Redis + Generator + Customer-Containers (capacity: ~300-500 small MCP-Server)
- **1× CX22 Cloud** (€5/Mo) als Backup-Target + Status-Page
- **Cloudflare Free Plan** für DNS + DDoS
- **Backblaze B2** für DB-Backups (~€1/Mo bis 10GB)
### Domains-Plan
- `buildmymcpserver.com` — Marketing + Dashboard
- `api.buildmymcpserver.com` — Control-Plane API
- `*.mcp.buildmymcpserver.com` — Customer MCP-Server (Wildcard SSL via Let's Encrypt DNS challenge mit Cloudflare API)
- `docs.buildmymcpserver.com` (oder /docs auf Hauptdomain)
- `status.buildmymcpserver.com` (BetterStack hosted)
### Environment-Variables (.env.example)
```
# Control Plane
DATABASE_URL=postgresql://...
REDIS_URL=redis://...
BETTER_AUTH_SECRET=
BETTER_AUTH_URL=https://buildmymcpserver.com
GITHUB_OAUTH_ID=
GITHUB_OAUTH_SECRET=
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
STRIPE_PRICE_PRO=
STRIPE_PRICE_TEAM=
ANTHROPIC_API_KEY=
SECRETS_ENCRYPTION_KEY= # 32 bytes hex, AES-256-GCM
COOLIFY_API_URL=https://coolify.internal/...
COOLIFY_API_TOKEN=
TRAEFIK_API_URL=
CLOUDFLARE_API_TOKEN=
SENTRY_DSN=
OTEL_EXPORTER_OTLP_ENDPOINT=
# Per-MCP-Container (injected by generator/coolify)
PUBLIC_URL=https://{slug}.mcp.buildmymcpserver.com
CONTROL_PLANE_URL=https://api.buildmymcpserver.com
SERVER_ID=
PORT=3000
```
### CI/CD (GitHub Actions)
- `pnpm typecheck` (alle Pakete, strict)
- `pnpm test` (Vitest)
- `pnpm lint` (eslint + biome)
- `pnpm build` (turbo)
- Docker-Build mit cache
- Push zu GHCR
- Coolify-Webhook trigger
- Smoke-Test gegen Staging
- Promote zu Prod
---
## 9. WAS DU IN DIESER REIHENFOLGE BAUST
**Sprint 1 (Woche 1): Foundation**
1. Monorepo-Setup (turbo, pnpm, tsconfig, biome, prettier)
2. DB-Schema + Drizzle-Migrations
3. Better-Auth-Setup mit Magic-Link + GitHub
4. Web-App-Shell (Layout, Theme, Auth-Pages, leeres Dashboard)
5. Marketing-Landing (Hero, Pricing, FAQ — kann statisch)
**Sprint 2 (Woche 2): Generation-Pipeline**
6. MCP-Template (apps/runner-template) — fertig lauffähig stdio + Streamable HTTP
7. Generator-Worker mit BullMQ + Claude-API-Call
8. Static-Checks-Pipeline (tsc, lint, security-scan)
9. Docker-Build-Pipeline (lokal mit buildx)
10. Coolify-API-Integration zum Deploy
**Sprint 3 (Woche 3): OAuth + Dashboard**
11. OAuth-2.1-Implementation (Control-Plane als Authorization Server, Per-Server-Container als Resource Server)
12. JWKS Endpoint + Token-Signing (asymmetric, RS256)
13. Dashboard: New-Server-Wizard mit Live-SSE-Stream
14. Server-Detail-Page mit Logs/Metrics
15. Install-Snippets-Generator (Claude/Cursor/ChatGPT-Configs)
**Sprint 4 (Woche 4): Billing + Production**
16. Stripe-Integration (Subscriptions, Webhooks, Metered Usage)
17. Quota-Enforcement
18. Audit-Log
19. Docs-Site (10+ MDX-Pages)
20. Status-Page-Integration
21. Backup-Cron
22. End-to-End Smoke-Test
23. Production-Deploy
---
## 10. ACCEPTANCE CRITERIA — WANN BIST DU FERTIG
✅ User kann sich registrieren, einloggen, sieht ein leeres Dashboard.
✅ User klickt "New Server", gibt Prompt ein wie *"Create an MCP server that exposes a 'search_docs' tool which queries my Notion via API"*, gibt Notion-API-Key ein, sieht Build-Stream live.
✅ Nach <90 Sekunden: Live-URL `xyz.mcp.buildmymcpserver.com/mcp` ist erreichbar, return 401 ohne Token, 200 mit gültigem Token.
User kopiert Snippet in `claude_desktop_config.json`, startet Claude Desktop neu, OAuth-Flow geht durch, Tool ist verfügbar in Claude.
User stellt Frage in Claude die `search_docs` triggert, Result kommt zurück, Call wird in Dashboard-Metrics gezählt.
User kann den Server iterieren ("Add a 'create_page' tool"), neue Version wird gebaut, ohne dass die alte runtergeht (rolling deploy).
Stripe-Checkout für Pro-Plan funktioniert, Quota wird angehoben.
Bei Quota-Überschreitung: HTTP 429 + Hinweis im Dashboard.
Logs streamen in Echtzeit ins Dashboard.
Audit-Log zeigt alle Aktionen.
Lighthouse: 95+ auf allen Marketing-Pages, 90+ auf Dashboard.
TypeScript strict, 0 `any`, 0 unused imports, biome-clean.
Sentry erfasst Errors, OTel-Traces gehen durch.
DB-Backup läuft täglich automatisch.
Generated Server bestehen das offizielle MCP-Test-Suite (`mcp-inspector` + Anthropic's Reference-Client).
---
## 11. WAS DU NIEMALS TUN DARFST
KEIN `eval()` oder `Function()` im Generator-Worker für generierten Code immer durch Static-Build-Pipeline.
KEIN Sharing von Containern zwischen Kunden 1 Server = 1 Container = 1 Isolation-Boundary.
KEINE Plain-Text-Secrets in DB immer AES-256-GCM verschlüsselt.
KEINE Verwendung von deprecated SSE-Transport nur Streamable HTTP.
KEINE Token-Pass-Through (Spec verbietet es ausdrücklich) du musst Token-Exchange machen wenn der MCP-Server downstream-APIs aufruft.
KEINE Storage von OAuth-Access-Tokens im Klartext nur Hashes für Verification.
KEIN `*` als CORS explizit pro Server konfigurieren.
KEIN automatisches Deploy von generated Code ohne Static-Checks wenn Checks failen, Build = failed, klare Error-Message.
KEIN Branding wie "Powered by Claude" oder ähnliches im Marketing du bist eine eigenständige Plattform, kein Anthropic-Wrapper.
KEIN "AI-Sparkle-Design" Linear/Vercel-Style. Boring on the outside, magic on the inside.
---
## 12. REFERENCES (FÜR DICH, CLAUDE CODE, IM ZWEIFELSFALL CHECKEN)
- MCP Spec: https://modelcontextprotocol.io/specification/2025-11-25/
- MCP Authorization: https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization
- TypeScript SDK: https://github.com/modelcontextprotocol/typescript-sdk
- FastMCP (alternative wrapper, optional): https://github.com/punkpeye/fastmcp
- OAuth 2.1: https://datatracker.ietf.org/doc/draft-ietf-oauth-v2-1/
- RFC 8414 (Auth Server Metadata): https://datatracker.ietf.org/doc/html/rfc8414
- RFC 9728 (Protected Resource Metadata): https://datatracker.ietf.org/doc/html/rfc9728
- RFC 8707 (Resource Indicators): https://datatracker.ietf.org/doc/html/rfc8707
- RFC 7591 (Dynamic Client Registration): https://datatracker.ietf.org/doc/html/rfc7591
- Coolify Docs: https://coolify.io/docs
- Drizzle ORM: https://orm.drizzle.team/
---
## 13. START
Lies diese Spec komplett durch. Stelle Rückfragen wenn etwas mehrdeutig ist. Beginne dann mit Sprint 1, Task 1: Monorepo-Setup. Commit nach jedem abgeschlossenen Task mit conventional-commits-Format (`feat:`, `fix:`, `chore:`, `docs:`).
Wenn du an einem Punkt unsicher bist über die MCP-Spec, **fetche die offizielle Doku, rate nicht aus dem Speicher.** Die Spec ändert sich noch häufig.
**Build it like infrastructure, not like a demo. Production-grade from line 1.**
Let's go.