docs(workflows): say what to check when MCP tools don't load - #23
docs(workflows): say what to check when MCP tools don't load#23hercemer42 wants to merge 2 commits into
Conversation
An empty tool list, a rejected credential and an unreachable server are the three ways a connector fails, and they need three different fixes. Forest Runtime now names which one it was in its logs, so point readers at that rather than leaving "check that all the expected tools are exposed" as the only guidance. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
| Forest Runtime logs the reason at `Error`, so it is in your logs without changing `LOG_LEVEL`: | ||
|
|
||
| ```json | ||
| { |
There was a problem hiding this comment.
🟡 Medium workflows/forest-runtime.mdx:247
The documented JSON example shows a failures array with server, kind, and error fields, but the actual production log emits requestedMcpServerId, mcpServerName, and failedConfigNames — not failures. The kind field does not exist in the log, so operators following the guidance to use kind to distinguish auth, connection, and unknown failures cannot find it. Update the example to match the real payload, or change the logging to include the kind field.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @product/process/workflows/forest-runtime.mdx around line 247:
The documented JSON example shows a `failures` array with `server`, `kind`, and `error` fields, but the actual production log emits `requestedMcpServerId`, `mcpServerName`, and `failedConfigNames` — not `failures`. The `kind` field does not exist in the log, so operators following the guidance to use `kind` to distinguish `auth`, `connection`, and `unknown` failures cannot find it. Update the example to match the real payload, or change the logging to include the `kind` field.
A symptom-titled troubleshooting section restated the runtime page's failure taxonomy in product language, so the same knowledge lived on two pages and would drift. Only the empty-list meaning was new, and it belongs where the reader meets it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
PMerlet
left a comment
There was a problem hiding this comment.
Adversarial pass: I checked every claim against the executor code (agent-nodejs#1806 head) and ai-proxy on main. The mechanics check out — kind values, the 15s per-server timeout (LOAD_TOOLS_TIMEOUT_MS), the Debug per-server line, Error-at-default-level, and the empty-list behavior all match the code. forestadmin#9892 validates the UI claim in mcp-servers.mdx (empty list → no banner, 503 → banner).
One PR-level issue: the description promises a section that isn't in the diff. It announces a new "A connector's tools don't load" section in mcp-servers.mdx naming the three failure modes and pointing self-hosters at the runtime logs — but the actual diff on that file is a single sentence (+1/−1). Either the section was dropped without updating the description, or a commit is missing. Worth reconciling before merge, since the cross-page story the description tells doesn't exist.
Inline comments cover the rest — the only real content issue is the auth bullet vs the OAuth auto-refresh flow.
|
|
||
| The Docker image ships with [OpenTelemetry](https://opentelemetry.io/) APM built in, compatible with any OTLP backend (Datadog, Grafana Tempo, Jaeger, Honeycomb…). It is **off by default** and turns on as soon as you set `OTEL_EXPORTER_OTLP_ENDPOINT`. OpenTelemetry is bundled only in the Docker image, not the npm package. | ||
|
|
||
| ### When an MCP step can't load its tools |
There was a problem hiding this comment.
Terminology nit: this page (and the workflows overview) calls the step type "MCP Tasks" — see line 212, "guidance, decisions, MCP Tasks, and AI-assisted data steps". Suggest "When an MCP Task can't load its tools" for consistency.
|
|
||
| ### When an MCP step can't load its tools | ||
|
|
||
| Forest Runtime logs the reason at `Error`, so it is in your logs without changing `LOG_LEVEL`: |
There was a problem hiding this comment.
Sequencing caveat: merged ≠ released. This doc goes live when this PR merges, but the failures shape only reaches a self-hoster once agent-nodejs#1806 ships in a released @forestadmin/workflow-executor / Docker image and they upgrade. On earlier images the same event logs failedConfigNames instead, and an empty tool list from a healthy server is flagged as a failure (the old inference). If the merge→release gap is short, fine — otherwise consider a minimum-version note here.
| { | ||
| "level": "Error", | ||
| "message": "MCP servers failed to load tools", | ||
| "mcpServerName": "acme-crm", |
There was a problem hiding this comment.
This example is trimmed compared to what the logger actually emits: the console logger adds "timestamp" and this event also carries "requestedMcpServerId" (the executor README's own example keeps it). Since the intro sells this as the line "as it appears", either show the full shape or say fields were elided.
Also worth a clause: the JSON shape is the non-TTY output (the Docker default). A TTY run gets the pretty logger's HH:MM:SS error … format instead.
|
|
||
| `kind` tells you where to look: | ||
|
|
||
| - `auth` — the server rejected the credential. Reconnect the connector, or renew its token. |
There was a problem hiding this comment.
This bullet doesn't survive contact with the OAuth path in remote-tool-fetcher.ts:
- On an OAuth connector, a 401 triggers an automatic token refresh + one retry. If the retry succeeds, the executor logs
Info"MCP tools loaded after refreshing the credential" and no action is needed — as written, this bullet sends the reader to reconnect a connector that already self-healed. - If the retry also fails on auth, the code throws
OAuthReauthRequiredErrorbeforeerrorOnPartialLoadFailureruns — so a persistent OAuth 401 never appears askind: "auth"in this log line. The 401 surfaces as ai-proxy's bridged line (Error loading tools for <name>) instead.
Net effect: kind: "auth" here effectively concerns static-credential connectors (apiKey/header). Suggest mentioning the auto-refresh and the recovery Info line, e.g.: "auth — the server rejected the credential. For OAuth connectors the runtime refreshes the token and retries once on its own — act only if the failure repeats without a follow-up MCP tools loaded after refreshing the credential line. For static credentials, renew the credential in the connector's configuration."
|
|
||
| - `auth` — the server rejected the credential. Reconnect the connector, or renew its token. | ||
| - `connection` — unreachable, refused, or slower than the 15-second per-server load timeout. | ||
| - `unknown` — the server answered but the load failed anyway; `error` carries the reason. |
There was a problem hiding this comment.
One nugget worth adding here: classifyMcpLoadError only treats 401 as auth — a 403 (credential valid but insufficient scope/permission) is deliberately classified unknown, since no token refresh can fix it. A reader whose server rejects the credential with 403 will see unknown while the auth bullet above seems to cover their case. Suggest: "unknown — the server answered but the load failed anyway (including 403 permission/scope errors); error carries the reason."
| } | ||
| ``` | ||
| 4. Check that all the expected tools are exposed. | ||
| 4. Check that all the expected tools are exposed. An empty list means the server answered but published nothing — a configuration problem on the server, not a connection failure. |
There was a problem hiding this comment.
True on the executor path once forestadmin#9892 lands (its manual-verification table covers exactly these three cases). One caveat from that PR: the browser-engine path keeps the empty-means-failed inference — it can't distinguish "no tools" from "this server failed", so a project listing tools through that path still shows the "could not be retrieved" warning for a healthy server publishing no tools. If readers of this page can be on that path, this sentence overpromises; if the setup flow always goes through the executor, ignore me.
Follow-up to PRD-876. The executor half (agent-nodejs#1806) makes Forest Runtime name why an MCP connector failed to load its tools; this documents what a reader does with that.
Forest Runtime → Observability
New subsection, "When an MCP step can't load its tools": the
Errorline as it appears at the default log level, and what each failurekindimplies for the fix —authreconnect the credential,connectionunreachable or past the 15s per-server timeout,unknownread the error text. Also states that a server answering with no tools is an empty list rather than an error, which is the behaviour the PR changes.The
LOG_LEVELrow in Tuning now says whatDebugbuys you (per-server tool count and load time), since "Debug,Info,Warn, orError" gave no reason to ever lower it.MCP Connectors
New "A connector's tools don't load" section. The page previously ended at "Check that all the expected tools are exposed" with no guidance when they aren't. It now names the three failure modes in the order worth checking — nothing published, credential rejected, server unreachable — and points self-hosters at the runtime logs that distinguish them.
Nothing removed; no existing statement was inaccurate.
Note
Document MCP tool loading failures and empty tool list behavior in workflow docs
kindvalues (auth,connection,unknown).LOG_LEVELtoDebugadds one line per MCP server with its tool count and load time.Macroscope summarized d4a9ab2.