Merges feat/agent-project-membership (task 166/152). An agent can finally join many projects: membership routes, per-membership role and optional brain override, chat resolving context from the membership, and CLI attach-agent / detach-agent / get memberships. The legacy Agent.projectId fallback is kept for this release as the schema comment promises. Conflict resolved in main.ts: that branch added its RBAC case to mapUrlToPermission inline, while this branch had extracted the function to middleware/url-permission.ts so it could be tested. Kept the extraction and ported the case into it — joining a team is `edit:agents:<name>`, not `create:agents`, which is the authority to mint a whole agent. Also fixes something the extraction makes possible. security.test.ts contained a hand-copied reproduction of mapUrlToPermission, because main.ts self-invokes main() and nothing in it was importable. A security test asserting against a copy stops testing the system the moment the real one changes, and this copy had already drifted. It now imports the real function; all 18 assertions still pass, against the code that actually runs. Local: build clean, workspace 2550 passed, lint unchanged at 794 errors. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N4wNHWf7xSwnZCWpJcyv9p
6.2 KiB
Task ID: 28
Title: Add MCP proxy endpoint to mcpd
Status: done
Dependencies: None
Priority: high
Description: Create a new endpoint in mcpd at /api/v1/mcp/proxy that accepts MCP tool call requests and executes them on managed MCP server instances. Also add authentication endpoints (login/logout) that mcpctl will use to authenticate users.
Details:
MCP Proxy Endpoint
- Create new route file:
src/mcpd/src/routes/mcp-proxy.ts - Define request schema:
interface McpProxyRequest { serverId: string; // or instanceId method: string; // e.g., 'tools/call', 'resources/read' params: Record<string, unknown>; } - Create McpProxyService in
src/mcpd/src/services/mcp-proxy-service.ts:- Look up instance by serverId (auto-start if profile allows)
- Connect to the container via stdio or HTTP (depending on transport type)
- Execute the MCP JSON-RPC call
- Return the result
- Handle MCP JSON-RPC protocol:
async executeCall(instanceId: string, method: string, params: unknown) { const instance = await this.instanceService.getInstance(instanceId); const connection = await this.getOrCreateConnection(instance); const result = await connection.call(method, params); return result; } - Connection pooling: maintain persistent connections to running instances
- Add route:
POST /api/v1/mcp/proxy(must be behind auth middleware) - Add audit logging for all MCP proxy calls - include authenticated userId from request.userId
- Handle errors: instance not found, instance not running, MCP call failed
Authentication Endpoints
- Create auth routes file:
src/mcpd/src/routes/auth.ts - Implement
POST /api/v1/auth/login:- Request body:
{ username: string, password: string } - Validate credentials against User table (use bcrypt for password comparison)
- Create new Session record with token (use crypto.randomUUID or similar)
- Response:
{ token: string, expiresAt: string }
- Request body:
- Implement
POST /api/v1/auth/logout:- Requires Bearer token in Authorization header
- Delete/invalidate the Session record
- Response:
{ success: true }
Auth Integration Notes
- Existing auth middleware in
src/mcpd/src/middleware/auth.tsvalidates Bearer tokens against Session table - It sets
request.userIdon successful authentication - MCP proxy endpoint MUST use this auth middleware
- Auth endpoints (login) should NOT require auth middleware
- Logout endpoint SHOULD require auth middleware to validate the session being invalidated
Test Strategy:
- Unit test: Proxy service looks up correct instance
- Unit test: JSON-RPC call is formatted correctly
- Integration test: Full flow with a mock MCP server container
- Test error handling: non-existent server returns 404
- Test error handling: stopped instance returns appropriate error
- Test audit log entries include authenticated userId
- Test connection reuse for multiple calls to same instance
- Test login endpoint: valid credentials return session token
- Test login endpoint: invalid credentials return 401
- Test logout endpoint: valid session is invalidated
- Test logout endpoint: invalid/missing token returns 401
- Test MCP proxy endpoint without auth token returns 401
- Test MCP proxy endpoint with expired token returns 401
- Test MCP proxy endpoint with valid token succeeds and logs userId in audit
Subtasks
28.1. Create auth routes with login/logout endpoints
Status: pending
Dependencies: None
Create src/mcpd/src/routes/auth.ts with POST /api/v1/auth/login and POST /api/v1/auth/logout endpoints for mcpctl authentication.
Details:
Implement login endpoint: validate username/password against User table using bcrypt, create Session record with generated token and expiry. Implement logout endpoint: require auth middleware, delete/invalidate Session record. Login does NOT require auth, logout DOES require auth. Export registerAuthRoutes function and update routes/index.ts.
28.2. Create MCP proxy route file with auth middleware
Status: pending
Dependencies: 28.1
Create src/mcpd/src/routes/mcp-proxy.ts with POST /api/v1/mcp/proxy endpoint protected by auth middleware.
Details:
Define McpProxyRequest interface (serverId, method, params). Register route handler that extracts userId from request.userId (set by auth middleware). Apply auth middleware using preHandler hook. Validate request body schema.
28.3. Create McpProxyService for instance lookup and connection
Status: pending
Dependencies: None
Create src/mcpd/src/services/mcp-proxy-service.ts to handle instance lookup, connection management, and MCP call execution.
Details:
Implement getInstance to look up by serverId, auto-start if profile allows. Implement getOrCreateConnection for connection pooling. Handle both stdio and HTTP transports. Implement executeCall method that formats JSON-RPC call and returns result.
28.4. Implement MCP JSON-RPC call execution
Status: pending
Dependencies: 28.3
Implement the core JSON-RPC call logic in McpProxyService to execute tool calls on MCP server instances.
Details:
Format JSON-RPC 2.0 request with method and params. Send request over established connection (stdio/HTTP). Parse JSON-RPC response and handle errors. Return result or throw appropriate error for failed calls.
28.5. Add audit logging with userId for MCP proxy calls
Status: pending
Dependencies: 28.2, 28.4
Ensure all MCP proxy calls are logged to audit log including the authenticated userId from the session.
Details:
Use existing audit middleware/service. Include userId from request.userId in audit log entry. Log serverId, method, and outcome (success/failure). Log any errors that occur during MCP call execution.
28.6. Integrate auth and proxy routes into server.ts
Status: pending
Dependencies: 28.1, 28.2, 28.5
Register the new auth and mcp-proxy routes in the Fastify server with proper auth middleware wiring.
Details:
Update server.ts to register auth routes (no auth required for login). Register mcp-proxy routes with auth middleware. Ensure auth middleware is wired with findSession dependency from Prisma. Update routes/index.ts exports.