feat/search mcp auto config (#4780)
feat: mcp search carry basic configuration Co-authored-by: 寇佳龙 <koujialong@bonc.com.cn>
This commit is contained in:
parent
9ad40b9219
commit
0585d28312
@ -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)
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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 }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user