Skip to content

feat: add argent server start for long-lived tool-server#182

Open
filip131311 wants to merge 1 commit intomainfrom
feat/argent-server-start
Open

feat: add argent server start for long-lived tool-server#182
filip131311 wants to merge 1 commit intomainfrom
feat/argent-server-start

Conversation

@filip131311
Copy link
Copy Markdown
Collaborator

@filip131311 filip131311 commented May 4, 2026

Human summary:

This adds argent server start command to enable us to spawn tools server in arbitrary location. we want this to enable using remote tools-server via our mcp not only local ones.

Summary

  • Adds argent server start to spawn a tool-server that does not auto-shutdown on idle, with configurable bind port and host — the missing piece for running tool-server on a remote/dedicated host.
  • Foreground by default so process supervisors (systemd, Docker, supervisord) own the lifecycle; --detach keeps the existing daemon-spawn behavior. Refuses to clobber a healthy running server unless --force.
  • Phase 2 (consumer-side --tools-url flag pointing the MCP / CLI at a remote tool-server) is now a one-flag change away — tools-client.ts and mcp-server.ts already honor ARGENT_TOOLS_URL. Out of scope for this PR.

What changed

  • tool-server — read HOST env var (default 127.0.0.1 so the existing auto-spawn path is unaffected), bind to it, attach an error listener on app.listen so EADDRINUSE / EACCES exit cleanly instead of routing through uncaughtException.
  • argent-tools-client — broaden the readiness regex to match any host; extend buildToolsServerEnv with optional { host, idleTimeoutMinutes }; record host in ~/.argent/tool-server.json (backward-compat: missing field defaults to 127.0.0.1); export spawnToolsServer, findFreePort, isToolsServerHealthy, isToolsServerProcessAlive, the state read/write/clear helpers, and formatToolsServerUrl so the CLI can reuse them.
  • argent-cli — new server start [flags]: --port/-p (default 3001, 0 = pick free), --host (default 127.0.0.1), --idle-timeout (default 0 = never), --detach/-d, --force, --help. Warns when host is non-loopback. Foreground mode forwards SIGINT/SIGTERM to the child and writes the state file so local argent run / argent mcp can attach.
  • argent dispatcher — pass BUNDLED_RUNTIME_PATHS to server() so the new subcommand can find the bundled tool-server.

Test plan

  • argent server --help lists the new start subcommand
  • argent server start --help shows full flag reference
  • argent server start --detach --port 0 → status alive/healthy, /tools reachable, state file populated with host field
  • Second argent server start --detach refuses (exit 1) with helpful message
  • argent server start --detach --force kills the old PID and starts a new one
  • Foreground argent server start --port 0 runs with inherited stdio; child PID is the actual tool-server (not the wrapper); argent server stop from a sibling shell tears down both
  • Existing auto-spawn path (argent tools) still works — verifies the regex broadening + default-host change is backward-compatible
  • tool-server (444 tests) + argent-tools-client (1) + argent dispatcher (1) suites pass
  • Manual smoke on --host 0.0.0.0 — could not exercise locally (sandbox declined the public-interface bind); the warning + bind path is identical to loopback besides the listen address

Adds a CLI command to spawn a tool-server that does not auto-shutdown on
idle, with configurable port and bind host. Lays the groundwork for
remote tool-server connections (consumer side already honors
ARGENT_TOOLS_URL).

- tool-server: read HOST env var (default 127.0.0.1), bind to it,
  surface bind errors (EADDRINUSE / EACCES) cleanly.
- tools-client: broaden readiness regex to any host; extend
  buildToolsServerEnv with host/idleTimeoutMinutes; add `host` to the
  state file; export spawn/state/health helpers for CLI reuse.
- argent-cli: implement `server start [--port|-p] [--host]
  [--idle-timeout] [--detach|-d] [--force]`. Foreground by default for
  process supervisors; --detach reuses the daemon spawn path. Refuses
  to start when a healthy server is already running unless --force.
  Warns when binding to a non-loopback host.
- dispatcher: thread BUNDLED_RUNTIME_PATHS through to `server`.
Comment on lines +65 to +67
// HOST defaults to loopback so the local auto-spawn path stays safe.
// `argent server start --host 0.0.0.0` is the opt-in for remote exposure.
const HOST = process.env.HOST ?? "127.0.0.1";
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would change both the PORT and HOST env varibales in all the code references to ARGENT_HOST and ARGENT_PORT. There is a chance someone will have defined HOST env variable in their shell and this will cause hard-to-reproduce problems.

Comment on lines +112 to +114
process.stdout.write(`Tools server listening on http://${HOST}:${boundPort}\n`);
process.stderr.write(` GET http://${HOST}:${boundPort}/tools\n`);
process.stderr.write(` POST http://${HOST}:${boundPort}/tools/:name\n`);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tiny NIT, because it is only user-facing, but if host is ipv6 the authority part should be written in square brackets like this:

http://[::1]:3001

Comment on lines +368 to +371
if (stateWritten) {
clearToolsServerState().catch(() => {
/* non-fatal */
});
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The writeToolServerState is asynchronous, so there could be a situation when:

  • parent spawns child tool-server
  • parent starts async writeToolsServerState, which is not awaited
  • child may exit quickly (bind fail for example)
  • exit handler runs and only clears state if stateWritten === true
  • if write resolves after exit handler, stale state file remains, which can cause agent confusion as the data will be referencing a server that has failed at startup

or it may be fine, hard to tell, but this seems like a real edge-case

@pFornagiel
Copy link
Copy Markdown
Collaborator

Taking it from here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants