feat: remove ProjectMember, add expose RBAC role, attach/detach-server commands
Some checks failed
CI / lint (pull_request) Has been cancelled
CI / typecheck (pull_request) Has been cancelled
CI / test (pull_request) Has been cancelled
CI / build (pull_request) Has been cancelled
CI / package (pull_request) Has been cancelled

- Remove ProjectMember model entirely (RBAC manages project access)
- Add 'expose' RBAC role for /mcp-config endpoint access (edit implies expose)
- Rename CLI flags: --llm-provider → --proxy-mode-llm-provider, --llm-model → --proxy-mode-llm-model
- Add attach-server / detach-server CLI commands (mcpctl --project NAME attach-server SERVER)
- Add POST/DELETE /api/v1/projects/:id/servers endpoints for server attach/detach
- Remove members from backup/restore, apply, get, describe
- Prisma migration to drop ProjectMember table

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Michal
2026-02-23 17:50:01 +00:00
parent 5844d6c73f
commit 783cf15179
23 changed files with 283 additions and 219 deletions

View File

@@ -30,8 +30,8 @@ describe('project with new fields', () => {
'project', 'smart-home',
'-d', 'Smart home project',
'--proxy-mode', 'filtered',
'--llm-provider', 'gemini-cli',
'--llm-model', 'gemini-2.0-flash',
'--proxy-mode-llm-provider', 'gemini-cli',
'--proxy-mode-llm-model', 'gemini-2.0-flash',
'--server', 'my-grafana',
'--server', 'my-ha',
], { from: 'user' });
@@ -46,20 +46,6 @@ describe('project with new fields', () => {
}));
});
it('creates project with members', async () => {
const cmd = createCreateCommand({ client, log });
await cmd.parseAsync([
'project', 'team-project',
'--member', 'alice@test.com',
'--member', 'bob@test.com',
], { from: 'user' });
expect(client.post).toHaveBeenCalledWith('/api/v1/projects', expect.objectContaining({
name: 'team-project',
members: ['alice@test.com', 'bob@test.com'],
}));
});
it('defaults proxy mode to direct', async () => {
const cmd = createCreateCommand({ client, log });
await cmd.parseAsync(['project', 'basic'], { from: 'user' });
@@ -71,7 +57,7 @@ describe('project with new fields', () => {
});
describe('get projects shows new columns', () => {
it('shows MODE, SERVERS, MEMBERS columns', async () => {
it('shows MODE and SERVERS columns', async () => {
const deps = {
output: [] as string[],
fetchResource: vi.fn(async () => [{
@@ -81,7 +67,6 @@ describe('project with new fields', () => {
proxyMode: 'filtered',
ownerId: 'user-1',
servers: [{ server: { name: 'grafana' } }, { server: { name: 'ha' } }],
members: [{ user: { email: 'alice@test.com' } }],
}]),
log: (...args: string[]) => deps.output.push(args.join(' ')),
};
@@ -91,13 +76,12 @@ describe('project with new fields', () => {
const text = deps.output.join('\n');
expect(text).toContain('MODE');
expect(text).toContain('SERVERS');
expect(text).toContain('MEMBERS');
expect(text).toContain('smart-home');
});
});
describe('describe project shows full detail', () => {
it('shows servers and members', async () => {
it('shows servers and proxy config', async () => {
const deps = {
output: [] as string[],
client: mockClient(),
@@ -113,10 +97,6 @@ describe('project with new fields', () => {
{ server: { name: 'my-grafana' } },
{ server: { name: 'my-ha' } },
],
members: [
{ user: { email: 'alice@test.com' } },
{ user: { email: 'bob@test.com' } },
],
createdAt: '2025-01-01',
updatedAt: '2025-01-01',
})),
@@ -131,8 +111,6 @@ describe('project with new fields', () => {
expect(text).toContain('gemini-cli');
expect(text).toContain('my-grafana');
expect(text).toContain('my-ha');
expect(text).toContain('alice@test.com');
expect(text).toContain('bob@test.com');
});
});
});