mikromcp 1.0.1 → 1.0.3
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 +1 -1
- package/dist/main.js +114 -80
- package/package.json +2 -2
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)
|
package/dist/main.js
CHANGED
|
@@ -2,26 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
// src/main.ts
|
|
4
4
|
import { config as loadDotenv } from "dotenv";
|
|
5
|
+
import { join as join5 } from "path";
|
|
6
|
+
import { homedir as homedir5 } from "os";
|
|
5
7
|
|
|
6
8
|
// src/cli/index.ts
|
|
7
9
|
import { Command } from "commander";
|
|
8
10
|
|
|
9
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
|
+
}
|
|
10
17
|
function loadAppConfig() {
|
|
11
18
|
const env = process.env;
|
|
12
|
-
const dataDir = env.MIKROMCP_DATA_DIR ?? "data";
|
|
19
|
+
const dataDir = env.MIKROMCP_DATA_DIR ?? join(mikromcpDir(), "data");
|
|
13
20
|
return {
|
|
14
21
|
transport: env.MIKROMCP_TRANSPORT === "http" ? "http" : "stdio",
|
|
15
22
|
port: parseInt(env.MIKROMCP_PORT ?? "3000", 10),
|
|
16
23
|
bindHost: env.MIKROMCP_BIND_HOST ?? "127.0.0.1",
|
|
17
24
|
logLevel: env.MIKROMCP_LOG_LEVEL ?? "info",
|
|
18
|
-
configPath: env.MIKROMCP_CONFIG_PATH ?? "
|
|
25
|
+
configPath: env.MIKROMCP_CONFIG_PATH ?? join(mikromcpDir(), "routers.yaml"),
|
|
19
26
|
dataDir,
|
|
20
27
|
snapshotDir: `${dataDir}/snapshots`,
|
|
21
28
|
journalPath: `${dataDir}/write-journal.ndjson`,
|
|
22
29
|
cmdAllow: (env.MIKROMCP_CMD_ALLOW ?? "").split(",").map((s) => s.trim()).filter(Boolean),
|
|
23
30
|
cmdDeny: (env.MIKROMCP_CMD_DENY ?? "").split(",").map((s) => s.trim()).filter(Boolean),
|
|
24
|
-
identitiesPath: env.MIKROMCP_IDENTITIES_PATH ?? "
|
|
31
|
+
identitiesPath: env.MIKROMCP_IDENTITIES_PATH ?? join(mikromcpDir(), "identities.yaml"),
|
|
25
32
|
stdioIdentity: env.MIKROMCP_STDIO_IDENTITY || void 0,
|
|
26
33
|
confirmationSecret: env.MIKROMCP_CONFIRMATION_SECRET || void 0,
|
|
27
34
|
auditLogPath: env.MIKROMCP_AUDIT_LOG_PATH || void 0,
|
|
@@ -4898,10 +4905,12 @@ var containerTools = [listContainersTool, manageContainerTool];
|
|
|
4898
4905
|
// src/domain/tools/change-management-tools.ts
|
|
4899
4906
|
import { z as z23 } from "zod";
|
|
4900
4907
|
import { readFileSync as readFileSync2 } from "fs";
|
|
4908
|
+
import { join as join3 } from "path";
|
|
4909
|
+
import { homedir as homedir2 } from "os";
|
|
4901
4910
|
|
|
4902
4911
|
// src/domain/snapshot/snapshot-engine.ts
|
|
4903
4912
|
import { mkdirSync, writeFileSync, readFileSync } from "fs";
|
|
4904
|
-
import { join } from "path";
|
|
4913
|
+
import { join as join2 } from "path";
|
|
4905
4914
|
import { nanoid } from "nanoid";
|
|
4906
4915
|
function pathToSlug(rosPath) {
|
|
4907
4916
|
return rosPath.replace(/\//g, "-");
|
|
@@ -4912,8 +4921,8 @@ function timestampPrefix() {
|
|
|
4912
4921
|
async function takeSnapshot(client, routerId, path, snapshotDir) {
|
|
4913
4922
|
const records = await client.get(path, {});
|
|
4914
4923
|
const id = `${timestampPrefix()}-${pathToSlug(path)}-${nanoid(6)}`;
|
|
4915
|
-
const dir =
|
|
4916
|
-
const filePath =
|
|
4924
|
+
const dir = join2(snapshotDir, routerId);
|
|
4925
|
+
const filePath = join2(dir, `${id}.json`);
|
|
4917
4926
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
4918
4927
|
mkdirSync(dir, { recursive: true });
|
|
4919
4928
|
const stored = { id, routerId, path, ts, records };
|
|
@@ -5175,7 +5184,7 @@ function createChangeManagementTools(baseTools2) {
|
|
|
5175
5184
|
async handler(params, context) {
|
|
5176
5185
|
const parsed = applyPlanInputSchema.parse(params);
|
|
5177
5186
|
log24.info({ routerId: context.routerId, stepCount: parsed.steps.length }, "Applying plan");
|
|
5178
|
-
const dataDir = process.env.MIKROMCP_DATA_DIR ?? "data";
|
|
5187
|
+
const dataDir = process.env.MIKROMCP_DATA_DIR ?? join3(homedir2(), ".mikromcp", "data");
|
|
5179
5188
|
const journalPath = `${dataDir}/write-journal.ndjson`;
|
|
5180
5189
|
const snapshotDir = `${dataDir}/snapshots`;
|
|
5181
5190
|
const results2 = [];
|
|
@@ -5269,7 +5278,7 @@ function createChangeManagementTools(baseTools2) {
|
|
|
5269
5278
|
},
|
|
5270
5279
|
async handler(params, context) {
|
|
5271
5280
|
const parsed = rollbackChangeInputSchema.parse(params);
|
|
5272
|
-
const dataDir = process.env.MIKROMCP_DATA_DIR ?? "data";
|
|
5281
|
+
const dataDir = process.env.MIKROMCP_DATA_DIR ?? join3(homedir2(), ".mikromcp", "data");
|
|
5273
5282
|
const journalPath = `${dataDir}/write-journal.ndjson`;
|
|
5274
5283
|
const snapshotDir = `${dataDir}/snapshots`;
|
|
5275
5284
|
log24.info({ routerId: context.routerId, journalId: parsed.journalId }, "Rolling back change");
|
|
@@ -7933,7 +7942,7 @@ function getStdioIdentity(stdioIdentityName, registry) {
|
|
|
7933
7942
|
details: { stdioIdentityName },
|
|
7934
7943
|
recoverability: {
|
|
7935
7944
|
retryable: false,
|
|
7936
|
-
suggestedAction: `Add an identity named "${stdioIdentityName}" to
|
|
7945
|
+
suggestedAction: `Add an identity named "${stdioIdentityName}" to ~/.mikromcp/identities.yaml or unset MIKROMCP_STDIO_IDENTITY.`
|
|
7937
7946
|
}
|
|
7938
7947
|
});
|
|
7939
7948
|
}
|
|
@@ -8604,7 +8613,7 @@ function createServerFactory(config, identityRegistry) {
|
|
|
8604
8613
|
const makeServer = () => {
|
|
8605
8614
|
const server = new McpServer({
|
|
8606
8615
|
name: "mikromcp",
|
|
8607
|
-
version: "1.0.
|
|
8616
|
+
version: "1.0.3"
|
|
8608
8617
|
});
|
|
8609
8618
|
registerAllTools(server, registry, pool, circuitBreakers, config, identityRegistry);
|
|
8610
8619
|
return server;
|
|
@@ -8924,10 +8933,10 @@ async function runServe() {
|
|
|
8924
8933
|
|
|
8925
8934
|
// src/cli/doctor.ts
|
|
8926
8935
|
import { existsSync as existsSync3, readFileSync as readFileSync6 } from "fs";
|
|
8927
|
-
import { homedir } from "os";
|
|
8936
|
+
import { homedir as homedir3 } from "os";
|
|
8928
8937
|
import * as net from "net";
|
|
8929
8938
|
import chalk from "chalk";
|
|
8930
|
-
var localVersion = "1.0.
|
|
8939
|
+
var localVersion = "1.0.3";
|
|
8931
8940
|
var results = [];
|
|
8932
8941
|
function ok(msg) {
|
|
8933
8942
|
console.log(chalk.green("\u2705 ") + msg);
|
|
@@ -9083,7 +9092,7 @@ async function checkRouter(router) {
|
|
|
9083
9092
|
}
|
|
9084
9093
|
}
|
|
9085
9094
|
function checkIdentities(identitiesPath, transport2) {
|
|
9086
|
-
if (!process.env.MIKROMCP_IDENTITIES_PATH && identitiesPath
|
|
9095
|
+
if (!process.env.MIKROMCP_IDENTITIES_PATH && identitiesPath.endsWith(".mikromcp/identities.yaml")) {
|
|
9087
9096
|
if (!existsSync3(identitiesPath)) {
|
|
9088
9097
|
return;
|
|
9089
9098
|
}
|
|
@@ -9107,8 +9116,8 @@ function checkIdentities(identitiesPath, transport2) {
|
|
|
9107
9116
|
}
|
|
9108
9117
|
function checkClaudeDesktop() {
|
|
9109
9118
|
const candidatePaths = [
|
|
9110
|
-
`${
|
|
9111
|
-
`${
|
|
9119
|
+
`${homedir3()}/Library/Application Support/Claude/claude_desktop_config.json`,
|
|
9120
|
+
`${homedir3()}/.config/Claude/claude_desktop_config.json`
|
|
9112
9121
|
];
|
|
9113
9122
|
const configPath = candidatePaths.find((p) => existsSync3(p));
|
|
9114
9123
|
if (!configPath) {
|
|
@@ -9206,11 +9215,17 @@ import {
|
|
|
9206
9215
|
mkdirSync as mkdirSync3,
|
|
9207
9216
|
copyFileSync
|
|
9208
9217
|
} from "fs";
|
|
9209
|
-
import { homedir as
|
|
9210
|
-
import { join as
|
|
9218
|
+
import { homedir as homedir4 } from "os";
|
|
9219
|
+
import { join as join4 } from "path";
|
|
9211
9220
|
import { stringify as yamlStringify, parse as yamlParse } from "yaml";
|
|
9212
9221
|
import bcrypt2 from "bcryptjs";
|
|
9213
9222
|
import chalk2 from "chalk";
|
|
9223
|
+
function mikromcpDir2() {
|
|
9224
|
+
return join4(homedir4(), ".mikromcp");
|
|
9225
|
+
}
|
|
9226
|
+
function defaultAuditLogPath() {
|
|
9227
|
+
return join4(mikromcpDir2(), "audit.ndjson");
|
|
9228
|
+
}
|
|
9214
9229
|
function suggestEnvPrefix(routerId) {
|
|
9215
9230
|
return "ROUTER_" + routerId.toUpperCase().replace(/-/g, "_");
|
|
9216
9231
|
}
|
|
@@ -9223,22 +9238,11 @@ function splitTags(val) {
|
|
|
9223
9238
|
}
|
|
9224
9239
|
function findClaudeDesktopConfig() {
|
|
9225
9240
|
const candidates = [
|
|
9226
|
-
|
|
9227
|
-
|
|
9241
|
+
join4(homedir4(), "Library", "Application Support", "Claude", "claude_desktop_config.json"),
|
|
9242
|
+
join4(homedir4(), ".config", "Claude", "claude_desktop_config.json")
|
|
9228
9243
|
];
|
|
9229
9244
|
return candidates.find((p) => existsSync4(p)) ?? null;
|
|
9230
9245
|
}
|
|
9231
|
-
function appendToGitignore(projectRoot, entry) {
|
|
9232
|
-
const gitignorePath = join2(projectRoot, ".gitignore");
|
|
9233
|
-
if (existsSync4(gitignorePath)) {
|
|
9234
|
-
const content = readFileSync7(gitignorePath, "utf-8");
|
|
9235
|
-
if (!content.split("\n").some((line) => line.trim() === entry)) {
|
|
9236
|
-
writeFileSync2(gitignorePath, content.endsWith("\n") ? content + entry + "\n" : content + "\n" + entry + "\n");
|
|
9237
|
-
}
|
|
9238
|
-
} else {
|
|
9239
|
-
writeFileSync2(gitignorePath, entry + "\n");
|
|
9240
|
-
}
|
|
9241
|
-
}
|
|
9242
9246
|
async function collectRouterInfo() {
|
|
9243
9247
|
console.log(chalk2.bold("\n\u2500\u2500 Router configuration \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"));
|
|
9244
9248
|
const routerId = await input({
|
|
@@ -9246,19 +9250,20 @@ async function collectRouterInfo() {
|
|
|
9246
9250
|
validate: validateRouterId
|
|
9247
9251
|
});
|
|
9248
9252
|
const host = await input({
|
|
9249
|
-
message: "Host / IP (e.g. 192.168.
|
|
9253
|
+
message: "Host / IP (e.g. 192.168.88.1):",
|
|
9250
9254
|
validate: (v) => v.trim().length > 0 ? true : "Host is required"
|
|
9251
9255
|
});
|
|
9252
9256
|
const portStr = await input({
|
|
9253
9257
|
message: "Port:",
|
|
9254
|
-
default: "
|
|
9258
|
+
default: "443",
|
|
9255
9259
|
validate: (v) => {
|
|
9256
9260
|
const n = Number(v);
|
|
9257
9261
|
return Number.isInteger(n) && n >= 1 && n <= 65535 ? true : "Port must be 1\u201365535";
|
|
9258
9262
|
}
|
|
9259
9263
|
});
|
|
9260
9264
|
const port = Number(portStr);
|
|
9261
|
-
const
|
|
9265
|
+
const tlsDefault = port === 443 || port === 8443;
|
|
9266
|
+
const tlsEnabled = await confirm({ message: "Enable TLS?", default: tlsDefault });
|
|
9262
9267
|
let rejectUnauthorized = true;
|
|
9263
9268
|
if (tlsEnabled) {
|
|
9264
9269
|
rejectUnauthorized = await confirm({
|
|
@@ -9345,16 +9350,18 @@ async function collectIdentityInfo() {
|
|
|
9345
9350
|
tokenHash
|
|
9346
9351
|
};
|
|
9347
9352
|
}
|
|
9348
|
-
async function collectEnvPreference(
|
|
9349
|
-
|
|
9350
|
-
|
|
9353
|
+
async function collectEnvPreference() {
|
|
9354
|
+
return confirm({
|
|
9355
|
+
message: "Write credentials to ~/.mikromcp/.env? (No = inject env vars at runtime)",
|
|
9356
|
+
default: true
|
|
9357
|
+
});
|
|
9351
9358
|
}
|
|
9352
9359
|
async function collectClaudeDesktopPreference() {
|
|
9353
9360
|
console.log(chalk2.bold("\n\u2500\u2500 Claude Desktop integration \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"));
|
|
9354
9361
|
return confirm({ message: "Register with Claude Desktop?", default: true });
|
|
9355
9362
|
}
|
|
9356
|
-
function writeRoutersYaml(data
|
|
9357
|
-
const filePath =
|
|
9363
|
+
function writeRoutersYaml(data) {
|
|
9364
|
+
const filePath = join4(data.configDir, "routers.yaml");
|
|
9358
9365
|
const newEntry = {
|
|
9359
9366
|
host: data.host,
|
|
9360
9367
|
port: data.port,
|
|
@@ -9376,12 +9383,12 @@ function writeRoutersYaml(data, configDir) {
|
|
|
9376
9383
|
if (!parsed.routers) parsed.routers = {};
|
|
9377
9384
|
}
|
|
9378
9385
|
parsed.routers[data.routerId] = newEntry;
|
|
9379
|
-
mkdirSync3(configDir, { recursive: true });
|
|
9386
|
+
mkdirSync3(data.configDir, { recursive: true });
|
|
9380
9387
|
writeFileSync2(filePath, yamlStringify(parsed, { lineWidth: 0 }));
|
|
9381
9388
|
return true;
|
|
9382
9389
|
}
|
|
9383
|
-
function writeIdentitiesYaml(data
|
|
9384
|
-
const filePath =
|
|
9390
|
+
function writeIdentitiesYaml(data) {
|
|
9391
|
+
const filePath = join4(data.configDir, "identities.yaml");
|
|
9385
9392
|
const newEntry = {
|
|
9386
9393
|
token: data.tokenHash,
|
|
9387
9394
|
role: data.role,
|
|
@@ -9395,32 +9402,55 @@ function writeIdentitiesYaml(data, configDir) {
|
|
|
9395
9402
|
if (!parsed.identities) parsed.identities = {};
|
|
9396
9403
|
}
|
|
9397
9404
|
parsed.identities[data.identityId] = newEntry;
|
|
9398
|
-
mkdirSync3(configDir, { recursive: true });
|
|
9405
|
+
mkdirSync3(data.configDir, { recursive: true });
|
|
9399
9406
|
writeFileSync2(filePath, yamlStringify(parsed, { lineWidth: 0 }));
|
|
9400
9407
|
return true;
|
|
9401
9408
|
}
|
|
9402
|
-
function writeDotEnv(data
|
|
9403
|
-
const envPath = join2(projectRoot, ".env");
|
|
9404
|
-
const lines = [];
|
|
9405
|
-
lines.push(`ROUTER_${data.envPrefix.replace(/^ROUTER_/, "")}_USER=`);
|
|
9406
|
-
lines.push(`ROUTER_${data.envPrefix.replace(/^ROUTER_/, "")}_PASS=`);
|
|
9409
|
+
function writeDotEnv(data) {
|
|
9407
9410
|
const userKey = `${data.envPrefix}_USER`;
|
|
9408
9411
|
const passKey = `${data.envPrefix}_PASS`;
|
|
9409
|
-
|
|
9410
|
-
|
|
9411
|
-
|
|
9412
|
-
|
|
9413
|
-
|
|
9414
|
-
|
|
9415
|
-
|
|
9416
|
-
|
|
9412
|
+
const auditLogPath = defaultAuditLogPath();
|
|
9413
|
+
const transport2 = data.createIdentity ? "http" : "stdio";
|
|
9414
|
+
const confirmationSecret = data.createIdentity ? randomBytes(32).toString("hex") : "";
|
|
9415
|
+
const lines = [
|
|
9416
|
+
"# \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",
|
|
9417
|
+
`${userKey}=`,
|
|
9418
|
+
`${passKey}=`,
|
|
9419
|
+
"",
|
|
9420
|
+
"# \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",
|
|
9421
|
+
`# stdio \u2192 launched directly by Claude Desktop or another MCP client`,
|
|
9422
|
+
`# http \u2192 standalone server mode with bearer-token auth`,
|
|
9423
|
+
`MIKROMCP_TRANSPORT=${transport2}`,
|
|
9424
|
+
"",
|
|
9425
|
+
"# \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",
|
|
9426
|
+
`MIKROMCP_CONFIG_PATH=${join4(data.configDir, "routers.yaml")}`,
|
|
9427
|
+
`MIKROMCP_IDENTITIES_PATH=${join4(data.configDir, "identities.yaml")}`,
|
|
9428
|
+
"",
|
|
9429
|
+
"# \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",
|
|
9430
|
+
"# Levels: trace | debug | info | warn | error",
|
|
9431
|
+
"MIKROMCP_LOG_LEVEL=info",
|
|
9432
|
+
`MIKROMCP_AUDIT_LOG_PATH=${auditLogPath}`,
|
|
9433
|
+
"",
|
|
9434
|
+
"# \u2500\u2500 HTTP transport (only used when MIKROMCP_TRANSPORT=http) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
9435
|
+
"MIKROMCP_PORT=3000",
|
|
9436
|
+
`MIKROMCP_CONFIRMATION_SECRET=${confirmationSecret}`,
|
|
9437
|
+
"",
|
|
9438
|
+
"# \u2500\u2500 RBAC (optional, for stdio with identity enforcement) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
9439
|
+
"# MIKROMCP_STDIO_IDENTITY="
|
|
9440
|
+
];
|
|
9441
|
+
const content = lines.join("\n") + "\n";
|
|
9442
|
+
mkdirSync3(data.configDir, { recursive: true });
|
|
9443
|
+
if (existsSync4(data.envPath)) {
|
|
9444
|
+
const existing = readFileSync7(data.envPath, "utf-8");
|
|
9445
|
+
writeFileSync2(data.envPath, existing.endsWith("\n") ? existing + "\n" + content : existing + "\n\n" + content);
|
|
9417
9446
|
} else {
|
|
9418
|
-
writeFileSync2(envPath,
|
|
9447
|
+
writeFileSync2(data.envPath, content);
|
|
9419
9448
|
}
|
|
9420
|
-
|
|
9421
|
-
|
|
9449
|
+
console.log(
|
|
9450
|
+
chalk2.dim(` Fill in ${userKey} and ${passKey} in ${data.envPath}`)
|
|
9451
|
+
);
|
|
9422
9452
|
}
|
|
9423
|
-
function registerClaudeDesktop(
|
|
9453
|
+
function registerClaudeDesktop() {
|
|
9424
9454
|
const configPath = findClaudeDesktopConfig();
|
|
9425
9455
|
const entry = {
|
|
9426
9456
|
command: "mikromcp",
|
|
@@ -9452,17 +9482,17 @@ function registerClaudeDesktop(_projectRoot) {
|
|
|
9452
9482
|
}
|
|
9453
9483
|
async function runInit() {
|
|
9454
9484
|
console.log(chalk2.bold.cyan("\n\u{1F527} MikroMCP \u2014 Interactive Setup Wizard\n"));
|
|
9455
|
-
const projectRoot = process.cwd();
|
|
9456
|
-
const configDir = join2(projectRoot, "config");
|
|
9457
9485
|
const routerInfo = await collectRouterInfo();
|
|
9458
9486
|
const identityInfo = await collectIdentityInfo();
|
|
9459
|
-
const
|
|
9487
|
+
const configDir = mikromcpDir2();
|
|
9488
|
+
const envPath = join4(configDir, ".env");
|
|
9489
|
+
const writeEnv = await collectEnvPreference();
|
|
9460
9490
|
let writeRouters = true;
|
|
9461
|
-
const routersYamlPath =
|
|
9491
|
+
const routersYamlPath = join4(configDir, "routers.yaml");
|
|
9462
9492
|
if (existsSync4(routersYamlPath)) {
|
|
9463
9493
|
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"));
|
|
9464
9494
|
writeRouters = await confirm({
|
|
9465
|
-
message: "
|
|
9495
|
+
message: "routers.yaml already exists. Add this router to it?",
|
|
9466
9496
|
default: true
|
|
9467
9497
|
});
|
|
9468
9498
|
}
|
|
@@ -9470,6 +9500,8 @@ async function runInit() {
|
|
|
9470
9500
|
const data = {
|
|
9471
9501
|
...routerInfo,
|
|
9472
9502
|
...identityInfo,
|
|
9503
|
+
envPath,
|
|
9504
|
+
configDir,
|
|
9473
9505
|
writeEnv,
|
|
9474
9506
|
writeRoutersYaml: writeRouters,
|
|
9475
9507
|
writeIdentitiesYaml: identityInfo.createIdentity,
|
|
@@ -9478,23 +9510,23 @@ async function runInit() {
|
|
|
9478
9510
|
console.log(chalk2.bold("\n\u2500\u2500 Writing 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"));
|
|
9479
9511
|
const summary = [];
|
|
9480
9512
|
if (data.writeRoutersYaml) {
|
|
9481
|
-
writeRoutersYaml(data
|
|
9482
|
-
summary.push(`
|
|
9483
|
-
console.log(chalk2.green(
|
|
9513
|
+
writeRoutersYaml(data);
|
|
9514
|
+
summary.push(`routers.yaml written to ${data.configDir}`);
|
|
9515
|
+
console.log(chalk2.green(` \u2714 ${join4(data.configDir, "routers.yaml")}`));
|
|
9484
9516
|
}
|
|
9485
9517
|
if (data.writeIdentitiesYaml) {
|
|
9486
|
-
writeIdentitiesYaml(data
|
|
9487
|
-
summary.push(`
|
|
9488
|
-
console.log(chalk2.green(
|
|
9518
|
+
writeIdentitiesYaml(data);
|
|
9519
|
+
summary.push(`identities.yaml written to ${data.configDir}`);
|
|
9520
|
+
console.log(chalk2.green(` \u2714 ${join4(data.configDir, "identities.yaml")}`));
|
|
9489
9521
|
}
|
|
9490
9522
|
if (data.writeEnv) {
|
|
9491
|
-
writeDotEnv(data
|
|
9492
|
-
summary.push(`.env
|
|
9493
|
-
console.log(chalk2.green(
|
|
9523
|
+
writeDotEnv(data);
|
|
9524
|
+
summary.push(`.env written to ${data.envPath}`);
|
|
9525
|
+
console.log(chalk2.green(` \u2714 ${data.envPath}`));
|
|
9494
9526
|
}
|
|
9495
9527
|
let desktopRegistered = false;
|
|
9496
9528
|
if (data.registerClaudeDesktop) {
|
|
9497
|
-
const result = registerClaudeDesktop(
|
|
9529
|
+
const result = registerClaudeDesktop();
|
|
9498
9530
|
desktopRegistered = result.registered;
|
|
9499
9531
|
if (desktopRegistered) {
|
|
9500
9532
|
summary.push("Claude Desktop registered");
|
|
@@ -9506,22 +9538,24 @@ async function runInit() {
|
|
|
9506
9538
|
console.log(chalk2.green(` \u2705 ${item}`));
|
|
9507
9539
|
}
|
|
9508
9540
|
if (data.createIdentity && data.rawToken) {
|
|
9509
|
-
console.log(
|
|
9541
|
+
console.log(
|
|
9542
|
+
chalk2.bold.yellow(` \u2705 Bearer token: ${data.rawToken} (save this \u2014 it won't be shown again)`)
|
|
9543
|
+
);
|
|
9510
9544
|
}
|
|
9511
9545
|
console.log(chalk2.bold("\nNext steps:"));
|
|
9512
|
-
console.log(chalk2.dim(` 1.
|
|
9513
|
-
console.log(chalk2.dim(" 2. Run:
|
|
9514
|
-
console.log(chalk2.dim(" 3.
|
|
9546
|
+
console.log(chalk2.dim(` 1. Fill in router credentials in ${data.envPath}`));
|
|
9547
|
+
console.log(chalk2.dim(" 2. Run: mikromcp doctor"));
|
|
9548
|
+
console.log(chalk2.dim(" 3. Restart Claude Desktop to load the MCP server"));
|
|
9515
9549
|
console.log();
|
|
9516
9550
|
}
|
|
9517
9551
|
|
|
9518
9552
|
// src/cli/index.ts
|
|
9519
|
-
var program = new Command().name("mikromcp").description("MikroTik RouterOS MCP server").version("1.0.
|
|
9553
|
+
var program = new Command().name("mikromcp").description("MikroTik RouterOS MCP server").version("1.0.3");
|
|
9520
9554
|
program.command("serve", { isDefault: true }).description("Start the MCP server (default)").action(runServe);
|
|
9521
9555
|
program.command("doctor").description("Check config, connectivity, and permissions").action(runDoctor);
|
|
9522
9556
|
program.command("init").description("Interactive setup wizard").action(runInit);
|
|
9523
9557
|
|
|
9524
9558
|
// src/main.ts
|
|
9525
|
-
loadDotenv({ override: false });
|
|
9559
|
+
loadDotenv({ path: join5(homedir5(), ".mikromcp", ".env"), override: false });
|
|
9526
9560
|
program.parse();
|
|
9527
9561
|
//# sourceMappingURL=main.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mikromcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Production-grade MCP server for MikroTik RouterOS",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/main.js",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"dev": "tsx watch src/main.ts",
|
|
20
20
|
"build": "tsup",
|
|
21
21
|
"clean": "rm -rf dist",
|
|
22
|
-
"test": "vitest run",
|
|
22
|
+
"test": "vitest run && tsc --noEmit && eslint src/",
|
|
23
23
|
"test:watch": "vitest",
|
|
24
24
|
"lint": "eslint src/",
|
|
25
25
|
"format": "prettier --write 'src/**/*.ts' 'test/**/*.ts'",
|