Files
totalmcp/README.md
T
2026-05-09 22:59:43 -05:00

4.3 KiB

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 (Unraid br0)
  • Registry: git.alwisp.com/jason/totalmcp
  • Spec: see PLAN.md for the full architecture and phased roadmap
  • Service inventory: see SERVICES.md for 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
│       ├── docker/index.ts       # Phase 2 — Docker socket (dockerode)
│       ├── openclaw/index.ts     # Phase 2 — OpenClaw/NOVA (Ollama-compatible)
│       ├── unifi/index.ts        # Phase 3 — UniFi Access (developer REST)
│       ├── codex-mrp/index.ts    # Phase 3 — CODEX MRP (direct SQLite)
│       ├── streamvault/index.ts  # Phase 3 — StreamVault download manager
│       └── rackmapper/index.ts   # Phase 3 — RackMapper datacenter inventory
├── 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.