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/
*.vsix
.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
## [main] 0.0.5
- 支持对已经打开过的文件项目进行管理
## [main] 0.0.4
- 修复选择模型后点击确认跳转回 deepseek 的 bug
- 修复 mcp 项目初始化点击工具全部都是空的 bug

View File

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

View File

@ -3,19 +3,19 @@
<h3>{{ currentResource.template?.name }}</h3>
</div>
<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"
: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}`"
@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}`"
@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>
@ -38,6 +38,7 @@ import { tabs } from '../panel';
import { parseResourceTemplate, resourcesManager, ResourceStorage } from './resources';
import { CasualRestAPI, ResourcesReadResponse } from '@/hook/type';
import { useMessageBridge } from '@/api/message-bridge';
import { getDefaultValue, normaliseJavascriptType } from '@/hook/mcp';
defineComponent({ name: 'resource-reader' });
@ -53,9 +54,12 @@ const props = defineProps({
const tab = tabs.content[props.tabId];
const tabStorage = tab.storage as ResourceStorage;
if (!tabStorage.formData) {
tabStorage.formData = {};
}
//
const formRef = ref<FormInstance>();
const formData = ref<Record<string, any>>({});
const loading = ref(false);
const responseData = ref<ResourcesReadResponse>();
@ -94,12 +98,21 @@ const formRules = computed<FormRules>(() => {
});
//
const initFormData = () => {
formData.value = {}
currentResource.value?.params.forEach(param => {
formData.value[param.name] = param.type === 'number' ? 0 :
param.type === 'boolean' ? false : ''
if (!currentResource.value?.params) return;
const newSchemaDataForm: Record<string, number | boolean | string> = {};
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() {
const fillFn = currentResource.value.fill;
const uri = fillFn(formData.value);
const uri = fillFn(tabStorage.formData);
const bridge = useMessageBridge();

View File

@ -64,7 +64,6 @@ function reloadResources(option: { first: boolean }) {
function handleClick(template: ResourceTemplate) {
tabStorage.currentResourceName = template.name;
// TODO:
tabStorage.lastResourceReadResponse = undefined;
}
@ -74,9 +73,9 @@ onMounted(() => {
commandCancel = bridge.addCommandListener('resources/templates/list', (data: CasualRestAPI<ResourceTemplatesListResponse>) => {
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;
// TODO:
tabStorage.lastResourceReadResponse = undefined;
}
}, { once: false });

View File

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

View File

@ -54,6 +54,7 @@ import type { FormInstance, FormRules } from 'element-plus';
import { tabs } from '../panel';
import { callTool, toolsManager, ToolStorage } from './tools';
import { pinkLog } from '@/views/setting/util';
import { getDefaultValue, normaliseJavascriptType } from '@/hook/mcp';
defineComponent({ name: 'tool-executor' });
@ -99,15 +100,6 @@ const formRules = computed<FormRules>(() => {
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 = () => {
// inputSchema
@ -120,11 +112,8 @@ const initFormData = () => {
Object.entries(currentTool.value.inputSchema.properties).forEach(([name, property]) => {
newSchemaDataForm[name] = getDefaultValue(property);
let originType: string = typeof tabStorage.formData[name];
if (originType === 'number') {
originType = 'integer';
}
const originType = normaliseJavascriptType(typeof tabStorage.formData[name]);
if (tabStorage.formData[name] !== undefined && originType === property.type) {
newSchemaDataForm[name] = tabStorage.formData[name];
}
@ -135,7 +124,6 @@ const initFormData = () => {
const resetForm = () => {
formRef.value?.resetFields();
tabStorage.lastToolCallResponse = undefined;
};
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": [
{
"name": "交互测试",
@ -133,12 +133,33 @@
"content": [
{
"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
}
}
},
{
"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!"
}
]
}
}
}
]
}