Skip to content

Commit 10ba83b

Browse files
authored
Merge pull request #5893 from continuedev/nate/resource-templates
Nate/resource-templates
2 parents 15660fb + 208d6a6 commit 10ba83b

File tree

9 files changed

+85
-20
lines changed

9 files changed

+85
-20
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ Icon?
144144
.continuerc.json
145145
.aider*
146146

147+
*.notes.md
147148
notes.md
148149

149150
manual-testing-sandbox/.idea/**

core/config/profile/doLoadConfig.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -176,12 +176,21 @@ export default async function doLoadConfig(options: {
176176
);
177177
newConfig.slashCommands.push(...serverSlashCommands);
178178

179-
const submenuItems = server.resources.map((resource) => ({
180-
title: resource.name,
181-
description: resource.description ?? resource.name,
182-
id: resource.uri,
183-
icon: server.faviconUrl,
184-
}));
179+
const submenuItems = server.resources
180+
.map((resource) => ({
181+
title: resource.name,
182+
description: resource.description ?? resource.name,
183+
id: resource.uri,
184+
icon: server.faviconUrl,
185+
}))
186+
.concat(
187+
server.resourceTemplates.map((template) => ({
188+
title: template.name,
189+
description: template.description ?? template.name,
190+
id: template.uriTemplate,
191+
icon: server.faviconUrl,
192+
})),
193+
);
185194
if (submenuItems.length > 0) {
186195
const serverContextProvider = new MCPContextProvider({
187196
submenuItems,

core/context/mcp/MCPConnection.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ describe("MCPConnection", () => {
111111
errors: [],
112112
prompts: [],
113113
resources: [],
114+
resourceTemplates: [],
114115
tools: [],
115116
status: "not-connected",
116117
});

core/context/mcp/MCPConnection.ts

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@ import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
33

44
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
55
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
6-
import { WebSocketClientTransport } from "@modelcontextprotocol/sdk/client/websocket.js";
76
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
7+
import { WebSocketClientTransport } from "@modelcontextprotocol/sdk/client/websocket.js";
88

99
import {
1010
MCPConnectionStatus,
1111
MCPOptions,
1212
MCPPrompt,
1313
MCPResource,
14+
MCPResourceTemplate,
1415
MCPServerStatus,
1516
MCPTool,
1617
} from "../..";
@@ -37,6 +38,7 @@ class MCPConnection {
3738
public prompts: MCPPrompt[] = [];
3839
public tools: MCPTool[] = [];
3940
public resources: MCPResource[] = [];
41+
public resourceTemplates: MCPResourceTemplate[] = [];
4042
private transport: Transport;
4143
private connectionPromise: Promise<unknown> | null = null;
4244
private stdioOutput: { stdout: string; stderr: string } = {
@@ -72,6 +74,7 @@ class MCPConnection {
7274
errors: this.errors,
7375
prompts: this.prompts,
7476
resources: this.resources,
77+
resourceTemplates: this.resourceTemplates,
7578
tools: this.tools,
7679
status: this.status,
7780
};
@@ -95,6 +98,7 @@ class MCPConnection {
9598
this.tools = [];
9699
this.prompts = [];
97100
this.resources = [];
101+
this.resourceTemplates = [];
98102
this.errors = [];
99103
this.stdioOutput = { stdout: "", stderr: "" };
100104

@@ -169,6 +173,23 @@ class MCPConnection {
169173
}
170174
this.errors.push(errorMessage);
171175
}
176+
177+
// Resource templates
178+
try {
179+
const { resourceTemplates } =
180+
await this.client.listResourceTemplates(
181+
{},
182+
{ signal: timeoutController.signal },
183+
);
184+
185+
this.resourceTemplates = resourceTemplates;
186+
} catch (e) {
187+
let errorMessage = `Error loading resource templates for MCP Server ${this.options.name}`;
188+
if (e instanceof Error) {
189+
errorMessage += `: ${e.message}`;
190+
}
191+
this.errors.push(errorMessage);
192+
}
172193
}
173194

174195
// Tools <—> Tools
@@ -337,9 +358,12 @@ class MCPConnection {
337358
requestInit: { headers: options.transport.requestOptions?.headers },
338359
});
339360
case "streamable-http":
340-
return new StreamableHTTPClientTransport(new URL(options.transport.url), {
341-
requestInit: { headers: options.transport.requestOptions?.headers },
342-
});
361+
return new StreamableHTTPClientTransport(
362+
new URL(options.transport.url),
363+
{
364+
requestInit: { headers: options.transport.requestOptions?.headers },
365+
},
366+
);
343367
default:
344368
throw new Error(
345369
`Unsupported transport type: ${(options.transport as any).type}`,

core/context/providers/MCPContextProvider.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@ class MCPContextProvider extends BaseContextProvider {
2020
displayTitle: "MCP",
2121
description: "Model Context Protocol",
2222
type: "submenu",
23+
renderInlineAs: "",
2324
};
2425
override get description(): ContextProviderDescription {
2526
return {
2627
title: `${MCPContextProvider.description.title}-${this.options["mcpId"]}`,
2728
displayTitle: this.options["serverName"]
2829
? `${this.options["serverName"]} resources`
2930
: "MCP",
31+
renderInlineAs: "",
3032
description: "Model Context Protocol",
3133
type: "submenu",
3234
};
@@ -47,6 +49,18 @@ class MCPContextProvider extends BaseContextProvider {
4749
super(options);
4850
}
4951

52+
/**
53+
* Continue experimentally supports resource templates (https://modelcontextprotocol.io/docs/concepts/resources#resource-templates)
54+
* by allowing specifically just the "query" variable in the template, which we will update with the full input of the user in the input box
55+
*/
56+
private insertInputToUriTemplate(uri: string, query: string): string {
57+
const TEMPLATE_VAR = "query";
58+
if (uri.includes(`{${TEMPLATE_VAR}}`)) {
59+
return uri.replace(`{${TEMPLATE_VAR}}`, encodeURIComponent(query));
60+
}
61+
return uri;
62+
}
63+
5064
async getContextItems(
5165
query: string,
5266
extras: ContextProviderExtras,
@@ -58,7 +72,9 @@ class MCPContextProvider extends BaseContextProvider {
5872
throw new Error(`No MCP connection found for ${mcpId}`);
5973
}
6074

61-
const { contents } = await connection.client.readResource({ uri });
75+
const { contents } = await connection.client.readResource({
76+
uri: this.insertInputToUriTemplate(uri, extras.fullInput),
77+
});
6278

6379
return await Promise.all(
6480
contents.map(async (resource) => {

core/index.d.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1141,7 +1141,7 @@ export interface StreamableHTTPOptions {
11411141
requestOptions?: RequestOptions;
11421142
}
11431143

1144-
export type TransportOptions =
1144+
export type TransportOptions =
11451145
| StdioOptions
11461146
| WebSocketOptions
11471147
| SSEOptions
@@ -1174,13 +1174,22 @@ export interface MCPPrompt {
11741174
// Leaving here to ideate on
11751175
// export type ContinueConfigSource = "local-yaml" | "local-json" | "hub-assistant" | "hub"
11761176

1177+
// https://modelcontextprotocol.io/docs/concepts/resources#direct-resources
11771178
export interface MCPResource {
11781179
name: string;
11791180
uri: string;
11801181
description?: string;
11811182
mimeType?: string;
11821183
}
11831184

1185+
// https://modelcontextprotocol.io/docs/concepts/resources#resource-templates
1186+
export interface MCPResourceTemplate {
1187+
uriTemplate: string;
1188+
name: string;
1189+
description?: string;
1190+
mimeType?: string;
1191+
}
1192+
11841193
export interface MCPTool {
11851194
name: string;
11861195
description?: string;
@@ -1197,6 +1206,7 @@ export interface MCPServerStatus extends MCPOptions {
11971206
prompts: MCPPrompt[];
11981207
tools: MCPTool[];
11991208
resources: MCPResource[];
1209+
resourceTemplates: MCPResourceTemplate[];
12001210
}
12011211

12021212
export interface ContinueUIConfig {

extensions/vscode/package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

extensions/vscode/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "continue",
33
"icon": "media/icon.png",
44
"author": "Continue Dev, Inc",
5-
"version": "1.1.40",
5+
"version": "1.1.41",
66
"repository": {
77
"type": "git",
88
"url": "https://github.com/continuedev/continue"

gui/src/components/mainInput/Lump/sections/mcp/MCPSection.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,15 @@ function MCPServerPreview({ server, serverFromYaml }: MCPServerStatusProps) {
113113
data-tooltip-id={resourcesTooltipId}
114114
>
115115
<CircleStackIcon className="h-3 w-3" />
116-
<span className="text-xs">{server.resources.length}</span>
116+
<span className="text-xs">
117+
{server.resources.length + server.resourceTemplates.length}
118+
</span>
117119
<ToolTip id={resourcesTooltipId} className="flex flex-col gap-0.5">
118-
{server.resources.map((resource, idx) => (
119-
<code key={idx}>{resource.name}</code>
120-
))}
120+
{[...server.resources, ...server.resourceTemplates].map(
121+
(resource, idx) => (
122+
<code key={idx}>{resource.name}</code>
123+
),
124+
)}
121125
{server.resources.length === 0 && (
122126
<span className="text-lightgray">No resources</span>
123127
)}

0 commit comments

Comments
 (0)