feat/search mcp auto config (#4780)

feat: mcp search carry basic configuration

Co-authored-by: 寇佳龙 <koujialong@bonc.com.cn>
This commit is contained in:
karl 2025-04-15 16:05:17 +08:00 committed by GitHub
parent 9ad40b9219
commit 0585d28312
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 46 additions and 4 deletions

View File

@ -5,6 +5,7 @@ import { Center, HStack } from '@renderer/components/Layout'
import { useMCPServers } from '@renderer/hooks/useMCPServers'
import { builtinMCPServers } from '@renderer/store/mcp'
import { MCPServer } from '@renderer/types'
import { getMcpConfigSampleFromReadme } from '@renderer/utils'
import { Button, Card, Flex, Input, Space, Spin, Tag, Typography } from 'antd'
import { npxFinder } from 'npx-scope-finder'
import { type FC, useEffect, useState } from 'react'
@ -19,6 +20,7 @@ interface SearchResult {
npmLink: string
fullName: string
type: MCPServer['type']
configSample?: MCPServer['configSample']
}
const npmScopes = ['@cherry', '@modelcontextprotocol', '@gongrzhe', '@mcpmarket']
@ -73,9 +75,13 @@ const NpxSearch: FC<{
try {
// Call npxFinder to search for packages
const packages = await npxFinder(searchScope)
// Map the packages to our desired format
const formattedResults: SearchResult[] = packages.map((pkg) => {
let configSample
if (pkg.original?.readme) {
configSample = getMcpConfigSampleFromReadme(pkg.original.readme)
}
return {
key: pkg.name,
name: pkg.name?.split('/')[1] || '',
@ -84,7 +90,8 @@ const NpxSearch: FC<{
usage: `npx ${pkg.name}`,
npmLink: pkg.links?.npm || `https://www.npmjs.com/package/${pkg.name}`,
fullName: pkg.name || '',
type: 'stdio'
type: 'stdio',
configSample
}
})
@ -199,9 +206,11 @@ const NpxSearch: FC<{
name: record.name,
description: `${record.description}\n\n${t('settings.mcp.npx_list.usage')}: ${record.usage}\n${t('settings.mcp.npx_list.npm')}: ${record.npmLink}`,
command: 'npx',
args: ['-y', record.fullName],
args: record.configSample?.args ?? ['-y', record.fullName],
env: record.configSample?.env,
isActive: false,
type: record.type
type: record.type,
configSample: record.configSample
}
addMCPServer(newServer)

View File

@ -374,6 +374,12 @@ export interface MCPServerParameter {
description: string
}
export interface MCPConfigSample {
command: string
args: string[]
env?: Record<string, string> | undefined
}
export interface MCPServer {
id: string
name: string
@ -386,6 +392,7 @@ export interface MCPServer {
env?: Record<string, string>
isActive: boolean
disabledTools?: string[] // List of tool names that are disabled for this server
configSample?: MCPConfigSample
headers?: Record<string, string> // Custom headers to be sent with requests to this server
}

View File

@ -501,4 +501,30 @@ export function hasObjectKey(obj: any, key: string) {
return Object.keys(obj).includes(key)
}
/**
* npm readme中提取 npx mcp config
* @param readme readme字符串
* @returns mcp config sample
*/
export function getMcpConfigSampleFromReadme(readme: string) {
if (readme) {
// 使用正则表达式匹配 mcpServers 对象内容
const regex = /"mcpServers"\s*:\s*({(?:[^{}]*|{(?:[^{}]*|{[^{}]*})*})*})/
const match = readme.match(regex)
console.log('match', match)
if (match && match[1]) {
// 添加缺失的闭合括号检测
try {
let orgSample = JSON.parse(match[1])
orgSample = orgSample[Object.keys(orgSample)[0] ?? '']
if (orgSample.command === 'npx') {
return orgSample
}
} catch (e) {
console.log(e)
}
}
}
}
export { classNames }