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:
414
docs/consultations/gemini-mcp-web-implementation-2026-04-11.md
Normal file
414
docs/consultations/gemini-mcp-web-implementation-2026-04-11.md
Normal 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** 💙🔥❄️
|
||||
@@ -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** 💙🔥❄️
|
||||
80
docs/tasks-index/task-111-trinity-core-web-mcp.md
Normal file
80
docs/tasks-index/task-111-trinity-core-web-mcp.md
Normal 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
|
||||
Reference in New Issue
Block a user