Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/mcp/vscode-node/mcpToolCallingLoopPrompt.tsx
13400 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { BasePromptElementProps, PromptElement, TextChunk, UserMessage } from '@vscode/prompt-tsx';
7
import { JsonSchema } from '../../../platform/configuration/common/jsonSchema';
8
import { GenericBasePromptElementProps } from '../../context/node/resolvers/genericPanelIntentInvocation';
9
import { InstructionMessage } from '../../prompts/node/base/instructionMessage';
10
import { Tag } from '../../prompts/node/base/tag';
11
import { HistoryWithInstructions } from '../../prompts/node/panel/conversationHistory';
12
import { ChatToolCalls } from '../../prompts/node/panel/toolCalling';
13
import { CopilotToolMode } from '../../tools/common/toolsRegistry';
14
import { McpPickRef, QuickInputTool, QuickPickTool } from './mcpToolCallingTools';
15
16
export interface IMcpToolCallingLoopPromptContext {
17
packageName: string;
18
packageType: 'npm' | 'pip' | 'docker' | 'nuget';
19
packageReadme: string | undefined;
20
packageVersion: string | undefined;
21
targetSchema: JsonSchema;
22
pickRef: McpPickRef;
23
}
24
25
export interface IMcpToolCallingLoopProps extends GenericBasePromptElementProps, IMcpToolCallingLoopPromptContext { }
26
27
const packageTypePreferredCommands = {
28
pip: (name: string, version: string | undefined) => `uvx ${name.replaceAll('-', '_')}` + (version ? `==${version}` : ''),
29
npm: (name: string, version: string | undefined) => `npx ${name}` + (version ? `@${version}` : ''),
30
docker: (name: string, _version: string | undefined) => `docker run -i --rm ${name}`,
31
nuget: (name: string, version: string | undefined) => `dnx ${name}` + (version ? `@${version}` : '') + ` --yes`,
32
};
33
34
export class McpToolCallingLoopPrompt extends PromptElement<IMcpToolCallingLoopProps> {
35
async render() {
36
const { packageType, packageName, packageVersion, pickRef, packageReadme } = this.props;
37
const { history, toolCallRounds = [], toolCallResults = {} } = this.props.promptContext;
38
39
// We do kind of a 'special' thing here to have the tool only available to *this* prompt because
40
// we're in a quickpick flow (and don't really want the tool generally available)
41
for (const round of toolCallRounds) {
42
for (const tool of round.toolCalls) {
43
if (toolCallResults[tool.id]) {
44
// no-op
45
} else if (tool.name === QuickInputTool.ID) {
46
toolCallResults[tool.id] = await QuickInputTool.invoke(pickRef, JSON.parse(tool.arguments));
47
} else if (tool.name === QuickPickTool.ID) {
48
toolCallResults[tool.id] = await QuickPickTool.invoke(pickRef, JSON.parse(tool.arguments));
49
}
50
}
51
}
52
53
const hasMcpJson = packageReadme?.includes('"mcpServers":');
54
const command = packageTypePreferredCommands[packageType](packageName, packageVersion);
55
56
return (
57
<>
58
<HistoryWithInstructions flexGrow={1} passPriority historyPriority={700} history={history}>
59
<InstructionMessage>
60
<Tag name='instructions'>
61
You are an expert in reading documentation and extracting relevant results.<br />
62
A developer is setting up a Model Context Protocol (MCP) server based on a {packageType} package. Your task is to create a configuration for the server matching the provided JSON schema.<br />
63
{hasMcpJson ? <InstructionsWithMcpJson command={command} packageVersion={packageVersion} /> : <InstructionsWithout command={command} packageVersion={packageVersion} />}
64
<br />
65
<br />
66
When using a tool, follow the JSON schema very carefully and make sure to include all required fields. DO NOT write out a JSON codeblock with the tool inputs.<br />
67
</Tag>
68
<Tag name='example'>
69
<Tag name='request'>
70
User: I want to run the npm package `@modelcontextprotocol/server-redis` as an MCP server. This is its readme:<br /><br />
71
{redisExampleReadme}
72
</Tag>
73
<Tag name='response'>
74
{hasMcpJson && <>The readme has an example confirmation I'll work off of:<br />${clauseExampleConfiguration}</>}<br />
75
Based on {hasMcpJson ? 'this example' : 'the documentation'}, I need the following information to run the MCP server:<br />
76
- Redis hostname<br />
77
- Redis port number<br />
78
- Redis password (optional)<br />
79
<br />
80
I will now ask for this information.<br />
81
[[`{QuickInputTool.ID}` called requesting Redis hostname]]: "redis.example.com"<br />
82
[[`{QuickInputTool.ID}` called requesting Redis port number]]: "3000"<br />
83
[[`{QuickInputTool.ID}` called requesting Redis port password]]: ""<br />
84
<br />
85
{!hasMcpJson && <>Based on this data, the command needed to run the MCP server is `npx @modelcontextprotocol/server-redis redis://example.com:6379`</>}
86
Based on this data, the command needed to run the MCP server is `npx @modelcontextprotocol/server-redis redis://example.com:6379`<br />
87
<br />
88
Here is the JSON object that matches the provided schema:<br />
89
{redisExampleConfig}
90
</Tag>
91
</Tag>
92
</InstructionMessage>
93
</HistoryWithInstructions>
94
<UserMessage flexGrow={3}>
95
I want to run the {packageType} package `{packageName}` as an MCP server. This is its readme:<br />
96
<Tag name='readme'>{this.props.packageReadme}</Tag>
97
98
The schema for the final JSON object is:<br />
99
100
<Tag name='schema' flexGrow={1}>
101
<TextChunk breakOnWhitespace>
102
{JSON.stringify(this.props.targetSchema, null, 2)}
103
</TextChunk>
104
</Tag>
105
</UserMessage>
106
<ChatToolCalls priority={899} flexGrow={2} promptContext={this.props.promptContext} toolCallRounds={toolCallRounds} toolCallResults={toolCallResults} toolCallMode={CopilotToolMode.FullContext} />
107
</>
108
);
109
}
110
}
111
112
class InstructionsWithMcpJson extends PromptElement<{ command: string; packageVersion: string | undefined } & BasePromptElementProps> {
113
render() {
114
const [command, ...args] = this.props.command.split(' ');
115
return <>
116
Think step by step:<br />
117
1. Read the documentation for the MCP server and find the section that discusses setting up a configuration with `mcpServers`. If there are multiple such examples, find the one that works best when run as `{`{"command":"${command}", "args": ["${args.join('", "')}", ...], , "env": { ... } }`}. State this configuration in your response.<br />
118
2. Determine what placeholders are used in that example that the user would need to fill, such as configuration options, credentials, or API keys.<br />
119
3. Call the tool `{QuickInputTool.ID}` a maximum of 5 times to gather the placeholder information. You may make multiple calls using this tool in parallel, but the maximum number of questions must be 5.<br />
120
4. Transform that example configuration entry, replacing or adding any additional information the user gave you, into a JSON object matching the provided schema.<br />
121
{this.props.packageVersion && <>The package version is {this.props.packageVersion}, make sure your command runs the correct version, using the form `{this.props.command}`.<br /></>}
122
5. Return the resulting JSON object in a markdown code block wrapped with triple backticks (```)<br />
123
</>;
124
}
125
}
126
class InstructionsWithout extends PromptElement<{ command: string; packageVersion: string | undefined } & BasePromptElementProps> {
127
render() {
128
return <>
129
The MCP server the developer is asking about can be run using the command {this.props.command}, but it may need additional arguments or environment variables to function.<br /><br />
130
Think step by step:<br />
131
1. Read the documentation for the MCP server and determine what information you would need to run it on the command line.<br />
132
2. Call the tool `{QuickInputTool.ID}` a maximum of 5 times to gather the necessary information. You may make multiple calls using this tool in parallel, but the maximum number of questions must be 5.<br />
133
3. Use that information to construct a set of arguments and environment variables to run the server. <br />
134
{this.props.packageVersion && <>The package version is {this.props.packageVersion}, make sure your command runs the correct version, using the form `{this.props.command}`.<br /></>}
135
4. Translate the command, arguments and environment variables into a JSON object that matches the provided schema.<br />
136
5. Return the resulting JSON object in a markdown code block wrapped with triple backticks (```)<br />
137
<br />
138
Follow these rules when constructing your arguments and environment variables:<br />
139
1. Prefer to use environment variables over arguments when possible, especially for sensitive information. Command-line arguments are not secure.<br />
140
2. Look carefully in the readme for instructions for how to run the MCP server in `stdio` mode. If there are additional arguments needed to run the MCP server in `stdio` mode, then you MUST include them in your output.<br />
141
4. Briefly summarize how the above instructions were followed in your response.<br />
142
</>;
143
}
144
}
145
146
const clauseExampleConfiguration = `\`\`\`json
147
{
148
"mcpServers": {
149
"redis": {
150
"command": "npx",
151
"args": [
152
"@modelcontextprotocol/server-redis",
153
"redis://localhost:6379"
154
]
155
}
156
}
157
}
158
\`\`\``;
159
160
const redisExampleReadme = `<readme>
161
# Redis
162
163
A Model Context Protocol server that provides access to Redis databases. This server enables LLMs to interact with Redis key-value stores through a set of standardized tools.
164
165
## Components
166
167
### Tools
168
169
- **set**
170
- Set a Redis key-value pair with optional expiration
171
- Input:
172
- \`key\` (string): Redis key
173
- \`value\` (string): Value to store
174
- \`expireSeconds\` (number, optional): Expiration time in seconds
175
176
- **get**
177
- Get value by key from Redis
178
- Input: \`key\` (string): Redis key to retrieve
179
180
- **delete**
181
- Delete one or more keys from Redis
182
- Input: \`key\` (string | string[]): Key or array of keys to delete
183
184
- **list**
185
- List Redis keys matching a pattern
186
- Input: \`pattern\` (string, optional): Pattern to match keys (default: *)
187
188
## Usage with Claude Desktop
189
190
To use this server with the Claude Desktop app, add the following configuration to the "mcpServers" section of your \`claude_desktop_config.json\`:
191
192
### Docker
193
194
* when running docker on macos, use host.docker.internal if the server is running on the host network (eg localhost)
195
* Redis URL can be specified as an argument, defaults to "redis://localhost:6379"
196
197
\`\`\`json
198
{
199
"mcpServers": {
200
"redis": {
201
"command": "docker",
202
"args": [
203
"run",
204
"-i",
205
"--rm",
206
"mcp/redis",
207
"redis://host.docker.internal:6379"]
208
}
209
}
210
}
211
\`\`\`
212
213
### NPX
214
215
${clauseExampleConfiguration}
216
</readme>`;
217
218
const redisExampleConfig = `
219
\`\`\`json
220
{
221
"name": "redis",
222
"command": "npx",
223
"args": [
224
"@modelcontextprotocol/server-redis",
225
"redis://redis.example.com:3000"
226
]
227
}
228
\`\`\`
229
`;
230
231