Skip to content

feat(mcp-server): agentUrl option to route MCP tool calls internally#1740

Open
Scra3 wants to merge 2 commits into
mainfrom
feat/mcp-server-internal-agent-url
Open

feat(mcp-server): agentUrl option to route MCP tool calls internally#1740
Scra3 wants to merge 2 commits into
mainfrom
feat/mcp-server-internal-agent-url

Conversation

@Scra3

@Scra3 Scra3 commented Jul 7, 2026

Copy link
Copy Markdown
Member

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 via agent-client. The URL used is the environment's public api_endpoint (as registered in Forest), read from AuthInfo.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 agentUrl option that overrides only the internal tool→agent channel:

agent.mountAiMcpServer({ agentUrl: 'http://forest-agent.internal:3310' });
  • forest-oauth-provider.ts — new agentUrl (validated as an absolute URL, trailing slash stripped); verifyAccessToken puts agentUrl ?? environmentApiEndpoint into extra.environmentApiEndpoint (the single consumer, in agent-caller.ts). getBaseUrl() untouched.
  • server.tsForestMCPServerOptions.agentUrl, threaded to the provider.
  • cli.tsFOREST_AGENT_URL env var for the standalone binary.
  • agent.tsmountAiMcpServer({ agentUrl }) threading.

The advertised OAuth URLs (issuer, .well-known metadata, authorize/token) stay public — external MCP clients (n8n) still authenticate from outside. agentUrl only changes where tools reach the agent.

Same concept and name as addWorkflowExecutor({ agentUrl }). Default unchanged (public api_endpoint), so zero impact on existing deployments.

Tests

  • forest-oauth-provider.test.tsextra.environmentApiEndpoint = agentUrl when set (fallback to api_endpoint otherwise), trailing slash stripped, invalid URL throws.
  • server.test.ts — with agentUrl set, advertised OAuth metadata URLs stay public.
  • agent.test.tsmountAiMcpServer({ agentUrl }) reaches ForestMCPServer.

mcp-server: 608 tests pass, lint clean.

Definition of Done

General

  • Explicit title following Conventional Commits
  • Test manually the implemented changes
  • Validate the code quality

Security

  • Consider the security impact — agentUrl is validated; default behavior unchanged; the option improves security posture (keeps tool traffic off the public internet).

🤖 Generated with Claude Code

Note

Add agentUrl option to ForestMCPServer to route MCP tool calls to a self-hosted agent

  • Adds an optional agentUrl to ForestMCPServerOptions and ForestOAuthProvider that, when set, overrides the public api_endpoint embedded in OAuth tokens as the tool callback base URL.
  • Exposes the option through Agent.mountAiMcpServer (stored and forwarded on Agent.start) and through the CLI via the FOREST_AGENT_URL environment variable.
  • ForestOAuthProvider.normalizeAgentUrl validates that the value is an absolute http/https URL with no query string or fragment, and strips trailing slashes; invalid values throw at construction time.
  • OAuth metadata URLs are unaffected by agentUrl — only the per-token environmentApiEndpoint changes.
  • Risk: passing an invalid agentUrl now throws during ForestOAuthProvider construction, which is a new failure mode for existing callers that forward arbitrary user input.

Macroscope summarized c476514.

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>
Comment thread packages/mcp-server/src/forest-oauth-provider.ts
@qltysh

qltysh Bot commented Jul 7, 2026

Copy link
Copy Markdown

Qlty


Coverage Impact

This PR will not change total coverage.

Modified Files with Diff Coverage (3)

RatingFile% DiffUncovered Line #s
Coverage rating: A Coverage rating: A
packages/agent/src/agent.ts100.0%
Coverage rating: A Coverage rating: A
packages/mcp-server/src/forest-oauth-provider.ts100.0%
Coverage rating: A Coverage rating: A
packages/mcp-server/src/server.ts100.0%
Total100.0%
🚦 See full report on Qlty Cloud »

🛟 Help
  • Diff Coverage: Coverage for added or modified lines of code (excludes deleted files). Learn more.

  • Total Coverage: Coverage for the whole repository, calculated as the sum of all File Coverage. Learn more.

  • File Coverage: Covered Lines divided by Covered Lines plus Missed Lines. (Excludes non-executable lines including blank lines and comments.)

    • Indirect Changes: Changes to File Coverage for files that were not modified in this PR. Learn more.

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>
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.

1 participant