# Task ID: 27 **Title:** Implement mcplocal management proxy routes **Status:** done **Dependencies:** 26 ✓ **Priority:** high **Description:** Add REST endpoints to mcplocal that mirror mcpd's API and proxy management requests to mcpd without LLM processing. All requests must include proper authentication to mcpd using a Bearer token read from mcplocal config. **Details:** 1. Create HTTP client for mcpd communication with auth: `src/local-proxy/src/http/mcpd-client.ts` ```typescript export class McpdClient { private token: string; constructor(private baseUrl: string, token: string) { this.token = token; } private getHeaders(): Record { return { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.token}` }; } async get(path: string): Promise { const response = await fetch(`${this.baseUrl}${path}`, { method: 'GET', headers: this.getHeaders() }); await this.handleAuthError(response); return response.json(); } async post(path: string, body: unknown): Promise { const response = await fetch(`${this.baseUrl}${path}`, { method: 'POST', headers: this.getHeaders(), body: JSON.stringify(body) }); await this.handleAuthError(response); return response.json(); } async put(path: string, body: unknown): Promise { const response = await fetch(`${this.baseUrl}${path}`, { method: 'PUT', headers: this.getHeaders(), body: JSON.stringify(body) }); await this.handleAuthError(response); return response.json(); } async delete(path: string): Promise { const response = await fetch(`${this.baseUrl}${path}`, { method: 'DELETE', headers: this.getHeaders() }); await this.handleAuthError(response); return response.json(); } private async handleAuthError(response: Response): Promise { if (response.status === 401) { throw new AuthenticationError('Invalid or expired token. Please check mcplocal config.'); } } } export class AuthenticationError extends Error { constructor(message: string) { super(message); this.name = 'AuthenticationError'; } } ``` 2. Add token to mcplocal config type (extend ProxyConfig or similar): ```typescript export interface McpdAuthConfig { /** Bearer token for mcpd API authentication */ mcpdToken: string; } ``` 3. Create proxy routes in `src/local-proxy/src/http/routes/`: - `servers.ts`: GET/POST /api/v1/servers, GET/PUT/DELETE /api/v1/servers/:id - `profiles.ts`: GET/POST /api/v1/profiles, GET/PUT/DELETE /api/v1/profiles/:id - `instances.ts`: GET/POST /api/v1/instances, GET/POST/DELETE /api/v1/instances/:id, etc. - `projects.ts`: GET/POST /api/v1/projects, etc. - `audit.ts`: GET /api/v1/audit-logs - `backup.ts`: POST /api/v1/backup, POST /api/v1/restore 4. Each route handler forwards to mcpd with auth: ```typescript app.get('/api/v1/servers', async (req, reply) => { try { const result = await mcpdClient.get('/api/v1/servers'); return result; } catch (error) { if (error instanceof AuthenticationError) { return reply.status(401).send({ error: error.message }); } throw error; } }); ``` 5. Add comprehensive error handling: - If mcpd is unreachable, return 503 Service Unavailable - If mcpd returns 401, return 401 with clear message about token configuration - Forward other HTTP errors from mcpd with appropriate status codes 6. Add request/response logging for debugging **Test Strategy:** 1. Unit test: McpdClient attaches Authorization header to all request methods (GET, POST, PUT, DELETE) 2. Unit test: McpdClient throws AuthenticationError on 401 response from mcpd 3. Unit test: Each proxy route forwards requests correctly with auth headers 4. Unit test: Error handling when mcpd is unreachable (503 response) 5. Unit test: Error handling when mcpd returns 401 (clear error message returned) 6. Integration test: Full request flow mcpctl -> mcplocal -> mcpd with valid token 7. Integration test: Full request flow with invalid token returns 401 8. Test query parameters are forwarded correctly 9. Test request body is forwarded correctly for POST/PUT 10. Test path parameters (:id) are passed through correctly 11. Mock mcpd responses and verify mcplocal returns them unchanged 12. Test token is read correctly from mcplocal config