Files
mcpctl/.taskmaster/tasks/task_028.md
Michal a869f77070 merge: AgentProject membership — the junction is now load-bearing
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
2026-08-09 23:34:18 +01:00

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

  1. Create new route file: src/mcpd/src/routes/mcp-proxy.ts
  2. Define request schema:
    interface McpProxyRequest {
      serverId: string;  // or instanceId
      method: string;    // e.g., 'tools/call', 'resources/read'
      params: Record<string, unknown>;
    }
    
  3. 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
  4. 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;
    }
    
  5. Connection pooling: maintain persistent connections to running instances
  6. Add route: POST /api/v1/mcp/proxy (must be behind auth middleware)
  7. Add audit logging for all MCP proxy calls - include authenticated userId from request.userId
  8. Handle errors: instance not found, instance not running, MCP call failed

Authentication Endpoints

  1. Create auth routes file: src/mcpd/src/routes/auth.ts
  2. 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 }
  3. 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.ts validates Bearer tokens against Session table
  • It sets request.userId on 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:

  1. Unit test: Proxy service looks up correct instance
  2. Unit test: JSON-RPC call is formatted correctly
  3. Integration test: Full flow with a mock MCP server container
  4. Test error handling: non-existent server returns 404
  5. Test error handling: stopped instance returns appropriate error
  6. Test audit log entries include authenticated userId
  7. Test connection reuse for multiple calls to same instance
  8. Test login endpoint: valid credentials return session token
  9. Test login endpoint: invalid credentials return 401
  10. Test logout endpoint: valid session is invalidated
  11. Test logout endpoint: invalid/missing token returns 401
  12. Test MCP proxy endpoint without auth token returns 401
  13. Test MCP proxy endpoint with expired token returns 401
  14. 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.