Skip to content

Commit 332d8e9

Browse files
committed
vscode extension tool interface
1 parent b69825b commit 332d8e9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1285
-24
lines changed

ToolExtensions.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
## Extension Tool API
2+
3+
Roo now provides an API for VSCode extensions to register custom tools that can be used by Roo. This allows other extensions to extend Roo's capabilities without modifying the Roo codebase.
4+
5+
### Using the Extension Tool API
6+
7+
To use the Extension Tool API, your extension needs to:
8+
9+
1. Add `RooVeterinaryInc.roo-cline` as an extension dependency in your `package.json`.
10+
2. Get the Roo extension API and access the `extensionTools` property.
11+
3. Register your tools using the `registerTool` method.
12+
13+
Here's a simple example:
14+
15+
```typescript
16+
// Access the Roo extension
17+
const rooExtension = vscode.extensions.getExtension<RooAPI>("RooVeterinaryInc.roo-cline")
18+
19+
if (rooExtension && rooExtension.exports.extensionTools) {
20+
// Register a tool
21+
rooExtension.exports.extensionTools.registerTool(context.extension.id, {
22+
name: "my_tool",
23+
description: "Description of what the tool does",
24+
inputSchema: {
25+
// Optional JSON schema for tool arguments
26+
type: "object",
27+
properties: {
28+
myArg: {
29+
type: "string",
30+
description: "Description of the argument",
31+
},
32+
},
33+
},
34+
execute: async (args) => {
35+
// Implement your tool functionality
36+
return {
37+
content: [
38+
{
39+
type: "text",
40+
text: "Result of the tool execution",
41+
},
42+
],
43+
}
44+
},
45+
})
46+
}
47+
```
48+
49+
### Tool Response Format
50+
51+
Tools return responses in the same format as MCP tools:
52+
53+
```typescript
54+
{
55+
content: [
56+
{
57+
type: 'text',
58+
text: 'Text content'
59+
}
60+
// Can also include resources
61+
],
62+
isError?: boolean // Optional flag to indicate if the tool execution failed
63+
}
64+
```
65+
66+
### Example Extension
67+
68+
See the [Roo-NB](https://github.com/RooVeterinaryInc/Roo-NB) extension for a complete example of a tool provider extension.

evals/packages/types/src/roo-code.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,7 @@ export const globalSettingsSchema = z.object({
741741
alwaysApproveResubmit: z.boolean().optional(),
742742
requestDelaySeconds: z.number().optional(),
743743
alwaysAllowMcp: z.boolean().optional(),
744+
alwaysAllowExtTools: z.boolean().optional(),
744745
alwaysAllowModeSwitch: z.boolean().optional(),
745746
alwaysAllowSubtasks: z.boolean().optional(),
746747
alwaysAllowExecute: z.boolean().optional(),
@@ -820,6 +821,7 @@ const globalSettingsRecord: GlobalSettingsRecord = {
820821
alwaysApproveResubmit: undefined,
821822
requestDelaySeconds: undefined,
822823
alwaysAllowMcp: undefined,
824+
alwaysAllowExtTools: undefined,
823825
alwaysAllowModeSwitch: undefined,
824826
alwaysAllowSubtasks: undefined,
825827
alwaysAllowExecute: undefined,

src/core/assistant-message/presentAssistantMessage.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { askFollowupQuestionTool } from "../tools/askFollowupQuestionTool"
2626
import { switchModeTool } from "../tools/switchModeTool"
2727
import { attemptCompletionTool } from "../tools/attemptCompletionTool"
2828
import { newTaskTool } from "../tools/newTaskTool"
29+
import { useExtToolTool } from "../tools/useExtToolTool"
2930

3031
import { checkpointSave } from "../checkpoints"
3132

@@ -179,6 +180,8 @@ export async function presentAssistantMessage(cline: Task) {
179180
return `[${block.name} for '${block.params.server_name}']`
180181
case "access_mcp_resource":
181182
return `[${block.name} for '${block.params.server_name}']`
183+
case "use_ext_tool":
184+
return `[${block.name} for '${block.params.extension_id}']`
182185
case "ask_followup_question":
183186
return `[${block.name} for '${block.params.question}']`
184187
case "attempt_completion":
@@ -434,6 +437,9 @@ export async function presentAssistantMessage(cline: Task) {
434437
removeClosingTag,
435438
)
436439
break
440+
case "use_ext_tool":
441+
await useExtToolTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag)
442+
break
437443
case "ask_followup_question":
438444
await askFollowupQuestionTool(
439445
cline,

src/core/prompts/__tests__/custom-system-prompt.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ describe("File-Based Custom System Prompt", () => {
6767
"test/path", // Using a relative path without leading slash
6868
false, // supportsComputerUse
6969
undefined, // mcpHub
70+
undefined, // extensionToolManager
7071
undefined, // diffStrategy
7172
undefined, // browserViewportSize
7273
defaultModeSlug, // mode
@@ -101,6 +102,7 @@ describe("File-Based Custom System Prompt", () => {
101102
"test/path", // Using a relative path without leading slash
102103
false, // supportsComputerUse
103104
undefined, // mcpHub
105+
undefined, // extensionToolManager
104106
undefined, // diffStrategy
105107
undefined, // browserViewportSize
106108
defaultModeSlug, // mode
@@ -144,6 +146,7 @@ describe("File-Based Custom System Prompt", () => {
144146
"test/path", // Using a relative path without leading slash
145147
false, // supportsComputerUse
146148
undefined, // mcpHub
149+
undefined, // extensionToolManager
147150
undefined, // diffStrategy
148151
undefined, // browserViewportSize
149152
defaultModeSlug, // mode

src/core/prompts/__tests__/system.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ describe("SYSTEM_PROMPT", () => {
180180
"/test/path",
181181
false, // supportsComputerUse
182182
undefined, // mcpHub
183+
undefined, // extensionToolManager
183184
undefined, // diffStrategy
184185
undefined, // browserViewportSize
185186
defaultModeSlug, // mode
@@ -200,6 +201,7 @@ describe("SYSTEM_PROMPT", () => {
200201
"/test/path",
201202
true, // supportsComputerUse
202203
undefined, // mcpHub
204+
undefined, // extensionToolManager
203205
undefined, // diffStrategy
204206
"1280x800", // browserViewportSize
205207
defaultModeSlug, // mode
@@ -222,6 +224,7 @@ describe("SYSTEM_PROMPT", () => {
222224
"/test/path",
223225
false, // supportsComputerUse
224226
mockMcpHub, // mcpHub
227+
undefined, // extensionToolManager
225228
undefined, // diffStrategy
226229
undefined, // browserViewportSize
227230
defaultModeSlug, // mode
@@ -242,6 +245,7 @@ describe("SYSTEM_PROMPT", () => {
242245
"/test/path",
243246
false, // supportsComputerUse
244247
undefined, // explicitly undefined mcpHub
248+
undefined, // extensionToolManager
245249
undefined, // diffStrategy
246250
undefined, // browserViewportSize
247251
defaultModeSlug, // mode
@@ -262,6 +266,7 @@ describe("SYSTEM_PROMPT", () => {
262266
"/test/path",
263267
true, // supportsComputerUse
264268
undefined, // mcpHub
269+
undefined, // extensionToolManager
265270
undefined, // diffStrategy
266271
"900x600", // different viewport size
267272
defaultModeSlug, // mode
@@ -282,6 +287,7 @@ describe("SYSTEM_PROMPT", () => {
282287
"/test/path",
283288
false, // supportsComputerUse
284289
undefined, // mcpHub
290+
undefined, // extensionToolManager
285291
new MultiSearchReplaceDiffStrategy(), // Use actual diff strategy from the codebase
286292
undefined, // browserViewportSize
287293
defaultModeSlug, // mode
@@ -303,6 +309,7 @@ describe("SYSTEM_PROMPT", () => {
303309
"/test/path",
304310
false, // supportsComputerUse
305311
undefined, // mcpHub
312+
undefined, // extensionToolManager
306313
new MultiSearchReplaceDiffStrategy(), // Use actual diff strategy from the codebase
307314
undefined, // browserViewportSize
308315
defaultModeSlug, // mode
@@ -324,6 +331,7 @@ describe("SYSTEM_PROMPT", () => {
324331
"/test/path",
325332
false, // supportsComputerUse
326333
undefined, // mcpHub
334+
undefined, // extensionToolManager
327335
new MultiSearchReplaceDiffStrategy(), // Use actual diff strategy from the codebase
328336
undefined, // browserViewportSize
329337
defaultModeSlug, // mode
@@ -349,6 +357,7 @@ describe("SYSTEM_PROMPT", () => {
349357
"/test/path",
350358
false, // supportsComputerUse
351359
undefined, // mcpHub
360+
undefined, // extensionToolManager
352361
undefined, // diffStrategy
353362
undefined, // browserViewportSize
354363
defaultModeSlug, // mode
@@ -385,6 +394,7 @@ describe("SYSTEM_PROMPT", () => {
385394
"/test/path",
386395
false, // supportsComputerUse
387396
undefined, // mcpHub
397+
undefined, // extensionToolManager
388398
undefined, // diffStrategy
389399
undefined, // browserViewportSize
390400
"custom-mode", // mode
@@ -420,6 +430,7 @@ describe("SYSTEM_PROMPT", () => {
420430
"/test/path",
421431
false, // supportsComputerUse
422432
undefined, // mcpHub
433+
undefined, // extensionToolManager
423434
undefined, // diffStrategy
424435
undefined, // browserViewportSize
425436
defaultModeSlug as Mode, // mode
@@ -450,6 +461,7 @@ describe("SYSTEM_PROMPT", () => {
450461
"/test/path",
451462
false, // supportsComputerUse
452463
undefined, // mcpHub
464+
undefined, // extensionToolManager
453465
undefined, // diffStrategy
454466
undefined, // browserViewportSize
455467
defaultModeSlug as Mode, // mode
@@ -494,6 +506,7 @@ describe("addCustomInstructions", () => {
494506
"/test/path",
495507
false, // supportsComputerUse
496508
undefined, // mcpHub
509+
undefined, // extensionToolManager
497510
undefined, // diffStrategy
498511
undefined, // browserViewportSize
499512
"architect", // mode
@@ -514,6 +527,7 @@ describe("addCustomInstructions", () => {
514527
"/test/path",
515528
false, // supportsComputerUse
516529
undefined, // mcpHub
530+
undefined, // extensionToolManager
517531
undefined, // diffStrategy
518532
undefined, // browserViewportSize
519533
"ask", // mode
@@ -536,6 +550,7 @@ describe("addCustomInstructions", () => {
536550
"/test/path",
537551
false, // supportsComputerUse
538552
mockMcpHub, // mcpHub
553+
undefined, // extensionToolManager
539554
undefined, // diffStrategy
540555
undefined, // browserViewportSize
541556
defaultModeSlug, // mode
@@ -559,6 +574,7 @@ describe("addCustomInstructions", () => {
559574
"/test/path",
560575
false, // supportsComputerUse
561576
mockMcpHub, // mcpHub
577+
undefined, // extensionToolManager
562578
undefined, // diffStrategy
563579
undefined, // browserViewportSize
564580
defaultModeSlug, // mode

src/core/prompts/responses.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ Otherwise, if you have not completed the task and do not need additional informa
7171
invalidMcpToolArgumentError: (serverName: string, toolName: string) =>
7272
`Invalid JSON argument used with ${serverName} for ${toolName}. Please retry with a properly formatted JSON argument.`,
7373

74+
invalidExtToolArgumentError: (extensionId: string, toolName: string) =>
75+
`Invalid JSON argument used with ${extensionId} for ${toolName}. Please retry with a properly formatted JSON argument.`,
76+
7477
toolResult: (
7578
text: string,
7679
images?: string[],
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { ExtensionToolManager } from "../../../services/extensions/ExtensionToolManager"
2+
3+
/**
4+
* Generates the section of the system prompt that describes available extension tools
5+
*/
6+
export async function getExtToolsSection(extensionToolManager?: ExtensionToolManager): Promise<string> {
7+
// If no manager is provided, get the singleton instance
8+
if (!extensionToolManager) {
9+
try {
10+
extensionToolManager = await ExtensionToolManager.getInstance()
11+
} catch (error) {
12+
console.error("Failed to get ExtensionToolManager:", error)
13+
return ""
14+
}
15+
}
16+
17+
// Get all registered tools
18+
const allTools = extensionToolManager.getAllTools()
19+
20+
if (allTools.length === 0) {
21+
return ""
22+
}
23+
24+
// Group tools by extension
25+
const extensionTools: Record<string, string[]> = {}
26+
27+
for (const { extensionId, tool } of allTools) {
28+
if (!extensionTools[extensionId]) {
29+
extensionTools[extensionId] = []
30+
}
31+
32+
extensionTools[extensionId].push(`${tool.name}: ${tool.description}`)
33+
}
34+
35+
let result = "# Available Extension Tools\n\n"
36+
37+
for (const [extensionId, tools] of Object.entries(extensionTools)) {
38+
result += `## Extension: ${extensionId}\n\n`
39+
40+
for (const toolDesc of tools) {
41+
result += `- ${toolDesc}\n`
42+
}
43+
44+
result += "\n"
45+
}
46+
47+
return result
48+
}

src/core/prompts/sections/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export { getObjectiveSection } from "./objective"
44
export { addCustomInstructions } from "./custom-instructions"
55
export { getSharedToolUseSection } from "./tool-use"
66
export { getMcpServersSection } from "./mcp-servers"
7+
export { getExtToolsSection } from "./ext-tools"
78
export { getToolUseGuidelinesSection } from "./tool-use-guidelines"
89
export { getCapabilitiesSection } from "./capabilities"
910
export { getModesSection } from "./modes"

0 commit comments

Comments
 (0)