Skip to content

Commit bfecce5

Browse files
committed
feat: add access_mcp_resource as native tool and update MCP server instructions
- Refactored accessMcpResourceTool to extend BaseTool class - Added native tool definition for access_mcp_resource - Updated MCP servers section to provide different instructions based on protocol - XML mode: mentions use_mcp_tool wrapper - Native mode: explains mcp_{server}_{tool} naming pattern - Registered access_mcp_resource in native tools list
1 parent b1643ca commit bfecce5

File tree

5 files changed

+100
-40
lines changed

5 files changed

+100
-40
lines changed

src/core/assistant-message/NativeToolCallParser.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,9 +256,9 @@ export class NativeToolCallParser {
256256

257257
/**
258258
* Finalize a streaming tool call.
259-
* Parses the complete JSON and returns the final ToolUse.
259+
* Parses the complete JSON and returns the final ToolUse or McpToolUse.
260260
*/
261-
public static finalizeStreamingToolCall(id: string): ToolUse | null {
261+
public static finalizeStreamingToolCall(id: string): ToolUse | McpToolUse | null {
262262
const toolCall = this.streamingToolCalls.get(id)
263263
if (!toolCall) {
264264
console.warn(`[NativeToolCallParser] Attempting to finalize unknown tool call: ${id}`)

src/core/prompts/sections/mcp-servers.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ export async function getMcpServersSection(
5353
.join("\n\n")}`
5454
: "(No MCP servers currently connected)"
5555

56+
// Different instructions based on protocol
57+
const toolAccessInstructions = includeToolDescriptions
58+
? `When a server is connected, you can use the server's tools via the \`use_mcp_tool\` tool, and access the server's resources via the \`access_mcp_resource\` tool.`
59+
: `When a server is connected, each server's tools are available as native tools with the naming pattern \`mcp_{server_name}_{tool_name}\`. For example, a tool named 'get_forecast' from a server named 'weather' would be available as \`mcp_weather_get_forecast\`. You can also access server resources using the \`access_mcp_resource\` tool.`
60+
5661
const baseSection = `MCP SERVERS
5762
5863
The Model Context Protocol (MCP) enables communication between the system and MCP servers that provide additional tools and resources to extend your capabilities. MCP servers can be one of two types:
@@ -62,7 +67,7 @@ The Model Context Protocol (MCP) enables communication between the system and MC
6267
6368
# Connected MCP Servers
6469
65-
When a server is connected, you can use the server's tools via the \`use_mcp_tool\` tool, and access the server's resources via the \`access_mcp_resource\` tool.
70+
${toolAccessInstructions}
6671
6772
${connectedServers}`
6873

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import type OpenAI from "openai"
2+
3+
const ACCESS_MCP_RESOURCE_DESCRIPTION = `Request to access a resource provided by a connected MCP server. Resources represent data sources that can be used as context, such as files, API responses, or system information.
4+
5+
Parameters:
6+
- server_name: (required) The name of the MCP server providing the resource
7+
- uri: (required) The URI identifying the specific resource to access
8+
9+
Example: Accessing a weather resource
10+
{ "server_name": "weather-server", "uri": "weather://san-francisco/current" }
11+
12+
Example: Accessing a file resource from an MCP server
13+
{ "server_name": "filesystem-server", "uri": "file:///path/to/data.json" }`
14+
15+
const SERVER_NAME_PARAMETER_DESCRIPTION = `The name of the MCP server providing the resource`
16+
17+
const URI_PARAMETER_DESCRIPTION = `The URI identifying the specific resource to access`
18+
19+
export default {
20+
type: "function",
21+
function: {
22+
name: "access_mcp_resource",
23+
description: ACCESS_MCP_RESOURCE_DESCRIPTION,
24+
strict: true,
25+
parameters: {
26+
type: "object",
27+
properties: {
28+
server_name: {
29+
type: "string",
30+
description: SERVER_NAME_PARAMETER_DESCRIPTION,
31+
},
32+
uri: {
33+
type: "string",
34+
description: URI_PARAMETER_DESCRIPTION,
35+
},
36+
},
37+
required: ["server_name", "uri"],
38+
additionalProperties: false,
39+
},
40+
},
41+
} satisfies OpenAI.Chat.ChatCompletionTool

src/core/prompts/tools/native-tools/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type OpenAI from "openai"
2+
import accessMcpResource from "./access_mcp_resource"
23
import askFollowupQuestion from "./ask_followup_question"
34
import attemptCompletion from "./attempt_completion"
45
import browserAction from "./browser_action"
@@ -29,6 +30,7 @@ export { convertOpenAIToolToAnthropic, convertOpenAIToolsToAnthropic } from "./c
2930
*/
3031
export function getNativeTools(partialReadsEnabled: boolean = true): OpenAI.Chat.ChatCompletionTool[] {
3132
return [
33+
accessMcpResource,
3234
apply_diff_single_file,
3335
askFollowupQuestion,
3436
attemptCompletion,
Lines changed: 49 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,44 @@
11
import { ClineAskUseMcpServer } from "../../shared/ExtensionMessage"
2-
import { ToolUse, RemoveClosingTag, AskApproval, HandleError, PushToolResult } from "../../shared/tools"
2+
import type { ToolUse } from "../../shared/tools"
33
import { Task } from "../task/Task"
44
import { formatResponse } from "../prompts/responses"
5+
import { BaseTool, ToolCallbacks } from "./BaseTool"
56

6-
export async function accessMcpResourceTool(
7-
cline: Task,
8-
block: ToolUse,
9-
askApproval: AskApproval,
10-
handleError: HandleError,
11-
pushToolResult: PushToolResult,
12-
removeClosingTag: RemoveClosingTag,
13-
) {
14-
const server_name: string | undefined = block.params.server_name
15-
const uri: string | undefined = block.params.uri
16-
17-
try {
18-
if (block.partial) {
19-
const partialMessage = JSON.stringify({
20-
type: "access_mcp_resource",
21-
serverName: removeClosingTag("server_name", server_name),
22-
uri: removeClosingTag("uri", uri),
23-
} satisfies ClineAskUseMcpServer)
7+
interface AccessMcpResourceParams {
8+
server_name: string
9+
uri: string
10+
}
11+
12+
export class AccessMcpResourceTool extends BaseTool<"access_mcp_resource"> {
13+
readonly name = "access_mcp_resource" as const
14+
15+
parseLegacy(params: Partial<Record<string, string>>): AccessMcpResourceParams {
16+
return {
17+
server_name: params.server_name || "",
18+
uri: params.uri || "",
19+
}
20+
}
2421

25-
await cline.ask("use_mcp_server", partialMessage, block.partial).catch(() => {})
26-
return
27-
} else {
22+
async execute(params: AccessMcpResourceParams, task: Task, callbacks: ToolCallbacks): Promise<void> {
23+
const { askApproval, handleError, pushToolResult, toolProtocol } = callbacks
24+
const { server_name, uri } = params
25+
26+
try {
2827
if (!server_name) {
29-
cline.consecutiveMistakeCount++
30-
cline.recordToolError("access_mcp_resource")
31-
pushToolResult(await cline.sayAndCreateMissingParamError("access_mcp_resource", "server_name"))
28+
task.consecutiveMistakeCount++
29+
task.recordToolError("access_mcp_resource")
30+
pushToolResult(await task.sayAndCreateMissingParamError("access_mcp_resource", "server_name"))
3231
return
3332
}
3433

3534
if (!uri) {
36-
cline.consecutiveMistakeCount++
37-
cline.recordToolError("access_mcp_resource")
38-
pushToolResult(await cline.sayAndCreateMissingParamError("access_mcp_resource", "uri"))
35+
task.consecutiveMistakeCount++
36+
task.recordToolError("access_mcp_resource")
37+
pushToolResult(await task.sayAndCreateMissingParamError("access_mcp_resource", "uri"))
3938
return
4039
}
4140

42-
cline.consecutiveMistakeCount = 0
41+
task.consecutiveMistakeCount = 0
4342

4443
const completeMessage = JSON.stringify({
4544
type: "access_mcp_resource",
@@ -50,12 +49,13 @@ export async function accessMcpResourceTool(
5049
const didApprove = await askApproval("use_mcp_server", completeMessage)
5150

5251
if (!didApprove) {
52+
pushToolResult(formatResponse.toolDenied(toolProtocol))
5353
return
5454
}
5555

5656
// Now execute the tool
57-
await cline.say("mcp_server_request_started")
58-
const resourceResult = await cline.providerRef.deref()?.getMcpHub()?.readResource(server_name, uri)
57+
await task.say("mcp_server_request_started")
58+
const resourceResult = await task.providerRef.deref()?.getMcpHub()?.readResource(server_name, uri)
5959

6060
const resourceResultPretty =
6161
resourceResult?.contents
@@ -81,13 +81,25 @@ export async function accessMcpResourceTool(
8181
}
8282
})
8383

84-
await cline.say("mcp_server_response", resourceResultPretty, images)
84+
await task.say("mcp_server_response", resourceResultPretty, images)
8585
pushToolResult(formatResponse.toolResult(resourceResultPretty, images))
86-
87-
return
86+
} catch (error) {
87+
await handleError("accessing MCP resource", error instanceof Error ? error : new Error(String(error)))
8888
}
89-
} catch (error) {
90-
await handleError("accessing MCP resource", error)
91-
return
89+
}
90+
91+
override async handlePartial(task: Task, block: ToolUse<"access_mcp_resource">): Promise<void> {
92+
const server_name = this.removeClosingTag("server_name", block.params.server_name, true)
93+
const uri = this.removeClosingTag("uri", block.params.uri, true)
94+
95+
const partialMessage = JSON.stringify({
96+
type: "access_mcp_resource",
97+
serverName: server_name,
98+
uri: uri,
99+
} satisfies ClineAskUseMcpServer)
100+
101+
await task.ask("use_mcp_server", partialMessage, block.partial).catch(() => {})
92102
}
93103
}
104+
105+
export const accessMcpResourceTool = new AccessMcpResourceTool()

0 commit comments

Comments
 (0)