Skip to content

Commit 4172488

Browse files
committed
feat: replace get_anon_key with get_anon_or_publishable_keys
- Replace get_anon_key tool with get_anon_or_publishable_keys to support both legacy and modern API keys - Add ApiKeyType type and apiKeyTypeSchema for proper type safety - Update getAnonOrPublishableKeys to return array of structured ApiKey objects - Filter for client-safe keys (legacy 'anon' or 'publishable' type) - Update tests to verify both legacy and publishable keys are returned - Update README documentation to reflect new tool - Improve error messages and provide guidance on key types Fixes AI-166
1 parent c3e263c commit 4172488

File tree

6 files changed

+64
-15
lines changed

6 files changed

+64
-15
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ Enabled by default. Use `debugging` to target this group of tools with the [`fea
195195
Enabled by default. Use `development` to target this group of tools with the [`features`](#feature-groups) option.
196196

197197
- `get_project_url`: Gets the API URL for a project.
198-
- `get_anon_key`: Gets the anonymous API key for a project.
198+
- `get_anon_or_publishable_keys`: Gets the anonymous API keys for a project. Returns an array of client-safe API keys including legacy anon keys and modern publishable keys. Publishable keys are recommended for new applications.
199199
- `generate_typescript_types`: Generates TypeScript types based on the database schema. LLMs can save this to a file and use it in their code.
200200

201201
#### Edge Functions

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

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import {
2121
getLogsOptionsSchema,
2222
resetBranchOptionsSchema,
2323
type AccountOperations,
24+
type ApiKey,
25+
type ApiKeyType,
2426
type ApplyMigrationOptions,
2527
type BranchingOperations,
2628
type CreateBranchOptions,
@@ -298,7 +300,7 @@ export function createSupabaseApiPlatform(
298300
const apiUrl = new URL(managementApiUrl);
299301
return `https://${projectId}.${getProjectDomain(apiUrl.hostname)}`;
300302
},
301-
async getAnonKey(projectId: string): Promise<string> {
303+
async getAnonOrPublishableKeys(projectId: string): Promise<ApiKey[]> {
302304
const response = await managementApiClient.GET(
303305
'/v1/projects/{ref}/api-keys',
304306
{
@@ -315,13 +317,24 @@ export function createSupabaseApiPlatform(
315317

316318
assertSuccess(response, 'Failed to fetch API keys');
317319

318-
const anonKey = response.data?.find((key) => key.name === 'anon');
320+
// Filter for client-safe keys: legacy 'anon' or publishable type
321+
const clientKeys = response.data?.filter(
322+
(key) => key.name === 'anon' || key.type === 'publishable'
323+
) ?? [];
319324

320-
if (!anonKey?.api_key) {
321-
throw new Error('Anonymous key not found');
325+
if (clientKeys.length === 0) {
326+
throw new Error(
327+
'No client-safe API keys (anon or publishable) found. Please create a publishable key in your project settings.'
328+
);
322329
}
323330

324-
return anonKey.api_key;
331+
return clientKeys.map((key) => ({
332+
api_key: key.api_key!,
333+
name: key.name,
334+
type: (key.type === 'publishable' ? 'publishable' : 'legacy') satisfies ApiKeyType,
335+
description: key.description ?? undefined,
336+
id: key.id ?? undefined,
337+
}));
325338
},
326339
async generateTypescriptTypes(projectId: string) {
327340
const response = await managementApiClient.GET(

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,9 +208,20 @@ export type DebuggingOperations = {
208208
getPerformanceAdvisors(projectId: string): Promise<unknown>;
209209
};
210210

211+
export const apiKeyTypeSchema = z.enum(['legacy', 'publishable']);
212+
export type ApiKeyType = z.infer<typeof apiKeyTypeSchema>;
213+
214+
export type ApiKey = {
215+
api_key: string;
216+
name: string;
217+
type: ApiKeyType;
218+
description?: string;
219+
id?: string;
220+
};
221+
211222
export type DevelopmentOperations = {
212223
getProjectUrl(projectId: string): Promise<string>;
213-
getAnonKey(projectId: string): Promise<string>;
224+
getAnonOrPublishableKeys(projectId: string): Promise<ApiKey[]>;
214225
generateTypescriptTypes(
215226
projectId: string
216227
): Promise<GenerateTypescriptTypesResult>;

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

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,7 @@ describe('tools', () => {
606606
expect(result).toEqual(`https://${project.id}.supabase.co`);
607607
});
608608

609-
test('get anon key', async () => {
609+
test('get anon or publishable keys', async () => {
610610
const { callTool } = await setup();
611611
const org = await createOrganization({
612612
name: 'My Org',
@@ -621,12 +621,28 @@ describe('tools', () => {
621621
project.status = 'ACTIVE_HEALTHY';
622622

623623
const result = await callTool({
624-
name: 'get_anon_key',
624+
name: 'get_anon_or_publishable_keys',
625625
arguments: {
626626
project_id: project.id,
627627
},
628628
});
629-
expect(result).toEqual('dummy-anon-key');
629+
630+
expect(result).toBeInstanceOf(Array);
631+
expect(result.length).toBe(2);
632+
633+
// Check legacy anon key
634+
const anonKey = result.find((key: any) => key.name === 'anon');
635+
expect(anonKey).toBeDefined();
636+
expect(anonKey.api_key).toEqual('dummy-anon-key');
637+
expect(anonKey.type).toEqual('legacy');
638+
expect(anonKey.id).toEqual('anon-key-id');
639+
640+
// Check publishable key
641+
const publishableKey = result.find((key: any) => key.type === 'publishable');
642+
expect(publishableKey).toBeDefined();
643+
expect(publishableKey.api_key).toEqual('sb_publishable_dummy_key_1');
644+
expect(publishableKey.type).toEqual('publishable');
645+
expect(publishableKey.description).toEqual('Main publishable key');
630646
});
631647

632648
test('list storage buckets', async () => {
@@ -2607,7 +2623,7 @@ describe('feature groups', () => {
26072623

26082624
expect(toolNames).toEqual([
26092625
'get_project_url',
2610-
'get_anon_key',
2626+
'get_anon_or_publishable_keys',
26112627
'generate_typescript_types',
26122628
]);
26132629
});

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ export function getDevelopmentTools({
3131
return development.getProjectUrl(project_id);
3232
},
3333
}),
34-
get_anon_key: injectableTool({
35-
description: 'Gets the anonymous API key for a project.',
34+
get_anon_or_publishable_keys: injectableTool({
35+
description: 'Gets the anonymous API keys for a project. Returns an array of client-safe API keys that can be used in your application code. This includes legacy anon keys (JWT-based) and modern publishable keys (format: sb_publishable_...). Publishable keys are the recommended approach for new applications as they provide better security and can be rotated independently. These keys are safe to expose in client-side code.',
3636
annotations: {
37-
title: 'Get anon key',
37+
title: 'Get anon or publishable keys',
3838
readOnlyHint: true,
3939
destructiveHint: false,
4040
idempotentHint: true,
@@ -45,7 +45,7 @@ export function getDevelopmentTools({
4545
}),
4646
inject: { project_id },
4747
execute: async ({ project_id }) => {
48-
return development.getAnonKey(project_id);
48+
return development.getAnonOrPublishableKeys(project_id);
4949
},
5050
}),
5151
generate_typescript_types: injectableTool({

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,15 @@ export const mockManagementApi = [
251251
{
252252
name: 'anon',
253253
api_key: 'dummy-anon-key',
254+
type: 'legacy',
255+
id: 'anon-key-id',
256+
},
257+
{
258+
name: 'publishable-key-1',
259+
api_key: 'sb_publishable_dummy_key_1',
260+
type: 'publishable',
261+
id: 'publishable-key-1-id',
262+
description: 'Main publishable key',
254263
},
255264
]);
256265
}),

0 commit comments

Comments
 (0)