@johpaz/hive 1.1.0
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/CONTRIBUTING.md +44 -0
- package/README.md +310 -0
- package/package.json +96 -0
- package/packages/cli/package.json +28 -0
- package/packages/cli/src/commands/agent-run.ts +168 -0
- package/packages/cli/src/commands/agents.ts +398 -0
- package/packages/cli/src/commands/chat.ts +142 -0
- package/packages/cli/src/commands/config.ts +50 -0
- package/packages/cli/src/commands/cron.ts +161 -0
- package/packages/cli/src/commands/dev.ts +95 -0
- package/packages/cli/src/commands/doctor.ts +133 -0
- package/packages/cli/src/commands/gateway.ts +443 -0
- package/packages/cli/src/commands/logs.ts +57 -0
- package/packages/cli/src/commands/mcp.ts +175 -0
- package/packages/cli/src/commands/message.ts +77 -0
- package/packages/cli/src/commands/onboard.ts +1868 -0
- package/packages/cli/src/commands/security.ts +144 -0
- package/packages/cli/src/commands/service.ts +50 -0
- package/packages/cli/src/commands/sessions.ts +116 -0
- package/packages/cli/src/commands/skills.ts +187 -0
- package/packages/cli/src/commands/update.ts +25 -0
- package/packages/cli/src/index.ts +185 -0
- package/packages/cli/src/utils/token.ts +6 -0
- package/packages/code-bridge/README.md +78 -0
- package/packages/code-bridge/package.json +18 -0
- package/packages/code-bridge/src/index.ts +95 -0
- package/packages/code-bridge/src/process-manager.ts +212 -0
- package/packages/code-bridge/src/schemas.ts +133 -0
- package/packages/core/package.json +46 -0
- package/packages/core/src/agent/agent-loop.ts +369 -0
- package/packages/core/src/agent/compaction.ts +140 -0
- package/packages/core/src/agent/context-compiler.ts +378 -0
- package/packages/core/src/agent/context-guard.ts +91 -0
- package/packages/core/src/agent/context.ts +138 -0
- package/packages/core/src/agent/conversation-store.ts +198 -0
- package/packages/core/src/agent/curator.ts +158 -0
- package/packages/core/src/agent/hooks.ts +166 -0
- package/packages/core/src/agent/index.ts +116 -0
- package/packages/core/src/agent/llm-client.ts +503 -0
- package/packages/core/src/agent/native-tools.ts +505 -0
- package/packages/core/src/agent/prompt-builder.ts +532 -0
- package/packages/core/src/agent/providers/index.ts +167 -0
- package/packages/core/src/agent/providers.ts +1 -0
- package/packages/core/src/agent/reflector.ts +170 -0
- package/packages/core/src/agent/service.ts +64 -0
- package/packages/core/src/agent/stuck-loop.ts +133 -0
- package/packages/core/src/agent/supervisor.ts +39 -0
- package/packages/core/src/agent/tracer.ts +102 -0
- package/packages/core/src/agent/workspace.ts +110 -0
- package/packages/core/src/canvas/canvas-manager.test.ts +161 -0
- package/packages/core/src/canvas/canvas-manager.ts +319 -0
- package/packages/core/src/canvas/canvas-tools.ts +420 -0
- package/packages/core/src/canvas/emitter.ts +115 -0
- package/packages/core/src/canvas/index.ts +2 -0
- package/packages/core/src/channels/base.ts +138 -0
- package/packages/core/src/channels/discord.ts +260 -0
- package/packages/core/src/channels/index.ts +7 -0
- package/packages/core/src/channels/manager.ts +383 -0
- package/packages/core/src/channels/slack.ts +287 -0
- package/packages/core/src/channels/telegram.ts +502 -0
- package/packages/core/src/channels/webchat.ts +128 -0
- package/packages/core/src/channels/whatsapp.ts +375 -0
- package/packages/core/src/config/index.ts +12 -0
- package/packages/core/src/config/loader.ts +529 -0
- package/packages/core/src/events/event-bus.ts +169 -0
- package/packages/core/src/gateway/index.ts +5 -0
- package/packages/core/src/gateway/initializer.ts +290 -0
- package/packages/core/src/gateway/lane-queue.ts +169 -0
- package/packages/core/src/gateway/resolver.ts +108 -0
- package/packages/core/src/gateway/router.ts +124 -0
- package/packages/core/src/gateway/server.ts +3317 -0
- package/packages/core/src/gateway/session.ts +95 -0
- package/packages/core/src/gateway/slash-commands.ts +192 -0
- package/packages/core/src/heartbeat/index.ts +157 -0
- package/packages/core/src/index.ts +19 -0
- package/packages/core/src/integrations/catalog.ts +286 -0
- package/packages/core/src/integrations/env.ts +64 -0
- package/packages/core/src/integrations/index.ts +2 -0
- package/packages/core/src/memory/index.ts +1 -0
- package/packages/core/src/memory/notes.ts +68 -0
- package/packages/core/src/plugins/api.ts +128 -0
- package/packages/core/src/plugins/index.ts +2 -0
- package/packages/core/src/plugins/loader.ts +365 -0
- package/packages/core/src/resilience/circuit-breaker.ts +225 -0
- package/packages/core/src/security/google-chat.ts +269 -0
- package/packages/core/src/security/index.ts +192 -0
- package/packages/core/src/security/pairing.ts +250 -0
- package/packages/core/src/security/rate-limit.ts +270 -0
- package/packages/core/src/security/signal.ts +321 -0
- package/packages/core/src/state/store.ts +312 -0
- package/packages/core/src/storage/bun-sqlite-store.ts +188 -0
- package/packages/core/src/storage/crypto.ts +101 -0
- package/packages/core/src/storage/db-context.ts +333 -0
- package/packages/core/src/storage/onboarding.ts +1087 -0
- package/packages/core/src/storage/schema.ts +541 -0
- package/packages/core/src/storage/seed.ts +571 -0
- package/packages/core/src/storage/sqlite.ts +387 -0
- package/packages/core/src/storage/usage.ts +212 -0
- package/packages/core/src/tools/bridge-events.ts +74 -0
- package/packages/core/src/tools/browser.ts +275 -0
- package/packages/core/src/tools/codebridge.ts +421 -0
- package/packages/core/src/tools/coordinator-tools.ts +179 -0
- package/packages/core/src/tools/cron.ts +611 -0
- package/packages/core/src/tools/exec.ts +140 -0
- package/packages/core/src/tools/fs.ts +364 -0
- package/packages/core/src/tools/index.ts +12 -0
- package/packages/core/src/tools/memory.ts +176 -0
- package/packages/core/src/tools/notify.ts +113 -0
- package/packages/core/src/tools/project-management.ts +376 -0
- package/packages/core/src/tools/project.ts +375 -0
- package/packages/core/src/tools/read.ts +158 -0
- package/packages/core/src/tools/web.ts +436 -0
- package/packages/core/src/tools/workspace.ts +171 -0
- package/packages/core/src/utils/benchmark.ts +80 -0
- package/packages/core/src/utils/crypto.ts +73 -0
- package/packages/core/src/utils/date.ts +42 -0
- package/packages/core/src/utils/index.ts +4 -0
- package/packages/core/src/utils/logger.ts +388 -0
- package/packages/core/src/utils/retry.ts +70 -0
- package/packages/core/src/voice/index.ts +583 -0
- package/packages/core/tsconfig.json +9 -0
- package/packages/mcp/package.json +26 -0
- package/packages/mcp/src/config.ts +13 -0
- package/packages/mcp/src/index.ts +1 -0
- package/packages/mcp/src/logger.ts +42 -0
- package/packages/mcp/src/manager.ts +434 -0
- package/packages/mcp/src/transports/index.ts +67 -0
- package/packages/mcp/src/transports/sse.ts +241 -0
- package/packages/mcp/src/transports/websocket.ts +159 -0
- package/packages/skills/package.json +21 -0
- package/packages/skills/src/bundled/agent_management/SKILL.md +24 -0
- package/packages/skills/src/bundled/browser_automation/SKILL.md +30 -0
- package/packages/skills/src/bundled/context_compact/SKILL.md +35 -0
- package/packages/skills/src/bundled/cron_manager/SKILL.md +52 -0
- package/packages/skills/src/bundled/file_manager/SKILL.md +76 -0
- package/packages/skills/src/bundled/http_client/SKILL.md +24 -0
- package/packages/skills/src/bundled/memory/SKILL.md +42 -0
- package/packages/skills/src/bundled/project_management/SKILL.md +26 -0
- package/packages/skills/src/bundled/shell/SKILL.md +43 -0
- package/packages/skills/src/bundled/system_notify/SKILL.md +52 -0
- package/packages/skills/src/bundled/voice/SKILL.md +25 -0
- package/packages/skills/src/bundled/web_search/SKILL.md +29 -0
- package/packages/skills/src/index.ts +1 -0
- package/packages/skills/src/loader.ts +282 -0
- package/packages/tools/package.json +43 -0
- package/packages/tools/src/browser/browser.test.ts +111 -0
- package/packages/tools/src/browser/index.ts +272 -0
- package/packages/tools/src/canvas/index.ts +220 -0
- package/packages/tools/src/cron/cron.test.ts +164 -0
- package/packages/tools/src/cron/index.ts +304 -0
- package/packages/tools/src/filesystem/filesystem.test.ts +240 -0
- package/packages/tools/src/filesystem/index.ts +379 -0
- package/packages/tools/src/git/index.ts +239 -0
- package/packages/tools/src/index.ts +4 -0
- package/packages/tools/src/shell/detect-env.ts +70 -0
- package/packages/tools/tsconfig.json +9 -0
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
+
import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|
3
|
+
import type { MCPConfig, MCPServerConfig } from "./config";
|
|
4
|
+
import { logger, type LogHandler } from "./logger";
|
|
5
|
+
import * as path from "node:path";
|
|
6
|
+
import {
|
|
7
|
+
createTransport,
|
|
8
|
+
type TransportType,
|
|
9
|
+
} from "./transports/index";
|
|
10
|
+
|
|
11
|
+
export interface MCPTool {
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
inputSchema: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface MCPResource {
|
|
18
|
+
uri: string;
|
|
19
|
+
name: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
mimeType?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface MCPPrompt {
|
|
25
|
+
name: string;
|
|
26
|
+
description?: string;
|
|
27
|
+
arguments?: Array<{
|
|
28
|
+
name: string;
|
|
29
|
+
description?: string;
|
|
30
|
+
required?: boolean;
|
|
31
|
+
}>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface MCPServerState {
|
|
35
|
+
name: string;
|
|
36
|
+
config: MCPServerConfig;
|
|
37
|
+
client: Client | null;
|
|
38
|
+
transport: Transport | null;
|
|
39
|
+
status: "connected" | "disconnected" | "error" | "connecting";
|
|
40
|
+
tools: MCPTool[];
|
|
41
|
+
resources: MCPResource[];
|
|
42
|
+
prompts: MCPPrompt[];
|
|
43
|
+
reconnectAttempts: number;
|
|
44
|
+
lastError?: string; // CORRECCIÓN 3 — guardar el mensaje de error
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export class MCPClientManager {
|
|
48
|
+
private servers: Map<string, MCPServerState> = new Map();
|
|
49
|
+
private config: MCPConfig;
|
|
50
|
+
private log = logger.child("mcp");
|
|
51
|
+
|
|
52
|
+
constructor(config: MCPConfig) {
|
|
53
|
+
this.config = config;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
setLogHandler(handler: LogHandler): void {
|
|
57
|
+
logger.setHandler(handler);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async initialize(): Promise<void> {
|
|
61
|
+
const servers = this.config.servers ?? {};
|
|
62
|
+
|
|
63
|
+
for (const [name, serverConfig] of Object.entries(servers)) {
|
|
64
|
+
if (serverConfig.enabled !== false) {
|
|
65
|
+
this.servers.set(name, {
|
|
66
|
+
name,
|
|
67
|
+
config: serverConfig as MCPServerConfig,
|
|
68
|
+
client: null,
|
|
69
|
+
transport: null,
|
|
70
|
+
status: "disconnected",
|
|
71
|
+
tools: [],
|
|
72
|
+
resources: [],
|
|
73
|
+
prompts: [],
|
|
74
|
+
reconnectAttempts: 0,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
this.log.info(`MCP Client initialized with ${this.servers.size} servers`);
|
|
80
|
+
|
|
81
|
+
// CORRECCIÓN 1 — conectar todos los servers al inicializar
|
|
82
|
+
// initialize() solo registraba los servers pero nunca llamaba connectAll()
|
|
83
|
+
// por eso el log mostraba "initialized with 2 servers" pero nunca conectaba
|
|
84
|
+
await this.connectAll();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async updateConfig(config: MCPConfig): Promise<void> {
|
|
88
|
+
this.config = config;
|
|
89
|
+
const newServers = this.config.servers ?? {};
|
|
90
|
+
|
|
91
|
+
// Eliminar servers que ya no están en la config o fueron deshabilitados
|
|
92
|
+
for (const name of this.servers.keys()) {
|
|
93
|
+
if (!newServers[name] || newServers[name].enabled === false) {
|
|
94
|
+
await this.disconnectServer(name);
|
|
95
|
+
this.servers.delete(name);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Añadir o actualizar servers
|
|
100
|
+
for (const [name, serverConfig] of Object.entries(newServers)) {
|
|
101
|
+
if (serverConfig.enabled !== false) {
|
|
102
|
+
const existing = this.servers.get(name);
|
|
103
|
+
if (existing) {
|
|
104
|
+
const configChanged =
|
|
105
|
+
JSON.stringify(existing.config) !== JSON.stringify(serverConfig);
|
|
106
|
+
if (configChanged) {
|
|
107
|
+
const wasConnected = existing.status === "connected";
|
|
108
|
+
await this.disconnectServer(name);
|
|
109
|
+
existing.config = serverConfig as MCPServerConfig;
|
|
110
|
+
if (wasConnected) {
|
|
111
|
+
await this.connectServer(name).catch((err) => {
|
|
112
|
+
this.log.error(
|
|
113
|
+
`Failed to reconnect ${name} after config update: ${err.message}`
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
// Server nuevo — añadir y conectar inmediatamente
|
|
120
|
+
this.servers.set(name, {
|
|
121
|
+
name,
|
|
122
|
+
config: serverConfig as MCPServerConfig,
|
|
123
|
+
client: null,
|
|
124
|
+
transport: null,
|
|
125
|
+
status: "disconnected",
|
|
126
|
+
tools: [],
|
|
127
|
+
resources: [],
|
|
128
|
+
prompts: [],
|
|
129
|
+
reconnectAttempts: 0,
|
|
130
|
+
});
|
|
131
|
+
// CORRECCIÓN 2 — conectar el server nuevo inmediatamente
|
|
132
|
+
await this.connectServer(name).catch((err) => {
|
|
133
|
+
this.log.error(`Failed to connect new server ${name}: ${err.message}`);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private expandPath(p: string): string {
|
|
141
|
+
if (p.startsWith("~")) {
|
|
142
|
+
return path.join(process.env.HOME ?? "", p.slice(1));
|
|
143
|
+
}
|
|
144
|
+
return p;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private createTransportForServer(state: MCPServerState): Transport {
|
|
148
|
+
const transportType = state.config.transport as TransportType;
|
|
149
|
+
|
|
150
|
+
switch (transportType) {
|
|
151
|
+
case "stdio": {
|
|
152
|
+
const command = state.config.command ?? "npx";
|
|
153
|
+
const args = state.config.args ?? [];
|
|
154
|
+
|
|
155
|
+
const env: Record<string, string> = {};
|
|
156
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
157
|
+
if (value !== undefined) env[key] = value;
|
|
158
|
+
}
|
|
159
|
+
if (state.config.env) {
|
|
160
|
+
for (const [key, value] of Object.entries(state.config.env)) {
|
|
161
|
+
env[key] = this.expandPath(value);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return createTransport({ type: "stdio", stdio: { command, args, env } });
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
case "sse": {
|
|
169
|
+
const url = state.config.url;
|
|
170
|
+
if (!url) throw new Error("SSE transport requires 'url' config");
|
|
171
|
+
return createTransport({
|
|
172
|
+
type: "sse",
|
|
173
|
+
sse: { url, headers: state.config.headers },
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
case "websocket": {
|
|
178
|
+
const url = state.config.url;
|
|
179
|
+
if (!url) throw new Error("WebSocket transport requires 'url' config");
|
|
180
|
+
return createTransport({
|
|
181
|
+
type: "websocket",
|
|
182
|
+
websocket: { url, headers: state.config.headers },
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
default:
|
|
187
|
+
throw new Error(`Unknown transport type: ${transportType}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async connectServer(name: string): Promise<void> {
|
|
192
|
+
const state = this.servers.get(name);
|
|
193
|
+
if (!state) throw new Error(`MCP server not found: ${name}`);
|
|
194
|
+
if (state.status === "connected") return;
|
|
195
|
+
|
|
196
|
+
state.status = "connecting";
|
|
197
|
+
state.lastError = undefined; // limpiar error anterior
|
|
198
|
+
this.log.info(`Connecting to MCP server: ${name}`);
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
const transport = this.createTransportForServer(state);
|
|
202
|
+
|
|
203
|
+
const client = new Client(
|
|
204
|
+
{ name: "hive", version: "0.1.0" },
|
|
205
|
+
{ capabilities: {} }
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
await client.connect(transport);
|
|
209
|
+
|
|
210
|
+
state.client = client;
|
|
211
|
+
state.transport = transport;
|
|
212
|
+
state.status = "connected";
|
|
213
|
+
state.reconnectAttempts = 0;
|
|
214
|
+
|
|
215
|
+
await this.discoverCapabilities(name);
|
|
216
|
+
|
|
217
|
+
this.log.info(`Connected to MCP server: ${name}`, {
|
|
218
|
+
tools: state.tools.length,
|
|
219
|
+
resources: state.resources.length,
|
|
220
|
+
prompts: state.prompts.length,
|
|
221
|
+
});
|
|
222
|
+
} catch (error) {
|
|
223
|
+
state.status = "error";
|
|
224
|
+
// CORRECCIÓN 3 — guardar el mensaje de error para mostrarlo en el dashboard
|
|
225
|
+
state.lastError = (error as Error).message;
|
|
226
|
+
this.log.error(`Failed to connect to MCP server ${name}: ${state.lastError}`);
|
|
227
|
+
throw error;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
private async discoverCapabilities(name: string): Promise<void> {
|
|
232
|
+
const state = this.servers.get(name);
|
|
233
|
+
if (!state?.client) return;
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
const toolsResult = await state.client.listTools();
|
|
237
|
+
state.tools = (toolsResult.tools ?? []).map((t) => ({
|
|
238
|
+
name: t.name,
|
|
239
|
+
description: t.description ?? "",
|
|
240
|
+
inputSchema: t.inputSchema as Record<string, unknown>,
|
|
241
|
+
}));
|
|
242
|
+
} catch {
|
|
243
|
+
this.log.debug(`No tools from MCP server: ${name}`);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
try {
|
|
247
|
+
const resourcesResult = await state.client.listResources();
|
|
248
|
+
state.resources = (resourcesResult.resources ?? []).map((r) => ({
|
|
249
|
+
uri: r.uri,
|
|
250
|
+
name: r.name,
|
|
251
|
+
description: r.description,
|
|
252
|
+
mimeType: r.mimeType,
|
|
253
|
+
}));
|
|
254
|
+
} catch {
|
|
255
|
+
this.log.debug(`No resources from MCP server: ${name}`);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
const promptsResult = await state.client.listPrompts();
|
|
260
|
+
state.prompts = (promptsResult.prompts ?? []).map((p) => ({
|
|
261
|
+
name: p.name,
|
|
262
|
+
description: p.description,
|
|
263
|
+
arguments: p.arguments,
|
|
264
|
+
}));
|
|
265
|
+
} catch {
|
|
266
|
+
this.log.debug(`No prompts from MCP server: ${name}`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async disconnectServer(name: string): Promise<void> {
|
|
271
|
+
const state = this.servers.get(name);
|
|
272
|
+
if (!state) return;
|
|
273
|
+
|
|
274
|
+
if (state.client) {
|
|
275
|
+
try {
|
|
276
|
+
await state.client.close();
|
|
277
|
+
} catch {
|
|
278
|
+
// Ignorar errores al cerrar
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
state.client = null;
|
|
283
|
+
state.transport = null;
|
|
284
|
+
state.status = "disconnected";
|
|
285
|
+
state.lastError = undefined;
|
|
286
|
+
|
|
287
|
+
this.log.info(`Disconnected from MCP server: ${name}`);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
async callTool(
|
|
291
|
+
serverName: string,
|
|
292
|
+
toolName: string,
|
|
293
|
+
args: Record<string, unknown>
|
|
294
|
+
): Promise<unknown> {
|
|
295
|
+
const state = this.servers.get(serverName);
|
|
296
|
+
if (!state?.client) {
|
|
297
|
+
throw new Error(`MCP server not connected: ${serverName}`);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
this.log.debug(`Calling MCP tool: ${serverName}/${toolName}`, { args });
|
|
301
|
+
|
|
302
|
+
const result = await state.client.callTool({
|
|
303
|
+
name: toolName,
|
|
304
|
+
arguments: args,
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
return result.content;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
async readResource(serverName: string, uri: string): Promise<unknown> {
|
|
311
|
+
const state = this.servers.get(serverName);
|
|
312
|
+
if (!state?.client) {
|
|
313
|
+
throw new Error(`MCP server not connected: ${serverName}`);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const result = await state.client.readResource({ uri });
|
|
317
|
+
return result.contents;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
getServerStatus(name: string): MCPServerState["status"] | undefined {
|
|
321
|
+
return this.servers.get(name)?.status;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
getServerTools(name: string): MCPTool[] {
|
|
325
|
+
return this.servers.get(name)?.tools ?? [];
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
getServerResources(name: string): MCPResource[] {
|
|
329
|
+
return this.servers.get(name)?.resources ?? [];
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
getAllTools(): Map<string, MCPTool[]> {
|
|
333
|
+
const result = new Map<string, MCPTool[]>();
|
|
334
|
+
for (const [name, state] of this.servers) {
|
|
335
|
+
if (state.status === "connected") {
|
|
336
|
+
result.set(name, state.tools);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return result;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
listServers(): Array<{
|
|
343
|
+
name: string;
|
|
344
|
+
status: string;
|
|
345
|
+
tools: MCPTool[];
|
|
346
|
+
resources: MCPResource[];
|
|
347
|
+
prompts: MCPPrompt[];
|
|
348
|
+
url?: string;
|
|
349
|
+
error?: string;
|
|
350
|
+
}> {
|
|
351
|
+
return Array.from(this.servers.values()).map((s) => ({
|
|
352
|
+
name: s.name,
|
|
353
|
+
status: s.status,
|
|
354
|
+
tools: s.tools,
|
|
355
|
+
resources: s.resources,
|
|
356
|
+
prompts: s.prompts,
|
|
357
|
+
url: s.config.transport === "stdio" ? `${s.config.command} ${s.config.args?.join(" ")}` : s.config.url,
|
|
358
|
+
error: s.lastError,
|
|
359
|
+
}));
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
getServerDetails(
|
|
363
|
+
name: string
|
|
364
|
+
):
|
|
365
|
+
| {
|
|
366
|
+
name: string;
|
|
367
|
+
status: string;
|
|
368
|
+
tools: MCPTool[];
|
|
369
|
+
resources: MCPResource[];
|
|
370
|
+
prompts: MCPPrompt[];
|
|
371
|
+
config: MCPServerConfig;
|
|
372
|
+
error?: string;
|
|
373
|
+
}
|
|
374
|
+
| undefined {
|
|
375
|
+
const s = this.servers.get(name);
|
|
376
|
+
if (!s) return undefined;
|
|
377
|
+
|
|
378
|
+
// CORRECCIÓN 4 — redactar headers con tokens antes de exponer al dashboard
|
|
379
|
+
const safeConfig: MCPServerConfig = {
|
|
380
|
+
...s.config,
|
|
381
|
+
headers: s.config.headers
|
|
382
|
+
? Object.fromEntries(
|
|
383
|
+
Object.entries(s.config.headers).map(([k, v]) => [
|
|
384
|
+
k,
|
|
385
|
+
k.toLowerCase().includes("auth") ||
|
|
386
|
+
k.toLowerCase().includes("token") ||
|
|
387
|
+
k.toLowerCase().includes("key")
|
|
388
|
+
? `${(v as string).slice(0, 4)}••••••••`
|
|
389
|
+
: v,
|
|
390
|
+
])
|
|
391
|
+
)
|
|
392
|
+
: undefined,
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
return {
|
|
396
|
+
name: s.name,
|
|
397
|
+
status: s.status,
|
|
398
|
+
tools: s.tools,
|
|
399
|
+
resources: s.resources,
|
|
400
|
+
prompts: s.prompts,
|
|
401
|
+
config: safeConfig,
|
|
402
|
+
error: s.lastError,
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
async connectAll(): Promise<void> {
|
|
407
|
+
const promises: Promise<void>[] = [];
|
|
408
|
+
|
|
409
|
+
for (const name of this.servers.keys()) {
|
|
410
|
+
promises.push(
|
|
411
|
+
this.connectServer(name).catch((error) => {
|
|
412
|
+
// No relanzar — el Gateway sigue funcionando sin ese server
|
|
413
|
+
this.log.error(`Failed to connect ${name}: ${error.message}`);
|
|
414
|
+
})
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
await Promise.allSettled(promises);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
async disconnectAll(): Promise<void> {
|
|
422
|
+
const promises: Promise<void>[] = [];
|
|
423
|
+
|
|
424
|
+
for (const name of this.servers.keys()) {
|
|
425
|
+
promises.push(this.disconnectServer(name));
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
await Promise.allSettled(promises);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
export function createMCPManager(config: MCPConfig): MCPClientManager {
|
|
433
|
+
return new MCPClientManager(config);
|
|
434
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
2
|
+
import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|
3
|
+
|
|
4
|
+
// CORRECCIÓN 1 — quitar extensión .ts de los imports
|
|
5
|
+
// Bun resuelve los módulos sin extensión correctamente
|
|
6
|
+
// Con .ts puede fallar en algunos contextos de build/bundle
|
|
7
|
+
import { SSETransport, type SSETransportConfig } from "./sse";
|
|
8
|
+
import { WebSocketTransport, type WebSocketTransportConfig } from "./websocket";
|
|
9
|
+
|
|
10
|
+
export { SSETransport, type SSETransportConfig };
|
|
11
|
+
export { WebSocketTransport, type WebSocketTransportConfig };
|
|
12
|
+
|
|
13
|
+
// CORRECCIÓN 2 — exportar StdioTransportConfig
|
|
14
|
+
// Estaba definido pero no exportado — el resto del código no puede importarlo
|
|
15
|
+
export interface StdioTransportConfig {
|
|
16
|
+
command: string;
|
|
17
|
+
args?: string[];
|
|
18
|
+
env?: Record<string, string>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type TransportType = "stdio" | "sse" | "websocket";
|
|
22
|
+
|
|
23
|
+
export interface TransportOptions {
|
|
24
|
+
type: TransportType;
|
|
25
|
+
stdio?: StdioTransportConfig;
|
|
26
|
+
sse?: SSETransportConfig;
|
|
27
|
+
websocket?: WebSocketTransportConfig;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function createTransport(options: TransportOptions): Transport {
|
|
31
|
+
switch (options.type) {
|
|
32
|
+
case "stdio": {
|
|
33
|
+
if (!options.stdio) {
|
|
34
|
+
throw new Error("stdio config required for stdio transport");
|
|
35
|
+
}
|
|
36
|
+
return new StdioClientTransport({
|
|
37
|
+
command: options.stdio.command,
|
|
38
|
+
args: options.stdio.args ?? [],
|
|
39
|
+
env: options.stdio.env ?? (process.env as Record<string, string>),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
case "sse": {
|
|
44
|
+
if (!options.sse) {
|
|
45
|
+
throw new Error("sse config required for SSE transport");
|
|
46
|
+
}
|
|
47
|
+
// CORRECCIÓN 3 — sin cast as unknown as Transport
|
|
48
|
+
// SSETransport ahora implementa Transport directamente (implements Transport)
|
|
49
|
+
// el cast doble era señal de que el tipo no estaba bien declarado en la clase
|
|
50
|
+
return new SSETransport(options.sse);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
case "websocket": {
|
|
54
|
+
if (!options.websocket) {
|
|
55
|
+
throw new Error("websocket config required for WebSocket transport");
|
|
56
|
+
}
|
|
57
|
+
// Igual — WebSocketTransport ahora implementa Transport directamente
|
|
58
|
+
return new WebSocketTransport(options.websocket);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
default: {
|
|
62
|
+
// exhaustive check — TypeScript avisa si falta un caso
|
|
63
|
+
const _exhaustive: never = options.type;
|
|
64
|
+
throw new Error(`Unknown transport type: ${_exhaustive}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|