@remcp/runtime 0.2.130 → 0.2.131

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/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.131 — 2026-10-02
4
+
5
+ - `network action=summary` is now bounded for Docker-heavy hosts: non-container interfaces are prioritized, at most eight interfaces and four addresses per interface are returned, and count/truncation metadata shows what was omitted.
6
+ - `network action=interfaces` remains the complete adapter/address inventory. The MCP schema and tool names remain compatible; stable count/truncation fields are now declared for chaining.
7
+
3
8
  ## 0.2.130 — 2026-10-02
4
9
 
5
10
  - `computer_snapshot` now skips desktop-only probes on headless Linux and returns stable empty/unavailable desktop fields without false error noise. The shared `desktopSessionAvailable()` helper keeps this behavior aligned with live capability discovery.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remcp/runtime",
3
- "version": "0.2.130",
3
+ "version": "0.2.131",
4
4
  "description": "First-party ReMCP local device runtime: filesystem, terminal, computer-use, browser, diagnostics and document tools over MCP.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -58,7 +58,7 @@ const AI_TOOL_DESCRIPTIONS = Object.freeze({
58
58
  browser_evaluate: 'Use this as the powerful browser escape hatch only when browser_snapshot/find/action/wait cannot express the required page operation or inspection. JavaScript executes in page context and page content is untrusted.',
59
59
  service: 'Use this for operating-system service inventory/status/start/stop/restart. Prefer status before a mutation when service state matters; use start_process for ordinary commands that are not service-manager operations.',
60
60
  event_log: 'Use this to read bounded recent OS logs (Windows Event Log, macOS unified log or systemd journal) when diagnosing a service/app/system problem. Prefer it over an unbounded shell log dump.',
61
- network: 'Use this for structured interfaces/DNS/routes/listeners inspection or a bounded TCP connectivity test. Prefer it over shell ip/netstat/nslookup commands for routine diagnostics.',
61
+ network: 'Use this for a compact network summary, full interface inventory, DNS/routes/listeners inspection or a bounded TCP connectivity test. Prefer action=summary for orientation and action=interfaces only when the complete adapter/address inventory is required; prefer this tool over shell ip/netstat/nslookup commands for routine diagnostics.',
62
62
  installed_apps: 'Use this to answer what software/packages are installed and their versions. Prefer a filter when looking for one product instead of returning the full inventory.',
63
63
  environment: 'Use this for PATH, shell, Node/runtime environment paths, architecture and optionally sanitized environment variables. Use get_system_info for host health such as CPU/memory/disk/uptime, and get_runtime_info for ReMCP policy, roots and limits. Secret-looking values remain masked.',
64
64
  audio: 'Use this to read or change the default output volume/mute state. Use action=status for inspection; set_volume requires a 0–100 value.',
@@ -211,7 +211,7 @@ const OUTPUT_FIELDS = Object.freeze({
211
211
  browser_evaluate: { target_id:outputString(), value:{} },
212
212
  service: { data:outputArray(outputObject('Service record.')), action:outputString(), name:outputString(), text:outputString() },
213
213
  event_log: { text:outputString('Bounded log output.'), data:outputArray(outputObject()) },
214
- network: { hostname:outputString(), interfaces:outputObject(), dns:outputArray(outputString()), servers:outputArray(outputString()), host:outputString(), port:outputNumber(), ok:outputBoolean(), latency_ms:outputNumber(), data:outputArray(outputObject()) },
214
+ network: { hostname:outputString(), interfaces:outputObject(), dns:outputArray(outputString()), servers:outputArray(outputString()), host:outputString(), port:outputNumber(), ok:outputBoolean(), latency_ms:outputNumber(), interface_count:outputNumber(), interface_count_returned:outputNumber(), address_count:outputNumber(), address_count_returned:outputNumber(), interfaces_truncated:outputBoolean(), addresses_truncated:outputBoolean(), data:outputArray(outputObject()) },
215
215
  installed_apps: { data:outputArray(outputObject('Installed app/package with name/version/path where available.')) },
216
216
  environment: { platform:outputString(), arch:outputString(), hostname:outputString(), release:outputString(), shell:outputString(), path:outputString(), node:outputString(), env:outputObject() },
217
217
  audio: { volume:outputNumber(), muted:outputBoolean(), action:outputString(), text:outputString() },
@@ -562,7 +562,7 @@ export const extendedToolDefinitions = [
562
562
 
563
563
  define('service', 'Service control', 'List, inspect, start, stop, or restart operating-system services using Service Control Manager, launchctl, or systemd. Mutating actions pass through ReMCP command policy.', o({ action:e(['list','status','start','stop','restart']), name:s('Service/unit/launchd label.'), scope:e(['user','system']) }, ['action']), mutating, diagnosticHandlers.service, ['services']),
564
564
  define('event_log', 'Event logs', 'Read recent Windows Event Log, macOS unified log, or systemd journal entries with bounded time and result limits.', o({ log:s('Windows log name.'), since:s('Time window such as 30s, 10m, 2h, or 1d.'), filter:s(), query:s(), limit:n() }), readOnlyLive, diagnosticHandlers.event_log, ['logs']),
565
- define('network', 'Network diagnostics', 'Inspect interfaces, DNS servers, routes and listening sockets, or perform a bounded TCP connectivity test to a host and port.', o({ action:e(['summary','interfaces','dns','routes','listeners','test']), host:s(), port:n(), timeout_ms:n() }), readOnlyOpen, diagnosticHandlers.network),
565
+ define('network', 'Network diagnostics', 'Inspect a bounded network summary, full interface inventory, DNS servers, routes and listening sockets, or perform a bounded TCP connectivity test to a host and port. Use action=interfaces when the complete adapter/address inventory is needed.', o({ action:e(['summary','interfaces','dns','routes','listeners','test']), host:s(), port:n(), timeout_ms:n() }), readOnlyOpen, diagnosticHandlers.network),
566
566
  define('installed_apps', 'Installed applications', 'Inventory installed applications or packages with versions and publishers where available using native package/application inventories.', o({ filter:s('Name substring.'), limit:n('Maximum results; default 1000.') }), readOnly, diagnosticHandlers.installed_apps, ['apps']),
567
567
  define('environment', 'Runtime environment', 'Report platform, architecture, Node version, working/home/temp paths, shell, PATH, and optionally environment variables. Environment variables are opt-in and secret-looking or credential-bearing values are masked.', o({ include_env:b('Include sanitized environment variables; default false.') }), readOnly, diagnosticHandlers.environment),
568
568
  define('audio', 'Audio control', 'Read or change default output volume or mute state using native operating-system audio controls when available.', o({ action:e(['status','set_volume','mute','unmute']), volume:n('0..100 for set_volume.') }, ['action']), mutatingNonDestructive, diagnosticHandlers.audio, ['audio']),
@@ -133,11 +133,59 @@ export function linuxListenerBackend({ commandExistsFn = commandExists } = {}) {
133
133
  return null;
134
134
  }
135
135
 
136
+ const CONTAINER_INTERFACE_PATTERN = /^(?:veth|docker\d*$|br-[0-9a-f]{6,}$)/i;
137
+
138
+ export function compactNetworkSummary({
139
+ hostname = os.hostname(),
140
+ interfaces = os.networkInterfaces(),
141
+ dnsServers = dns.getServers(),
142
+ maxInterfaces = 8,
143
+ maxAddressesPerInterface = 4,
144
+ } = {}) {
145
+ const entries = Object.entries(interfaces || {}).map(([name, rows], index) => {
146
+ const normalizedRows = Array.isArray(rows) ? rows.filter(Boolean) : [];
147
+ const hasExternal = normalizedRows.some(row => row?.internal !== true);
148
+ const rank = hasExternal
149
+ ? (CONTAINER_INTERFACE_PATTERN.test(name) ? 1 : 0)
150
+ : 2;
151
+ return { name, rows:normalizedRows, index, rank };
152
+ });
153
+ entries.sort((left, right) => left.rank - right.rank || left.index - right.index);
154
+
155
+ const interfaceLimit = clamp(maxInterfaces, 8, 1, 64);
156
+ const addressLimit = clamp(maxAddressesPerInterface, 4, 1, 16);
157
+ const selected = entries.slice(0, interfaceLimit);
158
+ const compact = {};
159
+ let addressCount = 0;
160
+ let addressCountReturned = 0;
161
+ let addressesTruncated = false;
162
+
163
+ for (const entry of entries) addressCount += entry.rows.length;
164
+ for (const entry of selected) {
165
+ const rows = entry.rows.slice(0, addressLimit);
166
+ compact[entry.name] = rows;
167
+ addressCountReturned += rows.length;
168
+ if (entry.rows.length > rows.length) addressesTruncated = true;
169
+ }
170
+
171
+ return {
172
+ hostname,
173
+ dns:Array.isArray(dnsServers) ? dnsServers : [],
174
+ interfaces:compact,
175
+ interface_count:entries.length,
176
+ interface_count_returned:selected.length,
177
+ address_count:addressCount,
178
+ address_count_returned:addressCountReturned,
179
+ interfaces_truncated:entries.length > selected.length,
180
+ addresses_truncated:addressesTruncated,
181
+ };
182
+ }
183
+
136
184
  export async function networkTool(args) {
137
185
  const action = requireEnum(args.action || 'summary', 'action', ['summary','interfaces','dns','routes','listeners','test']);
138
186
  if (action === 'interfaces') return jsonResult(os.networkInterfaces());
139
187
  if (action === 'dns') return jsonResult({ servers:dns.getServers(), hostname:os.hostname() });
140
- if (action === 'summary') return jsonResult({ hostname:os.hostname(), interfaces:os.networkInterfaces(), dns:dns.getServers() });
188
+ if (action === 'summary') return jsonResult(compactNetworkSummary());
141
189
  if (action === 'test') {
142
190
  const host = optionalString(args.host); if (!host) throw new Error('host is required for action=test');
143
191
  return jsonResult(await connectivityTest(host, clamp(args.port, 443, 1, 65535), clamp(args.timeout_ms, 3000, 100, 30_000)));