@remcp/runtime 0.2.5 → 0.2.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remcp/runtime",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "First-party ReMCP local device runtime: file, search, terminal and process tools over MCP for computers paired with ReMCP.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/catalog.mjs CHANGED
@@ -4,6 +4,17 @@ import { terminalToolHandlers } from './tools/terminal.mjs';
4
4
  import { systemToolHandlers } from './tools/system.mjs';
5
5
  import { statsToolHandlers } from './tools/stats.mjs';
6
6
 
7
+ // Every tool answers with a text result (image tools add an image part as well), so the
8
+ // declared output schema is the same shape everywhere and clients can rely on it.
9
+ export const TEXT_OUTPUT_SCHEMA = {
10
+ type: 'object',
11
+ properties: {
12
+ text: { type: 'string', description: 'Human-readable result of the tool call.' },
13
+ },
14
+ required: ['text'],
15
+ additionalProperties: false,
16
+ };
17
+
7
18
  const readOnly = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
8
19
  const readOnlyNonIdempotent = { readOnlyHint: true, destructiveHint: false, idempotentHint: false, openWorldHint: false };
9
20
  const additive = { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false };
@@ -726,4 +737,15 @@ export const toolDefinitions = [
726
737
  },
727
738
  ];
728
739
 
740
+ export function advertisedTools() {
741
+ return toolDefinitions.map(({ name, title, description, inputSchema, annotations, outputSchema }) => ({
742
+ name,
743
+ title,
744
+ description,
745
+ inputSchema,
746
+ annotations,
747
+ outputSchema: outputSchema || TEXT_OUTPUT_SCHEMA,
748
+ }));
749
+ }
750
+
729
751
  export const toolHandlers = new Map(toolDefinitions.map(definition => [definition.name, definition]));
package/src/index.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  import process from 'node:process';
3
3
  import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
4
4
  import path from 'node:path';
5
- import { toolDefinitions } from './catalog.mjs';
5
+ import { advertisedTools, toolDefinitions } from './catalog.mjs';
6
6
  import { describeConfig, configurationError, runtimeConfigDir } from './config.mjs';
7
7
  import { invokeTool } from './invoke.mjs';
8
8
  import { shutdownSessions, startSessionSweeper } from './sessions.mjs';
@@ -40,7 +40,7 @@ if (args.includes('--version')) {
40
40
  }
41
41
 
42
42
  if (args.includes('--print-tools')) {
43
- process.stdout.write(`${JSON.stringify(toolDefinitions.map(({ name, title, description, inputSchema, annotations }) => ({ name, title, description, inputSchema, annotations })), null, 2)}\n`);
43
+ process.stdout.write(`${JSON.stringify(advertisedTools(), null, 2)}\n`);
44
44
  process.exit(0);
45
45
  }
46
46
 
@@ -86,9 +86,7 @@ const server = new Server(
86
86
  },
87
87
  );
88
88
 
89
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
90
- tools: toolDefinitions.map(({ name, title, description, inputSchema, annotations }) => ({ name, title, description, inputSchema, annotations })),
91
- }));
89
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: advertisedTools() }));
92
90
 
93
91
  server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
94
92
  return invokeTool(request.params.name, request.params.arguments, extra);
package/src/util.mjs CHANGED
@@ -114,7 +114,9 @@ export function truncate(text, maxBytes) {
114
114
 
115
115
  export function text(value, isError = false) {
116
116
  const body = typeof value === 'string' ? value : JSON.stringify(value, null, 2);
117
- return { content: [{ type: 'text', text: truncate(body) }], ...(isError ? { isError: true } : {}) };
117
+ const rendered = truncate(body);
118
+ // structuredContent mirrors the text so a client can rely on the declared outputSchema.
119
+ return { content: [{ type: 'text', text: rendered }], structuredContent: { text: rendered }, ...(isError ? { isError: true } : {}) };
118
120
  }
119
121
 
120
122
  export function image(data, mimeType) {
@@ -122,7 +124,8 @@ export function image(data, mimeType) {
122
124
  }
123
125
 
124
126
  export function multi(parts) {
125
- return { content: parts };
127
+ const summary = parts.find(part => part.type === 'text')?.text ?? '';
128
+ return { content: parts, structuredContent: { text: summary } };
126
129
  }
127
130
 
128
131
  export function splitLines(value) {