push 支持基本的 MCP 项目管理 to 70%
This commit is contained in:
parent
31a25f27bc
commit
b2b80c1a3f
@ -40,7 +40,7 @@
|
||||
|---------|---------|--------|---------|-----------|
|
||||
| `all` | 完成最基本的各类基础设施 | `完整版本` | 100% | `Done` |
|
||||
| `render` | chat 模式下支持进行成本分析 | `迭代版本` | 100% | `Done` |
|
||||
| `ext` | 支持基本的 MCP 项目管理 | `MVP` | 0% | `P0` |
|
||||
| `ext` | 支持基本的 MCP 项目管理 | `MVP` | 70% | `P0` |
|
||||
| `service` | 支持自定义支持 openai 接口协议的大模型接入 | `完整版本` | 100% | `Done` |
|
||||
| `service` | 支持自定义接口协议的大模型接入 | `MVP` | 0% | `P1` |
|
||||
| `all` | 支持同时调试多个 MCP Server | `MVP` | 0% | `P1` |
|
||||
|
@ -9,5 +9,9 @@ npm i
|
||||
node patch-mcp-sdk.js
|
||||
Set-Location ..
|
||||
|
||||
Set-Location servers
|
||||
uv sync
|
||||
Set-Location ..
|
||||
|
||||
# 安装根目录依赖
|
||||
npm i
|
12
package.json
12
package.json
@ -54,10 +54,16 @@
|
||||
"views": {
|
||||
"openmcp-sidebar": [
|
||||
{
|
||||
"id": "webview-sidebar.view",
|
||||
"id": "openmcp.sidebar.connect",
|
||||
"icon": "./icons/protocol.svg",
|
||||
"name": "chatbot",
|
||||
"type": "webview"
|
||||
"name": "MCP 连接",
|
||||
"type": "tree"
|
||||
},
|
||||
{
|
||||
"id": "openmcp.sidebar.help",
|
||||
"icon": "./icons/protocol.svg",
|
||||
"name": "入门与帮助",
|
||||
"type": "tree"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
import * as vscode from 'vscode';
|
||||
import * as fs from 'fs';
|
||||
import * as fspath from 'path';
|
||||
|
||||
import * as OpenMCPService from '../resources/service';
|
||||
import { getLaunchCWD, getWebviewContent } from './webview';
|
||||
import { panels } from './global';
|
||||
import { getLaunchCWD, revealOpenMcpWebviewPanel } from './webview';
|
||||
import { registerSidebar } from './sidebar';
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
console.log('activate openmcp');
|
||||
@ -15,81 +14,28 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
const workspace = workspaceFolder?.uri.fsPath || '';
|
||||
OpenMCPService.setVscodeWorkspace(workspace);
|
||||
|
||||
registerSidebar(context);
|
||||
|
||||
// 注册 showOpenMCP 命令
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('openmcp.showOpenMCP', async (uri: vscode.Uri) => {
|
||||
|
||||
if (panels.has(uri.fsPath)) {
|
||||
const panel = panels.get(uri.fsPath);
|
||||
panel?.reveal();
|
||||
return;
|
||||
}
|
||||
|
||||
const panel = vscode.window.createWebviewPanel(
|
||||
'OpenMCP',
|
||||
'OpenMCP',
|
||||
vscode.ViewColumn.One,
|
||||
{
|
||||
enableScripts: true,
|
||||
retainContextWhenHidden: true,
|
||||
enableFindWidget: true
|
||||
}
|
||||
);
|
||||
|
||||
panels.set(uri.fsPath, panel);
|
||||
|
||||
const cwd = getLaunchCWD(context, uri);
|
||||
// 获取 uri 相对于 cwd 的路径
|
||||
const relativePath = fspath.relative(cwd, uri.fsPath);
|
||||
|
||||
console.log('current file' + uri.fsPath);
|
||||
console.log(`relativePath: ${relativePath}`);
|
||||
// TODO: 实现从 connection.json 中读取配置,然后启动对应的 connection
|
||||
const command = 'mcp';
|
||||
const args = ['run', relativePath];
|
||||
|
||||
// 根据 relativePath 先去 setting 中进行选择
|
||||
|
||||
// 设置HTML内容
|
||||
const html = getWebviewContent(context, panel);
|
||||
panel.webview.html = html || '';
|
||||
panel.iconPath = vscode.Uri.file(fspath.join(context.extensionPath, 'resources', 'renderer', 'images', 'openmcp.png'));
|
||||
|
||||
// 处理来自webview的消息
|
||||
panel.webview.onDidReceiveMessage(message => {
|
||||
const { command, data } = message;
|
||||
console.log('receive message', message);
|
||||
|
||||
// 拦截消息,注入额外信息
|
||||
switch (command) {
|
||||
case 'vscode/launch-command':
|
||||
const commandString = 'mcp run ' + relativePath;
|
||||
const launchResult = {
|
||||
code: 200,
|
||||
msg: {
|
||||
commandString: commandString,
|
||||
cwd: cwd
|
||||
}
|
||||
}
|
||||
|
||||
panel.webview.postMessage({
|
||||
command: 'vscode/launch-command',
|
||||
data: launchResult
|
||||
revealOpenMcpWebviewPanel(context, uri.fsPath, {
|
||||
type: 'stdio',
|
||||
name: 'OpenMCP',
|
||||
command,
|
||||
args,
|
||||
cwd
|
||||
});
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
OpenMCPService.messageController(command, data, panel.webview);
|
||||
break;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
panel.onDidDispose(async () => {
|
||||
// 删除
|
||||
panels.delete(uri.fsPath);
|
||||
|
||||
// 退出
|
||||
panel.dispose();
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +1,43 @@
|
||||
import * as vscode from 'vscode';
|
||||
import * as os from 'os';
|
||||
import * as fspath from 'path';
|
||||
import * as fs from 'fs';
|
||||
|
||||
export type FsPath = string;
|
||||
export const panels = new Map<FsPath, vscode.WebviewPanel>();
|
||||
|
||||
|
||||
export interface IStdioConnectionItem {
|
||||
type: 'stdio';
|
||||
name: string;
|
||||
command: string;
|
||||
args: string[];
|
||||
cwd?: string;
|
||||
env?: { [key: string]: string };
|
||||
}
|
||||
|
||||
export interface ISSEConnectionItem {
|
||||
type: 'sse';
|
||||
name: string;
|
||||
url: string;
|
||||
oauth?: string;
|
||||
env?: { [key: string]: string };
|
||||
}
|
||||
|
||||
export interface IConnectionConfig {
|
||||
items: (IStdioConnectionItem | ISSEConnectionItem)[];
|
||||
}
|
||||
|
||||
export function getConnectionConfig() {
|
||||
const homeDir = os.homedir();
|
||||
const configDir = fspath.join(homeDir, '.openmcp');
|
||||
const connectionConfig = fspath.join(configDir, 'connection.json');
|
||||
if (!fs.existsSync(connectionConfig)) {
|
||||
fs.mkdirSync(configDir, { recursive: true });
|
||||
fs.writeFileSync(connectionConfig, JSON.stringify({ items: [] }), 'utf-8');
|
||||
}
|
||||
|
||||
const rawConnectionString = fs.readFileSync(connectionConfig, 'utf-8');
|
||||
const connection = JSON.parse(rawConnectionString) as IConnectionConfig;
|
||||
return connection;
|
||||
}
|
105
src/sidebar.ts
Normal file
105
src/sidebar.ts
Normal file
@ -0,0 +1,105 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { getConnectionConfig, ISSEConnectionItem, IStdioConnectionItem } from './global';
|
||||
import { revealOpenMcpWebviewPanel } from './webview';
|
||||
|
||||
export function registerSidebar(context: vscode.ExtensionContext) {
|
||||
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('openmcp.sidebar.revealOpenMcpWebviewPanel', (item: IStdioConnectionItem | ISSEConnectionItem) => {
|
||||
revealOpenMcpWebviewPanel(context, item.name, item);
|
||||
})
|
||||
)
|
||||
|
||||
// 注册 MCP 连接的 sidebar 视图
|
||||
context.subscriptions.push(
|
||||
vscode.window.registerTreeDataProvider('openmcp.sidebar.connect', new McpConnectProvider(context))
|
||||
);
|
||||
|
||||
// 注册 入门与帮助的 sidebar 视图
|
||||
context.subscriptions.push(
|
||||
vscode.window.registerTreeDataProvider('openmcp.sidebar.help', new HelpProvider(context))
|
||||
);
|
||||
}
|
||||
|
||||
class McpConnectProvider implements vscode.TreeDataProvider<SidebarItem> {
|
||||
|
||||
constructor(private context: vscode.ExtensionContext) {
|
||||
}
|
||||
|
||||
// 实现 TreeDataProvider 接口
|
||||
getTreeItem(element: SidebarItem): vscode.TreeItem {
|
||||
return element;
|
||||
}
|
||||
|
||||
getChildren(element?: SidebarItem): Thenable<SidebarItem[]> {
|
||||
// TODO: 读取 configDir 下的所有文件,作为子节点
|
||||
const connection = getConnectionConfig();
|
||||
const sidebarItems = connection.items.map((item, index) => {
|
||||
return new SidebarItem(item.name, vscode.TreeItemCollapsibleState.None, {
|
||||
command: 'openmcp.sidebar.revealOpenMcpWebviewPanel',
|
||||
title: 'OpenMCP',
|
||||
arguments: [item]
|
||||
}, 'server');
|
||||
})
|
||||
|
||||
// 返回子节点
|
||||
return Promise.resolve(sidebarItems);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class HelpProvider implements vscode.TreeDataProvider<SidebarItem> {
|
||||
|
||||
constructor(private context: vscode.ExtensionContext) {
|
||||
}
|
||||
|
||||
// 实现 TreeDataProvider 接口
|
||||
getTreeItem(element: SidebarItem): vscode.TreeItem {
|
||||
return element;
|
||||
}
|
||||
|
||||
getChildren(element?: SidebarItem): Thenable<SidebarItem[]> {
|
||||
// 返回子节点
|
||||
return Promise.resolve([
|
||||
new SidebarItem('入门', vscode.TreeItemCollapsibleState.None, {
|
||||
command: 'vscode.open',
|
||||
title: 'Open Guide',
|
||||
arguments: [vscode.Uri.parse('https://zhuanlan.zhihu.com/p/1894785817186121106')]
|
||||
}, 'book'),
|
||||
new SidebarItem('阅读文档', vscode.TreeItemCollapsibleState.None, {
|
||||
command: 'vscode.open',
|
||||
title: 'Open Documentation',
|
||||
arguments: [vscode.Uri.parse('https://document.kirigaya.cn/blogs/openmcp/main.html')]
|
||||
}, 'file-text'),
|
||||
new SidebarItem('报告问题', vscode.TreeItemCollapsibleState.None, {
|
||||
command: 'vscode.open',
|
||||
title: 'Report Issue',
|
||||
arguments: [vscode.Uri.parse('https://github.com/LSTM-Kirigaya/openmcp-client/issues')]
|
||||
}, 'bug'),
|
||||
new SidebarItem('参与项目', vscode.TreeItemCollapsibleState.None, {
|
||||
command: 'vscode.open',
|
||||
title: 'Join Project',
|
||||
arguments: [vscode.Uri.parse('https://qm.qq.com/cgi-bin/qm/qr?k=C6ZUTZvfqWoI12lWe7L93cWa1hUsuVT0&jump_from=webapi&authKey=McW6B1ogTPjPDrCyGttS890tMZGQ1KB3QLuG4aqVNRaYp4vlTSgf2c6dMcNjMuBD')]
|
||||
}, 'organization'),
|
||||
new SidebarItem('评论插件', vscode.TreeItemCollapsibleState.None, {
|
||||
command: 'vscode.open',
|
||||
title: 'Review Extension',
|
||||
arguments: [vscode.Uri.parse('https://marketplace.visualstudio.com/items?itemName=kirigaya.openmcp&ssr=false#review-details')]
|
||||
}, 'feedback')
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
class SidebarItem extends vscode.TreeItem {
|
||||
constructor(
|
||||
public readonly label: string,
|
||||
public readonly collapsibleState: vscode.TreeItemCollapsibleState,
|
||||
public readonly command?: vscode.Command,
|
||||
public readonly icon?: string
|
||||
) {
|
||||
super(label, collapsibleState);
|
||||
this.command = command;
|
||||
this.iconPath = new vscode.ThemeIcon(icon || 'circle-outline');
|
||||
}
|
||||
}
|
@ -1,6 +1,9 @@
|
||||
import * as vscode from 'vscode';
|
||||
import * as fs from 'fs';
|
||||
import * as fspath from 'path';
|
||||
import { ISSEConnectionItem, IStdioConnectionItem, panels } from './global';
|
||||
import * as OpenMCPService from '../resources/service';
|
||||
|
||||
|
||||
export function getWebviewContent(context: vscode.ExtensionContext, panel: vscode.WebviewPanel): string | undefined {
|
||||
const viewRoot = fspath.join(context.extensionPath, 'resources', 'renderer');
|
||||
@ -21,3 +24,89 @@ export function getLaunchCWD(context: vscode.ExtensionContext, uri: vscode.Uri)
|
||||
const workspaceFolder = vscode.workspace.getWorkspaceFolder(uri);
|
||||
return workspaceFolder?.uri.fsPath || '';
|
||||
}
|
||||
|
||||
|
||||
export function revealOpenMcpWebviewPanel(
|
||||
context: vscode.ExtensionContext,
|
||||
panelKey: string,
|
||||
option: (IStdioConnectionItem | ISSEConnectionItem) = {
|
||||
type: 'stdio',
|
||||
name: 'OpenMCP',
|
||||
command: 'mcp',
|
||||
args: ['run', 'main.py']
|
||||
}
|
||||
) {
|
||||
if (panels.has(panelKey)) {
|
||||
const panel = panels.get(panelKey);
|
||||
panel?.reveal();
|
||||
return panel;
|
||||
}
|
||||
|
||||
const panel = vscode.window.createWebviewPanel(
|
||||
'OpenMCP',
|
||||
'OpenMCP',
|
||||
vscode.ViewColumn.One,
|
||||
{
|
||||
enableScripts: true,
|
||||
retainContextWhenHidden: true,
|
||||
enableFindWidget: true
|
||||
}
|
||||
);
|
||||
|
||||
panels.set(panelKey, panel);
|
||||
|
||||
|
||||
// 设置HTML内容
|
||||
const html = getWebviewContent(context, panel);
|
||||
panel.webview.html = html || '';
|
||||
panel.iconPath = vscode.Uri.file(fspath.join(context.extensionPath, 'resources', 'renderer', 'images', 'openmcp.png'));
|
||||
|
||||
// 处理来自webview的消息
|
||||
panel.webview.onDidReceiveMessage(message => {
|
||||
const { command, data } = message;
|
||||
console.log('receive message', message);
|
||||
|
||||
// 拦截消息,注入额外信息
|
||||
switch (command) {
|
||||
case 'vscode/launch-command':
|
||||
const laucnResultMessage = option.type === 'stdio' ?
|
||||
{
|
||||
type: 'stdio',
|
||||
commandString: option.command + ' ' + option.args.join(' '),
|
||||
cwd: option.cwd
|
||||
} :
|
||||
{
|
||||
type: 'sse',
|
||||
url: option.url,
|
||||
oauth: option.oauth
|
||||
};
|
||||
|
||||
const launchResult = {
|
||||
code: 200,
|
||||
msg: laucnResultMessage
|
||||
};
|
||||
|
||||
panel.webview.postMessage({
|
||||
command: 'vscode/launch-command',
|
||||
data: launchResult
|
||||
});
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
OpenMCPService.messageController(command, data, panel.webview);
|
||||
break;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
panel.onDidDispose(async () => {
|
||||
// 删除
|
||||
panels.delete(panelKey);
|
||||
|
||||
// 退出
|
||||
panel.dispose();
|
||||
});
|
||||
|
||||
return panel;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user