Files
jason fffa421511
Build and Push Docker Image / build (push) Successful in 31s
build lockfile
2026-05-09 23:10:59 -05:00

35 KiB
Raw Permalink Blame History

Total MCP Gateway — PLAN.md

Project: totalmcp
Host: Unraid ALPHA · 10.2.0.35 (new static IP, next available above codedump @ 10.2.0.34)
Port: 8811
Stack: Node.js 20 + TypeScript + @modelcontextprotocol/sdk + Express 5
Registry: git.alwisp.com/jason/totalmcp
Last Updated: 2026-05-09


Overview

A single Dockerized MCP server that acts as the unified control plane for every AI agent and tool in the Jason ecosystem. Instead of N×M point-to-point integrations, all three agent interfaces — Claude Code, Codex, and Antigravity — connect to one stable endpoint and get access to every backend service through hot-swappable plugin modules.

Two first-class design concerns:

  1. Upgradability — new connectors drop in without touching core infrastructure or restarting the container
  2. Utility — every active service on ALPHA is reachable from any agent from day one

Architecture

┌─────────────────────────────────────────────────────────┐
│           Claude Code / Codex / Antigravity             │
└────────────────────┬────────────────────────────────────┘
                     │  MCP (Streamable HTTP + SSE fallback)
                     ▼
┌─────────────────────────────────────────────────────────┐
│           Total MCP Gateway  :8811                      │
│  ┌──────────────┐  ┌───────────────┐  ┌─────────────┐  │
│  │ Core Server  │  │Plugin Registry│  │ Auth / Rate  │  │
│  │ (TypeScript) │  │ (hot-reload)  │  │  Limiter     │  │
│  └──────────────┘  └───────────────┘  └─────────────┘  │
│                                                         │
│  Active Plugins:                                        │
│  ┌────────┐ ┌────────┐ ┌────────┐ ┌──────────────────┐ │
│  │ gitea  │ │ unraid │ │ docker │ │    openclaw       │ │
│  └────────┘ └────────┘ └────────┘ └──────────────────┘ │
│  ┌────────┐ ┌────────┐ ┌────────┐ ┌──────────────────┐ │
│  │ unifi  │ │  codex │ │stream  │ │   rackmapper      │ │
│  │ access │ │  mrp   │ │ vault  │ │                   │ │
│  └────────┘ └────────┘ └────────┘ └──────────────────┘ │
│                                                         │
│  Deferred Plugins (install first):                      │
│  ┌──────────┐  ┌──────────┐                            │
│  │ chronicle│  │ obsidian │                            │
│  └──────────┘  └──────────┘                            │
└─────────────────────────────────────────────────────────┘
                     │  Unraid br0 · 10.2.0.x
     ┌───────────────┼──────────────────────────┐
     ▼               ▼                          ▼
git.alwisp.com   Unraid host            NOVA (OpenClaw)
 10.2.0.15       10.2.0.2               10.2.0.26:18789

Transport

Both transports are exposed on the same container at :8811:

Transport Endpoint Use For
Streamable HTTP (primary) POST/GET /mcp Claude Code, Codex
SSE (legacy fallback) GET /sse + POST /message Antigravity
Health check GET /health Unraid, monitoring
Plugin registry API GET /plugins Admin, debugging
Admin UI GET /admin Dashboard, plugin management

Note: Use Streamable HTTP for Claude Code — there is a known SSE reconnection regression in Claude Code v2.1.83+. Use SSE for Antigravity until it supports Streamable HTTP natively.


Technology Stack

Layer Choice Reason
Runtime Node.js 20 LTS + TypeScript Matches existing stack
MCP SDK @modelcontextprotocol/sdk Official Anthropic SDK
HTTP Framework Express 5 Familiar, lightweight
Plugin loading Dynamic import() + chokidar Hot-reload without restart
Config .env + Zod config loader Env-var-first, validated at boot
Persistence SQLite via Prisma Consistent with CODEX/Chronicle pattern
Auth Bearer tokens (static, per-agent) Simple, no OAuth needed for LAN
Containerization Single Docker image Unraid-native
Registry git.alwisp.com Gitea Container Registry Auto-build via Gitea Actions
Networking Unraid br0 static IP Consistent LAN addressing

Directory Structure

totalmcp/
├── src/
│   ├── server.ts               # Express + MCP SDK bootstrap
│   ├── registry.ts             # Plugin loader + hot-reload watcher
│   ├── transport/
│   │   ├── streamable.ts       # Streamable HTTP transport
│   │   └── sse.ts              # Legacy SSE transport
│   ├── auth/
│   │   └── bearer.ts           # Per-agent token validation
│   ├── types/
│   │   └── plugin.ts           # MCPPlugin interface contract
│   └── plugins/
│       ├── gitea/
│       │   └── index.ts        # Phase 1
│       ├── unraid/
│       │   └── index.ts        # Phase 1
│       ├── docker/
│       │   └── index.ts        # Phase 2
│       ├── openclaw/
│       │   └── index.ts        # Phase 2
│       ├── unifi/
│       │   └── index.ts        # Phase 3
│       ├── codex-mrp/
│       │   └── index.ts        # Phase 3
│       ├── streamvault/
│       │   └── index.ts        # Phase 3
│       ├── rackmapper/
│       │   └── index.ts        # Phase 3
│       ├── chronicle/          # DEFERRED — install Chronicle first
│       │   └── index.ts
│       └── obsidian/           # DEFERRED — install Obsidian REST bridge first
│           └── index.ts
├── config/
│   └── catalog.yaml            # Enable/disable plugins without code changes
├── prisma/
│   └── schema.prisma           # Event log schema
├── admin/                      # React + Vite admin dashboard
│   ├── src/
│   └── dist/                   # Built, served at /admin
├── Dockerfile
├── docker-compose.yml          # Local dev
├── .env.example
├── AGENTS.md                   # Agent configuration reference
├── README.md
├── PLAN.md                     # This file
└── CHANGELOG.md

Plugin Interface Contract

Every connector implements this interface. The registry auto-discovers and loads any file matching src/plugins/*/index.ts.

// src/types/plugin.ts
export interface MCPPlugin {
  name: string;           // e.g., "gitea" — must be unique
  version: string;        // semver
  description: string;
  minGatewayVersion: string;  // semver — registry skips incompatible plugins
  tools: MCPTool[];
  resources?: MCPResource[];
  prompts?: MCPPrompt[];
  onLoad?: () => Promise<void>;    // init: connect, test auth
  onUnload?: () => Promise<void>;  // cleanup: close connections, timers
}

export interface MCPTool {
  name: string;
  description: string;
  inputSchema: ZodSchema;
  handler: (input: unknown) => Promise<unknown>;
}

Plugin Isolation Rules

  • Each plugin catches its own errors — never throws to core
  • No plugin reads another plugin's state directly
  • Credentials live in env vars, never in plugin source
  • Inter-plugin coordination (if ever needed) goes through the SQLite event log

Adding a New Connector (5-Minute Workflow)

  1. mkdir src/plugins/my-service && touch src/plugins/my-service/index.ts
  2. Implement MCPPlugin interface, export as default
  3. Add required env vars to /mnt/user/appdata/totalmcp/.env
  4. Add my-service to ENABLED_PLUGINS in .env
  5. Hot-reload picks it up in ~2s (dev) OR push to Gitea → CI rebuilds → Unraid force-updates (prod)

Phased Roadmap

Phase 0 — Scaffold & Core Transport

Goal: Container boots, endpoints respond, empty plugin registry loads.
Est: 12 days

  • Init repo at git.alwisp.com/jason/totalmcp
  • npm init, install core deps: @modelcontextprotocol/sdk, express, zod, chokidar, dotenv, prisma
  • Implement src/server.ts — Express bootstrap, mount both transports
  • Implement src/transport/streamable.ts — Streamable HTTP (primary)
  • Implement src/transport/sse.ts — SSE legacy fallback
  • Implement src/registry.ts — dynamic import(), chokidar file watcher, plugin lifecycle
  • Implement src/auth/bearer.ts — per-agent static token validation
  • Write Dockerfile — Node 20 Alpine, non-root user, HEALTHCHECK
  • Write docker-compose.yml — local dev with volume mounts
  • Write .env.example — all required vars documented
  • Write prisma/schema.prisma — event log table
  • Deploy to ALPHA via Unraid Docker GUI
  • Verify: curl http://10.2.0.35:8811/health200 OK
  • Verify: curl http://10.2.0.35:8811/plugins{ "plugins": [] }

Unraid Container Settings:

Setting Value
Container Name totalmcp
Repository git.alwisp.com/jason/totalmcp:latest
Network br0
IP 10.2.0.35 (next available static; gitea-mcp already owns 10.2.0.16:8081 and remains undisturbed during transition)
Port 8811:8811
Appdata /mnt/user/appdata/totalmcp/data → /app/data
Logs /mnt/user/appdata/totalmcp/logs → /app/logs
Docker Socket /var/run/docker.sock → /var/run/docker.sock (RW, required for Phase 2 docker plugin)
CODEX DB /mnt/user/appdata/codex/db.sqlite → /app/codex/db.sqlite (Phase 3 codex-mrp plugin)
Variables Set per-plugin env vars in the Unraid GUI (see INSTALL-UNRAID.md)
Restart Policy unless-stopped
Pids Limit 2048 (via Extra Parameters: --pids-limit=2048)

Full GUI walkthrough: INSTALL-UNRAID.md — every field, env var, and verification step.


Phase 1 — Gitea + Unraid Connectors

Goal: Agents can query repos and Unraid system state.
Est: 12 days
Prerequisite: Phase 0 complete

plugins/gitea/index.ts

  • Auth: GITEA_TOKEN env var → https://git.alwisp.com REST API
  • Tools:
    • gitea_list_repos — list all repos
    • gitea_get_repo — get repo details
    • gitea_create_repo — create a new repo
    • gitea_list_issues — list issues for a repo
    • gitea_create_issue — open a new issue
    • gitea_list_branches — list branches
    • gitea_get_file — read a file's contents
    • gitea_commit_file — create or update a file

plugins/unraid/index.ts

  • Auth: UNRAID_API_KEY + UNRAID_HOST
  • Tools:
    • unraid_host_summary — CPU, RAM, uptime, array status
    • unraid_list_containers — all Docker containers + status
    • unraid_get_container — single container details
    • unraid_list_shares — user shares + usage
    • unraid_list_vms — VM list + state
    • unraid_disk_health — disk S.M.A.R.T. summary

Agent Wiring (after Phase 1)

Claude Code:

claude mcp add --scope user --transport http totalmcp http://10.2.0.35:8811/mcp \
  -H "Authorization: Bearer <CLAUDE_CODE_TOKEN>"

Antigravity (mcp_config.json):

{
  "mcpServers": {
    "totalmcp": {
      "url": "http://10.2.0.35:8811/sse",
      "transport": "sse",
      "headers": { "Authorization": "Bearer <ANTIGRAVITY_TOKEN>" }
    }
  }
}

Codex:

{
  "mcpServers": {
    "totalmcp": {
      "url": "http://10.2.0.35:8811/mcp",
      "transport": "http",
      "headers": { "Authorization": "Bearer <CODEX_TOKEN>" }
    }
  }
}
  • Implement plugins/gitea/index.ts
  • Implement plugins/unraid/index.ts
  • Add both to ENABLED_PLUGINS
  • Connect Claude Code, Codex, Antigravity
  • Verify tool list shows gitea + unraid tools from each agent

Phase 2 — Docker + OpenClaw

Goal: Agents can manage containers and run local AI inference.
Est: 12 days
Prerequisite: Phase 1 complete

plugins/docker/index.ts

  • Auth: Docker socket mount (/var/run/docker.sock)
  • Tools:
    • docker_list_containers — running + stopped containers
    • docker_start_container — start by name or ID
    • docker_stop_container — stop by name or ID
    • docker_restart_container — restart by name or ID
    • docker_get_logs — tail N lines of logs
    • docker_get_stats — CPU + memory stats

plugins/openclaw/index.ts

  • Auth: OPENCLAW_HOST env var → NOVA instance at http://10.2.0.26:18789
  • Tools:
    • openclaw_chat — chat completion via OpenClaw/Ollama
    • openclaw_list_models — list available models
    • openclaw_get_model_info — model details + context size

Note: The OpenClaw-named container in the Unraid Docker tab serves the nova.alwisp.com proxy entry — same image, "NOVA" is the persona name. The donna.alwisp.com instance at 10.2.0.28:18789 is a stopped second container set up for a friend; do not target it.

  • Implement plugins/docker/index.ts
  • Implement plugins/openclaw/index.ts
  • Add both to ENABLED_PLUGINS
  • Verify container management works from Claude Code

Phase 3 — Service Connectors

Goal: Full parity with all active ALPHA services.
Est: 23 days
Prerequisite: Phase 2 complete

plugins/unifi/index.ts

  • Auth: UNIFI_HOST, UNIFI_API_KEY, UNIFI_SITE_ID
  • Tools:
    • unifi_list_access_events — recent badge/door events (V2 API)
    • unifi_list_users — access users with names
    • unifi_get_door_status — current lock state per door
    • unifi_list_sites — managed UniFi sites

plugins/codex-mrp/index.ts

  • Auth: Internal Docker network — direct Prisma client access to CODEX SQLite
  • Tools:
    • codex_list_work_orders — active work orders
    • codex_get_work_order — single WO detail + BOM
    • codex_create_work_order — create new WO
    • codex_get_inventory — inventory levels
    • codex_list_boms — bill of materials list

plugins/streamvault/index.ts

  • Auth: STREAMVAULT_HOST (internal Docker network)
  • Tools:
    • streamvault_list_jobs — all download jobs + status
    • streamvault_add_download — queue a new download URL
    • streamvault_get_job_status — single job detail
    • streamvault_cancel_job — cancel/remove a job

plugins/rackmapper/index.ts

  • Auth: RACKMAPPER_HOST (internal Docker network)

  • Tools:

    • rackmapper_list_racks — all racks
    • rackmapper_get_rack — rack layout with devices
    • rackmapper_list_devices — device inventory
    • rackmapper_map_service — link a device to a service
  • Implement all four Phase 3 plugins

  • Add all to ENABLED_PLUGINS

  • Verify each tool is callable from agents


Phase 4 — Upgradability Infrastructure

Goal: Admin UI, CI/CD pipeline, zero-downtime plugin updates.
Est: 23 days
Prerequisite: Phase 3 complete

  • Plugin Admin API
    • GET /plugins — list all plugins + tool counts, status, version
    • POST /plugins/:name/reload — hot-reload a single plugin
    • POST /plugins/:name/enable — enable a disabled plugin
    • POST /plugins/:name/disable — disable without removing
  • config/catalog.yaml — human-readable enable/disable, rate limits per plugin, per-agent tool allowlists
  • Admin Dashboard (React + Vite, served at /admin)
    • Plugin status cards (loaded / error / disabled)
    • Tool call counts per plugin
    • Last-used timestamps
    • Reload button per plugin
    • Live event log tail
  • Gitea Actions CI Pipeline
    • Trigger: push to main
    • Steps: npm cinpm run builddocker builddocker push git.alwisp.com/jason/totalmcp:latest
    • Unraid force-updates container on next check or manual trigger
  • Semantic versioning for plugin API contracts
    • Plugins declare minGatewayVersion
    • Registry checks compatibility at load time — skips with warning, never crashes
  • Health endpoint per plugin: GET /health/plugins/:name

Phase 5 — Security & Observability

Goal: Production-hardened for daily autonomous agent use.
Est: 12 days
Prerequisite: Phase 4 complete

  • Per-agent bearer tokensAGENT_TOKENS=claude-code:TOKEN1,antigravity:TOKEN2,codex:TOKEN3
  • Rate limiting — per-agent, per-tool, configurable in catalog.yaml
  • Input schema validation — Zod on every tool call; bad inputs return structured 400 errors, never reach handler
  • Structured JSON logging/mnt/user/appdata/totalmcp/logs/
  • SQLite event log — every tool call: agent, tool name, input hash, duration, success/error, timestamp
  • Prometheus metrics endpoint at /metrics (optional — wire to Grafana if desired)
  • Plugin error isolation test — verify one crashing plugin cannot bring down the gateway or other plugins

Phase 6 — Deferred Connectors (Install Prerequisites First)

Goal: Add Chronicle and Obsidian after those services are deployed to ALPHA.
Est: 1 day per connector once services are live
Prerequisite: Chronicle and Obsidian deployed + accessible on br0

Chronicle — Prerequisites

  • Deploy Chronicle container to ALPHA (see Chronicle build docs)
  • Verify http://chronicle:3003/health responds from within the Docker network
  • Obtain Chronicle bearer token

plugins/chronicle/index.ts

  • Auth: CHRONICLE_HOST, CHRONICLE_TOKEN
  • Tools:
    • memory_store — persist a memory entry
    • memory_recall — retrieve by key or semantic query
    • memory_search — full-text + semantic search across entries
    • memory_list_projects — browse memories by project scope
    • memory_delete — remove a specific entry

Obsidian — Prerequisites

  • Install Obsidian Local REST API plugin in the Obsidian container on ALPHA
  • Generate API key from Obsidian plugin settings
  • Verify http://obsidian:27123 is reachable from Docker network

plugins/obsidian/index.ts

  • Auth: OBSIDIAN_REST_HOST, OBSIDIAN_API_KEY
  • Tools:
    • obsidian_note_create — create a new note at a given path
    • obsidian_note_read — read note contents by path
    • obsidian_note_update — overwrite note contents
    • obsidian_note_append — append text to a note
    • obsidian_note_search — search notes by query
    • obsidian_list_vault — list notes in a folder

To activate either deferred connector once ready:

  1. Prerequisites above are complete
  2. touch src/plugins/chronicle/index.ts (or obsidian) and implement
  3. Add env vars to /mnt/user/appdata/totalmcp/.env
  4. Add to ENABLED_PLUGINS
  5. Push to Gitea → CI rebuilds → Unraid force-updates

Phase 7 — Infrastructure & Media Connectors

Goal: Cover the remaining third-party services on ALPHA so agents can manage networking, file sync, downloads, and media. Est: 23 days Prerequisite: Phase 5 complete (security hardening landed before broadening surface area)

plugins/npm/index.ts — Nginx Proxy Manager

  • Auth: NPM_HOST=http://10.2.0.3:81, NPM_EMAIL, NPM_PASSWORD (NPM token flow)
  • Tools:
    • npm_list_proxy_hosts — all configured proxy entries
    • npm_get_proxy_host — single entry detail
    • npm_create_proxy_host — new hostname → backend mapping
    • npm_update_proxy_host — change backend / SSL settings
    • npm_delete_proxy_host — remove entry
    • npm_renew_cert — force Let's Encrypt renewal
    • npm_list_certs — cert status across all hosts

plugins/uisp/index.ts — Ubiquiti UISP

  • Auth: UISP_HOST=https://10.2.0.4:443, UISP_TOKEN
  • Tools:
    • uisp_list_devices — all UISP-line devices
    • uisp_get_device_status — per-device state, CPU, RAM
    • uisp_list_sites — managed sites
    • uisp_get_link_quality — radio link RSSI / capacity / errors
    • uisp_list_outages — current device outages

plugins/transmission/index.ts — NEBULA

  • Auth: TRANSMISSION_HOST=http://10.2.0.5:9091, TRANSMISSION_USER, TRANSMISSION_PASS
  • Tools:
    • transmission_list_torrents — all torrents + status
    • transmission_add_torrent — magnet or URL
    • transmission_pause_torrent / transmission_resume_torrent
    • transmission_remove_torrent — with optional data deletion
    • transmission_get_stats — global up/down rates, totals

plugins/syncthing/index.ts

  • Auth: SYNCTHING_HOST=http://10.2.0.2:8384, SYNCTHING_API_KEY
  • Tools:
    • syncthing_list_folders — all configured folders + sync %
    • syncthing_folder_status — single folder detail
    • syncthing_list_devices — paired devices + connection state
    • syncthing_pause_folder / syncthing_resume_folder
    • syncthing_rescan — force a folder rescan

plugins/plex/index.ts

  • Auth: PLEX_HOST=http://10.2.0.2:32400, PLEX_TOKEN
  • Tools:
    • plex_list_libraries — all libraries (movies, TV, music)
    • plex_search_library — search across libraries
    • plex_recently_added — N most recently added items
    • plex_now_playing — active sessions
    • plex_server_status — version, transcoder activity, plugins

plugins/nyaa/index.ts

  • Auth: NYAA_HOST=http://10.2.0.21:<port>, NYAA_API_KEY (if applicable)

  • Tools:

    • nyaa_list_watches — active search/watch rules
    • nyaa_add_watch — new auto-download rule
    • nyaa_remove_watch — delete a rule
    • nyaa_get_recent_matches — last N triggered downloads
    • nyaa_force_check — run watch loop immediately
  • Implement all six Phase 7 plugins

  • Add to ENABLED_PLUGINS

  • Confirm each tool works from Claude Code


Phase 8 — Smart Home

Goal: Bring Home Assistant under MCP control so agents can read sensor state and trigger automations. Est: 12 days Prerequisite: Phase 7 complete

plugins/home-assistant/index.ts

  • Auth: HA_HOST=https://10.2.0.12:8123, HA_TOKEN (long-lived access token from HA UI)
  • Tools:
    • ha_list_entities — all entities + current state
    • ha_get_state — single entity state + attributes
    • ha_call_service — invoke any HA service (light.turn_on, automation.trigger, etc.)
    • ha_list_automations — all configured automations
    • ha_trigger_automation — fire an automation by entity ID
    • ha_get_history — historical state changes for an entity

Note: matter-server (raw Matter protocol bridge on ALPHA) is intentionally not wrapped as a separate plugin — Home Assistant already abstracts Matter, Zigbee, Z-Wave, and other ecosystems behind one API. Add a dedicated matter plugin only if a use case emerges that HA cannot serve.

  • Implement plugins/home-assistant/index.ts
  • Add to ENABLED_PLUGINS
  • Verify entity state + service calls work from Claude Code

Phase 9 — Business Operations

Goal: Connect remaining business apps so agents can support shop floor, HR, and finance workflows. Est: 23 days Prerequisite: Phase 8 complete

plugins/invoiceninja/index.ts

  • Auth: INVOICENINJA_HOST=http://10.2.0.2:8000, INVOICENINJA_TOKEN
  • Tools:
    • invoiceninja_list_invoices — filter by status, client, date range
    • invoiceninja_get_invoice — single invoice detail
    • invoiceninja_create_invoice — new invoice from line items
    • invoiceninja_send_invoice — email an invoice
    • invoiceninja_list_clients — client directory
    • invoiceninja_get_payment_status — payment state for invoice

plugins/fabdash/index.ts

  • Auth: FABDASH_HOST=http://10.2.0.13:8080, FABDASH_TOKEN
  • Tools:
    • fabdash_get_today_schedule — today's shop schedule
    • fabdash_list_active_jobs — jobs in flight
    • fabdash_machine_status — utilization per machine
    • fabdash_create_job — new shop job
    • fabdash_update_job — change status / assignment

plugins/cpas/index.ts

  • Auth: CPAS_HOST=http://10.2.0.14:3001, CPAS_TOKEN
  • Tools:
    • cpas_list_violations — recent infractions
    • cpas_get_employee_score — running point total
    • cpas_log_violation — record new infraction
    • cpas_list_at_risk_employees — employees at/near escalation thresholds

plugins/wfh/index.ts

  • Auth: WFH_HOST=http://10.2.0.18:3000, WFH_TOKEN

  • Tools:

    • wfh_list_tasks — task log per employee or team
    • wfh_get_employee_log — single employee daily log
    • wfh_submit_form — file required HR/timesheet form
    • wfh_list_pending_submissions — overdue forms
  • Implement all four Phase 9 plugins

  • Add to ENABLED_PLUGINS


Phase 10 — Personal & Niche Apps

Goal: Round out the catalog with personal and lower-priority custom apps. Est: 23 days Prerequisite: Phase 9 complete

plugins/breedr/index.ts

  • Auth: BREEDR_HOST=http://10.2.0.17:<port>, BREEDR_TOKEN
  • Tools: breedr_list_dogs, breedr_get_pedigree, breedr_list_upcoming_litters, breedr_log_whelp_event

plugins/codedump/index.ts

  • Auth: CODEDUMP_HOST=http://10.2.0.34:<port>, CODEDUMP_TOKEN
  • Tools: codedump_list_projects, codedump_get_project, codedump_update_completion, codedump_add_project

plugins/ui-tracker/index.ts

  • Auth: UITRACKER_HOST=http://10.2.0.29:<port>, UITRACKER_TOKEN
  • Tools: uitracker_list_watched, uitracker_add_watch, uitracker_remove_watch, uitracker_get_alert_history

plugins/stepview/index.ts

  • Auth: STEPVIEW_HOST=http://10.2.0.33:3000, STEPVIEW_TOKEN
  • Tools: stepview_list_models, stepview_upload_model, stepview_create_share_link, stepview_revoke_share

plugins/qrknit/index.ts

  • Auth: QRKNIT_HOST=http://10.2.0.9:5000, QRKNIT_TOKEN
  • Tools: qrknit_create_link, qrknit_get_analytics, qrknit_list_links, qrknit_generate_qr

plugins/memer/index.ts

  • Auth: MEMER_HOST=http://10.2.0.30:3000, MEMER_TOKEN
  • Tools: memer_search, memer_upload, memer_get_random, memer_list_tags

plugins/alwisp-web/index.ts

  • Auth: ALWISP_WEB_HOST=http://10.2.0.8:80, ALWISP_WEB_TOKEN

  • Tools: alwisp_publish_page, alwisp_update_page, alwisp_list_pages

  • Implement plugins for the apps that have stable APIs

  • Defer any app that lacks a usable API until one is added

  • Add to ENABLED_PLUGINS selectively — not all of these need to be on by default


Phase 11 — Future / Conditional

Goal: Track candidates that should not be built yet.

Plugin Condition to start
mrp-qrcode Once it stabilizes and its scope clearly differs from codex-mrp in production use
inven Once development converges (currently the third in-flight MRP design)
matter (raw) Only if a use case appears that HA cannot serve
email-sigs Only if an API is added (currently UI-only)
n8n Only if usage materially picks up (currently stopped)

Skipped (per service triage)

  • adminer — UI only, no programmatic API needed
  • MariaDB, postgresql16, Redis — services own their own DBs; no direct DB plugin
  • Gitea-Runner — managed via the Gitea plugin
  • bx (client UISP) — third-party trust boundary; do not target

Environment Variables

Full reference for /mnt/user/appdata/totalmcp/.env:

# ─── Core ───────────────────────────────────────────────
NODE_ENV=production
PORT=8811
LOG_LEVEL=info
GATEWAY_VERSION=1.0.0

# Comma-separated list of active plugin names
# Phase 13 (core)
ENABLED_PLUGINS=gitea,unraid,docker,openclaw,unifi,codex-mrp,streamvault,rackmapper
# Add Phase 7+ as plugins land:
#   ,npm,uisp,transmission,syncthing,plex,nyaa             # Phase 7 (infra/media)
#   ,home-assistant                                         # Phase 8 (smart home)
#   ,invoiceninja,fabdash,cpas,wfh                          # Phase 9 (business ops)
#   ,breedr,codedump,ui-tracker,stepview,qrknit,memer,alwisp-web  # Phase 10 (personal/niche)

# ─── Auth ───────────────────────────────────────────────
# Format: agentName:token,agentName2:token2
AGENT_TOKENS=claude-code:TOKEN1,antigravity:TOKEN2,codex:TOKEN3

# ─── Gitea ──────────────────────────────────────────────
GITEA_HOST=https://git.alwisp.com
GITEA_TOKEN=<pat>

# ─── Unraid ─────────────────────────────────────────────
UNRAID_HOST=http://10.2.0.2
UNRAID_API_KEY=<key>

# ─── OpenClaw / NOVA ────────────────────────────────────
OPENCLAW_HOST=http://10.2.0.26:18789

# ─── UniFi Access ───────────────────────────────────────
UNIFI_HOST=https://<controller-ip>
UNIFI_API_KEY=<key>
UNIFI_SITE_ID=<id>

# ─── CODEX MRP ──────────────────────────────────────────
CODEX_DB_PATH=/mnt/user/appdata/codex/db.sqlite

# ─── StreamVault ────────────────────────────────────────
STREAMVAULT_HOST=http://streamvault:3100

# ─── RackMapper ─────────────────────────────────────────
RACKMAPPER_HOST=http://rackmapper:3200

# ─── Chronicle (DEFERRED) ───────────────────────────────
# CHRONICLE_HOST=http://chronicle:3003
# CHRONICLE_TOKEN=<bearer>

# ─── Obsidian (DEFERRED) ────────────────────────────────
# OBSIDIAN_REST_HOST=http://obsidian:27123
# OBSIDIAN_API_KEY=<key>

# ─── Phase 7: Infrastructure & Media ────────────────────
NPM_HOST=http://10.2.0.3:81
NPM_EMAIL=<email>
NPM_PASSWORD=<password>

UISP_HOST=https://10.2.0.4:443
UISP_TOKEN=<token>

TRANSMISSION_HOST=http://10.2.0.5:9091
TRANSMISSION_USER=<user>
TRANSMISSION_PASS=<password>

SYNCTHING_HOST=http://10.2.0.2:8384
SYNCTHING_API_KEY=<key>

PLEX_HOST=http://10.2.0.2:32400
PLEX_TOKEN=<token>

NYAA_HOST=http://10.2.0.21
NYAA_API_KEY=<key>

# ─── Phase 8: Smart Home ────────────────────────────────
HA_HOST=https://10.2.0.12:8123
HA_TOKEN=<long-lived-access-token>

# ─── Phase 9: Business Operations ───────────────────────
INVOICENINJA_HOST=http://10.2.0.2:8000
INVOICENINJA_TOKEN=<token>

FABDASH_HOST=http://10.2.0.13:8080
FABDASH_TOKEN=<token>

CPAS_HOST=http://10.2.0.14:3001
CPAS_TOKEN=<token>

WFH_HOST=http://10.2.0.18:3000
WFH_TOKEN=<token>

# ─── Phase 10: Personal & Niche ─────────────────────────
BREEDR_HOST=http://10.2.0.17
BREEDR_TOKEN=<token>

CODEDUMP_HOST=http://10.2.0.34
CODEDUMP_TOKEN=<token>

UITRACKER_HOST=http://10.2.0.29
UITRACKER_TOKEN=<token>

STEPVIEW_HOST=http://10.2.0.33:3000
STEPVIEW_TOKEN=<token>

QRKNIT_HOST=http://10.2.0.9:5000
QRKNIT_TOKEN=<token>

MEMER_HOST=http://10.2.0.30:3000
MEMER_TOKEN=<token>

ALWISP_WEB_HOST=http://10.2.0.8:80
ALWISP_WEB_TOKEN=<token>

Dockerfile

FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json tsconfig.json ./
RUN npm ci
COPY src/ ./src/
COPY prisma/ ./prisma/
RUN npx prisma generate
RUN npm run build

FROM node:20-alpine
RUN addgroup -S mcp && adduser -S mcp -G mcp
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/prisma ./prisma
COPY package.json ./
USER mcp
EXPOSE 8811
HEALTHCHECK --interval=30s --timeout=10s --start-period=15s \
  CMD wget -qO- http://localhost:8811/health || exit 1
CMD ["node", "dist/server.js"]

Risk Register

Risk Mitigation
Docker socket exposure Plugin scoped; consider read-only mount once stable
Plugin crash brings down gateway All plugin calls wrapped in try/catch; registry isolates failures
Claude Code SSE reconnect bug (v2.1.83+) Use Streamable HTTP for Claude Code; SSE only for Antigravity
Gitea CI build fails silently Add Gitea issue notification step to Actions workflow
Hot-reload duplicate tool registration Registry calls onUnload() and removes old entry before re-import
br0 Unraid host routing quirk All clients connect from workstations, not from the Unraid host itself
Deferred plugins partially configured Chronicle and Obsidian env vars commented out until services are live

Files to Generate Next

File Purpose Priority
AGENTS.md Multi-agent config reference, full tool catalog, env var docs High
README.md Overview, quick-start, Unraid deployment steps High
src/server.ts Core server bootstrap Phase 0
src/registry.ts Plugin loader + hot-reload Phase 0
src/types/plugin.ts MCPPlugin interface Phase 0
src/transport/streamable.ts Streamable HTTP transport Phase 0
src/transport/sse.ts SSE legacy transport Phase 0
src/auth/bearer.ts Per-agent token validation Phase 0
Dockerfile Production image Phase 0
docker-compose.yml Local dev stack Phase 0
src/plugins/gitea/index.ts Gitea connector Phase 1
src/plugins/unraid/index.ts Unraid API connector Phase 1
src/plugins/docker/index.ts Docker socket connector Phase 2
src/plugins/openclaw/index.ts OpenClaw/Ollama connector Phase 2
config/catalog.yaml Plugin registry config Phase 4
.gitea/workflows/build.yml Gitea Actions CI pipeline Phase 4

Upgrade Policy

  • Patch (1.0.x): Bug fixes, no plugin API changes — always safe to update
  • Minor (1.x.0): New gateway capabilities, backward-compatible plugin API additions
  • Major (x.0.0): Breaking plugin API changes — dual-support period, migration guide in CHANGELOG.md
  • Plugins declare minGatewayVersion — incompatible plugins are skipped with a warning log, never crash the server
  • CHANGELOG.md is updated with every release