@remcp/remcp 0.2.49 → 0.2.50

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/agent.mjs +27 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remcp/remcp",
3
- "version": "0.2.49",
3
+ "version": "0.2.50",
4
4
  "description": "ReMCP device client: pair a computer with ReMCP and run the outbound-only agent that hosts the local MCP runtime.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/agent.mjs CHANGED
@@ -36,6 +36,8 @@ const RUNTIME_STDIO_BUFFER_BYTES = 24 * 1024 * 1024;
36
36
  const RECONNECT_MAX_MS = 60_000;
37
37
  const RUNTIME_RESTART_BASE_MS = 1_000;
38
38
  const RUNTIME_RESTART_MAX_MS = 30_000;
39
+ const RUNTIME_TOOLS_RETRY_BASE_MS = 1_000;
40
+ const RUNTIME_TOOLS_RETRY_MAX_MS = 30_000;
39
41
  // Below the relay's RPC timeout so the model gets a real error instead of a client-side
40
42
  // timeout while the device keeps working invisibly.
41
43
  const CALL_TIMEOUT_MARGIN_MS = 10_000;
@@ -109,6 +111,8 @@ export async function runAgent(options) {
109
111
  let transport = null;
110
112
  let runtimeRestartDelay = RUNTIME_RESTART_BASE_MS;
111
113
  let runtimeRestartTimer = null;
114
+ let runtimeToolsRetryDelay = RUNTIME_TOOLS_RETRY_BASE_MS;
115
+ let runtimeToolsRetryTimer = null;
112
116
  let stopPromise = null;
113
117
 
114
118
  function runtimeEnv() {
@@ -125,16 +129,31 @@ export async function runAgent(options) {
125
129
  return [...new Set(tools.map(tool => String(tool?.name || '').trim().slice(0, 128)).filter(Boolean))].slice(0, 256);
126
130
  }
127
131
 
132
+ function scheduleRuntimeToolsRetry(client) {
133
+ if (stopping || runtimeDown || mcp !== client || runtimeToolsRetryTimer) return;
134
+ const delay = jitter(runtimeToolsRetryDelay);
135
+ runtimeToolsRetryDelay = Math.min(RUNTIME_TOOLS_RETRY_MAX_MS, runtimeToolsRetryDelay * 2);
136
+ runtimeToolsRetryTimer = setTimeout(() => {
137
+ runtimeToolsRetryTimer = null;
138
+ void refreshRuntimeTools(client);
139
+ }, delay);
140
+ runtimeToolsRetryTimer.unref?.();
141
+ }
142
+
128
143
  async function refreshRuntimeTools(client = mcp, { announce = true } = {}) {
129
144
  if (!client || runtimeDown) return runtimeTools;
130
145
  try {
131
146
  const listed = await client.listTools(undefined, { timeout: 3000 });
147
+ if (runtimeToolsRetryTimer) clearTimeout(runtimeToolsRetryTimer);
148
+ runtimeToolsRetryTimer = null;
149
+ runtimeToolsRetryDelay = RUNTIME_TOOLS_RETRY_BASE_MS;
132
150
  const next = toolNames(listed);
133
151
  if (next.length === runtimeTools.length && next.every((name, index) => name === runtimeTools[index])) return runtimeTools;
134
152
  runtimeTools = next;
135
153
  if (announce) send({ type: 'capabilities', runtimeTools });
136
154
  } catch (error) {
137
155
  console.error(`ReMCP could not refresh runtime capabilities: ${error instanceof Error ? error.message : String(error)}`);
156
+ scheduleRuntimeToolsRetry(client);
138
157
  }
139
158
  return runtimeTools;
140
159
  }
@@ -143,6 +162,9 @@ export async function runAgent(options) {
143
162
  if (stopping) return;
144
163
  clearTimeout(runtimeRestartTimer);
145
164
  runtimeRestartTimer = null;
165
+ if (runtimeToolsRetryTimer) clearTimeout(runtimeToolsRetryTimer);
166
+ runtimeToolsRetryTimer = null;
167
+ runtimeToolsRetryDelay = RUNTIME_TOOLS_RETRY_BASE_MS;
146
168
  runtimeDown = true;
147
169
  const previous = mcp;
148
170
  mcp = null;
@@ -197,6 +219,9 @@ export async function runAgent(options) {
197
219
 
198
220
  function handleRuntimeExit(reason) {
199
221
  if (stopping || runtimeRestartTimer) return;
222
+ if (runtimeToolsRetryTimer) clearTimeout(runtimeToolsRetryTimer);
223
+ runtimeToolsRetryTimer = null;
224
+ runtimeToolsRetryDelay = RUNTIME_TOOLS_RETRY_BASE_MS;
200
225
  runtimeDown = true;
201
226
  runtimeTools = [];
202
227
  send({ type: 'capabilities', runtimeTools });
@@ -520,6 +545,8 @@ export async function runAgent(options) {
520
545
  if (stopPromise) return stopPromise;
521
546
  stopping = true;
522
547
  clearTimeout(runtimeRestartTimer);
548
+ clearTimeout(runtimeToolsRetryTimer);
549
+ runtimeToolsRetryTimer = null;
523
550
  clearTimeout(reconnectTimer);
524
551
  reconnectTimer = null;
525
552
  if (telemetryTimer) clearInterval(telemetryTimer);