@huyooo/ai-chat-core 0.2.19 → 0.2.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.ts ADDED
@@ -0,0 +1,309 @@
1
+ /**
2
+ * @huyooo/ai-chat-core
3
+ *
4
+ * AI Chat 核心包 - 纯 Agent 逻辑,无框架依赖
5
+ *
6
+ * 新架构(Protocol + Family 分离):
7
+ * - Model Registry: 模型注册表,管理所有模型配置
8
+ * - Protocol Layer: 只负责 HTTP/SSE 通信
9
+ * - Family Config: 统一管理模型行为差异
10
+ * - UnifiedAdapter: 组合 Protocol + Family
11
+ * - ChatOrchestrator: 统一处理工具调用循环
12
+ *
13
+ * 设计优势:
14
+ * 1. Protocol 只负责 HTTP/SSE 通信
15
+ * 2. FamilyConfig 统一管理行为差异
16
+ * 3. 新增模型只需在 Registry 配置,无需修改代码
17
+ *
18
+ * 支持模型:
19
+ * - ARK (豆包/DeepSeek) - 火山引擎
20
+ * - Qwen (通义千问) - DashScope
21
+ * - Gemini - Google AI
22
+ * - OpenRouter - Claude/GPT 等
23
+ */
24
+
25
+ // ==================== Agent ====================
26
+
27
+ export { HybridAgent, type RuntimeConfig } from './agent';
28
+
29
+ // ==================== 模型注册表 ====================
30
+
31
+ export {
32
+ // 家族配置
33
+ MODEL_FAMILIES,
34
+ DOUBAO_FAMILY,
35
+ DEEPSEEK_FAMILY,
36
+ QWEN_FAMILY,
37
+ GEMINI_FAMILY,
38
+ GPT_FAMILY,
39
+ CLAUDE_FAMILY,
40
+
41
+ // 模型注册表
42
+ MODEL_REGISTRY,
43
+
44
+ // 查询函数
45
+ getModelEntry,
46
+ getModelFamily,
47
+ getModelProtocol,
48
+ getVisibleModels,
49
+ getModelsByFamily,
50
+ getModelsByProtocol,
51
+ modelSupportsThinking,
52
+ modelSupportsNativeSearch,
53
+ getModelSearchStrategy,
54
+ } from './providers/model-registry';
55
+
56
+ export type {
57
+ ModelFamilyId,
58
+ ProtocolId,
59
+ ThinkingFormat,
60
+ SearchStrategy,
61
+ ToolCallFormat,
62
+ ModelFamilyConfig,
63
+ ModelRegistryEntry,
64
+ } from './providers/model-registry';
65
+
66
+ // ==================== 新架构:UnifiedAdapter + Protocol ====================
67
+
68
+ // UnifiedAdapter - 统一适配器
69
+ export { UnifiedAdapter, createUnifiedAdapter } from './providers/unified-adapter';
70
+ export type { UnifiedAdapterConfig, StreamOptions } from './providers/unified-adapter';
71
+
72
+ // Protocol Layer
73
+ export {
74
+ ArkProtocol,
75
+ createArkProtocol,
76
+ DeepSeekProtocol,
77
+ createDeepSeekProtocol,
78
+ QwenProtocol,
79
+ createQwenProtocol,
80
+ GeminiProtocol,
81
+ createGeminiProtocol,
82
+ OpenAIProtocol,
83
+ createOpenAIProtocol,
84
+ AnthropicProtocol,
85
+ createAnthropicProtocol,
86
+ } from './providers/protocols';
87
+
88
+ export type {
89
+ Protocol,
90
+ ProtocolConfig,
91
+ ProtocolMessage,
92
+ ProtocolToolDefinition,
93
+ ProtocolToolCall,
94
+ RawEvent,
95
+ RawEventType,
96
+ RawToolCall,
97
+ RawSearchResult,
98
+ } from './providers/protocols';
99
+
100
+ // Orchestrator - 统一的工具调用处理
101
+ export { ChatOrchestrator, createOrchestrator } from './providers/orchestrator';
102
+
103
+ // 新架构类型
104
+ export type {
105
+ StreamChunk,
106
+ StreamChunkType,
107
+ ToolCallRequest,
108
+ SearchResultItem,
109
+ StandardMessage,
110
+ ProviderAdapter,
111
+ AdapterConfig,
112
+ StreamOnceOptions,
113
+ OrchestratorConfig,
114
+ OrchestratorContext,
115
+ OrchestratorOptions,
116
+ SimpleToolDefinition,
117
+ ToolExecutor as OrchestratorToolExecutor,
118
+ } from './providers/types';
119
+
120
+ // Legacy Adapters 已删除,使用 UnifiedAdapter + Protocol 新架构
121
+
122
+ // ==================== 配置与类型 ====================
123
+
124
+ export type {
125
+ AgentConfig,
126
+ ChatOptions,
127
+ ChatMode,
128
+ ThinkingMode,
129
+ AutoRunMode,
130
+ AutoRunConfig,
131
+ ToolExecutor,
132
+ ToolDefinition,
133
+ ChatMessage,
134
+ ResponsesApiTool,
135
+ // 模型类型
136
+ ModelOption,
137
+ ProviderType,
138
+ // 工具接口
139
+ Tool,
140
+ ToolContext,
141
+ ToolResult,
142
+ // 副作用类型
143
+ SideEffect,
144
+ // 工具插件(Vite 风格)
145
+ ToolPlugin,
146
+ ToolConfigItem,
147
+ // 用户工具(透传模式)
148
+ UserToolDefinition,
149
+ } from './types';
150
+
151
+ export {
152
+ // 模型列表
153
+ MODELS,
154
+ getModelByModelId,
155
+ // 工具解析和辅助函数
156
+ resolveTools,
157
+ tool,
158
+ tools,
159
+ } from './types';
160
+
161
+ // 常量
162
+ export { DEFAULT_MODEL } from './constants';
163
+
164
+ // ==================== 事件系统 ====================
165
+
166
+ // 基础类型
167
+ export type {
168
+ SearchResult,
169
+ ToolCallStatus,
170
+ ToolCallInfo,
171
+ TokenUsage,
172
+ ErrorCategory,
173
+ ErrorDetails
174
+ } from './events';
175
+
176
+ // 思考事件
177
+ export type {
178
+ ThinkingStartEvent,
179
+ ThinkingDeltaEvent,
180
+ ThinkingEndEvent,
181
+ ThinkingEvent
182
+ } from './events';
183
+
184
+ // 搜索事件
185
+ export type {
186
+ SearchStartEvent,
187
+ SearchResultEvent,
188
+ SearchEndEvent,
189
+ SearchEvent
190
+ } from './events';
191
+
192
+ // 工具事件
193
+ export type {
194
+ ToolCallStartEvent,
195
+ ToolCallResultEvent,
196
+ ToolCallOutputEvent,
197
+ ToolApprovalRequestEvent,
198
+ ToolCallRequestEvent,
199
+ ToolEvent
200
+ } from './events';
201
+
202
+ // 文本事件
203
+ export type {
204
+ TextDeltaEvent,
205
+ TextEvent
206
+ } from './events';
207
+
208
+ // 计划类型(update_plan 工具通过 resultType:'plan' 渲染,无专用事件)
209
+ export type {
210
+ PlanStep,
211
+ PlanStepStatus,
212
+ } from './events';
213
+
214
+ // 状态事件
215
+ export type {
216
+ DoneEvent,
217
+ ErrorEvent,
218
+ AbortEvent,
219
+ StatusEvent
220
+ } from './events';
221
+
222
+ // 步骤事件
223
+ export type {
224
+ StepStartEvent,
225
+ StepEndEvent,
226
+ StepEvent
227
+ } from './events';
228
+
229
+ // 聚合类型
230
+ export type {
231
+ ChatEvent,
232
+ ChatEventType
233
+ } from './events';
234
+
235
+ // 事件创建函数
236
+ export {
237
+ createThinkingStart,
238
+ createThinkingDelta,
239
+ createThinkingEnd,
240
+ createSearchStart,
241
+ createSearchResult,
242
+ createSearchEnd,
243
+ createToolCallStart,
244
+ createToolCallResult,
245
+ createToolCallOutput,
246
+ createToolCallRequest,
247
+ createTextDelta,
248
+ createDone,
249
+ // 错误创建函数
250
+ createError,
251
+ createApiError,
252
+ createRateLimitError,
253
+ createToolError,
254
+ createTimeoutError,
255
+ createParseError,
256
+ createAbort,
257
+ // 步骤事件
258
+ createStepStart,
259
+ createStepEnd,
260
+ // 类型守卫
261
+ isThinkingEvent,
262
+ isSearchEvent,
263
+ isToolEvent,
264
+ isTextEvent,
265
+ isStatusEvent,
266
+ isErrorEvent,
267
+ isAbortEvent,
268
+ isStepEvent,
269
+ isRetryableError,
270
+ CHAT_EVENT_TYPES
271
+ } from './events';
272
+
273
+ // ==================== 工具执行器 ====================
274
+
275
+ export { createDefaultToolExecutor } from './tools';
276
+
277
+ // ==================== 工具已迁移 ====================
278
+ //
279
+ // 计划工具(update_plan)请使用:
280
+ // import { createUpdatePlanTool } from '@huyooo/ai-chat-tools-local';
281
+ //
282
+ // 云端工具(weather, web-search)请使用:
283
+ // import { getWeatherTool, createWebSearchTool } from '@huyooo/ai-chat-tools-cloud';
284
+ //
285
+ // 本地工具(execute-command, ui-actions 等)请使用:
286
+ // import { getCwdTool, executeCommandTool, ... } from '@huyooo/ai-chat-tools-local';
287
+ //
288
+
289
+ // ==================== 路由 ====================
290
+
291
+ export type { RouteResult } from './router';
292
+
293
+ export {
294
+ routeModelToProvider,
295
+ routeModelWithDetails,
296
+ getDefaultProvider,
297
+ isModelForProvider
298
+ } from './router';
299
+
300
+ // ==================== MCP ====================
301
+ // McpClientManager 只在内部使用(agent.ts 直接 import),不从主入口导出
302
+ // 避免在 renderer 中因 barrel file 加载整个模块图而引入 Node.js-only 的 MCP SDK
303
+ // 如需外部使用,请通过 @huyooo/ai-chat-core/mcp 子路径导入
304
+
305
+ export type { McpServerConfig, McpConnectionStatus, McpConnectionInfo } from './mcp';
306
+
307
+ // ==================== 调试工具 ====================
308
+
309
+ export { DebugLogger } from './utils';
@@ -0,0 +1,2 @@
1
+ // 已迁移到 @huyooo/ai-chat-tools-local
2
+ // import { createUpdatePlanTool } from '@huyooo/ai-chat-tools-local';
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Web Search 工具(内部使用,Tavily 实现)
3
+ *
4
+ * 对外统一使用工具名 web_search_ai,各协议一致使用,与厂商内置 web_search 不冲突。
5
+ */
6
+
7
+ import type { Tool } from '../types';
8
+
9
+ /** 联网搜索工具名(Tavily 实现,对外统一) */
10
+ export const WEB_SEARCH_TOOL_NAME = 'web_search_ai';
11
+
12
+ export function createWebSearchTool(tavilyApiKey: string): Tool {
13
+ return {
14
+ name: WEB_SEARCH_TOOL_NAME,
15
+ description:
16
+ '联网搜索工具。输入 query(搜索关键词/问题),返回搜索结果列表(title/url/snippet)。用于获取实时信息与可引用来源。',
17
+ parameters: {
18
+ type: 'object',
19
+ properties: {
20
+ query: { type: 'string', description: '搜索关键词或问题(必填)' },
21
+ max_results: { type: 'number', description: '最大返回结果数(可选,默认 5)' },
22
+ },
23
+ required: ['query'],
24
+ },
25
+ resultType: 'search_results',
26
+ execute: async (args, ctx) => {
27
+ const query = typeof args.query === 'string' ? args.query : '';
28
+ const maxResults = typeof args.max_results === 'number' && Number.isFinite(args.max_results)
29
+ ? Math.max(1, Math.min(10, Math.floor(args.max_results)))
30
+ : 5;
31
+
32
+ if (!query.trim()) {
33
+ return JSON.stringify({ query: '', results: [], error: '缺少 query' });
34
+ }
35
+ if (!tavilyApiKey) {
36
+ return JSON.stringify({ query, results: [], error: '缺少 Tavily API Key' });
37
+ }
38
+
39
+ const resp = await fetch('https://api.tavily.com/search', {
40
+ method: 'POST',
41
+ headers: {
42
+ Authorization: `Bearer ${tavilyApiKey}`,
43
+ 'Content-Type': 'application/json',
44
+ },
45
+ body: JSON.stringify({
46
+ query,
47
+ max_results: maxResults,
48
+ search_depth: 'basic',
49
+ include_answer: false,
50
+ include_raw_content: false,
51
+ }),
52
+ signal: ctx.signal,
53
+ });
54
+
55
+ if (!resp.ok) {
56
+ const t = await resp.text().catch(() => '');
57
+ return JSON.stringify({ query, results: [], error: `Tavily /search 错误: ${resp.status} ${t}`.trim() });
58
+ }
59
+
60
+ const data: unknown = await resp.json().catch(() => null);
61
+
62
+ const results: Array<{ title: string; url: string; snippet: string }> = [];
63
+ const arr = (data && typeof data === 'object' && Array.isArray((data as Record<string, unknown>).results))
64
+ ? (data as Record<string, unknown>).results as Array<Record<string, unknown>>
65
+ : [];
66
+ for (const r of arr) {
67
+ const url = typeof r?.url === 'string' ? r.url : '';
68
+ if (!url) continue;
69
+ const title = typeof r?.title === 'string' ? r.title : '';
70
+ const snippet = typeof r?.content === 'string' ? r.content : '';
71
+ results.push({ title, url, snippet });
72
+ }
73
+
74
+ return JSON.stringify({ query, results });
75
+ },
76
+ };
77
+ }
78
+
@@ -0,0 +1,301 @@
1
+ /**
2
+ * MCP Client Manager
3
+ *
4
+ * 管理多个 MCP Server 连接,自动发现工具并转换为 Tool 接口。
5
+ *
6
+ * 职责:
7
+ * 1. 连接管理:根据配置连接/断开 MCP Server
8
+ * 2. 工具发现:通过 MCP 协议自动发现 Server 提供的工具
9
+ * 3. 工具适配:将 MCP 工具转换为我们的 Tool 接口
10
+ * 4. 生命周期:优雅关闭所有连接
11
+ */
12
+
13
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
14
+ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
15
+ import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
16
+ import type { Tool, JsonSchemaObject, JsonSchemaProperty } from '../types';
17
+ import type { McpServerConfig, McpConnectionInfo, McpConnectionStatus } from './types';
18
+ import { DebugLogger } from '../utils';
19
+
20
+ const logger = DebugLogger.module('MCP');
21
+
22
+ /** 单个 MCP Server 连接 */
23
+ interface McpConnection {
24
+ config: McpServerConfig;
25
+ client: Client;
26
+ transport: StdioClientTransport | SSEClientTransport;
27
+ status: McpConnectionStatus;
28
+ tools: Tool[];
29
+ error?: string;
30
+ }
31
+
32
+ /**
33
+ * MCP Client Manager
34
+ *
35
+ * 使用方式:
36
+ * ```typescript
37
+ * const manager = new McpClientManager();
38
+ * await manager.connectAll(configs);
39
+ * const tools = manager.getAllTools(); // 返回 Tool[] 直接注册到 Agent
40
+ * // ... 使用完毕
41
+ * await manager.disconnectAll();
42
+ * ```
43
+ */
44
+ export class McpClientManager {
45
+ private connections = new Map<string, McpConnection>();
46
+
47
+ /**
48
+ * 连接到所有配置的 MCP Server
49
+ *
50
+ * 并行连接,单个失败不影响其他。
51
+ */
52
+ async connectAll(configs: McpServerConfig[]): Promise<void> {
53
+ const results = await Promise.allSettled(
54
+ configs.map(config => this.connect(config))
55
+ );
56
+
57
+ // 记录连接结果
58
+ for (let i = 0; i < results.length; i++) {
59
+ const result = results[i];
60
+ const name = configs[i].name;
61
+ if (result.status === 'rejected') {
62
+ logger.error(`[${name}] 连接失败:`, result.reason);
63
+ }
64
+ }
65
+ }
66
+
67
+ /**
68
+ * 连接单个 MCP Server
69
+ */
70
+ async connect(config: McpServerConfig): Promise<void> {
71
+ const { name } = config;
72
+
73
+ // 如果已连接,先断开
74
+ if (this.connections.has(name)) {
75
+ await this.disconnect(name);
76
+ }
77
+
78
+ logger.info(`[${name}] 正在连接...`);
79
+
80
+ const client = new Client(
81
+ { name: 'ai-chat-mcp-client', version: '1.0.0' },
82
+ );
83
+
84
+ let transport: StdioClientTransport | SSEClientTransport;
85
+
86
+ if (config.transport === 'stdio') {
87
+ if (!config.command) {
88
+ throw new Error(`[${name}] stdio 模式必须指定 command`);
89
+ }
90
+ transport = new StdioClientTransport({
91
+ command: config.command,
92
+ args: config.args,
93
+ env: config.env,
94
+ cwd: config.cwd,
95
+ });
96
+ } else if (config.transport === 'sse') {
97
+ if (!config.url) {
98
+ throw new Error(`[${name}] sse 模式必须指定 url`);
99
+ }
100
+ transport = new SSEClientTransport(new URL(config.url));
101
+ } else {
102
+ throw new Error(`[${name}] 不支持的传输方式: ${config.transport}`);
103
+ }
104
+
105
+ const connection: McpConnection = {
106
+ config,
107
+ client,
108
+ transport,
109
+ status: 'connecting',
110
+ tools: [],
111
+ };
112
+
113
+ this.connections.set(name, connection);
114
+
115
+ try {
116
+ await client.connect(transport);
117
+ connection.status = 'connected';
118
+ logger.info(`[${name}] 已连接`);
119
+
120
+ // 发现工具
121
+ const tools = await this.discoverTools(connection);
122
+ connection.tools = tools;
123
+ logger.info(`[${name}] 发现 ${tools.length} 个工具:`, tools.map(t => t.name));
124
+ } catch (error) {
125
+ connection.status = 'error';
126
+ connection.error = error instanceof Error ? error.message : String(error);
127
+ logger.error(`[${name}] 连接失败:`, connection.error);
128
+ throw error;
129
+ }
130
+ }
131
+
132
+ /**
133
+ * 从 MCP Server 发现工具并转换为 Tool 接口
134
+ */
135
+ private async discoverTools(connection: McpConnection): Promise<Tool[]> {
136
+ const { client, config } = connection;
137
+ const { tools: mcpTools } = await client.listTools();
138
+
139
+ return mcpTools.map(mcpTool => this.adaptTool(mcpTool, config.name, client));
140
+ }
141
+
142
+ /**
143
+ * 将 MCP 工具转换为我们的 Tool 接口
144
+ *
145
+ * 关键转换:
146
+ * - MCP inputSchema → 我们的 parameters (JsonSchemaObject)
147
+ * - MCP callTool() → 我们的 execute()
148
+ */
149
+ private adaptTool(
150
+ mcpTool: { name: string; description?: string; inputSchema: Record<string, unknown> },
151
+ serverName: string,
152
+ client: Client
153
+ ): Tool {
154
+ // 转换 inputSchema 为我们的 JsonSchemaObject
155
+ const parameters = this.convertSchema(mcpTool.inputSchema);
156
+
157
+ return {
158
+ name: mcpTool.name,
159
+ description: mcpTool.description || `MCP tool from ${serverName}`,
160
+ parameters,
161
+ execute: async (args: Record<string, unknown>) => {
162
+ const result = await client.callTool({
163
+ name: mcpTool.name,
164
+ arguments: args,
165
+ });
166
+
167
+ // MCP 返回 content 数组,拼接为字符串
168
+ const texts: string[] = [];
169
+ if (Array.isArray(result.content)) {
170
+ for (const item of result.content) {
171
+ if (typeof item === 'object' && item !== null) {
172
+ const content = item as Record<string, unknown>;
173
+ if (content.type === 'text' && typeof content.text === 'string') {
174
+ texts.push(content.text);
175
+ } else if (content.type === 'image') {
176
+ texts.push(`[image: ${content.mimeType || 'unknown'}]`);
177
+ } else if (content.type === 'resource') {
178
+ texts.push(JSON.stringify(content));
179
+ }
180
+ }
181
+ }
182
+ }
183
+
184
+ return texts.join('\n') || JSON.stringify(result.content);
185
+ },
186
+ };
187
+ }
188
+
189
+ /**
190
+ * 将 MCP inputSchema 转换为 JsonSchemaObject
191
+ */
192
+ private convertSchema(inputSchema: Record<string, unknown>): JsonSchemaObject {
193
+ const properties: Record<string, JsonSchemaProperty> = {};
194
+ const required: string[] = [];
195
+
196
+ const schemaProperties = inputSchema.properties as Record<string, Record<string, unknown>> | undefined;
197
+ if (schemaProperties) {
198
+ for (const [key, value] of Object.entries(schemaProperties)) {
199
+ properties[key] = this.convertProperty(value);
200
+ }
201
+ }
202
+
203
+ if (Array.isArray(inputSchema.required)) {
204
+ required.push(...inputSchema.required.filter((r): r is string => typeof r === 'string'));
205
+ }
206
+
207
+ return {
208
+ type: 'object',
209
+ properties,
210
+ required: required.length > 0 ? required : undefined,
211
+ };
212
+ }
213
+
214
+ /**
215
+ * 递归转换 JSON Schema 属性
216
+ */
217
+ private convertProperty(prop: Record<string, unknown>): JsonSchemaProperty {
218
+ const result: JsonSchemaProperty = {
219
+ type: typeof prop.type === 'string' ? prop.type : 'string',
220
+ };
221
+
222
+ if (typeof prop.description === 'string') {
223
+ result.description = prop.description;
224
+ }
225
+
226
+ if (Array.isArray(prop.enum)) {
227
+ result.enum = prop.enum;
228
+ }
229
+
230
+ // 递归处理 items(数组类型)
231
+ if (prop.items && typeof prop.items === 'object') {
232
+ result.items = this.convertProperty(prop.items as Record<string, unknown>);
233
+ }
234
+
235
+ // 递归处理 properties(对象类型)
236
+ if (prop.properties && typeof prop.properties === 'object') {
237
+ result.properties = {};
238
+ for (const [key, value] of Object.entries(prop.properties as Record<string, Record<string, unknown>>)) {
239
+ result.properties[key] = this.convertProperty(value);
240
+ }
241
+ }
242
+
243
+ if (Array.isArray(prop.required)) {
244
+ result.required = prop.required.filter((r): r is string => typeof r === 'string');
245
+ }
246
+
247
+ return result;
248
+ }
249
+
250
+ /**
251
+ * 获取所有已连接 Server 的工具
252
+ */
253
+ getAllTools(): Tool[] {
254
+ const tools: Tool[] = [];
255
+ for (const connection of this.connections.values()) {
256
+ if (connection.status === 'connected') {
257
+ tools.push(...connection.tools);
258
+ }
259
+ }
260
+ return tools;
261
+ }
262
+
263
+ /**
264
+ * 获取所有连接状态
265
+ */
266
+ getConnectionInfos(): McpConnectionInfo[] {
267
+ return Array.from(this.connections.values()).map(conn => ({
268
+ name: conn.config.name,
269
+ status: conn.status,
270
+ toolCount: conn.tools.length,
271
+ error: conn.error,
272
+ }));
273
+ }
274
+
275
+ /**
276
+ * 断开单个 Server
277
+ */
278
+ async disconnect(name: string): Promise<void> {
279
+ const connection = this.connections.get(name);
280
+ if (!connection) return;
281
+
282
+ try {
283
+ await connection.client.close();
284
+ logger.info(`[${name}] 已断开`);
285
+ } catch (error) {
286
+ logger.error(`[${name}] 断开失败:`, error);
287
+ } finally {
288
+ connection.status = 'disconnected';
289
+ connection.tools = [];
290
+ this.connections.delete(name);
291
+ }
292
+ }
293
+
294
+ /**
295
+ * 断开所有连接
296
+ */
297
+ async disconnectAll(): Promise<void> {
298
+ const names = Array.from(this.connections.keys());
299
+ await Promise.allSettled(names.map(name => this.disconnect(name)));
300
+ }
301
+ }
@@ -0,0 +1,2 @@
1
+ export { McpClientManager } from './client-manager';
2
+ export type { McpServerConfig, McpConnectionStatus, McpConnectionInfo } from './types';