This commit is contained in:
Deleted user 2026-05-27 10:52:44 +08:00 committed by GitHub
commit 5366c331e3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 225 additions and 1 deletions

View file

@ -0,0 +1,156 @@
import type { McpServer, McpTool } from "@roo-code/types"
import type { McpHub } from "../../../../services/mcp/McpHub"
import { getMcpServersSection } from "../mcp-servers"
describe("getMcpServersSection", () => {
const createMockTool = (name: string, description = "Test tool", enabledForPrompt?: boolean): McpTool => ({
name,
description,
inputSchema: {
type: "object",
properties: {},
},
...(enabledForPrompt !== undefined ? { enabledForPrompt } : {}),
})
const createMockServer = (
name: string,
tools: McpTool[],
options: { instructions?: string; source?: "global" | "project" } = {},
): McpServer => ({
name,
config: JSON.stringify({ type: "stdio", command: "test" }),
status: "connected",
source: options.source ?? "global",
tools,
instructions: options.instructions,
})
const createMockMcpHub = (servers: McpServer[]): Partial<McpHub> => ({
getServers: vi.fn().mockReturnValue(servers),
})
it("should return empty string when mcpHub is undefined", () => {
const result = getMcpServersSection(undefined)
expect(result).toBe("")
})
it("should return empty string when no servers are available", () => {
const mockHub = createMockMcpHub([])
const result = getMcpServersSection(mockHub as McpHub)
expect(result).toBe("")
})
it("should return empty string when servers have no enabled tools", () => {
const server = createMockServer("testServer", [])
const mockHub = createMockMcpHub([server])
const result = getMcpServersSection(mockHub as McpHub)
expect(result).toBe("")
})
it("should return empty string when all tools are disabled", () => {
const server = createMockServer("testServer", [
createMockTool("tool1", "Tool 1", false),
createMockTool("tool2", "Tool 2", false),
])
const mockHub = createMockMcpHub([server])
const result = getMcpServersSection(mockHub as McpHub)
expect(result).toBe("")
})
it("should include MCP SERVERS header", () => {
const server = createMockServer("testServer", [createMockTool("testTool")])
const mockHub = createMockMcpHub([server])
const result = getMcpServersSection(mockHub as McpHub)
expect(result).toContain("MCP SERVERS")
})
it("should explain the naming convention", () => {
const server = createMockServer("testServer", [createMockTool("testTool")])
const mockHub = createMockMcpHub([server])
const result = getMcpServersSection(mockHub as McpHub)
expect(result).toContain("mcp--serverName--toolName")
})
it("should list server name as heading", () => {
const server = createMockServer("context7", [createMockTool("resolve-library-id")])
const mockHub = createMockMcpHub([server])
const result = getMcpServersSection(mockHub as McpHub)
expect(result).toContain("## context7")
})
it("should list tool names using the mcp-- naming convention", () => {
const server = createMockServer("context7", [
createMockTool("resolve-library-id"),
createMockTool("get-library-docs"),
])
const mockHub = createMockMcpHub([server])
const result = getMcpServersSection(mockHub as McpHub)
expect(result).toContain("mcp--context7--resolve-library-id")
expect(result).toContain("mcp--context7--get-library-docs")
})
it("should include server instructions when provided", () => {
const server = createMockServer("conport", [createMockTool("init-db")], {
instructions: "Always initialize the database before performing queries.",
})
const mockHub = createMockMcpHub([server])
const result = getMcpServersSection(mockHub as McpHub)
expect(result).toContain("Server Instructions:")
expect(result).toContain("Always initialize the database before performing queries.")
})
it("should not include server instructions section when not provided", () => {
const server = createMockServer("testServer", [createMockTool("testTool")])
const mockHub = createMockMcpHub([server])
const result = getMcpServersSection(mockHub as McpHub)
expect(result).not.toContain("Server Instructions:")
})
it("should list multiple servers", () => {
const server1 = createMockServer("context7", [createMockTool("resolve-library-id")])
const server2 = createMockServer("git", [createMockTool("git-status")])
const mockHub = createMockMcpHub([server1, server2])
const result = getMcpServersSection(mockHub as McpHub)
expect(result).toContain("## context7")
expect(result).toContain("## git")
expect(result).toContain("mcp--context7--resolve-library-id")
expect(result).toContain("mcp--git--git-status")
})
it("should filter out disabled tools", () => {
const server = createMockServer("testServer", [
createMockTool("enabledTool", "Enabled tool"),
createMockTool("disabledTool", "Disabled tool", false),
])
const mockHub = createMockMcpHub([server])
const result = getMcpServersSection(mockHub as McpHub)
expect(result).toContain("mcp--testServer--enabledTool")
expect(result).not.toContain("mcp--testServer--disabledTool")
})
it("should skip servers with only disabled tools", () => {
const serverWithDisabledTools = createMockServer("disabledServer", [createMockTool("tool1", "Tool 1", false)])
const serverWithEnabledTools = createMockServer("enabledServer", [createMockTool("tool1", "Tool 1")])
const mockHub = createMockMcpHub([serverWithDisabledTools, serverWithEnabledTools])
const result = getMcpServersSection(mockHub as McpHub)
expect(result).not.toContain("## disabledServer")
expect(result).toContain("## enabledServer")
})
it("should skip servers with undefined tools", () => {
const serverWithUndefinedTools: McpServer = {
name: "noTools",
config: JSON.stringify({ type: "stdio", command: "test" }),
status: "connected",
tools: undefined,
}
const serverWithTools = createMockServer("withTools", [createMockTool("tool1")])
const mockHub = createMockMcpHub([serverWithUndefinedTools, serverWithTools])
const result = getMcpServersSection(mockHub as McpHub)
expect(result).not.toContain("## noTools")
expect(result).toContain("## withTools")
})
})

View file

@ -8,3 +8,4 @@ export { getCapabilitiesSection } from "./capabilities"
export { getModesSection } from "./modes"
export { markdownFormattingSection } from "./markdown-formatting"
export { getSkillsSection } from "./skills"
export { getMcpServersSection } from "./mcp-servers"

View file

@ -0,0 +1,64 @@
import type { McpTool } from "@roo-code/types"
import { McpHub } from "../../../services/mcp/McpHub"
import { buildMcpToolName } from "../../../utils/mcp-name"
/**
* Generates a lightweight MCP servers section for the system prompt.
*
* This provides the model with context about connected MCP servers, including:
* - The naming convention for MCP tool calls (mcp--serverName--toolName)
* - A list of connected servers and their available tools
* - Server-specific instructions (from the MCP protocol's `instructions` field)
*
* This does NOT duplicate tool schemas or descriptions, since those are already
* provided via native tool definitions. The purpose is to give the model enough
* context to understand how to use these tools, which is especially important
* for models like OpenAI's GPT series that benefit from explicit system prompt
* guidance about available MCP tools.
*/
export function getMcpServersSection(mcpHub?: McpHub): string {
if (!mcpHub) {
return ""
}
const servers = mcpHub.getServers()
if (servers.length === 0) {
return ""
}
// Build per-server entries with tool names and optional instructions
const serverEntries: string[] = []
for (const server of servers) {
const enabledTools = (server.tools || []).filter((tool: McpTool) => tool.enabledForPrompt !== false)
if (enabledTools.length === 0) {
continue
}
const toolNames = enabledTools.map((tool: McpTool) => ` - ${buildMcpToolName(server.name, tool.name)}`)
let entry = `## ${server.name}\n`
entry += `Tools:\n${toolNames.join("\n")}`
if (server.instructions) {
entry += `\n\nServer Instructions:\n${server.instructions}`
}
serverEntries.push(entry)
}
if (serverEntries.length === 0) {
return ""
}
return `====
MCP SERVERS
The following MCP (Model Context Protocol) servers are connected and provide additional tools you can use. MCP tools are called as native tool calls using the naming convention \`mcp--serverName--toolName\`. When a task could benefit from an MCP tool, prefer using it.
${serverEntries.join("\n\n")}`
}

View file

@ -23,6 +23,7 @@ import {
addCustomInstructions,
markdownFormattingSection,
getSkillsSection,
getMcpServersSection,
} from "./sections"
// Helper function to get prompt component, filtering out empty objects
@ -82,6 +83,8 @@ async function generatePrompt(
// Tools catalog is not included in the system prompt.
const toolsCatalog = ""
const mcpServersSection = shouldIncludeMcp ? getMcpServersSection(mcpHub) : ""
const basePrompt = `${roleDefinition}
${markdownFormattingSection()}
@ -91,7 +94,7 @@ ${getSharedToolUseSection()}${toolsCatalog}
${getToolUseGuidelinesSection()}
${getCapabilitiesSection(cwd, shouldIncludeMcp ? mcpHub : undefined)}
${mcpServersSection ? `\n${mcpServersSection}` : ""}
${modesSection}
${skillsSection ? `\n${skillsSection}` : ""}
${getRulesSection(cwd, settings)}