mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-10 23:59:45 +08:00
* feat(mcp): add hub server type definitions
- Add 'hub' to BuiltinMCPServerNames enum as '@cherry/hub'
- Create GeneratedTool, SearchQuery, ExecInput, ExecOutput types
- Add ExecutionContext and ConsoleMethods interfaces
Amp-Thread-ID: https://ampcode.com/threads/T-019b4e7d-86a3-770d-82f8-9e646e7e597e
Co-authored-by: Amp <amp@ampcode.com>
* feat(mcp): implement hub server core components
- generator.ts: Convert MCP tools to JS functions with JSDoc
- tool-registry.ts: In-memory cache with 10-min TTL
- search.ts: Comma-separated keyword search with ranking
- runtime.ts: Code execution with parallel/settle/console helpers
Amp-Thread-ID: https://ampcode.com/threads/T-019b4e7d-86a3-770d-82f8-9e646e7e597e
Co-authored-by: Amp <amp@ampcode.com>
* feat(mcp): integrate hub server with MCP infrastructure
- Create HubServer class with search/exec tools
- Implement mcp-bridge for calling tools via MCPService
- Register hub server in factory with dependency injection
- Initialize hub dependencies in MCPService constructor
- Add hub server description label for i18n
Amp-Thread-ID: https://ampcode.com/threads/T-019b4e7d-86a3-770d-82f8-9e646e7e597e
Co-authored-by: Amp <amp@ampcode.com>
* test(mcp): add unit tests for hub server
- generator.test.ts: Test schema conversion and JSDoc generation
- search.test.ts: Test keyword matching, ranking, and limits
- runtime.test.ts: Test code execution, helpers, and error handling
Amp-Thread-ID: https://ampcode.com/threads/T-019b4e7d-86a3-770d-82f8-9e646e7e597e
Co-authored-by: Amp <amp@ampcode.com>
* docs(mcp): add hub server documentation
- Document search/exec tool usage and parameters
- Explain configuration and caching behavior
- Include architecture diagram and file structure
Amp-Thread-ID: https://ampcode.com/threads/T-019b4e7d-86a3-770d-82f8-9e646e7e597e
Co-authored-by: Amp <amp@ampcode.com>
* ♻️ refactor(hub): simplify dependency injection for HubServer
- Remove HubServerDependencies interface and setHubServerDependencies from factory
- Add initHubBridge() to mcp-bridge for direct initialization
- Make HubServer constructor parameterless (uses pre-initialized bridge)
- MCPService now calls initHubBridge() directly instead of factory setter
- Add integration tests for full search → exec flow
* 📝 docs(hub): add comments explaining why hub is not in builtin list
- Add JSDoc to HubServer class explaining its purpose and design
- Add comment to builtinMCPServers explaining hub exclusion
- Hub is a meta-server for LLM code mode, auto-enabled internally
* ✨ feat: add available tools section to HUB_MODE_SYSTEM_PROMPT
- Add shared utility for generating MCP tool function names (serverName_toolName format)
- Update hub server to use consistent function naming across search, exec and prompt
- Add fetchAllActiveServerTools to ApiService for renderer process
- Update parameterBuilder to include available tools in auto/hub mode prompt
- Use CacheService for 1-minute tools caching in hub server
- Remove ToolRegistry in favor of direct fetching with caching
- Update search ranking to include server name matching
- Fix tests to use new naming format
Amp-Thread-ID: https://ampcode.com/threads/T-019b6971-d5c9-7719-9245-a89390078647
Co-authored-by: Amp <amp@ampcode.com>
* ♻️ refactor: consolidate MCP tool name utilities into shared module
- Merge buildFunctionCallToolName from src/main/utils/mcp.ts into packages/shared/mcp.ts
- Create unified buildMcpToolName base function with options for prefix, delimiter, maxLength, existingNames
- Fix toCamelCase to normalize uppercase snake case (MY_SERVER → myServer)
- Fix maxLength + existingNames interaction to respect length limit when adding collision suffix
- Add comprehensive JSDoc documentation
- Update tests and hub.test.ts for new lowercase normalization behavior
* ✨ feat: isolate hub exec worker and filter disabled tools
* 🐛 fix: inline hub worker source
* 🐛 fix: sync hub tool cache and map
* Update import path for buildFunctionCallToolName in BaseService
* ✨ feat: refine hub mode system prompt
* 🐛 fix: propagate hub tool errors
* 📝 docs: clarify hub exec return
* ✨ feat(hub): improve prompts and tool descriptions for better LLM success rate
- Rewrite HUB_MODE_SYSTEM_PROMPT_BASE with Critical Rules section
- Add Common Mistakes to Avoid section with examples
- Update exec tool description with IMPORTANT return requirement
- Improve search tool description clarity
- Simplify generator output with return reminder in header
- Add per-field @param JSDoc with required/optional markers
Fixes issue where LLMs forgot to return values from exec code
* ♻️ refactor(hub): return empty string when no tools available
* ✨ feat(hub): add dedicated AUTO_MODE_SYSTEM_PROMPT for auto mode
- Create self-contained prompt teaching XML tool_use format
- Only shows search/exec tools (no generic examples)
- Add complete workflow example with common mistakes
- Update parameterBuilder to use getAutoModeSystemPrompt()
- User prompt comes first, then auto mode instructions
- Skip hub prompt when no tools available
* ♻️ refactor: move hub prompts to dedicated prompts-code-mode.ts
- Create src/renderer/src/config/prompts-code-mode.ts
- Move HUB_MODE_SYSTEM_PROMPT_BASE and AUTO_MODE_SYSTEM_PROMPT_BASE
- Move getHubModeSystemPrompt() and getAutoModeSystemPrompt()
- Extract shared buildToolsSection() helper
- Update parameterBuilder.ts import
* ♻️ refactor: add mcpMode support to promptToolUsePlugin
- Add mcpMode parameter to PromptToolUseConfig and defaultBuildSystemPrompt
- Pass mcpMode through middleware config to plugin builder
- Consolidate getAutoModeSystemPrompt into getHubModeSystemPrompt
- Update parameterBuilder to use getHubModeSystemPrompt
* ♻️ refactor: move getHubModeSystemPrompt to shared package
- Create @cherrystudio/shared workspace package with exports
- Move getHubModeSystemPrompt and ToolInfo to packages/shared/prompts
- Add @cherrystudio/shared dependency to @cherrystudio/ai-core
- Update promptToolUsePlugin to import from shared package
- Update renderer prompts-code-mode.ts to re-export from shared
- Add toolSetToToolInfoArray converter for type compatibility
* Revert "♻️ refactor: move getHubModeSystemPrompt to shared package"
This reverts commit 894b2fd487.
* Remove duplicate Tool Use Examples header from system prompt
* fix: add handleModeChange call in MCPToolsButton for manual mode activation
* style: update AssistantMCPSettings to use min-height instead of overflow for better layout control
* feat(i18n): add MCP server modes and truncate messages in multiple languages
- Introduced new "mode" options for MCP servers: auto, disabled, and manual with corresponding descriptions and labels.
- Added translations for "base64DataTruncated" and "truncated" messages across various language files.
- Enhanced user experience by providing clearer feedback on data truncation.
* Normalize tool names for search and exec in parser
* Clarify tool usage rules in code mode prompts and examples
* Clarify code execution instructions and update example usage
* refactor: simplify JSDoc description handling by removing unnecessary truncation
* refactor: optimize listAllActiveServerTools method to use Promise.allSettled for improved error handling and performance
---------
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: kangfenmao <kangfenmao@qq.com>
158 lines
3.5 KiB
TypeScript
158 lines
3.5 KiB
TypeScript
import { vi } from 'vitest'
|
|
|
|
// Mock LoggerService globally for main process tests
|
|
vi.mock('@logger', async () => {
|
|
const { MockMainLoggerService, mockMainLoggerService } = await import('./__mocks__/MainLoggerService')
|
|
return {
|
|
LoggerService: MockMainLoggerService,
|
|
loggerService: mockMainLoggerService
|
|
}
|
|
})
|
|
|
|
// Mock electron modules that are commonly used in main process
|
|
vi.mock('electron', () => {
|
|
const mock = {
|
|
app: {
|
|
getPath: vi.fn((key: string) => {
|
|
switch (key) {
|
|
case 'userData':
|
|
return '/mock/userData'
|
|
case 'temp':
|
|
return '/mock/temp'
|
|
case 'logs':
|
|
return '/mock/logs'
|
|
default:
|
|
return '/mock/unknown'
|
|
}
|
|
}),
|
|
getVersion: vi.fn(() => '1.0.0')
|
|
},
|
|
ipcMain: {
|
|
handle: vi.fn(),
|
|
on: vi.fn(),
|
|
once: vi.fn(),
|
|
removeHandler: vi.fn(),
|
|
removeAllListeners: vi.fn()
|
|
},
|
|
BrowserWindow: vi.fn(),
|
|
dialog: {
|
|
showErrorBox: vi.fn(),
|
|
showMessageBox: vi.fn(),
|
|
showOpenDialog: vi.fn(),
|
|
showSaveDialog: vi.fn()
|
|
},
|
|
shell: {
|
|
openExternal: vi.fn(),
|
|
showItemInFolder: vi.fn()
|
|
},
|
|
session: {
|
|
defaultSession: {
|
|
clearCache: vi.fn(),
|
|
clearStorageData: vi.fn()
|
|
}
|
|
},
|
|
webContents: {
|
|
getAllWebContents: vi.fn(() => [])
|
|
},
|
|
systemPreferences: {
|
|
getMediaAccessStatus: vi.fn(),
|
|
askForMediaAccess: vi.fn()
|
|
},
|
|
nativeTheme: {
|
|
themeSource: 'system',
|
|
shouldUseDarkColors: false,
|
|
on: vi.fn(),
|
|
removeListener: vi.fn()
|
|
},
|
|
screen: {
|
|
getPrimaryDisplay: vi.fn(),
|
|
getAllDisplays: vi.fn()
|
|
},
|
|
Notification: vi.fn()
|
|
}
|
|
|
|
return { __esModule: true, ...mock, default: mock }
|
|
})
|
|
|
|
// Mock Winston for LoggerService dependencies
|
|
vi.mock('winston', () => ({
|
|
createLogger: vi.fn(() => ({
|
|
log: vi.fn(),
|
|
error: vi.fn(),
|
|
warn: vi.fn(),
|
|
info: vi.fn(),
|
|
debug: vi.fn(),
|
|
level: 'info',
|
|
on: vi.fn(),
|
|
end: vi.fn()
|
|
})),
|
|
format: {
|
|
combine: vi.fn(),
|
|
splat: vi.fn(),
|
|
timestamp: vi.fn(),
|
|
errors: vi.fn(),
|
|
json: vi.fn()
|
|
},
|
|
transports: {
|
|
Console: vi.fn(),
|
|
File: vi.fn()
|
|
}
|
|
}))
|
|
|
|
// Mock winston-daily-rotate-file
|
|
vi.mock('winston-daily-rotate-file', () => {
|
|
return vi.fn().mockImplementation(() => ({
|
|
on: vi.fn(),
|
|
log: vi.fn()
|
|
}))
|
|
})
|
|
|
|
// Mock Node.js modules
|
|
vi.mock('node:os', () => {
|
|
const mock = {
|
|
platform: vi.fn(() => 'darwin'),
|
|
arch: vi.fn(() => 'x64'),
|
|
version: vi.fn(() => '20.0.0'),
|
|
cpus: vi.fn(() => [{ model: 'Mock CPU' }]),
|
|
homedir: vi.fn(() => '/mock/home'),
|
|
totalmem: vi.fn(() => 8 * 1024 * 1024 * 1024) // 8GB
|
|
}
|
|
return { ...mock, default: mock }
|
|
})
|
|
|
|
vi.mock('node:path', async () => {
|
|
const actual = await vi.importActual('node:path')
|
|
return {
|
|
...actual,
|
|
join: vi.fn((...args: string[]) => args.join('/')),
|
|
resolve: vi.fn((...args: string[]) => args.join('/'))
|
|
}
|
|
})
|
|
|
|
vi.mock('node:fs', () => {
|
|
const mock = {
|
|
promises: {
|
|
access: vi.fn(),
|
|
readFile: vi.fn(),
|
|
writeFile: vi.fn(),
|
|
mkdir: vi.fn(),
|
|
readdir: vi.fn(),
|
|
stat: vi.fn(),
|
|
unlink: vi.fn(),
|
|
rmdir: vi.fn()
|
|
},
|
|
existsSync: vi.fn(),
|
|
readFileSync: vi.fn(),
|
|
writeFileSync: vi.fn(),
|
|
mkdirSync: vi.fn(),
|
|
readdirSync: vi.fn(),
|
|
statSync: vi.fn(),
|
|
unlinkSync: vi.fn(),
|
|
rmdirSync: vi.fn(),
|
|
createReadStream: vi.fn(),
|
|
createWriteStream: vi.fn()
|
|
}
|
|
|
|
return { ...mock, default: mock }
|
|
})
|