Skip to content

Commit 1d0f02c

Browse files
authored
Merge pull request #12 from supabase-community/feat/project-pause-restore
Project pause and restore tools
2 parents 007926d + 4900da8 commit 1d0f02c

File tree

4 files changed

+109
-9
lines changed

4 files changed

+109
-9
lines changed

README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
![supabase-mcp-demo](https://github.com/user-attachments/assets/3fce101a-b7d4-482f-9182-0be70ed1ad56)
66

7-
The [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP) standardizes how Large Language Models (LLMs) talk to external services like Supabase. It connects AI assistants directly with your Supabase project and allows them to perform tasks like managing tables, fetching config, and querying data.
7+
The [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP) standardizes how Large Language Models (LLMs) talk to external services like Supabase. It connects AI assistants directly with your Supabase project and allows them to perform tasks like managing tables, fetching config, and querying data. See the [full list of tools](#tools).
88

99
## Prerequisites
1010

@@ -113,17 +113,19 @@ Make sure Node.js is available in your system `PATH` environment variable. If yo
113113

114114
## Tools
115115

116-
_**Note:** This server is currently pre-1.0, so expect some breaking changes between versions. Since LLMs will automatically adapt to the tools available, this shouldn't affect most users._
116+
_**Note:** This server is pre-1.0, so expect some breaking changes between versions. Since LLMs will automatically adapt to the tools available, this shouldn't affect most users._
117117

118118
The following Supabase tools are available to the LLM:
119119

120120
#### Project Management
121121

122122
- `list_projects`: Lists all Supabase projects for the user.
123-
- `get_project`: Gets a project by ID.
123+
- `get_project`: Gets details for a project.
124124
- `create_project`: Creates a new Supabase project.
125-
- `list_organizations`: Lists all organizations for the user.
126-
- `get_organization`: Gets an organization by ID.
125+
- `pause_project`: Pauses a project.
126+
- `restore_project`: Restores a project.
127+
- `list_organizations`: Lists all organizations that the user is a member of.
128+
- `get_organization`: Gets details for an organization.
127129

128130
#### Database Operations
129131

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

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ async function setup(options: SetupOptions = {}) {
8585
const [textContent] = content;
8686

8787
if (!textContent) {
88-
throw new Error('tool result content is missing');
88+
return undefined;
8989
}
9090

9191
if (textContent.type !== 'text') {
@@ -215,6 +215,32 @@ describe('tools', () => {
215215
});
216216
});
217217

218+
test('pause project', async () => {
219+
const { callTool } = await setup();
220+
const project = mockProjects.values().next().value!;
221+
await callTool({
222+
name: 'pause_project',
223+
arguments: {
224+
project_id: project.id,
225+
},
226+
});
227+
228+
expect(project.status).toEqual('INACTIVE');
229+
});
230+
231+
test('restore project', async () => {
232+
const { callTool } = await setup();
233+
const project = mockProjects.values().next().value!;
234+
await callTool({
235+
name: 'restore_project',
236+
arguments: {
237+
project_id: project.id,
238+
},
239+
});
240+
241+
expect(project.status).toEqual('ACTIVE_HEALTHY');
242+
});
243+
218244
test('get project url', async () => {
219245
const { callTool } = await setup();
220246
const project = mockProjects.values().next().value!;

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

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ export function createSupabaseMcpServer(options: SupabaseMcpServerOptions) {
103103
},
104104
}),
105105
get_project: tool({
106-
description: 'Gets a project by ID.',
106+
description: 'Gets details for a Supabase project.',
107107
parameters: z.object({
108108
id: z.string().describe('The project ID'),
109109
}),
@@ -153,8 +153,49 @@ export function createSupabaseMcpServer(options: SupabaseMcpServerOptions) {
153153
return response.data;
154154
},
155155
}),
156+
pause_project: tool({
157+
description: 'Pauses a Supabase project.',
158+
parameters: z.object({
159+
project_id: z.string(),
160+
}),
161+
execute: async ({ project_id }) => {
162+
const response = await managementApiClient.POST(
163+
'/v1/projects/{ref}/pause',
164+
{
165+
params: {
166+
path: {
167+
ref: project_id,
168+
},
169+
},
170+
}
171+
);
172+
173+
assertSuccess(response, 'Failed to pause project');
174+
},
175+
}),
176+
restore_project: tool({
177+
description: 'Restores a Supabase project.',
178+
parameters: z.object({
179+
project_id: z.string(),
180+
}),
181+
execute: async ({ project_id }) => {
182+
const response = await managementApiClient.POST(
183+
'/v1/projects/{ref}/restore',
184+
{
185+
params: {
186+
path: {
187+
ref: project_id,
188+
},
189+
},
190+
body: {},
191+
}
192+
);
193+
194+
assertSuccess(response, 'Failed to restore project');
195+
},
196+
}),
156197
list_organizations: tool({
157-
description: 'Lists all organizations for the user.',
198+
description: 'Lists all organizations that the user is a member of.',
158199
parameters: z.object({}),
159200
execute: async () => {
160201
const response = await managementApiClient.GET('/v1/organizations');
@@ -165,7 +206,8 @@ export function createSupabaseMcpServer(options: SupabaseMcpServerOptions) {
165206
},
166207
}),
167208
get_organization: tool({
168-
description: 'Gets an organization by ID.',
209+
description:
210+
'Gets details for an organization. Includes subscription plan.',
169211
parameters: z.object({
170212
id: z.string().describe('The organization ID'),
171213
}),

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,36 @@ export const mockManagementApi = [
9898
return HttpResponse.json(projectResponse);
9999
}),
100100

101+
http.post<{ projectId: string }>(
102+
`${API_URL}/v1/projects/:projectId/pause`,
103+
({ params }) => {
104+
const project = mockProjects.get(params.projectId);
105+
if (!project) {
106+
return HttpResponse.json(
107+
{ error: 'Project not found' },
108+
{ status: 404 }
109+
);
110+
}
111+
project.status = 'INACTIVE';
112+
return HttpResponse.json(project.details);
113+
}
114+
),
115+
116+
http.post<{ projectId: string }>(
117+
`${API_URL}/v1/projects/:projectId/restore`,
118+
({ params }) => {
119+
const project = mockProjects.get(params.projectId);
120+
if (!project) {
121+
return HttpResponse.json(
122+
{ error: 'Project not found' },
123+
{ status: 404 }
124+
);
125+
}
126+
project.status = 'ACTIVE_HEALTHY';
127+
return HttpResponse.json(project.details);
128+
}
129+
),
130+
101131
http.get(`${API_URL}/v1/organizations`, () => {
102132
return HttpResponse.json(mockOrgs);
103133
}),

0 commit comments

Comments
 (0)