add mcp tools

This commit is contained in:
duanfuxiang 2025-06-03 09:33:53 +08:00
parent ec6c4cde83
commit 1dffe5292a
10 changed files with 25 additions and 33 deletions

View File

@ -783,7 +783,6 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
model: undefined, model: undefined,
}, },
}) })
console.log('Updated chat messages:', newChatMessages);
} }
setChatMessages(newChatMessages); setChatMessages(newChatMessages);

View File

@ -63,7 +63,7 @@ const CustomModeView = () => {
const [roleDefinition, setRoleDefinition] = useState<string>('') const [roleDefinition, setRoleDefinition] = useState<string>('')
// Selected tool groups // Selected tool groups
const [selectedTools, setSelectedTools] = useState<GroupEntry[]>([]) const [selectedTools, setSelectedTools] = useState<GroupEntry[]>([]);
// Custom instructions // Custom instructions
const [customInstructions, setCustomInstructions] = useState<string>('') const [customInstructions, setCustomInstructions] = useState<string>('')
@ -87,7 +87,7 @@ const CustomModeView = () => {
setModeName(builtinMode.slug); setModeName(builtinMode.slug);
setRoleDefinition(builtinMode.roleDefinition); setRoleDefinition(builtinMode.roleDefinition);
setCustomInstructions(builtinMode.customInstructions || ''); setCustomInstructions(builtinMode.customInstructions || '');
setSelectedTools(builtinMode.groups); setSelectedTools(builtinMode.groups as GroupEntry[]);
setCustomModeId(''); // Built-in modes don't have custom IDs setCustomModeId(''); // Built-in modes don't have custom IDs
} else { } else {
setIsBuiltinMode(false); setIsBuiltinMode(false);

View File

@ -1,9 +1,8 @@
import { Server } from 'lucide-react' import { Server } from 'lucide-react'
import React from 'react' import React from 'react'
import { useSettings } from "../../../contexts/SettingsContext"
import { t } from '../../../lang/helpers' import { t } from '../../../lang/helpers'
import { ApplyStatus, SearchWebToolArgs } from "../../../types/apply" import { ApplyStatus, UseMcpToolArgs } from "../../../types/apply"
export default function UseMcpToolBlock({ export default function UseMcpToolBlock({
applyStatus, applyStatus,
@ -14,16 +13,13 @@ export default function UseMcpToolBlock({
finish finish
}: { }: {
applyStatus: ApplyStatus applyStatus: ApplyStatus
onApply: (args: SearchWebToolArgs) => void onApply: (args: UseMcpToolArgs) => void
serverName: string, serverName: string,
toolName: string, toolName: string,
parameters: Record<string, unknown>, parameters: Record<string, unknown>,
finish: boolean finish: boolean
}) { }) {
const { settings } = useSettings()
React.useEffect(() => { React.useEffect(() => {
if (finish && applyStatus === ApplyStatus.Idle) { if (finish && applyStatus === ApplyStatus.Idle) {
onApply({ onApply({
@ -43,7 +39,7 @@ export default function UseMcpToolBlock({
<div className={'infio-chat-code-block-header'}> <div className={'infio-chat-code-block-header'}>
<div className={'infio-chat-code-block-header-filename'}> <div className={'infio-chat-code-block-header-filename'}>
<Server size={14} className="infio-chat-code-block-header-icon" /> <Server size={14} className="infio-chat-code-block-header-icon" />
use mcp tool from {t('mcpHub.useMcpToolFrom')}
<span className="infio-mcp-tool-server-name">{serverName}</span> <span className="infio-mcp-tool-server-name">{serverName}</span>
</div> </div>
</div> </div>
@ -56,7 +52,7 @@ export default function UseMcpToolBlock({
<span className="infio-mcp-tool-name">{toolName}</span> <span className="infio-mcp-tool-name">{toolName}</span>
</div> </div>
</div> </div>
: <div className="infio-mcp-tool-parameters"> {t('mcpHub.parameters')}: <div className="infio-mcp-tool-parameters">
<pre className="infio-json-pre"><code>{JSON.stringify(parameters, null, 2)}</code></pre> <pre className="infio-json-pre"><code>{JSON.stringify(parameters, null, 2)}</code></pre>
</div> </div>
</div> </div>

View File

@ -21,10 +21,8 @@ const McpHubView = () => {
const fetchServers = async () => { const fetchServers = async () => {
const hub = await getMcpHub() const hub = await getMcpHub()
console.log('Fetching MCP Servers from hub:', hub)
if (hub) { if (hub) {
const serversData = hub.getAllServers() const serversData = hub.getAllServers()
console.log('Fetched MCP Servers:', serversData)
setMcpServers(serversData) setMcpServers(serversData)
} }
} }
@ -65,7 +63,7 @@ const McpHubView = () => {
const handleDelete = async (serverName: string) => { const handleDelete = async (serverName: string) => {
const hub = await getMcpHub(); const hub = await getMcpHub();
if (hub) { if (hub) {
if (confirm(t('mcpHub.deleteConfirm', { name: serverName }))) { if (confirm(t('mcpHub.deleteConfirm').replace('{name}', serverName) as string)) {
await hub.deleteServer(serverName, "global") await hub.deleteServer(serverName, "global")
const updatedServers = hub.getAllServers() const updatedServers = hub.getAllServers()
setMcpServers(updatedServers) setMcpServers(updatedServers)
@ -103,9 +101,9 @@ const McpHubView = () => {
// 清空表单 // 清空表单
setNewServerName('') setNewServerName('')
setNewServerConfig('') setNewServerConfig('')
new Notice(t('mcpHub.createSuccess', { name: newServerName })) new Notice(t('mcpHub.createSuccess').replace('{name}', newServerName) as string)
} catch (error) { } catch (error) {
new Notice(t('mcpHub.createFailed', { error: error.message })) new Notice(t('mcpHub.createFailed').replace('{error}', error.message) as string)
} }
} }
} }

View File

@ -95,7 +95,6 @@ class LLMManager implements LLMManagerInterface {
request: LLMRequestNonStreaming, request: LLMRequestNonStreaming,
options?: LLMOptions, options?: LLMOptions,
): Promise<LLMResponseNonStreaming> { ): Promise<LLMResponseNonStreaming> {
console.log("model", model)
switch (model.provider) { switch (model.provider) {
case ApiProvider.Infio: case ApiProvider.Infio:
return await this.infioProvider.generateResponse( return await this.infioProvider.generateResponse(

View File

@ -140,14 +140,10 @@ export class McpHub {
public async onload() { public async onload() {
// Ensure the MCP configuration directory exists // Ensure the MCP configuration directory exists
console.log("McpHub: Loading MCP Hub")
await this.ensureMcpFileExists() await this.ensureMcpFileExists()
console.log("McpHub: file exists")
await this.watchMcpSettingsFile(); await this.watchMcpSettingsFile();
console.log("McpHub: watchMcpSettingsFile")
// this.setupWorkspaceWatcher(); // this.setupWorkspaceWatcher();
await this.initializeGlobalMcpServers(); await this.initializeGlobalMcpServers();
console.log("McpHub: initializeGlobalMcpServers")
} }
/** /**
@ -156,7 +152,6 @@ export class McpHub {
*/ */
public registerClient(): void { public registerClient(): void {
this.refCount++ this.refCount++
console.log(`McpHub: Client registered. Ref count: ${this.refCount}`)
} }
/** /**
@ -165,9 +160,7 @@ export class McpHub {
*/ */
public async unregisterClient(): Promise<void> { public async unregisterClient(): Promise<void> {
this.refCount-- this.refCount--
console.log(`McpHub: Client unregistered. Ref count: ${this.refCount}`)
if (this.refCount <= 0) { if (this.refCount <= 0) {
console.log("McpHub: Last client unregistered. Disposing hub.")
await this.dispose() await this.dispose()
} }
} }
@ -336,7 +329,7 @@ export class McpHub {
// which should create it, then something is wrong. // which should create it, then something is wrong.
// However, getMcpSettingsFilePath should handle creation. // However, getMcpSettingsFilePath should handle creation.
// This check is more of a safeguard. // This check is more of a safeguard.
console.log("MCP config file does not exist, skipping initialization."); // console.log("MCP config file does not exist, skipping initialization.");
return; return;
} }
@ -646,6 +639,7 @@ export class McpHub {
alwaysAllow: alwaysAllowConfig.includes(tool.name), alwaysAllow: alwaysAllowConfig.includes(tool.name),
})) }))
// @ts-expect-error - 服务器返回的工具对象中 name 是可选的,但 McpTool 类型要求它是必需的
return tools return tools
} catch (error) { } catch (error) {
console.error(`Failed to fetch tools for ${serverName}:`, error) console.error(`Failed to fetch tools for ${serverName}:`, error)
@ -660,6 +654,7 @@ export class McpHub {
return [] return []
} }
const response = await connection.client.request({ method: "resources/list" }, ListResourcesResultSchema) const response = await connection.client.request({ method: "resources/list" }, ListResourcesResultSchema)
// @ts-expect-error - 服务器返回的资源对象中 name 是可选的,但 McpResource 类型要求它是必需的
return response?.resources || [] return response?.resources || []
} catch (error) { } catch (error) {
// console.error(`Failed to fetch resources for ${serverName}:`, error) // console.error(`Failed to fetch resources for ${serverName}:`, error)
@ -680,6 +675,7 @@ export class McpHub {
{ method: "resources/templates/list" }, { method: "resources/templates/list" },
ListResourceTemplatesResultSchema, ListResourceTemplatesResultSchema,
) )
// @ts-expect-error - 服务器返回的资源模板对象中 name 是可选的,但 McpResourceTemplate 类型要求它是必需的
return response?.resourceTemplates || [] return response?.resourceTemplates || []
} catch (error) { } catch (error) {
// console.error(`Failed to fetch resource templates for ${serverName}:`, error) // console.error(`Failed to fetch resource templates for ${serverName}:`, error)
@ -1194,6 +1190,7 @@ export class McpHub {
if (connection.server.disabled) { if (connection.server.disabled) {
throw new Error(`Server "${serverName}" is disabled`) throw new Error(`Server "${serverName}" is disabled`)
} }
// @ts-expect-error - 服务器返回的资源对象中 name 是可选的,但 McpResourceResponse 类型要求它是必需的
return await connection.client.request( return await connection.client.request(
{ {
method: "resources/read", method: "resources/read",
@ -1231,6 +1228,7 @@ export class McpHub {
timeout = 60 * 1000 timeout = 60 * 1000
} }
// @ts-expect-error - 服务器返回的工具调用对象中 name 是可选的,但 McpToolCallResponse 类型要求它是必需的
return await connection.client.request( return await connection.client.request(
{ {
method: "tools/call", method: "tools/call",

View File

@ -381,8 +381,8 @@ export default {
disable: "Disable Server", disable: "Disable Server",
restart: "Restart Server", restart: "Restart Server",
delete: "Delete Server", delete: "Delete Server",
deleteConfirm: "Are you sure you want to delete server \"{name}\"?", deleteConfirm: "Are you sure you want to delete server {name}?",
createSuccess: "Server \"{name}\" created successfully", createSuccess: "Server {name} created successfully",
createFailed: "Failed to create server: {error}", createFailed: "Failed to create server: {error}",
serverNameRequired: "Server name cannot be empty", serverNameRequired: "Server name cannot be empty",
configRequired: "Configuration cannot be empty", configRequired: "Configuration cannot be empty",
@ -396,6 +396,7 @@ export default {
noResources: "No resources available", noResources: "No resources available",
noErrors: "No error records", noErrors: "No error records",
parameters: "Parameters", parameters: "Parameters",
toolNoDescription: "No description" toolNoDescription: "No description",
useMcpToolFrom: "Use MCP tool from",
} }
} }

View File

@ -396,7 +396,8 @@ export default {
noResources: "没有可用资源", noResources: "没有可用资源",
noErrors: "没有错误记录", noErrors: "没有错误记录",
parameters: "参数", parameters: "参数",
toolNoDescription: "无描述" toolNoDescription: "无描述",
useMcpToolFrom: "Use MCP tool from",
} }
} }
}; };

View File

@ -22,7 +22,7 @@ export type ModeConfig = {
name: string name: string
roleDefinition: string roleDefinition: string
customInstructions?: string customInstructions?: string
groups: readonly GroupEntry[] // Now supports both simple strings and tuples with options groups: GroupEntry[] // Now supports both simple strings and tuples with options
source?: "global" | "project" // Where this mode was loaded from source?: "global" | "project" // Where this mode was loaded from
} }
@ -79,7 +79,7 @@ export function getToolsForMode(groups: readonly GroupEntry[]): string[] {
} }
// Main modes configuration as an ordered array // Main modes configuration as an ordered array
export const defaultModes: readonly ModeConfig[] = [ export const defaultModes: ModeConfig[] = [
{ {
slug: "ask", slug: "ask",
name: "Ask", name: "Ask",
@ -107,7 +107,7 @@ export const defaultModes: readonly ModeConfig[] = [
customInstructions: customInstructions:
"You can conduct thorough research by analyzing available information, connecting related concepts, and applying structured reasoning methods. Help users explore topics in depth by considering multiple angles, identifying relevant evidence, and evaluating the reliability of sources. Use step-by-step analysis when tackling complex problems, explaining your thought process clearly. Create visual representations like Mermaid diagrams when they help clarify relationships between ideas. Use Markdown tables to present statistical data or comparative information when appropriate. Present balanced viewpoints while highlighting the strength of evidence behind different conclusions.", "You can conduct thorough research by analyzing available information, connecting related concepts, and applying structured reasoning methods. Help users explore topics in depth by considering multiple angles, identifying relevant evidence, and evaluating the reliability of sources. Use step-by-step analysis when tackling complex problems, explaining your thought process clearly. Create visual representations like Mermaid diagrams when they help clarify relationships between ideas. Use Markdown tables to present statistical data or comparative information when appropriate. Present balanced viewpoints while highlighting the strength of evidence behind different conclusions.",
}, },
] as const ]
// Export the default mode slug // Export the default mode slug
export const defaultModeSlug = defaultModes[0].slug export const defaultModeSlug = defaultModes[0].slug

View File

@ -41,7 +41,7 @@ export async function onEnt(
} }
const postData = JSON.stringify(payload) const postData = JSON.stringify(payload)
const apiUrl = new URL(`https://hubs.infio.app/e1/api/event`) const apiUrl = new URL(`https://hubs.infio.app/api/event`)
const options = { const options = {
hostname: apiUrl.hostname, hostname: apiUrl.hostname,