feat(mcp-server): agentUrl option to route MCP tool calls internally#1740
feat(mcp-server): agentUrl option to route MCP tool calls internally#1740Scra3 wants to merge 5 commits into
Conversation
MCP tools call back into the agent's data layer over HTTP. By default the URL is the environment's public api_endpoint, so tool calls leave over the public internet even when the MCP server is mounted inside the agent. Add an opt-in agentUrl option (mountAiMcpServer, ForestMCPServerOptions, FOREST_AGENT_URL for the CLI) that overrides only the internal tool->agent channel — the advertised OAuth discovery URLs stay public so external MCP clients still authenticate. Requested by a self-hosted customer (Swaive) who wants MCP callbacks to stay on their private network. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Coverage Impact This PR will not change total coverage. Modified Files with Diff Coverage (3)
🛟 Help
|
Review follow-ups on the agentUrl option:
- normalizeAgentUrl now parses once, rejects non-http(s) schemes and URLs with
a query string or fragment (which would swallow the request path in
agent-client's `${url}${path}` join), and returns the parsed, whitespace-
normalized form instead of the raw string. Previously such inputs passed
validation and silently misrouted every tool call.
- Add an end-to-end test proving a `tools/call` actually targets agentUrl and
not the environment's public api_endpoint.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fix: extract normalizeAgentUrl to a shared/exported helper and call it in the ForestMCPServer constructor so invalid values throw eagerly, proximate to the misconfiguration. forest-oauth-provider.test.ts already tests the throw via the direct constructor path, so moving the throw site earlier won't break existing tests.
|
…ctor Extract normalizeAgentUrl to a shared helper and call it from the ForestMCPServer constructor, so a malformed agentUrl / FOREST_AGENT_URL throws at construction — proximate to the misconfiguration — instead of later inside ForestOAuthProvider during run()/start(). Add a verifyAccessToken test for the fallback path (no agentUrl -> environment api_endpoint). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thanks @ShohanRahman — both addressed in c26d56b:
613 tests green, coverage 100%. |
|
No direct unit test for normalize-agent-url.ts; https:// and path-preserving URLs uncovered Confirmed: no test/utils/normalize-agent-url.test.ts exists, and every test agentUrl is http://. Two silent-regression risks:
Add a focused unit test covering: valid https://, path-preserving base, trailing-slash strip, empty/undefined → undefined, and the four throw cases. Also worth a new ForestMCPServer({ agentUrl: 'not-a-url' }) throws test in server.test.ts to lock the eager-fail guarantee (currently only proven via the provider test). |
ForestMCPServer is now the sole validator/normalizer of agentUrl; the internal ForestOAuthProvider accepts a pre-normalized value instead of re-running normalizeAgentUrl. Move the validation/normalization unit tests (invalid inputs, trailing slash, http/https, path preservation) into a dedicated normalize-agent-url.test.ts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@ShohanRahman both addressed in 0507fb9:
616 tests green. |
Drop the agentUrl option from agent.mountAiMcpServer() — mounted mode will dispatch tool calls to the agent in-process (follow-up), making a callback URL unnecessary. agentUrl stays available for the standalone MCP server via FOREST_AGENT_URL / ForestMCPServerOptions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Context
MCP tools don't touch the datasource directly — each tool call (
list,create,executeAction…) calls back into the agent's data layer over HTTP viaagent-client. The URL used is the environment's publicapi_endpoint(as registered in Forest), read fromAuthInfo.extra.environmentApiEndpoint.Consequence: even when the MCP server is mounted inside the agent (same process), every tool call leaves over the public internet and comes back. A self-hosted customer (Swaive, KYB prod use case with n8n) asked to keep this traffic on their private network.
Change
Opt-in
agentUrloption that overrides only the internal tool→agent channel:forest-oauth-provider.ts— newagentUrl(validated as an absolute URL, trailing slash stripped);verifyAccessTokenputsagentUrl ?? environmentApiEndpointintoextra.environmentApiEndpoint(the single consumer, inagent-caller.ts).getBaseUrl()untouched.server.ts—ForestMCPServerOptions.agentUrl, threaded to the provider.cli.ts—FOREST_AGENT_URLenv var for the standalone binary.agent.ts—mountAiMcpServer({ agentUrl })threading.The advertised OAuth URLs (issuer,
.well-knownmetadata, authorize/token) stay public — external MCP clients (n8n) still authenticate from outside.agentUrlonly changes where tools reach the agent.Same concept and name as
addWorkflowExecutor({ agentUrl }). Default unchanged (publicapi_endpoint), so zero impact on existing deployments.Tests
forest-oauth-provider.test.ts—extra.environmentApiEndpoint=agentUrlwhen set (fallback toapi_endpointotherwise), trailing slash stripped, invalid URL throws.server.test.ts— withagentUrlset, advertised OAuth metadata URLs stay public.agent.test.ts—mountAiMcpServer({ agentUrl })reachesForestMCPServer.mcp-server: 608 tests pass, lint clean.
Definition of Done
General
Security
agentUrlis validated; default behavior unchanged; the option improves security posture (keeps tool traffic off the public internet).🤖 Generated with Claude Code
Note
Add
agentUrloption toForestMCPServerto route MCP tool calls internallyagentUrltoForestMCPServer(and propagates it throughAgent.mountAiMcpServerand the CLI viaFOREST_AGENT_URL) so tool callbacks can be directed to an internal agent URL instead of the publicapi_endpoint.normalizeAgentUrlutility validates that the URL is an absolutehttp(s)URL with no query string or fragment, stripping trailing slashes; invalid values throw at construction time.ForestOAuthProviderplacesagentUrlinextra.environmentApiEndpointduring token verification, overriding the environment'sapi_endpointfor tool routing only — OAuth metadata endpoints are unaffected.agentUrlthrows an error at server startup.Changes since #1740 opened
agentUrlparameter support fromAgent.mountAiMcpServermethod and internal MCP server instantiation [0327a3c]agentUrlpassthrough in MCP server configuration [0327a3c]agentUrlinForestMCPServerOptionsinterface [0327a3c]Macroscope summarized 0507fb9.