Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions packages/mcp-server-supabase/src/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1473,6 +1473,46 @@ describe('tools', () => {
expect(result).toEqual({ lints: [] });
});

test('get advisors supports paging large lint results', async () => {
const { callTool } = await setup();

const org = await createOrganization({
name: 'My Org',
plan: 'free',
allowed_release_channels: ['ga'],
});

const project = await createProject({
name: 'Project 1',
region: 'us-east-1',
organization_id: org.id,
});
project.status = 'ACTIVE_HEALTHY';
project.security_advisors = Array.from({ length: 5 }, (_, index) => ({
name: `lint_${index}`,
}));

const { result } = await callTool({
name: 'get_advisors',
arguments: {
project_id: project.id,
type: 'security',
limit: 2,
offset: 1,
},
});

expect(result).toEqual({
lints: [{ name: 'lint_1' }, { name: 'lint_2' }],
pagination: {
total: 5,
offset: 1,
limit: 2,
next_offset: 3,
},
});
});

test('get performance advisors', async () => {
const { callTool } = await setup();

Expand Down
45 changes: 43 additions & 2 deletions packages/mcp-server-supabase/src/tools/debugging-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,53 @@ const getAdvisorsInputSchema = z.object({
type: z
.enum(['security', 'performance'])
.describe('The type of advisors to fetch'),
limit: z
.number()
.int()
.positive()
.optional()
.describe('Maximum number of advisor notices to return.'),
offset: z
.number()
.int()
.nonnegative()
.optional()
.describe('Number of advisor notices to skip before returning results.'),
});

const getAdvisorsOutputSchema = z.object({
result: z.unknown(),
});

function paginateAdvisorResult(result: unknown, limit?: number, offset = 0) {
if (limit === undefined && offset === 0) return result;

if (
!result ||
typeof result !== 'object' ||
!('lints' in result) ||
!Array.isArray(result.lints)
) {
return result;
}

const total = result.lints.length;
const lints = result.lints.slice(offset, limit ? offset + limit : undefined);
const next_offset =
limit !== undefined && offset + limit < total ? offset + limit : undefined;

return {
...result,
lints,
pagination: {
total,
offset,
limit: limit ?? total - offset,
...(next_offset !== undefined && { next_offset }),
},
};
}

export const debuggingToolDefs = {
get_logs: {
description:
Expand Down Expand Up @@ -84,7 +125,7 @@ export function getDebuggingTools({
get_advisors: injectableTool({
...debuggingToolDefs.get_advisors,
inject: { project_id },
execute: async ({ project_id, type }) => {
execute: async ({ project_id, type, limit, offset }) => {
let result: unknown;
switch (type) {
case 'security':
Expand All @@ -96,7 +137,7 @@ export function getDebuggingTools({
default:
throw new Error(`Unknown advisor type: ${type}`);
}
return { result };
return { result: paginateAdvisorResult(result, limit, offset) };
},
}),
};
Expand Down
6 changes: 4 additions & 2 deletions packages/mcp-server-supabase/test/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,7 @@ export const mockManagementApi = [
}

return HttpResponse.json({
lints: [],
lints: project.security_advisors,
});
}
),
Expand All @@ -606,7 +606,7 @@ export const mockManagementApi = [
}

return HttpResponse.json({
lints: [],
lints: project.performance_advisors,
});
}
),
Expand Down Expand Up @@ -1178,6 +1178,8 @@ export class MockProject {
migrations: Migration[] = [];
edge_functions = new Map<string, MockEdgeFunction>();
storage_buckets = new Map<string, MockStorageBucket>();
security_advisors: unknown[] = [];
performance_advisors: unknown[] = [];

#db?: PGliteInterface;

Expand Down