完成主要协议的中台实现
This commit is contained in:
parent
c8c069574e
commit
4947a0d2c2
@ -26,6 +26,7 @@
|
||||
- [x] 完成最基本的各类基础设施
|
||||
- [ ] 支持同时调试多个 MCP Server
|
||||
- [ ] 支持通过大模型进行在线验证
|
||||
- [ ] 支持 completion/complete 协议字段
|
||||
|
||||
---
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
<template>
|
||||
<div class="resource-module">
|
||||
<h2>资源模块</h2>
|
||||
<!-- 资源模块内容将在这里实现 -->
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -16,13 +16,14 @@
|
||||
import { defineComponent, computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { Connection } from './sidebar';
|
||||
import { connectionResult } from '@/views/connect/connection';
|
||||
|
||||
defineComponent({ name: 'connected' });
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const statusString = computed(() => {
|
||||
if (Connection.connected) {
|
||||
if (connectionResult.success) {
|
||||
return t('connected');
|
||||
} else {
|
||||
return t('disconnected');
|
||||
@ -30,7 +31,7 @@ const statusString = computed(() => {
|
||||
});
|
||||
|
||||
const statusColorStyle = computed(() => {
|
||||
if (Connection.connected) {
|
||||
if (connectionResult.success) {
|
||||
return 'connected-color';
|
||||
} else {
|
||||
return 'disconnected-color';
|
||||
|
@ -20,6 +20,5 @@ export const sidebarItems = reactive([
|
||||
]);
|
||||
|
||||
export const Connection = reactive({
|
||||
connected: false,
|
||||
showPanel: false
|
||||
});
|
||||
|
@ -104,5 +104,6 @@
|
||||
"connection-method": "طريقة الاتصال",
|
||||
"command": "أمر",
|
||||
"env-var": "متغيرات البيئة",
|
||||
"log": "سجلات"
|
||||
"log": "سجلات",
|
||||
"warning.click-to-connect": "يرجى النقر أولاً على $1 على اليسار للاتصال"
|
||||
}
|
@ -104,5 +104,6 @@
|
||||
"connection-method": "Verbindungsmethode",
|
||||
"command": "Befehl",
|
||||
"env-var": "Umgebungsvariablen",
|
||||
"log": "Protokolle"
|
||||
"log": "Protokolle",
|
||||
"warning.click-to-connect": "Bitte klicken Sie zuerst auf $1 links, um eine Verbindung herzustellen"
|
||||
}
|
@ -104,5 +104,6 @@
|
||||
"connection-method": "Connection method",
|
||||
"command": "Command",
|
||||
"env-var": "Environment variables",
|
||||
"log": "Logs"
|
||||
"log": "Logs",
|
||||
"warning.click-to-connect": "Please first click on $1 on the left to connect"
|
||||
}
|
@ -104,5 +104,6 @@
|
||||
"connection-method": "Méthode de connexion",
|
||||
"command": "Commande",
|
||||
"env-var": "Variables d'environnement",
|
||||
"log": "Journaux"
|
||||
"log": "Journaux",
|
||||
"warning.click-to-connect": "Veuillez d'abord cliquer sur $1 à gauche pour vous connecter"
|
||||
}
|
@ -104,5 +104,6 @@
|
||||
"connection-method": "接続方法",
|
||||
"command": "コマンド",
|
||||
"env-var": "環境変数",
|
||||
"log": "ログ"
|
||||
"log": "ログ",
|
||||
"warning.click-to-connect": "まず左側の$1をクリックして接続してください"
|
||||
}
|
@ -104,5 +104,6 @@
|
||||
"connection-method": "연결 방법",
|
||||
"command": "명령",
|
||||
"env-var": "환경 변수",
|
||||
"log": "로그"
|
||||
"log": "로그",
|
||||
"warning.click-to-connect": "먼저 왼쪽의 $1을 클릭하여 연결하십시오"
|
||||
}
|
@ -104,5 +104,6 @@
|
||||
"connection-method": "Способ подключения",
|
||||
"command": "Команда",
|
||||
"env-var": "Переменные среды",
|
||||
"log": "Логи"
|
||||
"log": "Логи",
|
||||
"warning.click-to-connect": "Пожалуйста, сначала нажмите на $1 слева для подключения"
|
||||
}
|
@ -104,5 +104,6 @@
|
||||
"connection-method": "连接方式",
|
||||
"command": "命令",
|
||||
"env-var": "环境变量",
|
||||
"log": "日志"
|
||||
"log": "日志",
|
||||
"warning.click-to-connect": "请先点击左侧的 $1 进行连接"
|
||||
}
|
@ -104,5 +104,6 @@
|
||||
"connection-method": "連接方式",
|
||||
"command": "命令",
|
||||
"env-var": "環境變數",
|
||||
"log": "日誌"
|
||||
"log": "日誌",
|
||||
"warning.click-to-connect": "請先點擊左側的 $1 進行連接"
|
||||
}
|
@ -2,8 +2,10 @@
|
||||
<div class="debug-welcome">
|
||||
<span>{{ t('choose-a-project-debug') }}</span>
|
||||
<div class="welcome-container">
|
||||
<!-- TODO: 支持更多的 server -->
|
||||
<span
|
||||
class="debug-option"
|
||||
:class="{ 'disable': !connectionResult.success }"
|
||||
v-for="(option, index) of debugOptions"
|
||||
:key="index"
|
||||
@click="chooseDebugMode(index)"
|
||||
@ -21,6 +23,8 @@
|
||||
import { debugModes, tabs } from '@/components/main-panel/panel';
|
||||
import { defineComponent, markRaw } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { connectionResult } from '../connect/connection';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
defineComponent({ name: 'welcome' });
|
||||
|
||||
@ -50,10 +54,24 @@ const debugOptions = [
|
||||
];
|
||||
|
||||
function chooseDebugMode(index: number) {
|
||||
const activeTab = tabs.activeTab;
|
||||
activeTab.component = markRaw(debugModes[index]);
|
||||
activeTab.icon = debugOptions[index].icon;
|
||||
activeTab.name = debugOptions[index].name;
|
||||
|
||||
// TODO: 支持更多的 server
|
||||
if (connectionResult.success) {
|
||||
const activeTab = tabs.activeTab;
|
||||
activeTab.component = markRaw(debugModes[index]);
|
||||
activeTab.icon = debugOptions[index].icon;
|
||||
activeTab.name = debugOptions[index].name;
|
||||
} else {
|
||||
const message = t('warning.click-to-connect')
|
||||
.replace('$1', t('connect'));
|
||||
|
||||
ElMessage({
|
||||
message,
|
||||
type: 'error',
|
||||
duration: 3000,
|
||||
showClose: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
@ -112,4 +130,9 @@ function chooseDebugMode(index: number) {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.debug-option.disable {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
</style>
|
@ -22,7 +22,7 @@ export interface MCPOptions {
|
||||
}
|
||||
|
||||
// 增强的客户端类
|
||||
class MCPClient {
|
||||
export class MCPClient {
|
||||
private client: Client;
|
||||
private transport?: McpTransport;
|
||||
private options: MCPOptions;
|
||||
@ -77,29 +77,29 @@ class MCPClient {
|
||||
}
|
||||
|
||||
// 列出提示
|
||||
public async listPrompts(): Promise<any> {
|
||||
public async listPrompts() {
|
||||
return await this.client.listPrompts();
|
||||
}
|
||||
|
||||
// 获取提示
|
||||
public async getPrompt(name: string, args: Record<string, any> = {}): Promise<any> {
|
||||
public async getPrompt(name: string, args: Record<string, any> = {}) {
|
||||
return await this.client.getPrompt({ name }, args);
|
||||
}
|
||||
|
||||
// 列出资源
|
||||
public async listResources(): Promise<any> {
|
||||
public async listResources() {
|
||||
return await this.client.listResources();
|
||||
}
|
||||
|
||||
// 读取资源
|
||||
public async readResource(uri: string): Promise<any> {
|
||||
public async readResource(uri: string) {
|
||||
return await this.client.readResource({
|
||||
uri
|
||||
});
|
||||
}
|
||||
|
||||
// 调用工具
|
||||
public async callTool(options: { name: string; arguments: Record<string, any> }): Promise<any> {
|
||||
public async callTool(options: { name: string; arguments: Record<string, any> }) {
|
||||
return await this.client.callTool(options);
|
||||
}
|
||||
}
|
||||
|
185
test/src/controller/handler.ts
Normal file
185
test/src/controller/handler.ts
Normal file
@ -0,0 +1,185 @@
|
||||
import { VSCodeWebViewLike } from "../adapter";
|
||||
import { MCPClient } from "./connect";
|
||||
|
||||
// ==================== 接口定义 ====================
|
||||
export interface GetPromptOption {
|
||||
promptId: string;
|
||||
args?: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface ReadResourceOption {
|
||||
resourceUri: string;
|
||||
}
|
||||
|
||||
export interface CallToolOption {
|
||||
toolName: string;
|
||||
toolArgs: Record<string, any>;
|
||||
}
|
||||
|
||||
// ==================== 函数实现 ====================
|
||||
|
||||
/**
|
||||
* @description 列出所有 prompts
|
||||
*/
|
||||
export async function listPrompts(
|
||||
client: MCPClient | undefined,
|
||||
webview: VSCodeWebViewLike
|
||||
) {
|
||||
if (!client) {
|
||||
const connectResult = {
|
||||
code: 501,
|
||||
msg: 'mcp client 尚未连接'
|
||||
};
|
||||
webview.postMessage({ command: 'prompts/list', data: connectResult });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const prompts = await client.listPrompts();
|
||||
const result = {
|
||||
code: 200,
|
||||
msg: prompts
|
||||
};
|
||||
webview.postMessage({ command: 'prompts/list', data: result });
|
||||
} catch (error) {
|
||||
const result = {
|
||||
code: 500,
|
||||
msg: (error as any).toString()
|
||||
};
|
||||
webview.postMessage({ command: 'prompts/list', data: result });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取特定 prompt
|
||||
*/
|
||||
export async function getPrompt(
|
||||
client: MCPClient | undefined,
|
||||
option: GetPromptOption,
|
||||
webview: VSCodeWebViewLike
|
||||
) {
|
||||
if (!client) {
|
||||
const connectResult = {
|
||||
code: 501,
|
||||
msg: 'mcp client 尚未连接'
|
||||
};
|
||||
webview.postMessage({ command: 'prompts/get', data: connectResult });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const prompt = await client.getPrompt(option.promptId, option.args || {});
|
||||
const result = {
|
||||
code: 200,
|
||||
msg: prompt
|
||||
};
|
||||
webview.postMessage({ command: 'prompts/get', data: result });
|
||||
} catch (error) {
|
||||
const result = {
|
||||
code: 500,
|
||||
msg: (error as any).toString()
|
||||
};
|
||||
webview.postMessage({ command: 'prompts/get', data: result });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 列出所有resources
|
||||
*/
|
||||
export async function listResources(
|
||||
client: MCPClient | undefined,
|
||||
webview: VSCodeWebViewLike
|
||||
) {
|
||||
if (!client) {
|
||||
const connectResult = {
|
||||
code: 501,
|
||||
msg: 'mcp client 尚未连接'
|
||||
};
|
||||
webview.postMessage({ command: 'resources/list', data: connectResult });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const resources = await client.listResources();
|
||||
const result = {
|
||||
code: 200,
|
||||
msg: resources
|
||||
};
|
||||
webview.postMessage({ command: 'resources/list', data: result });
|
||||
} catch (error) {
|
||||
const result = {
|
||||
code: 500,
|
||||
msg: (error as any).toString()
|
||||
};
|
||||
webview.postMessage({ command: 'resources/list', data: result });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 读取特定resource
|
||||
*/
|
||||
export async function readResource(
|
||||
client: MCPClient | undefined,
|
||||
option: ReadResourceOption,
|
||||
webview: VSCodeWebViewLike
|
||||
) {
|
||||
if (!client) {
|
||||
const connectResult = {
|
||||
code: 501,
|
||||
msg: 'mcp client 尚未连接'
|
||||
};
|
||||
webview.postMessage({ command: 'resources/read', data: connectResult });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const resource = await client.readResource(option.resourceUri);
|
||||
const result = {
|
||||
code: 200,
|
||||
msg: resource
|
||||
};
|
||||
webview.postMessage({ command: 'resources/read', data: result });
|
||||
} catch (error) {
|
||||
const result = {
|
||||
code: 500,
|
||||
msg: (error as any).toString()
|
||||
};
|
||||
webview.postMessage({ command: 'resources/read', data: result });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 调用工具
|
||||
*/
|
||||
export async function callTool(
|
||||
client: MCPClient | undefined,
|
||||
option: CallToolOption,
|
||||
webview: VSCodeWebViewLike
|
||||
) {
|
||||
if (!client) {
|
||||
const connectResult = {
|
||||
code: 501,
|
||||
msg: 'mcp client 尚未连接'
|
||||
};
|
||||
webview.postMessage({ command: 'tools/call', data: connectResult });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const toolResult = await client.callTool({
|
||||
name: option.toolName,
|
||||
arguments: option.toolArgs
|
||||
});
|
||||
const result = {
|
||||
code: 200,
|
||||
msg: toolResult
|
||||
};
|
||||
webview.postMessage({ command: 'tools/call', data: result });
|
||||
} catch (error) {
|
||||
const result = {
|
||||
code: 500,
|
||||
msg: (error as any).toString()
|
||||
};
|
||||
webview.postMessage({ command: 'tools/call', data: result });
|
||||
}
|
||||
}
|
@ -1,13 +1,18 @@
|
||||
|
||||
import { VSCodeWebViewLike } from '../adapter';
|
||||
import { connect, type MCPOptions } from './connect';
|
||||
import { connect, MCPClient, type MCPOptions } from './connect';
|
||||
import { callTool, getPrompt, listPrompts, listResources, readResource } from './handler';
|
||||
|
||||
|
||||
// TODO: 支持更多的 client
|
||||
let client: MCPClient | undefined = undefined;
|
||||
|
||||
async function connectHandler(option: MCPOptions, webview: VSCodeWebViewLike) {
|
||||
try {
|
||||
const client = await connect(option);
|
||||
client = await connect(option);
|
||||
const connectResult = {
|
||||
code: 200,
|
||||
msg: 'connect success'
|
||||
msg: 'connect success\nHello from OpenMCP | virtual client version: 0.0.1'
|
||||
};
|
||||
webview.postMessage({ command: 'connect', data: connectResult });
|
||||
} catch (error) {
|
||||
@ -24,12 +29,33 @@ async function connectHandler(option: MCPOptions, webview: VSCodeWebViewLike) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function messageController(command: string, data: any, webview: VSCodeWebViewLike) {
|
||||
switch (command) {
|
||||
case 'connect':
|
||||
connectHandler(data, webview);
|
||||
break;
|
||||
|
||||
case 'prompts/list':
|
||||
listPrompts(client, webview);
|
||||
break;
|
||||
|
||||
case 'prompts/get':
|
||||
getPrompt(client, data, webview);
|
||||
break;
|
||||
|
||||
case 'resources/list':
|
||||
listResources(client, webview);
|
||||
break;
|
||||
|
||||
case 'resources/read':
|
||||
readResource(client, data, webview);
|
||||
break;
|
||||
|
||||
case 'tools/call':
|
||||
callTool(client, data, webview);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user