修复全局安装的 mcp 服务器 name 更新的问题
This commit is contained in:
parent
dca2a9c820
commit
56145bfdf9
@ -4,7 +4,7 @@
|
||||
## [main] 0.0.6
|
||||
- 修复部分因为服务器名称特殊字符而导致的保存实效的错误
|
||||
- 插件模式下,左侧管理面板中的「MCP连接(工作区)」视图可以进行增删改查了
|
||||
- 新增「MCP连接(全局)」,用于安装全局范围的 mcp server
|
||||
- 新增「安装的 MCP 服务器」,用于安装全局范围的 mcp server
|
||||
- 增加引导页面
|
||||
|
||||
## [main] 0.0.5
|
||||
|
@ -6,11 +6,10 @@
|
||||
|
||||
<a href="https://qm.qq.com/cgi-bin/qm/qr?k=C6ZUTZvfqWoI12lWe7L93cWa1hUsuVT0&jump_from=webapi&authKey=McW6B1ogTPjPDrCyGttS890tMZGQ1KB3QLuG4aqVNRaYp4vlTSgf2c6dMcNjMuBD" target="_blank" style="display: inline-block; padding: 8px 16px; background-color: #CB81DA; color: white; border-radius: .5em; text-decoration: none;">👉 加入 OpenMCP正式级技术组</a>
|
||||
|
||||
<a href="https://qm.qq.com/q/qyVJ189OUg" target="_blank" style="display: inline-block; padding: 8px 16px; background-color: rgb(84, 176, 84); color: white; border-radius: .5em; text-decoration: none;">加入 OpenMCP咖啡厅</a>
|
||||
|
||||
<a href="https://qm.qq.com/q/AO0sJS3r7U" target="_blank" style="display: inline-block; padding: 8px 16px; background-color: rgb(84, 176, 84); color: white; border-radius: .5em; text-decoration: none;">加入 OpenMCP正式级宣传组</a>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
## OpenMCP
|
||||
|
||||
一款用于 MCP 服务端调试的一体化 vscode/trae 插件。
|
||||
|
21
package.json
21
package.json
@ -2,7 +2,7 @@
|
||||
"name": "openmcp",
|
||||
"displayName": "OpenMCP",
|
||||
"description": "An all in one MCP Client/TestTool",
|
||||
"version": "0.0.5",
|
||||
"version": "0.0.6",
|
||||
"publisher": "kirigaya",
|
||||
"author": {
|
||||
"name": "kirigaya",
|
||||
@ -65,6 +65,15 @@
|
||||
"category": "openmcp",
|
||||
"icon": "$(gear)"
|
||||
},
|
||||
{
|
||||
"command": "openmcp.sidebar.installed-connection.revealWebviewPanel",
|
||||
"title": "连接",
|
||||
"category": "openmcp",
|
||||
"icon": {
|
||||
"light": "./icons/light/protocol.svg",
|
||||
"dark": "./icons/dark/protocol.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "openmcp.sidebar.installed-connection.deleteConnection",
|
||||
"title": "删除连接",
|
||||
@ -134,7 +143,7 @@
|
||||
{
|
||||
"command": "openmcp.sidebar.workspace-connection.revealWebviewPanel",
|
||||
"group": "inline@1",
|
||||
"when": "view == openmcp.sidebar.workspace-connection || view == openmcp.sidebar.installed-connection",
|
||||
"when": "view == openmcp.sidebar.workspace-connection",
|
||||
"args": {
|
||||
"view": "${viewItem}"
|
||||
}
|
||||
@ -147,6 +156,14 @@
|
||||
"view": "${viewItem}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "openmcp.sidebar.installed-connection.revealWebviewPanel",
|
||||
"group": "inline@1",
|
||||
"when": "view == openmcp.sidebar.installed-connection",
|
||||
"args": {
|
||||
"view": "${viewItem}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "openmcp.sidebar.installed-connection.deleteConnection",
|
||||
"group": "inline@2",
|
||||
|
@ -205,7 +205,7 @@ a {
|
||||
}
|
||||
|
||||
.openmcp-image {
|
||||
background-image: url('./images/openmcp.png');
|
||||
background-image: url('https://picx.zhimg.com/80/v2-a0aa51e8a61f86586e374520995b5df5_1440w.png?source=d16d100b');
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
|
@ -232,7 +232,8 @@
|
||||
<TourTitle>终章?</TourTitle>
|
||||
</template>
|
||||
<div class="tour-common-text">
|
||||
<pre><code>(base) <span style="color: greenyellow">➜</span> <span style="color: #6AC2CF">.openmcp</span> <span style="color: #6BC34B">cat</span> <span style="color: #D357DB">KEY</span>
|
||||
<pre><code style="color: unset !important; background-color: unset !important;"
|
||||
>(base) <span style="color: greenyellow">➜</span> <span style="color: #6AC2CF">.openmcp</span> <span style="color: #6BC34B">cat</span> <span style="color: #D357DB">KEY</span>
|
||||
直面恐惧,创造未来
|
||||
Face your fears, create the future
|
||||
恐怖に直面し、未来を創り出</code></pre>
|
||||
@ -282,4 +283,9 @@ function finishTour() {
|
||||
padding: 5px;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.tour-common-text code {
|
||||
color: unset!important;
|
||||
background-color: unset!important;
|
||||
}
|
||||
</style>
|
@ -56,7 +56,7 @@
|
||||
|
||||
<KCuteTextarea
|
||||
v-model="userInput"
|
||||
placeholder="输入消息..."
|
||||
:placeholder="t('enter-message-dot')"
|
||||
:customClass="'chat-input'"
|
||||
@press-enter="handleSend()"
|
||||
/>
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
<KCuteTextarea v-else
|
||||
v-model="userInput"
|
||||
placeholder="输入消息..."
|
||||
:placeholder="t('enter-message-dot')"
|
||||
@press-enter="handleKeydown"
|
||||
/>
|
||||
<div class="message-actions" v-if="!isEditing">
|
||||
@ -39,6 +39,9 @@ import { ChatStorage, IRenderMessage } from '../chat';
|
||||
import KCuteTextarea from '@/components/k-cute-textarea/index.vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
import { useI18n } from 'vue-i18n';
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps({
|
||||
message: {
|
||||
type: Object as PropType<IRenderMessage>,
|
||||
|
@ -146,5 +146,6 @@
|
||||
"press-and-run": "اكتب سؤالاً لبدء الاختبار",
|
||||
"connect-sigature": "توقيع الاتصال",
|
||||
"finish-refresh": "تم التحديث",
|
||||
"add-system-prompt.name-placeholder": "عنوان prompt المخصص"
|
||||
"add-system-prompt.name-placeholder": "عنوان prompt المخصص",
|
||||
"enter-message-dot": "أدخل الرسالة..."
|
||||
}
|
@ -146,5 +146,6 @@
|
||||
"press-and-run": "Geben Sie eine Frage ein, um den Test zu starten",
|
||||
"connect-sigature": "Verbindungssignatur",
|
||||
"finish-refresh": "Aktualisierung abgeschlossen",
|
||||
"add-system-prompt.name-placeholder": "Titel für benutzerdefinierte Eingabeaufforderung"
|
||||
"add-system-prompt.name-placeholder": "Titel für benutzerdefinierte Eingabeaufforderung",
|
||||
"enter-message-dot": "Nachricht eingeben..."
|
||||
}
|
@ -146,5 +146,6 @@
|
||||
"press-and-run": "Type a question to start the test",
|
||||
"connect-sigature": "Connection signature",
|
||||
"finish-refresh": "Refresh completed",
|
||||
"add-system-prompt.name-placeholder": "Title for custom prompt"
|
||||
"add-system-prompt.name-placeholder": "Title for custom prompt",
|
||||
"enter-message-dot": "Enter message..."
|
||||
}
|
@ -146,5 +146,6 @@
|
||||
"press-and-run": "Tapez une question pour commencer le test",
|
||||
"connect-sigature": "Signature de connexion",
|
||||
"finish-refresh": "Actualisation terminée",
|
||||
"add-system-prompt.name-placeholder": "Titre de l'invite personnalisée"
|
||||
"add-system-prompt.name-placeholder": "Titre de l'invite personnalisée",
|
||||
"enter-message-dot": "Entrez un message..."
|
||||
}
|
@ -146,5 +146,6 @@
|
||||
"press-and-run": "テストを開始するには質問を入力してください",
|
||||
"connect-sigature": "接続署名",
|
||||
"finish-refresh": "更新が完了しました",
|
||||
"add-system-prompt.name-placeholder": "カスタムプロンプトのタイトル"
|
||||
"add-system-prompt.name-placeholder": "カスタムプロンプトのタイトル",
|
||||
"enter-message-dot": "メッセージを入力..."
|
||||
}
|
@ -146,5 +146,6 @@
|
||||
"press-and-run": "테스트를 시작하려면 질문을 입력하세요",
|
||||
"connect-sigature": "연결 서명",
|
||||
"finish-refresh": "새로 고침 완료",
|
||||
"add-system-prompt.name-placeholder": "사용자 지정 프롬프트 제목"
|
||||
"add-system-prompt.name-placeholder": "사용자 지정 프롬프트 제목",
|
||||
"enter-message-dot": "메시지를 입력하세요..."
|
||||
}
|
@ -146,5 +146,6 @@
|
||||
"press-and-run": "Введите вопрос, чтобы начать тест",
|
||||
"connect-sigature": "Подпись соединения",
|
||||
"finish-refresh": "Обновление завершено",
|
||||
"add-system-prompt.name-placeholder": "Заголовок пользовательского prompt"
|
||||
"add-system-prompt.name-placeholder": "Заголовок пользовательского prompt",
|
||||
"enter-message-dot": "Введите сообщение..."
|
||||
}
|
@ -146,5 +146,6 @@
|
||||
"press-and-run": "键入问题以开始测试",
|
||||
"connect-sigature": "连接签名",
|
||||
"finish-refresh": "完成刷新",
|
||||
"add-system-prompt.name-placeholder": "输入自定义 prompt 的标题"
|
||||
"add-system-prompt.name-placeholder": "输入自定义 prompt 的标题",
|
||||
"enter-message-dot": "输入消息..."
|
||||
}
|
@ -146,5 +146,6 @@
|
||||
"press-and-run": "輸入問題以開始測試",
|
||||
"connect-sigature": "連接簽名",
|
||||
"finish-refresh": "刷新完成",
|
||||
"add-system-prompt.name-placeholder": "自定義提示的標題"
|
||||
"add-system-prompt.name-placeholder": "自定義提示的標題",
|
||||
"enter-message-dot": "輸入訊息..."
|
||||
}
|
@ -5,11 +5,7 @@ export const registerTreeDataProviders = new Map<string, IRegisterTreeDataProvid
|
||||
|
||||
export function RegisterCommand(command: string, options?: any) {
|
||||
return function (target: any, propertyKey: string, descriptor: CommandHandlerDescriptor) {
|
||||
const handler = descriptor.value;
|
||||
|
||||
console.log(propertyKey);
|
||||
console.log(descriptor);
|
||||
|
||||
const handler = descriptor.value;
|
||||
|
||||
if (handler) {
|
||||
registerCommands.push([command, { handler, target, propertyKey, options }]);
|
||||
|
101
src/global.ts
101
src/global.ts
@ -250,6 +250,59 @@ export function updateWorkspaceConnectionConfig(
|
||||
}
|
||||
}
|
||||
|
||||
export function updateInstalledConnectionConfig(
|
||||
absPath: string,
|
||||
data: (ClientStdioConnectionItem | ClientSseConnectionItem) & { serverInfo: ServerInfo }
|
||||
) {
|
||||
const connectionItem = getInstalledConnectionConfigItemByPath(absPath);
|
||||
const installedConnectionConfig = getConnectionConfig();
|
||||
|
||||
// 如果存在,删除老的 connectionItem
|
||||
if (connectionItem) {
|
||||
const index = installedConnectionConfig.items.indexOf(connectionItem);
|
||||
if (index !== -1) {
|
||||
installedConnectionConfig.items.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (data.connectionType === 'STDIO') {
|
||||
const connectionItem: IStdioConnectionItem = {
|
||||
type: 'stdio',
|
||||
name: data.serverInfo.name,
|
||||
version: data.serverInfo.version,
|
||||
command: data.command,
|
||||
args: data.args,
|
||||
cwd: data.cwd.replace(/\\/g, '/'),
|
||||
env: data.env,
|
||||
filePath: absPath.replace(/\\/g, '/')
|
||||
};
|
||||
|
||||
console.log('get connectionItem: ', connectionItem);
|
||||
|
||||
|
||||
// 插入到第一个
|
||||
installedConnectionConfig.items.unshift(connectionItem);
|
||||
saveConnectionConfig();
|
||||
vscode.commands.executeCommand('openmcp.sidebar.installed-connection.refresh');
|
||||
|
||||
} else {
|
||||
const connectionItem: ISSEConnectionItem = {
|
||||
type: 'sse',
|
||||
name: data.serverInfo.name,
|
||||
version: data.serverInfo.version,
|
||||
url: data.url,
|
||||
oauth: data.oauth,
|
||||
filePath: absPath.replace(/\\/g, '/')
|
||||
};
|
||||
|
||||
// 插入到第一个
|
||||
installedConnectionConfig.items.unshift(connectionItem);
|
||||
saveConnectionConfig();
|
||||
vscode.commands.executeCommand('openmcp.sidebar.installed-connection.refresh');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function normaliseConnectionFilePath(item: IConnectionItem, workspace: string) {
|
||||
if (item.filePath) {
|
||||
if (item.filePath.startsWith('{workspace}')) {
|
||||
@ -284,4 +337,50 @@ export function getWorkspaceConnectionConfigItemByPath(absPath: string) {
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 根据输入的文件路径,获取该文件的 mcp 连接签名
|
||||
* @param absPath
|
||||
*/
|
||||
export function getInstalledConnectionConfigItemByPath(absPath: string) {
|
||||
const installedConnectionConfig = getConnectionConfig();
|
||||
|
||||
const normaliseAbsPath = absPath.replace(/\\/g, '/');
|
||||
for (const item of installedConnectionConfig.items) {
|
||||
const filePath = (item.filePath || '').replace(/\\/g, '/');
|
||||
if (filePath === normaliseAbsPath) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
export async function getFirstValidPathFromCommand(command: string, cwd: string): Promise<string | undefined> {
|
||||
// 分割命令字符串
|
||||
const parts = command.split(' ');
|
||||
|
||||
// 遍历命令部分,寻找第一个可能是路径的部分
|
||||
for (let i = 1; i < parts.length; i++) {
|
||||
const part = parts[i];
|
||||
|
||||
// 跳过以 '-' 开头的参数
|
||||
if (part.startsWith('-')) continue;
|
||||
|
||||
// 处理相对路径
|
||||
let fullPath = part;
|
||||
if (!fspath.isAbsolute(part)) {
|
||||
fullPath = fspath.join(cwd, part);
|
||||
}
|
||||
|
||||
console.log(fullPath);
|
||||
|
||||
if (fs.existsSync(fullPath)) {
|
||||
return fullPath;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import { RegisterCommand, RegisterTreeDataProvider } from '../common';
|
||||
import { ConnectionViewItem } from './common';
|
||||
import { getConnectionConfig, getInstalledConnectionConfigPath, saveConnectionConfig } from '../global';
|
||||
import { acquireInstalledConnection, deleteInstalledConnection } from './installed.service';
|
||||
import { revealOpenMcpWebviewPanel } from '../webview/webview.service';
|
||||
|
||||
@RegisterTreeDataProvider('openmcp.sidebar.installed-connection')
|
||||
export class McpInstalledConnectProvider implements vscode.TreeDataProvider<ConnectionViewItem> {
|
||||
@ -29,6 +30,11 @@ export class McpInstalledConnectProvider implements vscode.TreeDataProvider<Conn
|
||||
return Promise.resolve(sidebarItems);
|
||||
}
|
||||
|
||||
@RegisterCommand('revealWebviewPanel')
|
||||
public revealWebviewPanel(context: vscode.ExtensionContext, view: ConnectionViewItem) {
|
||||
const item = view.item;
|
||||
revealOpenMcpWebviewPanel(context, 'installed', item.filePath || item.name, item);
|
||||
}
|
||||
|
||||
@RegisterCommand('refresh')
|
||||
public refresh(context: vscode.ExtensionContext): void {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { getConnectionConfig, IConnectionItem, panels, saveConnectionConfig } from "../global";
|
||||
import { getConnectionConfig, IConnectionItem, panels, saveConnectionConfig, getFirstValidPathFromCommand } from "../global";
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
@ -51,7 +51,8 @@ export async function validateAndGetCommandPath(command: string, cwd?: string):
|
||||
export async function acquireInstalledConnection(): Promise<IConnectionItem | undefined> {
|
||||
// 让用户选择连接类型
|
||||
const connectionType = await vscode.window.showQuickPick(['stdio', 'sse'], {
|
||||
placeHolder: '请选择连接类型'
|
||||
placeHolder: '请选择连接类型',
|
||||
canPickMany: false
|
||||
});
|
||||
|
||||
if (!connectionType) {
|
||||
@ -88,13 +89,16 @@ export async function acquireInstalledConnection(): Promise<IConnectionItem | un
|
||||
const command = commands[0];
|
||||
const args = commands.slice(1);
|
||||
|
||||
const filePath = await getFirstValidPathFromCommand(commandString, cwd || '');
|
||||
|
||||
// 保存连接配置
|
||||
return {
|
||||
type: 'stdio',
|
||||
name: `stdio-${Date.now()}`,
|
||||
command: command,
|
||||
args,
|
||||
cwd: cwd || ''
|
||||
cwd: cwd || '',
|
||||
filePath: filePath,
|
||||
};
|
||||
|
||||
} else if (connectionType === 'sse') {
|
||||
|
@ -33,7 +33,7 @@ export class McpWorkspaceConnectProvider implements vscode.TreeDataProvider<Conn
|
||||
@RegisterCommand('revealWebviewPanel')
|
||||
public revealWebviewPanel(context: vscode.ExtensionContext, view: ConnectionViewItem) {
|
||||
const item = view.item;
|
||||
revealOpenMcpWebviewPanel(context, item.filePath || item.name, item);
|
||||
revealOpenMcpWebviewPanel(context, 'workspace', item.filePath || item.name, item);
|
||||
}
|
||||
|
||||
@RegisterCommand('refresh')
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { getWorkspaceConnectionConfig, getWorkspacePath, IConnectionItem, panels, saveWorkspaceConnectionConfig } from "../global";
|
||||
import { getFirstValidPathFromCommand, getWorkspaceConnectionConfig, getWorkspacePath, IConnectionItem, panels, saveWorkspaceConnectionConfig } from "../global";
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
@ -43,6 +43,7 @@ export async function acquireUserCustomConnection(): Promise<IConnectionItem | u
|
||||
const commands = commandString.split(' ');
|
||||
const command = commands[0];
|
||||
const args = commands.slice(1);
|
||||
const filePath = await getFirstValidPathFromCommand(commandString, cwd || '');
|
||||
|
||||
// 保存连接配置
|
||||
return {
|
||||
@ -50,7 +51,8 @@ export async function acquireUserCustomConnection(): Promise<IConnectionItem | u
|
||||
name: `stdio-${Date.now()}`,
|
||||
command: command,
|
||||
args,
|
||||
cwd: cwd || ''
|
||||
cwd: cwd || '',
|
||||
filePath
|
||||
};
|
||||
|
||||
} else if (connectionType === 'sse') {
|
||||
|
@ -18,7 +18,7 @@ export class WebviewController {
|
||||
return;
|
||||
}
|
||||
|
||||
revealOpenMcpWebviewPanel(context, uri.fsPath, {
|
||||
revealOpenMcpWebviewPanel(context, 'workspace', uri.fsPath, {
|
||||
type: 'stdio',
|
||||
name: 'OpenMCP',
|
||||
command: sigature.command,
|
||||
@ -26,7 +26,7 @@ export class WebviewController {
|
||||
cwd
|
||||
});
|
||||
} else {
|
||||
revealOpenMcpWebviewPanel(context, uri.fsPath, connectionItem);
|
||||
revealOpenMcpWebviewPanel(context, 'workspace', uri.fsPath, connectionItem);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,19 +1,19 @@
|
||||
import * as vscode from 'vscode';
|
||||
import * as fs from 'fs';
|
||||
import * as fspath from 'path';
|
||||
import { IConnectionItem, ILaunchSigature, panels, updateWorkspaceConnectionConfig } from '../global';
|
||||
import { IConnectionItem, ILaunchSigature, panels, updateInstalledConnectionConfig, updateWorkspaceConnectionConfig } 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');
|
||||
const htmlIndexPath = fspath.join(viewRoot, 'index.html');
|
||||
const html = fs.readFileSync(htmlIndexPath, { encoding: 'utf-8' })?.replace(/(<link.+?href="|<script.+?src="|<img.+?src=")(.+?)"/g, (m, $1, $2) => {
|
||||
const html = fs.readFileSync(htmlIndexPath, { encoding: 'utf-8' })?.replace(/(<link.+?href="|<script.+?src="|<img.+?src="|url\()(.+?)(\)|")/g, (m, $1, $2) => {
|
||||
const absLocalPath = fspath.resolve(viewRoot, $2);
|
||||
const webviewUri = panel.webview.asWebviewUri(vscode.Uri.file(absLocalPath));
|
||||
|
||||
const replaceHref = $1 + webviewUri?.toString() + '"';
|
||||
return replaceHref;
|
||||
});
|
||||
});
|
||||
return html;
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@ export function getLaunchCWD(context: vscode.ExtensionContext, uri: vscode.Uri)
|
||||
|
||||
export function revealOpenMcpWebviewPanel(
|
||||
context: vscode.ExtensionContext,
|
||||
type: 'workspace' | 'installed',
|
||||
panelKey: string,
|
||||
option: IConnectionItem = {
|
||||
type: 'stdio',
|
||||
@ -92,7 +93,11 @@ export function revealOpenMcpWebviewPanel(
|
||||
break;
|
||||
|
||||
case 'vscode/update-connection-sigature':
|
||||
updateWorkspaceConnectionConfig(panelKey, data);
|
||||
if (type === 'installed') {
|
||||
updateInstalledConnectionConfig(panelKey, data);
|
||||
} else {
|
||||
updateWorkspaceConnectionConfig(panelKey, data);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
|
Loading…
x
Reference in New Issue
Block a user