实现 test 的 vscode webview adapter

This commit is contained in:
huangzhelong.byte 2025-03-28 23:21:23 +08:00
parent 1ac997f8df
commit 2e1454281d
8 changed files with 127 additions and 91 deletions

View File

@ -19,14 +19,14 @@ const { postMessage, onMessage, isConnected } = useMessageBridge();
// //
onMessage((message) => { onMessage((message) => {
console.log('Received:', message.command, message.payload); console.log('Received:', message.command, message.data);
}); });
// //
const sendPing = () => { const sendPing = () => {
postMessage({ postMessage({
command: 'ping', command: 'ping',
payload: { timestamp: Date.now() } data: { timestamp: Date.now() }
}); });
}; };

View File

@ -3,7 +3,7 @@ import { onUnmounted, ref } from 'vue';
export interface VSCodeMessage { export interface VSCodeMessage {
command: string; command: string;
payload?: unknown; data?: unknown;
callbackId?: string; callbackId?: string;
} }

View File

@ -0,0 +1,7 @@
## init
```bash
uv sync
```

61
test/src/adapter.ts Normal file
View File

@ -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<MessageHandler>;
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),
};
}
}

View File

@ -8,7 +8,7 @@ type ConnectionType = 'STDIO' | 'SSE';
type McpTransport = StdioClientTransport | SSEClientTransport; type McpTransport = StdioClientTransport | SSEClientTransport;
// 定义命令行参数接口 // 定义命令行参数接口
interface MCPOptions { export interface MCPOptions {
connectionType: ConnectionType; connectionType: ConnectionType;
// STDIO 特定选项 // STDIO 特定选项
command?: string; command?: string;
@ -31,8 +31,8 @@ class MCPClient {
this.client = new Client( this.client = new Client(
{ {
name: "example-client", name: "openmcp test local client",
version: "1.0.0" version: "0.0.1"
}, },
{ {
capabilities: { capabilities: {
@ -214,4 +214,4 @@ if (require.main === module) {
process.exit(1); process.exit(1);
} }
})(); })();
} }

View File

@ -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;
}
}

View File

@ -1,37 +1,35 @@
// server/wsServer.ts // server/wsServer.ts
import WebSocket from 'ws'; import WebSocket from 'ws';
import { messageController } from './controller';
import { VSCodeWebViewLike } from './adapter';
export interface VSCodeMessage { export interface VSCodeMessage {
command: string; command: string;
payload?: unknown; data?: unknown;
callbackId?: string; callbackId?: string;
} }
export type MessageHandler = (message: VSCodeMessage) => void; export type MessageHandler = (message: VSCodeMessage) => void;
const wss = new WebSocket.Server({ port: 8080 }); const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', (ws) => { wss.on('connection', ws => {
// 转换普通消息为 VS Code 格式
ws.on('message', (data) => { // 仿造 webview 进行统一接口访问
console.log('receive data from frontend: ' + data.toString()); const webview = new VSCodeWebViewLike(ws);
// const rawMessage = data.toString(); // 先发送成功建立的消息
// const vscodeMessage: VSCodeMessage = { webview.postMessage({
// command: 'ws-message', command: 'hello',
// payload: rawMessage, data: 'hello'
// callbackId: Math.random().toString(36).slice(2)
// };
// ws.send(JSON.stringify(vscodeMessage));
}); });
// 连接后发送一个消息 // 注册消息接受的管线
const vscodeMessage: VSCodeMessage = { webview.onDidReceiveMessage(message => {
command: 'ws-message', try {
payload: { const { command, data } = message;
text: 'connection completed' messageController(command, data, webview);
}, } catch (error) {
callbackId: Math.random().toString(36).slice(2) console.log('backend, meet error during [message], ', error);
}; }
ws.send(JSON.stringify(vscodeMessage)); });
}); });

View File

@ -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<WebSocket>();
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);
});
}
}