mikromcp 1.0.2 → 1.0.4
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 +136 -132
- package/dist/main.js +77 -46
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
[](https://github.com/AliKarami/MikroMCP/actions/workflows/ci.yml)
|
|
14
14
|
[](https://github.com/AliKarami/MikroMCP/actions/workflows/release.yml)
|
|
15
|
-
[](package.json)
|
|
16
16
|
[](LICENSE)
|
|
17
17
|
[](package.json)
|
|
18
18
|
[](https://help.mikrotik.com/docs/display/ROS/REST+API)
|
|
@@ -37,8 +37,6 @@ MikroMCP exists because raw router CLI access is the wrong abstraction for AI ag
|
|
|
37
37
|
| Raw network access for every assistant | RBAC identities, bearer tokens for HTTP mode, tool allowlists, and audit trails |
|
|
38
38
|
| Fragile troubleshooting workflows | Router-originated ping, traceroute, torch, logs, interfaces, DHCP, firewall, routes, WiFi, WireGuard, and more |
|
|
39
39
|
|
|
40
|
-
MikroMCP is especially useful when you want AI agents to help with network operations without giving them unchecked terminal access.
|
|
41
|
-
|
|
42
40
|
---
|
|
43
41
|
|
|
44
42
|
## Feature Showcase
|
|
@@ -56,8 +54,6 @@ MikroMCP is especially useful when you want AI agents to help with network opera
|
|
|
56
54
|
| 🤖 **AI-agent fit** | Human-readable responses plus structured JSON content for reasoning, chaining, and automation |
|
|
57
55
|
| 🧩 **MCP compatibility** | stdio for desktop clients, Streamable HTTP and legacy SSE for remote or service-style clients |
|
|
58
56
|
|
|
59
|
-
**Best-in-class strengths:** MikroMCP is not just a thin REST wrapper. It models operational safety around RouterOS: typed tool contracts, safe write patterns, router-aware credentials, destructive-action gates, and rollback-oriented workflows.
|
|
60
|
-
|
|
61
57
|
---
|
|
62
58
|
|
|
63
59
|
## Demo
|
|
@@ -78,10 +74,9 @@ MikroMCP is especially useful when you want AI agents to help with network opera
|
|
|
78
74
|
|
|
79
75
|
### Prerequisites
|
|
80
76
|
|
|
81
|
-
- Node.js 22 or newer
|
|
77
|
+
- Node.js 22 or newer (for npm install) — or use a standalone binary below
|
|
82
78
|
- MikroTik RouterOS 7.x with the REST API enabled
|
|
83
|
-
- A RouterOS user with the policies
|
|
84
|
-
- Git and npm
|
|
79
|
+
- A RouterOS user with the policies your tools require
|
|
85
80
|
|
|
86
81
|
Recommended RouterOS policies for full tool coverage:
|
|
87
82
|
|
|
@@ -89,29 +84,65 @@ Recommended RouterOS policies for full tool coverage:
|
|
|
89
84
|
read, write, api, rest-api, test, ssh, sniff, ftp
|
|
90
85
|
```
|
|
91
86
|
|
|
92
|
-
|
|
87
|
+
> `ssh` is required for `ping`, `traceroute`, `torch`, and `run_command`. `sniff` is required by `torch`. `ftp` is required only for `upload_file`.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
### Install
|
|
92
|
+
|
|
93
|
+
**npm (recommended)**
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
npm install -g mikromcp
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Standalone binaries**
|
|
93
100
|
|
|
94
|
-
|
|
95
|
-
- `sniff` is required by `torch`.
|
|
96
|
-
- `ftp` is required only for `upload_file`.
|
|
101
|
+
Download the binary for your platform from the [latest GitHub release](https://github.com/AliKarami/MikroMCP/releases/latest) — no Node.js required.
|
|
97
102
|
|
|
98
|
-
|
|
103
|
+
**Docker**
|
|
99
104
|
|
|
100
105
|
```bash
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
docker pull ghcr.io/alikarami/mikromcp:latest
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
### Set Up With the Init Wizard
|
|
112
|
+
|
|
113
|
+
Run the interactive setup wizard:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
mikromcp init
|
|
107
117
|
```
|
|
108
118
|
|
|
109
|
-
|
|
119
|
+
The wizard will ask for your router details and write everything to `~/.mikromcp/`:
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
~/.mikromcp/
|
|
123
|
+
├── routers.yaml # router registry
|
|
124
|
+
├── identities.yaml # RBAC identities (HTTP mode)
|
|
125
|
+
└── .env # credentials and runtime settings
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
`~/.mikromcp/.env` is loaded automatically every time MikroMCP starts — no shell exports or Claude Desktop `env` blocks needed. Fill in the credentials it generates:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# ~/.mikromcp/.env (generated by mikromcp init)
|
|
132
|
+
ROUTER_CORE01_USER=
|
|
133
|
+
ROUTER_CORE01_PASS=
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
To edit your router registry directly:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
nano ~/.mikromcp/routers.yaml
|
|
140
|
+
```
|
|
110
141
|
|
|
111
142
|
```yaml
|
|
112
143
|
routers:
|
|
113
144
|
core-01:
|
|
114
|
-
host: "
|
|
145
|
+
host: "192.168.88.1"
|
|
115
146
|
port: 443
|
|
116
147
|
tls:
|
|
117
148
|
enabled: true
|
|
@@ -119,59 +150,34 @@ routers:
|
|
|
119
150
|
credentials:
|
|
120
151
|
source: "env"
|
|
121
152
|
envPrefix: "ROUTER_CORE01"
|
|
122
|
-
tags: ["core"
|
|
123
|
-
rosVersion: "7
|
|
153
|
+
tags: ["core"]
|
|
154
|
+
rosVersion: "7"
|
|
124
155
|
```
|
|
125
156
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
```bash
|
|
129
|
-
export ROUTER_CORE01_USER="mcp-api"
|
|
130
|
-
export ROUTER_CORE01_PASS="your-router-password"
|
|
131
|
-
npm start
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
### Docker
|
|
157
|
+
---
|
|
135
158
|
|
|
136
|
-
|
|
159
|
+
### Verify With Doctor
|
|
137
160
|
|
|
138
161
|
```bash
|
|
139
|
-
|
|
162
|
+
mikromcp doctor
|
|
140
163
|
```
|
|
141
164
|
|
|
142
|
-
|
|
165
|
+
Doctor checks Node version, config files, router reachability, Claude Desktop registration, and whether a newer version is available.
|
|
143
166
|
|
|
144
|
-
|
|
145
|
-
docker run --rm \
|
|
146
|
-
-e MIKROMCP_TRANSPORT=http \
|
|
147
|
-
-e MIKROMCP_PORT=3000 \
|
|
148
|
-
-e MIKROMCP_CONFIRMATION_SECRET="$(openssl rand -hex 32)" \
|
|
149
|
-
-e ROUTER_CORE01_USER=mcp-api \
|
|
150
|
-
-e ROUTER_CORE01_PASS=your-router-password \
|
|
151
|
-
-v "$(pwd)/config:/app/config:ro" \
|
|
152
|
-
-p 3000:3000 \
|
|
153
|
-
ghcr.io/alikarami/mikromcp:latest
|
|
154
|
-
```
|
|
167
|
+
---
|
|
155
168
|
|
|
156
|
-
|
|
169
|
+
### Run
|
|
157
170
|
|
|
158
|
-
|
|
171
|
+
**stdio (for Claude Desktop and other desktop MCP clients)**
|
|
159
172
|
|
|
160
173
|
```bash
|
|
161
|
-
|
|
162
|
-
cp config/routers.example.yaml config/routers.yaml
|
|
163
|
-
cp config/identities.example.yaml config/identities.yaml
|
|
164
|
-
# edit config/routers.yaml and set MIKROMCP_CONFIRMATION_SECRET + router credentials in .env
|
|
165
|
-
docker compose up -d
|
|
174
|
+
mikromcp serve
|
|
166
175
|
```
|
|
167
176
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
#### Build from source
|
|
177
|
+
**HTTP mode (for service deployments)**
|
|
171
178
|
|
|
172
179
|
```bash
|
|
173
|
-
|
|
174
|
-
docker run --rm -e MIKROMCP_TRANSPORT=http -p 3000:3000 mikromcp
|
|
180
|
+
MIKROMCP_TRANSPORT=http mikromcp serve
|
|
175
181
|
```
|
|
176
182
|
|
|
177
183
|
---
|
|
@@ -180,25 +186,22 @@ docker run --rm -e MIKROMCP_TRANSPORT=http -p 3000:3000 mikromcp
|
|
|
180
186
|
|
|
181
187
|
### Claude Desktop
|
|
182
188
|
|
|
183
|
-
|
|
189
|
+
Run `mikromcp init` and choose **Register with Claude Desktop** — it patches `claude_desktop_config.json` automatically.
|
|
190
|
+
|
|
191
|
+
Or add it manually to `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS:
|
|
184
192
|
|
|
185
193
|
```json
|
|
186
194
|
{
|
|
187
195
|
"mcpServers": {
|
|
188
|
-
"
|
|
189
|
-
"command": "
|
|
190
|
-
"args": ["
|
|
191
|
-
"env": {
|
|
192
|
-
"MIKROMCP_CONFIG_PATH": "/absolute/path/to/MikroMCP/config/routers.yaml",
|
|
193
|
-
"ROUTER_CORE01_USER": "mcp-api",
|
|
194
|
-
"ROUTER_CORE01_PASS": "your-router-password"
|
|
195
|
-
}
|
|
196
|
+
"mikromcp": {
|
|
197
|
+
"command": "mikromcp",
|
|
198
|
+
"args": ["serve"]
|
|
196
199
|
}
|
|
197
200
|
}
|
|
198
201
|
}
|
|
199
202
|
```
|
|
200
203
|
|
|
201
|
-
Restart Claude Desktop, then ask:
|
|
204
|
+
No `env` block needed — credentials are loaded from `~/.mikromcp/.env` at startup. Restart Claude Desktop, then ask:
|
|
202
205
|
|
|
203
206
|
```text
|
|
204
207
|
Use MikroMCP to show CPU, memory, uptime, active interfaces, and warning logs for core-01.
|
|
@@ -206,48 +209,72 @@ Use MikroMCP to show CPU, memory, uptime, active interfaces, and warning logs fo
|
|
|
206
209
|
|
|
207
210
|
### HTTP / SSE Mode
|
|
208
211
|
|
|
209
|
-
HTTP mode is useful for service deployments and MCP clients that connect over a
|
|
212
|
+
HTTP mode is useful for service deployments and MCP clients that connect over a network endpoint.
|
|
213
|
+
|
|
214
|
+
Set in `~/.mikromcp/.env`:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
MIKROMCP_TRANSPORT=http
|
|
218
|
+
MIKROMCP_PORT=3000
|
|
219
|
+
MIKROMCP_CONFIRMATION_SECRET=<openssl rand -hex 32>
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Then run:
|
|
210
223
|
|
|
211
224
|
```bash
|
|
212
|
-
|
|
213
|
-
export MIKROMCP_PORT=3000
|
|
214
|
-
export MIKROMCP_BIND_HOST=127.0.0.1
|
|
215
|
-
export MIKROMCP_CONFIRMATION_SECRET="$(openssl rand -hex 32)"
|
|
216
|
-
export ROUTER_CORE01_USER="mcp-api"
|
|
217
|
-
export ROUTER_CORE01_PASS="your-router-password"
|
|
218
|
-
npm start
|
|
225
|
+
mikromcp serve
|
|
219
226
|
```
|
|
220
227
|
|
|
221
|
-
Every HTTP
|
|
228
|
+
Every HTTP request must include:
|
|
222
229
|
|
|
223
230
|
```text
|
|
224
231
|
Authorization: Bearer <token>
|
|
225
232
|
```
|
|
226
233
|
|
|
227
|
-
Tokens are configured as bcrypt hashes in
|
|
234
|
+
Tokens are configured as bcrypt hashes in `~/.mikromcp/identities.yaml`. Use `mikromcp init` to generate them.
|
|
235
|
+
|
|
236
|
+
### Docker
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
docker run --rm \
|
|
240
|
+
-e MIKROMCP_TRANSPORT=http \
|
|
241
|
+
-e MIKROMCP_PORT=3000 \
|
|
242
|
+
-e MIKROMCP_CONFIRMATION_SECRET="$(openssl rand -hex 32)" \
|
|
243
|
+
-e ROUTER_CORE01_USER=mcp-api \
|
|
244
|
+
-e ROUTER_CORE01_PASS=your-router-password \
|
|
245
|
+
-e MIKROMCP_CONFIG_PATH=/config/routers.yaml \
|
|
246
|
+
-v "$HOME/.mikromcp:/config:ro" \
|
|
247
|
+
-p 3000:3000 \
|
|
248
|
+
ghcr.io/alikarami/mikromcp:latest
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Pass `MIKROMCP_CONFIG_PATH` and `MIKROMCP_IDENTITIES_PATH` explicitly when running in Docker since `~/.mikromcp/` inside the container refers to the container's home directory.
|
|
228
252
|
|
|
229
253
|
---
|
|
230
254
|
|
|
231
255
|
## Configuration Reference
|
|
232
256
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
|
236
|
-
|
|
|
237
|
-
| `
|
|
238
|
-
| `
|
|
239
|
-
| `
|
|
240
|
-
| `
|
|
241
|
-
| `
|
|
242
|
-
| `
|
|
243
|
-
| `
|
|
244
|
-
| `
|
|
245
|
-
| `
|
|
246
|
-
| `
|
|
247
|
-
| `
|
|
248
|
-
| `
|
|
249
|
-
| `
|
|
250
|
-
| `
|
|
257
|
+
All settings can be placed in `~/.mikromcp/.env` or passed as environment variables. Values in `~/.mikromcp/.env` are loaded at startup; explicit environment variables always take precedence.
|
|
258
|
+
|
|
259
|
+
| Variable | Default | Purpose |
|
|
260
|
+
| --------------------------------- | -------------------------------- | ------------------------------------------------------ |
|
|
261
|
+
| `MIKROMCP_TRANSPORT` | `stdio` | `stdio` or `http` |
|
|
262
|
+
| `MIKROMCP_CONFIG_PATH` | `~/.mikromcp/routers.yaml` | Router registry path |
|
|
263
|
+
| `MIKROMCP_IDENTITIES_PATH` | `~/.mikromcp/identities.yaml` | Identity and bearer-token registry |
|
|
264
|
+
| `MIKROMCP_STDIO_IDENTITY` | built-in superadmin | Named identity for stdio mode |
|
|
265
|
+
| `MIKROMCP_PORT` | `3000` | HTTP transport port |
|
|
266
|
+
| `MIKROMCP_BIND_HOST` | `127.0.0.1` | HTTP bind address |
|
|
267
|
+
| `MIKROMCP_CONFIRMATION_SECRET` | unset | HMAC secret for destructive-action confirmation tokens |
|
|
268
|
+
| `MIKROMCP_AUDIT_LOG_PATH` | unset | Optional NDJSON audit log file path |
|
|
269
|
+
| `MIKROMCP_DATA_DIR` | `~/.mikromcp/data` | Snapshots and write-journal directory |
|
|
270
|
+
| `MIKROMCP_HTTP_MAX_BODY_BYTES` | `1048576` | HTTP request body cap |
|
|
271
|
+
| `MIKROMCP_HTTP_RATE_LIMIT_RPM` | `60` | Requests per minute per IP; `0` disables rate limiting |
|
|
272
|
+
| `MIKROMCP_SSH_COMMAND_TIMEOUT_MS` | `30000` | SSH command timeout |
|
|
273
|
+
| `MIKROMCP_SSH_MAX_OUTPUT_BYTES` | `524288` | SSH output cap |
|
|
274
|
+
| `MIKROMCP_CMD_ALLOW` | unset | Global allowlist patterns for `run_command` |
|
|
275
|
+
| `MIKROMCP_CMD_DENY` | unset | Global denylist patterns for `run_command` |
|
|
276
|
+
| `ROUTER_<PREFIX>_USER` | unset | Router username from `envPrefix` |
|
|
277
|
+
| `ROUTER_<PREFIX>_PASS` | unset | Router password from `envPrefix` |
|
|
251
278
|
|
|
252
279
|
---
|
|
253
280
|
|
|
@@ -353,14 +380,6 @@ to 1.1.1.1. If packet loss is present, use torch on the WAN interface for a
|
|
|
353
380
|
short traffic snapshot.
|
|
354
381
|
```
|
|
355
382
|
|
|
356
|
-
### Configuration Backup And Audit
|
|
357
|
-
|
|
358
|
-
```text
|
|
359
|
-
List files, scripts, scheduled jobs, firewall rules, DNS static records, and
|
|
360
|
-
routes on core-01. Produce an audit summary with change-risk notes and suggested
|
|
361
|
-
cleanup tasks.
|
|
362
|
-
```
|
|
363
|
-
|
|
364
383
|
### Plan / Apply / Rollback Workflow
|
|
365
384
|
|
|
366
385
|
```text
|
|
@@ -383,15 +402,13 @@ AI assistants can use MikroMCP to:
|
|
|
383
402
|
- Preview changes before mutation and explain exactly what would happen.
|
|
384
403
|
- Respect tool-level authorization, router scoping, maintenance windows, and confirmation gates.
|
|
385
404
|
|
|
386
|
-
This makes MikroMCP a practical bridge between MikroTik networks and the emerging AI infrastructure ecosystem: Claude MCP, LLM tooling, infrastructure automation, DevOps workflows, and network operations copilots.
|
|
387
|
-
|
|
388
405
|
---
|
|
389
406
|
|
|
390
407
|
## Documentation
|
|
391
408
|
|
|
392
409
|
| Resource | Use it for |
|
|
393
410
|
| ----------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
|
|
394
|
-
| [ROADMAP.md](ROADMAP.md) | Shipped milestones and planned
|
|
411
|
+
| [ROADMAP.md](ROADMAP.md) | Shipped milestones and planned work |
|
|
395
412
|
| [Architecture](https://github.com/AliKarami/MikroMCP/wiki/Architecture) | System layers and request pipeline |
|
|
396
413
|
| [Setup Guide](https://github.com/AliKarami/MikroMCP/wiki/Setup-Guide) | RouterOS REST setup and end-to-end onboarding |
|
|
397
414
|
| [Configuration](https://github.com/AliKarami/MikroMCP/wiki/Configuration) | Router registry, TLS, SSH, credentials, HTTP mode |
|
|
@@ -401,7 +418,6 @@ This makes MikroMCP a practical bridge between MikroTik networks and the emergin
|
|
|
401
418
|
| [Error Handling](https://github.com/AliKarami/MikroMCP/wiki/Error-Handling) | Typed errors, retry behavior, circuit breaker behavior |
|
|
402
419
|
| [Development](https://github.com/AliKarami/MikroMCP/wiki/Development) | Project structure, tests, MCP Inspector workflow |
|
|
403
420
|
| [Contributing](https://github.com/AliKarami/MikroMCP/wiki/Contributing) | Adding tools, coding conventions, PR checklist |
|
|
404
|
-
| [Roadmap](https://github.com/AliKarami/MikroMCP/wiki/Roadmap) | Wiki mirror of the milestone roadmap |
|
|
405
421
|
|
|
406
422
|
---
|
|
407
423
|
|
|
@@ -411,30 +427,23 @@ This makes MikroMCP a practical bridge between MikroTik networks and the emergin
|
|
|
411
427
|
npm run dev # tsx watch hot-reload
|
|
412
428
|
npm run build # build ESM output to dist/main.js
|
|
413
429
|
npm start # run built server
|
|
414
|
-
npm test #
|
|
415
|
-
npm run typecheck # TypeScript type checking
|
|
416
|
-
npm run lint # ESLint
|
|
430
|
+
npm test # vitest + tsc + eslint
|
|
417
431
|
npm run format # Prettier
|
|
418
432
|
```
|
|
419
433
|
|
|
420
|
-
Run this before committing:
|
|
421
|
-
|
|
422
|
-
```bash
|
|
423
|
-
npm test
|
|
424
|
-
npm run typecheck
|
|
425
|
-
```
|
|
426
|
-
|
|
427
434
|
Key project paths:
|
|
428
435
|
|
|
429
436
|
| Path | Purpose |
|
|
430
437
|
| -------------------------------- | ------------------------------------------------------------------------------------------- |
|
|
431
|
-
| `src/main.ts` | Loads
|
|
438
|
+
| `src/main.ts` | Loads `~/.mikromcp/.env` and starts stdio or HTTP transport |
|
|
432
439
|
| `src/mcp/tool-registry.ts` | Registers tools and applies auth, retry, circuit breaker, audit, snapshots, and credentials |
|
|
433
440
|
| `src/domain/tools/` | Tool definitions and handlers |
|
|
434
441
|
| `src/domain/snapshot/` | Snapshot, diff, and write-journal support |
|
|
435
442
|
| `src/adapter/rest-client.ts` | RouterOS REST API client |
|
|
436
443
|
| `src/adapter/ssh-client.ts` | SSH execution adapter for diagnostics and guarded commands |
|
|
437
444
|
| `src/config/router-registry.ts` | Router inventory loader |
|
|
445
|
+
| `src/cli/init.ts` | Interactive setup wizard (`mikromcp init`) |
|
|
446
|
+
| `src/cli/doctor.ts` | Health check command (`mikromcp doctor`) |
|
|
438
447
|
| `config/routers.example.yaml` | Example multi-router registry |
|
|
439
448
|
| `config/identities.example.yaml` | Example RBAC identity registry |
|
|
440
449
|
|
|
@@ -448,7 +457,7 @@ Key project paths:
|
|
|
448
457
|
| v0.7 | ✅ Shipped | Identity, bearer auth, RBAC, audit log, confirmation gate |
|
|
449
458
|
| v0.8 | ✅ Shipped | Snapshots, write journal, plan/apply, rollback, maintenance windows |
|
|
450
459
|
| v0.9 | ✅ Shipped | Fleet operations, IPSec, certificates, users, DHCP servers/pools, queues/QoS, VRRP, SNMP/NTP, Netwatch, discovery, ARP, health checks |
|
|
451
|
-
| v1.0 |
|
|
460
|
+
| v1.0 | ✅ Shipped | npm package, standalone binaries, Docker images, `mikromcp init` wizard, `mikromcp doctor`, `~/.mikromcp/` convention |
|
|
452
461
|
|
|
453
462
|
See [ROADMAP.md](ROADMAP.md) for the complete milestone plan.
|
|
454
463
|
|
|
@@ -468,12 +477,9 @@ Good first contributions:
|
|
|
468
477
|
|
|
469
478
|
Development standards:
|
|
470
479
|
|
|
471
|
-
- TypeScript strict mode
|
|
472
|
-
-
|
|
473
|
-
-
|
|
474
|
-
- Idempotency and `dryRun` for write tools
|
|
475
|
-
- `MikroMCPError` for domain errors
|
|
476
|
-
- Focused Vitest coverage for every tool
|
|
480
|
+
- TypeScript strict mode, ESM imports with `.js` extensions
|
|
481
|
+
- Zod schemas with `.strict()`, idempotency and `dryRun` for write tools
|
|
482
|
+
- `MikroMCPError` for domain errors, focused Vitest coverage for every tool
|
|
477
483
|
|
|
478
484
|
Please open an issue before large changes so maintainers can align on scope.
|
|
479
485
|
|
|
@@ -481,18 +487,17 @@ Please open an issue before large changes so maintainers can align on scope.
|
|
|
481
487
|
|
|
482
488
|
## Security
|
|
483
489
|
|
|
484
|
-
MikroMCP
|
|
490
|
+
MikroMCP controls real network devices. Treat it like an operations system.
|
|
485
491
|
|
|
486
492
|
- Use least-privilege RouterOS users.
|
|
487
493
|
- Prefer TLS verification and certificate fingerprint pinning.
|
|
488
|
-
-
|
|
489
|
-
- Keep router credentials in environment variables or a secrets system, not YAML.
|
|
494
|
+
- Keep router credentials in `~/.mikromcp/.env`, not in YAML or shell history.
|
|
490
495
|
- Use HTTP mode behind a trusted network boundary.
|
|
491
496
|
- Configure identities with the smallest practical `allowedRouters` and `allowedToolPatterns`.
|
|
492
|
-
- Enable audit logging for shared or production use.
|
|
497
|
+
- Enable audit logging (`MIKROMCP_AUDIT_LOG_PATH`) for shared or production use.
|
|
493
498
|
- Test write tools with `dryRun: true` before applying changes.
|
|
494
499
|
|
|
495
|
-
For vulnerabilities or unsafe behavior, open a private security advisory
|
|
500
|
+
For vulnerabilities or unsafe behavior, open a private security advisory or contact the maintainer before publishing details.
|
|
496
501
|
|
|
497
502
|
---
|
|
498
503
|
|
|
@@ -501,7 +506,6 @@ For vulnerabilities or unsafe behavior, open a private security advisory if avai
|
|
|
501
506
|
- ⭐ Star the repository if MikroMCP helps your MikroTik or MCP workflow.
|
|
502
507
|
- 🍴 Fork it to add RouterOS surfaces your network depends on.
|
|
503
508
|
- 🧵 Open an issue for bugs, feature requests, compatibility notes, or documentation gaps.
|
|
504
|
-
- 🛣️ Follow the [roadmap](ROADMAP.md) for upcoming fleet, distribution, and production-readiness work.
|
|
505
509
|
|
|
506
510
|
---
|
|
507
511
|
|
package/dist/main.js
CHANGED
|
@@ -2,28 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
// src/main.ts
|
|
4
4
|
import { config as loadDotenv } from "dotenv";
|
|
5
|
-
import { join as
|
|
6
|
-
import { homedir as
|
|
5
|
+
import { join as join5 } from "path";
|
|
6
|
+
import { homedir as homedir5 } from "os";
|
|
7
7
|
|
|
8
8
|
// src/cli/index.ts
|
|
9
9
|
import { Command } from "commander";
|
|
10
10
|
|
|
11
11
|
// src/config/app-config.ts
|
|
12
|
+
import { join } from "path";
|
|
13
|
+
import { homedir } from "os";
|
|
14
|
+
function mikromcpDir() {
|
|
15
|
+
return join(homedir(), ".mikromcp");
|
|
16
|
+
}
|
|
12
17
|
function loadAppConfig() {
|
|
13
18
|
const env = process.env;
|
|
14
|
-
const dataDir = env.MIKROMCP_DATA_DIR ?? "data";
|
|
19
|
+
const dataDir = env.MIKROMCP_DATA_DIR ?? join(mikromcpDir(), "data");
|
|
15
20
|
return {
|
|
16
21
|
transport: env.MIKROMCP_TRANSPORT === "http" ? "http" : "stdio",
|
|
17
22
|
port: parseInt(env.MIKROMCP_PORT ?? "3000", 10),
|
|
18
23
|
bindHost: env.MIKROMCP_BIND_HOST ?? "127.0.0.1",
|
|
19
24
|
logLevel: env.MIKROMCP_LOG_LEVEL ?? "info",
|
|
20
|
-
configPath: env.MIKROMCP_CONFIG_PATH ?? "
|
|
25
|
+
configPath: env.MIKROMCP_CONFIG_PATH ?? join(mikromcpDir(), "routers.yaml"),
|
|
21
26
|
dataDir,
|
|
22
27
|
snapshotDir: `${dataDir}/snapshots`,
|
|
23
28
|
journalPath: `${dataDir}/write-journal.ndjson`,
|
|
24
29
|
cmdAllow: (env.MIKROMCP_CMD_ALLOW ?? "").split(",").map((s) => s.trim()).filter(Boolean),
|
|
25
30
|
cmdDeny: (env.MIKROMCP_CMD_DENY ?? "").split(",").map((s) => s.trim()).filter(Boolean),
|
|
26
|
-
identitiesPath: env.MIKROMCP_IDENTITIES_PATH ?? "
|
|
31
|
+
identitiesPath: env.MIKROMCP_IDENTITIES_PATH ?? join(mikromcpDir(), "identities.yaml"),
|
|
27
32
|
stdioIdentity: env.MIKROMCP_STDIO_IDENTITY || void 0,
|
|
28
33
|
confirmationSecret: env.MIKROMCP_CONFIRMATION_SECRET || void 0,
|
|
29
34
|
auditLogPath: env.MIKROMCP_AUDIT_LOG_PATH || void 0,
|
|
@@ -4900,10 +4905,12 @@ var containerTools = [listContainersTool, manageContainerTool];
|
|
|
4900
4905
|
// src/domain/tools/change-management-tools.ts
|
|
4901
4906
|
import { z as z23 } from "zod";
|
|
4902
4907
|
import { readFileSync as readFileSync2 } from "fs";
|
|
4908
|
+
import { join as join3 } from "path";
|
|
4909
|
+
import { homedir as homedir2 } from "os";
|
|
4903
4910
|
|
|
4904
4911
|
// src/domain/snapshot/snapshot-engine.ts
|
|
4905
4912
|
import { mkdirSync, writeFileSync, readFileSync } from "fs";
|
|
4906
|
-
import { join } from "path";
|
|
4913
|
+
import { join as join2 } from "path";
|
|
4907
4914
|
import { nanoid } from "nanoid";
|
|
4908
4915
|
function pathToSlug(rosPath) {
|
|
4909
4916
|
return rosPath.replace(/\//g, "-");
|
|
@@ -4914,8 +4921,8 @@ function timestampPrefix() {
|
|
|
4914
4921
|
async function takeSnapshot(client, routerId, path, snapshotDir) {
|
|
4915
4922
|
const records = await client.get(path, {});
|
|
4916
4923
|
const id = `${timestampPrefix()}-${pathToSlug(path)}-${nanoid(6)}`;
|
|
4917
|
-
const dir =
|
|
4918
|
-
const filePath =
|
|
4924
|
+
const dir = join2(snapshotDir, routerId);
|
|
4925
|
+
const filePath = join2(dir, `${id}.json`);
|
|
4919
4926
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
4920
4927
|
mkdirSync(dir, { recursive: true });
|
|
4921
4928
|
const stored = { id, routerId, path, ts, records };
|
|
@@ -5177,7 +5184,7 @@ function createChangeManagementTools(baseTools2) {
|
|
|
5177
5184
|
async handler(params, context) {
|
|
5178
5185
|
const parsed = applyPlanInputSchema.parse(params);
|
|
5179
5186
|
log24.info({ routerId: context.routerId, stepCount: parsed.steps.length }, "Applying plan");
|
|
5180
|
-
const dataDir = process.env.MIKROMCP_DATA_DIR ?? "data";
|
|
5187
|
+
const dataDir = process.env.MIKROMCP_DATA_DIR ?? join3(homedir2(), ".mikromcp", "data");
|
|
5181
5188
|
const journalPath = `${dataDir}/write-journal.ndjson`;
|
|
5182
5189
|
const snapshotDir = `${dataDir}/snapshots`;
|
|
5183
5190
|
const results2 = [];
|
|
@@ -5271,7 +5278,7 @@ function createChangeManagementTools(baseTools2) {
|
|
|
5271
5278
|
},
|
|
5272
5279
|
async handler(params, context) {
|
|
5273
5280
|
const parsed = rollbackChangeInputSchema.parse(params);
|
|
5274
|
-
const dataDir = process.env.MIKROMCP_DATA_DIR ?? "data";
|
|
5281
|
+
const dataDir = process.env.MIKROMCP_DATA_DIR ?? join3(homedir2(), ".mikromcp", "data");
|
|
5275
5282
|
const journalPath = `${dataDir}/write-journal.ndjson`;
|
|
5276
5283
|
const snapshotDir = `${dataDir}/snapshots`;
|
|
5277
5284
|
log24.info({ routerId: context.routerId, journalId: parsed.journalId }, "Rolling back change");
|
|
@@ -7935,7 +7942,7 @@ function getStdioIdentity(stdioIdentityName, registry) {
|
|
|
7935
7942
|
details: { stdioIdentityName },
|
|
7936
7943
|
recoverability: {
|
|
7937
7944
|
retryable: false,
|
|
7938
|
-
suggestedAction: `Add an identity named "${stdioIdentityName}" to
|
|
7945
|
+
suggestedAction: `Add an identity named "${stdioIdentityName}" to ~/.mikromcp/identities.yaml or unset MIKROMCP_STDIO_IDENTITY.`
|
|
7939
7946
|
}
|
|
7940
7947
|
});
|
|
7941
7948
|
}
|
|
@@ -8606,7 +8613,7 @@ function createServerFactory(config, identityRegistry) {
|
|
|
8606
8613
|
const makeServer = () => {
|
|
8607
8614
|
const server = new McpServer({
|
|
8608
8615
|
name: "mikromcp",
|
|
8609
|
-
version: "1.0.
|
|
8616
|
+
version: "1.0.4"
|
|
8610
8617
|
});
|
|
8611
8618
|
registerAllTools(server, registry, pool, circuitBreakers, config, identityRegistry);
|
|
8612
8619
|
return server;
|
|
@@ -8926,10 +8933,10 @@ async function runServe() {
|
|
|
8926
8933
|
|
|
8927
8934
|
// src/cli/doctor.ts
|
|
8928
8935
|
import { existsSync as existsSync3, readFileSync as readFileSync6 } from "fs";
|
|
8929
|
-
import { homedir } from "os";
|
|
8936
|
+
import { homedir as homedir3 } from "os";
|
|
8930
8937
|
import * as net from "net";
|
|
8931
8938
|
import chalk from "chalk";
|
|
8932
|
-
var localVersion = "1.0.
|
|
8939
|
+
var localVersion = "1.0.4";
|
|
8933
8940
|
var results = [];
|
|
8934
8941
|
function ok(msg) {
|
|
8935
8942
|
console.log(chalk.green("\u2705 ") + msg);
|
|
@@ -9085,7 +9092,7 @@ async function checkRouter(router) {
|
|
|
9085
9092
|
}
|
|
9086
9093
|
}
|
|
9087
9094
|
function checkIdentities(identitiesPath, transport2) {
|
|
9088
|
-
if (!process.env.MIKROMCP_IDENTITIES_PATH && identitiesPath
|
|
9095
|
+
if (!process.env.MIKROMCP_IDENTITIES_PATH && identitiesPath.endsWith(".mikromcp/identities.yaml")) {
|
|
9089
9096
|
if (!existsSync3(identitiesPath)) {
|
|
9090
9097
|
return;
|
|
9091
9098
|
}
|
|
@@ -9109,8 +9116,8 @@ function checkIdentities(identitiesPath, transport2) {
|
|
|
9109
9116
|
}
|
|
9110
9117
|
function checkClaudeDesktop() {
|
|
9111
9118
|
const candidatePaths = [
|
|
9112
|
-
`${
|
|
9113
|
-
`${
|
|
9119
|
+
`${homedir3()}/Library/Application Support/Claude/claude_desktop_config.json`,
|
|
9120
|
+
`${homedir3()}/.config/Claude/claude_desktop_config.json`
|
|
9114
9121
|
];
|
|
9115
9122
|
const configPath = candidatePaths.find((p) => existsSync3(p));
|
|
9116
9123
|
if (!configPath) {
|
|
@@ -9208,16 +9215,16 @@ import {
|
|
|
9208
9215
|
mkdirSync as mkdirSync3,
|
|
9209
9216
|
copyFileSync
|
|
9210
9217
|
} from "fs";
|
|
9211
|
-
import { homedir as
|
|
9212
|
-
import { join as
|
|
9218
|
+
import { homedir as homedir4 } from "os";
|
|
9219
|
+
import { join as join4 } from "path";
|
|
9213
9220
|
import { stringify as yamlStringify, parse as yamlParse } from "yaml";
|
|
9214
9221
|
import bcrypt2 from "bcryptjs";
|
|
9215
9222
|
import chalk2 from "chalk";
|
|
9216
|
-
function
|
|
9217
|
-
return
|
|
9223
|
+
function mikromcpDir2() {
|
|
9224
|
+
return join4(homedir4(), ".mikromcp");
|
|
9218
9225
|
}
|
|
9219
9226
|
function defaultAuditLogPath() {
|
|
9220
|
-
return
|
|
9227
|
+
return join4(mikromcpDir2(), "audit.ndjson");
|
|
9221
9228
|
}
|
|
9222
9229
|
function suggestEnvPrefix(routerId) {
|
|
9223
9230
|
return "ROUTER_" + routerId.toUpperCase().replace(/-/g, "_");
|
|
@@ -9231,8 +9238,8 @@ function splitTags(val) {
|
|
|
9231
9238
|
}
|
|
9232
9239
|
function findClaudeDesktopConfig() {
|
|
9233
9240
|
const candidates = [
|
|
9234
|
-
|
|
9235
|
-
|
|
9241
|
+
join4(homedir4(), "Library", "Application Support", "Claude", "claude_desktop_config.json"),
|
|
9242
|
+
join4(homedir4(), ".config", "Claude", "claude_desktop_config.json")
|
|
9236
9243
|
];
|
|
9237
9244
|
return candidates.find((p) => existsSync4(p)) ?? null;
|
|
9238
9245
|
}
|
|
@@ -9255,8 +9262,7 @@ async function collectRouterInfo() {
|
|
|
9255
9262
|
}
|
|
9256
9263
|
});
|
|
9257
9264
|
const port = Number(portStr);
|
|
9258
|
-
const
|
|
9259
|
-
const tlsEnabled = await confirm({ message: "Enable TLS?", default: tlsDefault });
|
|
9265
|
+
const tlsEnabled = await confirm({ message: "Enable TLS?", default: false });
|
|
9260
9266
|
let rejectUnauthorized = true;
|
|
9261
9267
|
if (tlsEnabled) {
|
|
9262
9268
|
rejectUnauthorized = await confirm({
|
|
@@ -9277,6 +9283,14 @@ async function collectRouterInfo() {
|
|
|
9277
9283
|
default: suggestedPrefix,
|
|
9278
9284
|
validate: (v) => v.trim().length > 0 ? true : "Env prefix is required"
|
|
9279
9285
|
});
|
|
9286
|
+
const routerUser = await input({
|
|
9287
|
+
message: `Router username (${envPrefix}_USER):`,
|
|
9288
|
+
validate: (v) => v.trim().length > 0 ? true : "Username is required"
|
|
9289
|
+
});
|
|
9290
|
+
const routerPass = await input({
|
|
9291
|
+
message: `Router password (${envPrefix}_PASS):`,
|
|
9292
|
+
validate: (v) => v.trim().length > 0 ? true : "Password is required"
|
|
9293
|
+
});
|
|
9280
9294
|
const tagsRaw = await input({
|
|
9281
9295
|
message: "Tags (comma-separated, optional):",
|
|
9282
9296
|
default: ""
|
|
@@ -9287,7 +9301,7 @@ async function collectRouterInfo() {
|
|
|
9287
9301
|
default: "7",
|
|
9288
9302
|
validate: (v) => v.trim().length > 0 ? true : "Version is required"
|
|
9289
9303
|
});
|
|
9290
|
-
return { routerId, host, port, tlsEnabled, rejectUnauthorized, envPrefix, tags, rosVersion };
|
|
9304
|
+
return { routerId, host, port, tlsEnabled, rejectUnauthorized, envPrefix, routerUser, routerPass, tags, rosVersion };
|
|
9291
9305
|
}
|
|
9292
9306
|
async function collectIdentityInfo() {
|
|
9293
9307
|
console.log(chalk2.bold("\n\u2500\u2500 Identity / HTTP transport \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
@@ -9343,6 +9357,25 @@ async function collectIdentityInfo() {
|
|
|
9343
9357
|
tokenHash
|
|
9344
9358
|
};
|
|
9345
9359
|
}
|
|
9360
|
+
async function collectTransport() {
|
|
9361
|
+
console.log(chalk2.bold("\n\u2500\u2500 Transport \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
9362
|
+
const transport2 = await select({
|
|
9363
|
+
message: "Transport mode:",
|
|
9364
|
+
choices: [
|
|
9365
|
+
{ value: "stdio", name: "stdio \u2014 launched by Claude Desktop or an MCP client (recommended)" },
|
|
9366
|
+
{ value: "http", name: "http \u2014 standalone HTTP server with bearer-token auth" }
|
|
9367
|
+
],
|
|
9368
|
+
default: "stdio"
|
|
9369
|
+
});
|
|
9370
|
+
if (transport2 === "http") {
|
|
9371
|
+
console.log(
|
|
9372
|
+
chalk2.yellow(
|
|
9373
|
+
"\n \u26A0 HTTP mode requires every request to include:\n Authorization: Bearer <token>\n Set MIKROMCP_CONFIRMATION_SECRET in ~/.mikromcp/.env and create an identity above."
|
|
9374
|
+
)
|
|
9375
|
+
);
|
|
9376
|
+
}
|
|
9377
|
+
return transport2;
|
|
9378
|
+
}
|
|
9346
9379
|
async function collectEnvPreference() {
|
|
9347
9380
|
return confirm({
|
|
9348
9381
|
message: "Write credentials to ~/.mikromcp/.env? (No = inject env vars at runtime)",
|
|
@@ -9354,7 +9387,7 @@ async function collectClaudeDesktopPreference() {
|
|
|
9354
9387
|
return confirm({ message: "Register with Claude Desktop?", default: true });
|
|
9355
9388
|
}
|
|
9356
9389
|
function writeRoutersYaml(data) {
|
|
9357
|
-
const filePath =
|
|
9390
|
+
const filePath = join4(data.configDir, "routers.yaml");
|
|
9358
9391
|
const newEntry = {
|
|
9359
9392
|
host: data.host,
|
|
9360
9393
|
port: data.port,
|
|
@@ -9381,7 +9414,7 @@ function writeRoutersYaml(data) {
|
|
|
9381
9414
|
return true;
|
|
9382
9415
|
}
|
|
9383
9416
|
function writeIdentitiesYaml(data) {
|
|
9384
|
-
const filePath =
|
|
9417
|
+
const filePath = join4(data.configDir, "identities.yaml");
|
|
9385
9418
|
const newEntry = {
|
|
9386
9419
|
token: data.tokenHash,
|
|
9387
9420
|
role: data.role,
|
|
@@ -9403,21 +9436,20 @@ function writeDotEnv(data) {
|
|
|
9403
9436
|
const userKey = `${data.envPrefix}_USER`;
|
|
9404
9437
|
const passKey = `${data.envPrefix}_PASS`;
|
|
9405
9438
|
const auditLogPath = defaultAuditLogPath();
|
|
9406
|
-
const
|
|
9407
|
-
const confirmationSecret = data.createIdentity ? randomBytes(32).toString("hex") : "";
|
|
9439
|
+
const confirmationSecret = data.transport === "http" ? randomBytes(32).toString("hex") : "";
|
|
9408
9440
|
const lines = [
|
|
9409
9441
|
"# \u2500\u2500 Router credentials \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
9410
|
-
`${userKey}
|
|
9411
|
-
`${passKey}
|
|
9442
|
+
`${userKey}=${data.routerUser}`,
|
|
9443
|
+
`${passKey}=${data.routerPass}`,
|
|
9412
9444
|
"",
|
|
9413
9445
|
"# \u2500\u2500 Transport \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
9414
9446
|
`# stdio \u2192 launched directly by Claude Desktop or another MCP client`,
|
|
9415
9447
|
`# http \u2192 standalone server mode with bearer-token auth`,
|
|
9416
|
-
`MIKROMCP_TRANSPORT=${
|
|
9448
|
+
`MIKROMCP_TRANSPORT=${data.transport}`,
|
|
9417
9449
|
"",
|
|
9418
9450
|
"# \u2500\u2500 Config paths \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
9419
|
-
`MIKROMCP_CONFIG_PATH=${
|
|
9420
|
-
`MIKROMCP_IDENTITIES_PATH=${
|
|
9451
|
+
`MIKROMCP_CONFIG_PATH=${join4(data.configDir, "routers.yaml")}`,
|
|
9452
|
+
`MIKROMCP_IDENTITIES_PATH=${join4(data.configDir, "identities.yaml")}`,
|
|
9421
9453
|
"",
|
|
9422
9454
|
"# \u2500\u2500 Logging \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
9423
9455
|
"# Levels: trace | debug | info | warn | error",
|
|
@@ -9439,9 +9471,6 @@ function writeDotEnv(data) {
|
|
|
9439
9471
|
} else {
|
|
9440
9472
|
writeFileSync2(data.envPath, content);
|
|
9441
9473
|
}
|
|
9442
|
-
console.log(
|
|
9443
|
-
chalk2.dim(` Fill in ${userKey} and ${passKey} in ${data.envPath}`)
|
|
9444
|
-
);
|
|
9445
9474
|
}
|
|
9446
9475
|
function registerClaudeDesktop() {
|
|
9447
9476
|
const configPath = findClaudeDesktopConfig();
|
|
@@ -9477,11 +9506,12 @@ async function runInit() {
|
|
|
9477
9506
|
console.log(chalk2.bold.cyan("\n\u{1F527} MikroMCP \u2014 Interactive Setup Wizard\n"));
|
|
9478
9507
|
const routerInfo = await collectRouterInfo();
|
|
9479
9508
|
const identityInfo = await collectIdentityInfo();
|
|
9480
|
-
const configDir =
|
|
9481
|
-
const envPath =
|
|
9509
|
+
const configDir = mikromcpDir2();
|
|
9510
|
+
const envPath = join4(configDir, ".env");
|
|
9511
|
+
const transport2 = await collectTransport();
|
|
9482
9512
|
const writeEnv = await collectEnvPreference();
|
|
9483
9513
|
let writeRouters = true;
|
|
9484
|
-
const routersYamlPath =
|
|
9514
|
+
const routersYamlPath = join4(configDir, "routers.yaml");
|
|
9485
9515
|
if (existsSync4(routersYamlPath)) {
|
|
9486
9516
|
console.log(chalk2.bold("\n\u2500\u2500 Config files \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
9487
9517
|
writeRouters = await confirm({
|
|
@@ -9493,6 +9523,7 @@ async function runInit() {
|
|
|
9493
9523
|
const data = {
|
|
9494
9524
|
...routerInfo,
|
|
9495
9525
|
...identityInfo,
|
|
9526
|
+
transport: transport2,
|
|
9496
9527
|
envPath,
|
|
9497
9528
|
configDir,
|
|
9498
9529
|
writeEnv,
|
|
@@ -9505,12 +9536,12 @@ async function runInit() {
|
|
|
9505
9536
|
if (data.writeRoutersYaml) {
|
|
9506
9537
|
writeRoutersYaml(data);
|
|
9507
9538
|
summary.push(`routers.yaml written to ${data.configDir}`);
|
|
9508
|
-
console.log(chalk2.green(` \u2714 ${
|
|
9539
|
+
console.log(chalk2.green(` \u2714 ${join4(data.configDir, "routers.yaml")}`));
|
|
9509
9540
|
}
|
|
9510
9541
|
if (data.writeIdentitiesYaml) {
|
|
9511
9542
|
writeIdentitiesYaml(data);
|
|
9512
9543
|
summary.push(`identities.yaml written to ${data.configDir}`);
|
|
9513
|
-
console.log(chalk2.green(` \u2714 ${
|
|
9544
|
+
console.log(chalk2.green(` \u2714 ${join4(data.configDir, "identities.yaml")}`));
|
|
9514
9545
|
}
|
|
9515
9546
|
if (data.writeEnv) {
|
|
9516
9547
|
writeDotEnv(data);
|
|
@@ -9543,12 +9574,12 @@ async function runInit() {
|
|
|
9543
9574
|
}
|
|
9544
9575
|
|
|
9545
9576
|
// src/cli/index.ts
|
|
9546
|
-
var program = new Command().name("mikromcp").description("MikroTik RouterOS MCP server").version("1.0.
|
|
9577
|
+
var program = new Command().name("mikromcp").description("MikroTik RouterOS MCP server").version("1.0.4");
|
|
9547
9578
|
program.command("serve", { isDefault: true }).description("Start the MCP server (default)").action(runServe);
|
|
9548
9579
|
program.command("doctor").description("Check config, connectivity, and permissions").action(runDoctor);
|
|
9549
9580
|
program.command("init").description("Interactive setup wizard").action(runInit);
|
|
9550
9581
|
|
|
9551
9582
|
// src/main.ts
|
|
9552
|
-
loadDotenv({ path:
|
|
9583
|
+
loadDotenv({ path: join5(homedir5(), ".mikromcp", ".env"), override: false });
|
|
9553
9584
|
program.parse();
|
|
9554
9585
|
//# sourceMappingURL=main.js.map
|