Complete Trinity Core Web MCP implementation spec

Gemini consultation provided full production-ready code:
- MCP SDK with SSE transport
- OAuth shim (auto-approve for single user)
- CORS for claude.ai
- Complete index.js replacement (Blocks A-F)

Task #111 upgraded from Desktop to Native Web:
- No Claude Desktop needed
- Works directly in claude.ai browser
- ~2 hour implementation

Key insight: Custom remote MCPs ARE supported in claude.ai web
via HTTP Streamable + OAuth flow.

Chronicler #76
This commit is contained in:
Claude
2026-04-11 07:39:04 +00:00
parent f184c514f0
commit 9c7fa804ee
3 changed files with 494 additions and 244 deletions

View File

@@ -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** 💙🔥❄️

View File

@@ -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 <service>` | 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** 💙🔥❄️

View File

@@ -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