|
|
@@ -93,6 +93,8 @@ import { listCodeDefinitionNamesTool } from "./tools/listCodeDefinitionNamesTool
|
|
|
import { searchFilesTool } from "./tools/searchFilesTool"
|
|
|
import { browserActionTool } from "./tools/browserActionTool"
|
|
|
import { executeCommandTool } from "./tools/executeCommandTool"
|
|
|
+import { useMcpToolTool } from "./tools/useMcpToolTool"
|
|
|
+import { accessMcpResourceTool } from "./tools/accessMcpResourceTool"
|
|
|
|
|
|
export type ToolResponse = string | Array<Anthropic.TextBlockParam | Anthropic.ImageBlockParam>
|
|
|
type UserContent = Array<Anthropic.Messages.ContentBlockParam>
|
|
|
@@ -1630,170 +1632,19 @@ export class Cline extends EventEmitter<ClineEvents> {
|
|
|
break
|
|
|
}
|
|
|
case "use_mcp_tool": {
|
|
|
- const server_name: string | undefined = block.params.server_name
|
|
|
- const tool_name: string | undefined = block.params.tool_name
|
|
|
- const mcp_arguments: string | undefined = block.params.arguments
|
|
|
- try {
|
|
|
- if (block.partial) {
|
|
|
- const partialMessage = JSON.stringify({
|
|
|
- type: "use_mcp_tool",
|
|
|
- serverName: removeClosingTag("server_name", server_name),
|
|
|
- toolName: removeClosingTag("tool_name", tool_name),
|
|
|
- arguments: removeClosingTag("arguments", mcp_arguments),
|
|
|
- } satisfies ClineAskUseMcpServer)
|
|
|
- await this.ask("use_mcp_server", partialMessage, block.partial).catch(() => {})
|
|
|
- break
|
|
|
- } else {
|
|
|
- if (!server_name) {
|
|
|
- this.consecutiveMistakeCount++
|
|
|
- pushToolResult(
|
|
|
- await this.sayAndCreateMissingParamError("use_mcp_tool", "server_name"),
|
|
|
- )
|
|
|
- break
|
|
|
- }
|
|
|
- if (!tool_name) {
|
|
|
- this.consecutiveMistakeCount++
|
|
|
- pushToolResult(
|
|
|
- await this.sayAndCreateMissingParamError("use_mcp_tool", "tool_name"),
|
|
|
- )
|
|
|
- break
|
|
|
- }
|
|
|
- // arguments are optional, but if they are provided they must be valid JSON
|
|
|
- // if (!mcp_arguments) {
|
|
|
- // this.consecutiveMistakeCount++
|
|
|
- // pushToolResult(await this.sayAndCreateMissingParamError("use_mcp_tool", "arguments"))
|
|
|
- // break
|
|
|
- // }
|
|
|
- let parsedArguments: Record<string, unknown> | undefined
|
|
|
- if (mcp_arguments) {
|
|
|
- try {
|
|
|
- parsedArguments = JSON.parse(mcp_arguments)
|
|
|
- } catch (error) {
|
|
|
- this.consecutiveMistakeCount++
|
|
|
- await this.say(
|
|
|
- "error",
|
|
|
- `Roo tried to use ${tool_name} with an invalid JSON argument. Retrying...`,
|
|
|
- )
|
|
|
- pushToolResult(
|
|
|
- formatResponse.toolError(
|
|
|
- formatResponse.invalidMcpToolArgumentError(server_name, tool_name),
|
|
|
- ),
|
|
|
- )
|
|
|
- break
|
|
|
- }
|
|
|
- }
|
|
|
- this.consecutiveMistakeCount = 0
|
|
|
- const completeMessage = JSON.stringify({
|
|
|
- type: "use_mcp_tool",
|
|
|
- serverName: server_name,
|
|
|
- toolName: tool_name,
|
|
|
- arguments: mcp_arguments,
|
|
|
- } satisfies ClineAskUseMcpServer)
|
|
|
- const didApprove = await askApproval("use_mcp_server", completeMessage)
|
|
|
- if (!didApprove) {
|
|
|
- break
|
|
|
- }
|
|
|
- // now execute the tool
|
|
|
- await this.say("mcp_server_request_started") // same as browser_action_result
|
|
|
- const toolResult = await this.providerRef
|
|
|
- .deref()
|
|
|
- ?.getMcpHub()
|
|
|
- ?.callTool(server_name, tool_name, parsedArguments)
|
|
|
-
|
|
|
- // TODO: add progress indicator and ability to parse images and non-text responses
|
|
|
- const toolResultPretty =
|
|
|
- (toolResult?.isError ? "Error:\n" : "") +
|
|
|
- toolResult?.content
|
|
|
- .map((item) => {
|
|
|
- if (item.type === "text") {
|
|
|
- return item.text
|
|
|
- }
|
|
|
- if (item.type === "resource") {
|
|
|
- const { blob, ...rest } = item.resource
|
|
|
- return JSON.stringify(rest, null, 2)
|
|
|
- }
|
|
|
- return ""
|
|
|
- })
|
|
|
- .filter(Boolean)
|
|
|
- .join("\n\n") || "(No response)"
|
|
|
- await this.say("mcp_server_response", toolResultPretty)
|
|
|
- pushToolResult(formatResponse.toolResult(toolResultPretty))
|
|
|
- break
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- await handleError("executing MCP tool", error)
|
|
|
- break
|
|
|
- }
|
|
|
+ await useMcpToolTool(this, block, askApproval, handleError, pushToolResult, removeClosingTag)
|
|
|
+ break
|
|
|
}
|
|
|
case "access_mcp_resource": {
|
|
|
- const server_name: string | undefined = block.params.server_name
|
|
|
- const uri: string | undefined = block.params.uri
|
|
|
- try {
|
|
|
- if (block.partial) {
|
|
|
- const partialMessage = JSON.stringify({
|
|
|
- type: "access_mcp_resource",
|
|
|
- serverName: removeClosingTag("server_name", server_name),
|
|
|
- uri: removeClosingTag("uri", uri),
|
|
|
- } satisfies ClineAskUseMcpServer)
|
|
|
- await this.ask("use_mcp_server", partialMessage, block.partial).catch(() => {})
|
|
|
- break
|
|
|
- } else {
|
|
|
- if (!server_name) {
|
|
|
- this.consecutiveMistakeCount++
|
|
|
- pushToolResult(
|
|
|
- await this.sayAndCreateMissingParamError("access_mcp_resource", "server_name"),
|
|
|
- )
|
|
|
- break
|
|
|
- }
|
|
|
- if (!uri) {
|
|
|
- this.consecutiveMistakeCount++
|
|
|
- pushToolResult(
|
|
|
- await this.sayAndCreateMissingParamError("access_mcp_resource", "uri"),
|
|
|
- )
|
|
|
- break
|
|
|
- }
|
|
|
- this.consecutiveMistakeCount = 0
|
|
|
- const completeMessage = JSON.stringify({
|
|
|
- type: "access_mcp_resource",
|
|
|
- serverName: server_name,
|
|
|
- uri,
|
|
|
- } satisfies ClineAskUseMcpServer)
|
|
|
- const didApprove = await askApproval("use_mcp_server", completeMessage)
|
|
|
- if (!didApprove) {
|
|
|
- break
|
|
|
- }
|
|
|
- // now execute the tool
|
|
|
- await this.say("mcp_server_request_started")
|
|
|
- const resourceResult = await this.providerRef
|
|
|
- .deref()
|
|
|
- ?.getMcpHub()
|
|
|
- ?.readResource(server_name, uri)
|
|
|
- const resourceResultPretty =
|
|
|
- resourceResult?.contents
|
|
|
- .map((item) => {
|
|
|
- if (item.text) {
|
|
|
- return item.text
|
|
|
- }
|
|
|
- return ""
|
|
|
- })
|
|
|
- .filter(Boolean)
|
|
|
- .join("\n\n") || "(Empty response)"
|
|
|
-
|
|
|
- // handle images (image must contain mimetype and blob)
|
|
|
- let images: string[] = []
|
|
|
- resourceResult?.contents.forEach((item) => {
|
|
|
- if (item.mimeType?.startsWith("image") && item.blob) {
|
|
|
- images.push(item.blob)
|
|
|
- }
|
|
|
- })
|
|
|
- await this.say("mcp_server_response", resourceResultPretty, images)
|
|
|
- pushToolResult(formatResponse.toolResult(resourceResultPretty, images))
|
|
|
- break
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- await handleError("accessing MCP resource", error)
|
|
|
- break
|
|
|
- }
|
|
|
+ await accessMcpResourceTool(
|
|
|
+ this,
|
|
|
+ block,
|
|
|
+ askApproval,
|
|
|
+ handleError,
|
|
|
+ pushToolResult,
|
|
|
+ removeClosingTag,
|
|
|
+ )
|
|
|
+ break
|
|
|
}
|
|
|
case "ask_followup_question": {
|
|
|
const question: string | undefined = block.params.question
|