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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/main.js +114 -80
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -12,7 +12,7 @@
12
12
 
13
13
  [![CI](https://github.com/AliKarami/MikroMCP/actions/workflows/ci.yml/badge.svg)](https://github.com/AliKarami/MikroMCP/actions/workflows/ci.yml)
14
14
  [![Release](https://github.com/AliKarami/MikroMCP/actions/workflows/release.yml/badge.svg)](https://github.com/AliKarami/MikroMCP/actions/workflows/release.yml)
15
- [![Version](https://img.shields.io/badge/version-v1.0.0-EA580C)](package.json)
15
+ [![Version](https://img.shields.io/badge/version-v1.0.3-EA580C)](package.json)
16
16
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
17
17
  [![Node.js >= 22](https://img.shields.io/badge/node-%3E%3D22-339933)](package.json)
18
18
  [![RouterOS 7.x](https://img.shields.io/badge/RouterOS-7.x-293239)](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 ?? "config/routers.yaml",
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 ?? "config/identities.yaml",
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 = join(snapshotDir, routerId);
4916
- const filePath = join(dir, `${id}.json`);
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 config/identities.yaml or unset MIKROMCP_STDIO_IDENTITY.`
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.1"
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.1";
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 === "config/identities.yaml") {
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
- `${homedir()}/Library/Application Support/Claude/claude_desktop_config.json`,
9111
- `${homedir()}/.config/Claude/claude_desktop_config.json`
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 homedir2 } from "os";
9210
- import { join as join2 } from "path";
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
- join2(homedir2(), "Library", "Application Support", "Claude", "claude_desktop_config.json"),
9227
- join2(homedir2(), ".config", "Claude", "claude_desktop_config.json")
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.1.1):",
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: "80",
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 tlsEnabled = await confirm({ message: "Enable TLS?", default: false });
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(_data) {
9349
- console.log(chalk2.bold("\n\u2500\u2500 Environment file \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
9350
- return confirm({ message: "Write env vars to .env?", default: true });
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, configDir) {
9357
- const filePath = join2(configDir, "routers.yaml");
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, configDir) {
9384
- const filePath = join2(configDir, "identities.yaml");
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, projectRoot) {
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
- if (data.createIdentity) {
9410
- lines.push("MIKROMCP_TRANSPORT=http");
9411
- lines.push("MIKROMCP_CONFIRMATION_SECRET=");
9412
- }
9413
- const block = [`${userKey}=`, `${passKey}=`, ...data.createIdentity ? ["MIKROMCP_TRANSPORT=http", "MIKROMCP_CONFIRMATION_SECRET="] : []].join("\n") + "\n";
9414
- if (existsSync4(envPath)) {
9415
- const existing = readFileSync7(envPath, "utf-8");
9416
- writeFileSync2(envPath, existing.endsWith("\n") ? existing + block : existing + "\n" + block);
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, block);
9447
+ writeFileSync2(data.envPath, content);
9419
9448
  }
9420
- appendToGitignore(projectRoot, ".env");
9421
- console.log(chalk2.dim(` Edit .env and fill in your router credentials`));
9449
+ console.log(
9450
+ chalk2.dim(` Fill in ${userKey} and ${passKey} in ${data.envPath}`)
9451
+ );
9422
9452
  }
9423
- function registerClaudeDesktop(_projectRoot) {
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 writeEnv = await collectEnvPreference({ ...routerInfo, ...identityInfo });
9487
+ const configDir = mikromcpDir2();
9488
+ const envPath = join4(configDir, ".env");
9489
+ const writeEnv = await collectEnvPreference();
9460
9490
  let writeRouters = true;
9461
- const routersYamlPath = join2(configDir, "routers.yaml");
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: "config/routers.yaml already exists. Add this router to it?",
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, configDir);
9482
- summary.push(`config/routers.yaml updated`);
9483
- console.log(chalk2.green(" \u2714 config/routers.yaml written"));
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, configDir);
9487
- summary.push(`config/identities.yaml updated`);
9488
- console.log(chalk2.green(" \u2714 config/identities.yaml written"));
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, projectRoot);
9492
- summary.push(`.env created (fill in ${data.envPrefix}_USER and ${data.envPrefix}_PASS)`);
9493
- console.log(chalk2.green(" \u2714 .env written"));
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(projectRoot);
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(chalk2.bold.yellow(` \u2705 Bearer token: ${data.rawToken} (save this \u2014 it won't be shown again)`));
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. Edit .env and fill in router credentials`));
9513
- console.log(chalk2.dim(" 2. Run: npm start"));
9514
- console.log(chalk2.dim(" 3. Verify: mikromcp doctor"));
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.1");
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.1",
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'",