@kenkaiiii/ggcoder 4.3.235 → 4.3.236

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 (50) hide show
  1. package/dist/cli/command-routing.d.ts +1 -1
  2. package/dist/cli/command-routing.d.ts.map +1 -1
  3. package/dist/cli/command-routing.js +2 -0
  4. package/dist/cli/command-routing.js.map +1 -1
  5. package/dist/cli/command-routing.test.js +1 -0
  6. package/dist/cli/command-routing.test.js.map +1 -1
  7. package/dist/cli/mcp.d.ts +2 -0
  8. package/dist/cli/mcp.d.ts.map +1 -0
  9. package/dist/cli/mcp.js +309 -0
  10. package/dist/cli/mcp.js.map +1 -0
  11. package/dist/cli.d.ts.map +1 -1
  12. package/dist/cli.js +6 -2
  13. package/dist/cli.js.map +1 -1
  14. package/dist/config.d.ts +1 -0
  15. package/dist/config.d.ts.map +1 -1
  16. package/dist/config.js +1 -0
  17. package/dist/config.js.map +1 -1
  18. package/dist/core/mcp/client.d.ts +20 -0
  19. package/dist/core/mcp/client.d.ts.map +1 -1
  20. package/dist/core/mcp/client.js +55 -11
  21. package/dist/core/mcp/client.js.map +1 -1
  22. package/dist/core/mcp/defaults.d.ts +7 -0
  23. package/dist/core/mcp/defaults.d.ts.map +1 -1
  24. package/dist/core/mcp/defaults.js +14 -0
  25. package/dist/core/mcp/defaults.js.map +1 -1
  26. package/dist/core/mcp/index.d.ts +6 -1
  27. package/dist/core/mcp/index.d.ts.map +1 -1
  28. package/dist/core/mcp/index.js +3 -1
  29. package/dist/core/mcp/index.js.map +1 -1
  30. package/dist/core/mcp/parse-add-command.d.ts +25 -0
  31. package/dist/core/mcp/parse-add-command.d.ts.map +1 -0
  32. package/dist/core/mcp/parse-add-command.js +220 -0
  33. package/dist/core/mcp/parse-add-command.js.map +1 -0
  34. package/dist/core/mcp/parse-add-command.test.d.ts +2 -0
  35. package/dist/core/mcp/parse-add-command.test.d.ts.map +1 -0
  36. package/dist/core/mcp/parse-add-command.test.js +75 -0
  37. package/dist/core/mcp/parse-add-command.test.js.map +1 -0
  38. package/dist/core/mcp/store.d.ts +80 -0
  39. package/dist/core/mcp/store.d.ts.map +1 -0
  40. package/dist/core/mcp/store.js +157 -0
  41. package/dist/core/mcp/store.js.map +1 -0
  42. package/dist/core/mcp/store.test.d.ts +2 -0
  43. package/dist/core/mcp/store.test.d.ts.map +1 -0
  44. package/dist/core/mcp/store.test.js +101 -0
  45. package/dist/core/mcp/store.test.js.map +1 -0
  46. package/dist/ui/mcp.d.ts +62 -0
  47. package/dist/ui/mcp.d.ts.map +1 -0
  48. package/dist/ui/mcp.js +302 -0
  49. package/dist/ui/mcp.js.map +1 -0
  50. package/package.json +4 -4
@@ -0,0 +1,220 @@
1
+ const NAME_PATTERN = /^[A-Za-z0-9_-]+$/;
2
+ const PREFIX_PATTERN = /^(claude|ggcoder)\s+mcp\s+add\s+/i;
3
+ /**
4
+ * Shell-style tokenizer: splits on whitespace but respects single/double
5
+ * quotes and backslash escapes. Good enough for `claude mcp add …` lines.
6
+ */
7
+ function tokenize(input) {
8
+ const tokens = [];
9
+ let current = "";
10
+ let inSingle = false;
11
+ let inDouble = false;
12
+ let hasToken = false;
13
+ for (let i = 0; i < input.length; i++) {
14
+ const ch = input[i];
15
+ if (ch === "\\" && !inSingle) {
16
+ const next = input[i + 1];
17
+ if (next !== undefined) {
18
+ current += next;
19
+ hasToken = true;
20
+ i++;
21
+ continue;
22
+ }
23
+ }
24
+ if (ch === "'" && !inDouble) {
25
+ inSingle = !inSingle;
26
+ hasToken = true;
27
+ continue;
28
+ }
29
+ if (ch === '"' && !inSingle) {
30
+ inDouble = !inDouble;
31
+ hasToken = true;
32
+ continue;
33
+ }
34
+ if ((ch === " " || ch === "\t" || ch === "\n") && !inSingle && !inDouble) {
35
+ if (hasToken) {
36
+ tokens.push(current);
37
+ current = "";
38
+ hasToken = false;
39
+ }
40
+ continue;
41
+ }
42
+ current += ch;
43
+ hasToken = true;
44
+ }
45
+ if (inSingle || inDouble) {
46
+ return { ok: false, error: "Unbalanced quote in the command." };
47
+ }
48
+ if (hasToken)
49
+ tokens.push(current);
50
+ return { ok: true, value: tokens };
51
+ }
52
+ function normalizeTransport(value) {
53
+ const v = value.toLowerCase();
54
+ if (v === "stdio")
55
+ return { ok: true, value: "stdio" };
56
+ if (v === "http" || v === "streamable-http")
57
+ return { ok: true, value: "http" };
58
+ if (v === "sse")
59
+ return { ok: true, value: "sse" };
60
+ if (v === "ws" || v === "websocket")
61
+ return { ok: true, value: "ws" };
62
+ return { ok: false, error: `Unknown transport "${value}". Use stdio, http, or sse.` };
63
+ }
64
+ function mapScope(value) {
65
+ const v = value.toLowerCase();
66
+ if (v === "user" || v === "global")
67
+ return { ok: true, value: "global" };
68
+ if (v === "local" || v === "project")
69
+ return { ok: true, value: "project" };
70
+ return { ok: false, error: `Unknown scope "${value}". Use local, project, or user.` };
71
+ }
72
+ /**
73
+ * Parse a pasted `claude mcp add …` / `ggcoder mcp add …` line (or just the
74
+ * args after `add`) into an MCPServerConfig + requested scope.
75
+ *
76
+ * Grammar matches Claude Code:
77
+ * add [--transport t] [--env K=V]… [--header "K: V"]… [--scope s] <name> -- <command> [args…]
78
+ * add --transport http|sse <name> <url> [--header "K: V"]…
79
+ */
80
+ export function parseMcpAddCommand(input) {
81
+ const stripped = input.trim().replace(PREFIX_PATTERN, "");
82
+ if (!stripped.trim()) {
83
+ return { ok: false, error: "Nothing to parse — provide a server name and command or URL." };
84
+ }
85
+ const tokenized = tokenize(stripped);
86
+ if (!tokenized.ok)
87
+ return tokenized;
88
+ const tokens = tokenized.value;
89
+ let transport;
90
+ const env = {};
91
+ const headers = {};
92
+ let scope;
93
+ let timeout;
94
+ let name;
95
+ const positionals = [];
96
+ let commandArgv;
97
+ for (let i = 0; i < tokens.length; i++) {
98
+ const tok = tokens[i];
99
+ // `--` ends option parsing; remainder is the literal spawn argv.
100
+ if (tok === "--") {
101
+ commandArgv = tokens.slice(i + 1);
102
+ break;
103
+ }
104
+ const needNext = (label) => {
105
+ const next = tokens[i + 1];
106
+ if (next === undefined)
107
+ return { ok: false, error: `${label} expects a value.` };
108
+ i++;
109
+ return { ok: true, value: next };
110
+ };
111
+ if (tok === "--transport" || tok === "-t") {
112
+ const next = needNext(tok);
113
+ if (!next.ok)
114
+ return next;
115
+ const t = normalizeTransport(next.value);
116
+ if (!t.ok)
117
+ return t;
118
+ transport = t.value;
119
+ continue;
120
+ }
121
+ if (tok === "--env" || tok === "-e") {
122
+ const next = needNext(tok);
123
+ if (!next.ok)
124
+ return next;
125
+ const eq = next.value.indexOf("=");
126
+ if (eq <= 0)
127
+ return { ok: false, error: `Invalid --env "${next.value}". Use KEY=VALUE.` };
128
+ env[next.value.slice(0, eq)] = next.value.slice(eq + 1);
129
+ continue;
130
+ }
131
+ if (tok === "--header" || tok === "-H") {
132
+ const next = needNext(tok);
133
+ if (!next.ok)
134
+ return next;
135
+ const colon = next.value.indexOf(":");
136
+ if (colon <= 0) {
137
+ return { ok: false, error: `Invalid --header "${next.value}". Use "Name: Value".` };
138
+ }
139
+ headers[next.value.slice(0, colon).trim()] = next.value.slice(colon + 1).trim();
140
+ continue;
141
+ }
142
+ if (tok === "--scope" || tok === "-s") {
143
+ const next = needNext(tok);
144
+ if (!next.ok)
145
+ return next;
146
+ const s = mapScope(next.value);
147
+ if (!s.ok)
148
+ return s;
149
+ scope = s.value;
150
+ continue;
151
+ }
152
+ if (tok === "--timeout") {
153
+ const next = needNext(tok);
154
+ if (!next.ok)
155
+ return next;
156
+ const n = Number(next.value);
157
+ if (!Number.isFinite(n) || n <= 0) {
158
+ return { ok: false, error: `Invalid --timeout "${next.value}".` };
159
+ }
160
+ timeout = n;
161
+ continue;
162
+ }
163
+ if (tok.startsWith("-")) {
164
+ return { ok: false, error: `Unknown option "${tok}".` };
165
+ }
166
+ // First non-flag token is the name; the rest are positionals (e.g. url).
167
+ if (name === undefined) {
168
+ name = tok;
169
+ }
170
+ else {
171
+ positionals.push(tok);
172
+ }
173
+ }
174
+ if (!name) {
175
+ return { ok: false, error: "Missing server name." };
176
+ }
177
+ if (!NAME_PATTERN.test(name)) {
178
+ return {
179
+ ok: false,
180
+ error: `Invalid server name "${name}". Use only letters, numbers, hyphens, and underscores.`,
181
+ };
182
+ }
183
+ if (transport === "ws") {
184
+ return { ok: false, error: "WebSocket transport isn't supported yet." };
185
+ }
186
+ const effectiveTransport = transport ?? (commandArgv ? "stdio" : positionals.length > 0 ? "http" : "stdio");
187
+ const config = { name };
188
+ if (timeout !== undefined)
189
+ config.timeout = timeout;
190
+ if (effectiveTransport === "http" || effectiveTransport === "sse") {
191
+ const url = positionals[0];
192
+ if (!url) {
193
+ return {
194
+ ok: false,
195
+ error: `The ${effectiveTransport} transport requires a URL after the name.`,
196
+ };
197
+ }
198
+ config.url = url;
199
+ if (Object.keys(headers).length > 0)
200
+ config.headers = headers;
201
+ }
202
+ else {
203
+ // stdio
204
+ if (!commandArgv || commandArgv.length === 0) {
205
+ return {
206
+ ok: false,
207
+ error: "stdio servers need a command after `--`, e.g. `name -- npx -y some-mcp-server`.",
208
+ };
209
+ }
210
+ config.command = commandArgv[0];
211
+ if (commandArgv.length > 1)
212
+ config.args = commandArgv.slice(1);
213
+ if (Object.keys(env).length > 0)
214
+ config.env = env;
215
+ }
216
+ return scope === undefined
217
+ ? { ok: true, value: { config } }
218
+ : { ok: true, value: { config, scope } };
219
+ }
220
+ //# sourceMappingURL=parse-add-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-add-command.js","sourceRoot":"","sources":["../../../src/core/mcp/parse-add-command.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=parse-add-command.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-add-command.test.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/parse-add-command.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,75 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { parseMcpAddCommand } from "./parse-add-command.js";
3
+ describe("parseMcpAddCommand", () => {
4
+ it("parses an http server (Notion docs example)", () => {
5
+ const r = parseMcpAddCommand("--transport http notion https://mcp.notion.com/mcp");
6
+ expect(r).toEqual({
7
+ ok: true,
8
+ value: { config: { name: "notion", url: "https://mcp.notion.com/mcp" } },
9
+ });
10
+ });
11
+ it("parses an sse server (Asana docs example)", () => {
12
+ const r = parseMcpAddCommand("--transport sse asana https://mcp.asana.com/sse");
13
+ expect(r.ok && r.value.config).toEqual({ name: "asana", url: "https://mcp.asana.com/sse" });
14
+ });
15
+ it("parses a stdio server with --env (Airtable docs example)", () => {
16
+ const r = parseMcpAddCommand("--env AIRTABLE_API_KEY=key airtable -- npx -y airtable-mcp-server");
17
+ expect(r.ok && r.value.config).toEqual({
18
+ name: "airtable",
19
+ command: "npx",
20
+ args: ["-y", "airtable-mcp-server"],
21
+ env: { AIRTABLE_API_KEY: "key" },
22
+ });
23
+ });
24
+ it("parses a bearer --header", () => {
25
+ const r = parseMcpAddCommand('--transport http api https://api.example/mcp --header "Authorization: Bearer tok"');
26
+ expect(r.ok && r.value.config.headers).toEqual({ Authorization: "Bearer tok" });
27
+ });
28
+ it("strips a `claude mcp add` prefix", () => {
29
+ const r = parseMcpAddCommand("claude mcp add --transport http notion https://mcp.notion.com/mcp");
30
+ expect(r.ok && r.value.config.name).toBe("notion");
31
+ });
32
+ it("strips a `ggcoder mcp add` prefix", () => {
33
+ const r = parseMcpAddCommand("ggcoder mcp add foo -- node server.js");
34
+ expect(r.ok && r.value.config).toEqual({
35
+ name: "foo",
36
+ command: "node",
37
+ args: ["server.js"],
38
+ });
39
+ });
40
+ it("handles quoted args", () => {
41
+ const r = parseMcpAddCommand('foo -- node "my server.js" --flag "a b"');
42
+ expect(r.ok && r.value.config.args).toEqual(["my server.js", "--flag", "a b"]);
43
+ });
44
+ it("maps --scope user to global and project to project", () => {
45
+ const u = parseMcpAddCommand("--scope user --transport http n https://x/mcp");
46
+ expect(u.ok && u.value.scope).toBe("global");
47
+ const p = parseMcpAddCommand("--scope project --transport http n https://x/mcp");
48
+ expect(p.ok && p.value.scope).toBe("project");
49
+ });
50
+ it("defaults stdio when a command follows --", () => {
51
+ const r = parseMcpAddCommand("local -- ./bin/server");
52
+ expect(r.ok && r.value.config.command).toBe("./bin/server");
53
+ });
54
+ it("errors on missing name", () => {
55
+ const r = parseMcpAddCommand("--transport http");
56
+ expect(r.ok).toBe(false);
57
+ });
58
+ it("errors when http transport has no url", () => {
59
+ const r = parseMcpAddCommand("--transport http notion");
60
+ expect(r.ok).toBe(false);
61
+ });
62
+ it("errors when stdio has no command", () => {
63
+ const r = parseMcpAddCommand("notion");
64
+ expect(r.ok).toBe(false);
65
+ });
66
+ it("rejects websocket transport", () => {
67
+ const r = parseMcpAddCommand("--transport ws notion ws://x");
68
+ expect(r).toEqual({ ok: false, error: "WebSocket transport isn't supported yet." });
69
+ });
70
+ it("rejects an unsafe name", () => {
71
+ const r = parseMcpAddCommand("bad/name -- node x.js");
72
+ expect(r.ok).toBe(false);
73
+ });
74
+ });
75
+ //# sourceMappingURL=parse-add-command.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-add-command.test.js","sourceRoot":"","sources":["../../../src/core/mcp/parse-add-command.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ import { z } from "zod";
2
+ import type { MCPServerConfig } from "./types.js";
3
+ /**
4
+ * Where a server config lives. We collapse Claude Code's three scopes
5
+ * (local/project/user) into two:
6
+ * - "global" → ~/.gg/mcp.json (all GG Coder sessions)
7
+ * - "project" → ./.gg/mcp.json (the current project root)
8
+ */
9
+ export type MCPScope = "global" | "project";
10
+ /**
11
+ * On-disk entry shape. Accepts both Claude's `.mcp.json` fields
12
+ * (`type`, `url`, `headers`, `command`, `args`, `env`, `timeout`) and our
13
+ * extra (`enabled`) so configs are portable in both directions.
14
+ */
15
+ declare const StoredServerEntrySchema: z.ZodObject<{
16
+ type: z.ZodOptional<z.ZodEnum<{
17
+ stdio: "stdio";
18
+ http: "http";
19
+ "streamable-http": "streamable-http";
20
+ sse: "sse";
21
+ }>>;
22
+ url: z.ZodOptional<z.ZodString>;
23
+ headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
24
+ command: z.ZodOptional<z.ZodString>;
25
+ args: z.ZodOptional<z.ZodArray<z.ZodString>>;
26
+ env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
27
+ timeout: z.ZodOptional<z.ZodNumber>;
28
+ enabled: z.ZodOptional<z.ZodBoolean>;
29
+ }, z.core.$loose>;
30
+ export type StoredServerEntry = z.infer<typeof StoredServerEntrySchema>;
31
+ declare const McpFileSchema: z.ZodObject<{
32
+ mcpServers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
33
+ type: z.ZodOptional<z.ZodEnum<{
34
+ stdio: "stdio";
35
+ http: "http";
36
+ "streamable-http": "streamable-http";
37
+ sse: "sse";
38
+ }>>;
39
+ url: z.ZodOptional<z.ZodString>;
40
+ headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
41
+ command: z.ZodOptional<z.ZodString>;
42
+ args: z.ZodOptional<z.ZodArray<z.ZodString>>;
43
+ env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
44
+ timeout: z.ZodOptional<z.ZodNumber>;
45
+ enabled: z.ZodOptional<z.ZodBoolean>;
46
+ }, z.core.$loose>>>;
47
+ }, z.core.$strip>;
48
+ export type McpFile = z.infer<typeof McpFileSchema>;
49
+ /** A loaded server config paired with the scope it came from. */
50
+ export interface ScopedServer {
51
+ config: MCPServerConfig;
52
+ scope: MCPScope;
53
+ }
54
+ export declare function globalMcpPath(): string;
55
+ export declare function projectMcpPath(cwd: string): string;
56
+ /** Map an on-disk entry to a runtime MCPServerConfig. */
57
+ export declare function fromStoredEntry(name: string, entry: StoredServerEntry): MCPServerConfig;
58
+ /** Map a runtime MCPServerConfig to the on-disk (Claude-compatible) entry. */
59
+ export declare function toStoredEntry(config: MCPServerConfig): StoredServerEntry;
60
+ /**
61
+ * Load servers from both scopes. Project wins on name collision (global entries
62
+ * whose name also exists in project are dropped). Each result carries its scope.
63
+ */
64
+ export declare function loadServers(cwd: string): Promise<ScopedServer[]>;
65
+ /**
66
+ * Add (or overwrite) a server in the target scope's file. Rejects a duplicate
67
+ * name within the same scope unless `overwrite` is true.
68
+ */
69
+ export declare function addServer(entry: MCPServerConfig, scope: MCPScope, cwd: string, overwrite?: boolean): Promise<{
70
+ ok: true;
71
+ } | {
72
+ ok: false;
73
+ error: string;
74
+ }>;
75
+ /** Remove a server from a scope. Returns true if it existed. */
76
+ export declare function removeServer(name: string, scope: MCPScope, cwd: string): Promise<boolean>;
77
+ /** Look up a single server across both scopes (project wins). */
78
+ export declare function getServer(name: string, cwd: string): Promise<ScopedServer | undefined>;
79
+ export {};
80
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/store.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE5C;;;;GAIG;AACH,QAAA,MAAM,uBAAuB;;;;;;;;;;;;;;iBAWb,CAAC;AAEjB,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,QAAA,MAAM,aAAa;;;;;;;;;;;;;;;;iBAEjB,CAAC;AAEH,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAEpD,iEAAiE;AACjE,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,eAAe,CAAC;IACxB,KAAK,EAAE,QAAQ,CAAC;CACjB;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAElD;AA6BD,yDAAyD;AACzD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,GAAG,eAAe,CAcvF;AAED,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,MAAM,EAAE,eAAe,GAAG,iBAAiB,CAexE;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAiBtE;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,KAAK,EAAE,eAAe,EACtB,KAAK,EAAE,QAAQ,EACf,GAAG,EAAE,MAAM,EACX,SAAS,UAAQ,GAChB,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAYtD;AAED,gEAAgE;AAChE,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO/F;AAED,iEAAiE;AACjE,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAG5F"}
@@ -0,0 +1,157 @@
1
+ import path from "node:path";
2
+ import fs from "node:fs/promises";
3
+ import { z } from "zod";
4
+ import { getAppPaths } from "../../config.js";
5
+ import { log } from "../logger.js";
6
+ /**
7
+ * On-disk entry shape. Accepts both Claude's `.mcp.json` fields
8
+ * (`type`, `url`, `headers`, `command`, `args`, `env`, `timeout`) and our
9
+ * extra (`enabled`) so configs are portable in both directions.
10
+ */
11
+ const StoredServerEntrySchema = z
12
+ .object({
13
+ type: z.enum(["stdio", "http", "streamable-http", "sse"]).optional(),
14
+ url: z.string().optional(),
15
+ headers: z.record(z.string(), z.string()).optional(),
16
+ command: z.string().optional(),
17
+ args: z.array(z.string()).optional(),
18
+ env: z.record(z.string(), z.string()).optional(),
19
+ timeout: z.number().optional(),
20
+ enabled: z.boolean().optional(),
21
+ })
22
+ .passthrough();
23
+ const McpFileSchema = z.object({
24
+ mcpServers: z.record(z.string(), StoredServerEntrySchema).default({}),
25
+ });
26
+ export function globalMcpPath() {
27
+ return getAppPaths().mcpFile;
28
+ }
29
+ export function projectMcpPath(cwd) {
30
+ return path.join(cwd, ".gg", "mcp.json");
31
+ }
32
+ /**
33
+ * Read + validate one mcp.json file. On missing file → empty. On malformed
34
+ * JSON/schema → log + treat as empty (don't crash), matching SettingsManager.
35
+ */
36
+ async function readMcpFile(filePath) {
37
+ let content;
38
+ try {
39
+ content = await fs.readFile(filePath, "utf-8");
40
+ }
41
+ catch {
42
+ return { mcpServers: {} };
43
+ }
44
+ try {
45
+ const raw = JSON.parse(content);
46
+ return McpFileSchema.parse(raw);
47
+ }
48
+ catch (err) {
49
+ log("WARN", "mcp", `Ignoring malformed MCP config at ${filePath}`, {
50
+ error: err instanceof Error ? err.message : String(err),
51
+ });
52
+ return { mcpServers: {} };
53
+ }
54
+ }
55
+ async function writeMcpFile(filePath, file) {
56
+ await fs.mkdir(path.dirname(filePath), { recursive: true, mode: 0o700 });
57
+ await fs.writeFile(filePath, JSON.stringify(file, null, 2) + "\n", "utf-8");
58
+ }
59
+ /** Map an on-disk entry to a runtime MCPServerConfig. */
60
+ export function fromStoredEntry(name, entry) {
61
+ const config = { name };
62
+ // URL transports: explicit url, or type marks it as http/sse.
63
+ if (entry.url) {
64
+ config.url = entry.url;
65
+ if (entry.headers)
66
+ config.headers = entry.headers;
67
+ }
68
+ else if (entry.command) {
69
+ config.command = entry.command;
70
+ if (entry.args)
71
+ config.args = entry.args;
72
+ if (entry.env)
73
+ config.env = entry.env;
74
+ }
75
+ if (typeof entry.timeout === "number")
76
+ config.timeout = entry.timeout;
77
+ if (typeof entry.enabled === "boolean")
78
+ config.enabled = entry.enabled;
79
+ return config;
80
+ }
81
+ /** Map a runtime MCPServerConfig to the on-disk (Claude-compatible) entry. */
82
+ export function toStoredEntry(config) {
83
+ const entry = {};
84
+ if (config.url) {
85
+ entry.type = "http";
86
+ entry.url = config.url;
87
+ if (config.headers)
88
+ entry.headers = config.headers;
89
+ }
90
+ else if (config.command) {
91
+ entry.type = "stdio";
92
+ entry.command = config.command;
93
+ if (config.args)
94
+ entry.args = config.args;
95
+ if (config.env)
96
+ entry.env = config.env;
97
+ }
98
+ if (typeof config.timeout === "number")
99
+ entry.timeout = config.timeout;
100
+ if (typeof config.enabled === "boolean")
101
+ entry.enabled = config.enabled;
102
+ return entry;
103
+ }
104
+ /**
105
+ * Load servers from both scopes. Project wins on name collision (global entries
106
+ * whose name also exists in project are dropped). Each result carries its scope.
107
+ */
108
+ export async function loadServers(cwd) {
109
+ const [globalFile, projectFile] = await Promise.all([
110
+ readMcpFile(globalMcpPath()),
111
+ readMcpFile(projectMcpPath(cwd)),
112
+ ]);
113
+ const projectNames = new Set(Object.keys(projectFile.mcpServers));
114
+ const result = [];
115
+ for (const [name, entry] of Object.entries(globalFile.mcpServers)) {
116
+ if (projectNames.has(name))
117
+ continue; // project overrides global
118
+ result.push({ config: fromStoredEntry(name, entry), scope: "global" });
119
+ }
120
+ for (const [name, entry] of Object.entries(projectFile.mcpServers)) {
121
+ result.push({ config: fromStoredEntry(name, entry), scope: "project" });
122
+ }
123
+ return result;
124
+ }
125
+ /**
126
+ * Add (or overwrite) a server in the target scope's file. Rejects a duplicate
127
+ * name within the same scope unless `overwrite` is true.
128
+ */
129
+ export async function addServer(entry, scope, cwd, overwrite = false) {
130
+ const filePath = scope === "global" ? globalMcpPath() : projectMcpPath(cwd);
131
+ const file = await readMcpFile(filePath);
132
+ if (file.mcpServers[entry.name] && !overwrite) {
133
+ return {
134
+ ok: false,
135
+ error: `A "${entry.name}" server already exists in ${scope} scope. Remove it first or use a different name.`,
136
+ };
137
+ }
138
+ file.mcpServers[entry.name] = toStoredEntry(entry);
139
+ await writeMcpFile(filePath, file);
140
+ return { ok: true };
141
+ }
142
+ /** Remove a server from a scope. Returns true if it existed. */
143
+ export async function removeServer(name, scope, cwd) {
144
+ const filePath = scope === "global" ? globalMcpPath() : projectMcpPath(cwd);
145
+ const file = await readMcpFile(filePath);
146
+ if (!file.mcpServers[name])
147
+ return false;
148
+ delete file.mcpServers[name];
149
+ await writeMcpFile(filePath, file);
150
+ return true;
151
+ }
152
+ /** Look up a single server across both scopes (project wins). */
153
+ export async function getServer(name, cwd) {
154
+ const servers = await loadServers(cwd);
155
+ return servers.find((s) => s.config.name === name);
156
+ }
157
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/core/mcp/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=store.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.test.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/store.test.ts"],"names":[],"mappings":""}