synapse-mcp 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 +607 -0
- package/dist/constants.d.ts +23 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +58 -0
- package/dist/constants.js.map +1 -0
- package/dist/formatters/index.d.ts +275 -0
- package/dist/formatters/index.d.ts.map +1 -0
- package/dist/formatters/index.js +461 -0
- package/dist/formatters/index.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +178 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas/common.d.ts +48 -0
- package/dist/schemas/common.d.ts.map +1 -0
- package/dist/schemas/common.js +69 -0
- package/dist/schemas/common.js.map +1 -0
- package/dist/schemas/discriminator.d.ts +20 -0
- package/dist/schemas/discriminator.d.ts.map +1 -0
- package/dist/schemas/discriminator.js +25 -0
- package/dist/schemas/discriminator.js.map +1 -0
- package/dist/schemas/flux/compose.d.ts +93 -0
- package/dist/schemas/flux/compose.d.ts.map +1 -0
- package/dist/schemas/flux/compose.js +112 -0
- package/dist/schemas/flux/compose.js.map +1 -0
- package/dist/schemas/flux/container.d.ts +144 -0
- package/dist/schemas/flux/container.d.ts.map +1 -0
- package/dist/schemas/flux/container.js +163 -0
- package/dist/schemas/flux/container.js.map +1 -0
- package/dist/schemas/flux/docker.d.ts +91 -0
- package/dist/schemas/flux/docker.d.ts.map +1 -0
- package/dist/schemas/flux/docker.js +101 -0
- package/dist/schemas/flux/docker.js.map +1 -0
- package/dist/schemas/flux/host.d.ts +61 -0
- package/dist/schemas/flux/host.d.ts.map +1 -0
- package/dist/schemas/flux/host.js +72 -0
- package/dist/schemas/flux/host.js.map +1 -0
- package/dist/schemas/flux/index.d.ts +20 -0
- package/dist/schemas/flux/index.d.ts.map +1 -0
- package/dist/schemas/flux/index.js +88 -0
- package/dist/schemas/flux/index.js.map +1 -0
- package/dist/schemas/index.d.ts +11 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +11 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/scout/index.d.ts +151 -0
- package/dist/schemas/scout/index.d.ts.map +1 -0
- package/dist/schemas/scout/index.js +41 -0
- package/dist/schemas/scout/index.js.map +1 -0
- package/dist/schemas/scout/logs.d.ts +48 -0
- package/dist/schemas/scout/logs.d.ts.map +1 -0
- package/dist/schemas/scout/logs.js +47 -0
- package/dist/schemas/scout/logs.js.map +1 -0
- package/dist/schemas/scout/simple.d.ts +68 -0
- package/dist/schemas/scout/simple.d.ts.map +1 -0
- package/dist/schemas/scout/simple.js +75 -0
- package/dist/schemas/scout/simple.js.map +1 -0
- package/dist/schemas/scout/zfs.d.ts +37 -0
- package/dist/schemas/scout/zfs.d.ts.map +1 -0
- package/dist/schemas/scout/zfs.js +36 -0
- package/dist/schemas/scout/zfs.js.map +1 -0
- package/dist/schemas/unified.d.ts +674 -0
- package/dist/schemas/unified.d.ts.map +1 -0
- package/dist/schemas/unified.js +453 -0
- package/dist/schemas/unified.js.map +1 -0
- package/dist/services/compose.d.ts +107 -0
- package/dist/services/compose.d.ts.map +1 -0
- package/dist/services/compose.js +308 -0
- package/dist/services/compose.js.map +1 -0
- package/dist/services/container.d.ts +69 -0
- package/dist/services/container.d.ts.map +1 -0
- package/dist/services/container.js +111 -0
- package/dist/services/container.js.map +1 -0
- package/dist/services/docker.d.ts +243 -0
- package/dist/services/docker.d.ts.map +1 -0
- package/dist/services/docker.js +812 -0
- package/dist/services/docker.js.map +1 -0
- package/dist/services/file-service.d.ts +79 -0
- package/dist/services/file-service.d.ts.map +1 -0
- package/dist/services/file-service.js +226 -0
- package/dist/services/file-service.js.map +1 -0
- package/dist/services/interfaces.d.ts +537 -0
- package/dist/services/interfaces.d.ts.map +1 -0
- package/dist/services/interfaces.js +2 -0
- package/dist/services/interfaces.js.map +1 -0
- package/dist/services/ssh-pool-exec.d.ts +10 -0
- package/dist/services/ssh-pool-exec.d.ts.map +1 -0
- package/dist/services/ssh-pool-exec.js +10 -0
- package/dist/services/ssh-pool-exec.js.map +1 -0
- package/dist/services/ssh-pool.d.ts +66 -0
- package/dist/services/ssh-pool.d.ts.map +1 -0
- package/dist/services/ssh-pool.js +253 -0
- package/dist/services/ssh-pool.js.map +1 -0
- package/dist/services/ssh-service.d.ts +39 -0
- package/dist/services/ssh-service.d.ts.map +1 -0
- package/dist/services/ssh-service.js +143 -0
- package/dist/services/ssh-service.js.map +1 -0
- package/dist/services/ssh.d.ts +37 -0
- package/dist/services/ssh.d.ts.map +1 -0
- package/dist/services/ssh.js +50 -0
- package/dist/services/ssh.js.map +1 -0
- package/dist/tools/flux.d.ts +14 -0
- package/dist/tools/flux.d.ts.map +1 -0
- package/dist/tools/flux.js +86 -0
- package/dist/tools/flux.js.map +1 -0
- package/dist/tools/index.d.ts +7 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +43 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/scout.d.ts +14 -0
- package/dist/tools/scout.d.ts.map +1 -0
- package/dist/tools/scout.js +96 -0
- package/dist/tools/scout.js.map +1 -0
- package/dist/tools/unified.d.ts +7 -0
- package/dist/tools/unified.d.ts.map +1 -0
- package/dist/tools/unified.js +827 -0
- package/dist/tools/unified.js.map +1 -0
- package/dist/types.d.ts +93 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/errors.d.ts +60 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +131 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/help.d.ts +69 -0
- package/dist/utils/help.d.ts.map +1 -0
- package/dist/utils/help.js +259 -0
- package/dist/utils/help.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/path-security.d.ts +64 -0
- package/dist/utils/path-security.d.ts.map +1 -0
- package/dist/utils/path-security.js +138 -0
- package/dist/utils/path-security.js.map +1 -0
- package/package.json +85 -0
package/README.md
ADDED
|
@@ -0,0 +1,607 @@
|
|
|
1
|
+
# Synapse MCP
|
|
2
|
+
|
|
3
|
+
MCP (Model Context Protocol) server providing **Flux** (Docker management) and **Scout** (SSH operations) tools for homelab infrastructure. The neural connection point for your distributed systems.
|
|
4
|
+
|
|
5
|
+
Designed for use with Claude Code and other MCP-compatible clients.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
### Flux Tool (Docker Infrastructure Management)
|
|
10
|
+
- **Container lifecycle**: Start, stop, restart, pause/resume, pull, recreate, exec
|
|
11
|
+
- **Docker Compose**: Full project management (up, down, restart, logs, build, pull, recreate)
|
|
12
|
+
- **Image operations**: List, pull, build, remove Docker images
|
|
13
|
+
- **Host operations**: Status checks, resource monitoring, systemd services, network info
|
|
14
|
+
- **Log retrieval**: Advanced filtering with time ranges, grep, stream selection
|
|
15
|
+
- **Resource monitoring**: Real-time CPU, memory, network, I/O statistics
|
|
16
|
+
- **Smart search**: Find containers by name, image, or labels across all hosts
|
|
17
|
+
- **Pagination & filtering**: All list operations support limits, offsets, and filtering
|
|
18
|
+
|
|
19
|
+
### Scout Tool (SSH Remote Operations)
|
|
20
|
+
- **File operations**: Read files, directory trees, file transfer (beam), diff comparison
|
|
21
|
+
- **Remote execution**: Execute commands with allowlist security
|
|
22
|
+
- **Process monitoring**: List and filter processes by user, CPU, memory
|
|
23
|
+
- **ZFS management**: Pools, datasets, snapshots with health monitoring
|
|
24
|
+
- **System logs**: Access syslog, journald, dmesg, auth logs with filtering
|
|
25
|
+
- **Disk monitoring**: Filesystem usage across all mounts
|
|
26
|
+
- **Multi-host operations**: Execute commands or read files across multiple hosts (emit)
|
|
27
|
+
|
|
28
|
+
### Infrastructure
|
|
29
|
+
- **Multi-host support**: Manage Docker and SSH across Unraid, Proxmox, bare metal
|
|
30
|
+
- **Auto-detect local Docker**: Automatically adds local Docker socket if available
|
|
31
|
+
- **Dual transport**: stdio for Claude Code, HTTP for remote access
|
|
32
|
+
- **O(1) validation**: Discriminated union pattern for instant schema validation
|
|
33
|
+
- **SSH connection pooling**: 50× faster repeated operations
|
|
34
|
+
|
|
35
|
+
## Tools
|
|
36
|
+
|
|
37
|
+
The server provides two powerful tools with discriminated union schemas for O(1) validation:
|
|
38
|
+
|
|
39
|
+
### Tool 1: `flux` - Docker Infrastructure Management
|
|
40
|
+
|
|
41
|
+
**4 actions, 39 subactions** - State changes, lifecycle control, destructive operations.
|
|
42
|
+
|
|
43
|
+
#### Container Operations (`action: "container"`) - 14 subactions
|
|
44
|
+
|
|
45
|
+
| Subaction | Description |
|
|
46
|
+
| ---------|-------------|
|
|
47
|
+
| `list` | List containers with filtering by state, name, image, labels |
|
|
48
|
+
| `start` | Start a stopped container |
|
|
49
|
+
| `stop` | Stop a running container |
|
|
50
|
+
| `restart` | Restart a container |
|
|
51
|
+
| `pause` | Pause a running container |
|
|
52
|
+
| `resume` | Resume a paused container (was `unpause`) |
|
|
53
|
+
| `logs` | Retrieve container logs with time and grep filters |
|
|
54
|
+
| `stats` | Get real-time CPU, memory, network, I/O statistics |
|
|
55
|
+
| `inspect` | Detailed container configuration and state (with summary mode) |
|
|
56
|
+
| `search` | Search containers by name, image, or labels |
|
|
57
|
+
| `pull` | Pull latest image for a container |
|
|
58
|
+
| `recreate` | Recreate container with latest image |
|
|
59
|
+
| `exec` | Execute command inside a container (allowlist validated) |
|
|
60
|
+
| `top` | Show running processes in a container |
|
|
61
|
+
|
|
62
|
+
#### Docker Compose Operations (`action: "compose"`) - 9 subactions
|
|
63
|
+
|
|
64
|
+
| Subaction | Description |
|
|
65
|
+
| ---------|-------------|
|
|
66
|
+
| `list` | List Docker Compose projects on a host |
|
|
67
|
+
| `status` | Get status of services in a project |
|
|
68
|
+
| `up` | Start a compose project |
|
|
69
|
+
| `down` | Stop a compose project |
|
|
70
|
+
| `restart` | Restart a compose project |
|
|
71
|
+
| `logs` | Get logs from compose project services |
|
|
72
|
+
| `build` | Build images for a compose project |
|
|
73
|
+
| `pull` | Pull images for a compose project |
|
|
74
|
+
| `recreate` | Force recreate containers in a project |
|
|
75
|
+
|
|
76
|
+
#### Docker System Operations (`action: "docker"`) - 9 subactions
|
|
77
|
+
|
|
78
|
+
| Subaction | Description |
|
|
79
|
+
| ---------|-------------|
|
|
80
|
+
| `info` | Get Docker daemon information |
|
|
81
|
+
| `df` | Get Docker disk usage (images, containers, volumes, cache) |
|
|
82
|
+
| `prune` | Remove unused Docker resources (requires `force: true`) |
|
|
83
|
+
| `images` | List Docker images on a host |
|
|
84
|
+
| `pull` | Pull a Docker image |
|
|
85
|
+
| `build` | Build a Docker image from Dockerfile |
|
|
86
|
+
| `rmi` | Remove a Docker image |
|
|
87
|
+
| `networks` | List Docker networks |
|
|
88
|
+
| `volumes` | List Docker volumes |
|
|
89
|
+
|
|
90
|
+
#### Host Operations (`action: "host"`) - 7 subactions
|
|
91
|
+
|
|
92
|
+
| Subaction | Description |
|
|
93
|
+
| ---------|-------------|
|
|
94
|
+
| `status` | Check Docker connectivity to host |
|
|
95
|
+
| `resources` | Get CPU, memory, disk usage via SSH |
|
|
96
|
+
| `info` | Get OS, kernel, architecture, hostname |
|
|
97
|
+
| `uptime` | Get system uptime |
|
|
98
|
+
| `services` | Get systemd service status |
|
|
99
|
+
| `network` | Get network interfaces and IP addresses |
|
|
100
|
+
| `mounts` | Get mounted filesystems |
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
### Tool 2: `scout` - SSH Remote Operations
|
|
105
|
+
|
|
106
|
+
**11 actions, 16 operations** - Read-mostly remote file and system operations.
|
|
107
|
+
|
|
108
|
+
#### Simple Actions (9)
|
|
109
|
+
|
|
110
|
+
| Action | Description |
|
|
111
|
+
| ------|-------------|
|
|
112
|
+
| `nodes` | List all configured SSH hosts |
|
|
113
|
+
| `peek` | Read file or directory contents (with tree mode) |
|
|
114
|
+
| `exec` | Execute command on remote host (allowlist validated) |
|
|
115
|
+
| `find` | Find files by glob pattern |
|
|
116
|
+
| `delta` | Compare files or content between locations |
|
|
117
|
+
| `emit` | Multi-host operations (read files or execute commands) |
|
|
118
|
+
| `beam` | File transfer between local/remote or remote/remote |
|
|
119
|
+
| `ps` | List and search processes with filtering |
|
|
120
|
+
| `df` | Disk usage information |
|
|
121
|
+
|
|
122
|
+
#### ZFS Operations (`action: "zfs"`) - 3 subactions
|
|
123
|
+
|
|
124
|
+
| Subaction | Description |
|
|
125
|
+
| ---------|-------------|
|
|
126
|
+
| `pools` | List ZFS storage pools with health status |
|
|
127
|
+
| `datasets` | List ZFS datasets (filesystems and volumes) |
|
|
128
|
+
| `snapshots` | List ZFS snapshots |
|
|
129
|
+
|
|
130
|
+
#### Log Operations (`action: "logs"`) - 4 subactions
|
|
131
|
+
|
|
132
|
+
| Subaction | Description |
|
|
133
|
+
| ---------|-------------|
|
|
134
|
+
| `syslog` | Access system log files (/var/log) |
|
|
135
|
+
| `journal` | Access systemd journal logs with unit filtering |
|
|
136
|
+
| `dmesg` | Access kernel ring buffer logs |
|
|
137
|
+
| `auth` | Access authentication logs |
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Example Usage
|
|
142
|
+
|
|
143
|
+
### Flux Tool Examples
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
// List running containers
|
|
147
|
+
{ "tool": "flux", "action": "container", "subaction": "list", "state": "running" }
|
|
148
|
+
|
|
149
|
+
// Restart a container
|
|
150
|
+
{ "tool": "flux", "action": "container", "subaction": "restart", "container_id": "plex", "host": "tootie" }
|
|
151
|
+
|
|
152
|
+
// Start a compose project
|
|
153
|
+
{ "tool": "flux", "action": "compose", "subaction": "up", "host": "tootie", "project": "media-stack" }
|
|
154
|
+
|
|
155
|
+
// Get host resources
|
|
156
|
+
{ "tool": "flux", "action": "host", "subaction": "resources", "host": "tootie" }
|
|
157
|
+
|
|
158
|
+
// Pull an image
|
|
159
|
+
{ "tool": "flux", "action": "docker", "subaction": "pull", "host": "tootie", "image": "nginx:latest" }
|
|
160
|
+
|
|
161
|
+
// Execute command in container
|
|
162
|
+
{ "tool": "flux", "action": "container", "subaction": "exec", "container_id": "nginx", "command": "nginx -t" }
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Scout Tool Examples
|
|
166
|
+
|
|
167
|
+
```json
|
|
168
|
+
// List configured SSH hosts
|
|
169
|
+
{ "tool": "scout", "action": "nodes" }
|
|
170
|
+
|
|
171
|
+
// Read a remote file
|
|
172
|
+
{ "tool": "scout", "action": "peek", "target": "tootie:/etc/nginx/nginx.conf" }
|
|
173
|
+
|
|
174
|
+
// Show directory tree
|
|
175
|
+
{ "tool": "scout", "action": "peek", "target": "dookie:/var/log", "tree": true }
|
|
176
|
+
|
|
177
|
+
// Execute remote command
|
|
178
|
+
{ "tool": "scout", "action": "exec", "target": "tootie:/var/www", "command": "du -sh *" }
|
|
179
|
+
|
|
180
|
+
// Transfer file between hosts
|
|
181
|
+
{ "tool": "scout", "action": "beam", "source": "tootie:/tmp/backup.tar.gz", "destination": "dookie:/backup/" }
|
|
182
|
+
|
|
183
|
+
// Check ZFS pool health
|
|
184
|
+
{ "tool": "scout", "action": "zfs", "subaction": "pools", "host": "dookie" }
|
|
185
|
+
|
|
186
|
+
// View systemd journal
|
|
187
|
+
{ "tool": "scout", "action": "logs", "subaction": "journal", "host": "tootie", "unit": "docker.service" }
|
|
188
|
+
|
|
189
|
+
// Multi-host command execution
|
|
190
|
+
{ "tool": "scout", "action": "emit", "targets": ["tootie:/tmp", "dookie:/tmp"], "command": "df -h" }
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Installation
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
# Clone or copy the server files
|
|
197
|
+
cd synapse-mcp
|
|
198
|
+
|
|
199
|
+
# Install dependencies
|
|
200
|
+
pnpm install
|
|
201
|
+
|
|
202
|
+
# Build
|
|
203
|
+
pnpm run build
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Configuration
|
|
207
|
+
|
|
208
|
+
Create a config file at one of these locations (checked in order):
|
|
209
|
+
|
|
210
|
+
1. Path in `SYNAPSE_CONFIG_FILE` env var
|
|
211
|
+
2. `./synapse.config.json` (current directory)
|
|
212
|
+
3. `~/.config/synapse-mcp/config.json`
|
|
213
|
+
4. `~/.synapse-mcp.json`
|
|
214
|
+
|
|
215
|
+
### Example Config
|
|
216
|
+
|
|
217
|
+
```json
|
|
218
|
+
{
|
|
219
|
+
"hosts": [
|
|
220
|
+
{
|
|
221
|
+
"name": "unraid",
|
|
222
|
+
"host": "unraid.local",
|
|
223
|
+
"port": 2375,
|
|
224
|
+
"protocol": "http",
|
|
225
|
+
"tags": ["media", "storage"]
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
"name": "proxmox-docker",
|
|
229
|
+
"host": "192.168.1.100",
|
|
230
|
+
"port": 2375,
|
|
231
|
+
"protocol": "http",
|
|
232
|
+
"tags": ["vms"]
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
"name": "local",
|
|
236
|
+
"host": "localhost",
|
|
237
|
+
"protocol": "http",
|
|
238
|
+
"dockerSocketPath": "/var/run/docker.sock"
|
|
239
|
+
}
|
|
240
|
+
]
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Copy `synapse.config.example.json` as a starting point:
|
|
245
|
+
```bash
|
|
246
|
+
cp synapse.config.example.json ~/.config/synapse-mcp/config.json
|
|
247
|
+
# or
|
|
248
|
+
cp synapse.config.example.json ~/.synapse-mcp.json
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
> **Note:** If `/var/run/docker.sock` exists and isn't already in your config, it will be automatically added as a host using your machine's hostname. This means the server works out-of-the-box for local Docker without any configuration.
|
|
252
|
+
|
|
253
|
+
### Host Configuration Options
|
|
254
|
+
|
|
255
|
+
| Field | Type | Description |
|
|
256
|
+
| ----- | ---- | ----------- |
|
|
257
|
+
| `name` | `string` | Unique identifier for the host |
|
|
258
|
+
| `host` | `string` | Hostname or IP address |
|
|
259
|
+
| `port` | `number` | Docker API port (default: 2375) |
|
|
260
|
+
| `protocol` | `"http"` / `"https"` / `"ssh"` | Connection protocol |
|
|
261
|
+
| `dockerSocketPath` | `string` | Path to Docker socket (for local connections) |
|
|
262
|
+
| `sshUser` | `string` | SSH username for remote connections (protocol: "ssh") |
|
|
263
|
+
| `sshKeyPath` | `string` | Path to SSH private key for authentication |
|
|
264
|
+
| `tags` | `string[]` | Optional tags for filtering |
|
|
265
|
+
|
|
266
|
+
### Resource Limits & Defaults
|
|
267
|
+
|
|
268
|
+
| Setting | Value | Description |
|
|
269
|
+
| -------|-------|-------------|
|
|
270
|
+
| `CHARACTER_LIMIT` | 40,000 | Maximum response size (~12.5k tokens) |
|
|
271
|
+
| `DEFAULT_LIMIT` | 20 | Default pagination limit for list operations |
|
|
272
|
+
| `MAX_LIMIT` | 100 | Maximum pagination limit |
|
|
273
|
+
| `DEFAULT_LOG_LINES` | 50 | Default number of log lines to fetch |
|
|
274
|
+
| `MAX_LOG_LINES` | 500 | Maximum log lines allowed |
|
|
275
|
+
| `API_TIMEOUT` | 30s | Docker API operation timeout |
|
|
276
|
+
| `STATS_TIMEOUT` | 5s | Stats collection timeout |
|
|
277
|
+
|
|
278
|
+
### Enabling Docker API on Hosts
|
|
279
|
+
|
|
280
|
+
#### Unraid
|
|
281
|
+
Docker API is typically available at port 2375 by default.
|
|
282
|
+
|
|
283
|
+
#### Standard Docker (systemd)
|
|
284
|
+
Edit `/etc/docker/daemon.json`:
|
|
285
|
+
```json
|
|
286
|
+
{
|
|
287
|
+
"hosts": ["unix:///var/run/docker.sock", "tcp://0.0.0.0:2375"]
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Or override the systemd service:
|
|
292
|
+
```bash
|
|
293
|
+
sudo systemctl edit docker.service
|
|
294
|
+
```
|
|
295
|
+
```ini
|
|
296
|
+
[Service]
|
|
297
|
+
ExecStart=
|
|
298
|
+
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2375
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
⚠️ **Security Note**: Exposing Docker API without TLS is insecure. Use on trusted networks only, or set up TLS certificates.
|
|
302
|
+
|
|
303
|
+
## Usage
|
|
304
|
+
|
|
305
|
+
### With Claude Code
|
|
306
|
+
|
|
307
|
+
Add to `~/.claude/claude_code_config.json`:
|
|
308
|
+
|
|
309
|
+
```json
|
|
310
|
+
{
|
|
311
|
+
"mcpServers": {
|
|
312
|
+
"synapse": {
|
|
313
|
+
"command": "node",
|
|
314
|
+
"args": ["/absolute/path/to/synapse-mcp/dist/index.js"],
|
|
315
|
+
"env": {
|
|
316
|
+
"SYNAPSE_CONFIG_FILE": "/home/youruser/.config/synapse-mcp/config.json"
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Or if your config is in one of the default locations, you can skip the env entirely:
|
|
324
|
+
|
|
325
|
+
```json
|
|
326
|
+
{
|
|
327
|
+
"mcpServers": {
|
|
328
|
+
"synapse": {
|
|
329
|
+
"command": "node",
|
|
330
|
+
"args": ["/absolute/path/to/synapse-mcp/dist/index.js"]
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Then in Claude Code:
|
|
337
|
+
```
|
|
338
|
+
> List all running containers on tootie (uses flux tool)
|
|
339
|
+
> Restart the plex container (uses flux tool)
|
|
340
|
+
> Show me the logs from sonarr with errors in the last hour (uses flux tool)
|
|
341
|
+
> Which containers are using the most memory? (uses flux tool)
|
|
342
|
+
> Read the nginx config on tootie (uses scout tool)
|
|
343
|
+
> Check ZFS pool health on dookie (uses scout tool)
|
|
344
|
+
> Show me systemd journal errors from the last hour (uses scout tool)
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### HTTP Mode
|
|
348
|
+
|
|
349
|
+
For remote access or multi-client scenarios:
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
# Start HTTP server
|
|
353
|
+
node dist/index.js --http
|
|
354
|
+
|
|
355
|
+
# Server runs on http://127.0.0.1:3000/mcp
|
|
356
|
+
# Health check: http://127.0.0.1:3000/health
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
Environment variables for HTTP mode:
|
|
360
|
+
- `PORT`: Server port (default: 3000)
|
|
361
|
+
- `HOST`: Bind address (default: 127.0.0.1)
|
|
362
|
+
|
|
363
|
+
### CLI Help
|
|
364
|
+
|
|
365
|
+
```bash
|
|
366
|
+
node dist/index.js --help
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
## Example Interactions
|
|
370
|
+
|
|
371
|
+
### Flux Tool - Container Management
|
|
372
|
+
```
|
|
373
|
+
User: What containers are running on tootie?
|
|
374
|
+
|
|
375
|
+
Claude: [calls flux with action="container", subaction="list", host="tootie", state="running"]
|
|
376
|
+
|
|
377
|
+
I found 23 running containers on tootie:
|
|
378
|
+
|
|
379
|
+
🟢 plex (tootie) - Image: linuxserver/plex | Up 3 days
|
|
380
|
+
🟢 sonarr (tootie) - Image: linuxserver/sonarr | Up 3 days
|
|
381
|
+
🟢 radarr (tootie) - Image: linuxserver/radarr | Up 3 days
|
|
382
|
+
...
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### Flux Tool - Log Analysis
|
|
386
|
+
```
|
|
387
|
+
User: Show me any errors from nginx in the last hour
|
|
388
|
+
|
|
389
|
+
Claude: [calls flux with action="container", subaction="logs",
|
|
390
|
+
container_id="nginx", since="1h", grep="error"]
|
|
391
|
+
|
|
392
|
+
Found 3 error entries in nginx logs:
|
|
393
|
+
[14:23:15] 2024/12/15 14:23:15 [error] connect() failed...
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
### Scout Tool - Remote File Access
|
|
397
|
+
```
|
|
398
|
+
User: Read the nginx config on tootie
|
|
399
|
+
|
|
400
|
+
Claude: [calls scout with action="peek", target="tootie:/etc/nginx/nginx.conf"]
|
|
401
|
+
|
|
402
|
+
Here's the nginx configuration from tootie:
|
|
403
|
+
|
|
404
|
+
user nginx;
|
|
405
|
+
worker_processes auto;
|
|
406
|
+
...
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### Scout Tool - ZFS Health Check
|
|
410
|
+
```
|
|
411
|
+
User: Check ZFS pool health on dookie
|
|
412
|
+
|
|
413
|
+
Claude: [calls scout with action="zfs", subaction="pools", host="dookie"]
|
|
414
|
+
|
|
415
|
+
ZFS Pools on dookie:
|
|
416
|
+
|
|
417
|
+
tank - ONLINE | Size: 24TB | Free: 8.2TB | Health: 100%
|
|
418
|
+
backup - ONLINE | Size: 12TB | Free: 5.1TB | Health: 100%
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### Scout Tool - System Logs
|
|
422
|
+
```
|
|
423
|
+
User: Show me Docker service errors from systemd journal
|
|
424
|
+
|
|
425
|
+
Claude: [calls scout with action="logs", subaction="journal",
|
|
426
|
+
host="tootie", unit="docker.service", priority="err"]
|
|
427
|
+
|
|
428
|
+
Recent errors from docker.service:
|
|
429
|
+
|
|
430
|
+
[15:42:10] Failed to allocate directory watch: Too many open files
|
|
431
|
+
[15:42:15] containerd: connection error: desc = "transport: error while dialing"
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
## Security
|
|
435
|
+
|
|
436
|
+
### Path Traversal Protection (CWE-22)
|
|
437
|
+
|
|
438
|
+
The `image_build` tool implements strict path validation to prevent directory traversal attacks:
|
|
439
|
+
|
|
440
|
+
- **Absolute paths required**: All paths (context, dockerfile) must start with `/`
|
|
441
|
+
- **Traversal blocked**: Paths containing `..` or `.` components are rejected
|
|
442
|
+
- **Character validation**: Only alphanumeric, dots (in filenames), hyphens, underscores, and forward slashes allowed
|
|
443
|
+
- **Pre-execution validation**: Paths validated before SSH commands are executed
|
|
444
|
+
|
|
445
|
+
Example of rejected paths:
|
|
446
|
+
```bash
|
|
447
|
+
# Rejected: Directory traversal
|
|
448
|
+
../../../etc/passwd
|
|
449
|
+
/app/../../../etc/passwd
|
|
450
|
+
|
|
451
|
+
# Rejected: Relative paths
|
|
452
|
+
./build
|
|
453
|
+
relative/path
|
|
454
|
+
|
|
455
|
+
# Accepted: Absolute paths without traversal
|
|
456
|
+
/home/user/docker/build
|
|
457
|
+
/opt/myapp/Dockerfile.prod
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### General Security Notes
|
|
461
|
+
|
|
462
|
+
- Docker API on port 2375 is insecure without TLS
|
|
463
|
+
- Always use execFile for shell commands (prevents injection)
|
|
464
|
+
- Validate host config fields with regex
|
|
465
|
+
- Require force=true for destructive operations
|
|
466
|
+
|
|
467
|
+
## Development
|
|
468
|
+
|
|
469
|
+
```bash
|
|
470
|
+
# Watch mode for development
|
|
471
|
+
pnpm run dev
|
|
472
|
+
|
|
473
|
+
# Build
|
|
474
|
+
pnpm run build
|
|
475
|
+
|
|
476
|
+
# Run tests
|
|
477
|
+
pnpm test
|
|
478
|
+
|
|
479
|
+
# Run tests with coverage
|
|
480
|
+
pnpm run test:coverage
|
|
481
|
+
|
|
482
|
+
# Test with MCP Inspector
|
|
483
|
+
npx @modelcontextprotocol/inspector node dist/index.js
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
## Architecture
|
|
487
|
+
|
|
488
|
+
```
|
|
489
|
+
synapse-mcp/
|
|
490
|
+
├── src/
|
|
491
|
+
│ ├── index.ts # Entry point, transport setup
|
|
492
|
+
│ ├── types.ts # TypeScript interfaces
|
|
493
|
+
│ ├── constants.ts # Configuration constants
|
|
494
|
+
│ ├── config/
|
|
495
|
+
│ │ └── command-allowlist.json # Allowed commands for scout:exec
|
|
496
|
+
│ ├── formatters/
|
|
497
|
+
│ │ ├── index.ts # Response formatting utilities
|
|
498
|
+
│ │ └── formatters.test.ts # Formatter tests
|
|
499
|
+
│ ├── tools/
|
|
500
|
+
│ │ ├── index.ts # Tool registration router
|
|
501
|
+
│ │ ├── flux.ts # Flux tool handler + routing
|
|
502
|
+
│ │ ├── scout.ts # Scout tool handler + routing
|
|
503
|
+
│ │ ├── container.ts # handleContainerAction()
|
|
504
|
+
│ │ ├── compose.ts # handleComposeAction()
|
|
505
|
+
│ │ ├── docker.ts # handleDockerAction()
|
|
506
|
+
│ │ └── host.ts # handleHostAction()
|
|
507
|
+
│ ├── services/
|
|
508
|
+
│ │ ├── docker.ts # DockerService
|
|
509
|
+
│ │ ├── compose.ts # ComposeService
|
|
510
|
+
│ │ ├── ssh.ts # SSHService
|
|
511
|
+
│ │ └── scout/ # Scout-specific services
|
|
512
|
+
│ │ ├── pool.ts # SSH connection pool
|
|
513
|
+
│ │ ├── executors.ts # Command execution
|
|
514
|
+
│ │ └── transfer.ts # File transfer (beam)
|
|
515
|
+
│ ├── schemas/
|
|
516
|
+
│ │ ├── index.ts # FluxSchema + ScoutSchema exports
|
|
517
|
+
│ │ ├── common.ts # Shared schemas (pagination, response_format)
|
|
518
|
+
│ │ ├── container.ts # Container subaction schemas
|
|
519
|
+
│ │ ├── compose.ts # Compose subaction schemas
|
|
520
|
+
│ │ ├── docker.ts # Docker subaction schemas
|
|
521
|
+
│ │ ├── host.ts # Host subaction schemas
|
|
522
|
+
│ │ └── scout.ts # Scout action schemas
|
|
523
|
+
│ └── lint.test.ts # Linting tests
|
|
524
|
+
├── dist/ # Compiled JavaScript
|
|
525
|
+
├── package.json
|
|
526
|
+
├── tsconfig.json
|
|
527
|
+
└── README.md
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
### Key Architectural Decisions
|
|
531
|
+
|
|
532
|
+
**V3 Schema Refactor - Two Tools Pattern**:
|
|
533
|
+
- **Flux**: 4 actions (container, compose, docker, host) with 39 total subactions
|
|
534
|
+
- **Scout**: 11 actions (9 simple + 2 with subactions) for 16 total operations
|
|
535
|
+
- Clean separation: Flux = Docker/state changes, Scout = SSH/read operations
|
|
536
|
+
- Total: 55 discriminator keys across both tools
|
|
537
|
+
|
|
538
|
+
**Discriminated Union for O(1) Validation**:
|
|
539
|
+
- **Flux**: Composite `action_subaction` discriminator (`container:list`, `compose:up`, etc.)
|
|
540
|
+
- **Scout**: Primary `action` discriminator with nested discriminators for `zfs` and `logs`
|
|
541
|
+
- Validation latency: <0.005ms average across all operations
|
|
542
|
+
- Zero performance degradation regardless of which operation is called
|
|
543
|
+
|
|
544
|
+
**Help System**:
|
|
545
|
+
- Auto-generated help handlers for both tools
|
|
546
|
+
- Introspects Zod schemas using `.describe()` metadata
|
|
547
|
+
- Supports topic-specific help (e.g., `flux help container:logs`)
|
|
548
|
+
- Available in markdown or JSON format
|
|
549
|
+
|
|
550
|
+
**SSH Connection Pooling**:
|
|
551
|
+
- 50× faster for repeated operations
|
|
552
|
+
- Automatic idle timeout and health checks
|
|
553
|
+
- Configurable pool size and connection reuse
|
|
554
|
+
- Transparent integration (no code changes required)
|
|
555
|
+
|
|
556
|
+
**Test Coverage**:
|
|
557
|
+
- Unit tests for all services, schemas, and tools
|
|
558
|
+
- Integration tests for end-to-end workflows
|
|
559
|
+
- Performance benchmarks for schema validation
|
|
560
|
+
- TDD approach for all new features
|
|
561
|
+
|
|
562
|
+
## Performance
|
|
563
|
+
|
|
564
|
+
### Schema Validation
|
|
565
|
+
|
|
566
|
+
Both Flux and Scout tools use Zod discriminated union for O(1) constant-time schema validation:
|
|
567
|
+
|
|
568
|
+
- **Validation latency**: <0.005ms average across all 55 operations
|
|
569
|
+
- **Flux optimization**: Composite `action_subaction` discriminator with preprocessor
|
|
570
|
+
- **Scout optimization**: Primary `action` discriminator with nested discriminators for zfs/logs
|
|
571
|
+
- **Consistency**: All operations perform identically fast (no worst-case scenarios)
|
|
572
|
+
|
|
573
|
+
Flux inputs are automatically preprocessed to inject the `action_subaction` discriminator key.
|
|
574
|
+
|
|
575
|
+
### SSH Connection Pooling
|
|
576
|
+
|
|
577
|
+
All SSH operations use connection pooling for optimal performance:
|
|
578
|
+
|
|
579
|
+
- **50× faster** for repeated operations
|
|
580
|
+
- Connections reused across compose operations
|
|
581
|
+
- Automatic idle timeout and health checks
|
|
582
|
+
- Configurable via environment variables
|
|
583
|
+
|
|
584
|
+
See [docs/ssh-connection-pooling.md](docs/ssh-connection-pooling.md) for details.
|
|
585
|
+
|
|
586
|
+
**Key Benefits:**
|
|
587
|
+
- Eliminate 250ms connection overhead per operation
|
|
588
|
+
- Support high-concurrency scenarios (configurable pool size)
|
|
589
|
+
- Automatic connection cleanup and health monitoring
|
|
590
|
+
- Zero code changes required (transparent integration)
|
|
591
|
+
|
|
592
|
+
### Benchmarks
|
|
593
|
+
|
|
594
|
+
Run performance benchmarks:
|
|
595
|
+
|
|
596
|
+
```bash
|
|
597
|
+
npm run test:bench
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
Expected results:
|
|
601
|
+
- Worst-case validation: <0.005ms (0.003ms typical)
|
|
602
|
+
- Average-case validation: <0.005ms (0.003ms typical)
|
|
603
|
+
- Performance variance: <0.001ms (proves O(1) consistency)
|
|
604
|
+
|
|
605
|
+
## License
|
|
606
|
+
|
|
607
|
+
MIT
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export declare const CHARACTER_LIMIT = 40000;
|
|
2
|
+
export declare const DEFAULT_LIMIT = 20;
|
|
3
|
+
export declare const MAX_LIMIT = 100;
|
|
4
|
+
export declare const DEFAULT_LOG_LINES = 50;
|
|
5
|
+
export declare const MAX_LOG_LINES = 500;
|
|
6
|
+
export declare const API_TIMEOUT = 30000;
|
|
7
|
+
export declare const STATS_TIMEOUT = 5000;
|
|
8
|
+
export declare const DEFAULT_DOCKER_SOCKET = "/var/run/docker.sock";
|
|
9
|
+
export declare const ENV_HOSTS_CONFIG = "HOMELAB_HOSTS_CONFIG";
|
|
10
|
+
export declare const ENV_DEFAULT_HOST = "HOMELAB_DEFAULT_HOST";
|
|
11
|
+
export declare const DEFAULT_MAX_FILE_SIZE = 1048576;
|
|
12
|
+
export declare const MAX_FILE_SIZE_LIMIT = 10485760;
|
|
13
|
+
export declare const DEFAULT_COMMAND_TIMEOUT = 30000;
|
|
14
|
+
export declare const MAX_COMMAND_TIMEOUT = 300000;
|
|
15
|
+
export declare const DEFAULT_TREE_DEPTH = 3;
|
|
16
|
+
export declare const MAX_TREE_DEPTH = 10;
|
|
17
|
+
export declare const DEFAULT_FIND_LIMIT = 100;
|
|
18
|
+
export declare const MAX_FIND_LIMIT = 1000;
|
|
19
|
+
export declare const DEFAULT_DIFF_CONTEXT_LINES = 3;
|
|
20
|
+
export declare const MAX_DIFF_CONTEXT_LINES = 50;
|
|
21
|
+
export declare const ALLOWED_COMMANDS: Set<string>;
|
|
22
|
+
export declare const ENV_ALLOW_ANY_COMMAND = "HOMELAB_ALLOW_ANY_COMMAND";
|
|
23
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,eAAe,QAAQ,CAAC;AAGrC,eAAO,MAAM,aAAa,KAAK,CAAC;AAChC,eAAO,MAAM,SAAS,MAAM,CAAC;AAG7B,eAAO,MAAM,iBAAiB,KAAK,CAAC;AACpC,eAAO,MAAM,aAAa,MAAM,CAAC;AAGjC,eAAO,MAAM,WAAW,QAAQ,CAAC;AACjC,eAAO,MAAM,aAAa,OAAO,CAAC;AAGlC,eAAO,MAAM,qBAAqB,yBAAyB,CAAC;AAG5D,eAAO,MAAM,gBAAgB,yBAAyB,CAAC;AACvD,eAAO,MAAM,gBAAgB,yBAAyB,CAAC;AAKvD,eAAO,MAAM,qBAAqB,UAAU,CAAC;AAC7C,eAAO,MAAM,mBAAmB,WAAW,CAAC;AAG5C,eAAO,MAAM,uBAAuB,QAAQ,CAAC;AAC7C,eAAO,MAAM,mBAAmB,SAAS,CAAC;AAG1C,eAAO,MAAM,kBAAkB,IAAI,CAAC;AACpC,eAAO,MAAM,cAAc,KAAK,CAAC;AAGjC,eAAO,MAAM,kBAAkB,MAAM,CAAC;AACtC,eAAO,MAAM,cAAc,OAAO,CAAC;AAGnC,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAC5C,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAGzC,eAAO,MAAM,gBAAgB,aAqB3B,CAAC;AAGH,eAAO,MAAM,qBAAqB,8BAA8B,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Character limit for responses to prevent context overflow (~12.5k tokens)
|
|
2
|
+
export const CHARACTER_LIMIT = 40000;
|
|
3
|
+
// Default pagination settings
|
|
4
|
+
export const DEFAULT_LIMIT = 20;
|
|
5
|
+
export const MAX_LIMIT = 100;
|
|
6
|
+
// Log retrieval defaults
|
|
7
|
+
export const DEFAULT_LOG_LINES = 50;
|
|
8
|
+
export const MAX_LOG_LINES = 500;
|
|
9
|
+
// Timeout settings (ms)
|
|
10
|
+
export const API_TIMEOUT = 30000;
|
|
11
|
+
export const STATS_TIMEOUT = 5000;
|
|
12
|
+
// Default Docker socket path
|
|
13
|
+
export const DEFAULT_DOCKER_SOCKET = "/var/run/docker.sock";
|
|
14
|
+
// Environment variable names for config
|
|
15
|
+
export const ENV_HOSTS_CONFIG = "HOMELAB_HOSTS_CONFIG";
|
|
16
|
+
export const ENV_DEFAULT_HOST = "HOMELAB_DEFAULT_HOST";
|
|
17
|
+
// ===== Scout File Operations Constants =====
|
|
18
|
+
// File size limits (bytes)
|
|
19
|
+
export const DEFAULT_MAX_FILE_SIZE = 1048576; // 1MB
|
|
20
|
+
export const MAX_FILE_SIZE_LIMIT = 10485760; // 10MB
|
|
21
|
+
// Command timeout limits (milliseconds)
|
|
22
|
+
export const DEFAULT_COMMAND_TIMEOUT = 30000; // 30s
|
|
23
|
+
export const MAX_COMMAND_TIMEOUT = 300000; // 300s (5 min)
|
|
24
|
+
// Tree depth limits
|
|
25
|
+
export const DEFAULT_TREE_DEPTH = 3;
|
|
26
|
+
export const MAX_TREE_DEPTH = 10;
|
|
27
|
+
// Find result limits
|
|
28
|
+
export const DEFAULT_FIND_LIMIT = 100;
|
|
29
|
+
export const MAX_FIND_LIMIT = 1000;
|
|
30
|
+
// Diff context lines limits
|
|
31
|
+
export const DEFAULT_DIFF_CONTEXT_LINES = 3;
|
|
32
|
+
export const MAX_DIFF_CONTEXT_LINES = 50;
|
|
33
|
+
// Allowed commands for exec subaction (read-only operations)
|
|
34
|
+
export const ALLOWED_COMMANDS = new Set([
|
|
35
|
+
"cat",
|
|
36
|
+
"head",
|
|
37
|
+
"tail",
|
|
38
|
+
"grep",
|
|
39
|
+
"rg",
|
|
40
|
+
"find",
|
|
41
|
+
"ls",
|
|
42
|
+
"tree",
|
|
43
|
+
"wc",
|
|
44
|
+
"sort",
|
|
45
|
+
"uniq",
|
|
46
|
+
"diff",
|
|
47
|
+
"stat",
|
|
48
|
+
"file",
|
|
49
|
+
"du",
|
|
50
|
+
"df",
|
|
51
|
+
"pwd",
|
|
52
|
+
"hostname",
|
|
53
|
+
"uptime",
|
|
54
|
+
"whoami"
|
|
55
|
+
]);
|
|
56
|
+
// Environment variable to disable command allowlist
|
|
57
|
+
export const ENV_ALLOW_ANY_COMMAND = "HOMELAB_ALLOW_ANY_COMMAND";
|
|
58
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,CAAC;AAErC,8BAA8B;AAC9B,MAAM,CAAC,MAAM,aAAa,GAAG,EAAE,CAAC;AAChC,MAAM,CAAC,MAAM,SAAS,GAAG,GAAG,CAAC;AAE7B,yBAAyB;AACzB,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAC;AACpC,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,CAAC;AAEjC,wBAAwB;AACxB,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,CAAC;AACjC,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,CAAC;AAElC,6BAA6B;AAC7B,MAAM,CAAC,MAAM,qBAAqB,GAAG,sBAAsB,CAAC;AAE5D,wCAAwC;AACxC,MAAM,CAAC,MAAM,gBAAgB,GAAG,sBAAsB,CAAC;AACvD,MAAM,CAAC,MAAM,gBAAgB,GAAG,sBAAsB,CAAC;AAEvD,8CAA8C;AAE9C,2BAA2B;AAC3B,MAAM,CAAC,MAAM,qBAAqB,GAAG,OAAO,CAAC,CAAC,MAAM;AACpD,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAQ,CAAC,CAAC,OAAO;AAEpD,wCAAwC;AACxC,MAAM,CAAC,MAAM,uBAAuB,GAAG,KAAK,CAAC,CAAC,MAAM;AACpD,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC,CAAC,eAAe;AAE1D,oBAAoB;AACpB,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC;AACpC,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAC;AAEjC,qBAAqB;AACrB,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,CAAC;AACtC,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC;AAEnC,4BAA4B;AAC5B,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC;AAC5C,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAEzC,6DAA6D;AAC7D,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC;IACtC,KAAK;IACL,MAAM;IACN,MAAM;IACN,MAAM;IACN,IAAI;IACJ,MAAM;IACN,IAAI;IACJ,MAAM;IACN,IAAI;IACJ,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,IAAI;IACJ,IAAI;IACJ,KAAK;IACL,UAAU;IACV,QAAQ;IACR,QAAQ;CACT,CAAC,CAAC;AAEH,oDAAoD;AACpD,MAAM,CAAC,MAAM,qBAAqB,GAAG,2BAA2B,CAAC"}
|