Skip to content
This repository was archived by the owner on May 15, 2026. It is now read-only.

Commit 803a264

Browse files
committed
fix: re-add lightweight MCP servers section to system prompt for OpenAI compatibility
1 parent 12cddc9 commit 803a264

5 files changed

Lines changed: 245 additions & 1 deletion

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"roo-cline": patch
3+
---
4+
5+
Re-add lightweight MCP servers section to system prompt for better OpenAI/ChatGPT compatibility
6+
7+
When MCP tools were migrated to native tool definitions (PR #10895), the MCP SERVERS section was removed from the system prompt. While Claude and Gemini models can infer MCP tool usage from native tool definitions alone, OpenAI models need additional context to understand the `mcp--serverName--toolName` naming convention. This adds back a lightweight section that lists connected MCP servers, their tool name mappings, server-specific instructions, and explains the naming convention -- without duplicating tool schemas that are already in the native tool definitions.
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
import type { McpHub } from "../../../../services/mcp/McpHub"
2+
3+
import { getMcpServersSection } from "../mcp-servers"
4+
5+
const createMockMcpHub = (servers: any[]): McpHub =>
6+
({
7+
getServers: () => servers,
8+
}) as unknown as McpHub
9+
10+
describe("getMcpServersSection", () => {
11+
it("should return empty string when mcpHub is undefined", () => {
12+
const result = getMcpServersSection(undefined)
13+
expect(result).toBe("")
14+
})
15+
16+
it("should return empty string when no servers are connected", () => {
17+
const hub = createMockMcpHub([])
18+
const result = getMcpServersSection(hub)
19+
expect(result).toBe("")
20+
})
21+
22+
it("should return empty string when servers exist but none are connected", () => {
23+
const hub = createMockMcpHub([
24+
{
25+
name: "test-server",
26+
status: "disconnected",
27+
tools: [{ name: "tool1", description: "A tool" }],
28+
},
29+
])
30+
const result = getMcpServersSection(hub)
31+
expect(result).toBe("")
32+
})
33+
34+
it("should include connected server with tools", () => {
35+
const hub = createMockMcpHub([
36+
{
37+
name: "git",
38+
status: "connected",
39+
tools: [
40+
{ name: "git_log", description: "Show git log" },
41+
{ name: "git_status", description: "Show git status" },
42+
],
43+
},
44+
])
45+
const result = getMcpServersSection(hub)
46+
47+
expect(result).toContain("MCP SERVERS")
48+
expect(result).toContain("## git")
49+
expect(result).toContain("mcp--git--git_log")
50+
expect(result).toContain("mcp--git--git_status")
51+
expect(result).toContain("mcp--serverName--toolName")
52+
})
53+
54+
it("should filter out tools with enabledForPrompt === false", () => {
55+
const hub = createMockMcpHub([
56+
{
57+
name: "testServer",
58+
status: "connected",
59+
tools: [
60+
{ name: "enabled_tool", description: "Enabled", enabledForPrompt: true },
61+
{ name: "disabled_tool", description: "Disabled", enabledForPrompt: false },
62+
{ name: "default_tool", description: "Default (no flag)" },
63+
],
64+
},
65+
])
66+
const result = getMcpServersSection(hub)
67+
68+
expect(result).toContain("mcp--testServer--enabled_tool")
69+
expect(result).not.toContain("disabled_tool")
70+
expect(result).toContain("mcp--testServer--default_tool")
71+
})
72+
73+
it("should include server instructions when available", () => {
74+
const hub = createMockMcpHub([
75+
{
76+
name: "context7",
77+
status: "connected",
78+
instructions: "Always use resolve-library-id before get-library-docs.",
79+
tools: [{ name: "resolve-library-id", description: "Resolve lib" }],
80+
},
81+
])
82+
const result = getMcpServersSection(hub)
83+
84+
expect(result).toContain("## context7")
85+
expect(result).toContain("Instructions: Always use resolve-library-id before get-library-docs.")
86+
expect(result).toContain("mcp--context7--resolve-library-id")
87+
})
88+
89+
it("should not include instructions block when server has no instructions", () => {
90+
const hub = createMockMcpHub([
91+
{
92+
name: "simple-server",
93+
status: "connected",
94+
tools: [{ name: "do_thing", description: "Does a thing" }],
95+
},
96+
])
97+
const result = getMcpServersSection(hub)
98+
99+
expect(result).toContain("## simple-server")
100+
expect(result).not.toContain("Instructions:")
101+
})
102+
103+
it("should handle server with no tools", () => {
104+
const hub = createMockMcpHub([
105+
{
106+
name: "no-tools-server",
107+
status: "connected",
108+
tools: undefined,
109+
},
110+
])
111+
const result = getMcpServersSection(hub)
112+
113+
expect(result).toContain("## no-tools-server")
114+
expect(result).toContain("(No tools available)")
115+
})
116+
117+
it("should handle multiple connected servers", () => {
118+
const hub = createMockMcpHub([
119+
{
120+
name: "git",
121+
status: "connected",
122+
tools: [{ name: "git_log", description: "Show log" }],
123+
},
124+
{
125+
name: "context7",
126+
status: "connected",
127+
instructions: "Resolve library IDs first.",
128+
tools: [{ name: "resolve-library-id", description: "Resolve lib" }],
129+
},
130+
{
131+
name: "offline-server",
132+
status: "disconnected",
133+
tools: [{ name: "should_not_appear", description: "Hidden" }],
134+
},
135+
])
136+
const result = getMcpServersSection(hub)
137+
138+
expect(result).toContain("## git")
139+
expect(result).toContain("## context7")
140+
expect(result).not.toContain("## offline-server")
141+
expect(result).not.toContain("should_not_appear")
142+
})
143+
144+
it("should only include connected servers in the section", () => {
145+
const hub = createMockMcpHub([
146+
{
147+
name: "connecting-server",
148+
status: "connecting",
149+
tools: [{ name: "tool1", description: "Tool 1" }],
150+
},
151+
])
152+
const result = getMcpServersSection(hub)
153+
154+
// "connecting" is not "connected", so no servers section should be generated
155+
expect(result).toBe("")
156+
})
157+
158+
it("should handle server with all tools disabled", () => {
159+
const hub = createMockMcpHub([
160+
{
161+
name: "all-disabled",
162+
status: "connected",
163+
tools: [
164+
{ name: "tool1", description: "Tool 1", enabledForPrompt: false },
165+
{ name: "tool2", description: "Tool 2", enabledForPrompt: false },
166+
],
167+
},
168+
])
169+
const result = getMcpServersSection(hub)
170+
171+
expect(result).toContain("## all-disabled")
172+
expect(result).toContain("(No tools available)")
173+
expect(result).not.toContain("mcp--all-disabled--tool1")
174+
expect(result).not.toContain("mcp--all-disabled--tool2")
175+
})
176+
})

src/core/prompts/sections/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ export { getCapabilitiesSection } from "./capabilities"
88
export { getModesSection } from "./modes"
99
export { markdownFormattingSection } from "./markdown-formatting"
1010
export { getSkillsSection } from "./skills"
11+
export { getMcpServersSection } from "./mcp-servers"
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { McpHub } from "../../../services/mcp/McpHub"
2+
import { buildMcpToolName } from "../../../utils/mcp-name"
3+
4+
/**
5+
* Generates a lightweight MCP servers section for the system prompt.
6+
*
7+
* This section provides the model with context about connected MCP servers,
8+
* their tool name mappings, and any server-specific instructions. It does NOT
9+
* duplicate tool schemas (those are already provided via native tool definitions).
10+
*
11+
* This context is particularly important for models like OpenAI's GPT series,
12+
* which need explicit guidance to understand the mcp--serverName--toolName
13+
* naming convention used by native tool definitions.
14+
*/
15+
export function getMcpServersSection(mcpHub?: McpHub): string {
16+
if (!mcpHub) {
17+
return ""
18+
}
19+
20+
const servers = mcpHub.getServers()
21+
const connectedServers = servers.filter((server) => server.status === "connected")
22+
23+
if (connectedServers.length === 0) {
24+
return ""
25+
}
26+
27+
const serverSections: string[] = []
28+
29+
for (const server of connectedServers) {
30+
const toolLines: string[] = []
31+
32+
if (server.tools) {
33+
for (const tool of server.tools) {
34+
if (tool.enabledForPrompt === false) {
35+
continue
36+
}
37+
toolLines.push(` - ${buildMcpToolName(server.name, tool.name)}`)
38+
}
39+
}
40+
41+
const toolList = toolLines.length > 0 ? toolLines.join("\n") : " (No tools available)"
42+
const instructionsBlock = server.instructions ? `\nInstructions: ${server.instructions}` : ""
43+
44+
serverSections.push(`## ${server.name}${instructionsBlock}\nTools:\n${toolList}`)
45+
}
46+
47+
return `====
48+
49+
MCP SERVERS
50+
51+
MCP servers provide additional tools beyond the built-in set. Tools from MCP servers are called using the naming convention \`mcp--serverName--toolName\` (e.g., \`mcp--git--git_log\`). These tools are available as callable functions alongside the built-in tools. When a task could benefit from an MCP server's capabilities, prefer using the appropriate MCP tool.
52+
53+
# Connected MCP Servers
54+
55+
${serverSections.join("\n\n")}`
56+
}

src/core/prompts/system.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
addCustomInstructions,
2727
markdownFormattingSection,
2828
getSkillsSection,
29+
getMcpServersSection,
2930
} from "./sections"
3031

3132
// Helper function to get prompt component, filtering out empty objects
@@ -86,6 +87,9 @@ async function generatePrompt(
8687
// Tools catalog is not included in the system prompt.
8788
const toolsCatalog = ""
8889

90+
// Generate the lightweight MCP servers section (server names, tool mappings, instructions)
91+
const mcpServersSection = shouldIncludeMcp ? getMcpServersSection(mcpHub) : ""
92+
8993
const basePrompt = `${roleDefinition}
9094
9195
${markdownFormattingSection()}
@@ -95,7 +99,7 @@ ${getSharedToolUseSection()}${toolsCatalog}
9599
${getToolUseGuidelinesSection()}
96100
97101
${getCapabilitiesSection(cwd, shouldIncludeMcp ? mcpHub : undefined)}
98-
102+
${mcpServersSection ? `\n${mcpServersSection}\n` : ""}
99103
${modesSection}
100104
${skillsSection ? `\n${skillsSection}` : ""}
101105
${getRulesSection(cwd, settings)}

0 commit comments

Comments
 (0)