diff --git a/app/src/App.vue b/app/src/App.vue index 85332d8..2392c7a 100644 --- a/app/src/App.vue +++ b/app/src/App.vue @@ -19,14 +19,14 @@ const { postMessage, onMessage, isConnected } = useMessageBridge(); // 监听所有消息 onMessage((message) => { - console.log('Received:', message.command, message.payload); + console.log('Received:', message.command, message.data); }); // 发送消息 const sendPing = () => { postMessage({ command: 'ping', - payload: { timestamp: Date.now() } + data: { timestamp: Date.now() } }); }; diff --git a/app/src/api/message-bridge.ts b/app/src/api/message-bridge.ts index 184d569..29e3e52 100644 --- a/app/src/api/message-bridge.ts +++ b/app/src/api/message-bridge.ts @@ -3,7 +3,7 @@ import { onUnmounted, ref } from 'vue'; export interface VSCodeMessage { command: string; - payload?: unknown; + data?: unknown; callbackId?: string; } diff --git a/servers/README.md b/servers/README.md index e69de29..39534c6 100644 --- a/servers/README.md +++ b/servers/README.md @@ -0,0 +1,7 @@ + +## init + +```bash +uv sync +``` + diff --git a/test/src/adapter.ts b/test/src/adapter.ts new file mode 100644 index 0000000..5fa83d1 --- /dev/null +++ b/test/src/adapter.ts @@ -0,0 +1,61 @@ +import { WebSocket } from 'ws'; + +// WebSocket 消息格式 +export interface WebSocketMessage { + command: string; + data: any; +} + +// 服务器返回的消息格式 +export interface WebSocketResponse { + result?: any; + timeCost?: number; + error?: string; +} + +// 监听器回调类型 +export type MessageHandler = (message: any) => void; + +export class VSCodeWebViewLike { + private ws: WebSocket; + private messageHandlers: Set; + + constructor(ws: WebSocket) { + this.ws = ws; + this.messageHandlers = new Set(); + + // 监听消息并触发回调 + this.ws.on('message', (rawData: Buffer | string) => { + try { + const message: any = JSON.parse(rawData.toString()); + this.messageHandlers.forEach((handler) => handler(message)); + } catch (error) { + console.error('Failed to parse WebSocket message:', error); + } + }); + } + + /** + * 发送消息(模拟 vscode.webview.postMessage) + * @param message - 包含 command 和 args 的消息 + */ + postMessage(message: WebSocketMessage): void { + if (this.ws.readyState === WebSocket.OPEN) { + this.ws.send(JSON.stringify(message)); + } else { + console.error('WebSocket is not open, cannot send message'); + } + } + + /** + * 接收消息(模拟 vscode.webview.onDidReceiveMessage) + * @param callback - 消息回调 + * @returns {{ dispose: () => void }} - 可销毁的监听器 + */ + onDidReceiveMessage(callback: MessageHandler): { dispose: () => void } { + this.messageHandlers.add(callback); + return { + dispose: () => this.messageHandlers.delete(callback), + }; + } +} \ No newline at end of file diff --git a/test/src/connect.ts b/test/src/controller/connect.ts similarity index 98% rename from test/src/connect.ts rename to test/src/controller/connect.ts index 342a015..e0c3610 100644 --- a/test/src/connect.ts +++ b/test/src/controller/connect.ts @@ -8,7 +8,7 @@ type ConnectionType = 'STDIO' | 'SSE'; type McpTransport = StdioClientTransport | SSEClientTransport; // 定义命令行参数接口 -interface MCPOptions { +export interface MCPOptions { connectionType: ConnectionType; // STDIO 特定选项 command?: string; @@ -31,8 +31,8 @@ class MCPClient { this.client = new Client( { - name: "example-client", - version: "1.0.0" + name: "openmcp test local client", + version: "0.0.1" }, { capabilities: { @@ -214,4 +214,4 @@ if (require.main === module) { process.exit(1); } })(); -} \ No newline at end of file +} diff --git a/test/src/controller/index.ts b/test/src/controller/index.ts new file mode 100644 index 0000000..a1e5bf2 --- /dev/null +++ b/test/src/controller/index.ts @@ -0,0 +1,31 @@ + +import { VSCodeWebViewLike } from '../adapter'; +import { connect, type MCPOptions } from './connect'; + +async function connectHandler(option: MCPOptions, webview: VSCodeWebViewLike) { + try { + const client = await connect(option); + const connectResult = { + code: 200, + msg: 'connect success' + }; + webview.postMessage({ command: 'connect', data: connectResult }); + } catch (error) { + const connectResult = { + code: 500, + msg: error + }; + webview.postMessage({ command: 'connect', data: connectResult }); + } +} + +export function messageController(command: string, data: any, webview: VSCodeWebViewLike) { + switch (command) { + case 'connect': + connectHandler(data, webview); + break; + + default: + break; + } +} \ No newline at end of file diff --git a/test/src/main.ts b/test/src/main.ts index 9f70240..7f09ed5 100644 --- a/test/src/main.ts +++ b/test/src/main.ts @@ -1,37 +1,35 @@ // server/wsServer.ts import WebSocket from 'ws'; +import { messageController } from './controller'; +import { VSCodeWebViewLike } from './adapter'; export interface VSCodeMessage { command: string; - payload?: unknown; + data?: unknown; callbackId?: string; } export type MessageHandler = (message: VSCodeMessage) => void; - const wss = new WebSocket.Server({ port: 8080 }); -wss.on('connection', (ws) => { - // 转换普通消息为 VS Code 格式 - ws.on('message', (data) => { - console.log('receive data from frontend: ' + data.toString()); - - // const rawMessage = data.toString(); - // const vscodeMessage: VSCodeMessage = { - // command: 'ws-message', - // payload: rawMessage, - // callbackId: Math.random().toString(36).slice(2) - // }; - // ws.send(JSON.stringify(vscodeMessage)); +wss.on('connection', ws => { + + // 仿造 webview 进行统一接口访问 + const webview = new VSCodeWebViewLike(ws); + + // 先发送成功建立的消息 + webview.postMessage({ + command: 'hello', + data: 'hello' }); - // 连接后发送一个消息 - const vscodeMessage: VSCodeMessage = { - command: 'ws-message', - payload: { - text: 'connection completed' - }, - callbackId: Math.random().toString(36).slice(2) - }; - ws.send(JSON.stringify(vscodeMessage)); + // 注册消息接受的管线 + webview.onDidReceiveMessage(message => { + try { + const { command, data } = message; + messageController(command, data, webview); + } catch (error) { + console.log('backend, meet error during [message], ', error); + } + }); }); \ No newline at end of file diff --git a/test/src/wsHandler.ts b/test/src/wsHandler.ts deleted file mode 100644 index 322c2df..0000000 --- a/test/src/wsHandler.ts +++ /dev/null @@ -1,61 +0,0 @@ -// server/src/wsHandler.ts -import { WebSocketServer, WebSocket } from 'ws'; -import { IMessage } from './types'; - -export class WSServer { - private wss: WebSocketServer; - private clients = new Set(); - - constructor(port: number) { - this.wss = new WebSocketServer({ port }); - this.setupConnection(); - } - - private setupConnection() { - this.wss.on('connection', (ws) => { - this.clients.add(ws); - console.log('Client connected'); - - ws.on('message', (data) => { - try { - const message: IMessage = JSON.parse(data.toString()); - this.handleMessage(ws, message); - } catch (err) { - console.error('Message parse error:', err); - } - }); - - ws.on('close', () => { - this.clients.delete(ws); - console.log('Client disconnected'); - }); - }); - } - - private handleMessage(ws: WebSocket, message: IMessage) { - console.log('Received:', message); - - // 模拟 VS Code 的 postMessage 响应 - if (message.type === 'client-message') { - this.send(ws, { - type: 'server-response', - data: { - original: message.data, - response: 'Message received at ' + new Date().toISOString() - } - }); - } - } - - public send(ws: WebSocket, message: IMessage) { - if (ws.readyState === ws.OPEN) { - ws.send(JSON.stringify(message)); - } - } - - public broadcast(message: IMessage) { - this.clients.forEach(client => { - this.send(client, message); - }); - } -} \ No newline at end of file