From 1256d64498f604c0bdb54e42d99446286f2c709f Mon Sep 17 00:00:00 2001 From: dimitrievgs Date: Sun, 14 Jun 2026 01:33:49 +0300 Subject: [PATCH] Add customization of prompts for MCP tools --- main.ts | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/main.ts b/main.ts index e642327..f908a2a 100644 --- a/main.ts +++ b/main.ts @@ -14,12 +14,20 @@ import 'src/css/styles.css'; import { LLM_DEFAULTS } from 'src/constants'; +export interface MCPToolState { + name: string; + originalDescription: string; + customDescription: string; +} + export interface MCPServerConfig { id: string; name: string; command: string; args: string; envString: string; + serverInstruction?: string; + fetchedTools?: MCPToolState[]; } // ----------------------------------------------- Settings --------------------------------------------------------------- @@ -707,6 +715,20 @@ class SampleSettingTab extends PluginSettingTab { await this.plugin.saveSettings(); }); }); + + new Setting(serverDiv) + .setName('Общая инструкция для сервера (Над-инструкция)') + .setDesc('Эти инструкции будут добавлены в системный промпт при использовании графа.') + .addTextArea(t => { + t.inputEl.style.width = '100%'; + t.inputEl.style.height = '60px'; + t.setValue(server.serverInstruction || '').onChange(async v => { + server.serverInstruction = v; + await this.plugin.saveSettings(); + }); + }); + + this.createFetchedTools(server, serverDiv); }); new Setting(container).addButton(b => b.setButtonText('➕ Добавить MCP сервер').setCta().onClick(async () => { @@ -716,6 +738,71 @@ class SampleSettingTab extends PluginSettingTab { })); } + createFetchedTools(server: MCPServerConfig, serverDiv: HTMLDivElement) { + const toolsContainer = serverDiv.createDiv({ cls: 'mcp-tools-container', attr: { style: 'margin-top: 15px; border-top: 1px solid var(--background-modifier-border); padding-top: 10px;' } }); + + const fetchBtn = toolsContainer.createEl('button', { text: '🔄 Подгрузить/Обновить инструменты' }); + fetchBtn.style.marginBottom = '10px'; + + fetchBtn.onclick = async () => { + fetchBtn.textContent = '⏳ Подгрузка...'; + fetchBtn.disabled = true; + try { + const response = await fetch(`${this.plugin.settings.apiBaseUrl}/mcp/fetch`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(server) + }); + + if (!response.ok) throw new Error("Ошибка HTTP: " + response.status); + + const remoteTools: {name: string, description: string}[] = await response.json(); + + // Логика синхронизации: оставляем кастомные описания, удаляем пропавшие, добавляем новые + if (!server.fetchedTools) server.fetchedTools = []; + const existingMap = new Map(server.fetchedTools.map(t => [t.name, t])); + + server.fetchedTools = remoteTools.map(rt => { + if (existingMap.has(rt.name)) { + const existing = existingMap.get(rt.name)!; + return { ...existing, originalDescription: rt.description }; // обновляем оригинальное описание на всякий случай + } + return { name: rt.name, originalDescription: rt.description, customDescription: '' }; + }); + + await this.plugin.saveSettings(); + new Notice(`Загружено инструментов: ${remoteTools.length}`); + this.display(); // Перерисовываем UI + } catch (e) { + console.error(e); + new Notice('Ошибка подгрузки инструментов: ' + e); + } finally { + fetchBtn.textContent = '🔄 Подгрузить/Обновить инструменты'; + fetchBtn.disabled = false; + } + }; + + // Отрисовка списка тулов + if (server.fetchedTools && server.fetchedTools.length > 0) { + server.fetchedTools.forEach((tool, tIndex) => { + const toolDiv = toolsContainer.createDiv({ attr: { style: 'margin-bottom: 10px; padding: 10px; background: var(--background-primary); border-radius: 6px;' } }); + toolDiv.createEl('div', { text: `🛠 ${tool.name}`, attr: { style: 'font-weight: bold; margin-bottom: 5px;' } }); + toolDiv.createEl('div', { text: tool.originalDescription, attr: { style: 'font-size: 0.85em; color: var(--text-muted); margin-bottom: 8px;' } }); + + new Setting(toolDiv) + .setName('Кастомное описание (Override)') + .setDesc('Оставьте пустым, чтобы использовать оригинальное.') + .addTextArea(t => { + t.inputEl.style.width = '100%'; + t.setValue(tool.customDescription).onChange(async v => { + tool.customDescription = v; + await this.plugin.saveSettings(); + }); + }); + }); + } + } + renderVoiceSettings(container: HTMLElement) { container.createEl('h3', { text: 'Транскрибация Vosk' });