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 { useMCPServers } from '@renderer/hooks/useMCPServers'
|
||||||
import { builtinMCPServers } from '@renderer/store/mcp'
|
import { builtinMCPServers } from '@renderer/store/mcp'
|
||||||
import { MCPServer } from '@renderer/types'
|
import { MCPServer } from '@renderer/types'
|
||||||
|
import { getMcpConfigSampleFromReadme } from '@renderer/utils'
|
||||||
import { Button, Card, Flex, Input, Space, Spin, Tag, Typography } from 'antd'
|
import { Button, Card, Flex, Input, Space, Spin, Tag, Typography } from 'antd'
|
||||||
import { npxFinder } from 'npx-scope-finder'
|
import { npxFinder } from 'npx-scope-finder'
|
||||||
import { type FC, useEffect, useState } from 'react'
|
import { type FC, useEffect, useState } from 'react'
|
||||||
@ -19,6 +20,7 @@ interface SearchResult {
|
|||||||
npmLink: string
|
npmLink: string
|
||||||
fullName: string
|
fullName: string
|
||||||
type: MCPServer['type']
|
type: MCPServer['type']
|
||||||
|
configSample?: MCPServer['configSample']
|
||||||
}
|
}
|
||||||
|
|
||||||
const npmScopes = ['@cherry', '@modelcontextprotocol', '@gongrzhe', '@mcpmarket']
|
const npmScopes = ['@cherry', '@modelcontextprotocol', '@gongrzhe', '@mcpmarket']
|
||||||
@ -73,9 +75,13 @@ const NpxSearch: FC<{
|
|||||||
try {
|
try {
|
||||||
// Call npxFinder to search for packages
|
// Call npxFinder to search for packages
|
||||||
const packages = await npxFinder(searchScope)
|
const packages = await npxFinder(searchScope)
|
||||||
|
|
||||||
// Map the packages to our desired format
|
// Map the packages to our desired format
|
||||||
const formattedResults: SearchResult[] = packages.map((pkg) => {
|
const formattedResults: SearchResult[] = packages.map((pkg) => {
|
||||||
|
let configSample
|
||||||
|
if (pkg.original?.readme) {
|
||||||
|
configSample = getMcpConfigSampleFromReadme(pkg.original.readme)
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
key: pkg.name,
|
key: pkg.name,
|
||||||
name: pkg.name?.split('/')[1] || '',
|
name: pkg.name?.split('/')[1] || '',
|
||||||
@ -84,7 +90,8 @@ const NpxSearch: FC<{
|
|||||||
usage: `npx ${pkg.name}`,
|
usage: `npx ${pkg.name}`,
|
||||||
npmLink: pkg.links?.npm || `https://www.npmjs.com/package/${pkg.name}`,
|
npmLink: pkg.links?.npm || `https://www.npmjs.com/package/${pkg.name}`,
|
||||||
fullName: pkg.name || '',
|
fullName: pkg.name || '',
|
||||||
type: 'stdio'
|
type: 'stdio',
|
||||||
|
configSample
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -199,9 +206,11 @@ const NpxSearch: FC<{
|
|||||||
name: record.name,
|
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}`,
|
description: `${record.description}\n\n${t('settings.mcp.npx_list.usage')}: ${record.usage}\n${t('settings.mcp.npx_list.npm')}: ${record.npmLink}`,
|
||||||
command: 'npx',
|
command: 'npx',
|
||||||
args: ['-y', record.fullName],
|
args: record.configSample?.args ?? ['-y', record.fullName],
|
||||||
|
env: record.configSample?.env,
|
||||||
isActive: false,
|
isActive: false,
|
||||||
type: record.type
|
type: record.type,
|
||||||
|
configSample: record.configSample
|
||||||
}
|
}
|
||||||
|
|
||||||
addMCPServer(newServer)
|
addMCPServer(newServer)
|
||||||
|
|||||||
@ -374,6 +374,12 @@ export interface MCPServerParameter {
|
|||||||
description: string
|
description: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MCPConfigSample {
|
||||||
|
command: string
|
||||||
|
args: string[]
|
||||||
|
env?: Record<string, string> | undefined
|
||||||
|
}
|
||||||
|
|
||||||
export interface MCPServer {
|
export interface MCPServer {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
@ -386,6 +392,7 @@ export interface MCPServer {
|
|||||||
env?: Record<string, string>
|
env?: Record<string, string>
|
||||||
isActive: boolean
|
isActive: boolean
|
||||||
disabledTools?: string[] // List of tool names that are disabled for this server
|
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
|
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)
|
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 }
|
export { classNames }
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user