totalmcp
Unified MCP gateway for Jason's ALPHA stack. One Dockerized server, hot-reloadable plugin architecture, three agent clients (Claude Code, Codex, Antigravity) all connecting to one stable endpoint.
- Port:
8811 - Static IP:
10.2.0.35(Unraidbr0) - Registry:
git.alwisp.com/jason/totalmcp - Spec: see
PLAN.mdfor the full architecture and phased roadmap - Service inventory: see
SERVICES.mdfor the catalog this gateway plugs into
Quick Start (local dev)
cp .env.example .env # then fill in tokens
npm install
npm run prisma:generate
npm run dev # starts tsx watch on src/server.ts
Verify:
curl http://localhost:8811/health
# → { "status": "ok", "version": "0.1.0", "plugins": 0, "enabled": [] }
Build & run
npm run build # tsc → dist/
npm start # node dist/server.js
Docker
docker compose up --build -d
docker compose logs -f totalmcp
Endpoints
| Method | Path | Auth | Purpose |
|---|---|---|---|
| GET | /health |
none | Liveness — used by Unraid HEALTHCHECK |
| GET | /plugins |
bearer | Loaded plugin list + tool counts |
| POST | /mcp |
bearer | Streamable HTTP — primary MCP transport |
| GET | /mcp |
bearer | Streamable HTTP server-sent stream |
| DELETE | /mcp |
bearer | Streamable HTTP session close |
| GET | /sse |
bearer | Legacy SSE (Antigravity) |
| POST | /message |
bearer | Legacy SSE message channel |
Authoring a plugin
Drop a directory under src/plugins/<name>/index.ts and export default an MCPPlugin. Tool names must be fully-qualified (<service>_<action>) and unique across all loaded plugins.
import { z } from "zod";
import type { MCPPlugin } from "../../types/plugin.js";
const plugin: MCPPlugin = {
name: "example",
version: "0.1.0",
description: "Example plugin",
minGatewayVersion: "0.1.0",
tools: [
{
name: "example_ping",
description: "Returns pong",
inputSchema: z.object({}),
handler: async () => ({ result: "pong" }),
},
],
};
export default plugin;
Then add example to ENABLED_PLUGINS in .env. In dev (npm run dev), chokidar picks up the change in ~2s. In prod, push to Gitea and let CI rebuild the image.
Reference implementations live at src/plugins/gitea/ and src/plugins/unraid/.
Repo layout
totalmcp/
├── src/
│ ├── server.ts # Express bootstrap
│ ├── registry.ts # Plugin loader + hot-reload
│ ├── config.ts # Zod env validation
│ ├── logger.ts # Structured JSON logger
│ ├── types/plugin.ts # MCPPlugin interface
│ ├── auth/bearer.ts # Per-agent token middleware
│ ├── mcp/build.ts # MCP server wiring
│ ├── transport/
│ │ ├── streamable.ts # Streamable HTTP transport
│ │ └── sse.ts # Legacy SSE transport
│ ├── util/http.ts # Shared HTTP helper (timeouts, JSON, errors)
│ └── plugins/
│ ├── gitea/index.ts # Phase 1 — Gitea REST API
│ └── unraid/index.ts # Phase 1 — Unraid GraphQL API
├── prisma/schema.prisma # Event log schema
├── Dockerfile
├── docker-compose.yml
├── .env.example
└── package.json
Roadmap
See PLAN.md for the full phased plan. Phase 0 (this scaffold) is intentionally minimal — empty registry, both transports respond, Docker image builds, container boots clean.