-
-
-
-
-
+
+
\ No newline at end of file
diff --git a/renderer/src/views/connect/core.ts b/renderer/src/views/connect/core.ts
index 277121b..6bb004a 100644
--- a/renderer/src/views/connect/core.ts
+++ b/renderer/src/views/connect/core.ts
@@ -1,9 +1,11 @@
import { useMessageBridge } from "@/api/message-bridge";
-import { reactive } from "vue";
+import { reactive, type Reactive } from "vue";
import type { IConnectionResult, ConnectionTypeOptionItem, IConnectionArgs, IConnectionEnvironment, McpOptions } from "./type";
import { ElMessage } from "element-plus";
import { loadPanels } from "@/hook/panel";
import { getPlatform } from "@/api/platform";
+import type { PromptsGetResponse, PromptsListResponse, PromptTemplate, Resources, ResourcesListResponse, ResourcesReadResponse, ResourceTemplate, ResourceTemplatesListResponse, ToolCallResponse, ToolItem, ToolsListResponse } from "@/hook/type";
+import { mcpSetting } from "@/hook/mcp";
export const connectionSelectDataViewOption: ConnectionTypeOptionItem[] = [
{
@@ -37,6 +39,11 @@ export class McpClient {
// setting 面板的 ref
public connectionSettingRef: any = null;
+ public tools: Map | null = null;
+ public promptTemplates: Map | null = null;
+ public resources: Map | null = null;
+ public resourceTemplates: Map | null = null;
+
constructor(
public clientVersion: string = '0.0.1',
public clientNamePrefix: string = 'openmcp.connect'
@@ -104,6 +111,83 @@ export class McpClient {
return env;
}
+ public async getTools() {
+ if (this.tools) {
+ return this.tools;
+ }
+
+ const bridge = useMessageBridge();
+
+ const { code, msg } = await bridge.commandRequest('tools/list', { clientId: this.clientId });
+ if (code!== 200) {
+ return new Map();
+ }
+
+ this.tools = new Map();
+ msg.tools.forEach(tool => {
+ this.tools!.set(tool.name, tool);
+ });
+
+ return this.tools;
+ }
+
+ public async getPromptTemplates() {
+ if (this.promptTemplates) {
+ return this.promptTemplates;
+ }
+
+ const bridge = useMessageBridge();
+
+ const { code, msg } = await bridge.commandRequest('prompts/list', { clientId: this.clientId });
+ if (code!== 200) {
+ return new Map();
+ }
+
+ this.promptTemplates = new Map();
+ msg.prompts.forEach(template => {
+ this.promptTemplates!.set(template.name, template);
+ });
+
+ return this.promptTemplates;
+ }
+
+ public async getResources() {
+ if (this.resources) {
+ return this.resources;
+ }
+
+ const bridge = useMessageBridge();
+
+ const { code, msg } = await bridge.commandRequest('resources/list', { clientId: this.clientId });
+ if (code!== 200) {
+ return new Map();
+ }
+
+ this.resources = new Map();
+ msg.resources.forEach(resource => {
+ this.resources!.set(resource.name, resource);
+ });
+ return this.resources;
+ }
+
+ public async getResourceTemplates() {
+ if (this.resourceTemplates) {
+ return this.resourceTemplates;
+ }
+
+ const bridge = useMessageBridge();
+
+ const { code, msg } = await bridge.commandRequest('resources/templates/list', { clientId: this.clientId });
+ if (code!== 200) {
+ return new Map();
+ }
+ this.resourceTemplates = new Map();
+ msg.resourceTemplates.forEach(template => {
+ this.resourceTemplates!.set(template.name, template);
+ });
+ return this.resourceTemplates;
+ }
+
private get commandAndArgs() {
const commandString = this.connectionArgs.commandString;
@@ -163,14 +247,6 @@ export class McpClient {
ElMessage.error(message);
return false;
} else {
- const info = msg.info || '';
- if (info) {
- this.connectionResult.logString.push({
- type: 'info',
- message: msg.info || ''
- });
- }
-
this.connectionResult.logString.push({
type: 'info',
message: msg.name + ' ' + msg.version + ' 连接成功'
@@ -247,10 +323,11 @@ export class McpClient {
class McpClientAdapter {
- public clients: McpClient[] = [];
+ public clients: Reactive = [];
public currentClientIndex: number = 0;
private defaultClient: McpClient = new McpClient();
+ public connectLogListenerCancel: (() => void) | null = null;
constructor(
public platform: string
@@ -298,6 +375,28 @@ class McpClientAdapter {
}
public async launch() {
+ // 创建对于 log/output 的监听
+ if (!this.connectLogListenerCancel) {
+ const bridge = useMessageBridge();
+ this.connectLogListenerCancel = bridge.addCommandListener('connect/log', (message) => {
+ const { code, msg } = message;
+
+ console.log(code, msg);
+ const client = this.clients.at(-1);
+ console.log(client);
+
+ if (!client) {
+ return;
+ }
+
+ client.connectionResult.logString.push({
+ type: code === 200 ? 'info': 'error',
+ message: msg
+ });
+
+ }, { once: false });
+ }
+
const launchSignature = await this.getLaunchSignature();
console.log('launchSignature', launchSignature);
@@ -306,7 +405,7 @@ class McpClientAdapter {
for (const item of launchSignature) {
// 创建一个新的客户端
- const client = new McpClient();
+ const client = reactive(new McpClient());
// 同步连接参数
await client.acquireConnectionSignature(item);
@@ -314,11 +413,11 @@ class McpClientAdapter {
// 同步环境变量
await client.handleEnvSwitch(true);
+ this.clients.push(client);
+
// 连接
const ok = await client.connect();
allOk &&= ok;
-
- this.clients.push(client);
}
// 如果全部成功,保存连接参数
@@ -327,6 +426,76 @@ class McpClientAdapter {
}
}
+ public async readResource(resourceUri?: string) {
+ if (!resourceUri) {
+ return undefined;
+ }
+
+ // TODO: 如果遇到不同服务器的同名 tool,请拓展解决方案
+ // 目前只找到第一个匹配 toolName 的工具进行调用
+ let clientId = this.clients[0].clientId;
+
+ for (const client of this.clients) {
+ const resources = await client.getResources();
+ const resource = resources.get(resourceUri);
+ if (resource) {
+ clientId = client.clientId;
+ break;
+ }
+ }
+
+ const bridge = useMessageBridge();
+ const { code, msg } = await bridge.commandRequest('resources/read', { clientId, resourceUri });
+
+ return msg;
+ }
+
+ public async readPromptTemplate(promptId: string, args: Record) {
+ // TODO: 如果遇到不同服务器的同名 tool,请拓展解决方案
+ // 目前只找到第一个匹配 toolName 的工具进行调用
+ let clientId = this.clients[0].clientId;
+
+ for (const client of this.clients) {
+ const promptTemplates = await client.getPromptTemplates();
+ const promptTemplate = promptTemplates.get(promptId);
+ if (promptTemplate) {
+ clientId = client.clientId;
+ break;
+ }
+ }
+
+ const bridge = useMessageBridge();
+ const { code, msg } = await bridge.commandRequest('prompts/get', { clientId, promptId, args });
+ return msg;
+ }
+
+ public async callTool(toolName: string, toolArgs: Record) {
+ // TODO: 如果遇到不同服务器的同名 tool,请拓展解决方案
+ // 目前只找到第一个匹配 toolName 的工具进行调用
+ let clientId = this.clients[0].clientId;
+
+ for (const client of this.clients) {
+ const tools = await client.getTools();
+ const tool = tools.get(toolName);
+ if (tool) {
+ clientId = client.clientId;
+ break;
+ }
+ }
+
+ const bridge = useMessageBridge();
+ const { msg } = await bridge.commandRequest('tools/call', {
+ clientId,
+ toolName,
+ toolArgs: JSON.parse(JSON.stringify(toolArgs)),
+ callToolOption: {
+ timeout: mcpSetting.timeout * 1000
+ }
+ });
+
+ return msg;
+ }
+
public async loadPanels() {
const masterNode = this.clients[0];
await loadPanels(masterNode);
@@ -336,4 +505,13 @@ class McpClientAdapter {
const platform = getPlatform();
export const mcpClientAdapter = reactive(
new McpClientAdapter(platform)
-);
\ No newline at end of file
+);
+
+export interface ISegmentViewItem {
+ value: any;
+ label: string;
+ client: McpClient;
+ index: number;
+}
+
+export const segmentsView = reactive([]);
\ No newline at end of file
diff --git a/renderer/src/views/connect/index.ts b/renderer/src/views/connect/index.ts
index 68a4d45..50dfbb0 100644
--- a/renderer/src/views/connect/index.ts
+++ b/renderer/src/views/connect/index.ts
@@ -20,11 +20,11 @@ export async function initialise() {
// 获取引导状态
await getTour();
+ loading.close();
+
// 尝试进行初始化连接
await mcpClientAdapter.launch();
// loading panels
await mcpClientAdapter.loadPanels();
-
- loading.close();
}
\ No newline at end of file
diff --git a/renderer/src/views/connect/index.vue b/renderer/src/views/connect/index.vue
index c1c4ca4..075c62a 100644
--- a/renderer/src/views/connect/index.vue
+++ b/renderer/src/views/connect/index.vue
@@ -24,7 +24,9 @@
-
+