diff --git a/README.md b/README.md
index b57de77..0733455 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,7 @@
- [x] 完成最基本的各类基础设施
- [ ] 支持同时调试多个 MCP Server
- [ ] 支持通过大模型进行在线验证
+- [ ] 支持 completion/complete 协议字段
---
diff --git a/app/src/components/main-panel/resource/index.vue b/app/src/components/main-panel/resource/index.vue
index cae7784..994ebc6 100644
--- a/app/src/components/main-panel/resource/index.vue
+++ b/app/src/components/main-panel/resource/index.vue
@@ -1,7 +1,9 @@
资源模块
-
+
+
+
diff --git a/app/src/components/sidebar/connected.vue b/app/src/components/sidebar/connected.vue
index dca9935..9b95ed2 100644
--- a/app/src/components/sidebar/connected.vue
+++ b/app/src/components/sidebar/connected.vue
@@ -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';
diff --git a/app/src/components/sidebar/sidebar.ts b/app/src/components/sidebar/sidebar.ts
index df2f63e..84bef05 100644
--- a/app/src/components/sidebar/sidebar.ts
+++ b/app/src/components/sidebar/sidebar.ts
@@ -20,6 +20,5 @@ export const sidebarItems = reactive([
]);
export const Connection = reactive({
- connected: false,
showPanel: false
});
diff --git a/app/src/i18n/ar.json b/app/src/i18n/ar.json
index 0fc8c6a..44ab901 100644
--- a/app/src/i18n/ar.json
+++ b/app/src/i18n/ar.json
@@ -104,5 +104,6 @@
"connection-method": "طريقة الاتصال",
"command": "أمر",
"env-var": "متغيرات البيئة",
- "log": "سجلات"
+ "log": "سجلات",
+ "warning.click-to-connect": "يرجى النقر أولاً على $1 على اليسار للاتصال"
}
\ No newline at end of file
diff --git a/app/src/i18n/de.json b/app/src/i18n/de.json
index e22998f..a960b9f 100644
--- a/app/src/i18n/de.json
+++ b/app/src/i18n/de.json
@@ -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"
}
\ No newline at end of file
diff --git a/app/src/i18n/en.json b/app/src/i18n/en.json
index d16bc29..59e1cef 100644
--- a/app/src/i18n/en.json
+++ b/app/src/i18n/en.json
@@ -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"
}
\ No newline at end of file
diff --git a/app/src/i18n/fr.json b/app/src/i18n/fr.json
index 8e5783d..b7e672f 100644
--- a/app/src/i18n/fr.json
+++ b/app/src/i18n/fr.json
@@ -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"
}
\ No newline at end of file
diff --git a/app/src/i18n/ja.json b/app/src/i18n/ja.json
index 30a4603..68ee6a4 100644
--- a/app/src/i18n/ja.json
+++ b/app/src/i18n/ja.json
@@ -104,5 +104,6 @@
"connection-method": "接続方法",
"command": "コマンド",
"env-var": "環境変数",
- "log": "ログ"
+ "log": "ログ",
+ "warning.click-to-connect": "まず左側の$1をクリックして接続してください"
}
\ No newline at end of file
diff --git a/app/src/i18n/ko.json b/app/src/i18n/ko.json
index 320778e..e447e40 100644
--- a/app/src/i18n/ko.json
+++ b/app/src/i18n/ko.json
@@ -104,5 +104,6 @@
"connection-method": "연결 방법",
"command": "명령",
"env-var": "환경 변수",
- "log": "로그"
+ "log": "로그",
+ "warning.click-to-connect": "먼저 왼쪽의 $1을 클릭하여 연결하십시오"
}
\ No newline at end of file
diff --git a/app/src/i18n/ru.json b/app/src/i18n/ru.json
index 0da8c4a..3ea6652 100644
--- a/app/src/i18n/ru.json
+++ b/app/src/i18n/ru.json
@@ -104,5 +104,6 @@
"connection-method": "Способ подключения",
"command": "Команда",
"env-var": "Переменные среды",
- "log": "Логи"
+ "log": "Логи",
+ "warning.click-to-connect": "Пожалуйста, сначала нажмите на $1 слева для подключения"
}
\ No newline at end of file
diff --git a/app/src/i18n/zh-cn.json b/app/src/i18n/zh-cn.json
index f4ad466..08923ee 100644
--- a/app/src/i18n/zh-cn.json
+++ b/app/src/i18n/zh-cn.json
@@ -104,5 +104,6 @@
"connection-method": "连接方式",
"command": "命令",
"env-var": "环境变量",
- "log": "日志"
+ "log": "日志",
+ "warning.click-to-connect": "请先点击左侧的 $1 进行连接"
}
\ No newline at end of file
diff --git a/app/src/i18n/zh-tw.json b/app/src/i18n/zh-tw.json
index 6b0537d..b46798e 100644
--- a/app/src/i18n/zh-tw.json
+++ b/app/src/i18n/zh-tw.json
@@ -104,5 +104,6 @@
"connection-method": "連接方式",
"command": "命令",
"env-var": "環境變數",
- "log": "日誌"
+ "log": "日誌",
+ "warning.click-to-connect": "請先點擊左側的 $1 進行連接"
}
\ No newline at end of file
diff --git a/app/src/views/debug/welcome.vue b/app/src/views/debug/welcome.vue
index edaafc5..cb20fa9 100644
--- a/app/src/views/debug/welcome.vue
+++ b/app/src/views/debug/welcome.vue
@@ -2,8 +2,10 @@
{{ t('choose-a-project-debug') }}
+
@@ -112,4 +130,9 @@ function chooseDebugMode(index: number) {
user-select: none;
}
+.debug-option.disable {
+ cursor: not-allowed;
+ opacity: 0.5;
+}
+
\ No newline at end of file
diff --git a/test/src/controller/connect.ts b/test/src/controller/connect.ts
index eedf368..54703aa 100644
--- a/test/src/controller/connect.ts
+++ b/test/src/controller/connect.ts
@@ -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 {
+ public async listPrompts() {
return await this.client.listPrompts();
}
// 获取提示
- public async getPrompt(name: string, args: Record = {}): Promise {
+ public async getPrompt(name: string, args: Record = {}) {
return await this.client.getPrompt({ name }, args);
}
// 列出资源
- public async listResources(): Promise {
+ public async listResources() {
return await this.client.listResources();
}
// 读取资源
- public async readResource(uri: string): Promise {
+ public async readResource(uri: string) {
return await this.client.readResource({
uri
});
}
// 调用工具
- public async callTool(options: { name: string; arguments: Record }): Promise {
+ public async callTool(options: { name: string; arguments: Record }) {
return await this.client.callTool(options);
}
}
diff --git a/test/src/controller/handler.ts b/test/src/controller/handler.ts
new file mode 100644
index 0000000..ecb08ab
--- /dev/null
+++ b/test/src/controller/handler.ts
@@ -0,0 +1,185 @@
+import { VSCodeWebViewLike } from "../adapter";
+import { MCPClient } from "./connect";
+
+// ==================== 接口定义 ====================
+export interface GetPromptOption {
+ promptId: string;
+ args?: Record;
+}
+
+export interface ReadResourceOption {
+ resourceUri: string;
+}
+
+export interface CallToolOption {
+ toolName: string;
+ toolArgs: Record;
+}
+
+// ==================== 函数实现 ====================
+
+/**
+ * @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 });
+ }
+}
diff --git a/test/src/controller/index.ts b/test/src/controller/index.ts
index 0661867..f73e427 100644
--- a/test/src/controller/index.ts
+++ b/test/src/controller/index.ts
@@ -1,17 +1,22 @@
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) {
-
+
// TODO: 这边获取到的 error 不够精致,如何才能获取到更加精准的错误
// 比如 error: Failed to spawn: `server.py`
// Caused by: No such file or directory (os error 2)
@@ -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;
}