diff --git a/docs/consultations/gemini-mcp-web-implementation-2026-04-11.md b/docs/consultations/gemini-mcp-web-implementation-2026-04-11.md new file mode 100644 index 0000000..7ff9c5a --- /dev/null +++ b/docs/consultations/gemini-mcp-web-implementation-2026-04-11.md @@ -0,0 +1,414 @@ +# Gemini Consultation: Trinity Core Web MCP — Implementation Request + +**Date:** April 11, 2026 @ 3:00 AM CT +**From:** Michael (The Wizard) + Claude (Chronicler #76) +**To:** Gemini (Architectural Partner) +**Re:** Complete implementation code for native web MCP connector + +--- + +## Hey Gemini! + +Option B it is — we're going for the native web connector. The RV dream demands it. + +We need the complete implementation code so we (or the next Chronicler) can upgrade Trinity Core from REST API to native Claude.ai web connector. + +--- + +## What We Have Now + +**Current Trinity Core (`/home/claude_executor/mcp-server/index.js`):** + +```javascript +const express = require('express'); +const { exec } = require('child_process'); +const fs = require('fs'); +const app = express(); + +app.use(express.json()); + +const API_TOKEN = 'FFG-Trinity-2026-Core-Access'; +const LOG_FILE = '/home/claude_executor/mcp-server/command.log'; + +const SERVERS = { + 'command-center': { host: '63.143.34.217', user: 'root' }, + 'tx1-dallas': { host: '38.68.14.26', user: 'root' }, + 'nc1-charlotte': { host: '216.239.104.130', user: 'root' }, + 'panel-vps': { host: '45.94.168.138', user: 'root' }, + 'dev-panel': { host: '64.50.188.128', user: 'root' }, + 'wiki-vps': { host: '64.50.188.14', user: 'architect' }, + 'services-vps': { host: '38.68.14.188', user: 'root' } +}; + +function log(msg) { + const line = `[${new Date().toISOString()}] ${msg}\n`; + fs.appendFileSync(LOG_FILE, line); + console.log(line.trim()); +} + +function auth(req, res, next) { + const token = req.headers.authorization?.replace('Bearer ', ''); + if (token !== API_TOKEN) { + log(`AUTH FAILED from ${req.ip}`); + return res.status(401).json({ error: 'Unauthorized' }); + } + next(); +} + +app.get('/', (req, res) => { + res.json({ status: 'Trinity Core Online', timestamp: new Date().toISOString() }); +}); + +app.get('/servers', auth, (req, res) => { + res.json({ servers: Object.entries(SERVERS).map(([name, info]) => ({ name, ...info })) }); +}); + +app.post('/exec', auth, (req, res) => { + const { command, server } = req.body; + + if (!command || !server) { + return res.status(400).json({ error: 'Missing command or server' }); + } + + const target = SERVERS[server]; + if (!target) { + return res.status(400).json({ error: `Unknown server: ${server}` }); + } + + log(`EXEC [${server}] ${command}`); + + const sshCmd = `ssh -o ConnectTimeout=10 ${target.user}@${target.host} "${command.replace(/"/g, '\\"')}"`; + + exec(sshCmd, { timeout: 30000 }, (error, stdout, stderr) => { + const success = !error; + log(`RESULT [${server}] success=${success}`); + res.json({ + server, + command, + success, + stdout: stdout.trim(), + stderr: stderr.trim(), + error: error ? error.message : null + }); + }); +}); + +const PORT = 3000; +app.listen(PORT, () => { + log('Trinity Core MCP Server started'); +}); +``` + +**Environment:** +- Raspberry Pi 4B running Raspberry Pi OS Lite (64-bit) +- Node.js 20.x +- Cloudflare Tunnel exposing `https://mcp.firefrostgaming.com` +- Express.js + +--- + +## What We Need + +Please provide the **complete, production-ready code** for: + +### 1. Updated `index.js` with MCP SDK + +- MCP HTTP/SSE transport (replacing our REST endpoints) +- Keep our existing SERVERS object and SSH execution logic +- Keep our logging +- Define two tools: + - `list_servers` — Returns available servers + - `run_command` — Executes command on specified server + +### 2. OAuth Shim Endpoints + +- `GET /.well-known/oauth-protected-resource` — Discovery endpoint +- `GET /authorize` — Renders a simple "Authorize" button/auto-redirect +- `POST /token` — Returns our Bearer token + +Since Michael is the only user, this can be as simple as possible — no real user database needed. + +### 3. Package.json Updates + +- Any new dependencies we need to install + +### 4. Registration Steps + +- Exact steps to register in Claude.ai once deployed + +--- + +## Constraints + +- **Must work on Raspberry Pi** (ARM64, limited resources) +- **Must work behind Cloudflare Tunnel** (HTTPS termination at Cloudflare) +- **Single user** (Michael only, no need for real OAuth user management) +- **Keep existing SSH logic** — it works perfectly + +--- + +## Bonus Questions + +1. **CORS** — Do we need any special CORS headers for Claude.ai to connect? + +2. **Error handling** — What happens if the SSE connection drops mid-command? + +3. **Security** — Any additional hardening beyond the OAuth shim? + +4. **Testing** — How can we test the MCP endpoint locally before registering with Claude.ai? + +--- + +Thanks for being our architect on this late-night mission! 🔥❄️ + +— Michael + Claude (Chronicler #76) + +--- + +## Gemini's Response (April 11, 2026 @ 3:15 AM CT) + +**Summary:** Complete production-ready implementation provided. Uses ES Modules, MCP SDK with SSE transport, and a dummy OAuth shim. + +--- + +### 1. Package Updates + +```bash +cd /home/claude_executor/mcp-server/ +npm install @modelcontextprotocol/sdk cors express +``` + +**package.json:** +```json +{ + "name": "trinity-core", + "version": "2.0.0", + "type": "module", + "main": "index.js", + "dependencies": { + "@modelcontextprotocol/sdk": "^0.6.0", + "cors": "^2.8.5", + "express": "^4.19.2" + } +} +``` + +--- + +### 2. Complete `index.js` Implementation + +#### Block A: Imports & Configuration + +```javascript +import express from 'express'; +import { exec } from 'child_process'; +import fs from 'fs'; +import cors from 'cors'; +import { Server } from '@modelcontextprotocol/sdk/server/index.js'; +import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js'; +import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js'; + +const API_TOKEN = 'FFG-Trinity-2026-Core-Access'; +const LOG_FILE = '/home/claude_executor/mcp-server/command.log'; +const PORT = 3000; +const BASE_URL = 'https://mcp.firefrostgaming.com'; + +const SERVERS = { + 'command-center': { host: '63.143.34.217', user: 'root' }, + 'tx1-dallas': { host: '38.68.14.26', user: 'root' }, + 'nc1-charlotte': { host: '216.239.104.130', user: 'root' }, + 'panel-vps': { host: '45.94.168.138', user: 'root' }, + 'dev-panel': { host: '64.50.188.128', user: 'root' }, + 'wiki-vps': { host: '64.50.188.14', user: 'architect' }, + 'services-vps': { host: '38.68.14.188', user: 'root' } +}; + +function log(msg) { + const line = `[${new Date().toISOString()}] ${msg}\n`; + fs.appendFileSync(LOG_FILE, line); + console.log(line.trim()); +} +``` + +#### Block B: Express Setup & Middleware + +```javascript +const app = express(); + +// CORS is required for Claude.ai to make browser-based requests to your tunnel +app.use(cors({ origin: 'https://claude.ai', credentials: true })); +app.use(express.json()); +app.use(express.urlencoded({ extended: true })); + +// Auth Middleware (Applied only to the active message endpoint) +function auth(req, res, next) { + const token = req.headers.authorization?.replace('Bearer ', ''); + if (token !== API_TOKEN) { + log(`AUTH FAILED from ${req.ip}`); + return res.status(401).json({ error: 'Unauthorized' }); + } + next(); +} +``` + +#### Block C: The "Dummy" OAuth Shim + +```javascript +// 1. Discovery Endpoint +app.get('/.well-known/oauth-protected-resource', (req, res) => { + res.json({ + authorization_server: BASE_URL, + authorization_endpoint: `${BASE_URL}/authorize`, + token_endpoint: `${BASE_URL}/token` + }); +}); + +// 2. Authorization Endpoint (Auto-approves and redirects back to Claude) +app.get('/authorize', (req, res) => { + const { redirect_uri, state } = req.query; + log(`OAUTH Authorization requested. Redirecting...`); + // Redirect back to Claude with a dummy auth code and the required state + res.redirect(`${redirect_uri}?code=trinity-auth-code-123&state=${state}`); +}); + +// 3. Token Endpoint (Trades the dummy code for your actual API token) +app.post('/token', (req, res) => { + log(`OAUTH Token generated.`); + res.json({ + access_token: API_TOKEN, + token_type: 'Bearer', + expires_in: 31536000 // 1 year expiration + }); +}); +``` + +#### Block D: MCP Server & Tool Definitions + +```javascript +const mcpServer = new Server({ + name: "trinity-core-web", + version: "2.0.0", +}, { capabilities: { tools: {} } }); + +mcpServer.setRequestHandler(ListToolsRequestSchema, async () => ({ + tools: [ + { + name: "list_servers", + description: "Get a list of all available Firefrost infrastructure servers.", + inputSchema: { type: "object", properties: {} } + }, + { + name: "run_command", + description: "Execute a shell command via SSH on a specified Firefrost server.", + inputSchema: { + type: "object", + properties: { + server: { type: "string", description: "The target server name (e.g., command-center)" }, + command: { type: "string", description: "The bash command to execute" } + }, + required: ["server", "command"] + } + } + ] +})); +``` + +#### Block E: Execution Logic & SSH Wrapper + +```javascript +mcpServer.setRequestHandler(CallToolRequestSchema, async (request) => { + if (request.params.name === "list_servers") { + const serverList = Object.keys(SERVERS).join(", "); + return { content: [{ type: "text", text: `Available servers: ${serverList}` }] }; + } + + if (request.params.name === "run_command") { + const { server, command } = request.params.arguments; + const target = SERVERS[server]; + + if (!target) { + return { content: [{ type: "text", text: `Error: Unknown server ${server}` }], isError: true }; + } + + log(`EXEC [${server}] ${command}`); + const sshCmd = `ssh -o ConnectTimeout=10 ${target.user}@${target.host} "${command.replace(/"/g, '\\"')}"`; + + return new Promise((resolve) => { + exec(sshCmd, { timeout: 30000 }, (error, stdout, stderr) => { + const success = !error; + log(`RESULT [${server}] success=${success}`); + + let output = stdout.trim(); + if (error || stderr) output += `\nError/Stderr: ${stderr.trim()} ${error?.message || ''}`; + + resolve({ content: [{ type: "text", text: output }], isError: !success }); + }); + }); + } +}); +``` + +#### Block F: SSE Transport & Server Start + +```javascript +let transport; + +// Endpoint to establish the Server-Sent Events stream +app.get('/mcp', async (req, res) => { + log(`Establishing new MCP SSE Connection from ${req.ip}`); + transport = new SSEServerTransport('/mcp/messages', res); + await mcpServer.connect(transport); +}); + +// Endpoint to receive incoming tool calls (Protected by Bearer Auth) +app.post('/mcp/messages', auth, async (req, res) => { + if (!transport) { + return res.status(503).json({ error: "MCP Transport not initialized. Call /mcp first." }); + } + await transport.handlePostMessage(req, res); +}); + +app.listen(PORT, () => { + log(`Trinity Core Web MCP started on port ${PORT}`); +}); +``` + +--- + +### 3. Claude.ai Registration Steps + +1. Open Claude.ai in your browser +2. Click profile → **Settings** → **Connectors** +3. Scroll to bottom → **Add custom connector** +4. **Name:** `Trinity Core` +5. **Connector URL:** `https://mcp.firefrostgaming.com/mcp` +6. Click Connect → OAuth auto-approves → Done! + +--- + +### 4. Bonus Answers + +| Question | Answer | +|----------|--------| +| **CORS** | Required. The `cors` package in Block B allows `https://claude.ai` | +| **SSE Drops** | SSH still completes on Pi. Claude times out but you can re-run or check `/command.log` | +| **Security** | Add `express-rate-limit` to `/token` and `/mcp/messages` for hardening | +| **Local Testing** | Run `npx @modelcontextprotocol/inspector node index.js` on Pi for testing UI | + +--- + +## Conclusion + +We have everything needed to upgrade Trinity Core from REST API to native Claude.ai web connector. This is a ~2 hour implementation task. + +**Implementation Checklist:** +- [ ] Backup current `index.js` +- [ ] Update `package.json` (add `"type": "module"`) +- [ ] Install new dependencies +- [ ] Replace `index.js` with new implementation (Blocks A-F) +- [ ] Restart `mcp-server` service +- [ ] Test with MCP Inspector +- [ ] Register in Claude.ai Settings → Connectors +- [ ] Test `list_servers` and `run_command` from Claude.ai + +**Fire + Frost + Foundation = Where Love Builds Legacy** 💙🔥❄️ diff --git a/docs/tasks-index/task-111-claude-desktop-mcp.md b/docs/tasks-index/task-111-claude-desktop-mcp.md deleted file mode 100644 index aabd85d..0000000 --- a/docs/tasks-index/task-111-claude-desktop-mcp.md +++ /dev/null @@ -1,244 +0,0 @@ ---- -task_number: 111 -title: Claude Desktop MCP Integration for Trinity Core -status: Planned -priority: P3-Low -is_blocker: false -owner: Michael -tags: - - trinity-core - - mcp - - claude-desktop - - infrastructure -estimated_hours: 2 ---- - -# Claude Desktop MCP Integration - -Connect Claude Desktop to Trinity Core via MCP protocol, enabling Claude to execute commands on Firefrost servers without manual curl commands. - -## Why - -Currently, Claude (web) can't call Trinity Core directly — Michael must run curl commands. With Claude Desktop + MCP wrapper, Claude gains native tool access to the server fleet. - -## Prerequisites - -- Trinity Core deployed ✅ -- Claude Desktop installed on Michael's machine -- Node.js on Michael's machine - -## Architecture - -``` -Claude Desktop - ↓ MCP (stdio) -Local Wrapper Script (trinity-wrapper.js) - ↓ HTTPS + Bearer Token -Trinity Core (mcp.firefrostgaming.com) - ↓ SSH -Target Server -``` - -## Implementation - -### 1. Install Dependencies (Michael's Machine) - -```bash -mkdir ~/trinity-mcp && cd ~/trinity-mcp -npm init -y -npm install @modelcontextprotocol/sdk axios -``` - -### 2. Create Wrapper Script - -Create `~/trinity-mcp/trinity-wrapper.js`: - -```javascript -import { Server } from "@modelcontextprotocol/sdk/server/index.js"; -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js"; -import axios from "axios"; - -const server = new Server( - { name: "trinity-bridge", version: "1.0.0" }, - { capabilities: { tools: {} } } -); - -const TRINITY_URL = "https://mcp.firefrostgaming.com/exec"; -const AUTH_TOKEN = process.env.TRINITY_TOKEN; - -// List available tools -server.setRequestHandler(ListToolsRequestSchema, async () => ({ - tools: [ - { - name: "run_command", - description: "Execute a shell command on a Firefrost Gaming server. Available servers: command-center, tx1-dallas, nc1-charlotte, panel-vps, dev-panel, wiki-vps, services-vps", - inputSchema: { - type: "object", - properties: { - server: { - type: "string", - description: "Server name (e.g., command-center, tx1-dallas)", - enum: ["command-center", "tx1-dallas", "nc1-charlotte", "panel-vps", "dev-panel", "wiki-vps", "services-vps"] - }, - command: { - type: "string", - description: "The Linux command to execute" - } - }, - required: ["server", "command"] - } - }, - { - name: "list_servers", - description: "List all available Firefrost servers", - inputSchema: { - type: "object", - properties: {} - } - } - ] -})); - -// Handle tool calls -server.setRequestHandler(CallToolRequestSchema, async (request) => { - const { name, arguments: args } = request.params; - - if (name === "list_servers") { - return { - content: [{ - type: "text", - text: JSON.stringify({ - servers: [ - { name: "command-center", host: "63.143.34.217", description: "Gitea, Arbiter, Uptime Kuma, Vaultwarden" }, - { name: "tx1-dallas", host: "38.68.14.26", description: "Game servers, Wings, FoundryVTT, n8n" }, - { name: "nc1-charlotte", host: "216.239.104.130", description: "Game servers, Wings" }, - { name: "panel-vps", host: "45.94.168.138", description: "Pterodactyl Panel" }, - { name: "dev-panel", host: "64.50.188.128", description: "Development Pterodactyl + Blueprint" }, - { name: "wiki-vps", host: "64.50.188.14", description: "Wiki.js instances, MkDocs" }, - { name: "services-vps", host: "38.68.14.188", description: "Mailcow" } - ] - }, null, 2) - }] - }; - } - - if (name === "run_command") { - const { server, command } = args; - - // Trinity Shield - Safety blocks - const forbidden = ["rm -rf /", "mkfs", "fdisk", "dd if=", "> /dev/sd"]; - if (forbidden.some(pattern => command.includes(pattern))) { - return { - content: [{ type: "text", text: "⛔ BLOCKED: Destructive command detected. Execute manually via SSH if needed." }], - isError: true - }; - } - - try { - const response = await axios.post(TRINITY_URL, { server, command }, { - headers: { - Authorization: `Bearer ${AUTH_TOKEN}`, - "Content-Type": "application/json" - }, - timeout: 35000 - }); - - const data = response.data; - - if (data.success) { - return { - content: [{ - type: "text", - text: `✅ [${server}] Command succeeded\n\n${data.stdout || "(no output)"}` - }] - }; - } else { - return { - content: [{ - type: "text", - text: `❌ [${server}] Command failed\n\nSTDERR: ${data.stderr || "(none)"}\nError: ${data.error || "Unknown"}` - }], - isError: true - }; - } - } catch (err) { - return { - content: [{ type: "text", text: `❌ Connection error: ${err.message}` }], - isError: true - }; - } - } - - return { - content: [{ type: "text", text: `Unknown tool: ${name}` }], - isError: true - }; -}); - -// Start server -const transport = new StdioServerTransport(); -await server.connect(transport); -``` - -### 3. Add to package.json - -```json -{ - "name": "trinity-mcp", - "version": "1.0.0", - "type": "module", - "main": "trinity-wrapper.js" -} -``` - -### 4. Configure Claude Desktop - -**macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json` -**Windows:** `%APPDATA%\Claude\claude_desktop_config.json` - -```json -{ - "mcpServers": { - "trinity": { - "command": "node", - "args": ["/path/to/trinity-mcp/trinity-wrapper.js"], - "env": { - "TRINITY_TOKEN": "FFG-Trinity-2026-Core-Access" - } - } - } -} -``` - -### 5. Restart Claude Desktop - -After restarting, a hammer icon (🔨) should appear indicating MCP tools are available. - -## Testing - -In Claude Desktop, try: -- "List my Firefrost servers" -- "Check uptime on command-center" -- "What's the disk usage on tx1-dallas?" - -## Trinity Shield (Safety Tiers) - -| Tier | Commands | Behavior | -|------|----------|----------| -| Safe | `hostname`, `uptime`, `df`, `free`, `docker ps` | Execute immediately | -| Operational | `git pull`, `systemctl restart ` | Execute + Log | -| Blocked | `rm -rf /`, `mkfs`, `fdisk`, destructive patterns | Return error, require manual SSH | - -## Future: Claude.ai Web Integration - -When Anthropic enables "Remote MCP" for web: -1. Update Trinity Core to support SSE (Server-Sent Events) -2. Register URL directly in Claude.ai settings -3. Delete local wrapper script - -See `docs/consultations/gemini-mcp-connector-2026-04-11.md` for full Gemini consultation. - ---- - -**Fire + Frost + Foundation = Where Love Builds Legacy** 💙🔥❄️ diff --git a/docs/tasks-index/task-111-trinity-core-web-mcp.md b/docs/tasks-index/task-111-trinity-core-web-mcp.md new file mode 100644 index 0000000..4b13ffd --- /dev/null +++ b/docs/tasks-index/task-111-trinity-core-web-mcp.md @@ -0,0 +1,80 @@ +--- +task_number: 111 +title: Trinity Core Native Web MCP Connector +status: Planned +priority: P1-High +is_blocker: false +owner: Michael +tags: + - trinity-core + - mcp + - claude-web + - infrastructure +estimated_hours: 2 +--- + +# Trinity Core Native Web MCP Connector + +Upgrade Trinity Core from REST API to native Claude.ai web connector using MCP SDK with SSE transport and OAuth shim. + +## Why + +Currently Claude can't call Trinity Core directly. This upgrade enables Claude to execute commands on Firefrost servers **directly from claude.ai web** — no Desktop app, no curl commands, no manual intervention. + +**This is the RV dream:** Full infrastructure access from any browser, anywhere. + +## Prerequisites + +- Trinity Core v1 deployed ✅ +- SSH keys configured ✅ +- Cloudflare Tunnel active ✅ + +## Full Implementation + +See `docs/consultations/gemini-mcp-web-implementation-2026-04-11.md` for complete code (Blocks A-F) and step-by-step instructions. + +## Quick Summary + +1. **Update package.json** — Add `"type": "module"`, install `@modelcontextprotocol/sdk` and `cors` +2. **Replace index.js** — MCP SDK with SSE transport + OAuth shim +3. **Register in Claude.ai** — Settings → Connectors → Add custom → `https://mcp.firefrostgaming.com/mcp` + +## Architecture + +``` +Claude.ai (browser) + ↓ MCP over SSE +Trinity Core (mcp.firefrostgaming.com) + ↓ SSH +Target Server +``` + +## New Endpoints + +| Endpoint | Purpose | +|----------|---------| +| `GET /.well-known/oauth-protected-resource` | OAuth discovery | +| `GET /authorize` | Auto-approve redirect | +| `POST /token` | Return Bearer token | +| `GET /mcp` | Establish SSE stream | +| `POST /mcp/messages` | Receive tool calls | + +## Tools Exposed + +| Tool | Description | +|------|-------------| +| `list_servers` | Get available Firefrost servers | +| `run_command` | Execute SSH command on specified server | + +## Testing + +Before registering with Claude.ai: +```bash +npx @modelcontextprotocol/inspector node index.js +``` + +## Security Notes + +- CORS configured for `https://claude.ai` only +- OAuth shim auto-approves (single user) +- Consider adding `express-rate-limit` for hardening