12. External tool services

The agent manager → "Configure tools" panel connects standard-protocol tool services (Notion, internal APIs, private knowledge bases, etc.).

Don't confuse this with Settings → Providers: that panel configures large language model providers, which is a different thing from the external tool services here.

12.1 Add a service

  1. The gear next to the mode slider in the input area (agent manager) → "Configure tools" → add a service.
  2. Pick a channel:
    • Streamable HTTP (default) / SSE: enter endpoint URL + credential — for cloud services. Most modern MCP services use the former; if you pick the wrong one, the connection negotiates a fallback automatically, so there's no need to agonize over the choice.
    • stdio (local process): enter the command line to launch (e.g. npx -y @some/mcp-server) + environment variables — spawns a child process as the MCP server locally, so you no longer need a remote HTTP endpoint. Handy for any ready-made MCP server on npm / pip.
  3. Fill in:
    • Name: e.g. notion.
    • Credential: token from the provider (HTTP / SSE), or environment variables for the child process (stdio).
  4. Enable.

UI redesigned: the new-server form, backend details, health board, and install-confirmation dialog were all reworked to a more restrained datatable look — monospace fonts for technical data, hairline borders, status dots — cleaner and more professional overall.

12.1.1 OAuth login for remote MCP servers

For remote MCP servers that require login, the new-server flow embeds OAuth 2.1 + PKCE authorization:

  • Log in at create time: the OAuth entry sits inside the "New MCP" flow (not buried under details after the fact). Click OAuth Authorize, your browser walks the standard authorization-code + PKCE flow, and the local loopback callback receives the token and persists it.
  • Auto refresh: access tokens are refreshed in the background or at startup using the refresh token before they expire; success is reflected in the UI immediately — no more repeated manual reconnects.
  • Both public clients (no client_secret) and confidential clients (with client_secret) are supported; endpoints can be auto-discovered via .well-known or entered manually.

12.1.2 Install confirmation: permissions and dependencies up front

Before installing an MCP backend / plugin, the install confirmation dialog discloses:

  • Tool permissions to be requested: which tools get enabled, and each tool's default allow / ask / deny state.
  • Declared modes: permissionMode / allowed-tools from the plugin manifest.
  • Host dependencies: required local commands (node / python / git …), OS, and arch constraints.
  • Remote workspace: an extra lightweight hint so you don't install something whose dependencies are missing on the remote host.

12.2 Namespacing

External-service tools are auto-prefixed (e.g. notion.search, notion.create_page) and grouped separately so they don't clash.

12.3 Live activation

Newly-connected services become visible to the assistant on the next turn — no need to start a new conversation. So you can wire up services mid-conversation.

With tool category lazy loading on (the default, see §9.2.1): a service connected mid-conversation is immediately usable, without waiting for the assistant to load it; from the next new session it goes back to "loaded when needed".

12.3.1 Lazy loading per service

In the agent manager → the "Configure tools" tab, every external tool service has a "Lazily load this service's tools" switch:

  • On (default): the service's tools aren't resident; they're loaded when the assistant needs them.
  • Off: the service's tools are always available — good for a service you use often.

If the global "Tool category lazy loading" is already off, all tools are resident anyway and this switch has no effect for now; the UI says so directly.

The system prompt tells the assistant which external services you've connected, along with each service's tool count and a few example tool names, so it knows who to turn to even when the tools aren't loaded yet.

12.4 Error handling

  • Reconnect: the backend retries with exponential backoff.
  • Error drawer: failures include the raw server response so you can tell whether it's a stale credential, a network issue or a server fault.
  • Readiness probe: after connect, AVL Code does a quick ping to confirm the peer really is serviceable before registering tools.
  • A momentarily-absent backend no longer takes everything down: when a backend is temporarily absent / not ready, a tool call used to return an "unknown tool" protocol error and break tools wholesale; now unknown tools are handled gracefully and the backend is reconnected / self-healed on demand, recovering automatically once it's ready.

12.4.1 MCP health board

MCP Config → Health Board shows each MCP backend's call count / failure rate / response latency (P50 / P95) — instantly tells you which service is slow and which is erroring. The most recent failure for a given backend expands inline for quick diagnosis.

Data comes from the MCP gateway's ring-buffer metrics (recent window), updated live.

12.5 Security — GM-encrypted credential VAULT

  • MCP backend auth tokens (HTTP Bearer tokens) and the entire stdio environment-variable map are no longer written in plaintext to disk — they go into a GM-encrypted credential vault (SM4-GCM + HKDF-SM3, bound to local machineID — useless if copied to another machine).
  • OAuth refresh_token + refresh metadata also live in the VAULT — never in zmcp.yaml or logs.
  • Existing configs are migrated lazily: any leftover plaintext credentials are passively migrated to the encrypted VAULT on next use — no manual action required. If machineID is unavailable / has changed, the system degrades gracefully (asks you to re-enter) rather than failing silently.
  • Cold-start 401 fixed: previously, auth-protected HTTP MCP servers would return 401 on the first call after a fresh app / OS cold start — root cause was a key mismatch in the credential VAULT (the zMCP gateway sub-process keyed its archive by a per-launch ephemeral 127.0.0.1:<port> URL, while the parent App keyed Save by the stable workspaceID — so Load always missed and fell back to the now-empty migrated plaintext, sending an empty Bearer header). Both ends now use the stable workspaceID as the key — credentials connect correctly after cold-start, no manual reconfig needed.
  • Credentials are stored encrypted on the local machine and never sent along with conversation content to the model.
  • Third-party stdio services can't obtain the gateway admin key: third-party MCP servers launched over standard input/output no longer inherit the tool gateway's admin key — a third-party process never holds gateway-management privileges it shouldn't have.
  • The agent manager → "Configure tools" can apply permission policy per external service independently.