@usex/mikrotik-mcp 5.9.0 → 5.11.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 CHANGED
@@ -46,6 +46,12 @@ Then just ask:
46
46
 
47
47
  ## Highlights
48
48
 
49
+ - **MCP App workspaces** — 13 host-themed views, with dedicated investigation,
50
+ round-trip, service-health, L2 fabric, operations and report workspaces alongside
51
+ the existing device, interface, firewall and record views. Responsive grids,
52
+ named path interfaces, expandable evidence and colored JSON keep results readable
53
+ inside compatible MCP clients. New report views never execute or replay tools.
54
+ [View guide](docs/mcp-app-workspaces.md)
49
55
  - **Round-trip path lab** — model explicit forward/reverse static IPv4 paths across
50
56
  fresh snapshots, with per-hop evidence and declared asymmetry. NAT or unsupported
51
57
  state stops with UNKNOWN; modelled forwarding is not live connectivity.
package/dist/cli.js CHANGED
@@ -16764,7 +16764,7 @@ function poolStatus() {
16764
16764
  }
16765
16765
 
16766
16766
  // src/ssh/safe-mode.ts
16767
- var PROMPT_RE = /\[.+?@.+?\] (?:<SAFE> )?> ?$/m;
16767
+ var PROMPT_RE = /\[[^\r\n\]]+@[^\r\n\]]+\][ \t]+(?:\/[^\r\n<>]*[ \t]*)?(?:<SAFE>(?:[ \t]*>)?|>)[ \t]*$/;
16768
16768
  var ANSI_RE = /\x1B(?:\[[0-9;]*[mA-HJ-MSTfhilnprsu]|[()][0-9A-Za-z]|\[?\?\d+[hl])/g;
16769
16769
  function stripAnsi(text) {
16770
16770
  return text.replace(ANSI_RE, "");
@@ -16832,8 +16832,9 @@ class SafeModeManager {
16832
16832
  this.cleanup();
16833
16833
  return `Error: Timed out waiting for MikroTik shell prompt. Got: ${JSON.stringify(initial.slice(0, 300))}`;
16834
16834
  }
16835
+ const activation = this.readUntilPrompt(1e4, (c) => isSafeModeActivated(c));
16835
16836
  this.channel.write(CTRL_X);
16836
- const response = (await this.readUntilPrompt(1e4, (c) => isSafeModeActivated(c))).text;
16837
+ const response = (await activation).text;
16837
16838
  if (!isSafeModeActivated(response)) {
16838
16839
  this.cleanup();
16839
16840
  return `Error: Safe mode did not activate. Response: ${JSON.stringify(response.slice(0, 300))}`;
@@ -16850,11 +16851,12 @@ class SafeModeManager {
16850
16851
  if (!this.active || !this.channel) {
16851
16852
  throw new Error("Safe mode session is not active.");
16852
16853
  }
16854
+ const response = this.readUntilPrompt();
16853
16855
  this.channel.write(`${command}
16854
16856
  `);
16855
- const { text, timedOut } = await this.readUntilPrompt();
16857
+ const { text, timedOut } = await response;
16856
16858
  if (timedOut) {
16857
- throw new Error(`Safe Mode shell went silent for ${IDLE_TIMEOUT_MS / 1000}s (command: ${command}). The ` + "interactive session appears wedged \u2014 some RouterOS builds/terminals don't support Safe " + "Mode over SSH. Apply the change with the direct write tools instead (verify each with a read).");
16859
+ throw new Error(`Safe Mode command did not return a recognized prompt before the timeout (command: ${command}). ` + "Execution may have occurred; do not retry the write blindly. Inspect the session " + "and roll back if its state cannot be verified.");
16858
16860
  }
16859
16861
  return this.extractOutput(text, command);
16860
16862
  });
@@ -16961,12 +16963,17 @@ class SafeModeManager {
16961
16963
  if (!this.channel)
16962
16964
  return "unknown";
16963
16965
  const token = "__MCP_SAFEMODE_PROBE__";
16966
+ const response = this.readUntilPrompt(8000, (cleaned) => {
16967
+ const lines = cleaned.replace(/\r/g, `
16968
+ `).split(`
16969
+ `);
16970
+ const i = lines.findIndex((line) => line.trim() === token);
16971
+ return i >= 0 && PROMPT_RE.test(lines.slice(i + 1).join(`
16972
+ `));
16973
+ });
16964
16974
  this.channel.write(`:put "${token}"
16965
16975
  `);
16966
- const out = (await this.readUntilPrompt(8000, (cleaned) => {
16967
- const i = cleaned.lastIndexOf(token);
16968
- return i >= 0 && PROMPT_RE.test(cleaned.slice(i));
16969
- })).text;
16976
+ const out = (await response).text;
16970
16977
  return classifyPrompt(out);
16971
16978
  }
16972
16979
  extractOutput(raw, command) {
@@ -16984,6 +16991,11 @@ class SafeModeManager {
16984
16991
  pastEcho = true;
16985
16992
  continue;
16986
16993
  }
16994
+ if (stripped.endsWith(command.trim())) {
16995
+ const prefix = stripped.slice(0, -command.trim().length).trimEnd();
16996
+ if (PROMPT_RE.test(prefix))
16997
+ continue;
16998
+ }
16987
16999
  if (PROMPT_RE.test(stripped))
16988
17000
  break;
16989
17001
  result.push(line);
@@ -17656,12 +17668,117 @@ function indicatesFailure(text) {
17656
17668
  return /^Failed\b/.test(firstLine) || /\(line \d+ column \d+\)/.test(text);
17657
17669
  }
17658
17670
 
17671
+ // src/core/report-views.ts
17672
+ var REPORT_VIEW_TOOLS = {
17673
+ investigations: [
17674
+ "create_investigation",
17675
+ "get_investigation",
17676
+ "list_investigations",
17677
+ "inspect_flow_path",
17678
+ "list_client_flow_candidates"
17679
+ ],
17680
+ "round-trip": ["trace_round_trip"],
17681
+ "service-health": [
17682
+ "create_service_contract",
17683
+ "list_service_contracts",
17684
+ "list_service_probe_targets",
17685
+ "run_service_contract",
17686
+ "service_contract_history",
17687
+ "audit_service_contracts"
17688
+ ],
17689
+ fabric: ["map_l2_fabric", "locate_host_port"],
17690
+ operations: [
17691
+ "plan_changes",
17692
+ "apply_plan",
17693
+ "begin_transaction",
17694
+ "add_transaction_step",
17695
+ "verify_transaction",
17696
+ "commit_transaction",
17697
+ "abort_transaction",
17698
+ "plan_rollout",
17699
+ "rollout_status",
17700
+ "config_check_drift",
17701
+ "diff_config_snapshots",
17702
+ "get_access_scope",
17703
+ "safe_mode_status",
17704
+ "packet_capture_status"
17705
+ ],
17706
+ reports: [
17707
+ "diagnose",
17708
+ "trace_path",
17709
+ "correlate_events",
17710
+ "suggest_fix",
17711
+ "run_compliance_audit",
17712
+ "audit_fleet",
17713
+ "audit_known_vulnerabilities",
17714
+ "audit_certificate_expiry",
17715
+ "audit_firewall_hardening",
17716
+ "audit_firewall_default_deny",
17717
+ "audit_address_list_enforcement",
17718
+ "audit_kernel_ip_hardening",
17719
+ "audit_ipv6_firewall_baseline",
17720
+ "audit_ssh_hardening",
17721
+ "audit_ip_service_exposure",
17722
+ "audit_connection_tracking_helpers",
17723
+ "audit_management_plane_exposure",
17724
+ "audit_account_hygiene",
17725
+ "audit_certificate_hygiene",
17726
+ "audit_network_segmentation",
17727
+ "audit_dns_resolver_exposure",
17728
+ "run_security_hardening_audit",
17729
+ "run_capsman_audit",
17730
+ "audit_capsman_coverage",
17731
+ "report_weak_signal_clients",
17732
+ "audit_capsman_load",
17733
+ "audit_capsman_ft",
17734
+ "audit_capsman_ha",
17735
+ "simulate_packet",
17736
+ "simulate_change",
17737
+ "simulate_suite",
17738
+ "explain_rule_reachability",
17739
+ "run_policy_check",
17740
+ "check_policy_snapshot",
17741
+ "explain_policy_finding",
17742
+ "explain_device",
17743
+ "explain_section",
17744
+ "diff_explanations",
17745
+ "flow_top_talkers",
17746
+ "analyze_flows",
17747
+ "forecast_link_saturation"
17748
+ ]
17749
+ };
17750
+ var REPORT_VIEW_META = "mikrotik/reportView";
17751
+ var byTool = new Map(Object.entries(REPORT_VIEW_TOOLS).flatMap(([kind, names]) => names.map((name) => [name, kind])));
17752
+ function reportViewForTool(name) {
17753
+ return byTool.get(name);
17754
+ }
17755
+ function buildReportView(kind, tool, title, device, raw, structured) {
17756
+ let data = structured;
17757
+ if (data === undefined) {
17758
+ try {
17759
+ data = JSON.parse(raw);
17760
+ } catch {
17761
+ data = null;
17762
+ }
17763
+ }
17764
+ return {
17765
+ __mikrotikView: "report",
17766
+ kind,
17767
+ tool,
17768
+ title,
17769
+ device: device ?? "default",
17770
+ raw,
17771
+ data,
17772
+ generatedAt: new Date().toISOString()
17773
+ };
17774
+ }
17775
+
17659
17776
  // src/core/ui-meta.ts
17660
17777
  var UI_META_KEY = "ui";
17661
17778
  var UI_RESOURCE_URI_LEGACY_KEY = "ui/resourceUri";
17662
17779
  var OPENAI_OUTPUT_TEMPLATE_KEY = "openai/outputTemplate";
17663
17780
  function uiViewUri(id) {
17664
- return `ui://mikrotik/${id}.html`;
17781
+ return `ui://mikrotik/${id}.html?v=2`;
17665
17782
  }
17666
17783
  function toolUiMeta(ui) {
17667
17784
  const meta = {
@@ -19418,6 +19535,9 @@ var UI_OUTPUT_SCHEMA = object({}).passthrough();
19418
19535
  function effectiveUi(def) {
19419
19536
  if (def.ui)
19420
19537
  return { ui: def.ui, auto: false };
19538
+ const report = reportViewForTool(def.name);
19539
+ if (report)
19540
+ return { ui: { resourceUri: uiViewUri(report), visibility: ["model"] }, auto: true };
19421
19541
  if (def.annotations.readOnlyHint === true && AUTO_RECORDS_VERB.test(def.name)) {
19422
19542
  return { ui: { resourceUri: uiViewUri("records"), visibility: ["model", "app"] }, auto: true };
19423
19543
  }
@@ -19597,10 +19717,9 @@ function defineTool(def) {
19597
19717
  return { content: [{ type: "text", text: out.text }], isError: true };
19598
19718
  }
19599
19719
  if (auto && !out.structuredContent) {
19600
- const view = buildRecordsView(def.name, def.title, out.text, new Date().toISOString());
19601
- if (view.rows.length > 0) {
19602
- out.structuredContent = view;
19603
- }
19720
+ const report = reportViewForTool(def.name);
19721
+ const view = report ? buildReportView(report, def.name, def.title, deviceName, out.text) : buildRecordsView(def.name, def.title, out.text, new Date().toISOString());
19722
+ out.structuredContent = view;
19604
19723
  }
19605
19724
  const result = {
19606
19725
  content: [{ type: "text", text: out.text }]
@@ -19609,6 +19728,12 @@ function defineTool(def) {
19609
19728
  result.structuredContent = out.structuredContent;
19610
19729
  hasStructured = true;
19611
19730
  }
19731
+ const report = auto && reportViewForTool(def.name);
19732
+ if (report && out.structuredContent?.__mikrotikView !== "report") {
19733
+ result._meta = {
19734
+ [REPORT_VIEW_META]: buildReportView(report, def.name, def.title, deviceName, out.text, out.structuredContent)
19735
+ };
19736
+ }
19612
19737
  if (deviceStamp)
19613
19738
  result.content.push({ type: "text", text: deviceStamp });
19614
19739
  return result;
@@ -19666,7 +19791,7 @@ function defineTool(def) {
19666
19791
  inputSchema,
19667
19792
  annotations: { ...def.annotations, title: def.title },
19668
19793
  ...ui ? { _meta: toolUiMeta(ui) } : {},
19669
- ...ui && !auto ? { outputSchema: UI_OUTPUT_SCHEMA } : {}
19794
+ ...ui ? { outputSchema: UI_OUTPUT_SCHEMA } : {}
19670
19795
  }, callback);
19671
19796
  }
19672
19797
  };
@@ -24555,6 +24680,36 @@ var UI_VIEWS = [
24555
24680
  id: "aaa",
24556
24681
  name: "MikroTik RADIUS & User Manager",
24557
24682
  description: "Full RADIUS client + built-in User Manager RADIUS server management: servers, users, " + "profiles, limitations, NAS clients, assignments, sessions and settings, with add/edit/remove."
24683
+ },
24684
+ {
24685
+ id: "investigations",
24686
+ name: "MikroTik Client Investigations",
24687
+ description: "Client attachment, router-scoped evidence, exported flow interfaces and unresolved visibility gaps."
24688
+ },
24689
+ {
24690
+ id: "round-trip",
24691
+ name: "MikroTik Round-trip Lab",
24692
+ description: "Forward and return snapshot paths with named interfaces, modelled boundaries and unknown hops. Not live packet proof."
24693
+ },
24694
+ {
24695
+ id: "service-health",
24696
+ name: "MikroTik Service Health",
24697
+ description: "Approved endpoint checks, contract definitions and historical PASS/FAIL/UNKNOWN evidence from the MCP host."
24698
+ },
24699
+ {
24700
+ id: "fabric",
24701
+ name: "MikroTik Layer-2 Fabric",
24702
+ description: "Bridge-port occupancy, attached clients and inferred access/uplink roles."
24703
+ },
24704
+ {
24705
+ id: "operations",
24706
+ name: "MikroTik Change Operations",
24707
+ description: "Read-only presentation of plans, transaction results, rollout progress, scope and drift. No apply/commit controls."
24708
+ },
24709
+ {
24710
+ id: "reports",
24711
+ name: "MikroTik Evidence Reports",
24712
+ description: "Readable diagnostic, compliance, security, simulation and traffic reports with searchable evidence and JSON export."
24558
24713
  }
24559
24714
  ];
24560
24715
  function placeholderHtml(view) {
@@ -30068,7 +30223,7 @@ var cache2 = null;
30068
30223
  async function gateway() {
30069
30224
  if (cache2)
30070
30225
  return cache2;
30071
- const { moduleCatalog } = await import("./shared/cli-2a9bs005.js");
30226
+ const { moduleCatalog } = await import("./shared/cli-m947aqb2.js");
30072
30227
  const forIndex = [];
30073
30228
  const byName = new Map;
30074
30229
  for (const mod of moduleCatalog) {
@@ -52294,7 +52449,7 @@ var riskByTool = null;
52294
52449
  async function riskIndex() {
52295
52450
  if (riskByTool)
52296
52451
  return riskByTool;
52297
- const { moduleCatalog } = await import("./shared/cli-2a9bs005.js");
52452
+ const { moduleCatalog } = await import("./shared/cli-m947aqb2.js");
52298
52453
  const index = new Map;
52299
52454
  for (const mod of moduleCatalog) {
52300
52455
  for (const tool of mod.tools)
package/dist/index.js CHANGED
@@ -62,7 +62,7 @@ import {
62
62
  updateSummaryLine,
63
63
  allToolModules2,
64
64
  selectToolModules2
65
- } from "./shared/library-dyeaqzj6.js";
65
+ } from "./shared/library-tm6yshkz.js";
66
66
 
67
67
  // node_modules/ajv/dist/compile/codegen/code.js
68
68
  var require_code = __commonJS(function(exports) {
@@ -3,7 +3,7 @@ import {
3
3
  moduleCatalog2,
4
4
  allToolModules2,
5
5
  selectToolModules2
6
- } from "./library-dyeaqzj6.js";
6
+ } from "./library-tm6yshkz.js";
7
7
  export {
8
8
  allToolModules2 as allToolModules,
9
9
  moduleCatalog2 as moduleCatalog,
@@ -10181,7 +10181,7 @@ async function runPooled(command, deviceName, opts) {
10181
10181
  }
10182
10182
 
10183
10183
  // src/ssh/safe-mode.ts
10184
- var PROMPT_RE = /\[.+?@.+?\] (?:<SAFE> )?> ?$/m;
10184
+ var PROMPT_RE = /\[[^\r\n\]]+@[^\r\n\]]+\][ \t]+(?:\/[^\r\n<>]*[ \t]*)?(?:<SAFE>(?:[ \t]*>)?|>)[ \t]*$/;
10185
10185
  var ANSI_RE = /\x1B(?:\[[0-9;]*[mA-HJ-MSTfhilnprsu]|[()][0-9A-Za-z]|\[?\?\d+[hl])/g;
10186
10186
  function stripAnsi(text) {
10187
10187
  return text.replace(ANSI_RE, "");
@@ -10249,8 +10249,9 @@ class SafeModeManager2 {
10249
10249
  this.cleanup();
10250
10250
  return `Error: Timed out waiting for MikroTik shell prompt. Got: ${JSON.stringify(initial.slice(0, 300))}`;
10251
10251
  }
10252
+ const activation = this.readUntilPrompt(1e4, (c) => isSafeModeActivated(c));
10252
10253
  this.channel.write(CTRL_X);
10253
- const response = (await this.readUntilPrompt(1e4, (c) => isSafeModeActivated(c))).text;
10254
+ const response = (await activation).text;
10254
10255
  if (!isSafeModeActivated(response)) {
10255
10256
  this.cleanup();
10256
10257
  return `Error: Safe mode did not activate. Response: ${JSON.stringify(response.slice(0, 300))}`;
@@ -10267,11 +10268,12 @@ class SafeModeManager2 {
10267
10268
  if (!this.active || !this.channel) {
10268
10269
  throw new Error("Safe mode session is not active.");
10269
10270
  }
10271
+ const response = this.readUntilPrompt();
10270
10272
  this.channel.write(`${command}
10271
10273
  `);
10272
- const { text, timedOut } = await this.readUntilPrompt();
10274
+ const { text, timedOut } = await response;
10273
10275
  if (timedOut) {
10274
- throw new Error(`Safe Mode shell went silent for ${IDLE_TIMEOUT_MS / 1000}s (command: ${command}). The ` + "interactive session appears wedged \u2014 some RouterOS builds/terminals don't support Safe " + "Mode over SSH. Apply the change with the direct write tools instead (verify each with a read).");
10276
+ throw new Error(`Safe Mode command did not return a recognized prompt before the timeout (command: ${command}). ` + "Execution may have occurred; do not retry the write blindly. Inspect the session " + "and roll back if its state cannot be verified.");
10275
10277
  }
10276
10278
  return this.extractOutput(text, command);
10277
10279
  });
@@ -10378,12 +10380,17 @@ class SafeModeManager2 {
10378
10380
  if (!this.channel)
10379
10381
  return "unknown";
10380
10382
  const token = "__MCP_SAFEMODE_PROBE__";
10383
+ const response = this.readUntilPrompt(8000, (cleaned) => {
10384
+ const lines = cleaned.replace(/\r/g, `
10385
+ `).split(`
10386
+ `);
10387
+ const i = lines.findIndex((line) => line.trim() === token);
10388
+ return i >= 0 && PROMPT_RE.test(lines.slice(i + 1).join(`
10389
+ `));
10390
+ });
10381
10391
  this.channel.write(`:put "${token}"
10382
10392
  `);
10383
- const out = (await this.readUntilPrompt(8000, (cleaned) => {
10384
- const i = cleaned.lastIndexOf(token);
10385
- return i >= 0 && PROMPT_RE.test(cleaned.slice(i));
10386
- })).text;
10393
+ const out = (await response).text;
10387
10394
  return classifyPrompt(out);
10388
10395
  }
10389
10396
  extractOutput(raw, command) {
@@ -10401,6 +10408,11 @@ class SafeModeManager2 {
10401
10408
  pastEcho = true;
10402
10409
  continue;
10403
10410
  }
10411
+ if (stripped.endsWith(command.trim())) {
10412
+ const prefix = stripped.slice(0, -command.trim().length).trimEnd();
10413
+ if (PROMPT_RE.test(prefix))
10414
+ continue;
10415
+ }
10404
10416
  if (PROMPT_RE.test(stripped))
10405
10417
  break;
10406
10418
  result.push(line);
@@ -11073,12 +11085,117 @@ function indicatesFailure(text) {
11073
11085
  return /^Failed\b/.test(firstLine) || /\(line \d+ column \d+\)/.test(text);
11074
11086
  }
11075
11087
 
11088
+ // src/core/report-views.ts
11089
+ var REPORT_VIEW_TOOLS = {
11090
+ investigations: [
11091
+ "create_investigation",
11092
+ "get_investigation",
11093
+ "list_investigations",
11094
+ "inspect_flow_path",
11095
+ "list_client_flow_candidates"
11096
+ ],
11097
+ "round-trip": ["trace_round_trip"],
11098
+ "service-health": [
11099
+ "create_service_contract",
11100
+ "list_service_contracts",
11101
+ "list_service_probe_targets",
11102
+ "run_service_contract",
11103
+ "service_contract_history",
11104
+ "audit_service_contracts"
11105
+ ],
11106
+ fabric: ["map_l2_fabric", "locate_host_port"],
11107
+ operations: [
11108
+ "plan_changes",
11109
+ "apply_plan",
11110
+ "begin_transaction",
11111
+ "add_transaction_step",
11112
+ "verify_transaction",
11113
+ "commit_transaction",
11114
+ "abort_transaction",
11115
+ "plan_rollout",
11116
+ "rollout_status",
11117
+ "config_check_drift",
11118
+ "diff_config_snapshots",
11119
+ "get_access_scope",
11120
+ "safe_mode_status",
11121
+ "packet_capture_status"
11122
+ ],
11123
+ reports: [
11124
+ "diagnose",
11125
+ "trace_path",
11126
+ "correlate_events",
11127
+ "suggest_fix",
11128
+ "run_compliance_audit",
11129
+ "audit_fleet",
11130
+ "audit_known_vulnerabilities",
11131
+ "audit_certificate_expiry",
11132
+ "audit_firewall_hardening",
11133
+ "audit_firewall_default_deny",
11134
+ "audit_address_list_enforcement",
11135
+ "audit_kernel_ip_hardening",
11136
+ "audit_ipv6_firewall_baseline",
11137
+ "audit_ssh_hardening",
11138
+ "audit_ip_service_exposure",
11139
+ "audit_connection_tracking_helpers",
11140
+ "audit_management_plane_exposure",
11141
+ "audit_account_hygiene",
11142
+ "audit_certificate_hygiene",
11143
+ "audit_network_segmentation",
11144
+ "audit_dns_resolver_exposure",
11145
+ "run_security_hardening_audit",
11146
+ "run_capsman_audit",
11147
+ "audit_capsman_coverage",
11148
+ "report_weak_signal_clients",
11149
+ "audit_capsman_load",
11150
+ "audit_capsman_ft",
11151
+ "audit_capsman_ha",
11152
+ "simulate_packet",
11153
+ "simulate_change",
11154
+ "simulate_suite",
11155
+ "explain_rule_reachability",
11156
+ "run_policy_check",
11157
+ "check_policy_snapshot",
11158
+ "explain_policy_finding",
11159
+ "explain_device",
11160
+ "explain_section",
11161
+ "diff_explanations",
11162
+ "flow_top_talkers",
11163
+ "analyze_flows",
11164
+ "forecast_link_saturation"
11165
+ ]
11166
+ };
11167
+ var REPORT_VIEW_META = "mikrotik/reportView";
11168
+ var byTool = new Map(Object.entries(REPORT_VIEW_TOOLS).flatMap(([kind, names]) => names.map((name) => [name, kind])));
11169
+ function reportViewForTool(name) {
11170
+ return byTool.get(name);
11171
+ }
11172
+ function buildReportView(kind, tool, title, device, raw, structured) {
11173
+ let data = structured;
11174
+ if (data === undefined) {
11175
+ try {
11176
+ data = JSON.parse(raw);
11177
+ } catch {
11178
+ data = null;
11179
+ }
11180
+ }
11181
+ return {
11182
+ __mikrotikView: "report",
11183
+ kind,
11184
+ tool,
11185
+ title,
11186
+ device: device ?? "default",
11187
+ raw,
11188
+ data,
11189
+ generatedAt: new Date().toISOString()
11190
+ };
11191
+ }
11192
+
11076
11193
  // src/core/ui-meta.ts
11077
11194
  var UI_META_KEY = "ui";
11078
11195
  var UI_RESOURCE_URI_LEGACY_KEY = "ui/resourceUri";
11079
11196
  var OPENAI_OUTPUT_TEMPLATE_KEY = "openai/outputTemplate";
11080
11197
  function uiViewUri(id) {
11081
- return `ui://mikrotik/${id}.html`;
11198
+ return `ui://mikrotik/${id}.html?v=2`;
11082
11199
  }
11083
11200
  function toolUiMeta(ui) {
11084
11201
  const meta = {
@@ -12411,6 +12528,9 @@ var UI_OUTPUT_SCHEMA = object({}).passthrough();
12411
12528
  function effectiveUi(def) {
12412
12529
  if (def.ui)
12413
12530
  return { ui: def.ui, auto: false };
12531
+ const report = reportViewForTool(def.name);
12532
+ if (report)
12533
+ return { ui: { resourceUri: uiViewUri(report), visibility: ["model"] }, auto: true };
12414
12534
  if (def.annotations.readOnlyHint === true && AUTO_RECORDS_VERB.test(def.name)) {
12415
12535
  return { ui: { resourceUri: uiViewUri("records"), visibility: ["model", "app"] }, auto: true };
12416
12536
  }
@@ -12590,10 +12710,9 @@ function defineTool2(def) {
12590
12710
  return { content: [{ type: "text", text: out.text }], isError: true };
12591
12711
  }
12592
12712
  if (auto && !out.structuredContent) {
12593
- const view = buildRecordsView(def.name, def.title, out.text, new Date().toISOString());
12594
- if (view.rows.length > 0) {
12595
- out.structuredContent = view;
12596
- }
12713
+ const report = reportViewForTool(def.name);
12714
+ const view = report ? buildReportView(report, def.name, def.title, deviceName, out.text) : buildRecordsView(def.name, def.title, out.text, new Date().toISOString());
12715
+ out.structuredContent = view;
12597
12716
  }
12598
12717
  const result = {
12599
12718
  content: [{ type: "text", text: out.text }]
@@ -12602,6 +12721,12 @@ function defineTool2(def) {
12602
12721
  result.structuredContent = out.structuredContent;
12603
12722
  hasStructured = true;
12604
12723
  }
12724
+ const report = auto && reportViewForTool(def.name);
12725
+ if (report && out.structuredContent?.__mikrotikView !== "report") {
12726
+ result._meta = {
12727
+ [REPORT_VIEW_META]: buildReportView(report, def.name, def.title, deviceName, out.text, out.structuredContent)
12728
+ };
12729
+ }
12605
12730
  if (deviceStamp)
12606
12731
  result.content.push({ type: "text", text: deviceStamp });
12607
12732
  return result;
@@ -12659,7 +12784,7 @@ function defineTool2(def) {
12659
12784
  inputSchema,
12660
12785
  annotations: { ...def.annotations, title: def.title },
12661
12786
  ...ui ? { _meta: toolUiMeta(ui) } : {},
12662
- ...ui && !auto ? { outputSchema: UI_OUTPUT_SCHEMA } : {}
12787
+ ...ui ? { outputSchema: UI_OUTPUT_SCHEMA } : {}
12663
12788
  }, callback);
12664
12789
  }
12665
12790
  };
@@ -17356,6 +17481,36 @@ var UI_VIEWS = [
17356
17481
  id: "aaa",
17357
17482
  name: "MikroTik RADIUS & User Manager",
17358
17483
  description: "Full RADIUS client + built-in User Manager RADIUS server management: servers, users, " + "profiles, limitations, NAS clients, assignments, sessions and settings, with add/edit/remove."
17484
+ },
17485
+ {
17486
+ id: "investigations",
17487
+ name: "MikroTik Client Investigations",
17488
+ description: "Client attachment, router-scoped evidence, exported flow interfaces and unresolved visibility gaps."
17489
+ },
17490
+ {
17491
+ id: "round-trip",
17492
+ name: "MikroTik Round-trip Lab",
17493
+ description: "Forward and return snapshot paths with named interfaces, modelled boundaries and unknown hops. Not live packet proof."
17494
+ },
17495
+ {
17496
+ id: "service-health",
17497
+ name: "MikroTik Service Health",
17498
+ description: "Approved endpoint checks, contract definitions and historical PASS/FAIL/UNKNOWN evidence from the MCP host."
17499
+ },
17500
+ {
17501
+ id: "fabric",
17502
+ name: "MikroTik Layer-2 Fabric",
17503
+ description: "Bridge-port occupancy, attached clients and inferred access/uplink roles."
17504
+ },
17505
+ {
17506
+ id: "operations",
17507
+ name: "MikroTik Change Operations",
17508
+ description: "Read-only presentation of plans, transaction results, rollout progress, scope and drift. No apply/commit controls."
17509
+ },
17510
+ {
17511
+ id: "reports",
17512
+ name: "MikroTik Evidence Reports",
17513
+ description: "Readable diagnostic, compliance, security, simulation and traffic reports with searchable evidence and JSON export."
17359
17514
  }
17360
17515
  ];
17361
17516
  function placeholderHtml(view) {
@@ -22869,7 +23024,7 @@ var cache2 = null;
22869
23024
  async function gateway() {
22870
23025
  if (cache2)
22871
23026
  return cache2;
22872
- const { moduleCatalog } = await import("./library-rkr0yp3t.js");
23027
+ const { moduleCatalog } = await import("./library-drn9gmjm.js");
22873
23028
  const forIndex = [];
22874
23029
  const byName = new Map;
22875
23030
  for (const mod of moduleCatalog) {
@@ -44847,7 +45002,7 @@ var riskByTool = null;
44847
45002
  async function riskIndex() {
44848
45003
  if (riskByTool)
44849
45004
  return riskByTool;
44850
- const { moduleCatalog } = await import("./library-rkr0yp3t.js");
45005
+ const { moduleCatalog } = await import("./library-drn9gmjm.js");
44851
45006
  const index = new Map;
44852
45007
  for (const mod of moduleCatalog) {
44853
45008
  for (const tool of mod.tools)