- Add @tpmjs/mcp-client package for connecting to MCP servers - Add @tpmjs/bridge CLI for bridging local MCP servers to TPMJS - Add @tpmjs/test-file-writer test MCP server - Add BridgeConnection and CollectionBridgeTool database models - Add /api/bridge endpoints for bridge communication - Add /api/collections/[id]/bridge-tools API for managing bridge tools - Update MCP handlers to include bridge tools in tools/list - Add bridge status UI at /dashboard/settings/bridge - Add interactive bridge tutorial at /docs/tutorials/bridge
50 KiB
PRD: TPMJS MCP Bridge
Product Requirements Document
| Field | Value |
|---|---|
| Author | Ajax Davis |
| Status | Draft |
| Created | 2025-01-12 |
| Last Updated | 2025-01-12 |
Table of Contents
- Overview
- Problem Statement
- Solution
- User Stories
- Architecture
- User Experience
- Technical Specification
- Database Schema
- API Specification
- Security Considerations
- Implementation Phases
- Success Metrics
- Open Questions
Overview
What
The TPMJS MCP Bridge enables users to connect local MCP (Model Context Protocol) servers to their TPMJS collections. This allows a single TPMJS MCP endpoint to aggregate tools from:
- npm packages (existing)
- Remote MCP servers (HTTP/SSE)
- Local MCP servers (via bridge)
Why
Users currently need to configure multiple MCP servers in their AI tools (Claude Desktop, Cursor, etc.). Each local MCP server (Chrome DevTools, Blender, filesystem, etc.) requires separate configuration. TPMJS can become the single point of control for all tools.
Goal
One MCP endpoint to rule them all. Users add one TPMJS MCP server to Claude Desktop and manage all their tools through the TPMJS UI.
Problem Statement
Current Pain Points
- Multiple MCP Configurations: Users must manually configure each MCP server in Claude Desktop's config file
- No Central Management: No UI to manage which tools are available
- Local Tools Inaccessible from Cloud: TPMJS runs in the cloud and cannot access local MCP servers that use stdio transport
- Tool Discovery Fragmented: Users must find and configure each MCP server separately
Example: Current State
// ~/.config/claude/claude_desktop_config.json
{
"mcpServers": {
"tpmjs": {
"type": "url",
"url": "https://tpmjs.com/api/mcp/ajax/my-tools/http"
},
"chrome-devtools": {
"command": "npx",
"args": ["-y", "chrome-devtools-mcp"]
},
"blender": {
"command": "uvx",
"args": ["blender-mcp"]
},
"filesystem": {
"command": "npx",
"args": ["-y", "@anthropic/mcp-filesystem", "/home/user"]
},
"slack": {
"command": "npx",
"args": ["-y", "@anthropic/mcp-slack"]
}
}
}
Problems:
- 5 separate server configurations
- Must edit JSON file manually
- No visibility into available tools
- Cannot share configurations easily
Solution
The Bridge
A CLI tool (@tpmjs/bridge) that runs on the user's machine and:
- Connects to local MCP servers (stdio)
- Discovers their tools
- Registers tools with TPMJS via WebSocket
- Proxies tool calls from TPMJS to local MCP servers
Target State
// ~/.config/claude/claude_desktop_config.json
{
"mcpServers": {
"tpmjs": {
"type": "url",
"url": "https://tpmjs.com/api/mcp/ajax/unified/http"
}
}
}
One endpoint providing access to:
- npm tools
- Chrome DevTools (via bridge)
- Blender (via bridge)
- Filesystem (via bridge)
- Slack (via bridge)
- Any other MCP server
User Stories
US-1: Add Local MCP Server to Bridge
As a TPMJS user I want to add a local MCP server (like Blender) to my bridge So that its tools appear in my TPMJS collection
Acceptance Criteria:
- User can edit bridge config file to add new server
- Bridge discovers tools when server is added
- Tools appear in TPMJS dashboard
US-2: Select Tools for Collection
As a TPMJS user I want to choose which bridge tools to include in my collection So that I only expose the tools I need
Acceptance Criteria:
- User sees all available bridge tools in UI
- User can add/remove tools from collection
- MCP endpoint only includes selected tools
US-3: Use Bridge Tools from Claude
As a Claude Desktop user I want to use bridge tools (like Chrome screenshot) through TPMJS So that I don't need multiple MCP server configurations
Acceptance Criteria:
- Tool calls route through TPMJS to bridge
- Results return to Claude Desktop
- Latency is acceptable (<2s for most operations)
US-4: Handle Bridge Disconnection
As a TPMJS user I want to see clear errors when my bridge is offline So that I understand why certain tools aren't working
Acceptance Criteria:
- UI shows bridge connection status
- Tool calls return helpful error when bridge is offline
- Bridge auto-reconnects when possible
US-5: Quick CLI Setup
As a developer I want to set up the bridge quickly via CLI So that I can start using local tools immediately
Acceptance Criteria:
npx @tpmjs/bridge initcreates config filenpx @tpmjs/bridge add <server>adds common serversnpx @tpmjs/bridge startconnects everything
Architecture
System Overview
┌─────────────────────────────────────────────────────────────────────────────┐
│ TPMJS Platform │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ MCP Aggregator │ │
│ │ │ │
│ │ Tool Sources: │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐ │ │
│ │ │ npm Registry │ │ Remote MCP │ │ User's Bridge │ │ │
│ │ │ (always on) │ │ (HTTP/SSE) │ │ (when connected) │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────────────────┘ │ │
│ │ │ │ │ │ │
│ │ ▼ ▼ ▼ │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ Unified Tool Registry │ │ │
│ │ │ • npm tools: always available │ │ │
│ │ │ • Remote MCP tools: always available │ │ │
│ │ │ • Bridge tools: available when bridge connected │ │ │
│ │ └─────────────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ MCP Server: /api/mcp/{user}/{collection}/http │ │ │
│ │ └─────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ │ WebSocket │
│ ▼ │
└────────────────────────────────────────┼────────────────────────────────────┘
│
┌──────────────────────────┴──────────────────────────┐
│ │
▼ ▼
┌──────────────────────────────┐ ┌──────────────────────────┐
│ Claude Desktop │ │ User's Machine │
│ (MCP Client) │ │ │
│ │ │ ┌────────────────────┐ │
│ Only needs ONE server: │ │ │ @tpmjs/bridge │ │
│ tpmjs.com/api/mcp/... │ │ │ │ │
│ │ │ │ MCP Clients: │ │
└──────────────────────────────┘ │ │ ├── chrome │ │
│ │ ├── blender │ │
│ │ ├── filesystem │ │
│ │ └── slack │ │
│ └────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────┐ │
│ │ Local MCP Servers │ │
│ │ (stdio processes) │ │
│ └────────────────────┘ │
└──────────────────────────┘
Bridge Detail
User's Machine
┌─────────────────────────────────────────────────────────────────────────────┐
│ │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ @tpmjs/bridge │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────────────┐ │ │
│ │ │ MCP Client Manager │ │ │
│ │ │ │ │ │
│ │ │ Maintains connections to local MCP servers: │ │ │
│ │ │ │ │ │
│ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │
│ │ │ │ chrome │ │ blender │ │ filesystem │ │ │ │
│ │ │ │ devtools │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │ │ │
│ │ │ │ stdio ↕ │ │ stdio ↕ │ │ stdio ↕ │ │ │ │
│ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │
│ │ │ │ │ │ │ │ │
│ │ │ └────────────────┼────────────────┘ │ │ │
│ │ │ │ │ │ │
│ │ │ ▼ │ │ │
│ │ │ ┌─────────────────────┐ │ │ │
│ │ │ │ Tool Registry │ │ │ │
│ │ │ │ (aggregated) │ │ │ │
│ │ │ └─────────────────────┘ │ │ │
│ │ │ │ │ │ │
│ │ └─────────────────────────┼────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ┌─────────────────────────▼────────────────────────────────────────┐ │ │
│ │ │ WebSocket Connection │ │ │
│ │ │ to TPMJS Cloud │ │ │
│ │ │ │ │ │
│ │ │ • Registers available tools │ │ │
│ │ │ • Receives tool call requests │ │ │
│ │ │ • Sends tool results │ │ │
│ │ │ • Auto-reconnects on disconnect │ │ │
│ │ └───────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Tool Call Flow
Step-by-step: User asks Claude to take a screenshot of GitHub
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Claude Desktop │ │ TPMJS Cloud │ │ User's Bridge │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
│ tools/call │ │
│ "chrome--screenshot" │ │
│ ─────────────────────▶│ │
│ │ │
│ │ Parse tool name │
│ │ "chrome--screenshot" │
│ │ → server: chrome │
│ │ → tool: screenshot │
│ │ │
│ │ Lookup server │
│ │ chrome = bridge type │
│ │ │
│ │ Check bridge status │
│ │ → connected ✓ │
│ │ │
│ │ tool_call │
│ │ ─────────────────────▶│
│ │ { │
│ │ serverId: "chrome",│
│ │ toolName: "screenshot",
│ │ args: {} │
│ │ } │
│ │ │
│ │ │ Route to chrome
│ │ │ MCP client
│ │ │
│ │ │ client.callTool({
│ │ │ name: "screenshot",
│ │ │ arguments: {}
│ │ │ })
│ │ │
│ │ │ ┌─────────────┐
│ │ │ │ Chrome │
│ │ │──│ DevTools │
│ │ │ │ Protocol │
│ │ │ └─────────────┘
│ │ │
│ │ │ Screenshot taken
│ │ │
│ │ tool_result │
│ │ ◀─────────────────────│
│ │ { │
│ │ callId: "...", │
│ │ result: { │
│ │ content: [{ │
│ │ type: "image", │
│ │ data: "base64" │
│ │ }] │
│ │ } │
│ │ } │
│ │ │
│ MCP result │ │
│ ◀─────────────────────│ │
│ { │ │
│ content: [{ │ │
│ type: "image", │ │
│ data: "base64" │ │
│ }] │ │
│ } │ │
│ │ │
▼ ▼ ▼
User Experience
Initial Setup
Step 1: Install Bridge
npm install -g @tpmjs/bridge
Step 2: Initialize Configuration
tpmjs-bridge init
# Output:
# Created ~/.tpmjs/bridge.json
# Created ~/.tpmjs/credentials.json
#
# Next steps:
# 1. Run: tpmjs-bridge login
# 2. Add MCP servers to ~/.tpmjs/bridge.json
# 3. Run: tpmjs-bridge start
Step 3: Login
tpmjs-bridge login
# Opens browser to tpmjs.com/auth/bridge
# User authenticates
# Token saved to ~/.tpmjs/credentials.json
# Output:
# ✓ Logged in as ajax@example.com
Step 4: Add MCP Servers
# Add from preset list
tpmjs-bridge add chrome-devtools
tpmjs-bridge add blender
tpmjs-bridge add filesystem --args "/home/user/documents"
# Or manually edit ~/.tpmjs/bridge.json
Config file structure:
{
"servers": [
{
"id": "chrome-devtools",
"name": "Chrome DevTools",
"command": "npx",
"args": ["-y", "chrome-devtools-mcp"],
"env": {}
},
{
"id": "blender",
"name": "Blender",
"command": "uvx",
"args": ["blender-mcp"],
"env": {}
},
{
"id": "filesystem",
"name": "Filesystem",
"command": "npx",
"args": ["-y", "@anthropic/mcp-filesystem", "/home/user/documents"],
"env": {}
}
]
}
Step 5: Start Bridge
tpmjs-bridge start
# Output:
# Starting MCP servers...
# ✓ chrome-devtools: 5 tools (navigate, screenshot, click, type, evaluate)
# ✓ blender: 12 tools (create_object, modify_mesh, render, ...)
# ✓ filesystem: 4 tools (read_file, write_file, list_directory, search)
#
# Connecting to TPMJS...
# ✓ Connected as ajax@example.com
# ✓ Registered 21 tools
#
# Bridge running. Press Ctrl+C to stop.
#
# Your tools are available at:
# https://tpmjs.com/api/mcp/ajax/unified/http
Managing Tools in UI
Dashboard View
┌────────────────────────────────────────────────────────────────────────────┐
│ Collection: unified │
├────────────────────────────────────────────────────────────────────────────┤
│ │
│ Bridge Status: ● Connected Last seen: just now │
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ Your MCP Endpoint (copy to Claude Desktop config): │ │
│ │ │ │
│ │ https://tpmjs.com/api/mcp/ajax/unified/http [Copy] │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
│ ═══════════════════════════════════════════════════════════════════════ │
│ │
│ TOOLS IN COLLECTION (8) │
│ │
│ 📦 npm Tools │
│ ├── @tpmjs/hello / helloWorldTool │
│ └── @tpmjs/weather / getWeather │
│ │
│ 🔗 chrome-devtools ● Connected │
│ ├── navigate Navigate browser to URL [−] │
│ ├── screenshot Take page screenshot [−] │
│ └── click Click an element [−] │
│ │
│ 🔗 blender ● Connected │
│ ├── create_object Create 3D object [−] │
│ ├── render Render scene to image [−] │
│ └── export Export to file format [−] │
│ │
│ ═══════════════════════════════════════════════════════════════════════ │
│ │
│ AVAILABLE FROM BRIDGE (not in collection) [+ Add All] │
│ │
│ 🔗 chrome-devtools │
│ ├── type Type text into element [+] │
│ └── evaluate Run JavaScript [+] │
│ │
│ 🔗 blender │
│ ├── modify_mesh Modify mesh geometry [+] │
│ ├── apply_material Apply material [+] │
│ ├── animate Create animation [+] │
│ └── ... 6 more [+] │
│ │
│ 🔗 filesystem │
│ ├── read_file Read file contents [+] │
│ ├── write_file Write to file [+] │
│ ├── list_directory List directory contents [+] │
│ └── search Search for files [+] │
│ │
└────────────────────────────────────────────────────────────────────────────┘
Adding a Tool
User clicks [+] next to a tool:
┌─────────────────────────────────────────────────────────┐
│ Add Tool to Collection │
├─────────────────────────────────────────────────────────┤
│ │
│ Tool: blender / modify_mesh │
│ │
│ Description: │
│ Modify the geometry of a mesh object in Blender. │
│ Supports operations like subdivide, smooth, and │
│ extrude. │
│ │
│ Parameters: │
│ ┌───────────────────────────────────────────────────┐ │
│ │ object_name (string, required) │ │
│ │ Name of the mesh object to modify │ │
│ │ │ │
│ │ operation (enum, required) │ │
│ │ One of: subdivide, smooth, extrude, bevel │ │
│ │ │ │
│ │ amount (number, optional) │ │
│ │ Amount/intensity of the operation │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ ⚠️ Requires bridge connection │
│ │
│ [Cancel] [Add to Collection] │
└─────────────────────────────────────────────────────────┘
Offline State
When bridge is disconnected:
┌────────────────────────────────────────────────────────────────────────────┐
│ Collection: unified │
├────────────────────────────────────────────────────────────────────────────┤
│ │
│ Bridge Status: ○ Disconnected Last seen: 2 hours ago │
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ ⚠️ Bridge tools unavailable │ │
│ │ │ │
│ │ To reconnect, run on your machine: │ │
│ │ $ tpmjs-bridge start │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
│ TOOLS IN COLLECTION (8) │
│ │
│ 📦 npm Tools ● Available │
│ ├── @tpmjs/hello / helloWorldTool │
│ └── @tpmjs/weather / getWeather │
│ │
│ 🔗 chrome-devtools ○ Offline │
│ ├── navigate ⚠️ Requires bridge │
│ ├── screenshot ⚠️ Requires bridge │
│ └── click ⚠️ Requires bridge │
│ │
│ 🔗 blender ○ Offline │
│ ├── create_object ⚠️ Requires bridge │
│ └── render ⚠️ Requires bridge │
│ │
└────────────────────────────────────────────────────────────────────────────┘
Technical Specification
Package: @tpmjs/bridge
Installation
npm install -g @tpmjs/bridge
# or
npx @tpmjs/bridge <command>
CLI Commands
| Command | Description |
|---|---|
init |
Create config files |
login |
Authenticate with TPMJS |
logout |
Remove credentials |
add <preset> |
Add MCP server from preset |
remove <id> |
Remove MCP server |
list |
List configured servers |
start |
Start bridge daemon |
stop |
Stop bridge daemon |
status |
Show connection status |
config |
Open config file in editor |
Config Files
~/.tpmjs/bridge.json
{
"servers": [
{
"id": "chrome-devtools",
"name": "Chrome DevTools",
"command": "npx",
"args": ["-y", "chrome-devtools-mcp"],
"env": {
"CHROME_PATH": "/Applications/Google Chrome.app"
},
"disabled": false
}
]
}
~/.tpmjs/credentials.json
{
"apiKey": "tpmjs_xxxxxxxxxxxxxxxxxxxx",
"userId": "user_abc123",
"email": "user@example.com",
"expiresAt": "2026-01-12T00:00:00Z"
}
Presets
Built-in presets for common MCP servers:
| Preset | Package | Description |
|---|---|---|
chrome-devtools |
chrome-devtools-mcp |
Chrome browser automation |
browser-mcp |
@anthropic/browser-mcp |
Puppeteer-based automation |
filesystem |
@anthropic/mcp-filesystem |
File system access |
git |
@anthropic/mcp-git |
Git operations |
slack |
@anthropic/mcp-slack |
Slack integration |
blender |
blender-mcp |
Blender 3D automation |
postgres |
@anthropic/mcp-postgres |
PostgreSQL access |
Package: @tpmjs/mcp-client
Internal library for connecting to MCP servers.
import { MCPClientManager } from '@tpmjs/mcp-client';
const manager = new MCPClientManager();
// Connect to a stdio-based MCP server
await manager.connect({
id: 'chrome',
transport: 'stdio',
command: 'npx',
args: ['-y', 'chrome-devtools-mcp'],
});
// List tools
const tools = await manager.listTools('chrome');
// Call a tool
const result = await manager.callTool('chrome', 'screenshot', {
fullPage: true
});
// Disconnect
await manager.disconnect('chrome');
WebSocket Protocol
Connection
wss://tpmjs.com/api/bridge?token=tpmjs_xxxx
Messages: Bridge → TPMJS
Register Tools
{
"type": "register",
"tools": [
{
"serverId": "chrome-devtools",
"serverName": "Chrome DevTools",
"name": "screenshot",
"description": "Take a screenshot of the current page",
"inputSchema": {
"type": "object",
"properties": {
"fullPage": {
"type": "boolean",
"description": "Capture full scrollable page"
}
}
}
}
]
}
Tool Result
{
"type": "tool_result",
"callId": "call_abc123",
"result": {
"content": [
{
"type": "image",
"mimeType": "image/png",
"data": "base64..."
}
]
}
}
Tool Error
{
"type": "tool_error",
"callId": "call_abc123",
"error": {
"code": "EXECUTION_FAILED",
"message": "Chrome is not running"
}
}
Heartbeat
{
"type": "heartbeat",
"timestamp": 1705123456789
}
Messages: TPMJS → Bridge
Tool Call
{
"type": "tool_call",
"callId": "call_abc123",
"serverId": "chrome-devtools",
"toolName": "screenshot",
"args": {
"fullPage": true
}
}
Ping
{
"type": "ping"
}
Database Schema
New Models
// Track active bridge connections
model BridgeConnection {
id String @id @default(cuid())
userId String @unique
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
// Connection state
status String @default("disconnected") // "connected" | "disconnected"
socketId String? // Internal socket identifier for routing
// Cached tool definitions from bridge
tools Json @default("[]")
// Metadata
lastSeen DateTime?
clientVersion String?
clientOS String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([status])
}
// Track which bridge tools are added to collections
model CollectionBridgeTool {
id String @id @default(cuid())
collectionId String
collection Collection @relation(fields: [collectionId], references: [id], onDelete: Cascade)
// Reference to bridge tool
serverId String // e.g., "chrome-devtools"
toolName String // e.g., "screenshot"
// Display customization
displayName String? // Override tool name in MCP
note String? // User notes
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([collectionId, serverId, toolName])
@@index([collectionId])
}
// Update Collection model
model Collection {
// ... existing fields ...
bridgeTools CollectionBridgeTool[]
}
Migration
-- CreateTable
CREATE TABLE "BridgeConnection" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"status" TEXT NOT NULL DEFAULT 'disconnected',
"socketId" TEXT,
"tools" JSONB NOT NULL DEFAULT '[]',
"lastSeen" TIMESTAMP(3),
"clientVersion" TEXT,
"clientOS" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "BridgeConnection_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "CollectionBridgeTool" (
"id" TEXT NOT NULL,
"collectionId" TEXT NOT NULL,
"serverId" TEXT NOT NULL,
"toolName" TEXT NOT NULL,
"displayName" TEXT,
"note" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "CollectionBridgeTool_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "BridgeConnection_userId_key" ON "BridgeConnection"("userId");
CREATE INDEX "BridgeConnection_status_idx" ON "BridgeConnection"("status");
CREATE UNIQUE INDEX "CollectionBridgeTool_collectionId_serverId_toolName_key"
ON "CollectionBridgeTool"("collectionId", "serverId", "toolName");
-- AddForeignKey
ALTER TABLE "BridgeConnection" ADD CONSTRAINT "BridgeConnection_userId_fkey"
FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "CollectionBridgeTool" ADD CONSTRAINT "CollectionBridgeTool_collectionId_fkey"
FOREIGN KEY ("collectionId") REFERENCES "Collection"("id") ON DELETE CASCADE ON UPDATE CASCADE;
API Specification
Bridge WebSocket Endpoint
Endpoint: GET /api/bridge
Query Parameters:
token(required): User's API key
Upgrade: WebSocket
Authentication: Validates API key, returns 401 if invalid
Bridge Status API
Endpoint: GET /api/user/bridge
Response:
{
"success": true,
"data": {
"status": "connected",
"lastSeen": "2025-01-12T10:30:00Z",
"toolCount": 21,
"servers": [
{
"id": "chrome-devtools",
"name": "Chrome DevTools",
"toolCount": 5,
"tools": ["navigate", "screenshot", "click", "type", "evaluate"]
},
{
"id": "blender",
"name": "Blender",
"toolCount": 12,
"tools": ["create_object", "modify_mesh", "render", "..."]
}
]
}
}
Collection Bridge Tools API
Add Tool: POST /api/collections/{id}/bridge-tools
{
"serverId": "chrome-devtools",
"toolName": "screenshot"
}
Remove Tool: DELETE /api/collections/{id}/bridge-tools/{toolId}
List Tools: GET /api/collections/{id}/bridge-tools
{
"success": true,
"data": [
{
"id": "cbt_abc123",
"serverId": "chrome-devtools",
"toolName": "screenshot",
"displayName": null,
"note": null,
"available": true
}
]
}
Updated MCP Handlers
tools/list now includes bridge tools:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [
{
"name": "tpmjs-hello--helloWorldTool",
"description": "Says hello world",
"inputSchema": { ... }
},
{
"name": "chrome--screenshot",
"description": "Take a screenshot",
"inputSchema": { ... }
},
{
"name": "blender--create_object",
"description": "Create 3D object",
"inputSchema": { ... }
}
]
}
}
tools/call routes appropriately:
async function handleToolsCall(toolName: string, args: unknown) {
const [prefix, actualName] = parseToolName(toolName);
if (isNpmTool(prefix)) {
// Route to sandbox executor
return executeNpmTool(prefix, actualName, args);
}
if (isBridgeTool(prefix)) {
// Route to user's bridge
const bridge = await getBridgeConnection(userId);
if (!bridge || bridge.status !== 'connected') {
throw new Error('Bridge not connected. Run `tpmjs-bridge start` to connect.');
}
return callBridgeTool(bridge, prefix, actualName, args);
}
}
Security Considerations
Authentication
- Bridge Authentication: API key required for WebSocket connection
- Key Rotation: Support key rotation without disconnecting active bridges
- Session Tokens: Short-lived session tokens for active connections
Authorization
- User Isolation: Each user's bridge is isolated
- Collection Scoping: Bridge tools only accessible in user's own collections
- Tool Whitelisting: Users explicitly add tools to collections
Data Protection
- No Credential Storage: Bridge stores credentials locally only
- Encrypted Transport: WSS required (TLS)
- Tool Arguments: Logged but redacted for sensitive fields
Rate Limiting
- Connection Limit: 1 bridge per user
- Tool Call Rate: 60 calls/minute per bridge
- Reconnection Backoff: Exponential backoff on repeated failures
Implementation Phases
Phase 1: Bridge Core (3-4 weeks)
Goal: Basic bridge functionality
Deliverables:
@tpmjs/mcp-clientpackage@tpmjs/bridgeCLI withinit,start,stop- WebSocket endpoint
/api/bridge BridgeConnectiondatabase model- Basic UI showing bridge status
Acceptance Criteria:
- Bridge can connect to TPMJS
- Bridge can spawn local MCP servers
- Tools are registered with TPMJS
- Tool calls route through bridge
Phase 2: Collection Integration (2 weeks)
Goal: Add bridge tools to collections
Deliverables:
CollectionBridgeToolmodel- API for adding/removing bridge tools
- UI for managing bridge tools in collections
- Updated MCP handlers for bridge tools
Acceptance Criteria:
- Users can add bridge tools to collections
- MCP endpoint includes selected bridge tools
- Tool calls execute correctly
Phase 3: Polish & Presets (2 weeks)
Goal: Great user experience
Deliverables:
tpmjs-bridge add <preset>command- Preset library for common MCP servers
- Auto-reconnection logic
- Better error messages
- Connection health monitoring
Acceptance Criteria:
- New users can set up in <5 minutes
- Bridge handles disconnections gracefully
- Clear feedback when things go wrong
Phase 4: Advanced Features (Ongoing)
Goal: Power user features
Deliverables:
- Multiple bridge support (different machines)
- Bridge groups/profiles
- Tool filtering/search in UI
- Usage analytics
- Browser extension alternative to CLI
Success Metrics
Adoption
| Metric | Target (3 months) |
|---|---|
| Bridge installs | 1,000 |
| Daily active bridges | 200 |
| Tools registered via bridge | 5,000 |
Reliability
| Metric | Target |
|---|---|
| Bridge uptime | 99% (when running) |
| Tool call success rate | 95% |
| Reconnection success | 99% |
Performance
| Metric | Target |
|---|---|
| Tool call latency (p50) | <500ms |
| Tool call latency (p95) | <2000ms |
| Bridge startup time | <10s |
Open Questions
Q1: Multiple Bridges?
Question: Should we support multiple bridges per user (e.g., work laptop + home desktop)?
Options:
- One bridge per user (simpler)
- Multiple bridges with naming (more flexible)
- Bridge "profiles" that can be switched
Recommendation: Start with one, add multiple in Phase 4
Q2: Offline Tool Caching?
Question: Should bridge tools show in MCP list when bridge is offline?
Options:
- Hide tools when offline
- Show tools but return error on call
- Cache last-known tools, show with warning
Recommendation: Option 3 - better UX, clear expectations
Q3: Tool Permissions?
Question: Should we add permission scopes to bridge tools?
Options:
- All-or-nothing access
- Per-tool permissions
- Capability-based (read, write, execute)
Recommendation: Start with all-or-nothing, add granular later
Q4: Daemon vs On-Demand?
Question: Should bridge run as a daemon or on-demand?
Options:
- Daemon (always running)
- On-demand (start when needed)
- Hybrid (start on login, sleep when idle)
Recommendation: Daemon for now, easier to reason about
Appendix
A: Example Bridge Session
$ tpmjs-bridge start --verbose
[10:30:00] Loading config from ~/.tpmjs/bridge.json
[10:30:00] Found 3 servers configured
[10:30:00] Starting chrome-devtools...
[10:30:01] Spawning: npx -y chrome-devtools-mcp
[10:30:03] Connected via stdio
[10:30:03] Discovering tools...
[10:30:03] Found 5 tools: navigate, screenshot, click, type, evaluate
[10:30:03] Starting blender...
[10:30:03] Spawning: uvx blender-mcp
[10:30:05] Connected via stdio
[10:30:05] Discovering tools...
[10:30:05] Found 12 tools: create_object, modify_mesh, render, ...
[10:30:05] Starting filesystem...
[10:30:05] Spawning: npx -y @anthropic/mcp-filesystem /home/user
[10:30:06] Connected via stdio
[10:30:06] Discovering tools...
[10:30:06] Found 4 tools: read_file, write_file, list_directory, search
[10:30:06] Connecting to TPMJS...
[10:30:06] WebSocket: wss://tpmjs.com/api/bridge
[10:30:07] Authenticated as ajax@example.com
[10:30:07] Registering 21 tools...
[10:30:07] Registration complete
[10:30:07] Bridge ready!
[10:30:07] Tools available at: https://tpmjs.com/api/mcp/ajax/unified/http
[10:32:15] Tool call: chrome-devtools/navigate
[10:32:15] Args: { url: "https://github.com" }
[10:32:16] Result: Success (1.2s)
[10:32:18] Tool call: chrome-devtools/screenshot
[10:32:18] Args: { fullPage: true }
[10:32:20] Result: Success (1.8s)
^C
[10:45:00] Shutting down...
[10:45:00] Disconnecting from TPMJS
[10:45:00] Stopping chrome-devtools
[10:45:00] Stopping blender
[10:45:00] Stopping filesystem
[10:45:01] Bridge stopped
B: Claude Desktop Configuration
Before (multiple servers):
{
"mcpServers": {
"tpmjs": {
"type": "url",
"url": "https://tpmjs.com/api/mcp/ajax/tools/http"
},
"chrome": {
"command": "npx",
"args": ["-y", "chrome-devtools-mcp"]
},
"blender": {
"command": "uvx",
"args": ["blender-mcp"]
},
"files": {
"command": "npx",
"args": ["-y", "@anthropic/mcp-filesystem", "/home/user"]
}
}
}
After (single server):
{
"mcpServers": {
"tpmjs": {
"type": "url",
"url": "https://tpmjs.com/api/mcp/ajax/unified/http"
}
}
}
C: Error Messages
| Scenario | Error Message |
|---|---|
| Bridge not connected | "Bridge not connected. Run tpmjs-bridge start on your machine to enable local tools." |
| Tool not found | "Tool 'blender--render' not found. Make sure it's added to your collection." |
| Server not responding | "The blender MCP server is not responding. Check that Blender is running." |
| Timeout | "Tool call timed out after 5 minutes. The operation may still be running." |
| Auth failed | "Bridge authentication failed. Run tpmjs-bridge login to refresh credentials." |