feat: complete MCP Bridge implementation

- 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
This commit is contained in:
Ajax Davis 2026-01-13 00:59:34 +10:00
parent 490d76a50b
commit c8c1a12f22
37 changed files with 7318 additions and 23 deletions

View file

@ -0,0 +1,909 @@
# TPMJS MCP Aggregator: One MCP Server to Rule Them All
A design document for importing tools from external MCP servers into TPMJS collections, enabling a single unified MCP endpoint.
---
## Table of Contents
1. [The Vision](#the-vision)
2. [Current State](#current-state)
3. [The Challenge](#the-challenge)
4. [Architecture Options](#architecture-options)
5. [Recommended Implementation](#recommended-implementation)
6. [Technical Specifications](#technical-specifications)
7. [User Experience](#user-experience)
8. [Implementation Phases](#implementation-phases)
---
## The Vision
**Goal**: Add one MCP server to Claude Desktop and control ALL your tools from TPMJS.
```
Before (Current State):
┌─────────────────────────────────────────┐
│ Claude Desktop / Cursor / Claude Code │
│ │
│ MCP Servers: │
│ ├── tpmjs.com/mcp/user/my-tools │ ← TPMJS collection
│ ├── chrome-devtools-mcp │ ← Local stdio
│ ├── browser-mcp │ ← Local stdio
│ ├── filesystem-mcp │ ← Local stdio
│ └── slack-mcp │ ← Local stdio
└─────────────────────────────────────────┘
After (With Aggregator):
┌─────────────────────────────────────────┐
│ Claude Desktop / Cursor / Claude Code │
│ │
│ MCP Servers: │
│ └── tpmjs.com/mcp/user/unified │ ← ONE server with ALL tools
│ │
│ Contains: │
│ ├── npm tools (remote) │
│ ├── chrome tools (via bridge) │
│ ├── browser tools (via bridge) │
│ ├── filesystem tools (via bridge) │
│ └── slack tools (via bridge) │
└─────────────────────────────────────────┘
```
**Benefits**:
- Single MCP configuration
- Centralized tool management via TPMJS UI
- Mix remote npm tools with local MCP tools
- Easy sharing of tool configurations
- Unified environment variable management
---
## Current State
### TPMJS as MCP Server
TPMJS already exposes collections as MCP servers:
```
Endpoint: /api/mcp/{username}/{slug}/{transport}
Transport: HTTP or SSE
Protocol: JSON-RPC 2.0
```
**Supported Methods**:
- `initialize` - Server handshake
- `tools/list` - List all tools in collection
- `tools/call` - Execute a tool
**Tool Source**: Currently only npm packages synced from the TPMJS registry.
### What We Need to Add
1. **MCP Client Capability**: Connect TO other MCP servers
2. **Tool Import**: Pull tool definitions from external MCP servers
3. **Proxy Execution**: Route tool calls to original MCP server
4. **Bridge Infrastructure**: Handle local stdio-based servers
---
## The Challenge
### Transport Mismatch
Most powerful MCP servers use **stdio transport** which requires local execution:
| MCP Server | Transport | Why |
|------------|-----------|-----|
| Chrome DevTools MCP | stdio | Controls local Chrome via DevTools Protocol |
| Claude in Chrome | Native Messaging | Controls user's browser via Chrome extension |
| Browser MCP | stdio + extension | Puppeteer on user's machine |
| Filesystem MCP | stdio | Reads/writes local files |
| Git MCP | stdio | Operates on local git repos |
**Problem**: TPMJS runs in the cloud. It cannot directly connect to stdio-based MCP servers on user's machines.
### The Bridge Requirement
```
User's Machine TPMJS Cloud
┌────────────────────────────┐ ┌─────────────────────────────┐
│ │ │ │
│ ┌──────────────────────┐ │ │ ┌───────────────────────┐ │
│ │ Chrome DevTools MCP │ │ │ │ TPMJS cannot reach │ │
│ │ (stdio) │ │ │ │ local stdio servers │ │
│ └──────────────────────┘ │ ✗ │ │ directly │ │
│ ┌──────────────────────┐ │────────│ │ │ │
│ │ Filesystem MCP │ │ │ └───────────────────────┘ │
│ │ (stdio) │ │ │ │
│ └──────────────────────┘ │ │ │
└────────────────────────────┘ └─────────────────────────────┘
NEED: A BRIDGE
```
---
## Architecture Options
### Option A: Full Cloud (Limited)
Only support MCP servers that expose HTTP/SSE endpoints.
```
TPMJS Cloud
┌─────────────────────────────────────────────────────────────┐
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ TPMJS MCP Aggregator │ │
│ │ │ │
│ │ Connects to: │ │
│ │ ├── Remote MCP Server A (HTTP) ✓ │ │
│ │ ├── Remote MCP Server B (SSE) ✓ │ │
│ │ └── Local MCP Server (stdio) ✗ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
```
**Pros**: Simple, no user setup
**Cons**: Can't use Chrome, filesystem, or other local tools
---
### Option B: User-Hosted Bridge (CLI)
User runs a bridge CLI that connects local MCP servers to TPMJS.
```
User's Machine TPMJS Cloud
┌────────────────────────────────┐ ┌─────────────────────────────┐
│ │ │ │
│ ┌──────────────────────────┐ │ │ ┌───────────────────────┐ │
│ │ tpmjs-bridge CLI │◀─┼── WSS ──┼─▶│ TPMJS API │ │
│ │ │ │ │ │ │ │
│ │ Connects to local MCP: │ │ │ │ Routes tool calls │ │
│ │ ├── chrome-devtools │ │ │ │ to user's bridge │ │
│ │ ├── filesystem │ │ │ │ │ │
│ │ └── custom servers │ │ │ └───────────────────────┘ │
│ └──────────────────────────┘ │ │ │
│ │ │ │ │
│ ▼ │ │ │
│ ┌──────────────────────────┐ │ │ │
│ │ Local MCP Servers │ │ │ │
│ │ (stdio) │ │ │ │
│ └──────────────────────────┘ │ │ │
└────────────────────────────────┘ └─────────────────────────────┘
```
**Flow**:
1. User runs: `npx tpmjs-bridge --servers chrome-devtools,filesystem`
2. Bridge connects to TPMJS via WebSocket
3. Bridge discovers tools from local MCP servers
4. TPMJS receives tool definitions
5. Tool calls route: TPMJS → Bridge → Local MCP → Result → Bridge → TPMJS
**Pros**: Full local tool access, works with any MCP server
**Cons**: Requires CLI running, connection management
---
### Option C: Browser Extension Bridge
Use browser extension with native messaging for bridge functionality.
```
Browser (with TPMJS Extension)
┌─────────────────────────────────────────────────────────────────────┐
│ │
│ TPMJS Web App TPMJS Extension │
│ ┌─────────────────────┐ ┌─────────────────────────────┐ │
│ │ │◀─ msgs ─▶│ Native Messaging Host │ │
│ │ Tool Management │ │ ┌───────────────────────┐ │ │
│ │ UI │ │ │ Connects to MCP │ │ │
│ └─────────────────────┘ │ │ servers via stdio │ │ │
│ │ └───────────────────────┘ │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
┌───────────────────┐
│ Local MCP │
│ Servers (stdio) │
└───────────────────┘
```
**Pros**: No CLI needed, browser-native
**Cons**: Complex setup, browser-dependent
---
### Option D: Hybrid Approach (Recommended)
Combine cloud + bridge for best of both worlds:
```
┌────────────────────────────────────────────────────────────────────────┐
│ TPMJS Platform │
│ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ MCP Aggregator Service │ │
│ │ │ │
│ │ Tool Sources: │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │
│ │ │ npm Registry │ │ Remote MCP │ │ User Bridge │ │ │
│ │ │ (always avail) │ │ (HTTP/SSE) │ │ (when online) │ │ │
│ │ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ │
│ │ │ │ │ │ │
│ │ ▼ ▼ ▼ │ │
│ │ ┌────────────────────────────────────────────────────────────┐ │ │
│ │ │ Unified Tool Registry │ │ │
│ │ │ │ │ │
│ │ │ Tools: │ │ │
│ │ │ ├── @tpmjs/hello.helloWorld [npm] ✓ always │ │ │
│ │ │ ├── slack.postMessage [remote] ✓ always │ │ │
│ │ │ ├── chrome.navigate [bridge] ? online │ │ │
│ │ │ └── filesystem.readFile [bridge] ? online │ │ │
│ │ └────────────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌────────────────────────────────────────────────────────────┐ │ │
│ │ │ MCP Server Endpoint │ │ │
│ │ │ /api/mcp/{user}/{collection}/http │ │ │
│ │ └────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────────────────┘
│ ▲
▼ │
┌─────────────────────────┐ WebSocket │
│ MCP Client │─────────────────┘
│ (Claude Desktop, etc.) │
└─────────────────────────┘
User's Machine
┌─────────────────────────────────────┐
│ │
│ ┌───────────────────────────────┐ │
│ │ tpmjs-bridge │ │
│ │ Connected to TPMJS via WSS │◀──── (WebSocket)
│ │ │ │
│ │ Local MCP Servers: │ │
│ │ ├── chrome-devtools (stdio) │ │
│ │ ├── filesystem (stdio) │ │
│ │ └── custom (stdio) │ │
│ └───────────────────────────────┘ │
│ │
└─────────────────────────────────────┘
```
---
## Recommended Implementation
### Core Components
#### 1. MCP Client Library (`@tpmjs/mcp-client`)
A package that can connect to MCP servers and proxy their tools.
```typescript
// packages/mcp-client/src/index.ts
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
export interface MCPServerConfig {
id: string;
name: string;
transport: 'stdio' | 'http' | 'sse';
// For stdio
command?: string;
args?: string[];
// For http/sse
url?: string;
headers?: Record<string, string>;
}
export class MCPClientManager {
private clients: Map<string, Client> = new Map();
async connect(config: MCPServerConfig): Promise<void> {
const client = new Client({
name: 'tpmjs-aggregator',
version: '1.0.0',
});
let transport;
if (config.transport === 'stdio') {
transport = new StdioClientTransport({
command: config.command!,
args: config.args || [],
});
} else {
transport = new StreamableHTTPClientTransport(
new URL(config.url!),
{ headers: config.headers }
);
}
await client.connect(transport);
this.clients.set(config.id, client);
}
async listTools(serverId: string) {
const client = this.clients.get(serverId);
if (!client) throw new Error(`Server ${serverId} not connected`);
return client.listTools();
}
async callTool(serverId: string, name: string, args: unknown) {
const client = this.clients.get(serverId);
if (!client) throw new Error(`Server ${serverId} not connected`);
return client.callTool({ name, arguments: args as Record<string, unknown> });
}
async disconnect(serverId: string) {
const client = this.clients.get(serverId);
if (client) {
await client.close();
this.clients.delete(serverId);
}
}
}
```
#### 2. Bridge CLI (`tpmjs-bridge`)
Runs on user's machine, connects local MCP servers to TPMJS.
```typescript
// packages/tpmjs-bridge/src/index.ts
#!/usr/bin/env node
import { MCPClientManager, MCPServerConfig } from '@tpmjs/mcp-client';
import WebSocket from 'ws';
interface BridgeConfig {
apiKey: string;
tpmjsUrl: string;
servers: MCPServerConfig[];
}
class TPMJSBridge {
private mcpManager: MCPClientManager;
private ws: WebSocket | null = null;
private config: BridgeConfig;
constructor(config: BridgeConfig) {
this.config = config;
this.mcpManager = new MCPClientManager();
}
async start() {
// 1. Connect to all local MCP servers
for (const server of this.config.servers) {
console.log(`Connecting to ${server.name}...`);
await this.mcpManager.connect(server);
}
// 2. Gather all tools from connected servers
const allTools = [];
for (const server of this.config.servers) {
const { tools } = await this.mcpManager.listTools(server.id);
allTools.push(...tools.map(t => ({
...t,
serverId: server.id,
serverName: server.name,
})));
}
// 3. Connect to TPMJS WebSocket
this.ws = new WebSocket(
`${this.config.tpmjsUrl}/api/bridge?token=${this.config.apiKey}`
);
this.ws.on('open', () => {
console.log('Connected to TPMJS');
// Register available tools
this.ws!.send(JSON.stringify({
type: 'register',
tools: allTools,
}));
});
this.ws.on('message', async (data) => {
const message = JSON.parse(data.toString());
if (message.type === 'tool_call') {
// Execute tool via local MCP server
const result = await this.mcpManager.callTool(
message.serverId,
message.toolName,
message.args
);
// Send result back
this.ws!.send(JSON.stringify({
type: 'tool_result',
callId: message.callId,
result,
}));
}
});
this.ws.on('close', () => {
console.log('Disconnected from TPMJS, reconnecting...');
setTimeout(() => this.start(), 5000);
});
}
}
// CLI entry point
const config = loadConfig(); // from ~/.tpmjs/bridge.json
const bridge = new TPMJSBridge(config);
bridge.start();
```
#### 3. Bridge WebSocket API (`/api/bridge`)
Server-side handler for bridge connections.
```typescript
// apps/web/src/app/api/bridge/route.ts
import { prisma } from '@tpmjs/db';
export const runtime = 'nodejs';
// WebSocket upgrade handler
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const token = searchParams.get('token');
// Validate API key
const user = await validateApiKey(token);
if (!user) {
return new Response('Unauthorized', { status: 401 });
}
// Upgrade to WebSocket
const { socket, response } = Deno.upgradeWebSocket(request);
socket.onmessage = async (event) => {
const message = JSON.parse(event.data);
if (message.type === 'register') {
// Store bridge tools in database
await prisma.bridgeConnection.upsert({
where: { userId: user.id },
update: {
tools: message.tools,
lastSeen: new Date(),
status: 'connected',
},
create: {
userId: user.id,
tools: message.tools,
lastSeen: new Date(),
status: 'connected',
},
});
}
if (message.type === 'tool_result') {
// Forward result to waiting request
pendingCalls.get(message.callId)?.resolve(message.result);
}
};
socket.onclose = async () => {
await prisma.bridgeConnection.update({
where: { userId: user.id },
update: { status: 'disconnected' },
});
};
return response;
}
```
#### 4. Database Schema Updates
```prisma
// packages/db/prisma/schema.prisma
// Track connected bridges
model BridgeConnection {
id String @id @default(cuid())
userId String @unique
user User @relation(fields: [userId], references: [id])
tools Json // Array of tool definitions from bridge
status String // 'connected' | 'disconnected'
lastSeen DateTime
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// Track external MCP servers added to collections
model ExternalMCPServer {
id String @id @default(cuid())
collectionId String
collection Collection @relation(fields: [collectionId], references: [id])
name String
transport String // 'http' | 'sse' | 'bridge'
// For HTTP/SSE
url String?
headers Json? // Encrypted headers
// For bridge (tool IDs from user's connected bridge)
bridgeToolIds String[]
// Cached tool definitions
tools Json?
lastSync DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// Update Collection to include external servers
model Collection {
// ... existing fields ...
externalServers ExternalMCPServer[]
}
```
#### 5. Enhanced MCP Handlers
```typescript
// apps/web/src/lib/mcp/handlers.ts
export async function handleToolsList(
collection: CollectionWithTools,
userId: string
): Promise<MCPToolsListResult> {
const tools: MCPTool[] = [];
// 1. Add npm-based tools (existing)
for (const ct of collection.tools) {
tools.push(convertToMCPTool(ct.tool));
}
// 2. Add remote MCP server tools
for (const server of collection.externalServers) {
if (server.transport === 'http' || server.transport === 'sse') {
const serverTools = await fetchRemoteMCPTools(server);
tools.push(...serverTools.map(t => ({
...t,
name: `${server.name}--${t.name}`, // Namespace by server
})));
}
}
// 3. Add bridge tools (if user has connected bridge)
const bridge = await prisma.bridgeConnection.findUnique({
where: { userId },
});
if (bridge?.status === 'connected') {
for (const server of collection.externalServers) {
if (server.transport === 'bridge') {
const bridgeTools = bridge.tools.filter(
t => server.bridgeToolIds.includes(t.id)
);
tools.push(...bridgeTools.map(t => ({
...t,
name: `${server.name}--${t.name}`,
})));
}
}
}
return { tools };
}
export async function handleToolsCall(
collection: CollectionWithTools,
userId: string,
toolName: string,
args: unknown
): Promise<MCPToolResult> {
// Parse namespaced tool name
const [serverName, actualToolName] = toolName.split('--');
// Find the server
const server = collection.externalServers.find(s => s.name === serverName);
if (!server) {
// Must be an npm tool, use existing logic
return executeNpmTool(collection, toolName, args);
}
if (server.transport === 'http' || server.transport === 'sse') {
// Call remote MCP server directly
return callRemoteMCPTool(server, actualToolName, args);
}
if (server.transport === 'bridge') {
// Route through user's bridge
return callBridgeTool(userId, server, actualToolName, args);
}
}
async function callBridgeTool(
userId: string,
server: ExternalMCPServer,
toolName: string,
args: unknown
): Promise<MCPToolResult> {
const bridge = await getBridgeConnection(userId);
if (!bridge || bridge.status !== 'connected') {
throw new Error('Bridge not connected. Run `npx tpmjs-bridge` to connect.');
}
// Send tool call through WebSocket
const callId = generateId();
const result = await new Promise((resolve, reject) => {
pendingCalls.set(callId, { resolve, reject });
bridge.socket.send(JSON.stringify({
type: 'tool_call',
callId,
serverId: server.bridgeServerId,
toolName,
args,
}));
// Timeout after 5 minutes
setTimeout(() => {
pendingCalls.delete(callId);
reject(new Error('Bridge tool call timed out'));
}, 300000);
});
return result;
}
```
---
## Technical Specifications
### Tool Naming Convention
To avoid collisions when aggregating from multiple sources:
```
{source}--{originalName}
Examples:
- npm--@tpmjs/hello--helloWorldTool (npm package)
- chrome-devtools--navigate (remote MCP)
- bridge--filesystem--readFile (bridge MCP)
```
### Transport Priority
When a tool exists in multiple sources:
1. **npm** - Fastest, always available
2. **Remote HTTP/SSE** - Fast, usually available
3. **Bridge** - Requires user connection, variable latency
### Error Handling
```typescript
interface ToolExecutionError {
code: 'BRIDGE_DISCONNECTED' | 'REMOTE_TIMEOUT' | 'TOOL_NOT_FOUND';
message: string;
suggestion?: string;
}
// Examples:
{
code: 'BRIDGE_DISCONNECTED',
message: 'Cannot execute chrome.navigate - bridge not connected',
suggestion: 'Run `npx tpmjs-bridge` to connect your local tools'
}
```
### Security Considerations
1. **API Key Authentication**: Bridge connections require valid API key
2. **User Isolation**: Each user's bridge is isolated
3. **Tool Whitelisting**: Users explicitly add tools to collections
4. **Encrypted Credentials**: Remote MCP server credentials encrypted at rest
5. **WebSocket Security**: WSS (TLS) required for bridge connections
---
## User Experience
### Adding Remote MCP Tools via UI
```
┌────────────────────────────────────────────────────────────────────┐
│ Collection: My Dev Tools │
├────────────────────────────────────────────────────────────────────┤
│ │
│ Tools (12) [+ Add Tools ▼] │
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ + Add from npm registry │ │
│ │ + Add from remote MCP server (HTTP/SSE) │ │
│ │ + Add from local bridge │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
│ ───────────────────────────────────────────────────────────── │
│ │
│ 📦 npm Tools │
│ ├── @tpmjs/hello / helloWorldTool [Remove] │
│ └── @tpmjs/weather / getWeather [Remove] │
│ │
│ 🌐 Remote MCP: slack-mcp (https://slack-mcp.com) │
│ ├── postMessage [Remove] │
│ └── listChannels [Remove] │
│ │
│ 🔗 Bridge: chrome-devtools ● Connected │
│ ├── navigate [Remove] │
│ ├── screenshot [Remove] │
│ └── evaluate [Remove] │
│ │
│ 🔗 Bridge: filesystem ● Connected │
│ └── readFile [Remove] │
│ │
└────────────────────────────────────────────────────────────────────┘
```
### Bridge Setup Flow
```
┌────────────────────────────────────────────────────────────────────┐
│ Connect Local Tools │
├────────────────────────────────────────────────────────────────────┤
│ │
│ Your local MCP servers can be accessed through TPMJS. │
│ │
│ Step 1: Install the bridge │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ npm install -g @tpmjs/bridge │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
│ Step 2: Configure your MCP servers │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ tpmjs-bridge init │ │
│ │ │ │
│ │ # This creates ~/.tpmjs/bridge.json with: │ │
│ │ { │ │
│ │ "servers": [ │ │
│ │ { │ │
│ │ "name": "chrome-devtools", │ │
│ │ "command": "npx", │ │
│ │ "args": ["-y", "chrome-devtools-mcp"] │ │
│ │ } │ │
│ │ ] │ │
│ │ } │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
│ Step 3: Start the bridge │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ tpmjs-bridge start │ │
│ │ │ │
│ │ ✓ Connected to chrome-devtools (5 tools) │ │
│ │ ✓ Connected to TPMJS │ │
│ │ Bridge running. Press Ctrl+C to stop. │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Bridge Status: ● Connected │ │
│ │ Tools Available: 5 │ │
│ │ Last Seen: Just now │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────────────┘
```
### Unified MCP Configuration
After setup, user only needs ONE MCP server in their config:
```json
// ~/.config/claude/claude_desktop_config.json
{
"mcpServers": {
"tpmjs": {
"type": "url",
"url": "https://tpmjs.com/api/mcp/username/all-my-tools/http"
}
}
}
```
This single endpoint provides access to:
- All npm tools in the collection
- All remote MCP tools configured
- All local tools via connected bridge
---
## Implementation Phases
### Phase 1: Remote MCP Import (2-3 weeks)
**Goal**: Import tools from remote HTTP/SSE MCP servers
**Deliverables**:
1. `@tpmjs/mcp-client` package for connecting to MCP servers
2. UI for adding remote MCP server to collection
3. Updated MCP handlers to aggregate remote tools
4. Tool execution routing for remote servers
**No bridge needed** - works with any public HTTP MCP server.
### Phase 2: Bridge Foundation (3-4 weeks)
**Goal**: Enable local tool access via bridge
**Deliverables**:
1. `@tpmjs/bridge` CLI package
2. WebSocket API for bridge connections (`/api/bridge`)
3. Database schema for bridge connections
4. Bridge status UI in dashboard
### Phase 3: Tool Discovery & Sync (2 weeks)
**Goal**: Automatic tool discovery and sync
**Deliverables**:
1. Auto-discover tools when bridge connects
2. Sync tool definitions periodically
3. Handle schema changes gracefully
4. Tool health monitoring
### Phase 4: Advanced Features (Ongoing)
**Goal**: Enhanced reliability and UX
**Deliverables**:
1. Bridge auto-reconnection
2. Tool execution queuing
3. Offline tool caching
4. Multiple bridge support (different machines)
5. Browser extension alternative to CLI
---
## Summary
The MCP Aggregator transforms TPMJS from a tool registry into a **universal tool hub**:
| Feature | Before | After |
|---------|--------|-------|
| Tool Sources | npm only | npm + remote MCP + local MCP |
| MCP Servers | One per collection | One unified endpoint |
| Local Tools | Not possible | Via bridge |
| Chrome/Browser | Not possible | Via bridge |
| Configuration | Multiple MCP entries | Single TPMJS entry |
The hybrid approach (cloud + bridge) provides:
- **Always-on** npm and remote MCP tools
- **When-connected** local tools via bridge
- **Graceful degradation** when bridge is offline
- **Single point of management** for all tools
---
## References
- [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)
- [MCP Protocol Docs](https://modelcontextprotocol.io/docs)
- [Chrome DevTools MCP](https://github.com/anthropics/chrome-devtools-mcp)
- [Browser MCP](https://browsermcp.io/)
- [Claude in Chrome Docs](https://code.claude.com/docs/en/chrome)
- [Vercel AI SDK MCP](https://ai-sdk.dev/docs/ai-sdk-core/mcp-tools)

319
docs/MCP-BRIDGE-STATUS.md Normal file
View file

@ -0,0 +1,319 @@
# MCP Bridge Implementation Status
**Last Updated:** 2026-01-12
## Overview
The MCP Bridge system allows users to connect local MCP servers (like Chrome DevTools, file systems, or custom tools) to their TPMJS collections. Tools running on the user's machine can be accessed remotely through TPMJS.
## Architecture
```
┌─────────────────────────────────────────────────────────────────┐
│ User's Machine │
│ ┌─────────────┐ ┌─────────────────────────────────────┐ │
│ │ MCP Server │────▶│ @tpmjs/bridge CLI │ │
│ │ (stdio) │ │ - Connects to local MCP servers │ │
│ └─────────────┘ │ - Polls TPMJS for tool calls │ │
│ ┌─────────────┐ │ - Executes tools locally │ │
│ │ MCP Server │────▶│ - Returns results to TPMJS │ │
│ │ (stdio) │ └──────────────┬──────────────────────┘ │
│ └─────────────┘ │ │
└─────────────────────────────────────┼────────────────────────────┘
│ HTTP Polling
┌─────────────────────────────────────────────────────────────────┐
│ TPMJS Cloud │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────┐ │
│ │ /api/bridge │ │ MCP Handlers │ │ Database │ │
│ │ - Registration │◀──▶│ - tools/list │◀──▶│ - Bridge │ │
│ │ - Tool calls │ │ - tools/call │ │ Connection│ │
│ │ - Results │ │ │ │ - Bridge │ │
│ └─────────────────┘ └─────────────────┘ │ Tools │ │
│ └─────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
## Packages Created
### 1. `@tpmjs/mcp-client` (packages/mcp-client)
MCP client library for connecting to MCP servers.
**Features:**
- Connect to MCP servers via stdio transport
- Discover tools from servers
- Execute tool calls
- Manage multiple server connections
**Usage:**
```typescript
import { MCPClientManager } from '@tpmjs/mcp-client';
const manager = new MCPClientManager();
const tools = await manager.connect({
id: 'my-server',
name: 'My MCP Server',
transport: 'stdio',
command: 'node',
args: ['./server.js']
});
const result = await manager.callTool('my-server', 'toolName', { arg: 'value' });
await manager.disconnectAll();
```
### 2. `@tpmjs/bridge` (packages/bridge)
CLI for users to run on their local machine.
**Commands:**
```bash
tpmjs-bridge init # Create config file
tpmjs-bridge login # Authenticate with TPMJS
tpmjs-bridge logout # Remove credentials
tpmjs-bridge add <name> # Add an MCP server
tpmjs-bridge remove <name> # Remove an MCP server
tpmjs-bridge list # List configured servers
tpmjs-bridge config # Show config path
tpmjs-bridge start # Start the bridge
tpmjs-bridge status # Show bridge status
```
**Config file:** `~/.tpmjs/bridge.json`
```json
{
"servers": [
{
"id": "chrome-devtools",
"name": "Chrome DevTools",
"transport": "stdio",
"command": "npx",
"args": ["-y", "@anthropic/claude-in-chrome"]
}
]
}
```
### 3. `@tpmjs/test-file-writer` (packages/tools/test-file-writer)
Test MCP server for development and testing.
**Tools:**
- `write_file` - Write content to a file
- `read_file` - Read content from a file
- `list_files` - List all files
- `delete_file` - Delete a file
- `get_info` - Get server info
**Files stored in:** `~/.tpmjs/test-files/`
## Database Schema
### BridgeConnection
Tracks active bridge connections per user.
```prisma
model BridgeConnection {
id String @id @default(cuid())
userId String @unique @map("user_id")
user User @relation(...)
status String @default("disconnected") // 'connected' | 'disconnected'
socketId String? // Internal routing identifier
tools Json @default("[]") // Cached tool definitions
lastSeen DateTime?
clientVersion String?
clientOS String?
}
```
### CollectionBridgeTool
Links bridge tools to collections.
```prisma
model CollectionBridgeTool {
id String @id @default(cuid())
collectionId String
collection Collection @relation(...)
serverId String // e.g., "chrome-devtools"
toolName String // e.g., "screenshot"
displayName String? // Custom display name
note String? // User notes
}
```
## API Endpoints
### Bridge Communication API
**`POST /api/bridge`** - Bridge registration and tool results
```typescript
// Register bridge
{ type: 'register', tools: [...], clientVersion: '0.1.0', clientOS: 'darwin' }
// Submit tool result
{ type: 'result', callId: 'xxx', result: {...} }
// Submit tool error
{ type: 'result', callId: 'xxx', error: { message: '...' } }
// Heartbeat
{ type: 'heartbeat' }
```
**`GET /api/bridge`** - Poll for pending tool calls
```typescript
// Response
{ calls: [{ callId: 'xxx', serverId: 'chrome', toolName: 'screenshot', args: {} }] }
```
**`DELETE /api/bridge`** - Disconnect bridge
### User Bridge Status API
**`GET /api/user/bridge`** - Get bridge status for current user
```typescript
{
status: 'connected' | 'disconnected' | 'stale' | 'never_connected',
lastSeen: '2026-01-12T...',
clientVersion: '0.1.0',
clientOS: 'darwin',
toolCount: 5,
servers: [{ id: 'chrome', name: 'Chrome', toolCount: 3, tools: [...] }]
}
```
### Collection Bridge Tools API
**`GET /api/collections/[id]/bridge-tools`** - List bridge tools in collection
**`POST /api/collections/[id]/bridge-tools`** - Add bridge tool to collection
**`PATCH /api/collections/[id]/bridge-tools/[id]`** - Update bridge tool
**`DELETE /api/collections/[id]/bridge-tools/[id]`** - Remove bridge tool
## MCP Handler Integration
Bridge tools are included in MCP `tools/list` responses when:
1. The collection has bridge tools added
2. The collection owner has an active bridge connection
3. The bridge status is "connected"
Bridge tool names use the format: `bridge--{serverId}--{toolName}`
Example: `bridge--chrome-devtools--screenshot`
## UI
### Bridge Settings Page
Location: `/dashboard/settings/bridge`
Features:
- Shows connection status (connected/disconnected/stale/never_connected)
- Lists connected MCP servers and their tools
- Shows last seen time, client version, platform
- Quick start instructions for new users
### Navigation
Bridge link added to dashboard sidebar under "Bridge" with link icon.
## Testing Results
All components tested successfully:
| Component | Status | Notes |
|-----------|--------|-------|
| MCPClientManager | ✅ Pass | Connects, discovers tools, executes calls |
| Test File Writer | ✅ Pass | All 5 tools work correctly |
| Bridge CLI | ✅ Pass | All commands work |
| Bridge Class | ✅ Pass | Connects to servers, registers, polls |
| API Endpoints | ✅ Pass | Auth validation works |
| Type Check | ✅ Pass | No type errors |
| Lint | ✅ Pass | Only pre-existing warnings |
## What's Working
1. **Local MCP Server Connection** - Bridge connects to local MCP servers via stdio
2. **Tool Discovery** - Automatically discovers tools from connected servers
3. **Tool Execution** - Can execute tools and return results
4. **HTTP Polling** - Vercel-compatible polling instead of WebSocket
5. **Bridge CLI** - Full CLI with init, login, add, remove, start commands
6. **API Endpoints** - All endpoints implemented with proper auth
7. **Database Schema** - Bridge connections and collection tools stored
8. **MCP Integration** - Bridge tools included in MCP tools/list
9. **UI** - Bridge status page with connection info
## What Needs Manual Testing
1. **End-to-End Flow** - Requires logging in via web UI and getting a session token
2. **Real MCP Servers** - Test with Chrome DevTools, filesystem, etc.
3. **Tool Execution via MCP** - Call bridge tools through the MCP protocol
4. **Collection Integration** - Add bridge tools to collections via UI
## How to Test Locally
```bash
# 1. Build packages
pnpm --filter=@tpmjs/mcp-client build
pnpm --filter=@tpmjs/bridge build
pnpm --filter=@tpmjs/test-file-writer build
# 2. Initialize bridge config
node packages/bridge/dist/cli.js init
# 3. Add test server
node packages/bridge/dist/cli.js add test-file-writer \
--command "node" \
--args "$(pwd)/packages/tools/test-file-writer/dist/server.js"
# 4. Start dev server
pnpm --filter=@tpmjs/web dev
# 5. Log in via browser, get session token
# 6. Start bridge (with real token)
node packages/bridge/dist/cli.js start --token <session-token>
# 7. Visit /dashboard/settings/bridge to see status
```
## Future Improvements
- [ ] Proper API key authentication (not session tokens)
- [ ] Redis for tool call queuing (production)
- [ ] WebSocket support for lower latency
- [ ] Bridge tool UI in collection editor
- [ ] Tool call logging and debugging
- [ ] Rate limiting for bridge connections
- [ ] Multiple bridge instances per user
- [ ] Bridge health monitoring alerts
## Files Changed/Created
### New Packages
- `packages/mcp-client/` - MCP client library
- `packages/bridge/` - Bridge CLI
- `packages/tools/test-file-writer/` - Test MCP server
### Database
- `packages/db/prisma/schema.prisma` - Added BridgeConnection, CollectionBridgeTool
### API Routes
- `apps/web/src/app/api/bridge/route.ts` - Bridge API
- `apps/web/src/app/api/user/bridge/route.ts` - User bridge status
- `apps/web/src/app/api/collections/[id]/bridge-tools/route.ts` - Collection bridge tools
- `apps/web/src/app/api/collections/[id]/bridge-tools/[bridgeToolId]/route.ts` - Single bridge tool
### MCP Integration
- `apps/web/src/lib/mcp/handlers.ts` - Updated to include bridge tools
- `apps/web/src/lib/mcp/tool-converter.ts` - Added bridge tool conversion
- `apps/web/src/lib/mcp/index.ts` - Updated exports
### UI
- `apps/web/src/app/dashboard/settings/bridge/page.tsx` - Bridge status page
- `apps/web/src/components/dashboard/DashboardLayout.tsx` - Added Bridge nav link
### Types
- `packages/types/src/collection.ts` - Added bridge tool schemas

1298
docs/PRD-MCP-BRIDGE.md Normal file

File diff suppressed because it is too large Load diff

930
docs/TPMJS-ARCHITECTURE.md Normal file
View file

@ -0,0 +1,930 @@
# TPMJS: Tool Platform for Model Junctions
A comprehensive guide to how TPMJS works, its architecture, and strategies for handling local/computer-controlling tools in a remote execution environment.
---
## Table of Contents
1. [What is TPMJS?](#what-is-tpmjs)
2. [Core Architecture](#core-architecture)
3. [Tool Execution Flow](#tool-execution-flow)
4. [The Local Tool Challenge](#the-local-tool-challenge)
5. [Solution Strategies](#solution-strategies)
6. [Implementation Roadmap](#implementation-roadmap)
---
## What is TPMJS?
TPMJS (Tool Platform for Model Junctions) is an open platform for discovering, sharing, and executing AI agent tools. Think of it as "npm for AI tools" - developers publish tool packages to npm with a special `tpmjs` field, and the platform automatically discovers, catalogs, and makes them executable through AI agents.
### Key Capabilities
- **Tool Discovery**: Automatically syncs with npm to find packages with the `tpmjs` keyword
- **Tool Registry**: Catalogs tools with metadata, quality scores, and health checks
- **Agent Builder**: Create AI agents with custom tool collections
- **Remote Execution**: Execute npm package tools in isolated sandbox environments
- **Multi-Provider Support**: Works with OpenAI, Anthropic, Google, Groq, Mistral, and more
- **MCP Protocol Support**: Expose collections as MCP servers for use with Claude Desktop, etc.
### How Tools Get Published
Developers add a `tpmjs` field to their package.json:
```json
{
"name": "@company/my-tool",
"keywords": ["tpmjs"],
"tpmjs": {
"tools": {
"myTool": {
"description": "Does something useful",
"export": "myTool"
}
}
}
}
```
The platform discovers this via npm's changes feed and keyword search, validates the package, and adds it to the registry.
---
## Core Architecture
### System Components
```
┌─────────────────────────────────────────────────────────────────┐
│ TPMJS Platform │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ Web App │ │ Playground │ │ NPM Registry │ │
│ │ (Next.js) │ │ (Testing) │ │ (Package Source) │ │
│ └──────┬──────┘ └──────┬──────┘ └──────────┬──────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ API Layer ││
│ │ • /api/chat - Agent conversations ││
│ │ • /api/sync - NPM package discovery ││
│ │ • /api/agents - Agent CRUD ││
│ │ • /api/mcp - MCP protocol endpoints ││
│ └─────────────────────────────────────────────────────────────┘│
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ Tool Execution Layer ││
│ │ ││
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ ││
│ │ │ Sandbox │ │ Custom │ │ Local Executor │ ││
│ │ │ Executor │ │ Executor │ │ (Future) │ ││
│ │ │ (Default) │ │ (User URL) │ │ │ ││
│ │ └─────────────┘ └─────────────┘ └─────────────────────┘ ││
│ └─────────────────────────────────────────────────────────────┘│
│ │
└─────────────────────────────────────────────────────────────────┘
```
### Database Schema (Key Models)
```
Package (npm-level metadata)
├── Tool (individual tool within package)
│ ├── inputSchema (JSON Schema)
│ ├── health status (HEALTHY/BROKEN/UNKNOWN)
│ └── quality score
Agent (user-created AI agent)
├── Collections (grouped tools)
│ ├── CollectionTool (join table)
│ ├── executorConfig
│ └── envVars
├── Individual Tools
├── Conversations
│ └── Messages (USER/ASSISTANT/TOOL)
└── Configuration
├── provider, modelId
├── systemPrompt
├── executorType, executorConfig
└── envVars
```
### Executor Types
1. **Sandbox Executor (Default)**
- Remote service that loads npm packages dynamically
- Isolated execution environment
- 5-minute timeout per execution
- Supports environment variables
2. **Custom Executor**
- User-provided URL endpoint
- Optional API key authentication
- Same interface as sandbox executor
- Useful for private tools or specialized environments
3. **Configuration Cascade**
```
Agent Config → Collection Config → System Default
```
---
## Tool Execution Flow
### End-to-End Request Flow
```
User Message
┌─────────────────────────────────────────┐
│ Chat API Endpoint │
│ /api/chat/[user]/[agent]/conversation │
└────────────────┬────────────────────────┘
┌─────────────────────────────────────────┐
│ Agent Resolution │
│ • Fetch agent with collections/tools │
│ • Resolve executor config │
│ • Merge environment variables │
└────────────────┬────────────────────────┘
┌─────────────────────────────────────────┐
│ Build Tool Definitions │
│ • Convert TPMJS tools → AI SDK tools │
│ • Create execute functions with config │
│ • Inject env vars into executors │
└────────────────┬────────────────────────┘
┌─────────────────────────────────────────┐
│ AI Provider Stream │
│ • Stream text response │
│ • Intercept tool calls │
│ • Execute tools and stream results │
└────────────────┬────────────────────────┘
┌─────────────────────────────────────────┐
│ Tool Execution │
│ │
│ ┌─────────────────────────────────┐ │
│ │ executeWithExecutor() │ │
│ │ → resolveExecutorConfig() │ │
│ │ → executePackage() [sandbox] │ │
│ │ OR │ │
│ │ → executeWithCustomUrl() │ │
│ └─────────────────────────────────┘ │
└────────────────┬────────────────────────┘
┌─────────────────────────────────────────┐
│ Remote Sandbox Service │
│ • Dynamic import via esm.sh │
│ • Execute tool function │
│ • Return result │
└────────────────┬────────────────────────┘
┌─────────────────────────────────────────┐
│ Response & Persistence │
│ • Stream result to client (SSE) │
│ • Save messages to database │
│ • Track token usage │
└─────────────────────────────────────────┘
```
### SSE Event Types
```typescript
// During streaming, clients receive these events:
{ type: 'chunk', content: 'AI response text...' }
{ type: 'tool_call', toolCallId, toolName, args }
{ type: 'tool_result', toolCallId, toolName, result }
{ type: 'tokens', inputTokens, outputTokens }
{ type: 'complete' }
```
---
## The Local Tool Challenge
### The Problem
Many powerful AI tools require access to the user's local environment:
| Tool Type | Examples | Why Local? |
|-----------|----------|------------|
| **Browser Automation** | Chrome control, Puppeteer, Playwright | Needs access to user's browser, sessions, cookies |
| **File System** | Read/write local files | Operates on user's documents |
| **Desktop Automation** | Mouse/keyboard control, screenshots | Interacts with user's desktop |
| **Development Tools** | Git, terminal, IDE | Operates in user's dev environment |
| **System Utilities** | Clipboard, notifications, system settings | Requires OS-level access |
| **Database Access** | Local PostgreSQL, SQLite | Connects to local database servers |
### Current TPMJS Limitation
TPMJS executes tools in a **remote sandbox environment**:
```
User's Machine TPMJS Cloud
┌─────────────┐ ┌─────────────────────┐
│ │ │ │
│ Browser │ ──HTTP POST───▶ │ Sandbox Executor │
│ (Chat UI) │ │ (Isolated VM) │
│ │ │ │
│ Chrome │ │ ✗ No access to │
│ Files │ │ user's Chrome │
│ Desktop │ │ ✗ No access to │
│ │ │ user's files │
└─────────────┘ └─────────────────────┘
```
Tools that need local access simply **cannot work** in the remote sandbox because:
1. **No Network Path**: The sandbox cannot "reach back" to the user's machine
2. **Security Isolation**: Sandboxes are intentionally isolated for security
3. **Session State**: User's browser sessions, cookies, and auth state are local
4. **Hardware Access**: Screen, mouse, keyboard are local peripherals
### MCP: A Partial Solution
The **Model Context Protocol (MCP)** addresses this by running tools locally:
```
User's Machine
┌────────────────────────────────────────────────────┐
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Claude │ │ MCP Server │ │
│ │ Desktop │◀──▶│ (Local) │ │
│ └─────────────┘ └──────┬──────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ Chrome │ │
│ │ Files │ │
│ │ Desktop │ │
│ └─────────────┘ │
└────────────────────────────────────────────────────┘
```
**But MCP has limitations:**
- Only works with MCP-compatible clients (Claude Desktop, some IDEs)
- Cannot be used from web interfaces
- Requires manual server setup per user
- No centralized tool discovery/registry
---
## Solution Strategies
The goal is to enable local tool execution while maintaining TPMJS's web-based, shareable agent experience. Here are potential approaches:
### Strategy 1: Hybrid Executor Bridge
**Concept**: User runs a lightweight agent on their machine that bridges TPMJS to local tools.
```
User's Machine TPMJS Cloud
┌────────────────────────┐ ┌─────────────────────┐
│ │ │ │
│ ┌─────────────────┐ │ │ ┌───────────────┐ │
│ │ Local Bridge │◀─┼─WSS──▶│ │ TPMJS API │ │
│ │ Agent │ │ │ └───────────────┘ │
│ └────────┬────────┘ │ │ │
│ │ │ │ Tool execution │
│ ▼ │ │ request comes in │
│ ┌─────────────────┐ │ │ │ │
│ │ Local Tools │ │ │ ▼ │
│ │ • Chrome │ │ │ If local tool: │
│ │ • Files │ │ │ → Forward to │
│ │ • Desktop │ │ │ user's bridge │
│ └─────────────────┘ │ │ Else: │
│ │ │ → Use sandbox │
└────────────────────────┘ └─────────────────────┘
```
**Implementation Details:**
1. **Bridge Agent**:
- Electron app, CLI tool, or background service
- Maintains WebSocket connection to TPMJS
- Listens for tool execution requests
- Executes local tools and returns results
2. **Routing Logic**:
- Tools marked with `local: true` in metadata
- TPMJS routes these to user's connected bridge
- Falls back to remote execution for non-local tools
3. **Authentication**:
- Bridge authenticates with user's TPMJS API key
- Each bridge registered to specific user/agent
- Secure tunnel for sensitive operations
**Pros:**
- Works from web UI
- Mix of local and remote tools
- User controls what's exposed
**Cons:**
- Requires user to install/run software
- Bridge must stay connected
- Adds latency for local calls
---
### Strategy 2: Browser Extension with Native Messaging
**Concept**: Browser extension handles local tool execution via native messaging host.
```
Browser (TPMJS Chat)
┌─────────────────────────────────────────────────────┐
│ │
│ ┌─────────────┐ ┌─────────────────────────────┐│
│ │ TPMJS │ │ TPMJS Extension ││
│ │ Web App │◀──▶│ • Intercepts local calls ││
│ └─────────────┘ │ • Native messaging ││
│ └──────────────┬──────────────┘│
└────────────────────────────────────┼───────────────┘
┌────────────────▼───────────────┐
│ Native Messaging Host │
│ (Python/Node process) │
│ │
│ ┌─────────────────────────┐ │
│ │ Local Tool Executors │ │
│ │ • Puppeteer │ │
│ │ • File system │ │
│ │ • Shell commands │ │
│ └─────────────────────────┘ │
└────────────────────────────────┘
```
**Implementation Details:**
1. **Browser Extension**:
- Injects into TPMJS pages
- Intercepts tool execution for local-marked tools
- Communicates via native messaging
2. **Native Messaging Host**:
- Installed separately on user's machine
- Registered with browser for extension communication
- Executes actual local operations
3. **Tool Routing**:
- Extension registers available local tools
- TPMJS checks for local tool availability
- Routes appropriately
**Pros:**
- Seamless web experience
- No separate app window needed
- Browser handles connection management
**Cons:**
- Chrome/Firefox only (browser dependency)
- Complex installation (extension + native host)
- Native messaging has message size limits
---
### Strategy 3: Local-First with Cloud Sync
**Concept**: Run agent locally with cloud sync for sharing/collaboration.
```
User's Machine (Primary) TPMJS Cloud
┌──────────────────────────┐ ┌─────────────────────┐
│ │ │ │
│ ┌────────────────────┐ │ │ ┌───────────────┐ │
│ │ TPMJS Desktop │◀─┼──sync──▶│ │ Agent Config │ │
│ │ (Electron/Tauri) │ │ │ │ Conversations│ │
│ └────────┬───────────┘ │ │ │ Tool Registry│ │
│ │ │ │ └───────────────┘ │
│ ▼ │ │ │
│ ┌────────────────────┐ │ │ For sharing: │
│ │ Local Execution │ │ │ Expose via URL │
│ │ • All tools run │ │ │ with remote exec │
│ │ locally │ │ │ │
│ └────────────────────┘ │ │ │
└──────────────────────────┘ └─────────────────────┘
```
**Implementation Details:**
1. **Desktop Application**:
- Full TPMJS experience in native app
- All tool execution happens locally
- Syncs agent configs and conversations to cloud
2. **Sharing Mode**:
- Public agents can run from cloud
- Non-local tools execute remotely
- Local tools marked as "requires desktop app"
3. **Hybrid Operation**:
- Use web when away from main machine
- Use desktop for full local access
- Conversations sync between both
**Pros:**
- Full local access
- Works offline
- Best performance for local tools
**Cons:**
- Requires desktop app installation
- Sync complexity
- Different experience web vs desktop
---
### Strategy 4: Tunnel Service (ngrok-style)
**Concept**: User runs local executor and exposes it via secure tunnel.
```
User's Machine TPMJS Cloud
┌──────────────────────────┐ ┌─────────────────────────────┐
│ │ │ │
│ ┌────────────────────┐ │ │ ┌───────────────────────┐ │
│ │ Local Executor │ │ │ │ Tunnel Service │ │
│ │ + TPMJS Tunnel │──┼────▶│ │ user123.tpmjs.tunnel │ │
│ └────────┬───────────┘ │ │ └───────────┬───────────┘ │
│ │ │ │ │ │
│ ▼ │ │ ▼ │
│ ┌────────────────────┐ │ │ ┌───────────────────────┐ │
│ │ Local Resources │ │ │ │ Agent routes local │ │
│ │ • Chrome │ │ │ │ tools to tunnel URL │ │
│ │ • Files │ │ │ └───────────────────────┘ │
│ └────────────────────┘ │ │ │
└──────────────────────────┘ └─────────────────────────────┘
```
**Implementation Details:**
1. **Tunnel CLI**:
```bash
npx tpmjs-tunnel --port 3847 --token <api-key>
```
- Starts local executor service
- Connects to TPMJS tunnel service
- Gets assigned a unique tunnel URL
2. **Agent Configuration**:
- User sets executor type to "tunnel"
- TPMJS routes tool calls to their tunnel URL
- Tunnel forwards to local executor
3. **Security**:
- Authenticated tunnel connection
- HTTPS everywhere
- User can whitelist specific tools
**Pros:**
- Simple CLI-based setup
- Works with any tools
- User controls exposure
**Cons:**
- Tunnel must stay connected
- Potential latency
- Costs for tunnel infrastructure
---
### Strategy 5: WebRTC Peer Connection
**Concept**: Direct peer-to-peer connection between browser and local executor.
```
Browser (TPMJS Chat) User's Machine
┌─────────────────────────┐ ┌─────────────────────┐
│ │ │ │
│ TPMJS Web App │◀─WebRTC─▶│ Local Executor │
│ with WebRTC client │ │ with WebRTC server │
│ │ │ │
│ ┌───────────────────┐ │ │ ┌───────────────┐ │
│ │ Tool call comes in│ │ │ │ Execute local │ │
│ │ Check: is local? │ │ │ │ tool, return │ │
│ │ Yes → Send P2P │ │ │ │ result P2P │ │
│ │ No → Send cloud │ │ │ └───────────────┘ │
│ └───────────────────┘ │ │ │
└─────────────────────────┘ └─────────────────────┘
│ Signaling
┌─────────────────────────┐
│ TPMJS Signaling Server │
│ (Connection setup only)│
└─────────────────────────┘
```
**Implementation Details:**
1. **WebRTC Setup**:
- TPMJS provides signaling server
- Browser and local executor establish P2P connection
- Data channel for tool calls/results
2. **Local Executor**:
- Desktop app or CLI with WebRTC support
- Advertises available local tools
- Handles incoming tool calls
3. **Connection Flow**:
- User opens TPMJS, local executor connects
- Signaling exchanges connection info
- Direct P2P connection established
- Tool calls bypass cloud entirely
**Pros:**
- Very low latency
- No tunnel infrastructure needed
- Direct, secure connection
**Cons:**
- WebRTC complexity (NAT traversal)
- May not work on all networks
- Both ends need WebRTC support
---
### Strategy 6: Container-Based Local Executor
**Concept**: User runs a Docker container that connects to TPMJS.
```bash
docker run -v /home:/home \
-e TPMJS_API_KEY=xxx \
ghcr.io/tpmjs/local-executor
```
```
User's Machine (Docker) TPMJS Cloud
┌────────────────────────────┐ ┌─────────────────────┐
│ │ │ │
│ ┌──────────────────────┐ │ │ ┌───────────────┐ │
│ │ TPMJS Container │◀─┼─WSS─▶│ │ TPMJS API │ │
│ │ • Pre-installed │ │ │ └───────────────┘ │
│ │ tools │ │ │ │
│ │ • Mount user dirs │ │ │ Routes local tools │
│ └──────────┬───────────┘ │ │ to container │
│ │ │ │ │
│ ┌──────────▼───────────┐ │ │ │
│ │ Mounted Volumes │ │ │ │
│ │ • /home (files) │ │ │ │
│ │ • /var/run/docker │ │ │ │
│ │ (nested Docker) │ │ │ │
│ └──────────────────────┘ │ │ │
└────────────────────────────┘ └─────────────────────┘
```
**Implementation Details:**
1. **Container Image**:
- Pre-installed common tools (Puppeteer, etc.)
- WebSocket client to TPMJS
- Configurable volume mounts
2. **Tool Execution**:
- Container receives tool calls via WebSocket
- Executes with access to mounted volumes
- Returns results
3. **Browser Automation**:
- Container could run headless Chrome
- Or use browser running on host via port mapping
- VNC for visual debugging
**Pros:**
- Consistent environment
- Easy distribution via Docker Hub
- Isolated yet with controlled access
**Cons:**
- Docker dependency
- Limited GUI access
- Complex browser automation setup
---
### Strategy 7: Agent-to-Agent Delegation
**Concept**: Cloud agent delegates local tasks to user's local agent.
```
TPMJS Cloud User's Machine
┌─────────────────────────────┐ ┌─────────────────────────┐
│ │ │ │
│ ┌───────────────────────┐ │ │ ┌───────────────────┐ │
│ │ Cloud Agent │ │ │ │ Local Agent │ │
│ │ (Primary) │ │ │ │ (MCP Server) │ │
│ │ │ │ │ │ │ │
│ │ When local tool │──┼────▶│ │ Receives task, │ │
│ │ needed, delegate to │ │ │ │ executes locally │ │
│ │ local agent │◀─┼─────│ │ returns result │ │
│ └───────────────────────┘ │ │ └───────────────────┘ │
│ │ │ │ │
│ │ │ ▼ │
│ │ │ ┌───────────────────┐ │
│ │ │ │ Chrome, Files │ │
│ │ │ └───────────────────┘ │
└─────────────────────────────┘ └─────────────────────────┘
```
**Implementation Details:**
1. **Local Agent**:
- Runs as MCP server
- Connected to cloud agent via tool
- Advertises local capabilities
2. **Delegation Tool**:
```typescript
delegateToLocal({
task: "Take a screenshot of the current page",
context: { ... }
})
```
3. **Execution Flow**:
- Cloud agent determines task needs local access
- Uses delegation tool to send to local agent
- Local agent executes and returns result
- Cloud agent incorporates result
**Pros:**
- Clean separation of concerns
- Cloud agent coordinates, local executes
- Scales well conceptually
**Cons:**
- Adds complexity (two agents)
- Potential context loss between agents
- Requires sophisticated delegation logic
---
### Strategy 8: Progressive Enhancement
**Concept**: Same tools work in cloud (limited) and local (full), with graceful degradation.
```typescript
// Tool definition with progressive capability
{
name: "readFile",
capabilities: {
remote: {
description: "Read files from sandboxed storage",
restrictions: ["sandbox-only", "size-limit-1mb"]
},
local: {
description: "Read any accessible file",
restrictions: []
}
},
execute: async (input, context) => {
if (context.isLocal) {
return fs.readFile(input.path);
} else {
return sandboxFs.readFile(input.sandboxPath);
}
}
}
```
**Implementation Details:**
1. **Tool Metadata**:
- Tools declare remote and local capabilities
- Different restrictions per environment
- Same function name, different behaviors
2. **UI Indication**:
- Show which capabilities are available
- Prompt user to connect local executor for full access
- Graceful fallback to remote when local unavailable
3. **Runtime Detection**:
- Check for local executor connection
- Route to appropriate implementation
- Surface limitations in tool output
**Pros:**
- Works everywhere, better locally
- Clear capability communication
- No hard failures
**Cons:**
- Dual implementation complexity
- User confusion about capabilities
- Tool authors must handle both cases
---
### Strategy 9: Cloudflare Workers + Durable Objects
**Concept**: Edge execution with persistent state, user provides API access.
```
User configures API credentials
┌─────────────────────────────────────────────────────┐
│ Cloudflare Edge │
│ │
│ ┌────────────────────────────────────────────────┐ │
│ │ Durable Object (per user) │ │
│ │ • Persistent WebSocket to user services │ │
│ │ • Cached credentials (encrypted) │ │
│ │ • Session state for browser automation │ │
│ └────────────────────────────────────────────────┘ │
│ │ │
│ ┌──────────────┴──────────────┐ │
│ ▼ ▼ │
│ ┌────────────┐ ┌────────────────────┐ │
│ │ Tool Exec │ │ Browser (remote) │ │
│ │ (fast) │ │ via Browserless.io │ │
│ └────────────┘ └────────────────────┘ │
└─────────────────────────────────────────────────────┘
User's configured services
(if they have public APIs)
```
**Implementation Details:**
1. **Edge Functions**:
- Execute tools at edge, close to user
- Durable Objects maintain state
- Low latency for most operations
2. **Remote Browser Services**:
- Integrate with Browserless, Browserbase, etc.
- User provides API keys for these services
- Browser runs "close enough" to cloud
3. **User's Services**:
- If user has self-hosted services with APIs
- Configure credentials in TPMJS
- Edge function calls user's services
**Pros:**
- Low latency edge execution
- No local installation required
- Scales with Cloudflare infrastructure
**Cons:**
- Still remote execution
- Requires paid browser services
- Not truly local access
---
### Strategy 10: Sandboxed Local VM
**Concept**: TPMJS provisions a secure VM on user's machine.
```
User's Machine
┌──────────────────────────────────────────────────────┐
│ │
│ Host OS │
│ ┌────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ┌──────────────────────────────────────────┐ │ │
│ │ │ TPMJS Sandbox VM │ │ │
│ │ │ (Firecracker/gVisor/WASM) │ │ │
│ │ │ │ │ │
│ │ │ • Controlled network access │ │ │
│ │ │ • Mounted specific directories │ │ │
│ │ │ • Pre-approved tools only │ │ │
│ │ │ • Resource limits (CPU, RAM, time) │ │ │
│ │ └──────────────────────────────────────────┘ │ │
│ │ │ │
│ │ User approves: │ │
│ │ ✓ Mount ~/Documents (read-only) │ │
│ │ ✓ Allow outbound HTTPS │ │
│ │ ✗ Deny keylogger access │ │
│ │ │ │
│ └────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────┘
```
**Implementation Details:**
1. **Micro-VM Technology**:
- Firecracker for lightweight VMs
- gVisor for container sandboxing
- WebAssembly for in-browser sandboxing
2. **Capability-Based Security**:
- User explicitly grants permissions
- File system mounts with restrictions
- Network access whitelisting
- Hardware access controls
3. **Tool Verification**:
- Only signed/verified tools can run
- Code review for local-capable tools
- Sandboxed execution even locally
**Pros:**
- Security through isolation
- Fine-grained permissions
- Local but controlled
**Cons:**
- Complex implementation
- Performance overhead
- Still limited vs native access
---
## Implementation Roadmap
Based on complexity, impact, and user experience, here's a suggested prioritization:
### Phase 1: Foundation (Weeks 1-4)
**Strategy 4: Tunnel Service**
- Lowest friction entry point
- Works with existing TPMJS architecture
- Users already familiar with ngrok-style tools
**Deliverables:**
1. `tpmjs-tunnel` CLI package
2. Tunnel relay service on tpmjs.com
3. Agent executor type "tunnel"
4. Documentation and getting started guide
### Phase 2: Better UX (Weeks 5-8)
**Strategy 2: Browser Extension**
- Eliminates CLI requirement for web users
- Seamless web experience
- Works on any platform with Chrome/Firefox
**Deliverables:**
1. TPMJS Browser Extension
2. Native messaging host installer
3. Local tool capability detection
4. Extension distribution (Chrome Web Store, Firefox Add-ons)
### Phase 3: Power Users (Weeks 9-12)
**Strategy 3: Local-First Desktop App**
- Full power for power users
- Offline support
- Best performance
**Deliverables:**
1. TPMJS Desktop (Electron or Tauri)
2. Sync protocol for agents/conversations
3. Hybrid mode (web fallback)
### Phase 4: Advanced (Future)
**Strategy 8: Progressive Enhancement**
- Make existing tools smarter
- Better capability communication
- Graceful degradation
**Strategy 6: Container-Based Executor**
- For DevOps/engineer users
- Reproducible environments
- CI/CD integration
---
## Summary
TPMJS's remote execution model works well for stateless, API-based tools but faces challenges with local/computer-controlling tools. The solution isn't one-size-fits-all:
| User Type | Best Strategy | Why |
|-----------|---------------|-----|
| **Casual User** | Browser Extension | No CLI, just install extension |
| **Developer** | Tunnel Service | Familiar CLI workflow |
| **Power User** | Desktop App | Full control, best performance |
| **Enterprise** | Container + Custom Executor | Controlled, auditable |
The key insight is that **local execution isn't a single feature but a spectrum** of approaches, each with different trade-offs between:
- Ease of setup
- Security
- Performance
- Capability breadth
TPMJS should support multiple approaches, letting users choose based on their needs and comfort level.