whale-code 6.4.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/README.md +95 -0
- package/bin/swag-agent.js +9 -0
- package/bin/swagmanager-mcp.js +321 -0
- package/dist/cli/app.d.ts +26 -0
- package/dist/cli/app.js +64 -0
- package/dist/cli/chat/AgentSelector.d.ts +14 -0
- package/dist/cli/chat/AgentSelector.js +14 -0
- package/dist/cli/chat/ChatApp.d.ts +9 -0
- package/dist/cli/chat/ChatApp.js +267 -0
- package/dist/cli/chat/ChatInput.d.ts +39 -0
- package/dist/cli/chat/ChatInput.js +509 -0
- package/dist/cli/chat/MarkdownText.d.ts +10 -0
- package/dist/cli/chat/MarkdownText.js +20 -0
- package/dist/cli/chat/MessageList.d.ts +37 -0
- package/dist/cli/chat/MessageList.js +80 -0
- package/dist/cli/chat/ModelSelector.d.ts +20 -0
- package/dist/cli/chat/ModelSelector.js +73 -0
- package/dist/cli/chat/RewindViewer.d.ts +26 -0
- package/dist/cli/chat/RewindViewer.js +185 -0
- package/dist/cli/chat/StoreSelector.d.ts +14 -0
- package/dist/cli/chat/StoreSelector.js +24 -0
- package/dist/cli/chat/StreamingText.d.ts +12 -0
- package/dist/cli/chat/StreamingText.js +12 -0
- package/dist/cli/chat/SubagentPanel.d.ts +45 -0
- package/dist/cli/chat/SubagentPanel.js +110 -0
- package/dist/cli/chat/TeamPanel.d.ts +21 -0
- package/dist/cli/chat/TeamPanel.js +42 -0
- package/dist/cli/chat/ToolIndicator.d.ts +25 -0
- package/dist/cli/chat/ToolIndicator.js +436 -0
- package/dist/cli/chat/hooks/useAgentLoop.d.ts +39 -0
- package/dist/cli/chat/hooks/useAgentLoop.js +382 -0
- package/dist/cli/chat/hooks/useSlashCommands.d.ts +37 -0
- package/dist/cli/chat/hooks/useSlashCommands.js +387 -0
- package/dist/cli/commands/config-cmd.d.ts +10 -0
- package/dist/cli/commands/config-cmd.js +99 -0
- package/dist/cli/commands/doctor.d.ts +14 -0
- package/dist/cli/commands/doctor.js +172 -0
- package/dist/cli/commands/init.d.ts +16 -0
- package/dist/cli/commands/init.js +278 -0
- package/dist/cli/commands/mcp.d.ts +12 -0
- package/dist/cli/commands/mcp.js +162 -0
- package/dist/cli/login/LoginApp.d.ts +7 -0
- package/dist/cli/login/LoginApp.js +157 -0
- package/dist/cli/print-mode.d.ts +31 -0
- package/dist/cli/print-mode.js +202 -0
- package/dist/cli/serve-mode.d.ts +37 -0
- package/dist/cli/serve-mode.js +636 -0
- package/dist/cli/services/agent-definitions.d.ts +25 -0
- package/dist/cli/services/agent-definitions.js +91 -0
- package/dist/cli/services/agent-events.d.ts +178 -0
- package/dist/cli/services/agent-events.js +175 -0
- package/dist/cli/services/agent-loop.d.ts +90 -0
- package/dist/cli/services/agent-loop.js +762 -0
- package/dist/cli/services/agent-worker-base.d.ts +97 -0
- package/dist/cli/services/agent-worker-base.js +220 -0
- package/dist/cli/services/auth-service.d.ts +30 -0
- package/dist/cli/services/auth-service.js +160 -0
- package/dist/cli/services/background-processes.d.ts +126 -0
- package/dist/cli/services/background-processes.js +318 -0
- package/dist/cli/services/browser-auth.d.ts +24 -0
- package/dist/cli/services/browser-auth.js +180 -0
- package/dist/cli/services/claude-md-loader.d.ts +16 -0
- package/dist/cli/services/claude-md-loader.js +58 -0
- package/dist/cli/services/config-store.d.ts +47 -0
- package/dist/cli/services/config-store.js +79 -0
- package/dist/cli/services/debug-log.d.ts +10 -0
- package/dist/cli/services/debug-log.js +52 -0
- package/dist/cli/services/error-logger.d.ts +58 -0
- package/dist/cli/services/error-logger.js +269 -0
- package/dist/cli/services/file-history.d.ts +21 -0
- package/dist/cli/services/file-history.js +83 -0
- package/dist/cli/services/format-server-response.d.ts +16 -0
- package/dist/cli/services/format-server-response.js +440 -0
- package/dist/cli/services/git-context.d.ts +11 -0
- package/dist/cli/services/git-context.js +66 -0
- package/dist/cli/services/hooks.d.ts +85 -0
- package/dist/cli/services/hooks.js +258 -0
- package/dist/cli/services/interactive-tools.d.ts +125 -0
- package/dist/cli/services/interactive-tools.js +260 -0
- package/dist/cli/services/keybinding-manager.d.ts +52 -0
- package/dist/cli/services/keybinding-manager.js +115 -0
- package/dist/cli/services/local-tools.d.ts +22 -0
- package/dist/cli/services/local-tools.js +697 -0
- package/dist/cli/services/lsp-manager.d.ts +18 -0
- package/dist/cli/services/lsp-manager.js +717 -0
- package/dist/cli/services/mcp-client.d.ts +48 -0
- package/dist/cli/services/mcp-client.js +157 -0
- package/dist/cli/services/memory-manager.d.ts +16 -0
- package/dist/cli/services/memory-manager.js +57 -0
- package/dist/cli/services/model-manager.d.ts +18 -0
- package/dist/cli/services/model-manager.js +71 -0
- package/dist/cli/services/model-router.d.ts +26 -0
- package/dist/cli/services/model-router.js +149 -0
- package/dist/cli/services/permission-modes.d.ts +13 -0
- package/dist/cli/services/permission-modes.js +43 -0
- package/dist/cli/services/rewind.d.ts +84 -0
- package/dist/cli/services/rewind.js +194 -0
- package/dist/cli/services/ripgrep.d.ts +28 -0
- package/dist/cli/services/ripgrep.js +138 -0
- package/dist/cli/services/sandbox.d.ts +29 -0
- package/dist/cli/services/sandbox.js +97 -0
- package/dist/cli/services/server-tools.d.ts +61 -0
- package/dist/cli/services/server-tools.js +543 -0
- package/dist/cli/services/session-persistence.d.ts +23 -0
- package/dist/cli/services/session-persistence.js +99 -0
- package/dist/cli/services/subagent-worker.d.ts +19 -0
- package/dist/cli/services/subagent-worker.js +41 -0
- package/dist/cli/services/subagent.d.ts +47 -0
- package/dist/cli/services/subagent.js +647 -0
- package/dist/cli/services/system-prompt.d.ts +7 -0
- package/dist/cli/services/system-prompt.js +198 -0
- package/dist/cli/services/team-lead.d.ts +73 -0
- package/dist/cli/services/team-lead.js +512 -0
- package/dist/cli/services/team-state.d.ts +77 -0
- package/dist/cli/services/team-state.js +398 -0
- package/dist/cli/services/teammate.d.ts +31 -0
- package/dist/cli/services/teammate.js +689 -0
- package/dist/cli/services/telemetry.d.ts +61 -0
- package/dist/cli/services/telemetry.js +209 -0
- package/dist/cli/services/tools/agent-tools.d.ts +14 -0
- package/dist/cli/services/tools/agent-tools.js +347 -0
- package/dist/cli/services/tools/file-ops.d.ts +15 -0
- package/dist/cli/services/tools/file-ops.js +487 -0
- package/dist/cli/services/tools/search-tools.d.ts +8 -0
- package/dist/cli/services/tools/search-tools.js +186 -0
- package/dist/cli/services/tools/shell-exec.d.ts +10 -0
- package/dist/cli/services/tools/shell-exec.js +168 -0
- package/dist/cli/services/tools/task-manager.d.ts +28 -0
- package/dist/cli/services/tools/task-manager.js +209 -0
- package/dist/cli/services/tools/web-tools.d.ts +11 -0
- package/dist/cli/services/tools/web-tools.js +395 -0
- package/dist/cli/setup/SetupApp.d.ts +9 -0
- package/dist/cli/setup/SetupApp.js +191 -0
- package/dist/cli/shared/MatrixIntro.d.ts +4 -0
- package/dist/cli/shared/MatrixIntro.js +83 -0
- package/dist/cli/shared/Theme.d.ts +74 -0
- package/dist/cli/shared/Theme.js +127 -0
- package/dist/cli/shared/WhaleBanner.d.ts +10 -0
- package/dist/cli/shared/WhaleBanner.js +12 -0
- package/dist/cli/shared/markdown.d.ts +21 -0
- package/dist/cli/shared/markdown.js +756 -0
- package/dist/cli/status/StatusApp.d.ts +4 -0
- package/dist/cli/status/StatusApp.js +105 -0
- package/dist/cli/stores/StoreApp.d.ts +7 -0
- package/dist/cli/stores/StoreApp.js +81 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +538 -0
- package/dist/local-agent/connection.d.ts +48 -0
- package/dist/local-agent/connection.js +332 -0
- package/dist/local-agent/discovery.d.ts +18 -0
- package/dist/local-agent/discovery.js +146 -0
- package/dist/local-agent/executor.d.ts +34 -0
- package/dist/local-agent/executor.js +241 -0
- package/dist/local-agent/index.d.ts +14 -0
- package/dist/local-agent/index.js +198 -0
- package/dist/node/adapters/base.d.ts +35 -0
- package/dist/node/adapters/base.js +10 -0
- package/dist/node/adapters/discord.d.ts +29 -0
- package/dist/node/adapters/discord.js +299 -0
- package/dist/node/adapters/email.d.ts +23 -0
- package/dist/node/adapters/email.js +218 -0
- package/dist/node/adapters/imessage.d.ts +17 -0
- package/dist/node/adapters/imessage.js +118 -0
- package/dist/node/adapters/slack.d.ts +26 -0
- package/dist/node/adapters/slack.js +259 -0
- package/dist/node/adapters/sms.d.ts +23 -0
- package/dist/node/adapters/sms.js +161 -0
- package/dist/node/adapters/telegram.d.ts +17 -0
- package/dist/node/adapters/telegram.js +101 -0
- package/dist/node/adapters/webchat.d.ts +27 -0
- package/dist/node/adapters/webchat.js +160 -0
- package/dist/node/adapters/whatsapp.d.ts +28 -0
- package/dist/node/adapters/whatsapp.js +230 -0
- package/dist/node/cli.d.ts +2 -0
- package/dist/node/cli.js +325 -0
- package/dist/node/config.d.ts +17 -0
- package/dist/node/config.js +31 -0
- package/dist/node/runtime.d.ts +50 -0
- package/dist/node/runtime.js +351 -0
- package/dist/server/handlers/__test-utils__/mock-supabase.d.ts +11 -0
- package/dist/server/handlers/__test-utils__/mock-supabase.js +393 -0
- package/dist/server/handlers/analytics.d.ts +17 -0
- package/dist/server/handlers/analytics.js +266 -0
- package/dist/server/handlers/api-keys.d.ts +6 -0
- package/dist/server/handlers/api-keys.js +221 -0
- package/dist/server/handlers/billing.d.ts +33 -0
- package/dist/server/handlers/billing.js +272 -0
- package/dist/server/handlers/browser.d.ts +10 -0
- package/dist/server/handlers/browser.js +517 -0
- package/dist/server/handlers/catalog.d.ts +99 -0
- package/dist/server/handlers/catalog.js +976 -0
- package/dist/server/handlers/comms.d.ts +254 -0
- package/dist/server/handlers/comms.js +588 -0
- package/dist/server/handlers/creations.d.ts +6 -0
- package/dist/server/handlers/creations.js +479 -0
- package/dist/server/handlers/crm.d.ts +89 -0
- package/dist/server/handlers/crm.js +538 -0
- package/dist/server/handlers/discovery.d.ts +6 -0
- package/dist/server/handlers/discovery.js +288 -0
- package/dist/server/handlers/embeddings.d.ts +92 -0
- package/dist/server/handlers/embeddings.js +197 -0
- package/dist/server/handlers/enrichment.d.ts +8 -0
- package/dist/server/handlers/enrichment.js +768 -0
- package/dist/server/handlers/image-gen.d.ts +6 -0
- package/dist/server/handlers/image-gen.js +409 -0
- package/dist/server/handlers/inventory.d.ts +319 -0
- package/dist/server/handlers/inventory.js +447 -0
- package/dist/server/handlers/kali.d.ts +10 -0
- package/dist/server/handlers/kali.js +210 -0
- package/dist/server/handlers/llm-providers.d.ts +6 -0
- package/dist/server/handlers/llm-providers.js +673 -0
- package/dist/server/handlers/local-agent.d.ts +6 -0
- package/dist/server/handlers/local-agent.js +118 -0
- package/dist/server/handlers/meta-ads.d.ts +111 -0
- package/dist/server/handlers/meta-ads.js +2279 -0
- package/dist/server/handlers/nodes.d.ts +33 -0
- package/dist/server/handlers/nodes.js +699 -0
- package/dist/server/handlers/operations.d.ts +138 -0
- package/dist/server/handlers/operations.js +131 -0
- package/dist/server/handlers/platform.d.ts +23 -0
- package/dist/server/handlers/platform.js +227 -0
- package/dist/server/handlers/supply-chain.d.ts +19 -0
- package/dist/server/handlers/supply-chain.js +327 -0
- package/dist/server/handlers/transcription.d.ts +17 -0
- package/dist/server/handlers/transcription.js +121 -0
- package/dist/server/handlers/video-gen.d.ts +6 -0
- package/dist/server/handlers/video-gen.js +466 -0
- package/dist/server/handlers/voice.d.ts +8 -0
- package/dist/server/handlers/voice.js +1146 -0
- package/dist/server/handlers/workflow-steps.d.ts +86 -0
- package/dist/server/handlers/workflow-steps.js +2349 -0
- package/dist/server/handlers/workflows.d.ts +7 -0
- package/dist/server/handlers/workflows.js +989 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +2427 -0
- package/dist/server/lib/batch-client.d.ts +80 -0
- package/dist/server/lib/batch-client.js +467 -0
- package/dist/server/lib/code-worker-pool.d.ts +31 -0
- package/dist/server/lib/code-worker-pool.js +224 -0
- package/dist/server/lib/code-worker.d.ts +1 -0
- package/dist/server/lib/code-worker.js +188 -0
- package/dist/server/lib/compaction-service.d.ts +32 -0
- package/dist/server/lib/compaction-service.js +162 -0
- package/dist/server/lib/logger.d.ts +19 -0
- package/dist/server/lib/logger.js +46 -0
- package/dist/server/lib/otel.d.ts +38 -0
- package/dist/server/lib/otel.js +126 -0
- package/dist/server/lib/pg-rate-limiter.d.ts +21 -0
- package/dist/server/lib/pg-rate-limiter.js +86 -0
- package/dist/server/lib/prompt-sanitizer.d.ts +37 -0
- package/dist/server/lib/prompt-sanitizer.js +177 -0
- package/dist/server/lib/provider-capabilities.d.ts +85 -0
- package/dist/server/lib/provider-capabilities.js +190 -0
- package/dist/server/lib/provider-failover.d.ts +74 -0
- package/dist/server/lib/provider-failover.js +210 -0
- package/dist/server/lib/rate-limiter.d.ts +39 -0
- package/dist/server/lib/rate-limiter.js +147 -0
- package/dist/server/lib/server-agent-loop.d.ts +107 -0
- package/dist/server/lib/server-agent-loop.js +667 -0
- package/dist/server/lib/server-subagent.d.ts +78 -0
- package/dist/server/lib/server-subagent.js +203 -0
- package/dist/server/lib/session-checkpoint.d.ts +51 -0
- package/dist/server/lib/session-checkpoint.js +145 -0
- package/dist/server/lib/ssrf-guard.d.ts +13 -0
- package/dist/server/lib/ssrf-guard.js +240 -0
- package/dist/server/lib/supabase-client.d.ts +7 -0
- package/dist/server/lib/supabase-client.js +78 -0
- package/dist/server/lib/template-resolver.d.ts +31 -0
- package/dist/server/lib/template-resolver.js +215 -0
- package/dist/server/lib/utils.d.ts +16 -0
- package/dist/server/lib/utils.js +147 -0
- package/dist/server/local-agent-gateway.d.ts +82 -0
- package/dist/server/local-agent-gateway.js +426 -0
- package/dist/server/providers/anthropic.d.ts +20 -0
- package/dist/server/providers/anthropic.js +199 -0
- package/dist/server/providers/bedrock.d.ts +20 -0
- package/dist/server/providers/bedrock.js +194 -0
- package/dist/server/providers/gemini.d.ts +24 -0
- package/dist/server/providers/gemini.js +486 -0
- package/dist/server/providers/openai.d.ts +24 -0
- package/dist/server/providers/openai.js +522 -0
- package/dist/server/providers/registry.d.ts +32 -0
- package/dist/server/providers/registry.js +58 -0
- package/dist/server/providers/shared.d.ts +32 -0
- package/dist/server/providers/shared.js +124 -0
- package/dist/server/providers/types.d.ts +92 -0
- package/dist/server/providers/types.js +12 -0
- package/dist/server/proxy-handlers.d.ts +6 -0
- package/dist/server/proxy-handlers.js +89 -0
- package/dist/server/tool-router.d.ts +149 -0
- package/dist/server/tool-router.js +803 -0
- package/dist/server/validation.d.ts +24 -0
- package/dist/server/validation.js +301 -0
- package/dist/server/worker.d.ts +19 -0
- package/dist/server/worker.js +201 -0
- package/dist/setup.d.ts +8 -0
- package/dist/setup.js +181 -0
- package/dist/shared/agent-core.d.ts +157 -0
- package/dist/shared/agent-core.js +534 -0
- package/dist/shared/anthropic-types.d.ts +105 -0
- package/dist/shared/anthropic-types.js +7 -0
- package/dist/shared/api-client.d.ts +90 -0
- package/dist/shared/api-client.js +379 -0
- package/dist/shared/constants.d.ts +33 -0
- package/dist/shared/constants.js +80 -0
- package/dist/shared/sse-parser.d.ts +26 -0
- package/dist/shared/sse-parser.js +259 -0
- package/dist/shared/tool-dispatch.d.ts +52 -0
- package/dist/shared/tool-dispatch.js +191 -0
- package/dist/shared/types.d.ts +72 -0
- package/dist/shared/types.js +7 -0
- package/dist/updater.d.ts +25 -0
- package/dist/updater.js +140 -0
- package/dist/webchat/widget.d.ts +0 -0
- package/dist/webchat/widget.js +397 -0
- package/package.json +95 -0
- package/src/cli/services/builtin-skills/commit.md +19 -0
- package/src/cli/services/builtin-skills/review-pr.md +21 -0
- package/src/cli/services/builtin-skills/review.md +18 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* SwagManager MCP Server
|
|
4
|
+
*
|
|
5
|
+
* Thin proxy that connects any MCP client (Claude Code, Cursor, etc.)
|
|
6
|
+
* to the SwagManager platform.
|
|
7
|
+
*
|
|
8
|
+
* - Tool DEFINITIONS loaded from ai_tool_registry (database-driven)
|
|
9
|
+
* - Tool EXECUTION proxied to the Fly.io server (server-driven)
|
|
10
|
+
* - Auth via browser-based OAuth (`whale_login` tool) or env vars
|
|
11
|
+
*
|
|
12
|
+
* When tools change on the server, this MCP server automatically picks
|
|
13
|
+
* them up — no code changes, no rebuild, no redeploy.
|
|
14
|
+
*/
|
|
15
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
16
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
17
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
18
|
+
import { createClient } from "@supabase/supabase-js";
|
|
19
|
+
import { createRequire } from "module";
|
|
20
|
+
import { startUpdateLoop } from "./updater.js";
|
|
21
|
+
import { resolveConfig, loadConfig, updateConfig } from "./cli/services/config-store.js";
|
|
22
|
+
import { getValidToken, getStoresForUser, selectStore } from "./cli/services/auth-service.js";
|
|
23
|
+
import { signInWithBrowser } from "./cli/services/browser-auth.js";
|
|
24
|
+
import { LocalAgentConnection } from "./local-agent/connection.js";
|
|
25
|
+
import { initErrorLogger, setErrorLoggerUser, captureError } from "./cli/services/error-logger.js";
|
|
26
|
+
const require = createRequire(import.meta.url);
|
|
27
|
+
const PKG_VERSION = require("../package.json").version;
|
|
28
|
+
// ============================================================================
|
|
29
|
+
// CONFIGURATION — env vars → ~/.swagmanager/config.json (set by `whale login`)
|
|
30
|
+
// ============================================================================
|
|
31
|
+
let config = resolveConfig();
|
|
32
|
+
let rawConfig = loadConfig();
|
|
33
|
+
const SUPABASE_URL = config.supabaseUrl;
|
|
34
|
+
let INITIAL_SUPABASE_KEY = config.supabaseKey;
|
|
35
|
+
let STORE_ID = config.storeId;
|
|
36
|
+
const IS_SERVICE_ROLE = !!process.env.SUPABASE_SERVICE_ROLE_KEY;
|
|
37
|
+
// User identity — loaded from config (set by `whale login`)
|
|
38
|
+
let USER_ID = rawConfig.user_id || null;
|
|
39
|
+
let USER_EMAIL = rawConfig.email || null;
|
|
40
|
+
// ============================================================================
|
|
41
|
+
// AUTH STATE — tracks whether we can make authenticated API calls
|
|
42
|
+
// ============================================================================
|
|
43
|
+
let isAuthenticated = !!(SUPABASE_URL && INITIAL_SUPABASE_KEY);
|
|
44
|
+
let supabase = null;
|
|
45
|
+
if (isAuthenticated) {
|
|
46
|
+
supabase = createClient(SUPABASE_URL, INITIAL_SUPABASE_KEY);
|
|
47
|
+
if (IS_SERVICE_ROLE && !USER_ID) {
|
|
48
|
+
console.error("[MCP] WARNING: Using service role key without user login. Tools will work but store scoping is disabled.");
|
|
49
|
+
console.error("[MCP] For proper store-scoped access, use the whale_login tool or run `whale login` first.");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
console.error("[MCP] Not authenticated. Use the whale_login tool to sign in, or run `whale login` in a terminal.");
|
|
54
|
+
}
|
|
55
|
+
/** Reload config from disk and update in-memory state */
|
|
56
|
+
function reloadConfig() {
|
|
57
|
+
config = resolveConfig();
|
|
58
|
+
rawConfig = loadConfig();
|
|
59
|
+
INITIAL_SUPABASE_KEY = config.supabaseKey;
|
|
60
|
+
STORE_ID = config.storeId;
|
|
61
|
+
USER_ID = rawConfig.user_id || null;
|
|
62
|
+
USER_EMAIL = rawConfig.email || null;
|
|
63
|
+
if (config.supabaseUrl && config.supabaseKey) {
|
|
64
|
+
supabase = createClient(config.supabaseUrl, config.supabaseKey);
|
|
65
|
+
isAuthenticated = true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/** Get a fresh auth token — service role keys are static, JWTs auto-refresh */
|
|
69
|
+
async function getAuthToken() {
|
|
70
|
+
if (IS_SERVICE_ROLE)
|
|
71
|
+
return INITIAL_SUPABASE_KEY;
|
|
72
|
+
// JWT user — refresh if needed
|
|
73
|
+
const token = await getValidToken();
|
|
74
|
+
return token || INITIAL_SUPABASE_KEY;
|
|
75
|
+
}
|
|
76
|
+
// Fly.io server URL for tool execution
|
|
77
|
+
const SERVER_URL = config.serverUrl;
|
|
78
|
+
// Session ID for tracing — links all tool calls in one conversation
|
|
79
|
+
const SESSION_ID = crypto.randomUUID();
|
|
80
|
+
const WHALE_LOGIN_TOOL = {
|
|
81
|
+
name: "whale_login",
|
|
82
|
+
description: "Log in to your WhaleTools account. Opens a browser for secure authentication. Use this when you see 'not authenticated' errors.",
|
|
83
|
+
inputSchema: { type: "object", properties: {} },
|
|
84
|
+
};
|
|
85
|
+
const WHALE_STATUS_TOOL = {
|
|
86
|
+
name: "whale_status",
|
|
87
|
+
description: "Check current authentication status, active store, and account info.",
|
|
88
|
+
inputSchema: { type: "object", properties: {} },
|
|
89
|
+
};
|
|
90
|
+
const WHALE_STORES_TOOL = {
|
|
91
|
+
name: "whale_stores",
|
|
92
|
+
description: "List your stores or switch the active store.",
|
|
93
|
+
inputSchema: {
|
|
94
|
+
type: "object",
|
|
95
|
+
properties: {
|
|
96
|
+
store_id: {
|
|
97
|
+
type: "string",
|
|
98
|
+
description: "Store ID to switch to. Omit to list all available stores.",
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
const LOCAL_TOOLS = [WHALE_LOGIN_TOOL, WHALE_STATUS_TOOL, WHALE_STORES_TOOL];
|
|
104
|
+
// ============================================================================
|
|
105
|
+
// REMOTE TOOL DEFINITIONS (loaded from database)
|
|
106
|
+
// ============================================================================
|
|
107
|
+
let toolDefinitions = [];
|
|
108
|
+
let toolsLoadedAt = 0;
|
|
109
|
+
const TOOL_CACHE_TTL = 300_000; // 5 minutes — tool defs rarely change mid-session
|
|
110
|
+
async function loadToolDefinitions(force = false) {
|
|
111
|
+
if (!isAuthenticated || !supabase)
|
|
112
|
+
return [];
|
|
113
|
+
if (!force && toolDefinitions.length > 0 && Date.now() - toolsLoadedAt < TOOL_CACHE_TTL) {
|
|
114
|
+
return toolDefinitions;
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
const { data, error } = await supabase
|
|
118
|
+
.from("ai_tool_registry")
|
|
119
|
+
.select("name, description, definition")
|
|
120
|
+
.eq("is_active", true)
|
|
121
|
+
.neq("tool_mode", "code");
|
|
122
|
+
if (error) {
|
|
123
|
+
console.error("[MCP] Failed to load tools from registry:", error.message);
|
|
124
|
+
return toolDefinitions; // Return stale cache on error
|
|
125
|
+
}
|
|
126
|
+
toolDefinitions = (data || []).map(t => ({
|
|
127
|
+
name: t.name,
|
|
128
|
+
description: t.description || t.definition?.description || `Execute ${t.name}`,
|
|
129
|
+
inputSchema: t.definition?.input_schema || { type: "object", properties: {} }
|
|
130
|
+
}));
|
|
131
|
+
toolsLoadedAt = Date.now();
|
|
132
|
+
return toolDefinitions;
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
console.error("[MCP] Error loading tool definitions:", err);
|
|
136
|
+
return toolDefinitions;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
async function executeToolRemote(toolName, args, storeId, traceId) {
|
|
140
|
+
try {
|
|
141
|
+
const token = await getAuthToken();
|
|
142
|
+
const response = await fetch(SERVER_URL, {
|
|
143
|
+
method: "POST",
|
|
144
|
+
headers: {
|
|
145
|
+
"Content-Type": "application/json",
|
|
146
|
+
"Authorization": `Bearer ${token}`,
|
|
147
|
+
},
|
|
148
|
+
body: JSON.stringify({
|
|
149
|
+
mode: "tool",
|
|
150
|
+
tool_name: toolName,
|
|
151
|
+
args,
|
|
152
|
+
store_id: storeId,
|
|
153
|
+
conversation_id: SESSION_ID,
|
|
154
|
+
trace_id: traceId,
|
|
155
|
+
userId: USER_ID,
|
|
156
|
+
userEmail: USER_EMAIL,
|
|
157
|
+
source: "whale-code-mcp",
|
|
158
|
+
}),
|
|
159
|
+
});
|
|
160
|
+
const result = await response.json();
|
|
161
|
+
return result;
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
return {
|
|
165
|
+
success: false,
|
|
166
|
+
error: `Server tool call failed: ${err.message}`,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// ============================================================================
|
|
171
|
+
// LOCAL TOOL HANDLERS
|
|
172
|
+
// ============================================================================
|
|
173
|
+
async function handleWhaleLogin() {
|
|
174
|
+
try {
|
|
175
|
+
const result = await signInWithBrowser(config.platformUrl, {
|
|
176
|
+
onBrowserOpening: (url) => {
|
|
177
|
+
console.error(`[MCP] Opening browser for login: ${url}`);
|
|
178
|
+
},
|
|
179
|
+
onWaitingForCallback: () => {
|
|
180
|
+
console.error("[MCP] Waiting for browser login...");
|
|
181
|
+
},
|
|
182
|
+
onExchangingCode: () => {
|
|
183
|
+
console.error("[MCP] Exchanging auth code...");
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
if (!result.success) {
|
|
187
|
+
return {
|
|
188
|
+
content: [{ type: "text", text: `Login failed: ${result.error}\n\nYou can also try running \`whale login\` in a terminal.` }],
|
|
189
|
+
isError: true,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
// Reload config after successful login
|
|
193
|
+
reloadConfig();
|
|
194
|
+
// Force-reload tool definitions
|
|
195
|
+
const tools = await loadToolDefinitions(true);
|
|
196
|
+
// Notify MCP client that tools have changed
|
|
197
|
+
server.notification({ method: "notifications/tools/list_changed" });
|
|
198
|
+
const cfg = result.config;
|
|
199
|
+
let msg = `Logged in as ${cfg.email || "unknown"}`;
|
|
200
|
+
if (cfg.store_name || cfg.store_id) {
|
|
201
|
+
msg += `\nActive store: ${cfg.store_name || cfg.store_id}`;
|
|
202
|
+
}
|
|
203
|
+
msg += `\n${tools.length} tools now available.`;
|
|
204
|
+
if (result.stores && result.stores.length > 1) {
|
|
205
|
+
msg += `\n\nYou have ${result.stores.length} stores. Use whale_stores to switch:`;
|
|
206
|
+
for (const s of result.stores) {
|
|
207
|
+
msg += `\n - ${s.name} (${s.id})`;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return { content: [{ type: "text", text: msg }] };
|
|
211
|
+
}
|
|
212
|
+
catch (err) {
|
|
213
|
+
return {
|
|
214
|
+
content: [{ type: "text", text: `Login error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
215
|
+
isError: true,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function handleWhaleStatus() {
|
|
220
|
+
const cfg = loadConfig();
|
|
221
|
+
const lines = [];
|
|
222
|
+
if (isAuthenticated) {
|
|
223
|
+
lines.push("Authenticated: yes");
|
|
224
|
+
lines.push(`User: ${cfg.email || "(unknown)"}`);
|
|
225
|
+
lines.push(`User ID: ${cfg.user_id || "(unknown)"}`);
|
|
226
|
+
lines.push(`Store: ${cfg.store_name || cfg.store_id || "(none selected)"}`);
|
|
227
|
+
lines.push(`Auth method: ${IS_SERVICE_ROLE ? "service role key (env var)" : "user JWT (whale login)"}`);
|
|
228
|
+
if (cfg.expires_at) {
|
|
229
|
+
const exp = new Date(cfg.expires_at * 1000);
|
|
230
|
+
lines.push(`Token expires: ${exp.toISOString()}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
lines.push("Authenticated: no");
|
|
235
|
+
lines.push("Use whale_login to sign in.");
|
|
236
|
+
}
|
|
237
|
+
lines.push(`MCP version: ${PKG_VERSION}`);
|
|
238
|
+
lines.push(`Server: ${SERVER_URL}`);
|
|
239
|
+
lines.push(`Tools loaded: ${toolDefinitions.length}`);
|
|
240
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
241
|
+
}
|
|
242
|
+
async function handleWhaleStores(args) {
|
|
243
|
+
if (!isAuthenticated) {
|
|
244
|
+
return {
|
|
245
|
+
content: [{ type: "text", text: "Not authenticated. Use whale_login first." }],
|
|
246
|
+
isError: true,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
const switchToId = args.store_id;
|
|
250
|
+
if (switchToId) {
|
|
251
|
+
// Switch store
|
|
252
|
+
// Verify the store exists for this user
|
|
253
|
+
const token = await getAuthToken();
|
|
254
|
+
const stores = await getStoresForUser(token, USER_ID || "");
|
|
255
|
+
const target = stores.find(s => s.id === switchToId);
|
|
256
|
+
if (!target) {
|
|
257
|
+
return {
|
|
258
|
+
content: [{ type: "text", text: `Store not found: ${switchToId}\nAvailable stores: ${stores.map(s => `${s.name} (${s.id})`).join(", ")}` }],
|
|
259
|
+
isError: true,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
selectStore(target.id, target.name);
|
|
263
|
+
reloadConfig();
|
|
264
|
+
// Notify MCP client that tools may have changed context
|
|
265
|
+
server.notification({ method: "notifications/tools/list_changed" });
|
|
266
|
+
return {
|
|
267
|
+
content: [{ type: "text", text: `Switched to store: ${target.name} (${target.id})` }],
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
// List stores
|
|
271
|
+
const token = await getAuthToken();
|
|
272
|
+
const stores = await getStoresForUser(token, USER_ID || "");
|
|
273
|
+
if (stores.length === 0) {
|
|
274
|
+
return { content: [{ type: "text", text: "No stores found for your account." }] };
|
|
275
|
+
}
|
|
276
|
+
const cfg = loadConfig();
|
|
277
|
+
const lines = ["Your stores:"];
|
|
278
|
+
for (const s of stores) {
|
|
279
|
+
const active = s.id === cfg.store_id ? " (active)" : "";
|
|
280
|
+
lines.push(` - ${s.name}${s.slug ? ` [${s.slug}]` : ""}: ${s.id}${active}`);
|
|
281
|
+
}
|
|
282
|
+
lines.push("\nTo switch: use whale_stores with store_id parameter");
|
|
283
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
284
|
+
}
|
|
285
|
+
// ============================================================================
|
|
286
|
+
// MCP SERVER
|
|
287
|
+
// ============================================================================
|
|
288
|
+
const server = new Server({ name: "whale", version: PKG_VERSION }, { capabilities: { tools: {} } });
|
|
289
|
+
// List available tools — local auth tools always present; remote tools when authenticated
|
|
290
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
291
|
+
if (!isAuthenticated) {
|
|
292
|
+
console.error(`[MCP] Returning ${LOCAL_TOOLS.length} tools (not authenticated)`);
|
|
293
|
+
return {
|
|
294
|
+
tools: LOCAL_TOOLS.map(t => ({
|
|
295
|
+
name: t.name,
|
|
296
|
+
description: t.description,
|
|
297
|
+
inputSchema: t.inputSchema,
|
|
298
|
+
})),
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
const remoteTools = await loadToolDefinitions();
|
|
302
|
+
const allTools = [...LOCAL_TOOLS, ...remoteTools];
|
|
303
|
+
console.error(`[MCP] Returning ${allTools.length} tools (${LOCAL_TOOLS.length} local + ${remoteTools.length} remote)`);
|
|
304
|
+
return {
|
|
305
|
+
tools: allTools.map(t => ({
|
|
306
|
+
name: t.name,
|
|
307
|
+
description: t.description,
|
|
308
|
+
inputSchema: t.inputSchema,
|
|
309
|
+
})),
|
|
310
|
+
};
|
|
311
|
+
});
|
|
312
|
+
// Execute a tool — local auth tools handled here, everything else proxied to Fly.io
|
|
313
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
314
|
+
const toolName = request.params.name;
|
|
315
|
+
const toolArgs = (request.params.arguments || {});
|
|
316
|
+
// ── Local tools (handled in-process) ──
|
|
317
|
+
if (toolName === "whale_login") {
|
|
318
|
+
return handleWhaleLogin();
|
|
319
|
+
}
|
|
320
|
+
if (toolName === "whale_status") {
|
|
321
|
+
return handleWhaleStatus();
|
|
322
|
+
}
|
|
323
|
+
if (toolName === "whale_stores") {
|
|
324
|
+
return handleWhaleStores(toolArgs);
|
|
325
|
+
}
|
|
326
|
+
// ── Auth gate for remote tools ──
|
|
327
|
+
if (!isAuthenticated) {
|
|
328
|
+
return {
|
|
329
|
+
content: [{
|
|
330
|
+
type: "text",
|
|
331
|
+
text: "Not authenticated. Please use the whale_login tool first to sign in to your WhaleTools account.",
|
|
332
|
+
}],
|
|
333
|
+
isError: true,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
// ── Remote tool execution ──
|
|
337
|
+
const startTime = Date.now();
|
|
338
|
+
const traceId = crypto.randomUUID();
|
|
339
|
+
// Allow tool-level store_id override (e.g. multi-store users switching context)
|
|
340
|
+
const effectiveStoreId = toolArgs.store_id || STORE_ID || undefined;
|
|
341
|
+
if (toolArgs.store_id) {
|
|
342
|
+
delete toolArgs.store_id; // Don't send as a tool arg — it's a routing param
|
|
343
|
+
}
|
|
344
|
+
console.error(`[MCP] Executing: ${toolName} → Fly.io [${traceId.slice(0, 8)}] store=${effectiveStoreId?.slice(0, 8) || "none"}`);
|
|
345
|
+
const result = await executeToolRemote(toolName, toolArgs, effectiveStoreId, traceId);
|
|
346
|
+
const durationMs = Date.now() - startTime;
|
|
347
|
+
// Fire-and-forget telemetry
|
|
348
|
+
logMcpToolCall(toolName, toolArgs.action, result.success, durationMs, traceId, result.error).catch(() => { });
|
|
349
|
+
if (result.success) {
|
|
350
|
+
return {
|
|
351
|
+
content: [{
|
|
352
|
+
type: "text",
|
|
353
|
+
text: typeof result.data === "string"
|
|
354
|
+
? result.data
|
|
355
|
+
: JSON.stringify(result.data, null, 2),
|
|
356
|
+
}],
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
return {
|
|
361
|
+
content: [{
|
|
362
|
+
type: "text",
|
|
363
|
+
text: JSON.stringify({ error: result.error }),
|
|
364
|
+
}],
|
|
365
|
+
isError: true,
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
/**
|
|
370
|
+
* Fire-and-forget MCP proxy telemetry — logs to audit_logs as a third service
|
|
371
|
+
* ("mcp-proxy") alongside "whale-cli" and "agent-server", capturing the full
|
|
372
|
+
* network round-trip latency from MCP client → Fly.io server → response.
|
|
373
|
+
*/
|
|
374
|
+
async function logMcpToolCall(toolName, action, success, durationMs, traceId, error) {
|
|
375
|
+
if (!supabase)
|
|
376
|
+
return;
|
|
377
|
+
try {
|
|
378
|
+
const now = new Date();
|
|
379
|
+
const startTime = new Date(now.getTime() - durationMs);
|
|
380
|
+
const bytes = new Uint8Array(8);
|
|
381
|
+
crypto.getRandomValues(bytes);
|
|
382
|
+
const spanId = Array.from(bytes).map(b => b.toString(16).padStart(2, "0")).join("");
|
|
383
|
+
await supabase.from("audit_logs").insert({
|
|
384
|
+
action: `mcp.tool.${toolName}${action ? `.${action}` : ""}`,
|
|
385
|
+
severity: success ? "info" : "error",
|
|
386
|
+
store_id: STORE_ID || null,
|
|
387
|
+
user_id: USER_ID,
|
|
388
|
+
user_email: USER_EMAIL,
|
|
389
|
+
resource_type: "whale_code_mcp",
|
|
390
|
+
resource_id: toolName,
|
|
391
|
+
request_id: traceId,
|
|
392
|
+
conversation_id: SESSION_ID,
|
|
393
|
+
source: "whale_code_mcp",
|
|
394
|
+
details: {
|
|
395
|
+
session_id: SESSION_ID,
|
|
396
|
+
source: "whale_code_mcp",
|
|
397
|
+
tool_name: toolName,
|
|
398
|
+
action: action || null,
|
|
399
|
+
},
|
|
400
|
+
error_message: error || null,
|
|
401
|
+
duration_ms: durationMs,
|
|
402
|
+
trace_id: traceId,
|
|
403
|
+
span_id: spanId,
|
|
404
|
+
span_kind: "CLIENT",
|
|
405
|
+
service_name: "whale-code-mcp",
|
|
406
|
+
service_version: PKG_VERSION,
|
|
407
|
+
status_code: success ? "OK" : "ERROR",
|
|
408
|
+
start_time: startTime.toISOString(),
|
|
409
|
+
end_time: now.toISOString(),
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
catch {
|
|
413
|
+
// Telemetry must never break tool execution
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
// ============================================================================
|
|
417
|
+
// LOCAL AGENT (auto-spawned — shares this process with MCP server)
|
|
418
|
+
// ============================================================================
|
|
419
|
+
let localAgent = null;
|
|
420
|
+
/**
|
|
421
|
+
* Get or create a store API key for the local agent WebSocket connection.
|
|
422
|
+
* Persisted to ~/.swagmanager/config.json so we reuse across restarts.
|
|
423
|
+
*/
|
|
424
|
+
async function getOrCreateAgentApiKey() {
|
|
425
|
+
const rawConfig = loadConfig();
|
|
426
|
+
if (rawConfig.agent_api_key)
|
|
427
|
+
return rawConfig.agent_api_key;
|
|
428
|
+
// Auto-generate via the server's api_keys tool
|
|
429
|
+
try {
|
|
430
|
+
const result = await executeToolRemote("api_keys", {
|
|
431
|
+
action: "generate",
|
|
432
|
+
name: "local-agent-auto",
|
|
433
|
+
scopes: ["*"],
|
|
434
|
+
key_type: "live",
|
|
435
|
+
}, STORE_ID || undefined);
|
|
436
|
+
if (result.success && result.data) {
|
|
437
|
+
const data = result.data;
|
|
438
|
+
const key = data.key_value || data.api_key || data.key;
|
|
439
|
+
if (key) {
|
|
440
|
+
updateConfig({ agent_api_key: key });
|
|
441
|
+
console.error("[MCP] Auto-generated local agent API key");
|
|
442
|
+
return key;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
console.error("[MCP] Failed to auto-generate agent API key:", result.error);
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
catch (err) {
|
|
449
|
+
console.error("[MCP] Error generating agent API key:", err.message);
|
|
450
|
+
return null;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Derive WebSocket URL from the HTTP server URL.
|
|
455
|
+
* https://whale-agent.fly.dev → wss://whale-agent.fly.dev/agent/ws
|
|
456
|
+
*/
|
|
457
|
+
function getAgentWsUrl() {
|
|
458
|
+
const base = SERVER_URL.replace(/\/$/, "");
|
|
459
|
+
const wsBase = base.replace(/^https:/, "wss:").replace(/^http:/, "ws:");
|
|
460
|
+
return `${wsBase}/agent/ws`;
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Start the local agent WebSocket connection in the background.
|
|
464
|
+
* Non-blocking — MCP server continues even if this fails.
|
|
465
|
+
*/
|
|
466
|
+
async function startLocalAgent() {
|
|
467
|
+
const apiKey = await getOrCreateAgentApiKey();
|
|
468
|
+
if (!apiKey) {
|
|
469
|
+
console.error("[MCP] Skipping local agent — no API key available");
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
const wsUrl = getAgentWsUrl();
|
|
473
|
+
console.error(`[MCP] Starting local agent → ${wsUrl}`);
|
|
474
|
+
localAgent = new LocalAgentConnection({
|
|
475
|
+
apiKey,
|
|
476
|
+
serverUrl: wsUrl,
|
|
477
|
+
onConnected: () => console.error("[MCP] Local agent connected — WhaleChat can now use local tools"),
|
|
478
|
+
onDisconnected: (reason) => console.error(`[MCP] Local agent disconnected: ${reason}`),
|
|
479
|
+
onError: (err) => console.error(`[MCP] Local agent error: ${err.message}`),
|
|
480
|
+
onLog: (msg) => console.error(msg),
|
|
481
|
+
});
|
|
482
|
+
try {
|
|
483
|
+
await localAgent.connect();
|
|
484
|
+
}
|
|
485
|
+
catch (err) {
|
|
486
|
+
console.error(`[MCP] Local agent failed to connect: ${err.message} (non-fatal)`);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
// ============================================================================
|
|
490
|
+
// STARTUP
|
|
491
|
+
// ============================================================================
|
|
492
|
+
async function main() {
|
|
493
|
+
// Initialize error logging for MCP mode
|
|
494
|
+
initErrorLogger({ serviceName: "whale-code-mcp" });
|
|
495
|
+
if (USER_ID || USER_EMAIL) {
|
|
496
|
+
setErrorLoggerUser(USER_ID || undefined, USER_EMAIL || undefined, STORE_ID || undefined);
|
|
497
|
+
}
|
|
498
|
+
console.error(`[MCP] Whale MCP Server v${PKG_VERSION}`);
|
|
499
|
+
console.error(`[MCP] Supabase: ${SUPABASE_URL}`);
|
|
500
|
+
console.error(`[MCP] Server: ${SERVER_URL}`);
|
|
501
|
+
console.error(`[MCP] Store: ${STORE_ID || "(default)"}`);
|
|
502
|
+
console.error(`[MCP] User: ${USER_EMAIL || "(unknown)"} [${USER_ID?.slice(0, 8) || "no-id"}]`);
|
|
503
|
+
console.error(`[MCP] Session: ${SESSION_ID}`);
|
|
504
|
+
console.error(`[MCP] Authenticated: ${isAuthenticated ? "yes" : "no — use whale_login tool"}`);
|
|
505
|
+
// Pre-load tools from database (only if authenticated)
|
|
506
|
+
if (isAuthenticated) {
|
|
507
|
+
const tools = await loadToolDefinitions(true);
|
|
508
|
+
console.error(`[MCP] Loaded ${tools.length} tools from registry`);
|
|
509
|
+
}
|
|
510
|
+
else {
|
|
511
|
+
console.error(`[MCP] Skipping tool load — not authenticated. ${LOCAL_TOOLS.length} auth tools available.`);
|
|
512
|
+
}
|
|
513
|
+
// Start OTA update checker (non-blocking, runs in background)
|
|
514
|
+
startUpdateLoop(true);
|
|
515
|
+
// Connect via stdio
|
|
516
|
+
const transport = new StdioServerTransport();
|
|
517
|
+
await server.connect(transport);
|
|
518
|
+
console.error("[MCP] Ready");
|
|
519
|
+
// Start local agent in background — only if authenticated
|
|
520
|
+
if (isAuthenticated) {
|
|
521
|
+
startLocalAgent().catch((err) => {
|
|
522
|
+
console.error("[MCP] Local agent startup error:", err.message);
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
// Graceful shutdown — disconnect local agent
|
|
527
|
+
process.on("SIGINT", () => {
|
|
528
|
+
localAgent?.disconnect();
|
|
529
|
+
});
|
|
530
|
+
process.on("SIGTERM", () => {
|
|
531
|
+
localAgent?.disconnect();
|
|
532
|
+
});
|
|
533
|
+
main().catch((err) => {
|
|
534
|
+
console.error("[MCP] Fatal:", err);
|
|
535
|
+
captureError({ error: err instanceof Error ? err : new Error(String(err)), severity: "fatal", tags: { source: "mcp_main" } });
|
|
536
|
+
// Give error logger a moment to flush before exit
|
|
537
|
+
setTimeout(() => process.exit(1), 500);
|
|
538
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local Agent Connection — maintains WebSocket connection to SwagManager cloud.
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Outbound WSS connection (no firewall issues)
|
|
6
|
+
* - Auto-reconnection with exponential backoff
|
|
7
|
+
* - Heartbeat/pong handling
|
|
8
|
+
* - Command routing to executor
|
|
9
|
+
*/
|
|
10
|
+
export interface AgentConfig {
|
|
11
|
+
apiKey: string;
|
|
12
|
+
serverUrl?: string;
|
|
13
|
+
autoReconnect?: boolean;
|
|
14
|
+
onConnected?: () => void;
|
|
15
|
+
onDisconnected?: (reason: string) => void;
|
|
16
|
+
onError?: (err: Error) => void;
|
|
17
|
+
onLog?: (msg: string) => void;
|
|
18
|
+
}
|
|
19
|
+
export declare class LocalAgentConnection {
|
|
20
|
+
private ws;
|
|
21
|
+
private config;
|
|
22
|
+
private reconnectAttempts;
|
|
23
|
+
private reconnectTimer;
|
|
24
|
+
private intentionalClose;
|
|
25
|
+
private discoveredTools;
|
|
26
|
+
private _agentId;
|
|
27
|
+
constructor(config: AgentConfig);
|
|
28
|
+
/**
|
|
29
|
+
* Connect to the SwagManager cloud gateway.
|
|
30
|
+
*/
|
|
31
|
+
connect(): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Disconnect gracefully.
|
|
34
|
+
*/
|
|
35
|
+
disconnect(): void;
|
|
36
|
+
/**
|
|
37
|
+
* Check if connected.
|
|
38
|
+
*/
|
|
39
|
+
get connected(): boolean;
|
|
40
|
+
private handleMessage;
|
|
41
|
+
private handleExec;
|
|
42
|
+
private handleToolExec;
|
|
43
|
+
private handleDiscover;
|
|
44
|
+
private send;
|
|
45
|
+
private sendResult;
|
|
46
|
+
private scheduleReconnect;
|
|
47
|
+
private log;
|
|
48
|
+
}
|