Merge pull request #39 from LSTM-Kirigaya/dev

Dev
This commit is contained in:
Kirigaya Kazuto 2025-06-20 21:20:37 +08:00 committed by GitHub
commit 22a79cfc55
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 2287 additions and 3244 deletions

View File

@ -27,3 +27,4 @@ software/**
.turbo .turbo
.github .github
webpack webpack
.openmcp

View File

@ -159,3 +159,13 @@ npm run build:plugin
``` ```
Then just press F5, いただきます (Let's begin) Then just press F5, いただきます (Let's begin)
---
## CI Pipeline
✅ npm run build
✅ npm run build:task-loop
✅ openmcp-client UT
✅ openmcp-sdk UT
✅ vscode extension UT

4396
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -3,11 +3,11 @@ import * as fs from 'node:fs';
const targetFile = './openmcp-sdk/task-loop.js'; const targetFile = './openmcp-sdk/task-loop.js';
if (fs.existsSync(targetFile)) { if (fs.existsSync(targetFile)) {
let content = fs.readFileSync(targetFile, 'utf8'); let content = fs.readFileSync(targetFile, 'utf-8');
// Replace element-plus with ./tools.js // Replace element-plus with ./tools.js
content = content.replace(/'element-plus'/g, "'./tools.js'"); content = content.replace(/'element-plus'/g, "'./tools.mjs'");
content = content.replace(/"element-plus"/g, "\"./tools.js\""); content = content.replace(/"element-plus"/g, "\"./tools.mjs\"");
// content = content.replace(/const chalk = require\("chalk"\);/g, 'const chalk = require("chalk").default;'); // content = content.replace(/const chalk = require\("chalk"\);/g, 'const chalk = require("chalk").default;');

View File

@ -39,22 +39,22 @@ export class MessageBridge {
switch (platform) { switch (platform) {
case 'vscode': case 'vscode':
this.setupVsCodeListener(); this.setupVsCodeListener();
pinkLog('当前模式: vscode'); pinkLog('current platform: vscode');
break; break;
case 'electron': case 'electron':
this.setupElectronListener(); this.setupElectronListener();
pinkLog('当前模式: electron'); pinkLog('current platform: electron');
break; break;
case 'nodejs': case 'nodejs':
this.setupNodejsListener(); this.setupNodejsListener();
pinkLog('当前模式: nodejs'); pinkLog('current platform: nodejs');
break; break;
case 'web': case 'web':
this.setupWebSocket(); this.setupWebSocket();
pinkLog('当前模式: web'); pinkLog('current platform: web');
break; break;
} }
} }

View File

@ -1,5 +1,5 @@
import type { InputSchema, ToolCallContent, ToolItem } from "@/hook/type"; import type { InputSchema, ToolCallContent, ToolItem } from "@/hook/type";
import { type Ref, ref } from "vue"; import type { Ref } from "vue";
import type { OpenAI } from 'openai'; import type { OpenAI } from 'openai';
type ChatCompletionChunk = OpenAI.Chat.Completions.ChatCompletionChunk; type ChatCompletionChunk = OpenAI.Chat.Completions.ChatCompletionChunk;
@ -95,7 +95,7 @@ export type RichTextItem = PromptTextItem | ResourceTextItem | TextItem;
export interface ICommonRenderMessage { export interface ICommonRenderMessage {
role: 'user' | 'assistant/content'; role: 'user' | 'assistant/content';
content: string; content: string;
showJson?: Ref<boolean>; showJson?: any;
extraInfo: IExtraInfo; extraInfo: IExtraInfo;
} }
@ -104,7 +104,7 @@ export interface IToolRenderMessage {
content: string; content: string;
toolResults: ToolCallContent[][]; toolResults: ToolCallContent[][];
tool_calls: ToolCall[]; tool_calls: ToolCall[];
showJson?: Ref<boolean>; showJson?: any;
extraInfo: IExtraInfo; extraInfo: IExtraInfo;
} }

View File

@ -101,8 +101,6 @@ function handleSend(newMessage?: string) {
loop.bindStreaming(streamingContent, streamingToolCalls); loop.bindStreaming(streamingContent, streamingToolCalls);
loop.registerOnError((error) => { loop.registerOnError((error) => {
console.log('error.msg');
console.log(error.msg);
const errorMessage = clearErrorMessage(error.msg); const errorMessage = clearErrorMessage(error.msg);
ElMessage.error(errorMessage); ElMessage.error(errorMessage);
@ -114,7 +112,8 @@ function handleSend(newMessage?: string) {
extraInfo: { extraInfo: {
created: Date.now(), created: Date.now(),
state: error.state, state: error.state,
serverName: llms[llmManager.currentModelIndex].id || 'unknown' serverName: llms[llmManager.currentModelIndex].id || 'unknown',
enableXmlWrapper: false
} }
}); });
} }

View File

@ -34,6 +34,8 @@ export interface IErrorMssage {
msg: string msg: string
} }
export { MessageState };
export interface IDoConversationResult { export interface IDoConversationResult {
stop: boolean; stop: boolean;
} }
@ -63,8 +65,8 @@ export class TaskLoop {
}; };
constructor( constructor(
private readonly taskOptions: TaskLoopOptions = { private taskOptions: TaskLoopOptions = {
maxEpochs: 20, maxEpochs: 50,
maxJsonParseRetry: 3, maxJsonParseRetry: 3,
adapter: undefined, adapter: undefined,
verbose: 0 verbose: 0
@ -83,12 +85,37 @@ export class TaskLoop {
throw new Error('adapter is required'); throw new Error('adapter is required');
} }
// 根据 adapter 创建 nodejs 下特殊的、基于 event 的 message bridge (不占用任何端口)
createMessageBridge(adapter.emitter); createMessageBridge(adapter.emitter);
// 用于进行连接同步
this.nodejsStatus.connectionFut = mcpClientAdapter.launch(); this.nodejsStatus.connectionFut = mcpClientAdapter.launch();
} }
// web 环境下 bridge 会自动加载完成 // web 环境下 bridge 会自动加载完成
this.bridge = useMessageBridge(); this.bridge = useMessageBridge();
// 注册 HMR
mcpClientAdapter.addConnectRefreshListener();
}
public async waitConnection() {
await this.nodejsStatus.connectionFut;
}
public setTaskLoopOptions(taskOptions: TaskLoopOptions) {
const {
maxEpochs = 50,
maxJsonParseRetry = 3,
verbose = 1,
} = taskOptions;
this.taskOptions = {
maxEpochs,
maxJsonParseRetry,
verbose,
...this.taskOptions
};
} }
/** /**
@ -381,7 +408,7 @@ export class TaskLoop {
if (verbose > 0) { if (verbose > 0) {
console.log( console.log(
chalk.gray(`[${new Date().toLocaleString()}]`), chalk.gray(`[${new Date().toLocaleString()}]`),
chalk.blueBright('🔧 calling tool'), chalk.blueBright('🔧 using tool'),
chalk.blueBright(toolCall.function!.name) chalk.blueBright(toolCall.function!.name)
); );
} }
@ -395,13 +422,13 @@ export class TaskLoop {
if (result.state === 'success') { if (result.state === 'success') {
console.log( console.log(
chalk.gray(`[${new Date().toLocaleString()}]`), chalk.gray(`[${new Date().toLocaleString()}]`),
chalk.green('✓ call tools okey dockey'), chalk.green('✓ use tools'),
chalk.green(result.state) chalk.green(result.state)
); );
} else { } else {
console.log( console.log(
chalk.gray(`[${new Date().toLocaleString()}]`), chalk.gray(`[${new Date().toLocaleString()}]`),
chalk.red('× fail to call tools'), chalk.red('× use tools'),
chalk.red(result.content.map(item => item.text).join(', ')) chalk.red(result.content.map(item => item.text).join(', '))
); );
} }
@ -411,7 +438,7 @@ export class TaskLoop {
private consumeEpochs() { private consumeEpochs() {
const { verbose = 0 } = this.taskOptions; const { verbose = 0 } = this.taskOptions;
if (verbose > 0) { if (verbose > 1) {
console.log( console.log(
chalk.gray(`[${new Date().toLocaleString()}]`), chalk.gray(`[${new Date().toLocaleString()}]`),
chalk.blue('task loop enters a new epoch') chalk.blue('task loop enters a new epoch')
@ -422,12 +449,14 @@ export class TaskLoop {
private consumeDones() { private consumeDones() {
const { verbose = 0 } = this.taskOptions; const { verbose = 0 } = this.taskOptions;
if (verbose > 0) {
if (verbose > 1) {
console.log( console.log(
chalk.gray(`[${new Date().toLocaleString()}]`), chalk.gray(`[${new Date().toLocaleString()}]`),
chalk.green('task loop finish a epoch') chalk.green('task loop finish a epoch')
); );
} }
return this.onDone(); return this.onDone();
} }
@ -513,7 +542,7 @@ export class TaskLoop {
let jsonParseErrorRetryCount = 0; let jsonParseErrorRetryCount = 0;
const { const {
maxEpochs = 20, maxEpochs = 50,
verbose = 0 verbose = 0
} = this.taskOptions || {}; } = this.taskOptions || {};
@ -559,7 +588,7 @@ export class TaskLoop {
if (verbose > 0) { if (verbose > 0) {
console.log( console.log(
chalk.gray(`[${new Date().toLocaleString()}]`), chalk.gray(`[${new Date().toLocaleString()}]`),
chalk.yellow('🤖 llm wants to call these tools'), chalk.yellow('🤖 Agent wants to use these tools'),
chalk.yellow(this.streamingToolCalls.value.map(tool => tool.function!.name || '').join(', ')) chalk.yellow(this.streamingToolCalls.value.map(tool => tool.function!.name || '').join(', '))
); );
} }
@ -773,4 +802,16 @@ export class TaskLoop {
settings: _settings settings: _settings
} }
} }
public async getPrompt(promptId: string, args?: Record<string, any>) {
const prompt = await mcpClientAdapter.readPromptTemplate(promptId, args);
// transform prompt to string
const promptString = prompt.messages.map(m => m.content.text).join('\n');
return promptString;
}
public async getResource(resourceUri: string) {
const resource = await mcpClientAdapter.readResource(resourceUri);
return resource;
}
} }

View File

@ -289,9 +289,6 @@ export async function handleXmlWrapperToolcall(toolcall: XmlToolCall): Promise<T
} }
} }
// 进行调用,根据结果返回不同的值
console.log(toolcall);
const toolResponse = await mcpClientAdapter.callTool(toolcall.name, toolcall.parameters); const toolResponse = await mcpClientAdapter.callTool(toolcall.name, toolcall.parameters);
return handleToolResponse(toolResponse); return handleToolResponse(toolResponse);
} }

View File

@ -171,9 +171,6 @@ watchEffect(async () => {
} }
const renderAssistantMessage = message.content.replace(/```xml[\s\S]*?```/g, ''); const renderAssistantMessage = message.content.replace(/```xml[\s\S]*?```/g, '');
console.log(toolCalls);
renderMessages.value.push({ renderMessages.value.push({
role: 'assistant/tool_calls', role: 'assistant/tool_calls',
content: renderAssistantMessage, content: renderAssistantMessage,

View File

@ -61,7 +61,7 @@ export async function loadPanels(client: McpClient | Reactive<McpClient>) {
panelLoaded.value = true; panelLoaded.value = true;
} }
let debounceHandler: number; let debounceHandler: NodeJS.Timeout;
export function safeSavePanels() { export function safeSavePanels() {
clearTimeout(debounceHandler); clearTimeout(debounceHandler);

View File

@ -67,7 +67,7 @@ async function connect() {
} }
const isDraging = ref(false); const isDraging = ref(false);
let dragHandler: number; let dragHandler: NodeJS.Timeout;
function handleDragOver(event: DragEvent) { function handleDragOver(event: DragEvent) {
event.preventDefault(); event.preventDefault();

View File

@ -506,9 +506,7 @@ class McpClientAdapter {
constructor( constructor(
public platform: string public platform: string
) { ) {}
this.addConnectRefreshListener();
}
/** /**
* @description * @description
@ -562,7 +560,9 @@ class McpClientAdapter {
return index; return index;
} }
/**
* @description register HMR
*/
public addConnectRefreshListener() { public addConnectRefreshListener() {
// 创建对于 connect/refresh 的监听 // 创建对于 connect/refresh 的监听
if (!this.connectrefreshListener) { if (!this.connectrefreshListener) {
@ -677,7 +677,7 @@ class McpClientAdapter {
return msg; return msg;
} }
public async readPromptTemplate(promptId: string, args: Record<string, any>) { public async readPromptTemplate(promptId: string, args?: Record<string, any>) {
// TODO: 如果遇到不同服务器的同名 tool请拓展解决方案 // TODO: 如果遇到不同服务器的同名 tool请拓展解决方案
// 目前只找到第一个匹配 toolName 的工具进行调用 // 目前只找到第一个匹配 toolName 的工具进行调用
let clientId = this.clients[0].clientId; let clientId = this.clients[0].clientId;

View File

@ -32,8 +32,8 @@ export default defineConfig({
lib: { lib: {
entry: resolve(__dirname, '..', 'renderer/src/components/main-panel/chat/core/task-loop.ts'), entry: resolve(__dirname, '..', 'renderer/src/components/main-panel/chat/core/task-loop.ts'),
name: 'TaskLoop', name: 'TaskLoop',
fileName: 'task-loop', fileName: (format) => `task-loop.js`,
formats: ['cjs'] formats: ['es']
}, },
outDir: resolve(__dirname, '..', 'openmcp-sdk'), outDir: resolve(__dirname, '..', 'openmcp-sdk'),
emptyOutDir: false, emptyOutDir: false,

View File

@ -2,123 +2,110 @@
<img src="./icons/openmcp-sdk.svg" height="200px"/> <img src="./icons/openmcp-sdk.svg" height="200px"/>
<h3>openmcp-sdk : 适用于 openmcp 的部署框架</h3> <h3>openmcp-sdk: Deployment Framework for OpenMCP</h3>
<h4>闪电般将您的 agent 从实验室部署到生产环境</h4> <h4>Lightning-fast deployment of your agent from lab to production</h4>
<a href="https://kirigaya.cn/openmcp" target="_blank" style="display: inline-block; padding: 8px 16px; background-color: #7D3FF8; color: white; border-radius: .5em; text-decoration: none;">📄 OpenMCP 官方文档</a> <a href="https://kirigaya.cn/openmcp" target="_blank" style="display: inline-block; padding: 8px 16px; background-color: #7D3FF8; color: white; border-radius: .5em; text-decoration: none;">📄 OpenMCP Official Documentation</a>
<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;">QQ 讨论群</a><a href="https://discord.gg/af5cfB9a" target="_blank" style="display: inline-block; padding: 8px 16px; background-color: rgb(84, 176, 84); color: white; border-radius: .5em; text-decoration: none; margin-left: 5px;">Discord频道</a> <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;">QQ Discussion Group</a><a href="https://discord.gg/af5cfB9a" target="_blank" style="display: inline-block; padding: 8px 16px; background-color: rgb(84, 176, 84); color: white; border-radius: .5em; text-decoration: none; margin-left: 5px;">Discord Channel</a>
</div> </div>
## Installation
## 安装
```bash ```bash
npm install openmcp-sdk npm install openmcp-sdk
``` ```
> 目前 openmcp-sdk 只支持 esm 模式的导入 > Currently, openmcp-sdk only supports ESM-style imports.
## 使用 ## Usage
文件名main.ts Filename: `main.ts`
```typescript ```typescript
import { TaskLoop } from 'openmcp-sdk/task-loop'; import { OmAgent } from 'openmcp-sdk/service/sdk';
import { TaskLoopAdapter } from 'openmcp-sdk/service';
async function main() {
// 创建适配器,负责通信和 mcp 连接
const adapter = new TaskLoopAdapter();
// 添加 mcp 服务器 // create Agent
adapter.addMcp({ const agent = new OmAgent();
connectionType: 'STDIO',
commandString: 'node index.js',
cwd: '~/projects/openmcp-tutorial/my-browser/dist'
});
// 创建事件循环驱动器, verbose 数值越高,输出的日志越详细 // Load configuration, which can be automatically generated after debugging with openmcp client
const taskLoop = new TaskLoop({ adapter, verbose: 1 }); agent.loadMcpConfig('./mcpconfig.json');
// 获取所有工具 // Read the debugged prompt
const tools = await taskLoop.getTools(); const prompt = await agent.getPrompt('hacknews', { topn: '5' });
// 配置改次事件循环使用的大模型 // Execute the task
taskLoop.setLlmConfig({ const res = await agent.ainvoke({ messages: prompt });
id: 'deepseek',
baseUrl: 'https://api.deepseek.com/v1',
userToken: process.env['DEEPSEEK_API_TOKEN'],
userModel: 'deepseek-chat'
});
// 创建当前事件循环对应的上下文,并且配置当前上下文的设置 console.log('⚙️ Agent Response', res);
const storage = { ```
messages: [],
settings: { `mcpconfig.json` can be generated from [openmcp client](https://github.com/LSTM-Kirigaya/openmcp-client) directly, you don't have to write it by yourself. Here is the example:
temperature: 0.7,
// 在本次对话使用所有工具 ```json
enableTools: tools, {
// 系统提示词 "version": "1.0.0",
systemPrompt: 'you are a clever bot', "namespace": "openmcp",
// 对话上下文的轮数 "mcpServers": {
contextLength: 20 "my-browser": {
"command": "mcp",
"args": [
"run",
"~/projects/openmcp-tutorial/crawl4ai-mcp/main.py"
],
"description": "A MCP for long-term memory support",
"prompts": [
"hacknews"
]
} }
}; },
"defaultLLM": {
// 本次发出的问题 "baseURL": "https://api.deepseek.com",
const message = 'hello world'; "apiToken": "sk-xxxxxxxxxxxxxx",
"model": "deepseek-chat"
// 开启事件循环 }
await taskLoop.start(storage, message);
// 打印上下文,最终的回答在 messages.at(-1) 中
const content = storage.messages.at(-1).content;
console.log('最终回答:', content);
} }
main();
``` ```
下面是可能的输出: Run your agent and get example output:
``` ```
[6/5/2025, 8:16:15 PM] 🚀 [my-browser] 0.1.0 connected [2025/6/20 20:47:31] 🚀 [crawl4ai-mcp] 1.9.1 connected
[6/5/2025, 8:16:15 PM] task loop enters a new epoch [2025/6/20 20:47:35] 🤖 Agent wants to use these tools get_web_markdown
[6/5/2025, 8:16:23 PM] task loop finish a epoch [2025/6/20 20:47:35] 🔧 using tool get_web_markdown
[6/5/2025, 8:16:23 PM] 🤖 llm wants to call these tools k_navigate [2025/6/20 20:47:39] ✓ use tools success
[6/5/2025, 8:16:23 PM] 🔧 calling tool k_navigate [2025/6/20 20:47:46] 🤖 Agent wants to use these tools get_web_markdown, get_web_markdown, get_web_markdown
[6/5/2025, 8:16:34 PM] × fail to call tools McpError: MCP error -32603: net::ERR_CONNECTION_RESET at https://towardsdatascience.com/tag/editors-pick/ [2025/6/20 20:47:46] 🔧 using tool get_web_markdown
[6/5/2025, 8:16:34 PM] task loop enters a new epoch [2025/6/20 20:47:48] ✓ use tools success
[6/5/2025, 8:16:40 PM] task loop finish a epoch [2025/6/20 20:47:48] 🔧 using tool get_web_markdown
[6/5/2025, 8:16:40 PM] 🤖 llm wants to call these tools k_navigate [2025/6/20 20:47:54] ✓ use tools success
[6/5/2025, 8:16:40 PM] 🔧 calling tool k_navigate [2025/6/20 20:47:54] 🔧 using tool get_web_markdown
[6/5/2025, 8:16:44 PM] ✓ call tools okey dockey success [2025/6/20 20:47:57] ✓ use tools success
[6/5/2025, 8:16:44 PM] task loop enters a new epoch
[6/5/2025, 8:16:57 PM] task loop finish a epoch ⚙️ Agent Response
[6/5/2025, 8:16:57 PM] 🤖 llm wants to call these tools k_evaluate ⌨️ Today's Tech Article Roundup
[6/5/2025, 8:16:57 PM] 🔧 calling tool k_evaluate
[6/5/2025, 8:16:57 PM] ✓ call tools okey dockey success 📌 How to Detect or Observe Passing Gravitational Waves?
[6/5/2025, 8:16:57 PM] task loop enters a new epoch Summary: This article explores the physics of gravitational waves, explaining their effects on space-time and how humans might perceive or observe this cosmic phenomenon.
[6/5/2025, 8:17:06 PM] task loop finish a epoch Author: ynoxinul
[6/5/2025, 8:17:06 PM] 🤖 llm wants to call these tools k_navigate, k_navigate Posted: 2 hours ago
[6/5/2025, 8:17:06 PM] 🔧 calling tool k_navigate Link: https://physics.stackexchange.com/questions/338912/how-would-a-passing-gravitational-wave-look-or-feel
[6/5/2025, 8:17:09 PM] ✓ call tools okey dockey success
[6/5/2025, 8:17:09 PM] 🔧 calling tool k_navigate 📌 Learn Makefile Tutorial
[6/5/2025, 8:17:12 PM] ✓ call tools okey dockey success Summary: A comprehensive Makefile tutorial for beginners and advanced users, covering basic syntax, variables, automatic rules, and advanced features to help developers manage project builds efficiently.
[6/5/2025, 8:17:12 PM] task loop enters a new epoch Author: dsego
[6/5/2025, 8:17:19 PM] task loop finish a epoch Posted: 4 hours ago
[6/5/2025, 8:17:19 PM] 🤖 llm wants to call these tools k_evaluate, k_evaluate Link: https://makefiletutorial.com/
[6/5/2025, 8:17:19 PM] 🔧 calling tool k_evaluate
[6/5/2025, 8:17:19 PM] ✓ call tools okey dockey success 📌 Hurl: Run and Test HTTP Requests in Plain Text
[6/5/2025, 8:17:19 PM] 🔧 calling tool k_evaluate Summary: Hurl is a command-line tool that allows defining and executing HTTP requests in plain text format, ideal for data fetching and HTTP session testing. It supports chained requests, value capture, and response queries, making it perfect for testing REST, SOAP, and GraphQL APIs.
[6/5/2025, 8:17:19 PM] ✓ call tools okey dockey success Author: flykespice
[6/5/2025, 8:17:19 PM] task loop enters a new epoch Posted: 8 hours ago
[6/5/2025, 8:17:45 PM] task loop finish a epoch Link: https://github.com/Orange-OpenSource/hurl
"以下是整理好的热门文章信息,并已翻译为简体中文:\n\n---\n\n### K1 标题 \n**《数据漂移并非真正问题:你的监控策略才是》** \n\n**简介** \n在机器学习领域数据漂移常被视为模型性能下降的罪魁祸首但本文作者提出了一种颠覆性的观点数据漂移只是一个信号真正的核心问题在于监控策略的不足。文章通过实际案例如电商推荐系统和金融风控模型揭示了传统统计监控的局限性并提出了一个三层监控框架 \n1. **统计监控**:快速检测数据分布变化,但仅作为初步信号。 \n2. **上下文监控**:结合业务逻辑,判断漂移是否对关键指标产生影响。 \n3. **行为监控**:追踪模型预测的实际效果,避免“无声漂移”。 \n\n亮点在于作者强调了监控系统需要与业务目标紧密结合而非单纯依赖技术指标。 \n\n**原文链接** \n[点击阅读原文](https://towardsdatascience.com/data-drift-is-not-the-actual-problem-your-monitoring-strategy-is/) \n\n---\n\n### K2 标题 \n**《从 Jupyter 到程序员的快速入门指南》** \n\n**简介** \n本文为数据科学家和初学者提供了一条从 Jupyter Notebook 过渡到专业编程的清晰路径。作者通过实际代码示例和工具推荐(如 VS Code、Git 和 Docker帮助读者摆脱 Notebook 的局限性,提升代码的可维护性和可扩展性。 \n\n亮点包括 \n- 如何将 Notebook 代码模块化为可复用的 Python 脚本。 \n- 使用版本控制和容器化技术优化开发流程。 \n- 实战案例展示如何将实验性代码转化为生产级应用。 \n\n**原文链接** \n[点击阅读原文](https://towardsdatascience.com/the-journey-from-jupyter-to-programmer-a-quick-start-guide/) \n\n---\n\n如果需要进一步优化或补充其他内容请随时告诉我"
``` ```
更多使用请看官方文档https://kirigaya.cn/openmcp/sdk-tutorial/ For more details, see the official documentation: [https://kirigaya.cn/openmcp/sdk-tutorial/](https://kirigaya.cn/openmcp/sdk-tutorial/)
star 我们的项目https://github.com/LSTM-Kirigaya/openmcp-client Star our project: [https://github.com/LSTM-Kirigaya/openmcp-client](https://github.com/LSTM-Kirigaya/openmcp-client)

View File

@ -1,6 +1,7 @@
{ {
"name": "openmcp-sdk", "name": "openmcp-sdk",
"version": "0.0.8", "version": "0.0.8",
"type": "module",
"description": "openmcp-sdk", "description": "openmcp-sdk",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
@ -37,6 +38,8 @@
"open": "^10.1.2", "open": "^10.1.2",
"ws": "^8.18.1", "ws": "^8.18.1",
"cli-table3": "^0.6.5", "cli-table3": "^0.6.5",
"https-proxy-agent": "^7.0.6" "https-proxy-agent": "^7.0.6",
"pino": "^9.6.0",
"pino-pretty": "^13.0.0"
} }
} }

View File

@ -5,25 +5,25 @@ export type ChatCompletionChunk = OpenAI.Chat.Completions.ChatCompletionChunk;
export type ChatCompletionCreateParamsBase = OpenAI.Chat.Completions.ChatCompletionCreateParams & { id?: string }; export type ChatCompletionCreateParamsBase = OpenAI.Chat.Completions.ChatCompletionCreateParams & { id?: string };
interface SchemaProperty { interface SchemaProperty {
title: string; title: string;
type: string; type: string;
description?: string; description?: string;
} }
interface InputSchema { interface InputSchema {
type: string; type: string;
properties: Record<string, SchemaProperty>; properties: Record<string, SchemaProperty>;
required?: string[]; required?: string[];
title?: string; title?: string;
$defs?: any; $defs?: any;
} }
interface ToolItem { interface ToolItem {
name: string; name: string;
description: string; description: string;
inputSchema: InputSchema; inputSchema: InputSchema;
enabled: boolean; enabled: boolean;
anyOf?: any; anyOf?: any;
} }
interface IExtraInfo { interface IExtraInfo {
@ -87,7 +87,7 @@ export interface ToolCall {
export interface ToolCallContent { export interface ToolCallContent {
type: string; type: string;
text: string; text: string;
[key: string]: any; [key: string]: any;
} }
export interface ToolCallResult { export interface ToolCallResult {
@ -189,6 +189,17 @@ interface ChatSetting {
export class TaskLoop { export class TaskLoop {
constructor(taskOptions?: TaskLoopOptions); constructor(taskOptions?: TaskLoopOptions);
/**
* @description wait for connection
*/
waitConnection(): Promise<void>;
/**
* @description Set the task loop options
* @param taskOptions
*/
setTaskLoopOptions(taskOptions: TaskLoopOptions): void;
/** /**
* @description make chat data * @description make chat data
* @param tabStorage * @param tabStorage
@ -284,12 +295,22 @@ export class TaskLoop {
/** /**
* @description Start the loop and asynchronously update the DOM * @description Start the loop and asynchronously update the DOM
*/ */
start(tabStorage: any, userMessage: string): Promise<void>; start(tabStorage: ChatStorage, userMessage: string): Promise<void>;
/** /**
* @description Create single conversation context * @description Create single conversation context
*/ */
createStorage(settings?: ChatSetting): Promise<ChatStorage>; createStorage(settings?: ChatSetting): Promise<ChatStorage>;
/**
* @description Get prompt template from mcp server
*/
getPrompt(promptId: string, args?: Record<string, any>): Promise<string>;
/**
* @description Get resource template from mcp server
*/
getResource(resourceUri: string): Promise<string>;
} }
export declare const getToolSchema: any; export declare const getToolSchema: any;

View File

@ -1,8 +1,5 @@
import { WebSocket } from 'ws'; import { WebSocket } from 'ws';
import { EventEmitter } from 'events'; import { ConnectionType } from '../mcp/client.dto.js';
import { routeMessage } from '../common/router.js';
import { ConnectionType, McpOptions } from '../mcp/client.dto.js';
import { clientMap, connectService } from '../mcp/connect.service.js';
// WebSocket 消息格式 // WebSocket 消息格式
export interface WebSocketMessage { export interface WebSocketMessage {
@ -27,6 +24,10 @@ export interface IConnectionArgs {
cwd?: string; cwd?: string;
url?: string; url?: string;
oauth?: string; oauth?: string;
env?: {
[key: string]: string;
};
[key: string]: any;
} }
// 监听器回调类型 // 监听器回调类型
@ -75,80 +76,3 @@ export class VSCodeWebViewLike {
}; };
} }
} }
export class TaskLoopAdapter {
public emitter: EventEmitter;
private messageHandlers: Set<MessageHandler>;
private connectionOptions: IConnectionArgs[] = [];
constructor(option?: any) {
this.emitter = new EventEmitter(option);
this.messageHandlers = new Set();
this.emitter.on('message/renderer', (message: WebSocketMessage) => {
this.messageHandlers.forEach((handler) => handler(message));
});
// 默认需要将监听的消息导入到 routeMessage 中
this.onDidReceiveMessage((message) => {
const { command, data } = message;
switch (command) {
case 'nodejs/launch-signature':
this.postMessage({
command: 'nodejs/launch-signature',
data: {
code: 200,
msg: this.connectionOptions
}
})
break;
case 'nodejs/update-connection-signature':
// sdk 模式下不需要自动保存连接参数
break;
default:
routeMessage(command, data, this);
break;
}
});
}
/**
* @description
* @param message - command args
*/
public postMessage(message: WebSocketMessage): void {
this.emitter.emit('message/service', message);
}
/**
* @description
* @param callback -
* @returns {{ dispose: () => void }} -
*/
public onDidReceiveMessage(callback: MessageHandler): { dispose: () => void } {
this.messageHandlers.add(callback);
return {
dispose: () => this.messageHandlers.delete(callback),
};
}
/**
* @description mcp
* @param mcpOption
*/
public addMcp(mcpOption: IConnectionArgs) {
// 0.1.4 新版本开始,此处修改为懒加载连接
// 实际的连接移交给前端 mcpAdapter 中进行统一的调度
// 调度步骤如下:
// getLaunchSignature 先获取访问签名,访问签名通过当前函数 push 到 class 中
this.connectionOptions.push(mcpOption);
}
}

327
service/src/hook/sdk.ts Normal file
View File

@ -0,0 +1,327 @@
import { EventEmitter } from 'events';
import { routeMessage } from '../common/router.js';
import * as fs from 'fs';
export class TaskLoopAdapter {
public emitter: EventEmitter;
private messageHandlers: Set<MessageHandler>;
private connectionOptions: IConnectionArgs[] = [];
constructor(option?: any) {
this.emitter = new EventEmitter(option);
this.messageHandlers = new Set();
this.emitter.on('message/renderer', (message: WebSocketMessage) => {
this.messageHandlers.forEach((handler) => handler(message));
});
// 默认需要将监听的消息导入到 routeMessage 中
this.onDidReceiveMessage((message) => {
const { command, data } = message;
switch (command) {
case 'nodejs/launch-signature':
this.postMessage({
command: 'nodejs/launch-signature',
data: {
code: 200,
msg: this.connectionOptions
}
})
break;
case 'nodejs/update-connection-signature':
// sdk 模式下不需要自动保存连接参数
break;
default:
routeMessage(command, data, this);
break;
}
});
}
/**
* @description
* @param message - command args
*/
public postMessage(message: WebSocketMessage): void {
this.emitter.emit('message/service', message);
}
/**
* @description
* @param callback -
* @returns {{ dispose: () => void }} -
*/
public onDidReceiveMessage(callback: MessageHandler): { dispose: () => void } {
this.messageHandlers.add(callback);
return {
dispose: () => this.messageHandlers.delete(callback),
};
}
/**
* @description mcp
* @param mcpOption
*/
public addMcp(mcpOption: IConnectionArgs) {
// 0.1.4 新版本开始,此处修改为懒加载连接
// 实际的连接移交给前端 mcpAdapter 中进行统一的调度
// 调度步骤如下:
// getLaunchSignature 先获取访问签名,访问签名通过当前函数 push 到 class 中
this.connectionOptions.push(mcpOption);
}
}
interface StdioMCPConfig {
command: string;
args: string[];
env?: {
[key: string]: string;
};
description?: string;
prompts?: string[];
resources?: string[];
}
interface HttpMCPConfig {
url: string;
type?: string;
env?: {
[key: string]: string;
};
description?: string;
prompts?: string[];
resources?: string[];
}
export interface OmAgentConfiguration {
version: string;
mcpServers: {
[key: string]: StdioMCPConfig | HttpMCPConfig;
};
defaultLLM: {
baseURL: string;
apiToken: string;
model: string;
}
}
export interface DefaultLLM {
baseURL: string;
apiToken?: string;
model: string;
}
import {
MessageState,
type TaskLoopOptions,
type ChatMessage,
type ChatSetting,
type TaskLoop,
type TextMessage
} from '../../task-loop.js';
import { IConnectionArgs, MessageHandler, WebSocketMessage } from './adapter.js';
import { ConnectionType } from 'src/mcp/client.dto.js';
export function UserMessage(content: string): TextMessage {
return {
role: 'user',
content,
extraInfo: {
created: Date.now(),
state: MessageState.None,
serverName: '',
enableXmlWrapper: false
}
}
}
export function AssistantMessage(content: string): TextMessage {
return {
role: 'assistant',
content,
extraInfo: {
created: Date.now(),
state: MessageState.None,
serverName: '',
enableXmlWrapper: false
}
}
}
export class OmAgent {
private _adapter: TaskLoopAdapter;
private _loop?: TaskLoop;
private _defaultLLM?: DefaultLLM;
constructor() {
this._adapter = new TaskLoopAdapter();
}
/**
* @description Load MCP configuration from file.
* Supports multiple MCP backends and a default LLM model configuration.
*
* @example
* Example configuration:
* {
* "version": "1.0.0",
* "mcpServers": {
* "openmemory": {
* "command": "npx",
* "args": ["-y", "openmemory"],
* "env": {
* "OPENMEMORY_API_KEY": "YOUR_API_KEY",
* "CLIENT_NAME": "openmemory"
* },
* "description": "A MCP for long-term memory support"
* }
* },
* "defaultLLM": {
* "baseURL": "https://api.openmemory.ai",
* "apiToken": "YOUR_API_KEY",
* "model": "deepseek-chat"
* }
* }
*
* @param configPath - Path to the configuration file
*/
public loadMcpConfig(configPath: string) {
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8')) as OmAgentConfiguration;
const { mcpServers, defaultLLM } = config;
// set default llm
this.setDefaultLLM(defaultLLM);
for (const key in mcpServers) {
const mcpConfig = mcpServers[key];
if ('command' in mcpConfig) {
const commandString = (
mcpConfig.command + ' ' + mcpConfig.args.join(' ')
).trim();
this._adapter.addMcp({
commandString,
connectionType: 'STDIO',
env: mcpConfig.env,
description: mcpConfig.description,
});
} else {
const connectionType: ConnectionType = mcpConfig.type === 'http' ? 'STREAMABLE_HTTP' : 'SSE';
this._adapter.addMcp({
url: mcpConfig.url,
env: mcpConfig.env,
connectionType,
description: mcpConfig.description,
});
}
}
}
/**
* @description Add MCP server
*/
public addMcpServer(connectionArgs: IConnectionArgs) {
this._adapter.addMcp(connectionArgs);
}
private async getLoop(loopOption?: TaskLoopOptions) {
if (this._loop) {
if (loopOption) {
this._loop.setTaskLoopOptions(loopOption);
}
return this._loop;
}
const {
verbose = 1,
maxEpochs = 50,
maxJsonParseRetry = 3,
} = loopOption || {}
const adapter = this._adapter;
const { TaskLoop } = await import('../../task-loop.js');
this._loop = new TaskLoop({ adapter, verbose, maxEpochs, maxJsonParseRetry });
await this._loop.waitConnection();
return this._loop;
}
public setDefaultLLM(option: DefaultLLM) {
this._defaultLLM = option;
}
public async getPrompt(promptId: string, args: Record<string, any>) {
const loop = await this.getLoop();
const prompt = await loop.getPrompt(promptId, JSON.parse(JSON.stringify(args)));
return prompt;
}
/**
* @description Asynchronous invoking agent by string or messages
* @param messages Chat message or string
* @param settings Chat setting and task loop options
* @returns
*/
public async ainvoke(
{ messages, settings }: { messages: ChatMessage[] | string; settings?: ChatSetting & Partial<TaskLoopOptions>; }
) {
if (messages.length === 0) {
throw new Error('messages is empty');
}
// detach taskloop option from settings and set default value
const {
maxEpochs = 50,
maxJsonParseRetry = 3,
verbose = 1
} = settings || {};
const loop = await this.getLoop({ maxEpochs, maxJsonParseRetry, verbose });
const storage = await loop.createStorage(settings);
// set input message
// user can invoke [UserMessage("CONTENT")] to make messages quickly
// or use string directly
let userMessage: string;
if (typeof messages === 'string') {
userMessage = messages;
} else {
const lastMessageContent = messages.at(-1)?.content;
if (typeof lastMessageContent === 'string') {
userMessage = lastMessageContent;
} else {
throw new Error('last message content is undefined');
}
}
// select correct llm config
// user can set llm config via omagent.setDefaultLLM()
// or write "defaultLLM" in mcpconfig.json to specify
if (this._defaultLLM) {
loop.setLlmConfig({
baseUrl: this._defaultLLM.baseURL,
userToken: this._defaultLLM.apiToken,
userModel: this._defaultLLM.model,
});
} else {
// throw error to user and give the suggestion
throw new Error('default LLM is not set, please set it via omagent.setDefaultLLM() or write "defaultLLM" in mcpconfig.json');
}
await loop.start(storage, userMessage);
// get response from last message in message list
const lastMessage = storage.messages.at(-1)?.content;
return lastMessage;
}
}

View File

@ -1,4 +1,4 @@
export { routeMessage } from './common/router.js'; export { routeMessage } from './common/router.js';
export { VSCodeWebViewLike, TaskLoopAdapter } from './hook/adapter.js'; export { VSCodeWebViewLike } from './hook/adapter.js';
export { setVscodeWorkspace, setRunningCWD } from './hook/setting.js'; export { setVscodeWorkspace, setRunningCWD } from './hook/setting.js';
export { clientMap } from './mcp/connect.service.js'; export { clientMap } from './mcp/connect.service.js';

View File

@ -38,7 +38,7 @@ export class McpServerConnectMonitor {
this.filePath = getFilePath(options); this.filePath = getFilePath(options);
// 记录实例创建 // 记录实例创建
logger.info({ uuid, connectionType: options.connectionType }, 'Created new connection monitor instance'); // logger.info({ uuid, connectionType: options.connectionType }, 'Created new connection monitor instance');
switch (options.connectionType) { switch (options.connectionType) {
case 'STDIO': case 'STDIO':
@ -94,7 +94,7 @@ export class McpServerConnectMonitor {
}, },
onStart: () => { onStart: () => {
// 使用 info 级别记录监控开始 // 使用 info 级别记录监控开始
logger.info({ uuid: this.uuid, filePath: path.resolve(fileConfig.filePath) }, 'Started monitoring file'); // logger.info({ uuid: this.uuid, filePath: path.resolve(fileConfig.filePath) }, 'Started monitoring file');
try { try {
const stats = fs.statSync(fileConfig.filePath); const stats = fs.statSync(fileConfig.filePath);

View File

@ -268,24 +268,24 @@ export async function connectService(
): Promise<RestfulResponse> { ): Promise<RestfulResponse> {
try { try {
// 使用cli-table3创建美观的表格 // 使用cli-table3创建美观的表格
const table = new Table({ // const table = new Table({
head: ['Property', 'Value'], // head: ['Property', 'Value'],
colWidths: [20, 60], // colWidths: [20, 60],
style: { // style: {
head: ['green'], // head: ['green'],
border: ['grey'] // border: ['grey']
} // }
}); // });
table.push( // table.push(
['Connection Type', option.connectionType], // ['Connection Type', option.connectionType],
['Command', option.command || 'N/A'], // ['Command', option.command || 'N/A'],
['Arguments', option.args?.join(' ') || 'N/A'], // ['Arguments', option.args?.join(' ') || 'N/A'],
['Working Directory', option.cwd || 'N/A'], // ['Working Directory', option.cwd || 'N/A'],
['URL', option.url || 'N/A'] // ['URL', option.url || 'N/A']
); // );
console.log(table.toString()); // console.log(table.toString());
// 预处理字符串 // 预处理字符串
await preprocessCommand(option, webview); await preprocessCommand(option, webview);

View File

@ -71,7 +71,7 @@ class SingleFileMonitor {
this.handleFileChange(true); this.handleFileChange(true);
} }
}); });
console.log(`正在监控文件: ${this.filePath}`); // console.log(`正在监控文件: ${this.filePath}`);
} catch (error) { } catch (error) {
this.onError(error as Error); this.onError(error as Error);
} }
@ -171,7 +171,7 @@ class SingleFileMonitor {
if (this.watcher) { if (this.watcher) {
// 明确指定close方法的类型解决TS2554错误 // 明确指定close方法的类型解决TS2554错误
(this.watcher.close as (callback?: () => void) => void)(() => { (this.watcher.close as (callback?: () => void) => void)(() => {
console.log(`已停止监控文件: ${this.filePath}`); // console.log(`已停止监控文件: ${this.filePath}`);
}); });
this.watcher = null; this.watcher = null;
} }

1
service/src/sdk.ts Normal file
View File

@ -0,0 +1 @@
export { TaskLoopAdapter, OmAgent, OmAgentConfiguration, UserMessage, AssistantMessage } from './hook/sdk.js';

324
service/task-loop.d.ts vendored Normal file
View File

@ -0,0 +1,324 @@
/* eslint-disable */
import type { OpenAI } from 'openai';
export type ChatCompletionChunk = OpenAI.Chat.Completions.ChatCompletionChunk;
export type ChatCompletionCreateParamsBase = OpenAI.Chat.Completions.ChatCompletionCreateParams & { id?: string };
interface SchemaProperty {
title: string;
type: string;
description?: string;
}
interface InputSchema {
type: string;
properties: Record<string, SchemaProperty>;
required?: string[];
title?: string;
$defs?: any;
}
interface ToolItem {
name: string;
description: string;
inputSchema: InputSchema;
enabled: boolean;
anyOf?: any;
}
interface IExtraInfo {
created: number,
state: MessageState,
serverName: string,
usage?: ChatCompletionChunk['usage'];
enableXmlWrapper: boolean;
[key: string]: any;
}
interface ToolMessage {
role: 'tool';
index: number;
content: ToolCallContent[];
tool_call_id?: string
name?: string // 工具名称,当 role 为 tool
tool_calls?: ToolCall[],
extraInfo: IExtraInfo
}
interface TextMessage {
role: 'user' | 'assistant' | 'system';
content: string;
tool_call_id?: string
name?: string // 工具名称,当 role 为 tool
tool_calls?: ToolCall[],
extraInfo: IExtraInfo
}
export type ChatMessage = ToolMessage | TextMessage;
interface ChatStorage {
messages: ChatMessage[]
settings: ChatSetting
}
interface EnableToolItem {
name: string;
description: string;
enabled: boolean;
inputSchema: InputSchema;
}
export type Ref<T> = {
value: T;
};
export interface ToolCall {
id?: string;
index?: number;
type: string;
function: {
name: string;
arguments: string;
}
}
export interface ToolCallContent {
type: string;
text: string;
[key: string]: any;
}
export interface ToolCallResult {
state: MessageState;
content: ToolCallContent[];
}
export enum MessageState {
ServerError = 'server internal error',
ReceiveChunkError = 'receive chunk error',
Timeout = 'timeout',
MaxEpochs = 'max epochs',
Unknown = 'unknown error',
Abort = 'abort',
ToolCall = 'tool call failed',
None = 'none',
Success = 'success',
ParseJsonError = 'parse json error'
}
export interface IErrorMssage {
state: MessageState;
msg: string;
}
export interface IDoConversationResult {
stop: boolean;
}
export interface TaskLoopOptions {
/**
* The maximum number of epochs (conversation rounds) to perform.
*/
maxEpochs?: number;
/**
* The maximum number of retries allowed when parsing JSON responses fails.
*/
maxJsonParseRetry?: number;
/**
* A custom adapter that can be used to modify behavior or integrate with different environments.
*/
adapter?: any;
/**
* Verbosity level for logging:
* 0 - Silent, 1 - Errors only, 2 - Warnings and errors, 3 - Full debug output.
*/
verbose?: 0 | 1 | 2 | 3;
}
interface ChatSetting {
/**
* Index of the selected language model from a list of available models.
*/
modelIndex: number;
/**
* System-level prompt used to guide the behavior of the assistant.
*/
systemPrompt: string;
/**
* List of tools that are enabled and available during the chat.
*/
enableTools: EnableToolItem[];
/**
* Sampling temperature for generating responses.
* Higher values (e.g., 0.8) make output more random; lower values (e.g., 0.2) make it more focused and deterministic.
*/
temperature: number;
/**
* Whether web search is enabled for enhancing responses with real-time information.
*/
enableWebSearch: boolean;
/**
* Maximum length of the conversation context to keep.
*/
contextLength: number;
/**
* Whether multiple tools can be called in parallel within a single message.
*/
parallelToolCalls: boolean;
/**
* Whether to wrap tool call responses in XML format.
*/
enableXmlWrapper: boolean;
}
/**
* @description
*/
export class TaskLoop {
constructor(taskOptions?: TaskLoopOptions);
/**
* @description wait for connection
*/
waitConnection(): Promise<void>;
/**
* @description Set the task loop options
* @param taskOptions
*/
setTaskLoopOptions(taskOptions: TaskLoopOptions): void;
/**
* @description make chat data
* @param tabStorage
*/
makeChatData(tabStorage: any): ChatCompletionCreateParamsBase | undefined;
/**
* @description stop the task loop
*/
abort(): void;
/**
* @description Register a callback function triggered on error
* @param handler
*/
registerOnError(handler: (msg: IErrorMssage) => void): void;
/**
* @description Register a callback function triggered on chunk
* @param handler
*/
registerOnChunk(handler: (chunk: ChatCompletionChunk) => void): void;
/**
* @description Register a callback function triggered at the beginning of each epoch
* @param handler
*/
registerOnDone(handler: () => void): void;
/**
* @description Register a callback function triggered at the beginning of each epoch
* @param handler
*/
registerOnEpoch(handler: () => void): void;
/**
* @description Registers a callback function that is triggered when a tool call is completed. This method allows you to intercept and modify the output of the tool call.
* @param handler
*/
registerOnToolCalled(handler: (toolCallResult: ToolCallResult) => ToolCallResult): void;
/**
* @description Register a callback triggered after tool call finishes. You can intercept and modify the output.
* @param handler
*/
registerOnToolCall(handler: (toolCall: ToolCall) => ToolCall): void;
/**
* @description Get current LLM configuration
*/
getLlmConfig(): any;
/**
* @description Set the current LLM configuration, for Node.js environment
* @param config
* @example
* setLlmConfig({
* id: 'openai',
* baseUrl: 'https://api.openai.com/v1',
* userToken: 'sk-xxx',
* userModel: 'gpt-3.5-turbo',
* })
*/
setLlmConfig(config: any): void;
/**
* @description Set proxy server
* @param maxEpochs
*/
setMaxEpochs(maxEpochs: number): void;
/**
* @description bind streaming content and tool calls
*/
bindStreaming(content: Ref<string>, toolCalls: Ref<ToolCall[]>): void;
/**
* @description not finish
*/
connectToService(): Promise<void>;
/**
* @description
* @param proxyServer
*/
setProxyServer(proxyServer: string): void;
/**
* @description Get all available tool list
*/
listTools(): Promise<ToolItem[]>;
/**
* @description Start the loop and asynchronously update the DOM
*/
start(tabStorage: ChatStorage, userMessage: string): Promise<void>;
/**
* @description Create single conversation context
*/
createStorage(settings?: ChatSetting): Promise<ChatStorage>;
/**
* @description Get prompt template from mcp server
*/
getPrompt(promptId: string, args?: Record<string, any>): Promise<string>;
/**
* @description Get resource template from mcp server
*/
getResource(resourceUri: string): Promise<string>;
}
export declare const getToolSchema: any;
export declare const useMessageBridge: any;
export declare const llmManager: any;
export declare const llms: any;
export declare const pinkLog: any;
export declare const redLog: any;
export declare const ElMessage: any;
export declare const handleToolCalls: any;
export declare const getPlatform: any;

View File

@ -10,7 +10,7 @@
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"outDir": "./dist", "outDir": "./dist",
"declaration": true, "declaration": true,
"declarationMap": true, "declarationMap": false,
"experimentalDecorators": true, "experimentalDecorators": true,
}, },
"paths": { "paths": {

View File

@ -14,6 +14,9 @@
"noEmitHelpers": false, "noEmitHelpers": false,
"experimentalDecorators": true, "experimentalDecorators": true,
"sourceMap": true, "sourceMap": true,
"types": [
"./service/task-loop.d.ts"
],
// 访 openmcp-sdk // 访 openmcp-sdk
"paths": { "paths": {
"@openmcp-sdk/*": [ "@openmcp-sdk/*": [