13. External tool services
[!ref] Full details in the User Manual, "External tool services".
13.1 Service-entry fields
| Field | Type | Description |
|---|---|---|
name |
string | Name (also the tool prefix, e.g. notion.*) |
protocol |
enum | http (Streamable HTTP, the default when adding a service manually) / sse (legacy) / stdio. If you pick the wrong one, the connection negotiates a fallback automatically: when the server is reachable but rejects the handshake (e.g. 405 / Bad Request), the other HTTP transport is tried and the one that actually worked is written back to the config; if the server can't be reached at all (refused / timeout / EOF), no fallback is attempted |
lazy_load |
bool | Whether this service's tools are lazily loaded. Defaults to following the global "Tool category lazy loading"; turn it off explicitly and this service's tools stay resident. Persisted as mcp_lazy_load (keyed by backend name) |
endpoint |
string | URL — for HTTP / SSE; unused for stdio |
command |
string | Command to launch on stdio (e.g. npx -y @some/mcp-server) |
args |
array | Command-line arguments for the stdio channel |
env |
map | Environment variables for the stdio child process (credentials, tokens, …) |
credential |
string | Bearer token or API key (HTTP / SSE) |
oauth |
object | OAuth 2.1 + PKCE config (authorizationEndpoint / tokenEndpoint / clientId / scope / clientSecret?); endpoints can be auto-discovered via .well-known |
enabled |
bool | Active |
timeout_ms |
int | Request timeout |
retry |
object | max=3, base=500ms, jitter=0.3 |
Credential storage (VAULT):
credential/env/oauth.refresh_tokenand refresh metadata are not written to the main config file and not logged; they live in a GM-encrypted credential vault (SM4-GCM + HKDF-SM3, bound to machineID). Existing plaintext credentials are passively migrated on next use.
13.1.1 OAuth flow fields
| Field | Description |
|---|---|
authorizationEndpoint |
OAuth authorization endpoint |
tokenEndpoint |
OAuth token endpoint |
clientId |
Client ID |
clientSecret |
Optional; absent = public client (PKCE only) |
scope |
Requested scopes |
redirectUri |
Local loopback: http://127.0.0.1:<port>/cb |
pkce.codeVerifier / pkce.codeChallenge |
S256 |
accessTokenExpiresAt |
Auto-refreshed before expiry (startup / background) using refresh_token |
13.1.2 Health-board fields
MCP Config → Health Board — sourced from the zMCP gateway /admin/metrics ring buffer:
| Field | Description |
|---|---|
backend |
MCP backend name |
calls |
Calls in window |
failRate |
Failure rate |
p50Ms / p95Ms |
Response latency percentiles (ms) |
lastError / lastErrorAt |
Most recent failure message and timestamp |
13.2 Tool naming rules
| Source | Name |
|---|---|
| Built-in tools | No prefix (e.g. read, git.status) |
| Security analysis | sec.* prefix |
| External services | <name>.* prefix (e.g. notion.search) |
| Messaging outbound | channels.* prefix |
| Skill invocation | skill.invoke |
13.3 Error drawer entries
| Field | Description |
|---|---|
| Time | Trigger time |
| Tool | Tool name |
| Status / Class | HTTP status / exception class |
| Summary | One-line description |
| Detail | Raw server response (up to 4 KB) |
| Retries | Auto-retry attempts |
Absent-backend fallback: when a backend is momentarily absent / not ready,
tools/callused to return an "unknown tool" protocol error; unknown tools are now handled gracefully and the backend is reconnected / self-healed on demand, recovering automatically once ready — no longer taking tools down wholesale.
