@meowlynxsea/koi 0.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.
Files changed (109) hide show
  1. package/LICENSE +34 -0
  2. package/NOTICE +35 -0
  3. package/README.md +15 -0
  4. package/bin/koi +12 -0
  5. package/dist/highlights-eq9cgrbb.scm +604 -0
  6. package/dist/highlights-ghv9g403.scm +205 -0
  7. package/dist/highlights-hk7bwhj4.scm +284 -0
  8. package/dist/highlights-r812a2qc.scm +150 -0
  9. package/dist/highlights-x6tmsnaa.scm +115 -0
  10. package/dist/injections-73j83es3.scm +27 -0
  11. package/dist/main.js +489918 -0
  12. package/dist/tree-sitter-javascript-nd0q4pe9.wasm +0 -0
  13. package/dist/tree-sitter-markdown-411r6y9b.wasm +0 -0
  14. package/dist/tree-sitter-markdown_inline-j5349f42.wasm +0 -0
  15. package/dist/tree-sitter-typescript-zxjzwt75.wasm +0 -0
  16. package/dist/tree-sitter-zig-e78zbjpm.wasm +0 -0
  17. package/package.json +51 -0
  18. package/src/agent/check-permissions.ts +239 -0
  19. package/src/agent/hooks/message-utils.ts +305 -0
  20. package/src/agent/hooks/types.ts +32 -0
  21. package/src/agent/hooks.ts +1560 -0
  22. package/src/agent/mode.ts +163 -0
  23. package/src/agent/monitor-registry.ts +308 -0
  24. package/src/agent/permission-ui.ts +71 -0
  25. package/src/agent/plan-ui.ts +74 -0
  26. package/src/agent/question-ui.ts +58 -0
  27. package/src/agent/session-fork.ts +299 -0
  28. package/src/agent/session-snapshots.ts +216 -0
  29. package/src/agent/session-store.ts +649 -0
  30. package/src/agent/session-tasks.ts +305 -0
  31. package/src/agent/session.ts +27 -0
  32. package/src/agent/subagent-registry.ts +176 -0
  33. package/src/agent/subagent.ts +194 -0
  34. package/src/agent/tool-orchestration.ts +55 -0
  35. package/src/agent/tools.ts +8 -0
  36. package/src/cli/args.ts +6 -0
  37. package/src/cli/commands.ts +5 -0
  38. package/src/commands/skills/index.ts +23 -0
  39. package/src/config/models.ts +6 -0
  40. package/src/config/settings.ts +392 -0
  41. package/src/main.tsx +64 -0
  42. package/src/services/mcp/client.ts +194 -0
  43. package/src/services/mcp/config.ts +232 -0
  44. package/src/services/mcp/connection-manager.ts +258 -0
  45. package/src/services/mcp/index.ts +80 -0
  46. package/src/services/mcp/mcp-commands.ts +114 -0
  47. package/src/services/mcp/stdio-transport.ts +246 -0
  48. package/src/services/mcp/types.ts +155 -0
  49. package/src/skills/SkillsMenu.tsx +370 -0
  50. package/src/skills/bundled/batch.ts +106 -0
  51. package/src/skills/bundled/debug.ts +86 -0
  52. package/src/skills/bundled/loremIpsum.ts +101 -0
  53. package/src/skills/bundled/remember.ts +97 -0
  54. package/src/skills/bundled/simplify.ts +100 -0
  55. package/src/skills/bundled/skillify.ts +123 -0
  56. package/src/skills/bundled/stuck.ts +101 -0
  57. package/src/skills/bundled/updateConfig.ts +228 -0
  58. package/src/skills/bundled.ts +46 -0
  59. package/src/skills/frontmatter.ts +179 -0
  60. package/src/skills/index.ts +87 -0
  61. package/src/skills/invoke.ts +231 -0
  62. package/src/skills/loader.ts +710 -0
  63. package/src/skills/substitution.ts +169 -0
  64. package/src/skills/types.ts +201 -0
  65. package/src/tools/agent.ts +143 -0
  66. package/src/tools/ask-user-question.ts +46 -0
  67. package/src/tools/bash.ts +148 -0
  68. package/src/tools/edit.ts +164 -0
  69. package/src/tools/glob.ts +102 -0
  70. package/src/tools/grep.ts +248 -0
  71. package/src/tools/index.ts +73 -0
  72. package/src/tools/list-mcp-resources.ts +74 -0
  73. package/src/tools/ls.ts +85 -0
  74. package/src/tools/mcp.ts +76 -0
  75. package/src/tools/monitor.ts +159 -0
  76. package/src/tools/plan-mode.ts +134 -0
  77. package/src/tools/read-mcp-resource.ts +79 -0
  78. package/src/tools/read.ts +137 -0
  79. package/src/tools/skill.ts +176 -0
  80. package/src/tools/task.ts +349 -0
  81. package/src/tools/types.ts +52 -0
  82. package/src/tools/webfetch-domains.ts +239 -0
  83. package/src/tools/webfetch.ts +533 -0
  84. package/src/tools/write.ts +101 -0
  85. package/src/tui/app.tsx +1178 -0
  86. package/src/tui/components/chat-panel.tsx +1071 -0
  87. package/src/tui/components/command-panel.tsx +261 -0
  88. package/src/tui/components/confirm-modal.tsx +135 -0
  89. package/src/tui/components/connect-modal.tsx +435 -0
  90. package/src/tui/components/connecting-modal.tsx +167 -0
  91. package/src/tui/components/edit-pending-modal.tsx +103 -0
  92. package/src/tui/components/exit-modal.tsx +131 -0
  93. package/src/tui/components/fork-modal.tsx +377 -0
  94. package/src/tui/components/image-preview-modal.tsx +141 -0
  95. package/src/tui/components/image-utils.ts +128 -0
  96. package/src/tui/components/info-bar.tsx +103 -0
  97. package/src/tui/components/input-box.tsx +352 -0
  98. package/src/tui/components/mcp/MCPSettings.tsx +386 -0
  99. package/src/tui/components/mcp/index.ts +7 -0
  100. package/src/tui/components/model-modal.tsx +310 -0
  101. package/src/tui/components/pending-area.tsx +88 -0
  102. package/src/tui/components/rename-modal.tsx +119 -0
  103. package/src/tui/components/session-modal.tsx +233 -0
  104. package/src/tui/components/side-bar.tsx +349 -0
  105. package/src/tui/components/tool-output.ts +6 -0
  106. package/src/tui/hooks/user-prompt-history.ts +114 -0
  107. package/src/tui/theme.ts +63 -0
  108. package/src/types/commands.ts +80 -0
  109. package/src/types/cross-spawn.d.ts +24 -0
@@ -0,0 +1,80 @@
1
+ /**
2
+ * MCP Services - Koi Agent
3
+ *
4
+ * Model Context Protocol integration for Koi Agent.
5
+ * Supports stdio, SSE, HTTP, and WebSocket transport types.
6
+ */
7
+
8
+ // Types
9
+ export * from "./types.js";
10
+
11
+ // Configuration
12
+ export {
13
+ setMcpConfig,
14
+ getMcpConfig,
15
+ getAllMcpConfigs,
16
+ getMcpConfigsByScope,
17
+ removeMcpConfig,
18
+ isMcpServerDisabled,
19
+ setMcpServerEnabled,
20
+ getMcpServerNames,
21
+ loadMcpConfigs,
22
+ loadProjectMcpConfig,
23
+ loadLocalMcpConfig,
24
+ validateMcpConfig,
25
+ exportMcpConfigs,
26
+ importMcpConfigs,
27
+ } from "./config.js";
28
+
29
+ export type {
30
+ McpConfigValidationResult,
31
+ McpConfigExport,
32
+ } from "./config.js";
33
+
34
+ // Client
35
+ export {
36
+ connectToServer,
37
+ disconnectFromServer,
38
+ callMcpTool,
39
+ listMcpResources,
40
+ readMcpResource,
41
+ listMcpPrompts,
42
+ executeMcpPrompt,
43
+ } from "./client.js";
44
+
45
+ export type { ConnectResult } from "./client.js";
46
+
47
+ // Connection Manager
48
+ export {
49
+ initializeMcpConnections,
50
+ connectMcpServer,
51
+ disconnectMcpServer,
52
+ reconnectMcpServer,
53
+ toggleMcpServer,
54
+ getMcpConnections,
55
+ getMcpConnection,
56
+ getConnectedServers,
57
+ getAllMcpTools,
58
+ getAllMcpResources,
59
+ getMcpStatusSummary,
60
+ isMcpConnecting,
61
+ getMcpError,
62
+ disconnectAllMcpServers,
63
+ resetMcpConnectionManager,
64
+ type McpConnectionProgress,
65
+ type McpProgressCallback,
66
+ } from "./connection-manager.js";
67
+
68
+ // Stdio Transport with JSON filtering
69
+ export { FilteredStdioClientTransport } from "./stdio-transport.js";
70
+
71
+ // Commands
72
+ export {
73
+ addMcpServer,
74
+ removeMcpServer,
75
+ enableMcpServer,
76
+ disableMcpServer,
77
+ refreshMcpServer,
78
+ getMcpServerInfo,
79
+ parseMcpArgs,
80
+ } from "./mcp-commands.js";
@@ -0,0 +1,114 @@
1
+ /**
2
+ * MCP Command Support
3
+ */
4
+
5
+ import type { ScopedMcpConfig } from "./types.js";
6
+ import { getMcpConfig, setMcpConfig, removeMcpConfig, isMcpServerDisabled, setMcpServerEnabled, validateMcpConfig } from "./config.js";
7
+ import { connectMcpServer, disconnectMcpServer, reconnectMcpServer, getMcpConnections } from "./connection-manager.js";
8
+
9
+ export async function addMcpServer(name: string, config: Partial<ScopedMcpConfig>): Promise<{ success: boolean; error?: string; warning?: string }> {
10
+ const validation = validateMcpConfig(name, config);
11
+ if (!validation.valid) {
12
+ return { success: false, error: validation.errors?.join(", ") ?? "Invalid configuration" };
13
+ }
14
+
15
+ const existing = getMcpConfig(name);
16
+ if (existing) {
17
+ return { success: false, error: `Server '${name}' already exists` };
18
+ }
19
+
20
+ setMcpConfig(name, config, "user");
21
+ const result = await connectMcpServer(name);
22
+ if (!result.success) {
23
+ return { success: true, warning: `Server added but failed to connect: ${result.error}` };
24
+ }
25
+ return { success: true };
26
+ }
27
+
28
+ export function removeMcpServer(name: string): { success: boolean; error?: string } {
29
+ const config = getMcpConfig(name);
30
+ if (!config) return { success: false, error: `Server '${name}' not found` };
31
+ disconnectMcpServer(name).catch(() => {});
32
+ removeMcpConfig(name);
33
+ return { success: true };
34
+ }
35
+
36
+ export function enableMcpServer(name: string): { success: boolean; error?: string } {
37
+ const config = getMcpConfig(name);
38
+ if (!config) return { success: false, error: `Server '${name}' not found` };
39
+ setMcpServerEnabled(name, true);
40
+ return { success: true };
41
+ }
42
+
43
+ export function disableMcpServer(name: string): { success: boolean; error?: string } {
44
+ const config = getMcpConfig(name);
45
+ if (!config) return { success: false, error: `Server '${name}' not found` };
46
+ setMcpServerEnabled(name, false);
47
+ disconnectMcpServer(name).catch(() => {});
48
+ return { success: true };
49
+ }
50
+
51
+ export async function refreshMcpServer(name: string, options?: { onProgress?: (message: string) => void }): Promise<{ success: boolean; error?: string }> {
52
+ const config = getMcpConfig(name);
53
+ if (!config) return { success: false, error: `Server '${name}' not found` };
54
+ options?.onProgress?.(`Reconnecting to ${name}...`);
55
+ const result = await reconnectMcpServer(name, { onProgress: options?.onProgress });
56
+ if (!result.success) return { success: false, error: result.error };
57
+ return { success: true };
58
+ }
59
+
60
+ export function getMcpServerInfo(name: string): {
61
+ name: string;
62
+ config?: Partial<ScopedMcpConfig>;
63
+ status: string;
64
+ toolCount?: number;
65
+ resourceCount?: number;
66
+ error?: string;
67
+ } {
68
+ const config = getMcpConfig(name);
69
+ if (!config) return { name, status: "not_found" };
70
+ const connection = getMcpConnections().get(name);
71
+ const status = connection?.status ?? (isMcpServerDisabled(name) ? "disabled" : "disconnected");
72
+ const connectedConn = connection?.status === "connected" ? connection : null;
73
+ return {
74
+ name,
75
+ config,
76
+ status,
77
+ toolCount: connectedConn?.tools?.length,
78
+ resourceCount: connectedConn?.resources?.length,
79
+ error: connection?.status === "failed" ? (connection as { error?: string }).error : undefined,
80
+ };
81
+ }
82
+
83
+ export function parseMcpArgs(args: string): {
84
+ name?: string;
85
+ type?: "stdio" | "sse" | "http" | "ws";
86
+ command?: string;
87
+ url?: string;
88
+ args?: string[];
89
+ } {
90
+ const result: { name?: string; type?: "stdio" | "sse" | "http" | "ws"; command?: string; url?: string; args?: string[] } = {};
91
+ const parts = args.trim().split(/\s+/);
92
+ let i = 0;
93
+ while (i < parts.length) {
94
+ const part = parts[i]!;
95
+ if (part === "--stdio" || part === "-s") { result.type = "stdio"; i++; }
96
+ else if (part === "--sse" || part === "-e") { result.type = "sse"; i++; }
97
+ else if (part === "--http" || part === "-h") { result.type = "http"; i++; }
98
+ else if (part === "--ws" || part === "-w") { result.type = "ws"; i++; }
99
+ else if (part === "--name" || part === "-n") { result.name = parts[++i]; i++; }
100
+ else if (part.startsWith("-")) { i++; }
101
+ else if (!result.name && !result.command && !result.url) {
102
+ if (part.includes("://")) { result.url = part; }
103
+ else { result.command = part; }
104
+ i++;
105
+ } else if (result.command || result.url) {
106
+ result.args = result.args ?? [];
107
+ result.args.push(part);
108
+ i++;
109
+ } else {
110
+ i++;
111
+ }
112
+ }
113
+ return result;
114
+ }
@@ -0,0 +1,246 @@
1
+ /**
2
+ * MCP Stdio Transport with JSON Filtering
3
+ *
4
+ * Some MCP servers (like context7) output non-JSON text to stdout on startup,
5
+ * which would cause parsing errors. This wrapper filters out non-JSON lines.
6
+ */
7
+
8
+ import spawn from "cross-spawn";
9
+ import process from "node:process";
10
+ import { getDefaultEnvironment } from "@modelcontextprotocol/sdk/client/stdio.js";
11
+ import type { StdioServerParameters } from "@modelcontextprotocol/sdk/client/stdio.js";
12
+ import type { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js";
13
+
14
+ // Transport interface for FilteredStdioClientTransport
15
+ interface Transport {
16
+ onclose?: () => void;
17
+ onerror?: (error: Error) => void;
18
+ onmessage?: (message: JSONRPCMessage) => void;
19
+ start(): Promise<void>;
20
+ send(message: JSONRPCMessage): Promise<void>;
21
+ close(): Promise<void>;
22
+ readonly pid?: number | null;
23
+ }
24
+
25
+ // Inline serializeMessage to avoid internal path imports
26
+ function serializeMessage(message: JSONRPCMessage): string {
27
+ return JSON.stringify(message) + "\n";
28
+ }
29
+
30
+ /**
31
+ * Buffers a continuous stdio stream into discrete JSON-RPC messages.
32
+ */
33
+ class ReadBuffer {
34
+ private _buffer?: Buffer;
35
+
36
+ append(chunk: Buffer): void {
37
+ this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk;
38
+ }
39
+
40
+ readMessage(): { line: string; remaining: Buffer | undefined } | null {
41
+ if (!this._buffer) {
42
+ return null;
43
+ }
44
+ const index = this._buffer.indexOf("\n");
45
+ if (index === -1) {
46
+ return null;
47
+ }
48
+ const line = this._buffer.toString("utf8", 0, index).replace(/\r$/, "");
49
+ const remaining = this._buffer.subarray(index + 1);
50
+ this._buffer = remaining.length > 0 ? remaining : undefined;
51
+ return { line, remaining };
52
+ }
53
+
54
+ clear(): void {
55
+ this._buffer = undefined;
56
+ }
57
+ }
58
+
59
+ function isValidJsonRpc(line: string): boolean {
60
+ const trimmed = line.trim();
61
+ if (!trimmed.startsWith("{")) {
62
+ return false;
63
+ }
64
+ try {
65
+ const parsed: unknown = JSON.parse(trimmed);
66
+ return typeof parsed === "object" && parsed !== null && "jsonrpc" in (parsed as Record<string, unknown>);
67
+ } catch {
68
+ return false;
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Client transport for stdio with JSON filtering.
74
+ * Filters out non-JSON startup messages from MCP servers.
75
+ */
76
+ export class FilteredStdioClientTransport implements Transport {
77
+ private _process?: ReturnType<typeof spawn>;
78
+ private _readBuffer: ReadBuffer;
79
+ private _serverParams: StdioServerParameters;
80
+
81
+ onclose?: () => void;
82
+ onerror?: (error: Error) => void;
83
+ onmessage?: (message: JSONRPCMessage) => void;
84
+
85
+ constructor(server: StdioServerParameters) {
86
+ this._readBuffer = new ReadBuffer();
87
+ this._serverParams = server;
88
+ }
89
+
90
+ async start(): Promise<void> {
91
+ if (this._process) {
92
+ throw new Error("StdioClientTransport already started!");
93
+ }
94
+ return new Promise((resolve, reject) => {
95
+ // Always pipe stderr so we can capture and suppress startup messages
96
+ // Only inherit stderr explicitly if user requests it
97
+ const stderrMode: "pipe" | "inherit" | "ignore" =
98
+ this._serverParams.stderr === "inherit" ? "inherit" :
99
+ this._serverParams.stderr === "ignore" ? "ignore" :
100
+ "pipe";
101
+
102
+ this._process = spawn(this._serverParams.command, this._serverParams.args ?? [], {
103
+ env: {
104
+ ...getDefaultEnvironment(),
105
+ ...this._serverParams.env,
106
+ },
107
+ stdio: ["pipe", "pipe", stderrMode],
108
+ shell: false,
109
+ windowsHide: process.platform === "win32",
110
+ cwd: this._serverParams.cwd,
111
+ });
112
+
113
+ this._process.on("error", (error: Error) => {
114
+ reject(error);
115
+ this.onerror?.(error);
116
+ });
117
+
118
+ this._process.on("spawn", () => {
119
+ resolve();
120
+ });
121
+
122
+ this._process.on("close", (_code: number) => {
123
+ this._process = undefined;
124
+ this.onclose?.();
125
+ });
126
+
127
+ this._process.stdin?.on("error", (error: Error) => {
128
+ this.onerror?.(error);
129
+ });
130
+
131
+ this._process.stdout?.on("data", (chunk: Buffer) => {
132
+ this._readBuffer.append(chunk);
133
+ this.processReadBuffer();
134
+ });
135
+
136
+ this._process.stdout?.on("error", (error: Error) => {
137
+ this.onerror?.(error);
138
+ });
139
+
140
+ // Capture stderr but don't output it to console
141
+ // This filters out server startup messages like "Server v2.2.4 running on stdio"
142
+ if (this._process.stderr) {
143
+ this._process.stderr.on("data", (_chunk: Buffer) => {
144
+ // Silently consume stderr to prevent startup messages from appearing in TUI
145
+ // Only emit actual errors if needed
146
+ });
147
+ this._process.stderr.on("error", () => {
148
+ // Ignore stderr errors
149
+ });
150
+ }
151
+ });
152
+ }
153
+
154
+ get pid(): number | null {
155
+ return this._process?.pid ?? null;
156
+ }
157
+
158
+ private processReadBuffer(): void {
159
+ // Filter out non-JSON lines
160
+ while (true) {
161
+ const result = this._readBuffer.readMessage();
162
+ if (result === null) {
163
+ break;
164
+ }
165
+
166
+ const { line } = result;
167
+
168
+ // Skip empty lines
169
+ if (!line.trim()) {
170
+ continue;
171
+ }
172
+
173
+ // Skip non-JSON lines (like server startup messages)
174
+ if (!isValidJsonRpc(line)) {
175
+ continue;
176
+ }
177
+
178
+ try {
179
+ const message = JSON.parse(line) as JSONRPCMessage;
180
+ this.onmessage?.(message);
181
+ } catch {
182
+ // Skip invalid JSON-RPC messages
183
+ continue;
184
+ }
185
+ }
186
+ }
187
+
188
+ async close(): Promise<void> {
189
+ if (this._process) {
190
+ const processToClose = this._process;
191
+ this._process = undefined;
192
+ const closePromise = new Promise<void>((resolve) => {
193
+ processToClose.once("close", () => {
194
+ resolve();
195
+ });
196
+ });
197
+
198
+ try {
199
+ processToClose.stdin?.end();
200
+ } catch {
201
+ // ignore
202
+ }
203
+
204
+ await Promise.race([
205
+ closePromise,
206
+ new Promise<void>((resolve) => setTimeout(resolve, 2000).unref()),
207
+ ]);
208
+
209
+ if (processToClose.exitCode === null) {
210
+ try {
211
+ processToClose.kill("SIGTERM");
212
+ } catch {
213
+ // ignore
214
+ }
215
+ await Promise.race([
216
+ closePromise,
217
+ new Promise<void>((resolve) => setTimeout(resolve, 2000).unref()),
218
+ ]);
219
+ }
220
+
221
+ if (processToClose.exitCode === null) {
222
+ try {
223
+ processToClose.kill("SIGKILL");
224
+ } catch {
225
+ // ignore
226
+ }
227
+ }
228
+ }
229
+ this._readBuffer.clear();
230
+ }
231
+
232
+ send(message: JSONRPCMessage): Promise<void> {
233
+ return new Promise((resolve, reject) => {
234
+ if (!this._process?.stdin) {
235
+ reject(new Error("Not connected"));
236
+ return;
237
+ }
238
+ const json = serializeMessage(message);
239
+ if (this._process.stdin.write(json)) {
240
+ resolve();
241
+ } else {
242
+ this._process.stdin.once("drain", resolve);
243
+ }
244
+ });
245
+ }
246
+ }
@@ -0,0 +1,155 @@
1
+ /**
2
+ * MCP Types for Koi Agent
3
+ */
4
+
5
+ import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
6
+ import type { Tool, Resource, ServerCapabilities } from "@modelcontextprotocol/sdk/types.js";
7
+
8
+ // Transport Types
9
+ export const TransportSchema = ["stdio", "sse", "http", "ws"] as const;
10
+ export type Transport = (typeof TransportSchema)[number];
11
+
12
+ // Configuration Types - Simple flat structure
13
+ export interface McpServerConfig {
14
+ type?: "stdio" | "sse" | "http" | "ws";
15
+ command?: string;
16
+ args?: string[];
17
+ env?: Record<string, string>;
18
+ url?: string;
19
+ headers?: Record<string, string>;
20
+ authToken?: string;
21
+ }
22
+
23
+ // Config Scope
24
+ export type ConfigScope = "user" | "project" | "local";
25
+
26
+ export interface ScopedMcpConfig extends McpServerConfig {
27
+ scope: ConfigScope;
28
+ description?: string;
29
+ enabled?: boolean;
30
+ }
31
+
32
+ // Config File Format
33
+ export interface McpJsonConfig {
34
+ mcpServers?: Record<string, McpServerConfig>;
35
+ }
36
+
37
+ // Connection State Types
38
+ export type ConnectionStatus = "connected" | "failed" | "pending" | "disabled" | "disconnected";
39
+
40
+ export interface ConnectedMCPServer {
41
+ client: Client;
42
+ name: string;
43
+ status: "connected";
44
+ capabilities: ServerCapabilities;
45
+ serverInfo?: { name: string; version: string };
46
+ instructions?: string;
47
+ config: ScopedMcpConfig;
48
+ tools: Tool[];
49
+ resources: Resource[];
50
+ cleanup: () => Promise<void>;
51
+ }
52
+
53
+ export interface FailedMCPServer {
54
+ name: string;
55
+ status: "failed";
56
+ config: ScopedMcpConfig;
57
+ error?: string;
58
+ lastAttempt?: number;
59
+ }
60
+
61
+ export interface PendingMCPServer {
62
+ name: string;
63
+ status: "pending";
64
+ config: ScopedMcpConfig;
65
+ reconnectAttempt?: number;
66
+ maxReconnectAttempts?: number;
67
+ }
68
+
69
+ export interface DisabledMCPServer {
70
+ name: string;
71
+ status: "disabled";
72
+ config: ScopedMcpConfig;
73
+ }
74
+
75
+ export interface DisconnectedMCPServer {
76
+ name: string;
77
+ status: "disconnected";
78
+ config: ScopedMcpConfig;
79
+ }
80
+
81
+ export type MCPServerConnection =
82
+ | ConnectedMCPServer
83
+ | FailedMCPServer
84
+ | PendingMCPServer
85
+ | DisabledMCPServer
86
+ | DisconnectedMCPServer;
87
+
88
+ // Serialized Types
89
+ export interface SerializedTool {
90
+ name: string;
91
+ description?: string;
92
+ inputSchema?: Tool["inputSchema"];
93
+ isMcp?: boolean;
94
+ serverName?: string;
95
+ originalToolName?: string;
96
+ }
97
+
98
+ export interface SerializedServer {
99
+ name: string;
100
+ status: ConnectionStatus;
101
+ capabilities?: ServerCapabilities;
102
+ serverInfo?: { name: string; version: string };
103
+ error?: string;
104
+ toolCount?: number;
105
+ resourceCount?: number;
106
+ }
107
+
108
+ export interface MCPSerializedState {
109
+ servers: SerializedServer[];
110
+ configs: Record<string, ScopedMcpConfig>;
111
+ tools: SerializedTool[];
112
+ }
113
+
114
+ // Resource Types
115
+ export interface ServerResource extends Resource {
116
+ server: string;
117
+ serverName?: string;
118
+ }
119
+
120
+ // MCP Tool Call Types
121
+ export interface McpToolCall {
122
+ serverName: string;
123
+ toolName: string;
124
+ arguments: Record<string, unknown>;
125
+ }
126
+
127
+ export interface McpToolResult {
128
+ success: boolean;
129
+ content?: Array<{ type: string; text?: string; [key: string]: unknown }>;
130
+ error?: string;
131
+ isError?: boolean;
132
+ }
133
+
134
+ // Configuration Validation
135
+ export function isStdioConfig(config: McpServerConfig): boolean {
136
+ if (config.type === "stdio") return true;
137
+ return !!config.command && !config.url;
138
+ }
139
+
140
+ export function isRemoteConfig(config: McpServerConfig): boolean {
141
+ return !!config.url;
142
+ }
143
+
144
+ export function validateMcpServerConfig(config: unknown): config is McpServerConfig {
145
+ if (!config || typeof config !== "object") return false;
146
+ const obj = config as Record<string, unknown>;
147
+ return ("command" in obj && typeof obj["command"] === "string") ||
148
+ ("url" in obj && typeof obj["url"] === "string");
149
+ }
150
+
151
+ export function getServerTransport(config: McpServerConfig): Transport {
152
+ if (config.type === "stdio") return "stdio";
153
+ if (config.type === "sse" || config.type === "http" || config.type === "ws") return config.type;
154
+ return "stdio";
155
+ }