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.
- package/LICENSE +21 -0
- package/README.md +295 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +38 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/config.d.ts +6 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +81 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/connectors.d.ts +6 -0
- package/dist/commands/connectors.d.ts.map +1 -0
- package/dist/commands/connectors.js +205 -0
- package/dist/commands/connectors.js.map +1 -0
- package/dist/commands/index.d.ts +5 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +5 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/monitor.d.ts +6 -0
- package/dist/commands/monitor.d.ts.map +1 -0
- package/dist/commands/monitor.js +76 -0
- package/dist/commands/monitor.js.map +1 -0
- package/dist/commands/scan.d.ts +6 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +76 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/config/import.d.ts +19 -0
- package/dist/config/import.d.ts.map +1 -0
- package/dist/config/import.js +113 -0
- package/dist/config/import.js.map +1 -0
- package/dist/config/index.d.ts +4 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +4 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/manager.d.ts +29 -0
- package/dist/config/manager.d.ts.map +1 -0
- package/dist/config/manager.js +112 -0
- package/dist/config/manager.js.map +1 -0
- package/dist/config/schema.d.ts +21 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +124 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/events/index.d.ts +2 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/index.js +2 -0
- package/dist/events/index.js.map +1 -0
- package/dist/events/store.d.ts +25 -0
- package/dist/events/store.d.ts.map +1 -0
- package/dist/events/store.js +91 -0
- package/dist/events/store.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/scanner/index.d.ts +22 -0
- package/dist/scanner/index.d.ts.map +1 -0
- package/dist/scanner/index.js +179 -0
- package/dist/scanner/index.js.map +1 -0
- package/dist/transports/index.d.ts +2 -0
- package/dist/transports/index.d.ts.map +1 -0
- package/dist/transports/index.js +2 -0
- package/dist/transports/index.js.map +1 -0
- package/dist/transports/stdio.d.ts +50 -0
- package/dist/transports/stdio.d.ts.map +1 -0
- package/dist/transports/stdio.js +140 -0
- package/dist/transports/stdio.js.map +1 -0
- package/dist/types/config.d.ts +38 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +8 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/events.d.ts +23 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/events.js +10 -0
- package/dist/types/events.js.map +1 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/config-path.d.ts +15 -0
- package/dist/utils/config-path.d.ts.map +1 -0
- package/dist/utils/config-path.js +44 -0
- package/dist/utils/config-path.js.map +1 -0
- package/dist/utils/fs.d.ts +25 -0
- package/dist/utils/fs.d.ts.map +1 -0
- package/dist/utils/fs.js +80 -0
- package/dist/utils/fs.js.map +1 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/output.d.ts +22 -0
- package/dist/utils/output.d.ts.map +1 -0
- package/dist/utils/output.js +108 -0
- package/dist/utils/output.js.map +1 -0
- 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 @@
|
|
|
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
|
package/dist/cli.js.map
ADDED
|
@@ -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 @@
|
|
|
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 @@
|
|
|
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
|