Skip to content

Commit a560add

Browse files
authored
feat: add new tool to load mcp resources (#137)
**Context** As part of our onboarding devx, we want to add a set of steps and instructions to help users integrate their existing projects with Neon. We added [this get started guide](https://github.com/neondatabase-labs/ai-rules/blob/main/neon-get-started.mdc). This will typically be run through an `init` command that sets up the MCP server for the user. **What changes are proposed in this pull request?** This PR adds a `load_resource` tool to expose guides (starting with our `Get Started` resource) to the agent via a MCP tool. This is useful for two reasons. 1. Some IDEs don't support MCP resources and only have access to MCP tools 2. MCP resources are typically less likely to be loaded into context by agents (unless we explicitly instruct the agent to use the MCP resource) **How was this tested?** Tested this by deploying to preview env <img width="396" height="913" alt="Screenshot 2025-11-11 at 19 33 03" src="https://github.com/user-attachments/assets/919bdc93-9fa2-4f73-adc9-cfa74257cd1d" /> #[LKB-6294](https://databricks.atlassian.net/browse/LKB-6294)
1 parent e6f447e commit a560add

File tree

4 files changed

+73
-4
lines changed

4 files changed

+73
-4
lines changed

src/resources.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
import { ReadResourceCallback } from '@modelcontextprotocol/sdk/server/mcp.js';
22
import { Resource } from '@modelcontextprotocol/sdk/types.js';
33

4-
async function fetchRawGithubContent(rawPath: string) {
4+
export async function fetchRawGithubContent(rawPath: string) {
55
const path = rawPath.replace('/blob', '');
66

7-
return fetch(`https://raw.githubusercontent.com${path}`).then((res) =>
8-
res.text(),
9-
);
7+
const response = await fetch(`https://raw.githubusercontent.com${path}`);
8+
if (!response.ok) {
9+
throw new Error(
10+
`Failed to fetch GitHub content: ${response.status} ${response.statusText}`,
11+
);
12+
}
13+
return response.text();
1014
}
1115

1216
export const NEON_RESOURCES = [

src/tools/definitions.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
compareDatabaseSchemaInputSchema,
2727
searchInputSchema,
2828
fetchInputSchema,
29+
loadResourceInputSchema,
2930
} from './toolsSchema.js';
3031

3132
export const NEON_TOOLS = [
@@ -916,4 +917,27 @@ export const NEON_TOOLS = [
916917
inputSchema: fetchInputSchema,
917918
readOnlySafe: true,
918919
},
920+
{
921+
name: 'load_resource' as const,
922+
description: `
923+
<use_case>
924+
Loads comprehensive Neon documentation and usage guidelines from GitHub. This tool provides instructions for various Neon features and workflows.
925+
926+
Use this tool when:
927+
- User says "Get started with Neon" or similar onboarding phrases (with neon-get-started subject)
928+
- User needs detailed guidance for initial Neon setup and configuration (with neon-get-started subject)
929+
- You need comprehensive context about Neon workflows and best practices (with neon-get-started subject)
930+
931+
Available subjects:
932+
- neon-get-started: Comprehensive interactive guide covering organization/project setup, database configuration, connection strings, dependency installation, schema creation/migration, etc.
933+
</use_case>
934+
935+
<important_notes>
936+
- This tool provides general guidance on different subjects relevant to Neon.
937+
- This tool returns the FULL documentation content
938+
- Load this resource early when users need onboarding guidance
939+
</important_notes>`,
940+
inputSchema: loadResourceInputSchema,
941+
readOnlySafe: true,
942+
},
919943
];

src/tools/tools.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { handleProvisionNeonAuth } from './handlers/neon-auth.js';
1515
import { handleSearch } from './handlers/search.js';
1616
import { handleFetch } from './handlers/fetch.js';
1717
import { getMigrationFromMemory, persistMigrationToMemory } from './state.js';
18+
import { fetchRawGithubContent, NEON_RESOURCES } from '../resources.js';
1819

1920
import {
2021
getDefaultDatabase,
@@ -1014,6 +1015,26 @@ async function handleListSharedProjects(
10141015
return response.data.projects;
10151016
}
10161017

1018+
async function handleLoadResource({ subject }: { subject: string }) {
1019+
const resource = NEON_RESOURCES.find((r) => r.name === subject);
1020+
if (!resource) {
1021+
throw new InvalidArgumentError(`Resource not found: ${subject}`);
1022+
}
1023+
1024+
try {
1025+
const url = new URL(resource.uri);
1026+
const path = url.pathname;
1027+
const content = await fetchRawGithubContent(path);
1028+
return content;
1029+
} catch (error) {
1030+
const errorMessage =
1031+
error instanceof Error ? error.message : 'Unknown error';
1032+
throw new Error(
1033+
`Failed to load resource "${resource.name}": ${errorMessage}`,
1034+
);
1035+
}
1036+
}
1037+
10171038
async function handleCompareDatabaseSchema(
10181039
params: GetProjectBranchSchemaComparisonParams,
10191040
neonClient: Api<unknown>,
@@ -1596,4 +1617,16 @@ export const NEON_HANDLERS = {
15961617
fetch: async ({ params }, neonClient, extra) => {
15971618
return await handleFetch(params, neonClient, extra);
15981619
},
1620+
1621+
load_resource: async ({ params }) => {
1622+
const content = await handleLoadResource({ subject: params.subject });
1623+
return {
1624+
content: [
1625+
{
1626+
type: 'text',
1627+
text: content,
1628+
},
1629+
],
1630+
};
1631+
},
15991632
} satisfies ToolHandlers;

src/tools/toolsSchema.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,3 +358,11 @@ export const fetchInputSchema = z.object({
358358
'The ID returned by the search tool to fetch detailed information about the entity',
359359
),
360360
});
361+
362+
export const loadResourceInputSchema = z.object({
363+
subject: z
364+
.enum(['neon-get-started'])
365+
.describe(
366+
'The subject of the resource to load. Options: neon-get-started (Neon getting started guide).',
367+
),
368+
});

0 commit comments

Comments
 (0)