@kwonye/mcpx 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 (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +150 -0
  3. package/dist/adapters/claude.d.ts +7 -0
  4. package/dist/adapters/claude.js +69 -0
  5. package/dist/adapters/claude.js.map +1 -0
  6. package/dist/adapters/cline.d.ts +7 -0
  7. package/dist/adapters/cline.js +65 -0
  8. package/dist/adapters/cline.js.map +1 -0
  9. package/dist/adapters/codex.d.ts +7 -0
  10. package/dist/adapters/codex.js +52 -0
  11. package/dist/adapters/codex.js.map +1 -0
  12. package/dist/adapters/cursor.d.ts +7 -0
  13. package/dist/adapters/cursor.js +52 -0
  14. package/dist/adapters/cursor.js.map +1 -0
  15. package/dist/adapters/index.d.ts +2 -0
  16. package/dist/adapters/index.js +15 -0
  17. package/dist/adapters/index.js.map +1 -0
  18. package/dist/adapters/utils.d.ts +10 -0
  19. package/dist/adapters/utils.js +69 -0
  20. package/dist/adapters/utils.js.map +1 -0
  21. package/dist/adapters/vscode.d.ts +7 -0
  22. package/dist/adapters/vscode.js +52 -0
  23. package/dist/adapters/vscode.js.map +1 -0
  24. package/dist/cli.d.ts +2 -0
  25. package/dist/cli.js +577 -0
  26. package/dist/cli.js.map +1 -0
  27. package/dist/core/config.d.ts +4 -0
  28. package/dist/core/config.js +71 -0
  29. package/dist/core/config.js.map +1 -0
  30. package/dist/core/daemon.d.ts +25 -0
  31. package/dist/core/daemon.js +174 -0
  32. package/dist/core/daemon.js.map +1 -0
  33. package/dist/core/managed-index.d.ts +4 -0
  34. package/dist/core/managed-index.js +22 -0
  35. package/dist/core/managed-index.js.map +1 -0
  36. package/dist/core/paths.d.ts +12 -0
  37. package/dist/core/paths.js +46 -0
  38. package/dist/core/paths.js.map +1 -0
  39. package/dist/core/registry.d.ts +7 -0
  40. package/dist/core/registry.js +33 -0
  41. package/dist/core/registry.js.map +1 -0
  42. package/dist/core/secrets.d.ts +16 -0
  43. package/dist/core/secrets.js +108 -0
  44. package/dist/core/secrets.js.map +1 -0
  45. package/dist/core/server-auth.d.ts +19 -0
  46. package/dist/core/server-auth.js +112 -0
  47. package/dist/core/server-auth.js.map +1 -0
  48. package/dist/core/sync.d.ts +9 -0
  49. package/dist/core/sync.js +63 -0
  50. package/dist/core/sync.js.map +1 -0
  51. package/dist/gateway/server.d.ts +9 -0
  52. package/dist/gateway/server.js +960 -0
  53. package/dist/gateway/server.js.map +1 -0
  54. package/dist/types.d.ts +86 -0
  55. package/dist/types.js +2 -0
  56. package/dist/types.js.map +1 -0
  57. package/dist/util/fs.d.ts +4 -0
  58. package/dist/util/fs.js +34 -0
  59. package/dist/util/fs.js.map +1 -0
  60. package/dist/version.d.ts +1 -0
  61. package/dist/version.js +2 -0
  62. package/dist/version.js.map +1 -0
  63. package/package.json +60 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 kwonye
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,150 @@
1
+ # mcpx
2
+
3
+ Install MCP servers once, expose them to multiple clients through one local HTTP gateway.
4
+
5
+ ## What it does
6
+
7
+ - Stores upstream MCP server definitions in one place: `~/.config/mcpx/config.json`
8
+ - Runs a local gateway at `http://127.0.0.1:<port>/mcp`
9
+ - Syncs managed entries into supported clients (Claude, Codex, Cursor, Cline, VS Code)
10
+ - Creates one managed entry per upstream server name
11
+ - Routes client traffic through one daemon while preserving per-upstream auth/headers
12
+
13
+ ## Install
14
+
15
+ Prerequisite: Node.js `>=20`
16
+
17
+ ```bash
18
+ npm install -g @kwonye/mcpx@latest
19
+ mcpx --help
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ### Path A: Add servers with CLI (recommended)
25
+
26
+ ```bash
27
+ # HTTP upstream
28
+ mcpx add vercel --transport http https://example.com/mcp
29
+
30
+ # stdio upstream
31
+ mcpx add next-devtools --transport stdio npx next-devtools-mcp@latest
32
+ ```
33
+
34
+ `mcpx add` and `mcpx remove` auto-sync by default. Run `mcpx sync` when you want a manual re-sync or to target specific clients.
35
+
36
+ ### Path B: Add servers manually in JSON config
37
+
38
+ Edit `~/.config/mcpx/config.json` and add entries under `servers`:
39
+
40
+ ```json
41
+ {
42
+ "servers": {
43
+ "vercel": {
44
+ "transport": "http",
45
+ "url": "https://example.com/mcp",
46
+ "headers": {
47
+ "Authorization": "secret://vercel_auth_header"
48
+ }
49
+ },
50
+ "next-devtools": {
51
+ "transport": "stdio",
52
+ "command": "npx",
53
+ "args": ["next-devtools-mcp@latest"],
54
+ "env": {
55
+ "FOO": "bar"
56
+ },
57
+ "cwd": "/path/to/project"
58
+ }
59
+ }
60
+ }
61
+ ```
62
+
63
+ After manual edits, you must run:
64
+
65
+ ```bash
66
+ mcpx sync
67
+ ```
68
+
69
+ Manual config changes do not update client configs until `mcpx sync` runs.
70
+
71
+ ## Supported Clients
72
+
73
+ - Claude
74
+ - Codex
75
+ - Cursor
76
+ - Cline
77
+ - VS Code
78
+
79
+ ## Claude Convention
80
+
81
+ `mcpx` follows Claude-style MCP server conventions by syncing per-upstream entries keyed by server name under `mcpServers` in Claude config. Each entry is an HTTP endpoint to the local gateway (`/mcp?upstream=<name>`) and includes the required local auth header.
82
+
83
+ ## How it works
84
+
85
+ 1. Define upstream servers in central `mcpx` config.
86
+ 2. `mcpx` ensures local gateway auth and daemon state.
87
+ 3. `mcpx sync` writes managed client entries that point to the local gateway.
88
+
89
+ ## Advanced Usage
90
+
91
+ ### Auth and secrets
92
+
93
+ ```bash
94
+ mcpx secret set vercel_auth_header --value "Bearer <token>"
95
+ mcpx secret ls
96
+ mcpx secret rm vercel_auth_header
97
+
98
+ mcpx auth set vercel --header Authorization --value "Bearer <token>"
99
+ mcpx auth set next-devtools --env NEXT_DEVTOOLS_TOKEN --value "<token>"
100
+ mcpx auth show
101
+ mcpx auth rm vercel --header Authorization --delete-secret
102
+ mcpx auth rotate-local-token
103
+ ```
104
+
105
+ ### Daemon lifecycle
106
+
107
+ ```bash
108
+ mcpx daemon start
109
+ mcpx daemon status
110
+ mcpx daemon logs
111
+ mcpx daemon stop
112
+ ```
113
+
114
+ ### Targeted sync
115
+
116
+ ```bash
117
+ mcpx sync
118
+ mcpx sync claude
119
+ mcpx sync --client claude --client codex
120
+ ```
121
+
122
+ ### Config/data/state path overrides
123
+
124
+ - `MCPX_CONFIG_HOME`
125
+ - `MCPX_DATA_HOME`
126
+ - `MCPX_STATE_HOME`
127
+
128
+ ## Troubleshooting
129
+
130
+ ```bash
131
+ mcpx doctor
132
+ mcpx status
133
+ mcpx daemon logs
134
+ mcpx sync --json
135
+ ```
136
+
137
+ ## Build and test from source
138
+
139
+ ```bash
140
+ npm install
141
+ npm run build
142
+ npm test
143
+ ```
144
+
145
+ ## Notes
146
+
147
+ - Client connectivity is HTTP-first.
148
+ - Upstreams can be HTTP or stdio.
149
+ - macOS keychain is the secure secret backend.
150
+ - In CI/headless environments, `MCPX_SECRET_<name>` env vars can override keychain lookups.
@@ -0,0 +1,7 @@
1
+ import type { ClientAdapter, McpxConfig, SyncClientOptions, SyncResult } from "../types.js";
2
+ export declare class ClaudeAdapter implements ClientAdapter {
3
+ readonly id: "claude";
4
+ detectConfigPath(): string | null;
5
+ supportsHttp(): boolean;
6
+ syncGateway(_config: McpxConfig, options: SyncClientOptions): SyncResult;
7
+ }
@@ -0,0 +1,69 @@
1
+ import os from "node:os";
2
+ import path from "node:path";
3
+ import { readJsonFile, writeJsonAtomic } from "../util/fs.js";
4
+ import { ensureManagedEntryWritable, errorResult, okResult, pruneStaleManagedEntries, setManagedEntries } from "./utils.js";
5
+ export class ClaudeAdapter {
6
+ id = "claude";
7
+ detectConfigPath() {
8
+ return path.join(os.homedir(), ".claude.json");
9
+ }
10
+ supportsHttp() {
11
+ return true;
12
+ }
13
+ syncGateway(_config, options) {
14
+ const configPath = this.detectConfigPath();
15
+ if (!configPath) {
16
+ return errorResult(this.id, undefined, "Unable to resolve Claude config path.");
17
+ }
18
+ try {
19
+ const raw = readJsonFile(configPath, {});
20
+ const managedNames = options.managedEntries.map((entry) => entry.name);
21
+ const serverEntries = Object.fromEntries(options.managedEntries.map((entry) => [entry.name, {
22
+ type: "http",
23
+ url: entry.url,
24
+ headers: entry.headers
25
+ }]));
26
+ const topLevelServers = (raw.mcpServers ?? {});
27
+ for (const name of managedNames) {
28
+ const topLevelConflict = ensureManagedEntryWritable(options.managedIndex, this.id, name, topLevelServers[name]);
29
+ if (topLevelConflict) {
30
+ return errorResult(this.id, configPath, topLevelConflict);
31
+ }
32
+ }
33
+ pruneStaleManagedEntries(options.managedIndex, this.id, topLevelServers, managedNames);
34
+ for (const [name, entry] of Object.entries(serverEntries)) {
35
+ topLevelServers[name] = entry;
36
+ }
37
+ raw.mcpServers = topLevelServers;
38
+ if (raw.projects && typeof raw.projects === "object") {
39
+ const projects = raw.projects;
40
+ for (const [projectPath, projectValue] of Object.entries(projects)) {
41
+ if (!projectValue || typeof projectValue !== "object") {
42
+ continue;
43
+ }
44
+ const project = projectValue;
45
+ const projectServers = (project.mcpServers ?? {});
46
+ for (const name of managedNames) {
47
+ const projectConflict = ensureManagedEntryWritable(options.managedIndex, this.id, name, projectServers[name]);
48
+ if (projectConflict) {
49
+ return errorResult(this.id, configPath, `${projectConflict} (project: ${projectPath})`);
50
+ }
51
+ }
52
+ pruneStaleManagedEntries(options.managedIndex, this.id, projectServers, managedNames);
53
+ for (const [name, entry] of Object.entries(serverEntries)) {
54
+ projectServers[name] = entry;
55
+ }
56
+ project.mcpServers = projectServers;
57
+ projects[projectPath] = project;
58
+ }
59
+ }
60
+ writeJsonAtomic(configPath, raw);
61
+ setManagedEntries(options.managedIndex, this.id, configPath, Object.fromEntries(Object.entries(serverEntries).map(([name, entry]) => [name, JSON.stringify(entry)])));
62
+ return okResult(this.id, configPath);
63
+ }
64
+ catch (error) {
65
+ return errorResult(this.id, configPath, error.message);
66
+ }
67
+ }
68
+ }
69
+ //# sourceMappingURL=claude.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude.js","sourceRoot":"","sources":["../../src/adapters/claude.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { ClientAdapter, McpxConfig, SyncClientOptions, SyncResult } from "../types.js";
2
+ export declare class ClineAdapter implements ClientAdapter {
3
+ readonly id: "cline";
4
+ detectConfigPath(): string | null;
5
+ supportsHttp(): boolean;
6
+ syncGateway(_config: McpxConfig, options: SyncClientOptions): SyncResult;
7
+ }
@@ -0,0 +1,65 @@
1
+ import os from "node:os";
2
+ import path from "node:path";
3
+ import fs from "node:fs";
4
+ import { readJsonFile, writeJsonAtomic } from "../util/fs.js";
5
+ import { ensureManagedEntryWritable, errorResult, okResult, pruneStaleManagedEntries, setManagedEntries } from "./utils.js";
6
+ function getCandidatePaths() {
7
+ return [
8
+ path.join(os.homedir(), "Library", "Application Support", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json"),
9
+ path.join(os.homedir(), "Library", "Application Support", "Cursor", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json")
10
+ ];
11
+ }
12
+ export class ClineAdapter {
13
+ id = "cline";
14
+ detectConfigPath() {
15
+ const candidates = getCandidatePaths();
16
+ for (const candidate of candidates) {
17
+ if (fs.existsSync(candidate)) {
18
+ return candidate;
19
+ }
20
+ }
21
+ return candidates[0] ?? null;
22
+ }
23
+ supportsHttp() {
24
+ return true;
25
+ }
26
+ syncGateway(_config, options) {
27
+ const configPath = this.detectConfigPath();
28
+ if (!configPath) {
29
+ return errorResult(this.id, undefined, "Unable to resolve Cline MCP config path.");
30
+ }
31
+ try {
32
+ const raw = readJsonFile(configPath, {});
33
+ const servers = {
34
+ ...(raw.mcpServers ?? {})
35
+ };
36
+ const managedNames = options.managedEntries.map((entry) => entry.name);
37
+ const serverEntries = Object.fromEntries(options.managedEntries.map((entry) => [entry.name, {
38
+ transportType: "http",
39
+ url: entry.url,
40
+ headers: entry.headers
41
+ }]));
42
+ for (const name of managedNames) {
43
+ const conflict = ensureManagedEntryWritable(options.managedIndex, this.id, name, servers[name]);
44
+ if (conflict) {
45
+ return errorResult(this.id, configPath, conflict);
46
+ }
47
+ }
48
+ pruneStaleManagedEntries(options.managedIndex, this.id, servers, managedNames);
49
+ for (const [name, entry] of Object.entries(serverEntries)) {
50
+ servers[name] = entry;
51
+ }
52
+ const next = {
53
+ ...raw,
54
+ mcpServers: servers
55
+ };
56
+ writeJsonAtomic(configPath, next);
57
+ setManagedEntries(options.managedIndex, this.id, configPath, Object.fromEntries(Object.entries(serverEntries).map(([name, entry]) => [name, JSON.stringify(entry)])));
58
+ return okResult(this.id, configPath);
59
+ }
60
+ catch (error) {
61
+ return errorResult(this.id, configPath, error.message);
62
+ }
63
+ }
64
+ }
65
+ //# sourceMappingURL=cline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cline.js","sourceRoot":"","sources":["../../src/adapters/cline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { ClientAdapter, McpxConfig, SyncClientOptions, SyncResult } from "../types.js";
2
+ export declare class CodexAdapter implements ClientAdapter {
3
+ readonly id: "codex";
4
+ detectConfigPath(): string | null;
5
+ supportsHttp(): boolean;
6
+ syncGateway(_config: McpxConfig, options: SyncClientOptions): SyncResult;
7
+ }
@@ -0,0 +1,52 @@
1
+ import os from "node:os";
2
+ import path from "node:path";
3
+ import fs from "node:fs";
4
+ import { parse, stringify } from "@iarna/toml";
5
+ import { ensureManagedEntryWritable, errorResult, okResult, pruneStaleManagedEntries, setManagedEntries } from "./utils.js";
6
+ export class CodexAdapter {
7
+ id = "codex";
8
+ detectConfigPath() {
9
+ return path.join(os.homedir(), ".codex", "config.toml");
10
+ }
11
+ supportsHttp() {
12
+ return true;
13
+ }
14
+ syncGateway(_config, options) {
15
+ const configPath = this.detectConfigPath();
16
+ if (!configPath) {
17
+ return errorResult(this.id, undefined, "Unable to resolve Codex config.toml path.");
18
+ }
19
+ try {
20
+ const existing = fs.existsSync(configPath) ? fs.readFileSync(configPath, "utf8") : "";
21
+ const doc = existing.trim().length > 0 ? parse(existing) : {};
22
+ const mcpServers = (doc.mcp_servers ?? {});
23
+ const managedNames = options.managedEntries.map((entry) => entry.name);
24
+ const serverEntries = Object.fromEntries(options.managedEntries.map((entry) => [entry.name, {
25
+ enabled: true,
26
+ url: entry.url,
27
+ headers: entry.headers
28
+ }]));
29
+ for (const name of managedNames) {
30
+ const conflict = ensureManagedEntryWritable(options.managedIndex, this.id, name, mcpServers[name]);
31
+ if (conflict) {
32
+ return errorResult(this.id, configPath, conflict);
33
+ }
34
+ }
35
+ pruneStaleManagedEntries(options.managedIndex, this.id, mcpServers, managedNames);
36
+ for (const [name, entry] of Object.entries(serverEntries)) {
37
+ mcpServers[name] = entry;
38
+ }
39
+ doc.mcp_servers = mcpServers;
40
+ fs.mkdirSync(path.dirname(configPath), { recursive: true });
41
+ const tmpPath = `${configPath}.tmp-${process.pid}-${Date.now()}`;
42
+ fs.writeFileSync(tmpPath, stringify(doc), { mode: 0o600 });
43
+ fs.renameSync(tmpPath, configPath);
44
+ setManagedEntries(options.managedIndex, this.id, configPath, Object.fromEntries(Object.entries(serverEntries).map(([name, entry]) => [name, JSON.stringify(entry)])));
45
+ return okResult(this.id, configPath);
46
+ }
47
+ catch (error) {
48
+ return errorResult(this.id, configPath, error.message);
49
+ }
50
+ }
51
+ }
52
+ //# sourceMappingURL=codex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex.js","sourceRoot":"","sources":["../../src/adapters/codex.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { ClientAdapter, McpxConfig, SyncClientOptions, SyncResult } from "../types.js";
2
+ export declare class CursorAdapter implements ClientAdapter {
3
+ readonly id: "cursor";
4
+ detectConfigPath(): string | null;
5
+ supportsHttp(): boolean;
6
+ syncGateway(_config: McpxConfig, options: SyncClientOptions): SyncResult;
7
+ }
@@ -0,0 +1,52 @@
1
+ import os from "node:os";
2
+ import path from "node:path";
3
+ import { readJsonFile, writeJsonAtomic } from "../util/fs.js";
4
+ import { ensureManagedEntryWritable, errorResult, okResult, pruneStaleManagedEntries, setManagedEntries } from "./utils.js";
5
+ export class CursorAdapter {
6
+ id = "cursor";
7
+ detectConfigPath() {
8
+ return path.join(os.homedir(), "Library", "Application Support", "Cursor", "User", "mcp.json");
9
+ }
10
+ supportsHttp() {
11
+ return true;
12
+ }
13
+ syncGateway(_config, options) {
14
+ const configPath = this.detectConfigPath();
15
+ if (!configPath) {
16
+ return errorResult(this.id, undefined, "Unable to resolve Cursor MCP config path.");
17
+ }
18
+ try {
19
+ const raw = readJsonFile(configPath, {});
20
+ const servers = {
21
+ ...(raw.servers ?? {})
22
+ };
23
+ const managedNames = options.managedEntries.map((entry) => entry.name);
24
+ const serverEntries = Object.fromEntries(options.managedEntries.map((entry) => [entry.name, {
25
+ type: "http",
26
+ url: entry.url,
27
+ headers: entry.headers
28
+ }]));
29
+ for (const name of managedNames) {
30
+ const conflict = ensureManagedEntryWritable(options.managedIndex, this.id, name, servers[name]);
31
+ if (conflict) {
32
+ return errorResult(this.id, configPath, conflict);
33
+ }
34
+ }
35
+ pruneStaleManagedEntries(options.managedIndex, this.id, servers, managedNames);
36
+ for (const [name, entry] of Object.entries(serverEntries)) {
37
+ servers[name] = entry;
38
+ }
39
+ const next = {
40
+ ...raw,
41
+ servers
42
+ };
43
+ writeJsonAtomic(configPath, next);
44
+ setManagedEntries(options.managedIndex, this.id, configPath, Object.fromEntries(Object.entries(serverEntries).map(([name, entry]) => [name, JSON.stringify(entry)])));
45
+ return okResult(this.id, configPath);
46
+ }
47
+ catch (error) {
48
+ return errorResult(this.id, configPath, error.message);
49
+ }
50
+ }
51
+ }
52
+ //# sourceMappingURL=cursor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/adapters/cursor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { ClientAdapter } from "../types.js";
2
+ export declare function getAdapters(): ClientAdapter[];
@@ -0,0 +1,15 @@
1
+ import { ClaudeAdapter } from "./claude.js";
2
+ import { CodexAdapter } from "./codex.js";
3
+ import { CursorAdapter } from "./cursor.js";
4
+ import { ClineAdapter } from "./cline.js";
5
+ import { VsCodeAdapter } from "./vscode.js";
6
+ export function getAdapters() {
7
+ return [
8
+ new ClaudeAdapter(),
9
+ new CodexAdapter(),
10
+ new CursorAdapter(),
11
+ new ClineAdapter(),
12
+ new VsCodeAdapter()
13
+ ];
14
+ }
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,UAAU,WAAW;IACzB,OAAO;QACL,IAAI,aAAa,EAAE;QACnB,IAAI,YAAY,EAAE;QAClB,IAAI,aAAa,EAAE;QACnB,IAAI,YAAY,EAAE;QAClB,IAAI,aAAa,EAAE;KACpB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { ClientId, ManagedIndex, SyncResult } from "../types.js";
2
+ export declare function getManagedEntryNames(managedIndex: ManagedIndex, clientId: ClientId): string[];
3
+ export declare function isManagedEntry(managedIndex: ManagedIndex, clientId: ClientId, entryName: string): boolean;
4
+ export declare function pruneStaleManagedEntries(managedIndex: ManagedIndex, clientId: ClientId, servers: Record<string, unknown>, keepEntryNames: string[]): void;
5
+ export declare function ensureManagedEntryWritable(managedIndex: ManagedIndex, clientId: ClientId, entryName: string, existingValue: unknown): string | null;
6
+ export declare function setManagedEntries(managedIndex: ManagedIndex, clientId: ClientId, configPath: string, entries: Record<string, string>): void;
7
+ export declare function okResult(clientId: ClientId, configPath: string, message?: string): SyncResult;
8
+ export declare function unsupportedResult(clientId: ClientId, message: string): SyncResult;
9
+ export declare function errorResult(clientId: ClientId, configPath: string | undefined, message: string): SyncResult;
10
+ export declare function skippedResult(clientId: ClientId, message: string): SyncResult;
@@ -0,0 +1,69 @@
1
+ import { sha256 } from "../util/fs.js";
2
+ export function getManagedEntryNames(managedIndex, clientId) {
3
+ return Object.keys(managedIndex.managed[clientId]?.entries ?? {});
4
+ }
5
+ export function isManagedEntry(managedIndex, clientId, entryName) {
6
+ return Boolean(managedIndex.managed[clientId]?.entries?.[entryName]);
7
+ }
8
+ export function pruneStaleManagedEntries(managedIndex, clientId, servers, keepEntryNames) {
9
+ const keep = new Set(keepEntryNames);
10
+ const managedNames = getManagedEntryNames(managedIndex, clientId);
11
+ for (const name of managedNames) {
12
+ if (!keep.has(name)) {
13
+ delete servers[name];
14
+ }
15
+ }
16
+ }
17
+ export function ensureManagedEntryWritable(managedIndex, clientId, entryName, existingValue) {
18
+ if (existingValue === undefined || existingValue === null) {
19
+ return null;
20
+ }
21
+ if (isManagedEntry(managedIndex, clientId, entryName)) {
22
+ return null;
23
+ }
24
+ return `Cannot sync managed entry \"${entryName}\" because an unmanaged entry already exists.`;
25
+ }
26
+ export function setManagedEntries(managedIndex, clientId, configPath, entries) {
27
+ if (!managedIndex.managed[clientId]) {
28
+ managedIndex.managed[clientId] = {
29
+ configPath,
30
+ entries: {}
31
+ };
32
+ }
33
+ managedIndex.managed[clientId].configPath = configPath;
34
+ managedIndex.managed[clientId].entries = Object.fromEntries(Object.entries(entries).map(([entryName, serializedEntry]) => [entryName, {
35
+ fingerprint: sha256(serializedEntry),
36
+ lastSyncedAt: new Date().toISOString()
37
+ }]));
38
+ }
39
+ export function okResult(clientId, configPath, message) {
40
+ return {
41
+ clientId,
42
+ status: "SYNCED",
43
+ configPath,
44
+ message
45
+ };
46
+ }
47
+ export function unsupportedResult(clientId, message) {
48
+ return {
49
+ clientId,
50
+ status: "UNSUPPORTED_HTTP",
51
+ message
52
+ };
53
+ }
54
+ export function errorResult(clientId, configPath, message) {
55
+ return {
56
+ clientId,
57
+ status: "ERROR",
58
+ configPath,
59
+ message
60
+ };
61
+ }
62
+ export function skippedResult(clientId, message) {
63
+ return {
64
+ clientId,
65
+ status: "SKIPPED",
66
+ message
67
+ };
68
+ }
69
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/adapters/utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { ClientAdapter, McpxConfig, SyncClientOptions, SyncResult } from "../types.js";
2
+ export declare class VsCodeAdapter implements ClientAdapter {
3
+ readonly id: "vscode";
4
+ detectConfigPath(): string | null;
5
+ supportsHttp(): boolean;
6
+ syncGateway(_config: McpxConfig, options: SyncClientOptions): SyncResult;
7
+ }
@@ -0,0 +1,52 @@
1
+ import os from "node:os";
2
+ import path from "node:path";
3
+ import { readJsonFile, writeJsonAtomic } from "../util/fs.js";
4
+ import { ensureManagedEntryWritable, errorResult, okResult, pruneStaleManagedEntries, setManagedEntries } from "./utils.js";
5
+ export class VsCodeAdapter {
6
+ id = "vscode";
7
+ detectConfigPath() {
8
+ return path.join(os.homedir(), "Library", "Application Support", "Code", "User", "mcp.json");
9
+ }
10
+ supportsHttp() {
11
+ return true;
12
+ }
13
+ syncGateway(_config, options) {
14
+ const configPath = this.detectConfigPath();
15
+ if (!configPath) {
16
+ return errorResult(this.id, undefined, "Unable to resolve VS Code MCP config path.");
17
+ }
18
+ try {
19
+ const raw = readJsonFile(configPath, {});
20
+ const servers = {
21
+ ...(raw.servers ?? {})
22
+ };
23
+ const managedNames = options.managedEntries.map((entry) => entry.name);
24
+ const serverEntries = Object.fromEntries(options.managedEntries.map((entry) => [entry.name, {
25
+ type: "http",
26
+ url: entry.url,
27
+ headers: entry.headers
28
+ }]));
29
+ for (const name of managedNames) {
30
+ const conflict = ensureManagedEntryWritable(options.managedIndex, this.id, name, servers[name]);
31
+ if (conflict) {
32
+ return errorResult(this.id, configPath, conflict);
33
+ }
34
+ }
35
+ pruneStaleManagedEntries(options.managedIndex, this.id, servers, managedNames);
36
+ for (const [name, entry] of Object.entries(serverEntries)) {
37
+ servers[name] = entry;
38
+ }
39
+ const next = {
40
+ ...raw,
41
+ servers
42
+ };
43
+ writeJsonAtomic(configPath, next);
44
+ setManagedEntries(options.managedIndex, this.id, configPath, Object.fromEntries(Object.entries(serverEntries).map(([name, entry]) => [name, JSON.stringify(entry)])));
45
+ return okResult(this.id, configPath);
46
+ }
47
+ catch (error) {
48
+ return errorResult(this.id, configPath, error.message);
49
+ }
50
+ }
51
+ }
52
+ //# sourceMappingURL=vscode.js.map