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.8 KiB
Task ID: 33
Title: Update mcpctl to use mcplocal as daemon
Status: done
Dependencies: 27 ✓
Priority: high
Description: Modify mcpctl CLI to connect to mcplocal instead of mcpd directly, update configuration options, add dual connectivity status checking, and implement authentication commands (login/logout) with secure credential storage.
Details:
- Update
src/cli/src/config/schema.ts:export interface McpctlConfig { mcplocalUrl: string; // NEW: default 'http://localhost:3200' mcpdUrl: string; // Keep for reference/direct access if needed // ... other fields } - Update
src/cli/src/config/defaults.ts:- Change default daemonUrl to http://localhost:3200 (mcplocal)
- Update
src/cli/src/api-client.ts:- Default baseUrl now points to mcplocal
- Add new config commands in
src/cli/src/commands/config.ts:.command('set-mcplocal-url <url>') .command('set-mcpd-url <url>') .command('get-mcplocal-url') .command('get-mcpd-url') - Update
src/cli/src/commands/status.tsto show both connections and auth status:$ mcpctl status mcplocal: connected (localhost:3200) mcpd: connected (nas.local:3100) via mcplocal Auth: logged in as user@example.com LLM Provider: ollama (llama3.2) Token savings: 45% (last 24h) - Update CLI --daemon-url flag to point to mcplocal
- Add --direct flag to bypass mcplocal and talk to mcpd directly (for debugging)
- Create
src/cli/src/commands/auth.tswith login/logout commands:mcpctl login: Prompt for mcpd URL (if not configured) and credentials- Call POST /api/v1/auth/login with { email, password }
- Store session token in ~/.mcpctl/credentials with 0600 permissions
mcpctl logout: Invalidate session and delete stored token
- Create
src/cli/src/auth/credentials.tsfor secure token storage:- Use fs.chmod to set 0600 permissions on credentials file
- Token format: { token: string, mcpdUrl: string, user: string, expiresAt?: string }
- Update api-client.ts to include stored token in requests to mcplocal
- mcplocal passes this token to mcpd for authentication
Test Strategy:
- Unit test: Default config points to mcplocal URL
- Unit test: Config commands update correct fields
- Integration test: CLI commands work through mcplocal proxy
- Test status command shows both mcplocal and mcpd status
- Test --direct flag bypasses mcplocal
- Test backward compatibility with existing config files
- Unit test: login command stores token with correct permissions (0600)
- Unit test: logout command removes credentials file
- Integration test: login flow with POST /api/v1/auth/login
- Test status command shows auth status (logged in as user)
- Test token is passed to mcplocal in API requests
- Test invalid credentials return appropriate error message
- Test expired token handling
Subtasks
33.1. Update config schema for mcplocal and mcpd URLs
Status: pending
Dependencies: None
Modify McpctlConfigSchema in src/cli/src/config/schema.ts to include separate mcplocalUrl and mcpdUrl fields with appropriate defaults.
Details:
Update the Zod schema to add mcplocalUrl (default: http://localhost:3200) and mcpdUrl (default: http://localhost:3100). Update DEFAULT_CONFIG and ensure backward compatibility with existing daemonUrl field by mapping it to mcplocalUrl.
33.2. Create auth credentials storage module
Status: pending
Dependencies: None
Create src/cli/src/auth/credentials.ts to handle secure storage and retrieval of session tokens in ~/.mcpctl/credentials.
Details:
Implement saveCredentials(token, mcpdUrl, user), loadCredentials(), and deleteCredentials() functions. Use fs.chmod to set 0600 permissions. Store JSON format: { token, mcpdUrl, user, expiresAt }. Handle file not found gracefully in loadCredentials.
33.3. Implement login command
Status: pending
Dependencies: 33.2
Create src/cli/src/commands/auth.ts with mcpctl login command that prompts for mcpd URL and credentials, calls POST /api/v1/auth/login, and stores the session token.
Details:
Use inquirer or prompts library for interactive credential input (email, password). If mcpdUrl not configured, prompt for it. Call POST /api/v1/auth/login with credentials. On success, save token using credentials module. Display 'Logged in as {user}' on success. Handle errors (invalid credentials, network errors) with clear messages.
33.4. Implement logout command
Status: pending
Dependencies: 33.2
Add mcpctl logout command to auth.ts that invalidates the session and removes stored credentials.
Details:
Load stored credentials, optionally call a logout endpoint on mcpd to invalidate server-side session, then delete the local credentials file. Display 'Logged out successfully' or 'Not logged in' as appropriate.
33.5. Update api-client to include auth token
Status: pending
Dependencies: 33.2
Modify src/cli/src/api-client.ts to load and include stored session token in Authorization header for requests to mcplocal.
Details:
Import loadCredentials from auth module. Add Authorization: Bearer {token} header to requests when credentials exist. Handle expired token by returning appropriate error suggesting re-login.
33.6. Update status command to show auth status
Status: pending
Dependencies: 33.2, 33.5
Modify src/cli/src/commands/status.ts to display authentication status (logged in as user X or not logged in) along with mcplocal and mcpd connectivity.
Details:
Load credentials and display auth status line: 'Auth: logged in as {user}' or 'Auth: not logged in'. Update status output format to show mcplocal and mcpd status separately with the auth info.
33.7. Add config commands for mcplocal and mcpd URLs
Status: pending
Dependencies: 33.1
Add set-mcplocal-url, set-mcpd-url, get-mcplocal-url, and get-mcpd-url commands to src/cli/src/commands/config.ts.
Details:
Add four new subcommands to the config command for setting and getting the mcplocal and mcpd URLs independently. Update the generic 'set' command to handle these new schema fields.
33.8. Add --direct flag for mcpd bypass
Status: pending
Dependencies: 33.1, 33.5
Add --direct flag to CLI commands that bypasses mcplocal and connects directly to mcpd for debugging purposes.
Details:
Add global --direct option to the main CLI. When set, api-client uses mcpdUrl instead of mcplocalUrl. Useful for debugging connectivity issues between mcplocal and mcpd.
33.9. Register auth commands in CLI entry point
Status: pending
Dependencies: 33.3, 33.4
Import and register the login and logout commands in src/cli/src/index.ts.
Details:
Import createAuthCommand from commands/auth.ts and add it to the main program with program.addCommand(createAuthCommand()).