@k08200/mcp-probe 0.1.0 → 1.0.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/README.md +297 -13
- package/dist/checker.d.ts +2 -11
- package/dist/checker.d.ts.map +1 -1
- package/dist/checker.js +93 -9
- package/dist/checker.js.map +1 -1
- package/dist/cli.js +132 -5
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +143 -0
- package/dist/config.js.map +1 -0
- package/dist/protocols/mcp-client.d.ts.map +1 -1
- package/dist/protocols/mcp-client.js +225 -64
- package/dist/protocols/mcp-client.js.map +1 -1
- package/dist/reporters/badge.d.ts +12 -0
- package/dist/reporters/badge.d.ts.map +1 -0
- package/dist/reporters/badge.js +34 -0
- package/dist/reporters/badge.js.map +1 -0
- package/dist/reporters/github.d.ts +7 -0
- package/dist/reporters/github.d.ts.map +1 -0
- package/dist/reporters/github.js +138 -0
- package/dist/reporters/github.js.map +1 -0
- package/dist/reporters/json-reporter.d.ts +2 -2
- package/dist/reporters/json-reporter.d.ts.map +1 -1
- package/dist/reporters/json-reporter.js.map +1 -1
- package/dist/reporters/terminal.d.ts +2 -1
- package/dist/reporters/terminal.d.ts.map +1 -1
- package/dist/reporters/terminal.js +38 -0
- package/dist/reporters/terminal.js.map +1 -1
- package/dist/types.d.ts +73 -2
- package/dist/types.d.ts.map +1 -1
- package/examples/datadog.tools.json +13 -0
- package/examples/github-actions/fleet.yml +25 -0
- package/examples/github-actions/remote-server.yml +24 -0
- package/examples/github-actions/single-server.yml +21 -0
- package/examples/mcp-probe.config.json +23 -0
- package/examples/recipes/README.md +35 -0
- package/examples/recipes/datadog.tools.json +23 -0
- package/examples/recipes/gmail.tools.json +18 -0
- package/examples/recipes/supabase.tools.json +19 -0
- package/examples/self-check.config.json +14 -0
- package/examples/self-check.tools.json +17 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -1,14 +1,47 @@
|
|
|
1
1
|
# mcp-probe
|
|
2
2
|
|
|
3
3
|
[](https://github.com/k08200/mcp-probe/actions/workflows/ci.yml)
|
|
4
|
-
[](https://www.npmjs.com/package/mcp-probe)
|
|
4
|
+
[](https://www.npmjs.com/package/@k08200/mcp-probe)
|
|
5
5
|
[](LICENSE)
|
|
6
|
-
[](package.json)
|
|
6
|
+
[](package.json)
|
|
7
7
|
|
|
8
|
-
**Quality checker for MCP servers.** Validates protocol handshake, tool
|
|
8
|
+
**Quality checker for MCP servers.** Validates protocol handshake, discovery, optional tool-call dry-runs, and response latency in one command.
|
|
9
9
|
|
|
10
10
|
The `npm audit` for the [MCP](https://modelcontextprotocol.io) ecosystem — because [awesome-mcp-servers](https://github.com/punkpeye/awesome-mcp-servers) lists 200+ servers and there was no way to know if they actually worked.
|
|
11
11
|
|
|
12
|
+
## Quick Start for CI
|
|
13
|
+
|
|
14
|
+
Add this workflow to any project that depends on MCP servers:
|
|
15
|
+
|
|
16
|
+
```yaml
|
|
17
|
+
name: MCP Probe
|
|
18
|
+
|
|
19
|
+
on:
|
|
20
|
+
pull_request:
|
|
21
|
+
push:
|
|
22
|
+
branches: [main]
|
|
23
|
+
|
|
24
|
+
jobs:
|
|
25
|
+
mcp-probe:
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
timeout-minutes: 5
|
|
28
|
+
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@v4
|
|
31
|
+
|
|
32
|
+
- name: Validate MCP server
|
|
33
|
+
run: |
|
|
34
|
+
npx @k08200/mcp-probe @your-org/your-mcp-server \
|
|
35
|
+
--probe-tools \
|
|
36
|
+
--github-summary
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
For teams running several MCP servers, use a config file:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npx @k08200/mcp-probe --config mcp-probe.config.json --github-summary
|
|
43
|
+
```
|
|
44
|
+
|
|
12
45
|
```bash
|
|
13
46
|
npx @k08200/mcp-probe @modelcontextprotocol/server-memory
|
|
14
47
|
```
|
|
@@ -64,11 +97,38 @@ mcp-probe @modelcontextprotocol/server-filesystem /tmp /Users/me/projects
|
|
|
64
97
|
# Check a local server file
|
|
65
98
|
mcp-probe ./my-server.js
|
|
66
99
|
|
|
100
|
+
# Check a remote Streamable HTTP MCP server
|
|
101
|
+
mcp-probe https://mcp.example.com/mcp
|
|
102
|
+
|
|
103
|
+
# Check a legacy HTTP+SSE MCP server
|
|
104
|
+
mcp-probe https://mcp.example.com/sse --transport sse
|
|
105
|
+
|
|
106
|
+
# Pass headers to remote servers
|
|
107
|
+
mcp-probe https://mcp.example.com/mcp --header "Authorization: Bearer $TOKEN"
|
|
108
|
+
|
|
109
|
+
# Ignore known noisy stderr lines when classifying startup failures
|
|
110
|
+
mcp-probe @scope/server --stderr-allow "^Warning:" --stderr-fatal "panic|FATAL"
|
|
111
|
+
|
|
67
112
|
# JSON output for CI / scripting
|
|
68
113
|
mcp-probe @scope/server --output json
|
|
69
114
|
|
|
70
115
|
# Custom timeout (default: 10000ms)
|
|
71
116
|
mcp-probe @scope/server --timeout 30000
|
|
117
|
+
|
|
118
|
+
# Batch-check several servers from a config file
|
|
119
|
+
mcp-probe --config mcp-probe.config.json
|
|
120
|
+
|
|
121
|
+
# Write GitHub Actions summary and annotations
|
|
122
|
+
mcp-probe --config mcp-probe.config.json --github-summary
|
|
123
|
+
|
|
124
|
+
# Write shields.io endpoint JSON for a status badge
|
|
125
|
+
mcp-probe --config mcp-probe.config.json --badge-file mcp-probe-badge.json
|
|
126
|
+
|
|
127
|
+
# Call tools with generated minimal inputs
|
|
128
|
+
mcp-probe @scope/server --probe-tools
|
|
129
|
+
|
|
130
|
+
# Call tools with real sample inputs from a sidecar file
|
|
131
|
+
mcp-probe @scope/server --tools-file .mcp-probe.json
|
|
72
132
|
```
|
|
73
133
|
|
|
74
134
|
## What it checks
|
|
@@ -79,6 +139,151 @@ mcp-probe @scope/server --timeout 30000
|
|
|
79
139
|
| **MCP protocol handshake** | Does the server respond to `initialize`? Measures connect latency. |
|
|
80
140
|
| **Tools discovery** | Does `tools/list` return results? Measures list latency. |
|
|
81
141
|
| **Tool schema validation** | Are all tool schemas well-formed? |
|
|
142
|
+
| **Resources discovery** | Runs `resources/list` when the server advertises resources. |
|
|
143
|
+
| **Prompts discovery** | Runs `prompts/list` when the server advertises prompts. |
|
|
144
|
+
| **Tool call dry-run** | Optional `tools/call` checks via `--probe-tools` or `--tools-file`. |
|
|
145
|
+
|
|
146
|
+
## Batch CI gate
|
|
147
|
+
|
|
148
|
+
Use `--config` when a project depends on several MCP servers and you want one CI command to validate all of them:
|
|
149
|
+
|
|
150
|
+
```json
|
|
151
|
+
{
|
|
152
|
+
"timeoutMs": 10000,
|
|
153
|
+
"servers": [
|
|
154
|
+
{
|
|
155
|
+
"name": "memory",
|
|
156
|
+
"target": "@modelcontextprotocol/server-memory",
|
|
157
|
+
"probeTools": true
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"name": "datadog",
|
|
161
|
+
"target": "https://mcp.example.com/mcp",
|
|
162
|
+
"transport": "http",
|
|
163
|
+
"headers": {
|
|
164
|
+
"Authorization": "Bearer ${DATADOG_MCP_TOKEN}"
|
|
165
|
+
},
|
|
166
|
+
"stderr": {
|
|
167
|
+
"allow": ["^Warning:", "missing optional config"],
|
|
168
|
+
"fatal": ["panic", "FATAL"]
|
|
169
|
+
},
|
|
170
|
+
"toolsFile": "./recipes/datadog.tools.json"
|
|
171
|
+
}
|
|
172
|
+
]
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Run:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
mcp-probe --config mcp-probe.config.json
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
The process exits with `1` if any configured server fails. Warnings such as auth handoff failures still exit `0`, so CI can flag degraded MCP readiness without blocking deploys unless a server is truly broken.
|
|
183
|
+
|
|
184
|
+
Config fields:
|
|
185
|
+
|
|
186
|
+
| Field | Description |
|
|
187
|
+
|-------|-------------|
|
|
188
|
+
| `timeoutMs` | Optional global timeout in milliseconds. CLI `--timeout` is used when omitted. |
|
|
189
|
+
| `servers[].name` | Human-readable name shown in batch output. |
|
|
190
|
+
| `servers[].target` | npm package, local server path, or remote MCP URL. |
|
|
191
|
+
| `servers[].serverArgs` | Optional arguments passed to the MCP server. |
|
|
192
|
+
| `servers[].transport` | Optional transport override: `stdio`, `http`, or `sse`. URL targets default to `http`; package/path targets default to `stdio`. |
|
|
193
|
+
| `servers[].headers` | Optional HTTP headers for remote MCP servers. `${ENV_VAR}` placeholders are expanded at runtime. |
|
|
194
|
+
| `servers[].stderr.allow` | Optional regex patterns for stderr lines that should be ignored when startup fails. |
|
|
195
|
+
| `servers[].stderr.fatal` | Optional regex patterns for stderr lines that should always be treated as the startup failure reason. |
|
|
196
|
+
| `servers[].probeTools` | Enables dry-run tool calls for that server. |
|
|
197
|
+
| `servers[].toolsFile` | Sidecar input file for meaningful `tools/call` samples. Relative paths resolve from the config file directory. |
|
|
198
|
+
|
|
199
|
+
## Stderr classification
|
|
200
|
+
|
|
201
|
+
Many MCP servers write harmless warnings to stderr during startup: optional config notices, update checks, deprecation warnings, and similar noise. If the server later fails to initialize, raw stderr can make those warnings look like the root cause.
|
|
202
|
+
|
|
203
|
+
mcp-probe has built-in warning filters and also lets you declare server-specific regexes:
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
mcp-probe @scope/server \
|
|
207
|
+
--stderr-allow "^Warning:" \
|
|
208
|
+
--stderr-allow "missing optional config" \
|
|
209
|
+
--stderr-fatal "panic|FATAL"
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
For batch checks, put the rules in `mcp-probe.config.json`:
|
|
213
|
+
|
|
214
|
+
```json
|
|
215
|
+
{
|
|
216
|
+
"name": "datadog",
|
|
217
|
+
"target": "https://mcp.example.com/mcp",
|
|
218
|
+
"stderr": {
|
|
219
|
+
"allow": ["^Warning:", "missing optional config"],
|
|
220
|
+
"fatal": ["panic", "FATAL"]
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
`fatal` patterns win over `allow` patterns. If every stderr line is allowed noise, mcp-probe reports the actual connection/init error instead of the warning text.
|
|
226
|
+
|
|
227
|
+
## Tool call dry-runs
|
|
228
|
+
|
|
229
|
+
Discovery proves that a server starts and registers tools. It does **not** prove that the tools actually work in an agent loop. Use `--probe-tools` to call every discovered tool.
|
|
230
|
+
|
|
231
|
+
By default, mcp-probe generates minimal inputs from each tool schema. That catches broken call paths, but real CI gates should prefer a sidecar file with meaningful sample inputs:
|
|
232
|
+
|
|
233
|
+
```json
|
|
234
|
+
{
|
|
235
|
+
"tools": {
|
|
236
|
+
"logs_query": {
|
|
237
|
+
"input": {
|
|
238
|
+
"query": "service:web status:error",
|
|
239
|
+
"timeframe": "1h"
|
|
240
|
+
},
|
|
241
|
+
"expect": {
|
|
242
|
+
"not_error_code": [401, 403]
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Save this as `.mcp-probe.json` in your project root and run:
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
mcp-probe @your-org/datadog-mcp --probe-tools
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Or pass an explicit path:
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
mcp-probe @your-org/datadog-mcp --tools-file ./ci/mcp-tools.json
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Sidecar inputs are used first; generated minimal inputs are fallback only. Auth and permission failures such as 401/403 are surfaced as warnings so CI can distinguish "OAuth handoff needed" from transport or runtime failure.
|
|
262
|
+
|
|
263
|
+
## Status badges
|
|
264
|
+
|
|
265
|
+
Use `--badge-file` to write a [shields.io endpoint](https://shields.io/badges/endpoint-badge) JSON file:
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
mcp-probe --config mcp-probe.config.json --badge-file mcp-probe-badge.json
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Example output:
|
|
272
|
+
|
|
273
|
+
```json
|
|
274
|
+
{
|
|
275
|
+
"schemaVersion": 1,
|
|
276
|
+
"label": "mcp fleet",
|
|
277
|
+
"message": "2 pass, 1 warn",
|
|
278
|
+
"color": "yellow"
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Host that JSON file anywhere public and reference it from your README:
|
|
283
|
+
|
|
284
|
+
```markdown
|
|
285
|
+

|
|
286
|
+
```
|
|
82
287
|
|
|
83
288
|
## Exit codes
|
|
84
289
|
|
|
@@ -89,17 +294,90 @@ mcp-probe @scope/server --timeout 30000
|
|
|
89
294
|
|
|
90
295
|
## CI integration
|
|
91
296
|
|
|
297
|
+
Single server workflow:
|
|
298
|
+
|
|
92
299
|
```yaml
|
|
93
300
|
# .github/workflows/mcp-probe.yml
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
301
|
+
name: MCP Probe
|
|
302
|
+
|
|
303
|
+
on:
|
|
304
|
+
pull_request:
|
|
305
|
+
push:
|
|
306
|
+
branches: [main]
|
|
307
|
+
|
|
308
|
+
jobs:
|
|
309
|
+
mcp-probe:
|
|
310
|
+
runs-on: ubuntu-latest
|
|
311
|
+
timeout-minutes: 5
|
|
312
|
+
|
|
313
|
+
steps:
|
|
314
|
+
- uses: actions/checkout@v4
|
|
315
|
+
|
|
316
|
+
- name: Validate MCP server
|
|
317
|
+
run: |
|
|
318
|
+
npx @k08200/mcp-probe @your-org/your-mcp-server \
|
|
319
|
+
--probe-tools \
|
|
320
|
+
--github-summary \
|
|
321
|
+
--badge-file mcp-probe-badge.json
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Fleet workflow:
|
|
325
|
+
|
|
326
|
+
```yaml
|
|
327
|
+
# .github/workflows/mcp-fleet.yml
|
|
328
|
+
name: MCP Fleet Probe
|
|
329
|
+
|
|
330
|
+
on:
|
|
331
|
+
pull_request:
|
|
332
|
+
push:
|
|
333
|
+
branches: [main]
|
|
334
|
+
schedule:
|
|
335
|
+
- cron: "0 * * * *"
|
|
336
|
+
|
|
337
|
+
jobs:
|
|
338
|
+
mcp-probe:
|
|
339
|
+
runs-on: ubuntu-latest
|
|
340
|
+
timeout-minutes: 10
|
|
341
|
+
|
|
342
|
+
steps:
|
|
343
|
+
- uses: actions/checkout@v4
|
|
344
|
+
|
|
345
|
+
- name: Validate MCP fleet
|
|
346
|
+
run: |
|
|
347
|
+
npx @k08200/mcp-probe \
|
|
348
|
+
--config mcp-probe.config.json \
|
|
349
|
+
--github-summary \
|
|
350
|
+
--badge-file mcp-probe-badge.json
|
|
97
351
|
```
|
|
98
352
|
|
|
353
|
+
When `--github-summary` is enabled in GitHub Actions, mcp-probe appends a Markdown report to `$GITHUB_STEP_SUMMARY` and emits workflow annotations for failed checks, warnings, and tool-call dry-run errors. This makes PR failures point directly at the broken MCP server or tool call instead of burying the signal in raw logs.
|
|
354
|
+
|
|
355
|
+
Copy-ready examples live in [`examples/github-actions`](examples/github-actions):
|
|
356
|
+
|
|
357
|
+
| Example | Use case |
|
|
358
|
+
|---------|----------|
|
|
359
|
+
| [`single-server.yml`](examples/github-actions/single-server.yml) | Validate one stdio MCP package. |
|
|
360
|
+
| [`fleet.yml`](examples/github-actions/fleet.yml) | Validate several MCP servers from `mcp-probe.config.json` on PRs and hourly schedules. |
|
|
361
|
+
| [`remote-server.yml`](examples/github-actions/remote-server.yml) | Validate a remote Streamable HTTP MCP server with auth headers. |
|
|
362
|
+
|
|
363
|
+
mcp-probe also dogfoods itself in CI with [`examples/self-check.config.json`](examples/self-check.config.json), which validates batch mode, sidecar inputs, GitHub summaries, and badge output against a local fixture MCP server.
|
|
364
|
+
|
|
365
|
+
## Recipes
|
|
366
|
+
|
|
367
|
+
Production MCP checks work best with sidecar inputs that exercise real call paths instead of generated empty values. Copy-ready starting points live in [`examples/recipes`](examples/recipes):
|
|
368
|
+
|
|
369
|
+
| Recipe | Focus |
|
|
370
|
+
|--------|-------|
|
|
371
|
+
| [`datadog.tools.json`](examples/recipes/datadog.tools.json) | Logs/metrics queries that reveal auth handoff and downstream API failures. |
|
|
372
|
+
| [`supabase.tools.json`](examples/recipes/supabase.tools.json) | Project visibility and a harmless `select 1` SQL path. |
|
|
373
|
+
| [`gmail.tools.json`](examples/recipes/gmail.tools.json) | OAuth/token handoff and read-only mailbox access. |
|
|
374
|
+
|
|
375
|
+
Tool names vary by MCP server implementation. Run your server once with `--output json`, inspect the discovered tool names and schemas, then adjust the recipe file to match.
|
|
376
|
+
|
|
99
377
|
## JSON output
|
|
100
378
|
|
|
101
379
|
```bash
|
|
102
|
-
mcp-probe @modelcontextprotocol/server-memory --output json
|
|
380
|
+
mcp-probe @modelcontextprotocol/server-memory --probe-tools --output json
|
|
103
381
|
```
|
|
104
382
|
|
|
105
383
|
```json
|
|
@@ -111,10 +389,14 @@ mcp-probe @modelcontextprotocol/server-memory --output json
|
|
|
111
389
|
{ "name": "Target resolution", "status": "pass", "message": "npx --yes @modelcontextprotocol/server-memory" },
|
|
112
390
|
{ "name": "MCP protocol handshake", "status": "pass", "message": "memory-server v0.6.3", "latencyMs": 1392 },
|
|
113
391
|
{ "name": "Tools discovery", "status": "pass", "message": "Found 9 tools", "latencyMs": 33 },
|
|
114
|
-
{ "name": "Tool schema validation", "status": "pass", "message": "All tool schemas are valid" }
|
|
392
|
+
{ "name": "Tool schema validation", "status": "pass", "message": "All tool schemas are valid" },
|
|
393
|
+
{ "name": "Tool call dry-run", "status": "pass", "message": "9 passed (2 sidecar, 7 auto)" }
|
|
115
394
|
],
|
|
116
395
|
"serverInfo": { "name": "memory-server", "version": "0.6.3", "capabilities": ["tools"] },
|
|
117
396
|
"tools": [{ "name": "create_entities", "description": "Create multiple new entities in the knowledge graph" }],
|
|
397
|
+
"toolCallResults": [
|
|
398
|
+
{ "tool": "read_graph", "status": "pass", "latencyMs": 41, "source": "auto" }
|
|
399
|
+
],
|
|
118
400
|
"totalLatencyMs": 1455
|
|
119
401
|
}
|
|
120
402
|
```
|
|
@@ -129,11 +411,13 @@ mcp-probe @modelcontextprotocol/server-memory --output json
|
|
|
129
411
|
|
|
130
412
|
## Roadmap
|
|
131
413
|
|
|
132
|
-
- [
|
|
133
|
-
- [
|
|
134
|
-
- [
|
|
135
|
-
- [
|
|
136
|
-
- [
|
|
414
|
+
- [x] Self-check batch workflow for mcp-probe itself
|
|
415
|
+
- [x] HTTP/SSE transport support
|
|
416
|
+
- [x] Batch checking from a config file (`mcp-probe --config mcp-probe.config.json`)
|
|
417
|
+
- [x] GitHub Actions summary and annotations
|
|
418
|
+
- [x] Badge generation (`mcp-probe --badge-file mcp-probe-badge.json`)
|
|
419
|
+
- [x] Structured stderr classification rules
|
|
420
|
+
- [x] Server-specific recipe examples for Datadog, Supabase, and Gmail MCP servers
|
|
137
421
|
|
|
138
422
|
## Contributing
|
|
139
423
|
|
package/dist/checker.d.ts
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
import type { CheckReport } from './types.js';
|
|
2
|
-
|
|
3
|
-
target: string;
|
|
4
|
-
serverArgs?: string[];
|
|
5
|
-
timeoutMs: number;
|
|
6
|
-
};
|
|
7
|
-
export declare function resolveTarget(target: string): {
|
|
8
|
-
command: string;
|
|
9
|
-
args: string[];
|
|
10
|
-
};
|
|
1
|
+
import type { CheckOptions, CheckReport, ResolvedTarget, TransportMode } from './types.js';
|
|
2
|
+
export declare function resolveTarget(target: string, transport?: TransportMode): ResolvedTarget;
|
|
11
3
|
export declare function checkMcpServer(options: CheckOptions): Promise<CheckReport>;
|
|
12
|
-
export {};
|
|
13
4
|
//# sourceMappingURL=checker.d.ts.map
|
package/dist/checker.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checker.d.ts","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"checker.d.ts","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAa,YAAY,EAAE,WAAW,EAAe,cAAc,EAAe,aAAa,EAAE,MAAM,YAAY,CAAC;AAQhI,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,aAAa,GAAG,cAAc,CAavF;AAiDD,wBAAsB,cAAc,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CA8HhF"}
|
package/dist/checker.js
CHANGED
|
@@ -1,9 +1,57 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'fs';
|
|
1
2
|
import { probeMcpServer } from './protocols/mcp-client.js';
|
|
2
|
-
|
|
3
|
+
const SIDECAR_FILENAME = '.mcp-probe.json';
|
|
4
|
+
function isUrlTarget(target) {
|
|
5
|
+
return /^https?:\/\//i.test(target);
|
|
6
|
+
}
|
|
7
|
+
export function resolveTarget(target, transport) {
|
|
8
|
+
if (transport === 'http' || transport === 'sse') {
|
|
9
|
+
return { transport, url: target };
|
|
10
|
+
}
|
|
11
|
+
if (isUrlTarget(target)) {
|
|
12
|
+
return { transport: 'http', url: target };
|
|
13
|
+
}
|
|
3
14
|
if (target.startsWith('.') || target.startsWith('/')) {
|
|
4
|
-
return { command: 'node', args: [target] };
|
|
15
|
+
return { transport: 'stdio', command: 'node', args: [target] };
|
|
5
16
|
}
|
|
6
|
-
return { command: 'npx', args: ['--yes', target] };
|
|
17
|
+
return { transport: 'stdio', command: 'npx', args: ['--yes', target] };
|
|
18
|
+
}
|
|
19
|
+
function loadSidecar(toolsFile) {
|
|
20
|
+
const path = toolsFile ?? SIDECAR_FILENAME;
|
|
21
|
+
if (!existsSync(path)) {
|
|
22
|
+
if (!toolsFile)
|
|
23
|
+
return undefined;
|
|
24
|
+
throw new Error(`Cannot read tools file: ${path}`);
|
|
25
|
+
}
|
|
26
|
+
let parsed;
|
|
27
|
+
try {
|
|
28
|
+
const raw = readFileSync(path, 'utf8');
|
|
29
|
+
parsed = JSON.parse(raw);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
throw new Error(`Invalid tools file JSON: ${path}`);
|
|
33
|
+
}
|
|
34
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
35
|
+
throw new Error(`Invalid tools file: ${path} must be an object`);
|
|
36
|
+
}
|
|
37
|
+
const sidecar = parsed;
|
|
38
|
+
if (!sidecar.tools || typeof sidecar.tools !== 'object' || Array.isArray(sidecar.tools)) {
|
|
39
|
+
throw new Error(`Invalid tools file: ${path} must contain a tools object`);
|
|
40
|
+
}
|
|
41
|
+
for (const [toolName, entry] of Object.entries(sidecar.tools)) {
|
|
42
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
|
|
43
|
+
throw new Error(`Invalid tools file: ${toolName} entry must be an object`);
|
|
44
|
+
}
|
|
45
|
+
const toolEntry = entry;
|
|
46
|
+
if (!toolEntry.input || typeof toolEntry.input !== 'object' || Array.isArray(toolEntry.input)) {
|
|
47
|
+
throw new Error(`Invalid tools file: ${toolName}.input must be an object`);
|
|
48
|
+
}
|
|
49
|
+
const codes = toolEntry.expect?.not_error_code;
|
|
50
|
+
if (codes !== undefined && (!Array.isArray(codes) || !codes.every((c) => typeof c === 'number'))) {
|
|
51
|
+
throw new Error(`Invalid tools file: ${toolName}.expect.not_error_code must be a number array`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return sidecar;
|
|
7
55
|
}
|
|
8
56
|
function deriveOverallStatus(checks) {
|
|
9
57
|
if (checks.some((c) => c.status === 'fail'))
|
|
@@ -15,16 +63,30 @@ function deriveOverallStatus(checks) {
|
|
|
15
63
|
export async function checkMcpServer(options) {
|
|
16
64
|
const startTime = Date.now();
|
|
17
65
|
const checks = [];
|
|
18
|
-
const resolved = resolveTarget(options.target);
|
|
19
|
-
const args = [...resolved.args, ...(options.serverArgs ?? [])];
|
|
20
|
-
const
|
|
66
|
+
const resolved = resolveTarget(options.target, options.transport);
|
|
67
|
+
const args = [...(resolved.args ?? []), ...(options.serverArgs ?? [])];
|
|
68
|
+
const probeTools = options.probeTools || Boolean(options.toolsFile);
|
|
69
|
+
const resolutionMessage = resolved.transport === 'stdio'
|
|
70
|
+
? `${resolved.command} ${args.join(' ')}`
|
|
71
|
+
: `${resolved.transport} ${resolved.url}`;
|
|
21
72
|
checks.push({
|
|
22
73
|
name: 'Target resolution',
|
|
23
74
|
status: 'pass',
|
|
24
|
-
message:
|
|
75
|
+
message: resolutionMessage,
|
|
25
76
|
});
|
|
26
77
|
try {
|
|
27
|
-
const
|
|
78
|
+
const sidecar = probeTools ? loadSidecar(options.toolsFile) : undefined;
|
|
79
|
+
const probe = await probeMcpServer({
|
|
80
|
+
transport: resolved.transport,
|
|
81
|
+
command: resolved.command,
|
|
82
|
+
args,
|
|
83
|
+
url: resolved.url,
|
|
84
|
+
headers: options.headers,
|
|
85
|
+
stderr: options.stderr,
|
|
86
|
+
timeoutMs: options.timeoutMs,
|
|
87
|
+
probeTools,
|
|
88
|
+
sidecar,
|
|
89
|
+
});
|
|
28
90
|
checks.push({
|
|
29
91
|
name: 'MCP protocol handshake',
|
|
30
92
|
status: 'pass',
|
|
@@ -66,6 +128,26 @@ export async function checkMcpServer(options) {
|
|
|
66
128
|
latencyMs: probe.promptsLatencyMs,
|
|
67
129
|
});
|
|
68
130
|
}
|
|
131
|
+
if (probe.toolCallResults && probe.toolCallResults.length > 0) {
|
|
132
|
+
const failed = probe.toolCallResults.filter((r) => r.status === 'fail');
|
|
133
|
+
const warned = probe.toolCallResults.filter((r) => r.status === 'warn');
|
|
134
|
+
const passed = probe.toolCallResults.filter((r) => r.status === 'pass');
|
|
135
|
+
const sidecarCount = probe.toolCallResults.filter((r) => r.source === 'sidecar').length;
|
|
136
|
+
const status = failed.length > 0 ? 'fail' : warned.length > 0 ? 'warn' : 'pass';
|
|
137
|
+
const parts = [];
|
|
138
|
+
if (passed.length > 0)
|
|
139
|
+
parts.push(`${passed.length} passed`);
|
|
140
|
+
if (warned.length > 0)
|
|
141
|
+
parts.push(`${warned.length} auth/permission errors`);
|
|
142
|
+
if (failed.length > 0)
|
|
143
|
+
parts.push(`${failed.length} failed`);
|
|
144
|
+
const sourceNote = sidecarCount > 0 ? ` (${sidecarCount} sidecar, ${probe.toolCallResults.length - sidecarCount} auto)` : '';
|
|
145
|
+
checks.push({
|
|
146
|
+
name: 'Tool call dry-run',
|
|
147
|
+
status,
|
|
148
|
+
message: parts.join(', ') + sourceNote,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
69
151
|
return {
|
|
70
152
|
target: options.target,
|
|
71
153
|
timestamp: new Date().toISOString(),
|
|
@@ -75,13 +157,15 @@ export async function checkMcpServer(options) {
|
|
|
75
157
|
tools: probe.tools,
|
|
76
158
|
resources: probe.resources,
|
|
77
159
|
prompts: probe.prompts,
|
|
160
|
+
toolCallResults: probe.toolCallResults,
|
|
78
161
|
totalLatencyMs: Date.now() - startTime,
|
|
79
162
|
};
|
|
80
163
|
}
|
|
81
164
|
catch (error) {
|
|
82
165
|
const message = error instanceof Error ? error.message : String(error);
|
|
166
|
+
const isSidecarError = message.includes('tools file');
|
|
83
167
|
checks.push({
|
|
84
|
-
name: 'MCP protocol handshake',
|
|
168
|
+
name: isSidecarError ? 'Tool sidecar' : 'MCP protocol handshake',
|
|
85
169
|
status: 'fail',
|
|
86
170
|
message,
|
|
87
171
|
});
|
package/dist/checker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checker.js","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"checker.js","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAG3D,MAAM,gBAAgB,GAAG,iBAAiB,CAAC;AAE3C,SAAS,WAAW,CAAC,MAAc;IACjC,OAAO,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAc,EAAE,SAAyB;IACrE,IAAI,SAAS,KAAK,MAAM,IAAI,SAAS,KAAK,KAAK,EAAE,CAAC;QAChD,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;IACpC,CAAC;IAED,IAAI,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;QACxB,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;IAC5C,CAAC;IAED,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACrD,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC;IACjE,CAAC;IACD,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC;AACzE,CAAC;AAED,SAAS,WAAW,CAAC,SAAkB;IACrC,MAAM,IAAI,GAAG,SAAS,IAAI,gBAAgB,CAAC;IAC3C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,IAAI,CAAC,SAAS;YAAE,OAAO,SAAS,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,2BAA2B,IAAI,EAAE,CAAC,CAAC;IACrD,CAAC;IAED,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACvC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,4BAA4B,IAAI,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,oBAAoB,CAAC,CAAC;IACnE,CAAC;IAED,MAAM,OAAO,GAAG,MAA8B,CAAC;IAC/C,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,OAAO,OAAO,CAAC,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACxF,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,8BAA8B,CAAC,CAAC;IAC7E,CAAC;IAED,KAAK,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9D,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAChE,MAAM,IAAI,KAAK,CAAC,uBAAuB,QAAQ,0BAA0B,CAAC,CAAC;QAC7E,CAAC;QACD,MAAM,SAAS,GAAG,KAAmE,CAAC;QACtF,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,OAAO,SAAS,CAAC,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9F,MAAM,IAAI,KAAK,CAAC,uBAAuB,QAAQ,0BAA0B,CAAC,CAAC;QAC7E,CAAC;QACD,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,EAAE,cAAc,CAAC;QAC/C,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,EAAE,CAAC;YACjG,MAAM,IAAI,KAAK,CAAC,uBAAuB,QAAQ,+CAA+C,CAAC,CAAC;QAClG,CAAC;IACH,CAAC;IAED,OAAO,OAAsB,CAAC;AAChC,CAAC;AAED,SAAS,mBAAmB,CAAC,MAAmB;IAC9C,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC;IAC3D,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC;IAC3D,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAAqB;IACxD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,MAAM,GAAgB,EAAE,CAAC;IAC/B,MAAM,QAAQ,GAAG,aAAa,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IAClE,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,CAAC;IACvE,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACpE,MAAM,iBAAiB,GAAG,QAAQ,CAAC,SAAS,KAAK,OAAO;QACtD,CAAC,CAAC,GAAG,QAAQ,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;QACzC,CAAC,CAAC,GAAG,QAAQ,CAAC,SAAS,IAAI,QAAQ,CAAC,GAAG,EAAE,CAAC;IAE5C,MAAM,CAAC,IAAI,CAAC;QACV,IAAI,EAAE,mBAAmB;QACzB,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,iBAAiB;KAC3B,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACxE,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC;YACjC,SAAS,EAAE,QAAQ,CAAC,SAAS;YAC7B,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,IAAI;YACJ,GAAG,EAAE,QAAQ,CAAC,GAAG;YACjB,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,UAAU;YACV,OAAO;SACR,CAAC,CAAC;QAEH,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,wBAAwB;YAC9B,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,KAAK,KAAK,CAAC,UAAU,CAAC,OAAO,EAAE;YAChE,SAAS,EAAE,KAAK,CAAC,gBAAgB;SAClC,CAAC,CAAC;QAEH,MAAM,WAAW,GAAgB,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;QAC1E,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,iBAAiB;YACvB,MAAM,EAAE,WAAW;YACnB,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;gBAC7B,CAAC,CAAC,SAAS,KAAK,CAAC,KAAK,CAAC,MAAM,QAAQ,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC1E,CAAC,CAAC,4DAA4D;YAChE,SAAS,EAAE,KAAK,CAAC,cAAc;SAChC,CAAC,CAAC;QAEH,MAAM,gBAAgB,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC5D,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,wBAAwB;gBAC9B,MAAM,EAAE,gBAAgB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM;gBACrD,OAAO,EAAE,gBAAgB,CAAC,MAAM,GAAG,CAAC;oBAClC,CAAC,CAAC,GAAG,gBAAgB,CAAC,MAAM,sCAAsC;oBAClE,CAAC,CAAC,4BAA4B;aACjC,CAAC,CAAC;QACL,CAAC;QAED,IAAI,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,kBAAkB,KAAK,SAAS,EAAE,CAAC;YACzE,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,qBAAqB;gBAC3B,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,SAAS,KAAK,CAAC,SAAS,CAAC,MAAM,YAAY,KAAK,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC7F,SAAS,EAAE,KAAK,CAAC,kBAAkB;aACpC,CAAC,CAAC;QACL,CAAC;QAED,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;YACrE,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,mBAAmB;gBACzB,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,SAAS,KAAK,CAAC,OAAO,CAAC,MAAM,UAAU,KAAK,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;gBACvF,SAAS,EAAE,KAAK,CAAC,gBAAgB;aAClC,CAAC,CAAC;QACL,CAAC;QAED,IAAI,KAAK,CAAC,eAAe,IAAI,KAAK,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9D,MAAM,MAAM,GAAG,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;YACxE,MAAM,MAAM,GAAG,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;YACxE,MAAM,MAAM,GAAG,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;YACxE,MAAM,YAAY,GAAG,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC;YACxF,MAAM,MAAM,GAAgB,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;YAC7F,MAAM,KAAK,GAAa,EAAE,CAAC;YAC3B,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;gBAAE,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,SAAS,CAAC,CAAC;YAC7D,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;gBAAE,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,yBAAyB,CAAC,CAAC;YAC7E,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;gBAAE,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,SAAS,CAAC,CAAC;YAC7D,MAAM,UAAU,GAAG,YAAY,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,YAAY,aAAa,KAAK,CAAC,eAAe,CAAC,MAAM,GAAG,YAAY,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7H,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,mBAAmB;gBACzB,MAAM;gBACN,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,UAAU;aACvC,CAAC,CAAC;QACL,CAAC;QAED,OAAO;YACL,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,aAAa,EAAE,mBAAmB,CAAC,MAAM,CAAC;YAC1C,MAAM;YACN,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,eAAe,EAAE,KAAK,CAAC,eAAe;YACtC,cAAc,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;SACvC,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,MAAM,cAAc,GAAG,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;QACtD,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,wBAAwB;YAChE,MAAM,EAAE,MAAM;YACd,OAAO;SACR,CAAC,CAAC;QAEH,OAAO;YACL,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,aAAa,EAAE,MAAM;YACrB,MAAM;YACN,KAAK,EAAE,EAAE;YACT,SAAS,EAAE,EAAE;YACb,OAAO,EAAE,EAAE;YACX,cAAc,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;SACvC,CAAC;IACJ,CAAC;AACH,CAAC"}
|