@remcp/runtime 0.2.64 → 0.2.66

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
@@ -52,8 +52,8 @@ support and emits `notifications/tools/list_changed` when that capability set ch
52
52
  The hosted ReMCP endpoint adds eleven account/presentation tools — `list_devices`, `ping_device`,
53
53
  `who_am_i`, `create_pairing_command`, `get_recent_tool_calls`, `get_usage_statistics`,
54
54
  `get_configuration`, `render_file_preview`, `render_image_preview`, `render_terminal_preview`, and
55
- `rename_device` — for a 94-tool MCP Apps catalog. Plain MCP clients receive 91 tools because the
56
- three presentation-only render tools are omitted unless the UI extension is negotiated.
55
+ `rename_device` — for a 94-tool MCP Apps catalog. Native-only deployments receive 91 tools because the
56
+ three presentation-only render tools are disabled by configuration.
57
57
 
58
58
  `computer_snapshot` and `computer_action` keep OCR inside the compact high-level surface: when a local `tesseract` binary is available, snapshot can return bounded OCR text/boxes and click targeting can fall back through Accessibility → browser DOM/CDP → OCR → coordinates. OCR is optional and never adds another MCP tool or bundled OCR dependency.
59
59
 
@@ -73,9 +73,9 @@ A running device can advertise a smaller capability-aware subset; the runtime de
73
73
  agent forwards the refreshed tool names to the hosted relay. Browser/CDP tools are the primary dynamic
74
74
  example: they are enabled only while the runtime can use a supported loopback browser endpoint.
75
75
 
76
- The generated hosted reference covers the full **94-tool UI-capable surface**: 83 device tools,
77
- 8 account/fleet tools, and 3 chat-presentation tools. Clients without MCP Apps negotiation receive
78
- the 91-tool native surface. See https://remcp.site/docs#tools and public `docs/TOOLS.md`.
76
+ The generated hosted reference covers the full **94-tool production surface** with custom widgets enabled: 83 device tools,
77
+ 8 account/fleet tools, and 3 chat-presentation tools. Setting `REMCP_CUSTOM_WIDGETS_ENABLED=false` on the hosted server exposes
78
+ the 91-tool native-only surface. See https://remcp.site/docs#tools and public `docs/TOOLS.md`.
79
79
 
80
80
  ## No approval staircase
81
81
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remcp/runtime",
3
- "version": "0.2.64",
3
+ "version": "0.2.66",
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",
@@ -28,8 +28,7 @@
28
28
  "@modelcontextprotocol/server": "^2.0.0"
29
29
  },
30
30
  "devDependencies": {
31
- "@modelcontextprotocol/client": "^2.0.0",
32
- "@modelcontextprotocol/sdk": "^1.30.0"
31
+ "@modelcontextprotocol/client": "^2.0.0"
33
32
  },
34
33
  "repository": {
35
34
  "type": "git",
@@ -17,7 +17,7 @@ import {
17
17
  waitForProcessExit,
18
18
  waitForProcessActivity,
19
19
  } from '../sessions.mjs';
20
- import { clampInteger, fail, requireInteger, requireString, structured, text } from '../util.mjs';
20
+ import { clampInteger, fail, requireInteger, requireString, structured, text, truncate } from '../util.mjs';
21
21
 
22
22
  function shellCommand() {
23
23
  if (runtimeConfig.defaultShell) return runtimeConfig.defaultShell;
@@ -51,6 +51,21 @@ function throwIfAborted(signal) {
51
51
  if (signal?.aborted) throw abortError();
52
52
  }
53
53
 
54
+ // Terminal results have strict tool-specific output schemas. The generic structured() helper uses a
55
+ // schema-agnostic {truncated,bytes,preview} envelope above 256 KiB, which is valid for extended
56
+ // free-form tools but invalid for start_process/read_process_output and friends. Bound the few
57
+ // potentially large terminal strings before mirroring them so structuredContent always keeps the
58
+ // declared terminal shape.
59
+ function terminalStructured(value, fallbackText) {
60
+ const bounded = { ...value };
61
+ if (typeof bounded.output === 'string') bounded.output = truncate(bounded.output, 128 * 1024);
62
+ if (typeof bounded.partial === 'string') bounded.partial = truncate(bounded.partial, 64 * 1024);
63
+ if (typeof bounded.command === 'string') bounded.command = truncate(bounded.command, 16 * 1024);
64
+ if (typeof bounded.warning === 'string') bounded.warning = truncate(bounded.warning, 8 * 1024);
65
+ if (typeof bounded.pattern === 'string') bounded.pattern = truncate(bounded.pattern, 16 * 1024);
66
+ return structured(bounded, fallbackText);
67
+ }
68
+
54
69
  export async function startProcessTool(args, extra = {}) {
55
70
  const command = requireString(args.command, 'command');
56
71
  const verdict = assertAllowedCommand(command);
@@ -101,7 +116,7 @@ export async function startProcessTool(args, extra = {}) {
101
116
  const warning = verdict.note ? String(verdict.note) : '';
102
117
  const rendered = [warning, headline, output, partial].filter(Boolean).join('\n');
103
118
  const status = describeSession(session);
104
- return structured({
119
+ return terminalStructured({
105
120
  ...status,
106
121
  command,
107
122
  output,
@@ -141,7 +156,7 @@ export async function readProcessOutputTool(args, extra = {}) {
141
156
  const status = describeSession(session);
142
157
  const header = `pid ${pid} ${status.status} · lines ${range}`;
143
158
  const output = slice.join('\n');
144
- return structured({
159
+ return terminalStructured({
145
160
  ...status,
146
161
  range,
147
162
  output,
@@ -187,7 +202,7 @@ export async function waitForProcessOutputTool(args, extra = {}) {
187
202
  ? `pid ${pid} ${status.status} · pattern matched${bufferedMatch ? ' (already buffered)' : ''}`
188
203
  : `pid ${pid} ${status.status} · pattern not matched within ${timeoutMs}ms`;
189
204
  const output = slice.join('\n');
190
- return structured({
205
+ return terminalStructured({
191
206
  ...status,
192
207
  pattern,
193
208
  matched,
@@ -226,7 +241,7 @@ export async function interactWithProcessTool(args, extra = {}) {
226
241
  const slice = readNewOutput(session);
227
242
  const status = describeSession(session);
228
243
  const output = slice.join('\n');
229
- return structured({
244
+ return terminalStructured({
230
245
  ...status,
231
246
  output,
232
247
  }, [`pid ${pid} ${status.status}`, output].filter(Boolean).join('\n'));
@@ -238,7 +253,7 @@ export async function forceTerminateTool(args) {
238
253
  if (!session) fail(`No ReMCP session with pid ${pid}`);
239
254
  if (session.exited) {
240
255
  const status = describeSession(session);
241
- return structured({ ...status, terminated:false, alreadyExited:true, escalated:false }, `Process ${pid} already exited.`);
256
+ return terminalStructured({ ...status, terminated:false, alreadyExited:true, escalated:false }, `Process ${pid} already exited.`);
242
257
  }
243
258
  killSessionTree(session, 'SIGTERM');
244
259
  const deadline = Date.now() + 2000;
@@ -250,7 +265,7 @@ export async function forceTerminateTool(args) {
250
265
  await waitForProcessActivity(session, 1000);
251
266
  }
252
267
  const status = describeSession(session);
253
- return structured(
268
+ return terminalStructured(
254
269
  { ...status, terminated:Boolean(session.exited), alreadyExited:false, escalated },
255
270
  `Terminated session ${pid}${status.status.startsWith('exited') ? '' : ' (still running)'}. Status: ${status.status}.`,
256
271
  );
@@ -263,11 +278,11 @@ export async function listSessionsTool() {
263
278
  const blocked = session.exited ? '' : session.partial ? 'blocked-possibly' : 'idle-or-running';
264
279
  return { ...status, blocked, command:session.command.slice(0, 120) };
265
280
  });
266
- if (!facts.length) return structured({ sessions:[] }, 'No active terminal sessions.');
281
+ if (!facts.length) return terminalStructured({ sessions:[] }, 'No active terminal sessions.');
267
282
  const rows = facts.map(session =>
268
283
  `pid ${session.pid} · ${session.status} · ${Math.round(session.runtimeMs / 1000)}s · ${session.blocked} · ${session.command}`
269
284
  );
270
- return structured({ sessions:facts }, rows.join('\n'));
285
+ return terminalStructured({ sessions:facts }, rows.join('\n'));
271
286
  }
272
287
 
273
288
  export const terminalToolHandlers = {