Gemini identified 5 concerns: 1. Command injection (CRITICAL) - Fix: use spawn instead of exec 2. Race conditions (MEDIUM) - Future: server locking 3. SD card wear (HIGH) - FIXED: cron log rotation 4. Cloudflare Access (MEDIUM) - Future enhancement 5. System prompts (LOW) - Document for Catalyst/Orb Updated MCP implementation spec with secure spawn-based SSH execution. Task #112 is a BLOCKER for Task #111 (web MCP deployment). Chronicler #76
13 KiB
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):
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 serversrun_command— Executes command on specified server
2. OAuth Shim Endpoints
GET /.well-known/oauth-protected-resource— Discovery endpointGET /authorize— Renders a simple "Authorize" button/auto-redirectPOST /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
-
CORS — Do we need any special CORS headers for Claude.ai to connect?
-
Error handling — What happens if the SSE connection drops mid-command?
-
Security — Any additional hardening beyond the OAuth shim?
-
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
cd /home/claude_executor/mcp-server/
npm install @modelcontextprotocol/sdk cors express
package.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
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
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
// 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
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 (SECURE VERSION)
IMPORTANT: Uses spawn instead of exec to prevent command injection attacks.
import { spawn } from 'child_process';
function executeSSH(server, command) {
return new Promise((resolve) => {
const target = SERVERS[server];
log(`EXEC [${server}] ${command}`);
// spawn with array args prevents shell injection
const ssh = spawn('ssh', [
'-o', 'ConnectTimeout=10',
'-o', 'StrictHostKeyChecking=no',
`${target.user}@${target.host}`,
command
]);
let stdout = '';
let stderr = '';
ssh.stdout.on('data', (data) => stdout += data.toString());
ssh.stderr.on('data', (data) => stderr += data.toString());
ssh.on('close', (code) => {
const success = code === 0;
log(`RESULT [${server}] success=${success}`);
resolve({
success,
stdout: stdout.trim(),
stderr: stderr.trim(),
error: success ? null : `Exit code ${code}`
});
});
ssh.on('error', (err) => {
log(`ERROR [${server}] ${err.message}`);
resolve({
success: false,
stdout: '',
stderr: err.message,
error: err.message
});
});
// Timeout after 30 seconds
setTimeout(() => {
ssh.kill();
resolve({
success: false,
stdout: stdout.trim(),
stderr: 'Command timed out after 30 seconds',
error: 'Timeout'
});
}, 30000);
});
}
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 };
}
const result = await executeSSH(server, command);
let output = result.stdout || '(no output)';
if (result.stderr) output += `\nSTDERR: ${result.stderr}`;
if (result.error && !result.success) output += `\nError: ${result.error}`;
return { content: [{ type: "text", text: output }], isError: !result.success };
}
});
Block F: SSE Transport & Server Start
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
- Open Claude.ai in your browser
- Click profile → Settings → Connectors
- Scroll to bottom → Add custom connector
- Name:
Trinity Core - Connector URL:
https://mcp.firefrostgaming.com/mcp - 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.jswith new implementation (Blocks A-F) - Restart
mcp-serverservice - Test with MCP Inspector
- Register in Claude.ai Settings → Connectors
- Test
list_serversandrun_commandfrom Claude.ai
Fire + Frost + Foundation = Where Love Builds Legacy 💙🔥❄️