Skip to content

Commit 0c6a11d

Browse files
authored
Merge pull request #19 from supabase-community/chore/remove-enable
chore: remove enable branching tool
2 parents 0535994 + fbf3c7a commit 0c6a11d

File tree

4 files changed

+45
-242
lines changed

4 files changed

+45
-242
lines changed

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,8 @@ The following Supabase tools are available to the LLM:
141141
- `get_project_url`: Gets the API URL for a project.
142142
- `get_anon_key`: Gets the anonymous API key for a project.
143143

144-
#### Branching (Experimental)
144+
#### Branching (Experimental, requires a paid plan)
145145

146-
- `enable_branching`: Enables branching on a project (requires a paid plan).
147146
- `create_branch`: Creates a development branch with migrations from production branch.
148147
- `list_branches`: Lists all development branches.
149148
- `delete_branch`: Deletes a development branch.

packages/mcp-server-supabase/src/server.test.ts

Lines changed: 2 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -493,74 +493,10 @@ describe('tools', () => {
493493
);
494494
});
495495

496-
test('enable branching', async () => {
497-
const { callTool } = await setup();
498-
const project = mockProjects.values().next().value!;
499-
const result = await callTool({
500-
name: 'enable_branching',
501-
arguments: {
502-
project_id: project.id,
503-
},
504-
});
505-
506-
expect(result).toEqual({
507-
id: expect.stringMatching(/^.+$/),
508-
name: 'main',
509-
project_ref: expect.stringMatching(/^.+$/),
510-
parent_project_ref: project.id,
511-
is_default: true,
512-
persistent: false,
513-
status: 'MIGRATIONS_PASSED',
514-
created_at: expect.stringMatching(
515-
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/
516-
),
517-
updated_at: expect.stringMatching(
518-
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/
519-
),
520-
});
521-
});
522-
523-
test('enabling branching twice returns the same main branch', async () => {
524-
const { callTool } = await setup();
525-
const project = mockProjects.values().next().value!;
526-
527-
const firstResult = await callTool({
528-
name: 'enable_branching',
529-
arguments: {
530-
project_id: project.id,
531-
},
532-
});
533-
534-
const secondResult = await callTool({
535-
name: 'enable_branching',
536-
arguments: {
537-
project_id: project.id,
538-
},
539-
});
540-
541-
const listResult = await callTool({
542-
name: 'list_branches',
543-
arguments: {
544-
project_id: project.id,
545-
},
546-
});
547-
548-
expect(listResult).toHaveLength(1);
549-
expect(listResult[0]).toEqual(firstResult);
550-
expect(firstResult).toEqual(secondResult);
551-
});
552-
553496
test('create branch', async () => {
554497
const { callTool } = await setup();
555498
const project = mockProjects.values().next().value!;
556499

557-
await callTool({
558-
name: 'enable_branching',
559-
arguments: {
560-
project_id: project.id,
561-
},
562-
});
563-
564500
const branchName = 'test-branch';
565501
const result = await callTool({
566502
name: 'create_branch',
@@ -591,13 +527,6 @@ describe('tools', () => {
591527
const { callTool } = await setup();
592528
const project = mockProjects.values().next().value!;
593529

594-
await callTool({
595-
name: 'enable_branching',
596-
arguments: {
597-
project_id: project.id,
598-
},
599-
});
600-
601530
const branch = await callTool({
602531
name: 'create_branch',
603532
arguments: {
@@ -636,29 +565,8 @@ describe('tools', () => {
636565
expect.objectContaining({ id: branch.id })
637566
);
638567
expect(listBranchesResultAfterDelete).toHaveLength(1);
639-
});
640-
641-
test('delete main branch fails', async () => {
642-
const { callTool } = await setup();
643-
const project = mockProjects.values().next().value!;
644-
645-
await callTool({
646-
name: 'enable_branching',
647-
arguments: {
648-
project_id: project.id,
649-
},
650-
});
651-
652-
const listBranchesResult = await callTool({
653-
name: 'list_branches',
654-
arguments: {
655-
project_id: project.id,
656-
},
657-
});
658-
659-
expect(listBranchesResult).toHaveLength(1);
660568

661-
const mainBranch = listBranchesResult[0]!;
569+
const mainBranch = listBranchesResultAfterDelete[0];
662570

663571
const deleteBranchPromise = callTool({
664572
name: 'delete_branch',
@@ -676,50 +584,20 @@ describe('tools', () => {
676584
const { callTool } = await setup();
677585
const project = mockProjects.values().next().value!;
678586

679-
await callTool({
680-
name: 'enable_branching',
681-
arguments: {
682-
project_id: project.id,
683-
},
684-
});
685-
686587
const result = await callTool({
687588
name: 'list_branches',
688589
arguments: {
689590
project_id: project.id,
690591
},
691592
});
692593

693-
expect(result).toEqual([
694-
{
695-
id: expect.stringMatching(/^.+$/),
696-
name: 'main',
697-
project_ref: project.id,
698-
parent_project_ref: project.id,
699-
is_default: true,
700-
persistent: false,
701-
status: 'MIGRATIONS_PASSED',
702-
created_at: expect.stringMatching(
703-
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/
704-
),
705-
updated_at: expect.stringMatching(
706-
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/
707-
),
708-
},
709-
]);
594+
expect(result).toStrictEqual([]);
710595
});
711596

712597
test('merge branch', async () => {
713598
const { callTool } = await setup();
714599
const project = mockProjects.values().next().value!;
715600

716-
await callTool({
717-
name: 'enable_branching',
718-
arguments: {
719-
project_id: project.id,
720-
},
721-
});
722-
723601
const branch = await callTool({
724602
name: 'create_branch',
725603
arguments: {
@@ -769,13 +647,6 @@ describe('tools', () => {
769647
const { callTool } = await setup();
770648
const project = mockProjects.values().next().value!;
771649

772-
await callTool({
773-
name: 'enable_branching',
774-
arguments: {
775-
project_id: project.id,
776-
},
777-
});
778-
779650
const branch = await callTool({
780651
name: 'create_branch',
781652
arguments: {
@@ -830,13 +701,6 @@ describe('tools', () => {
830701
const { callTool } = await setup();
831702
const project = mockProjects.values().next().value!;
832703

833-
await callTool({
834-
name: 'enable_branching',
835-
arguments: {
836-
project_id: project.id,
837-
},
838-
});
839-
840704
const branch = await callTool({
841705
name: 'create_branch',
842706
arguments: {
@@ -915,13 +779,6 @@ describe('tools', () => {
915779
const { callTool } = await setup();
916780
const project = mockProjects.values().next().value!;
917781

918-
await callTool({
919-
name: 'enable_branching',
920-
arguments: {
921-
project_id: project.id,
922-
},
923-
});
924-
925782
const branch = await callTool({
926783
name: 'create_branch',
927784
arguments: {
@@ -967,34 +824,6 @@ describe('tools', () => {
967824
});
968825
});
969826

970-
test('list and create branch fails when branching disabled', async () => {
971-
const { callTool } = await setup();
972-
const project = mockProjects.values().next().value!;
973-
974-
const listBranchesPromise = callTool({
975-
name: 'list_branches',
976-
arguments: {
977-
project_id: project.id,
978-
},
979-
});
980-
981-
await expect(listBranchesPromise).rejects.toThrow(
982-
'Preview branching is not enabled for this project.'
983-
);
984-
985-
const createBranchPromise = callTool({
986-
name: 'create_branch',
987-
arguments: {
988-
project_id: project.id,
989-
name: 'test-branch',
990-
},
991-
});
992-
993-
await expect(createBranchPromise).rejects.toThrow(
994-
'Preview branching is not enabled for this project.'
995-
);
996-
});
997-
998827
// We use snake_case because it aligns better with most MCP clients
999828
test('all tools follow snake_case naming convention', async () => {
1000829
const { client } = await setup();

packages/mcp-server-supabase/src/server.ts

Lines changed: 32 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -439,48 +439,6 @@ export function createSupabaseMcpServer(options: SupabaseMcpServerOptions) {
439439
}),
440440

441441
// Experimental features
442-
enable_branching: tool({
443-
description:
444-
'Enables branching on a Supabase project if the organization has a paid subscription.',
445-
parameters: z.object({
446-
project_id: z.string(),
447-
}),
448-
execute: async ({ project_id }) => {
449-
const { error, data } = await managementApiClient.GET(
450-
'/v1/projects/{ref}/branches',
451-
{
452-
params: {
453-
path: {
454-
ref: project_id,
455-
},
456-
},
457-
}
458-
);
459-
460-
// If at least 1 branch exists, branching is already enabled.
461-
if (!error) {
462-
return data?.find((b) => b.is_default);
463-
}
464-
465-
const response = await managementApiClient.POST(
466-
'/v1/projects/{ref}/branches',
467-
{
468-
params: {
469-
path: {
470-
ref: project_id,
471-
},
472-
},
473-
body: {
474-
branch_name: 'main',
475-
},
476-
}
477-
);
478-
479-
assertSuccess(response, 'Failed to enable branching');
480-
481-
return response.data;
482-
},
483-
}),
484442
create_branch: tool({
485443
description:
486444
'Creates a development branch on a Supabase project. This will apply all migrations from the main project to a fresh branch database. Note that production data will not carry over. The branch will get its own project_id via the resulting project_ref. Use this ID to execute queries and migrations on the branch.',
@@ -492,35 +450,54 @@ export function createSupabaseMcpServer(options: SupabaseMcpServerOptions) {
492450
.describe('Name of the branch to create'),
493451
}),
494452
execute: async ({ project_id, name }) => {
495-
// First ensure branching is enabled by listing branches
496-
const listBranchesResponse = await managementApiClient.GET(
453+
const createBranchResponse = await managementApiClient.POST(
497454
'/v1/projects/{ref}/branches',
498455
{
499456
params: {
500457
path: {
501458
ref: project_id,
502459
},
503460
},
461+
body: {
462+
branch_name: name,
463+
},
504464
}
505465
);
506466

507-
assertSuccess(listBranchesResponse, 'Failed to list branches');
467+
assertSuccess(createBranchResponse, 'Failed to create branch');
508468

509-
const createBranchResponse = await managementApiClient.POST(
510-
'/v1/projects/{ref}/branches',
511-
{
469+
// Creating a default branch means we just enabled branching
470+
// TODO: move this logic to API eventually.
471+
if (createBranchResponse.data.is_default) {
472+
await managementApiClient.PATCH('/v1/branches/{branch_id}', {
512473
params: {
513474
path: {
514-
ref: project_id,
475+
branch_id: createBranchResponse.data.id,
515476
},
516477
},
517478
body: {
518-
branch_name: name,
479+
branch_name: 'main',
519480
},
520-
}
521-
);
481+
});
482+
483+
const response = await managementApiClient.POST(
484+
'/v1/projects/{ref}/branches',
485+
{
486+
params: {
487+
path: {
488+
ref: project_id,
489+
},
490+
},
491+
body: {
492+
branch_name: name,
493+
},
494+
}
495+
);
522496

523-
assertSuccess(createBranchResponse, 'Failed to create branch');
497+
assertSuccess(response, 'Failed to create branch');
498+
499+
return response.data;
500+
}
524501

525502
return createBranchResponse.data;
526503
},
@@ -543,6 +520,8 @@ export function createSupabaseMcpServer(options: SupabaseMcpServerOptions) {
543520
}
544521
);
545522

523+
// There are no branches if branching is disabled
524+
if (response.response.status === 422) return [];
546525
assertSuccess(response, 'Failed to list branches');
547526

548527
return response.data;

0 commit comments

Comments
 (0)