proofscan 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 (95) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +295 -0
  3. package/dist/cli.d.ts +7 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +38 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/commands/config.d.ts +6 -0
  8. package/dist/commands/config.d.ts.map +1 -0
  9. package/dist/commands/config.js +81 -0
  10. package/dist/commands/config.js.map +1 -0
  11. package/dist/commands/connectors.d.ts +6 -0
  12. package/dist/commands/connectors.d.ts.map +1 -0
  13. package/dist/commands/connectors.js +205 -0
  14. package/dist/commands/connectors.js.map +1 -0
  15. package/dist/commands/index.d.ts +5 -0
  16. package/dist/commands/index.d.ts.map +1 -0
  17. package/dist/commands/index.js +5 -0
  18. package/dist/commands/index.js.map +1 -0
  19. package/dist/commands/monitor.d.ts +6 -0
  20. package/dist/commands/monitor.d.ts.map +1 -0
  21. package/dist/commands/monitor.js +76 -0
  22. package/dist/commands/monitor.js.map +1 -0
  23. package/dist/commands/scan.d.ts +6 -0
  24. package/dist/commands/scan.d.ts.map +1 -0
  25. package/dist/commands/scan.js +76 -0
  26. package/dist/commands/scan.js.map +1 -0
  27. package/dist/config/import.d.ts +19 -0
  28. package/dist/config/import.d.ts.map +1 -0
  29. package/dist/config/import.js +113 -0
  30. package/dist/config/import.js.map +1 -0
  31. package/dist/config/index.d.ts +4 -0
  32. package/dist/config/index.d.ts.map +1 -0
  33. package/dist/config/index.js +4 -0
  34. package/dist/config/index.js.map +1 -0
  35. package/dist/config/manager.d.ts +29 -0
  36. package/dist/config/manager.d.ts.map +1 -0
  37. package/dist/config/manager.js +112 -0
  38. package/dist/config/manager.js.map +1 -0
  39. package/dist/config/schema.d.ts +21 -0
  40. package/dist/config/schema.d.ts.map +1 -0
  41. package/dist/config/schema.js +124 -0
  42. package/dist/config/schema.js.map +1 -0
  43. package/dist/events/index.d.ts +2 -0
  44. package/dist/events/index.d.ts.map +1 -0
  45. package/dist/events/index.js +2 -0
  46. package/dist/events/index.js.map +1 -0
  47. package/dist/events/store.d.ts +25 -0
  48. package/dist/events/store.d.ts.map +1 -0
  49. package/dist/events/store.js +91 -0
  50. package/dist/events/store.js.map +1 -0
  51. package/dist/index.d.ts +11 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +11 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/scanner/index.d.ts +22 -0
  56. package/dist/scanner/index.d.ts.map +1 -0
  57. package/dist/scanner/index.js +179 -0
  58. package/dist/scanner/index.js.map +1 -0
  59. package/dist/transports/index.d.ts +2 -0
  60. package/dist/transports/index.d.ts.map +1 -0
  61. package/dist/transports/index.js +2 -0
  62. package/dist/transports/index.js.map +1 -0
  63. package/dist/transports/stdio.d.ts +50 -0
  64. package/dist/transports/stdio.d.ts.map +1 -0
  65. package/dist/transports/stdio.js +140 -0
  66. package/dist/transports/stdio.js.map +1 -0
  67. package/dist/types/config.d.ts +38 -0
  68. package/dist/types/config.d.ts.map +1 -0
  69. package/dist/types/config.js +8 -0
  70. package/dist/types/config.js.map +1 -0
  71. package/dist/types/events.d.ts +23 -0
  72. package/dist/types/events.d.ts.map +1 -0
  73. package/dist/types/events.js +10 -0
  74. package/dist/types/events.js.map +1 -0
  75. package/dist/types/index.d.ts +3 -0
  76. package/dist/types/index.d.ts.map +1 -0
  77. package/dist/types/index.js +3 -0
  78. package/dist/types/index.js.map +1 -0
  79. package/dist/utils/config-path.d.ts +15 -0
  80. package/dist/utils/config-path.d.ts.map +1 -0
  81. package/dist/utils/config-path.js +44 -0
  82. package/dist/utils/config-path.js.map +1 -0
  83. package/dist/utils/fs.d.ts +25 -0
  84. package/dist/utils/fs.d.ts.map +1 -0
  85. package/dist/utils/fs.js +80 -0
  86. package/dist/utils/fs.js.map +1 -0
  87. package/dist/utils/index.d.ts +4 -0
  88. package/dist/utils/index.d.ts.map +1 -0
  89. package/dist/utils/index.js +4 -0
  90. package/dist/utils/index.js.map +1 -0
  91. package/dist/utils/output.d.ts +22 -0
  92. package/dist/utils/output.d.ts.map +1 -0
  93. package/dist/utils/output.js +108 -0
  94. package/dist/utils/output.js.map +1 -0
  95. package/package.json +60 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024
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,295 @@
1
+ # proofscan
2
+
3
+ MCP Server scanner - eliminate black boxes by capturing JSON-RPC from connection to tools/list.
4
+
5
+ ## Overview
6
+
7
+ proofscan provides visibility into MCP (Model Context Protocol) server communication. It:
8
+
9
+ - Connects to MCP servers via stdio transport
10
+ - Captures all JSON-RPC messages (requests, responses, notifications)
11
+ - Stores events in JSONL format for later analysis
12
+ - Supports importing server configurations from mcp.so / Claude Desktop format
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install
18
+ npm run build
19
+ npm link # Makes 'proofscan' available globally
20
+ ```
21
+
22
+ Or run directly:
23
+
24
+ ```bash
25
+ node dist/cli.js --help
26
+ ```
27
+
28
+ ## Quickstart
29
+
30
+ ### 1. Initialize Configuration
31
+
32
+ ```bash
33
+ # Create config in OS-standard location
34
+ proofscan config init
35
+
36
+ # Check where config is stored
37
+ proofscan config path
38
+ ```
39
+
40
+ ### 2. Import MCP Server from mcp.so / Claude Desktop
41
+
42
+ Copy the `mcpServers` JSON from mcp.so or Claude Desktop settings and import via stdin:
43
+
44
+ ```bash
45
+ # Full mcpServers format
46
+ echo '{ "mcpServers": { "time": { "command": "uvx", "args": ["mcp-server-time"] } } }' | proofscan connectors import --from mcpServers --stdin
47
+
48
+ # Or just the server definition (requires --name)
49
+ echo '{ "command": "uvx", "args": ["mcp-server-time"] }' | proofscan connectors import --from mcpServers --stdin --name time
50
+
51
+ # Import from file
52
+ proofscan connectors import --from mcpServers --file ./servers.json
53
+ ```
54
+
55
+ ### 3. List Connectors
56
+
57
+ ```bash
58
+ proofscan connectors list
59
+ ```
60
+
61
+ ### 4. Scan a Connector
62
+
63
+ ```bash
64
+ proofscan scan start --id time
65
+ ```
66
+
67
+ ### 5. View Events
68
+
69
+ ```bash
70
+ proofscan monitor tail --id time --last 20
71
+ ```
72
+
73
+ ## Commands
74
+
75
+ ### Global Options
76
+
77
+ ```
78
+ -c, --config <path> Path to config file
79
+ --json Output in JSON format
80
+ -v, --verbose Verbose output
81
+ ```
82
+
83
+ ### Config
84
+
85
+ ```bash
86
+ proofscan config path # Show config file path
87
+ proofscan config init [--force] # Initialize config
88
+ proofscan config show # Show config (secrets masked)
89
+ proofscan config validate # Validate config
90
+ ```
91
+
92
+ ### Connectors
93
+
94
+ ```bash
95
+ proofscan connectors list # List all connectors
96
+ proofscan connectors show --id <id> # Show connector details
97
+ proofscan connectors add --id <id> --stdio "cmd" # Add stdio connector
98
+ proofscan connectors enable --id <id> # Enable connector
99
+ proofscan connectors disable --id <id> # Disable connector
100
+ proofscan connectors remove --id <id> # Remove connector
101
+
102
+ # Import from mcpServers format
103
+ proofscan connectors import --from mcpServers --stdin
104
+ proofscan connectors import --from mcpServers --file <path> [--name <id>]
105
+ ```
106
+
107
+ ### Scan
108
+
109
+ ```bash
110
+ proofscan scan start --id <id> [--timeout <sec>]
111
+ ```
112
+
113
+ ### Monitor
114
+
115
+ ```bash
116
+ proofscan monitor tail --id <id> [--last <N>]
117
+ ```
118
+
119
+ ## Config File Format
120
+
121
+ Config is stored in the OS-standard location:
122
+
123
+ - **Windows**: `%APPDATA%\proofscan\config.json`
124
+ - **macOS**: `~/Library/Application Support/proofscan/config.json`
125
+ - **Linux**: `~/.config/proofscan/config.json`
126
+
127
+ ```json
128
+ {
129
+ "version": 1,
130
+ "connectors": [
131
+ {
132
+ "id": "time",
133
+ "enabled": true,
134
+ "transport": {
135
+ "type": "stdio",
136
+ "command": "uvx",
137
+ "args": ["mcp-server-time", "--local-timezone=America/New_York"]
138
+ }
139
+ }
140
+ ]
141
+ }
142
+ ```
143
+
144
+ ## Event Storage
145
+
146
+ Events are stored in JSONL format:
147
+
148
+ ```
149
+ ~/.config/proofscan/events/<connector_id>/<YYYYMMDD>.jsonl
150
+ ```
151
+
152
+ Each event contains:
153
+
154
+ ```json
155
+ {
156
+ "event_id": "uuid",
157
+ "ts": "2024-01-15T10:30:00.000Z",
158
+ "connector_id": "time",
159
+ "direction": "client_to_server",
160
+ "kind": "request",
161
+ "method": "tools/list",
162
+ "rpc_id": 1,
163
+ "ok": true,
164
+ "raw": "{...}"
165
+ }
166
+ ```
167
+
168
+ ## Example Session
169
+
170
+ ```bash
171
+ # Initialize
172
+ $ proofscan config init
173
+ ✓ Config created at: /home/user/.config/proofscan/config.json
174
+
175
+ # Import a server
176
+ $ echo '{"mcpServers":{"time":{"command":"uvx","args":["mcp-server-time"]}}}' \
177
+ | proofscan connectors import --from mcpServers --stdin
178
+ ✓ Imported 1 connector(s): time
179
+
180
+ # List connectors
181
+ $ proofscan connectors list
182
+ ID Enabled Type Command/URL
183
+ -----------------------------------
184
+ time yes stdio uvx mcp-server-time
185
+
186
+ # Scan the server
187
+ $ proofscan scan start --id time
188
+ Scanning connector: time...
189
+ ✓ Scan successful!
190
+ Connector: time
191
+ Tools found: 1
192
+ Tool names: get_current_time
193
+ Events recorded: 8
194
+
195
+ # View events
196
+ $ proofscan monitor tail --id time --last 5
197
+ Recent events for 'time' (last 5):
198
+
199
+ Time Dir Status Kind Summary
200
+ ----------------------------------------------------------------------
201
+ 10:30:01 → ✓ request initialize
202
+ 10:30:01 ← response
203
+ 10:30:01 → notification notifications/initialized
204
+ 10:30:01 → ✓ request tools/list
205
+ 10:30:01 ← response
206
+ ```
207
+
208
+ ## JSON Output
209
+
210
+ Add `--json` for machine-readable output:
211
+
212
+ ```bash
213
+ $ proofscan connectors list --json
214
+ [
215
+ {
216
+ "ID": "time",
217
+ "Enabled": "yes",
218
+ "Type": "stdio",
219
+ "Command/URL": "uvx mcp-server-time"
220
+ }
221
+ ]
222
+
223
+ $ proofscan scan start --id time --json
224
+ {
225
+ "success": true,
226
+ "connector_id": "time",
227
+ "tools_count": 1,
228
+ "tools": ["get_current_time"],
229
+ "event_count": 8
230
+ }
231
+ ```
232
+
233
+ ## Supported mcpServers Input Formats
234
+
235
+ proofscan accepts several JSON formats for import:
236
+
237
+ ### A) Full wrapper
238
+
239
+ ```json
240
+ {
241
+ "mcpServers": {
242
+ "time": { "command": "uvx", "args": ["mcp-server-time"] }
243
+ }
244
+ }
245
+ ```
246
+
247
+ ### B) Direct mcpServers object
248
+
249
+ ```json
250
+ {
251
+ "time": { "command": "uvx", "args": ["mcp-server-time"] }
252
+ }
253
+ ```
254
+
255
+ ### C) Single server definition (requires `--name`)
256
+
257
+ ```json
258
+ {
259
+ "command": "uvx",
260
+ "args": ["mcp-server-time"]
261
+ }
262
+ ```
263
+
264
+ ## Failure Handling
265
+
266
+ Failures are always recorded as events. Even if a scan fails, you can inspect what happened:
267
+
268
+ ```bash
269
+ $ proofscan scan start --id broken-server
270
+ Scanning connector: broken-server...
271
+ ✗ Scan failed!
272
+ Connector: broken-server
273
+ Error: Initialize failed: Request timeout for method: initialize
274
+ Events recorded: 3
275
+
276
+ $ proofscan monitor tail --id broken-server --last 5
277
+ Time Dir Status Kind Summary
278
+ ----------------------------------------------------------------------
279
+ 10:35:01 → transport [connect_attempt]
280
+ 10:35:01 ← transport [connected]
281
+ 10:35:31 → transport [initialize_failed] Initialize failed: Request timeout...
282
+ ```
283
+
284
+ ## Development
285
+
286
+ ```bash
287
+ npm install # Install dependencies
288
+ npm run build # Build TypeScript
289
+ npm run dev # Watch mode
290
+ npm test # Run tests
291
+ ```
292
+
293
+ ## License
294
+
295
+ MIT
package/dist/cli.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * proofscan CLI
4
+ * MCP Server scanner - eliminate black boxes
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;GAGG"}
package/dist/cli.js ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * proofscan CLI
4
+ * MCP Server scanner - eliminate black boxes
5
+ */
6
+ import { Command } from 'commander';
7
+ import { resolveConfigPath } from './utils/config-path.js';
8
+ import { setOutputOptions } from './utils/output.js';
9
+ import { createConfigCommand, createConnectorsCommand, createScanCommand, createMonitorCommand, } from './commands/index.js';
10
+ const program = new Command();
11
+ // Global state for config path
12
+ let globalConfigPath;
13
+ function getConfigPath() {
14
+ return resolveConfigPath({ configPath: globalConfigPath });
15
+ }
16
+ program
17
+ .name('proofscan')
18
+ .description('MCP Server scanner - eliminate black boxes by capturing JSON-RPC from connection to tools/list')
19
+ .version('0.1.0')
20
+ .option('-c, --config <path>', 'Path to config file')
21
+ .option('--json', 'Output in JSON format')
22
+ .option('-v, --verbose', 'Verbose output')
23
+ .hook('preAction', (thisCommand) => {
24
+ const opts = thisCommand.opts();
25
+ globalConfigPath = opts.config;
26
+ setOutputOptions({
27
+ json: opts.json,
28
+ verbose: opts.verbose,
29
+ });
30
+ });
31
+ // Add subcommands
32
+ program.addCommand(createConfigCommand(getConfigPath));
33
+ program.addCommand(createConnectorsCommand(getConfigPath));
34
+ program.addCommand(createScanCommand(getConfigPath));
35
+ program.addCommand(createMonitorCommand(getConfigPath));
36
+ // Parse and run
37
+ program.parse();
38
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,+BAA+B;AAC/B,IAAI,gBAAoC,CAAC;AAEzC,SAAS,aAAa;IACpB,OAAO,iBAAiB,CAAC,EAAE,UAAU,EAAE,gBAAgB,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED,OAAO;KACJ,IAAI,CAAC,WAAW,CAAC;KACjB,WAAW,CAAC,gGAAgG,CAAC;KAC7G,OAAO,CAAC,OAAO,CAAC;KAChB,MAAM,CAAC,qBAAqB,EAAE,qBAAqB,CAAC;KACpD,MAAM,CAAC,QAAQ,EAAE,uBAAuB,CAAC;KACzC,MAAM,CAAC,eAAe,EAAE,gBAAgB,CAAC;KACzC,IAAI,CAAC,WAAW,EAAE,CAAC,WAAW,EAAE,EAAE;IACjC,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,EAAE,CAAC;IAChC,gBAAgB,GAAG,IAAI,CAAC,MAAM,CAAC;IAC/B,gBAAgB,CAAC;QACf,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,OAAO,EAAE,IAAI,CAAC,OAAO;KACtB,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEL,kBAAkB;AAClB,OAAO,CAAC,UAAU,CAAC,mBAAmB,CAAC,aAAa,CAAC,CAAC,CAAC;AACvD,OAAO,CAAC,UAAU,CAAC,uBAAuB,CAAC,aAAa,CAAC,CAAC,CAAC;AAC3D,OAAO,CAAC,UAAU,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC,CAAC;AACrD,OAAO,CAAC,UAAU,CAAC,oBAAoB,CAAC,aAAa,CAAC,CAAC,CAAC;AAExD,gBAAgB;AAChB,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Config commands
3
+ */
4
+ import { Command } from 'commander';
5
+ export declare function createConfigCommand(getConfigPath: () => string): Command;
6
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,MAAM,MAAM,GAAG,OAAO,CAiFxE"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Config commands
3
+ */
4
+ import { Command } from 'commander';
5
+ import { ConfigManager } from '../config/index.js';
6
+ import { output, outputSuccess, outputError, maskSecretsInObject, getOutputOptions } from '../utils/output.js';
7
+ export function createConfigCommand(getConfigPath) {
8
+ const cmd = new Command('config')
9
+ .description('Manage proofscan configuration');
10
+ cmd
11
+ .command('path')
12
+ .description('Show the config file path')
13
+ .action(() => {
14
+ const configPath = getConfigPath();
15
+ output({ path: configPath }, configPath);
16
+ });
17
+ cmd
18
+ .command('init')
19
+ .description('Initialize a new config file')
20
+ .option('-f, --force', 'Overwrite existing config')
21
+ .option('-p, --path <path>', 'Custom config path')
22
+ .action(async (options) => {
23
+ try {
24
+ const configPath = options.path || getConfigPath();
25
+ const manager = new ConfigManager(configPath);
26
+ const result = await manager.init(options.force);
27
+ if (result.created) {
28
+ outputSuccess(`Config created at: ${result.path}`);
29
+ }
30
+ else {
31
+ output({ exists: true, path: result.path }, `Config already exists at: ${result.path}\nUse --force to overwrite.`);
32
+ }
33
+ }
34
+ catch (error) {
35
+ outputError('Failed to initialize config', error instanceof Error ? error : undefined);
36
+ process.exit(1);
37
+ }
38
+ });
39
+ cmd
40
+ .command('show')
41
+ .description('Show current config (secrets masked)')
42
+ .action(async () => {
43
+ try {
44
+ const manager = new ConfigManager(getConfigPath());
45
+ const config = await manager.load();
46
+ const masked = maskSecretsInObject(config);
47
+ if (getOutputOptions().json) {
48
+ output(masked);
49
+ }
50
+ else {
51
+ console.log(JSON.stringify(masked, null, 2));
52
+ }
53
+ }
54
+ catch (error) {
55
+ outputError('Failed to load config', error instanceof Error ? error : undefined);
56
+ process.exit(1);
57
+ }
58
+ });
59
+ cmd
60
+ .command('validate')
61
+ .description('Validate the config file')
62
+ .action(async () => {
63
+ try {
64
+ const manager = new ConfigManager(getConfigPath());
65
+ const result = await manager.validate();
66
+ if (result.valid) {
67
+ outputSuccess('Config is valid');
68
+ }
69
+ else {
70
+ output({ valid: false, errors: result.errors }, `Config validation failed:\n${result.errors.map(e => ` - ${e.path}: ${e.message}`).join('\n')}`);
71
+ process.exit(1);
72
+ }
73
+ }
74
+ catch (error) {
75
+ outputError('Failed to validate config', error instanceof Error ? error : undefined);
76
+ process.exit(1);
77
+ }
78
+ });
79
+ return cmd;
80
+ }
81
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Connectors commands
3
+ */
4
+ import { Command } from 'commander';
5
+ export declare function createConnectorsCommand(getConfigPath: () => string): Command;
6
+ //# sourceMappingURL=connectors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connectors.d.ts","sourceRoot":"","sources":["../../src/commands/connectors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,wBAAgB,uBAAuB,CAAC,aAAa,EAAE,MAAM,MAAM,GAAG,OAAO,CAoN5E"}
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Connectors commands
3
+ */
4
+ import { Command } from 'commander';
5
+ import { promises as fs } from 'fs';
6
+ import { ConfigManager, parseMcpServers, readStdin } from '../config/index.js';
7
+ import { output, outputSuccess, outputError, outputTable, maskSecretsInObject } from '../utils/output.js';
8
+ export function createConnectorsCommand(getConfigPath) {
9
+ const cmd = new Command('connectors')
10
+ .description('Manage MCP server connectors');
11
+ cmd
12
+ .command('list')
13
+ .description('List all connectors')
14
+ .action(async () => {
15
+ try {
16
+ const manager = new ConfigManager(getConfigPath());
17
+ const connectors = await manager.getConnectors();
18
+ if (connectors.length === 0) {
19
+ output({ connectors: [] }, 'No connectors configured.');
20
+ return;
21
+ }
22
+ const headers = ['ID', 'Enabled', 'Type', 'Command/URL'];
23
+ const rows = connectors.map(c => {
24
+ let target = '';
25
+ if (c.transport.type === 'stdio') {
26
+ const t = c.transport;
27
+ target = t.command + (t.args?.length ? ` ${t.args.join(' ')}` : '');
28
+ if (target.length > 50)
29
+ target = target.slice(0, 47) + '...';
30
+ }
31
+ else if ('url' in c.transport) {
32
+ target = c.transport.url;
33
+ }
34
+ return [c.id, c.enabled ? 'yes' : 'no', c.transport.type, target];
35
+ });
36
+ outputTable(headers, rows);
37
+ }
38
+ catch (error) {
39
+ outputError('Failed to list connectors', error instanceof Error ? error : undefined);
40
+ process.exit(1);
41
+ }
42
+ });
43
+ cmd
44
+ .command('show')
45
+ .description('Show connector details')
46
+ .requiredOption('--id <id>', 'Connector ID')
47
+ .action(async (options) => {
48
+ try {
49
+ const manager = new ConfigManager(getConfigPath());
50
+ const connector = await manager.getConnector(options.id);
51
+ if (!connector) {
52
+ outputError(`Connector not found: ${options.id}`);
53
+ process.exit(1);
54
+ }
55
+ const masked = maskSecretsInObject(connector);
56
+ output(masked, JSON.stringify(masked, null, 2));
57
+ }
58
+ catch (error) {
59
+ outputError('Failed to show connector', error instanceof Error ? error : undefined);
60
+ process.exit(1);
61
+ }
62
+ });
63
+ cmd
64
+ .command('add')
65
+ .description('Add a new stdio connector')
66
+ .requiredOption('--id <id>', 'Connector ID')
67
+ .requiredOption('--stdio <cmdline>', 'Command line (command and args as single string)')
68
+ .action(async (options) => {
69
+ try {
70
+ const manager = new ConfigManager(getConfigPath());
71
+ // Parse command line
72
+ const parts = options.stdio.trim().split(/\s+/);
73
+ const command = parts[0];
74
+ const args = parts.slice(1);
75
+ const connector = {
76
+ id: options.id,
77
+ enabled: true,
78
+ transport: {
79
+ type: 'stdio',
80
+ command,
81
+ ...(args.length > 0 && { args }),
82
+ },
83
+ };
84
+ await manager.addConnector(connector);
85
+ outputSuccess(`Connector '${options.id}' added`);
86
+ }
87
+ catch (error) {
88
+ outputError('Failed to add connector', error instanceof Error ? error : undefined);
89
+ process.exit(1);
90
+ }
91
+ });
92
+ cmd
93
+ .command('enable')
94
+ .description('Enable a connector')
95
+ .requiredOption('--id <id>', 'Connector ID')
96
+ .action(async (options) => {
97
+ try {
98
+ const manager = new ConfigManager(getConfigPath());
99
+ await manager.enableConnector(options.id);
100
+ outputSuccess(`Connector '${options.id}' enabled`);
101
+ }
102
+ catch (error) {
103
+ outputError('Failed to enable connector', error instanceof Error ? error : undefined);
104
+ process.exit(1);
105
+ }
106
+ });
107
+ cmd
108
+ .command('disable')
109
+ .description('Disable a connector')
110
+ .requiredOption('--id <id>', 'Connector ID')
111
+ .action(async (options) => {
112
+ try {
113
+ const manager = new ConfigManager(getConfigPath());
114
+ await manager.disableConnector(options.id);
115
+ outputSuccess(`Connector '${options.id}' disabled`);
116
+ }
117
+ catch (error) {
118
+ outputError('Failed to disable connector', error instanceof Error ? error : undefined);
119
+ process.exit(1);
120
+ }
121
+ });
122
+ cmd
123
+ .command('remove')
124
+ .description('Remove a connector')
125
+ .requiredOption('--id <id>', 'Connector ID')
126
+ .action(async (options) => {
127
+ try {
128
+ const manager = new ConfigManager(getConfigPath());
129
+ await manager.removeConnector(options.id);
130
+ outputSuccess(`Connector '${options.id}' removed`);
131
+ }
132
+ catch (error) {
133
+ outputError('Failed to remove connector', error instanceof Error ? error : undefined);
134
+ process.exit(1);
135
+ }
136
+ });
137
+ cmd
138
+ .command('import')
139
+ .description('Import connectors from mcpServers format')
140
+ .requiredOption('--from <format>', 'Import format (mcpServers)')
141
+ .option('--file <path>', 'Read from file')
142
+ .option('--stdin', 'Read from stdin')
143
+ .option('--name <id>', 'Connector ID (required for single server definition)')
144
+ .action(async (options) => {
145
+ try {
146
+ if (options.from !== 'mcpServers') {
147
+ outputError(`Unsupported format: ${options.from}. Only 'mcpServers' is supported.`);
148
+ process.exit(1);
149
+ }
150
+ if (!options.file && !options.stdin) {
151
+ outputError('Either --file or --stdin is required');
152
+ process.exit(1);
153
+ }
154
+ let jsonContent;
155
+ if (options.stdin) {
156
+ jsonContent = await readStdin();
157
+ }
158
+ else {
159
+ jsonContent = await fs.readFile(options.file, 'utf-8');
160
+ }
161
+ const result = parseMcpServers(jsonContent, options.name);
162
+ if (result.errors.length > 0) {
163
+ outputError(`Import errors:\n${result.errors.map(e => ` - ${e}`).join('\n')}`);
164
+ process.exit(1);
165
+ }
166
+ if (result.connectors.length === 0) {
167
+ output({ imported: 0 }, 'No connectors found in input.');
168
+ return;
169
+ }
170
+ const manager = new ConfigManager(getConfigPath());
171
+ // Ensure config exists
172
+ await manager.init(false);
173
+ // Add each connector
174
+ const added = [];
175
+ const errors = [];
176
+ for (const connector of result.connectors) {
177
+ try {
178
+ await manager.addConnector(connector);
179
+ added.push(connector.id);
180
+ }
181
+ catch (error) {
182
+ errors.push(`${connector.id}: ${error instanceof Error ? error.message : String(error)}`);
183
+ }
184
+ }
185
+ if (added.length > 0) {
186
+ outputSuccess(`Imported ${added.length} connector(s): ${added.join(', ')}`);
187
+ }
188
+ if (errors.length > 0) {
189
+ console.error(`Errors:\n${errors.map(e => ` - ${e}`).join('\n')}`);
190
+ }
191
+ // Validate after import
192
+ const validation = await manager.validate();
193
+ if (!validation.valid) {
194
+ console.error('Warning: Config validation failed after import');
195
+ process.exit(1);
196
+ }
197
+ }
198
+ catch (error) {
199
+ outputError('Failed to import connectors', error instanceof Error ? error : undefined);
200
+ process.exit(1);
201
+ }
202
+ });
203
+ return cmd;
204
+ }
205
+ //# sourceMappingURL=connectors.js.map