diff --git a/src/renderer/src/aiCore/prepareParams/parameterBuilder.ts b/src/renderer/src/aiCore/prepareParams/parameterBuilder.ts
index 1d92660072..21b07af811 100644
--- a/src/renderer/src/aiCore/prepareParams/parameterBuilder.ts
+++ b/src/renderer/src/aiCore/prepareParams/parameterBuilder.ts
@@ -26,7 +26,7 @@ import {
isSupportedThinkingTokenModel,
isWebSearchModel
} from '@renderer/config/models'
-import { getHubModeSystemPrompt } from '@renderer/config/prompts'
+import { getAutoModeSystemPrompt } from '@renderer/config/prompts'
import { fetchAllActiveServerTools } from '@renderer/services/ApiService'
import { getDefaultModel } from '@renderer/services/AssistantService'
import store from '@renderer/store'
@@ -245,16 +245,18 @@ export async function buildStreamTextParams(
params.tools = tools
}
- if (assistant.prompt) {
- let systemPrompt = await replacePromptVariables(assistant.prompt, model.name)
- if (getEffectiveMcpMode(assistant) === 'auto') {
- const allActiveTools = await fetchAllActiveServerTools()
- systemPrompt = systemPrompt + '\n\n' + getHubModeSystemPrompt(allActiveTools)
- }
- params.system = systemPrompt
- } else if (getEffectiveMcpMode(assistant) === 'auto') {
+ let systemPrompt = assistant.prompt ? await replacePromptVariables(assistant.prompt, model.name) : ''
+
+ if (getEffectiveMcpMode(assistant) === 'auto') {
const allActiveTools = await fetchAllActiveServerTools()
- params.system = getHubModeSystemPrompt(allActiveTools)
+ const autoModePrompt = getAutoModeSystemPrompt(allActiveTools)
+ if (autoModePrompt) {
+ systemPrompt = systemPrompt ? `${systemPrompt}\n\n${autoModePrompt}` : autoModePrompt
+ }
+ }
+
+ if (systemPrompt) {
+ params.system = systemPrompt
}
logger.debug('params', params)
diff --git a/src/renderer/src/config/prompts.ts b/src/renderer/src/config/prompts.ts
index fda3ec085d..db0128633d 100644
--- a/src/renderer/src/config/prompts.ts
+++ b/src/renderer/src/config/prompts.ts
@@ -619,3 +619,108 @@ export function getHubModeSystemPrompt(tools: ToolInfo[] = []): string {
${toolsSection}
`
}
+
+const AUTO_MODE_SYSTEM_PROMPT_BASE = `
+You can discover and invoke MCP tools through a hub using TWO meta-tools: \`search\` and \`exec\`.
+
+## Tool Invocation Format
+
+When you want to call a tool, output exactly one XML block:
+
+
+ {tool_name}
+ {json_arguments}
+
+
+Rules:
+- \`{tool_name}\` MUST be either \`search\` or \`exec\`
+- \`\` MUST contain valid JSON (no comments, no trailing commas)
+- Do NOT include extra text before or after the \`\` block
+
+## Available Tools
+
+1. **search** - Discover MCP tools by keyword
+ \`\`\`json
+ { "query": "keyword1,keyword2", "limit": 10 }
+ \`\`\`
+ Returns JavaScript function declarations with JSDoc showing names, parameters, and return types.
+
+2. **exec** - Execute JavaScript that calls discovered tools
+ \`\`\`json
+ { "code": "const r = await ToolName({...}); return r;" }
+ \`\`\`
+ **CRITICAL:** You MUST \`return\` the final value, or result will be \`undefined\`.
+
+## Workflow
+
+1. Call \`search\` with keywords to discover tools
+2. Read the returned function signatures carefully
+3. Call \`exec\` with JavaScript code that:
+ - Uses ONLY functions returned by \`search\`
+ - Calls them with \`await\`
+ - Ends with explicit \`return\`
+4. Answer the user based on the result
+
+## Example
+
+User: "Calculate 15 * 7"
+
+Assistant calls search:
+
+ search
+ {"query": "python,calculator"}
+
+
+Hub returns function signature:
+\`\`\`js
+async function CherryPython_pythonExecute(params: { code: string }): Promise
+\`\`\`
+
+Assistant calls exec:
+
+ exec
+ {"code": "const result = await CherryPython_pythonExecute({ code: '15 * 7' }); return result;"}
+
+
+Hub returns: { "result": 105 }
+
+Assistant answers: "15 × 7 = 105"
+
+## Common Mistakes
+
+❌ Forgetting to return (result will be undefined):
+\`\`\`js
+await SomeTool({ id: "123" })
+\`\`\`
+
+✅ Always return:
+\`\`\`js
+const data = await SomeTool({ id: "123" }); return data;
+\`\`\`
+
+❌ Calling exec before search - you must discover tools first
+
+❌ Using functions not returned by search
+`
+
+export function getAutoModeSystemPrompt(tools: ToolInfo[] = []): string {
+ if (tools.length === 0) {
+ return ''
+ }
+
+ const existingNames = new Set()
+ const toolsSection = tools
+ .map((t) => {
+ const functionName = generateMcpToolFunctionName(t.serverName, t.name, existingNames)
+ const desc = t.description || ''
+ const normalizedDesc = desc.replace(/\s+/g, ' ').trim()
+ const truncatedDesc = normalizedDesc.length > 50 ? `${normalizedDesc.slice(0, 50)}...` : normalizedDesc
+ return `- ${functionName}: ${truncatedDesc}`
+ })
+ .join('\n')
+
+ return `${AUTO_MODE_SYSTEM_PROMPT_BASE}
+## Discoverable Tools (use search to get full signatures)
+${toolsSection}
+`
+}