Skip to content

Commit a4d1e1d

Browse files
committed
vscode extension tool interface
1 parent 61a381a commit a4d1e1d

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
@@ -743,6 +743,7 @@ export const globalSettingsSchema = z.object({
743743
alwaysApproveResubmit: z.boolean().optional(),
744744
requestDelaySeconds: z.number().optional(),
745745
alwaysAllowMcp: z.boolean().optional(),
746+
alwaysAllowExtTools: z.boolean().optional(),
746747
alwaysAllowModeSwitch: z.boolean().optional(),
747748
alwaysAllowSubtasks: z.boolean().optional(),
748749
alwaysAllowExecute: z.boolean().optional(),
@@ -822,6 +823,7 @@ const globalSettingsRecord: GlobalSettingsRecord = {
822823
alwaysApproveResubmit: undefined,
823824
requestDelaySeconds: undefined,
824825
alwaysAllowMcp: undefined,
826+
alwaysAllowExtTools: undefined,
825827
alwaysAllowModeSwitch: undefined,
826828
alwaysAllowSubtasks: undefined,
827829
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

@@ -180,6 +181,8 @@ export async function presentAssistantMessage(cline: Task) {
180181
return `[${block.name} for '${block.params.server_name}']`
181182
case "access_mcp_resource":
182183
return `[${block.name} for '${block.params.server_name}']`
184+
case "use_ext_tool":
185+
return `[${block.name} for '${block.params.extension_id}']`
183186
case "ask_followup_question":
184187
return `[${block.name} for '${block.params.question}']`
185188
case "attempt_completion":
@@ -440,6 +443,9 @@ export async function presentAssistantMessage(cline: Task) {
440443
removeClosingTag,
441444
)
442445
break
446+
case "use_ext_tool":
447+
await useExtToolTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag)
448+
break
443449
case "ask_followup_question":
444450
await askFollowupQuestionTool(
445451
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
@@ -202,6 +202,7 @@ describe("SYSTEM_PROMPT", () => {
202202
"/test/path",
203203
false, // supportsComputerUse
204204
undefined, // mcpHub
205+
undefined, // extensionToolManager
205206
undefined, // diffStrategy
206207
undefined, // browserViewportSize
207208
defaultModeSlug, // mode
@@ -222,6 +223,7 @@ describe("SYSTEM_PROMPT", () => {
222223
"/test/path",
223224
true, // supportsComputerUse
224225
undefined, // mcpHub
226+
undefined, // extensionToolManager
225227
undefined, // diffStrategy
226228
"1280x800", // browserViewportSize
227229
defaultModeSlug, // mode
@@ -244,6 +246,7 @@ describe("SYSTEM_PROMPT", () => {
244246
"/test/path",
245247
false, // supportsComputerUse
246248
mockMcpHub, // mcpHub
249+
undefined, // extensionToolManager
247250
undefined, // diffStrategy
248251
undefined, // browserViewportSize
249252
defaultModeSlug, // mode
@@ -264,6 +267,7 @@ describe("SYSTEM_PROMPT", () => {
264267
"/test/path",
265268
false, // supportsComputerUse
266269
undefined, // explicitly undefined mcpHub
270+
undefined, // extensionToolManager
267271
undefined, // diffStrategy
268272
undefined, // browserViewportSize
269273
defaultModeSlug, // mode
@@ -284,6 +288,7 @@ describe("SYSTEM_PROMPT", () => {
284288
"/test/path",
285289
true, // supportsComputerUse
286290
undefined, // mcpHub
291+
undefined, // extensionToolManager
287292
undefined, // diffStrategy
288293
"900x600", // different viewport size
289294
defaultModeSlug, // mode
@@ -304,6 +309,7 @@ describe("SYSTEM_PROMPT", () => {
304309
"/test/path",
305310
false, // supportsComputerUse
306311
undefined, // mcpHub
312+
undefined, // extensionToolManager
307313
new MultiSearchReplaceDiffStrategy(), // Use actual diff strategy from the codebase
308314
undefined, // browserViewportSize
309315
defaultModeSlug, // mode
@@ -325,6 +331,7 @@ describe("SYSTEM_PROMPT", () => {
325331
"/test/path",
326332
false, // supportsComputerUse
327333
undefined, // mcpHub
334+
undefined, // extensionToolManager
328335
new MultiSearchReplaceDiffStrategy(), // Use actual diff strategy from the codebase
329336
undefined, // browserViewportSize
330337
defaultModeSlug, // mode
@@ -346,6 +353,7 @@ describe("SYSTEM_PROMPT", () => {
346353
"/test/path",
347354
false, // supportsComputerUse
348355
undefined, // mcpHub
356+
undefined, // extensionToolManager
349357
new MultiSearchReplaceDiffStrategy(), // Use actual diff strategy from the codebase
350358
undefined, // browserViewportSize
351359
defaultModeSlug, // mode
@@ -394,6 +402,7 @@ describe("SYSTEM_PROMPT", () => {
394402
"/test/path",
395403
false, // supportsComputerUse
396404
undefined, // mcpHub
405+
undefined, // extensionToolManager
397406
undefined, // diffStrategy
398407
undefined, // browserViewportSize
399408
defaultModeSlug, // mode
@@ -452,6 +461,7 @@ describe("SYSTEM_PROMPT", () => {
452461
"/test/path",
453462
false, // supportsComputerUse
454463
undefined, // mcpHub
464+
undefined, // extensionToolManager
455465
undefined, // diffStrategy
456466
undefined, // browserViewportSize
457467
"custom-mode", // mode
@@ -487,6 +497,7 @@ describe("SYSTEM_PROMPT", () => {
487497
"/test/path",
488498
false, // supportsComputerUse
489499
undefined, // mcpHub
500+
undefined, // extensionToolManager
490501
undefined, // diffStrategy
491502
undefined, // browserViewportSize
492503
defaultModeSlug as Mode, // mode
@@ -517,6 +528,7 @@ describe("SYSTEM_PROMPT", () => {
517528
"/test/path",
518529
false, // supportsComputerUse
519530
undefined, // mcpHub
531+
undefined, // extensionToolManager
520532
undefined, // diffStrategy
521533
undefined, // browserViewportSize
522534
defaultModeSlug as Mode, // mode
@@ -561,6 +573,7 @@ describe("addCustomInstructions", () => {
561573
"/test/path",
562574
false, // supportsComputerUse
563575
undefined, // mcpHub
576+
undefined, // extensionToolManager
564577
undefined, // diffStrategy
565578
undefined, // browserViewportSize
566579
"architect", // mode
@@ -581,6 +594,7 @@ describe("addCustomInstructions", () => {
581594
"/test/path",
582595
false, // supportsComputerUse
583596
undefined, // mcpHub
597+
undefined, // extensionToolManager
584598
undefined, // diffStrategy
585599
undefined, // browserViewportSize
586600
"ask", // mode
@@ -603,6 +617,7 @@ describe("addCustomInstructions", () => {
603617
"/test/path",
604618
false, // supportsComputerUse
605619
mockMcpHub, // mcpHub
620+
undefined, // extensionToolManager
606621
undefined, // diffStrategy
607622
undefined, // browserViewportSize
608623
defaultModeSlug, // mode
@@ -626,6 +641,7 @@ describe("addCustomInstructions", () => {
626641
"/test/path",
627642
false, // supportsComputerUse
628643
mockMcpHub, // mcpHub
644+
undefined, // extensionToolManager
629645
undefined, // diffStrategy
630646
undefined, // browserViewportSize
631647
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)