push save MVP to 95%

This commit is contained in:
锦恢 2025-04-22 15:13:54 +08:00
parent 03d79d0222
commit 3976670295
10 changed files with 92 additions and 40 deletions

4
.gitignore vendored
View File

@ -4,4 +4,6 @@ node_modules
.vscode-test/ .vscode-test/
*.vsix *.vsix
.env .env
resources resources
.DS_Store
.exe

View File

@ -1,5 +0,0 @@
import { defineConfig } from '@vscode/test-cli';
export default defineConfig({
files: 'out/test/**/*.test.js',
});

View File

@ -1,5 +1,8 @@
# Change Log # Change Log
## [main] 0.0.5
- 支持对已经打开过的文件项目进行管理
## [main] 0.0.4 ## [main] 0.0.4
- 修复选择模型后点击确认跳转回 deepseek 的 bug - 修复选择模型后点击确认跳转回 deepseek 的 bug
- 修复 mcp 项目初始化点击工具全部都是空的 bug - 修复 mcp 项目初始化点击工具全部都是空的 bug

View File

@ -2,7 +2,7 @@
"name": "openmcp", "name": "openmcp",
"displayName": "OpenMCP", "displayName": "OpenMCP",
"description": "An all in one MCP Client/TestTool", "description": "An all in one MCP Client/TestTool",
"version": "0.0.4", "version": "0.0.5",
"publisher": "kirigaya", "publisher": "kirigaya",
"author": { "author": {
"name": "kirigaya", "name": "kirigaya",

View File

@ -3,19 +3,19 @@
<h3>{{ currentResource.template?.name }}</h3> <h3>{{ currentResource.template?.name }}</h3>
</div> </div>
<div class="resource-reader-container"> <div class="resource-reader-container">
<el-form :model="formData" :rules="formRules" ref="formRef" label-position="top"> <el-form :model="tabStorage.formData" :rules="formRules" ref="formRef" label-position="top">
<el-form-item v-for="param in currentResource?.params" :key="param.name" <el-form-item v-for="param in currentResource?.params" :key="param.name"
:label="param.name" :prop="param.name"> :label="param.name" :prop="param.name">
<!-- 根据不同类型渲染不同输入组件 --> <!-- 根据不同类型渲染不同输入组件 -->
<el-input v-if="param.type === 'string'" v-model="formData[param.name]" <el-input v-if="param.type === 'string'" v-model="tabStorage.formData[param.name]"
:placeholder="param.placeholder || `请输入${param.name}`" :placeholder="param.placeholder || `请输入${param.name}`"
@keydown.enter.prevent="handleSubmit" /> @keydown.enter.prevent="handleSubmit" />
<el-input-number v-else-if="param.type === 'number'" v-model="formData[param.name]" <el-input-number v-else-if="param.type === 'number'" v-model="tabStorage.formData[param.name]"
:placeholder="param.placeholder || `请输入${param.name}`" :placeholder="param.placeholder || `请输入${param.name}`"
@keydown.enter.prevent="handleSubmit" /> @keydown.enter.prevent="handleSubmit" />
<el-switch v-else-if="param.type === 'boolean'" v-model="formData[param.name]" /> <el-switch v-else-if="param.type === 'boolean'" v-model="tabStorage.formData[param.name]" />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
@ -38,6 +38,7 @@ import { tabs } from '../panel';
import { parseResourceTemplate, resourcesManager, ResourceStorage } from './resources'; import { parseResourceTemplate, resourcesManager, ResourceStorage } from './resources';
import { CasualRestAPI, ResourcesReadResponse } from '@/hook/type'; import { CasualRestAPI, ResourcesReadResponse } from '@/hook/type';
import { useMessageBridge } from '@/api/message-bridge'; import { useMessageBridge } from '@/api/message-bridge';
import { getDefaultValue, normaliseJavascriptType } from '@/hook/mcp';
defineComponent({ name: 'resource-reader' }); defineComponent({ name: 'resource-reader' });
@ -53,9 +54,12 @@ const props = defineProps({
const tab = tabs.content[props.tabId]; const tab = tabs.content[props.tabId];
const tabStorage = tab.storage as ResourceStorage; const tabStorage = tab.storage as ResourceStorage;
if (!tabStorage.formData) {
tabStorage.formData = {};
}
// //
const formRef = ref<FormInstance>(); const formRef = ref<FormInstance>();
const formData = ref<Record<string, any>>({});
const loading = ref(false); const loading = ref(false);
const responseData = ref<ResourcesReadResponse>(); const responseData = ref<ResourcesReadResponse>();
@ -94,12 +98,21 @@ const formRules = computed<FormRules>(() => {
}); });
// //
const initFormData = () => { const initFormData = () => {
formData.value = {} if (!currentResource.value?.params) return;
currentResource.value?.params.forEach(param => {
formData.value[param.name] = param.type === 'number' ? 0 : const newSchemaDataForm: Record<string, number | boolean | string> = {};
param.type === 'boolean' ? false : ''
currentResource.value.params.forEach(param => {
newSchemaDataForm[param.name] = getDefaultValue(param);
const originType = normaliseJavascriptType(typeof tabStorage.formData[param.name]);
if (tabStorage.formData[param.name]!== undefined && originType === param.type) {
newSchemaDataForm[param.name] = tabStorage.formData[param.name];
}
}) })
} }
@ -112,7 +125,7 @@ const resetForm = () => {
// //
function handleSubmit() { function handleSubmit() {
const fillFn = currentResource.value.fill; const fillFn = currentResource.value.fill;
const uri = fillFn(formData.value); const uri = fillFn(tabStorage.formData);
const bridge = useMessageBridge(); const bridge = useMessageBridge();

View File

@ -64,7 +64,6 @@ function reloadResources(option: { first: boolean }) {
function handleClick(template: ResourceTemplate) { function handleClick(template: ResourceTemplate) {
tabStorage.currentResourceName = template.name; tabStorage.currentResourceName = template.name;
// TODO:
tabStorage.lastResourceReadResponse = undefined; tabStorage.lastResourceReadResponse = undefined;
} }
@ -74,9 +73,9 @@ onMounted(() => {
commandCancel = bridge.addCommandListener('resources/templates/list', (data: CasualRestAPI<ResourceTemplatesListResponse>) => { commandCancel = bridge.addCommandListener('resources/templates/list', (data: CasualRestAPI<ResourceTemplatesListResponse>) => {
resourcesManager.templates = data.msg.resourceTemplates || []; resourcesManager.templates = data.msg.resourceTemplates || [];
if (resourcesManager.templates.length > 0) { const targetResource = resourcesManager.templates.find(template => template.name === tabStorage.currentResourceName);
if (targetResource === undefined) {
tabStorage.currentResourceName = resourcesManager.templates[0].name; tabStorage.currentResourceName = resourcesManager.templates[0].name;
// TODO:
tabStorage.lastResourceReadResponse = undefined; tabStorage.lastResourceReadResponse = undefined;
} }
}, { once: false }); }, { once: false });

View File

@ -13,6 +13,7 @@ export const resourcesManager = reactive<{
export interface ResourceStorage { export interface ResourceStorage {
currentResourceName: string; currentResourceName: string;
lastResourceReadResponse?: ResourcesReadResponse; lastResourceReadResponse?: ResourcesReadResponse;
formData: Record<string, number | string | boolean>;
} }
/** /**
@ -22,7 +23,7 @@ export interface ResourceStorage {
*/ */
export function parseResourceTemplate(template: string): { export function parseResourceTemplate(template: string): {
params: string[], params: string[],
fill: (params: Record<string, string>) => string fill: (params: Record<string, number | boolean | string>) => string
} { } {
// 1. 提取所有参数名 // 1. 提取所有参数名
const paramRegex = /\{([^}]+)\}/g; const paramRegex = /\{([^}]+)\}/g;
@ -36,7 +37,7 @@ export function parseResourceTemplate(template: string): {
const paramList = Array.from(params); const paramList = Array.from(params);
// 2. 创建填充函数 // 2. 创建填充函数
const fill = (values: Record<string, string>): string => { const fill = (values: Record<string, number | boolean | string>): string => {
let result = template; let result = template;
// 验证所有必填参数 // 验证所有必填参数
@ -48,7 +49,7 @@ export function parseResourceTemplate(template: string): {
// 替换所有参数 // 替换所有参数
for (const param of paramList) { for (const param of paramList) {
result = result.replace(new RegExp(`\\{${param}\\}`, 'g'), values[param]); result = result.replace(new RegExp(`\\{${param}\\}`, 'g'), values[param].toString());
} }
return result; return result;

View File

@ -54,6 +54,7 @@ import type { FormInstance, FormRules } from 'element-plus';
import { tabs } from '../panel'; import { tabs } from '../panel';
import { callTool, toolsManager, ToolStorage } from './tools'; import { callTool, toolsManager, ToolStorage } from './tools';
import { pinkLog } from '@/views/setting/util'; import { pinkLog } from '@/views/setting/util';
import { getDefaultValue, normaliseJavascriptType } from '@/hook/mcp';
defineComponent({ name: 'tool-executor' }); defineComponent({ name: 'tool-executor' });
@ -99,15 +100,6 @@ const formRules = computed<FormRules>(() => {
return rules; return rules;
}); });
const getDefaultValue = (property: any) => {
if (property.type === 'number' || property.type === 'integer') {
return 0;
} else if (property.type === 'boolean') {
return false;
} else {
return '';
}
};
const initFormData = () => { const initFormData = () => {
// inputSchema // inputSchema
@ -120,11 +112,8 @@ const initFormData = () => {
Object.entries(currentTool.value.inputSchema.properties).forEach(([name, property]) => { Object.entries(currentTool.value.inputSchema.properties).forEach(([name, property]) => {
newSchemaDataForm[name] = getDefaultValue(property); newSchemaDataForm[name] = getDefaultValue(property);
let originType: string = typeof tabStorage.formData[name]; const originType = normaliseJavascriptType(typeof tabStorage.formData[name]);
if (originType === 'number') {
originType = 'integer';
}
if (tabStorage.formData[name] !== undefined && originType === property.type) { if (tabStorage.formData[name] !== undefined && originType === property.type) {
newSchemaDataForm[name] = tabStorage.formData[name]; newSchemaDataForm[name] = tabStorage.formData[name];
} }
@ -135,7 +124,6 @@ const initFormData = () => {
const resetForm = () => { const resetForm = () => {
formRef.value?.resetFields(); formRef.value?.resetFields();
tabStorage.lastToolCallResponse = undefined;
}; };
async function handleExecute() { async function handleExecute() {

30
renderer/src/hook/mcp.ts Normal file
View File

@ -0,0 +1,30 @@
import { SchemaProperty } from "./type";
interface TypeAble {
type: string;
}
export function getDefaultValue(property: TypeAble) {
if (property.type === 'number' || property.type === 'integer') {
return 0;
} else if (property.type === 'boolean') {
return false;
} else {
return '';
}
}
export function normaliseJavascriptType(type: string) {
switch (type) {
case 'integer':
return 'number';
case 'number':
return 'integer';
case 'boolean':
return 'boolean';
case 'string':
return 'string';
default:
return 'string';
}
}

View File

@ -1,5 +1,5 @@
{ {
"currentIndex": 1, "currentIndex": 2,
"tabs": [ "tabs": [
{ {
"name": "交互测试", "name": "交互测试",
@ -133,12 +133,33 @@
"content": [ "content": [
{ {
"type": "text", "type": "text",
"text": "CityWeather(city_name_en='hangzhou', city_name_cn='杭州', city_code='101210101', temp='20.7', wd='', ws='', sd='93%', aqi='13', weather='阴')" "text": "CityWeather(city_name_en='hangzhou', city_name_cn='杭州', city_code='101210101', temp='20.3', wd='', ws='', sd='89%', aqi='38', weather='阴')"
} }
], ],
"isError": false "isError": false
} }
} }
},
{
"name": "资源",
"icon": "icon-file",
"type": "blank",
"componentIndex": 0,
"storage": {
"currentResourceName": "greeting",
"formData": {
"name": "kirigaya"
},
"lastResourceReadResponse": {
"contents": [
{
"uri": "greeting://kirigaya",
"mimeType": "text/plain",
"text": "Hello, kirigaya!"
}
]
}
}
} }
] ]
} }