feat(MCPService, ModelSettings): Enhance path handling and model filtering
- Add enhanced PATH generation for MCP service across different platforms - Improve model filtering with new function calling model type - Refactor MCP service type definitions and transport initialization - Add platform-specific path handling for various development environments
This commit is contained in:
parent
aa75f90294
commit
aae12a21ac
@ -1,3 +1,7 @@
|
|||||||
|
import { isLinux, isMac, isWin } from '@main/constant'
|
||||||
|
import type { Client } from '@modelcontextprotocol/sdk/client/index.js'
|
||||||
|
import type { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js'
|
||||||
|
import type { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'
|
||||||
import { MCPServer, MCPTool } from '@types'
|
import { MCPServer, MCPTool } from '@types'
|
||||||
import log from 'electron-log'
|
import log from 'electron-log'
|
||||||
import { EventEmitter } from 'events'
|
import { EventEmitter } from 'events'
|
||||||
@ -12,9 +16,9 @@ export default class MCPService extends EventEmitter {
|
|||||||
private servers: MCPServer[] = []
|
private servers: MCPServer[] = []
|
||||||
private activeServers: Map<string, any> = new Map()
|
private activeServers: Map<string, any> = new Map()
|
||||||
private clients: { [key: string]: any } = {}
|
private clients: { [key: string]: any } = {}
|
||||||
private Client: any
|
private Client: typeof Client | undefined
|
||||||
private stoioTransport: any
|
private stdioTransport: typeof StdioClientTransport | undefined
|
||||||
private sseTransport: any
|
private sseTransport: typeof SSEClientTransport | undefined
|
||||||
private initialized = false
|
private initialized = false
|
||||||
private initPromise: Promise<void> | null = null
|
private initPromise: Promise<void> | null = null
|
||||||
|
|
||||||
@ -84,7 +88,7 @@ export default class MCPService extends EventEmitter {
|
|||||||
])
|
])
|
||||||
|
|
||||||
this.Client = Client
|
this.Client = Client
|
||||||
this.stoioTransport = StdioTransport
|
this.stdioTransport = StdioTransport
|
||||||
this.sseTransport = SSETransport
|
this.sseTransport = SSETransport
|
||||||
|
|
||||||
// Mark as initialized before loading servers
|
// Mark as initialized before loading servers
|
||||||
@ -295,35 +299,33 @@ export default class MCPService extends EventEmitter {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let transport: any = null
|
let transport: StdioClientTransport | SSEClientTransport
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Create appropriate transport based on configuration
|
// Create appropriate transport based on configuration
|
||||||
if (baseUrl) {
|
if (baseUrl) {
|
||||||
transport = new this.sseTransport(new URL(baseUrl))
|
transport = new this.sseTransport!(new URL(baseUrl))
|
||||||
} else if (command) {
|
} else if (command) {
|
||||||
let cmd: string = command
|
let cmd: string = command
|
||||||
if (command === 'npx') {
|
if (command === 'npx') {
|
||||||
cmd = process.platform === 'win32' ? `${command}.cmd` : command
|
cmd = process.platform === 'win32' ? `${command}.cmd` : command
|
||||||
}
|
}
|
||||||
|
|
||||||
const mergedEnv = {
|
transport = new this.stdioTransport!({
|
||||||
...env,
|
|
||||||
PATH: process.env.PATH
|
|
||||||
}
|
|
||||||
|
|
||||||
transport = new this.stoioTransport({
|
|
||||||
command: cmd,
|
command: cmd,
|
||||||
args,
|
args,
|
||||||
stderr: process.platform === 'win32' ? 'pipe' : 'inherit',
|
stderr: 'pipe',
|
||||||
env: mergedEnv
|
env: {
|
||||||
|
PATH: this.getEnhancedPath(process.env.PATH || ''),
|
||||||
|
...env
|
||||||
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Either baseUrl or command must be provided')
|
throw new Error('Either baseUrl or command must be provided')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create and connect client
|
// Create and connect client
|
||||||
const client = new this.Client({ name, version: '1.0.0' }, { capabilities: {} })
|
const client = new this.Client!({ name, version: '1.0.0' }, { capabilities: {} })
|
||||||
|
|
||||||
await client.connect(transport)
|
await client.connect(transport)
|
||||||
|
|
||||||
@ -491,4 +493,61 @@ export default class MCPService extends EventEmitter {
|
|||||||
|
|
||||||
log.info(`[MCP] Loaded and activated ${Object.keys(this.clients).length} servers`)
|
log.info(`[MCP] Loaded and activated ${Object.keys(this.clients).length} servers`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get enhanced PATH including common tool locations
|
||||||
|
*/
|
||||||
|
private getEnhancedPath(originalPath: string): string {
|
||||||
|
// 将原始 PATH 按分隔符分割成数组
|
||||||
|
const pathSeparator = process.platform === 'win32' ? ';' : ':'
|
||||||
|
const existingPaths = new Set(originalPath.split(pathSeparator).filter(Boolean))
|
||||||
|
const homeDir = process.env.HOME || process.env.USERPROFILE || ''
|
||||||
|
|
||||||
|
// 定义要添加的新路径
|
||||||
|
const newPaths: string[] = []
|
||||||
|
|
||||||
|
if (isMac) {
|
||||||
|
newPaths.push(
|
||||||
|
'/bin',
|
||||||
|
'/usr/bin',
|
||||||
|
'/usr/local/bin',
|
||||||
|
'/usr/local/sbin',
|
||||||
|
'/opt/homebrew/bin',
|
||||||
|
'/opt/homebrew/sbin',
|
||||||
|
'/usr/local/opt/node/bin',
|
||||||
|
`${homeDir}/.nvm/current/bin`,
|
||||||
|
`${homeDir}/.npm-global/bin`,
|
||||||
|
`${homeDir}/.yarn/bin`,
|
||||||
|
`${homeDir}/.cargo/bin`,
|
||||||
|
'/opt/local/bin'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLinux) {
|
||||||
|
newPaths.push(
|
||||||
|
'/bin',
|
||||||
|
'/usr/bin',
|
||||||
|
'/usr/local/bin',
|
||||||
|
`${homeDir}/.nvm/current/bin`,
|
||||||
|
`${homeDir}/.npm-global/bin`,
|
||||||
|
`${homeDir}/.yarn/bin`,
|
||||||
|
`${homeDir}/.cargo/bin`,
|
||||||
|
'/snap/bin'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isWin) {
|
||||||
|
newPaths.push(`${process.env.APPDATA}\\npm`, `${homeDir}\\AppData\\Local\\Yarn\\bin`, `${homeDir}\\.cargo\\bin`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只添加不存在的路径
|
||||||
|
newPaths.forEach((path) => {
|
||||||
|
if (path && !existingPaths.has(path)) {
|
||||||
|
existingPaths.add(path)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 转换回字符串
|
||||||
|
return Array.from(existingPaths).join(pathSeparator)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import ModelTags from '@renderer/components/ModelTags'
|
|||||||
import {
|
import {
|
||||||
getModelLogo,
|
getModelLogo,
|
||||||
isEmbeddingModel,
|
isEmbeddingModel,
|
||||||
|
isFunctionCallingModel,
|
||||||
isReasoningModel,
|
isReasoningModel,
|
||||||
isVisionModel,
|
isVisionModel,
|
||||||
isWebSearchModel,
|
isWebSearchModel,
|
||||||
@ -55,6 +56,7 @@ const PopupContainer: React.FC<Props> = ({ provider: _provider, resolve }) => {
|
|||||||
) {
|
) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (filterType) {
|
switch (filterType) {
|
||||||
case 'reasoning':
|
case 'reasoning':
|
||||||
return isReasoningModel(model)
|
return isReasoningModel(model)
|
||||||
@ -66,6 +68,8 @@ const PopupContainer: React.FC<Props> = ({ provider: _provider, resolve }) => {
|
|||||||
return isFreeModel(model)
|
return isFreeModel(model)
|
||||||
case 'embedding':
|
case 'embedding':
|
||||||
return isEmbeddingModel(model)
|
return isEmbeddingModel(model)
|
||||||
|
case 'function_calling':
|
||||||
|
return isFunctionCallingModel(model)
|
||||||
default:
|
default:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -159,6 +163,7 @@ const PopupContainer: React.FC<Props> = ({ provider: _provider, resolve }) => {
|
|||||||
<Radio.Button value="websearch">{t('models.websearch')}</Radio.Button>
|
<Radio.Button value="websearch">{t('models.websearch')}</Radio.Button>
|
||||||
<Radio.Button value="free">{t('models.free')}</Radio.Button>
|
<Radio.Button value="free">{t('models.free')}</Radio.Button>
|
||||||
<Radio.Button value="embedding">{t('models.embedding')}</Radio.Button>
|
<Radio.Button value="embedding">{t('models.embedding')}</Radio.Button>
|
||||||
|
<Radio.Button value="function_calling">{t('models.function_calling')}</Radio.Button>
|
||||||
</Radio.Group>
|
</Radio.Group>
|
||||||
</Center>
|
</Center>
|
||||||
<Search
|
<Search
|
||||||
|
|||||||
@ -113,7 +113,7 @@ const ModelEditContent: FC<ModelEditContentProps> = ({ model, onUpdateModel, ope
|
|||||||
...(isVisionModel(model) ? ['vision'] : []),
|
...(isVisionModel(model) ? ['vision'] : []),
|
||||||
...(isEmbeddingModel(model) ? ['embedding'] : []),
|
...(isEmbeddingModel(model) ? ['embedding'] : []),
|
||||||
...(isReasoningModel(model) ? ['reasoning'] : []),
|
...(isReasoningModel(model) ? ['reasoning'] : []),
|
||||||
...(isFunctionCallingModel(model) ? ['tools'] : [])
|
...(isFunctionCallingModel(model) ? ['function_calling'] : [])
|
||||||
] as ModelType[]
|
] as ModelType[]
|
||||||
|
|
||||||
// 合并现有选择和默认类型
|
// 合并现有选择和默认类型
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user