Skip to content

Commit efbbac2

Browse files
committed
fix: skip pricing confirmation for creating branch
1 parent 8430ed1 commit efbbac2

File tree

2 files changed

+12
-121
lines changed

2 files changed

+12
-121
lines changed

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

+1-98
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
mockOrgs,
1919
mockProjects,
2020
} from '../test/mocks.js';
21-
import { BRANCH_COST_HOURLY, PROJECT_COST_MONTHLY } from './pricing.js';
21+
import { PROJECT_COST_MONTHLY } from './pricing.js';
2222
import { createSupabaseMcpServer } from './server.js';
2323

2424
beforeEach(() => {
@@ -213,24 +213,6 @@ describe('tools', () => {
213213
);
214214
});
215215

216-
test('get branch cost', async () => {
217-
const { callTool } = await setup();
218-
219-
const paidOrg = mockOrgs.find((org) => org.plan !== 'free')!;
220-
221-
const result = await callTool({
222-
name: 'get_cost',
223-
arguments: {
224-
type: 'branch',
225-
organization_id: paidOrg.id,
226-
},
227-
});
228-
229-
expect(result).toEqual(
230-
`The new branch will cost $${BRANCH_COST_HOURLY} hourly. You must repeat this to the user and confirm their understanding.`
231-
);
232-
});
233-
234216
test('list projects', async () => {
235217
const { callTool } = await setup();
236218

@@ -637,22 +619,12 @@ describe('tools', () => {
637619
const { callTool } = await setup();
638620
const project = mockProjects.values().next().value!;
639621

640-
const confirm_cost_id = await callTool({
641-
name: 'confirm_cost',
642-
arguments: {
643-
type: 'branch',
644-
recurrence: 'hourly',
645-
amount: BRANCH_COST_HOURLY,
646-
},
647-
});
648-
649622
const branchName = 'test-branch';
650623
const result = await callTool({
651624
name: 'create_branch',
652625
arguments: {
653626
project_id: project.id,
654627
name: branchName,
655-
confirm_cost_id,
656628
},
657629
});
658630

@@ -673,44 +645,15 @@ describe('tools', () => {
673645
});
674646
});
675647

676-
test('create branch without cost confirmation fails', async () => {
677-
const { callTool } = await setup();
678-
679-
const project = mockProjects.values().next().value!;
680-
681-
const branchName = 'test-branch';
682-
const createBranchPromise = callTool({
683-
name: 'create_branch',
684-
arguments: {
685-
project_id: project.id,
686-
name: branchName,
687-
},
688-
});
689-
690-
await expect(createBranchPromise).rejects.toThrow(
691-
'User must confirm understanding of costs before creating a branch.'
692-
);
693-
});
694-
695648
test('delete branch', async () => {
696649
const { callTool } = await setup();
697650
const project = mockProjects.values().next().value!;
698651

699-
const confirm_cost_id = await callTool({
700-
name: 'confirm_cost',
701-
arguments: {
702-
type: 'branch',
703-
recurrence: 'hourly',
704-
amount: BRANCH_COST_HOURLY,
705-
},
706-
});
707-
708652
const branch = await callTool({
709653
name: 'create_branch',
710654
arguments: {
711655
project_id: project.id,
712656
name: 'test-branch',
713-
confirm_cost_id,
714657
},
715658
});
716659

@@ -777,21 +720,11 @@ describe('tools', () => {
777720
const { callTool } = await setup();
778721
const project = mockProjects.values().next().value!;
779722

780-
const confirm_cost_id = await callTool({
781-
name: 'confirm_cost',
782-
arguments: {
783-
type: 'branch',
784-
recurrence: 'hourly',
785-
amount: BRANCH_COST_HOURLY,
786-
},
787-
});
788-
789723
const branch = await callTool({
790724
name: 'create_branch',
791725
arguments: {
792726
project_id: project.id,
793727
name: 'test-branch',
794-
confirm_cost_id,
795728
},
796729
});
797730

@@ -836,21 +769,11 @@ describe('tools', () => {
836769
const { callTool } = await setup();
837770
const project = mockProjects.values().next().value!;
838771

839-
const confirm_cost_id = await callTool({
840-
name: 'confirm_cost',
841-
arguments: {
842-
type: 'branch',
843-
recurrence: 'hourly',
844-
amount: BRANCH_COST_HOURLY,
845-
},
846-
});
847-
848772
const branch = await callTool({
849773
name: 'create_branch',
850774
arguments: {
851775
project_id: project.id,
852776
name: 'test-branch',
853-
confirm_cost_id,
854777
},
855778
});
856779

@@ -900,21 +823,11 @@ describe('tools', () => {
900823
const { callTool } = await setup();
901824
const project = mockProjects.values().next().value!;
902825

903-
const confirm_cost_id = await callTool({
904-
name: 'confirm_cost',
905-
arguments: {
906-
type: 'branch',
907-
recurrence: 'hourly',
908-
amount: BRANCH_COST_HOURLY,
909-
},
910-
});
911-
912826
const branch = await callTool({
913827
name: 'create_branch',
914828
arguments: {
915829
project_id: project.id,
916830
name: 'test-branch',
917-
confirm_cost_id,
918831
},
919832
});
920833

@@ -988,21 +901,11 @@ describe('tools', () => {
988901
const { callTool } = await setup();
989902
const project = mockProjects.values().next().value!;
990903

991-
const confirm_cost_id = await callTool({
992-
name: 'confirm_cost',
993-
arguments: {
994-
type: 'branch',
995-
recurrence: 'hourly',
996-
amount: BRANCH_COST_HOURLY,
997-
},
998-
});
999-
1000904
const branch = await callTool({
1001905
name: 'create_branch',
1002906
arguments: {
1003907
project_id: project.id,
1004908
name: 'test-branch',
1005-
confirm_cost_id,
1006909
},
1007910
});
1008911

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

+11-23
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,12 @@ import {
1313
type ManagementApiClient,
1414
} from './management-api/index.js';
1515
import { generatePassword } from './password.js';
16-
import { getBranchCost, getNextProjectCost, type Cost } from './pricing.js';
16+
import {
17+
BRANCH_COST_HOURLY,
18+
getBranchCost,
19+
getNextProjectCost,
20+
type Cost,
21+
} from './pricing.js';
1722
import {
1823
AWS_REGION_CODES,
1924
getClosestAwsRegion,
@@ -124,7 +129,7 @@ export function createSupabaseMcpServer(options: SupabaseMcpServerOptions) {
124129
}),
125130
get_cost: tool({
126131
description:
127-
'Gets the cost of creating a new project or branch. Never assume organization as costs can be different for each.',
132+
'Gets the cost of creating a new project. Never assume organization when creating a project as costs can be different for each.',
128133
parameters: z.object({
129134
type: z.enum(['project', 'branch']),
130135
organization_id: z
@@ -154,7 +159,7 @@ export function createSupabaseMcpServer(options: SupabaseMcpServerOptions) {
154159
}),
155160
confirm_cost: tool({
156161
description:
157-
'Ask the user to confirm their understanding of the cost of creating a new project or branch. Call `get_cost` first. Returns a unique ID for this confirmation which should be passed to `create_project` or `create_branch`.',
162+
'Ask the user to confirm their understanding of the cost of creating a new project. Call `get_cost` first. Returns a unique ID for this confirmation which should be passed to `create_project`.',
158163
parameters: z.object({
159164
type: z.enum(['project', 'branch']),
160165
recurrence: z.enum(['hourly', 'monthly']),
@@ -504,33 +509,16 @@ export function createSupabaseMcpServer(options: SupabaseMcpServerOptions) {
504509

505510
// Experimental features
506511
create_branch: tool({
507-
description:
508-
'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.',
512+
description: `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.
513+
The cost of each active branch is $${BRANCH_COST_HOURLY} per hour. Always show this to the user before creating a branch and suggest deleting any unused branches to avoid unnecessary charges.`,
509514
parameters: z.object({
510515
project_id: z.string(),
511516
name: z
512517
.string()
513518
.default('develop')
514519
.describe('Name of the branch to create'),
515-
confirm_cost_id: z
516-
.string()
517-
.describe('The cost confirmation ID. Call `confirm_cost` first.'),
518520
}),
519-
execute: async ({ project_id, name, confirm_cost_id }) => {
520-
if (!confirm_cost_id) {
521-
throw new Error(
522-
'User must confirm understanding of costs before creating a branch.'
523-
);
524-
}
525-
526-
const cost = getBranchCost();
527-
const costHash = await hashObject(cost);
528-
if (costHash !== confirm_cost_id) {
529-
throw new Error(
530-
'Cost confirmation ID does not match the expected cost of creating a branch.'
531-
);
532-
}
533-
521+
execute: async ({ project_id, name }) => {
534522
const createBranchResponse = await managementApiClient.POST(
535523
'/v1/projects/{ref}/branches',
536524
{

0 commit comments

Comments
 (0)