MCP & agents / MCP gateway

MCP gateway guide

Connect remote MCP servers once, group their tools into toolsets with approvals and limits, and give any MCP client or model one URL to use them.

Remote servers you connect, nothing else. The gateway runs tools only on remote MCP servers your organization connects and allows. It does not host or run code, it does not run local (stdio) MCP servers, and every call is an HTTPS request to a server you registered, through the platform's network guard.

Concepts

Register the remote MCP servers you use (GitHub, Linear, your own) once, with their credentials stored sealed. Group them into toolsets. Each toolset is itself an MCP server that any MCP client (Claude, ChatGPT, Cursor, VS Code, an agent framework) can connect to with a Yelno API key or with OAuth, and that /v1/responses can use by reference. Every tool call through it is governed (allow and deny lists, read-only mode, approvals, rate limits, kill switches), traced, and optionally priced. Manage all of it in the console under MCP.

ThingIdWhat it is
connectionmcpcn_…A remote MCP server your organization registered: its URL, auth type, ownership, status and cached tool list. Up to 100 per organization.
connected accountmcpa_…The credential a connection uses. One for an organization connection; one per external user for an end-user connection. Stored sealed, never returned.
toolsetmcpts_…A named set of connections plus a tool policy. Each toolset is itself an MCP server at POST /v1/mcp/toolsets/{id}.
directory entrymcpd_…A curated, staff-verified remote MCP server (URL, auth type, docs) you can connect in one click.
connect linkmcpl_…A hosted, single-use page you hand to one of your own users so they can connect their own account.
approvalmcpap_…A tool call waiting for a human decision in the approvals inbox.

Ownership. An organization connection has one shared credential, used for every call. An end_user connection has none: each of your own users connects their own account, and every call uses the caller's account (see End users).

What it is not. No local or stdio servers, no hosted code, no arbitrary agent tool execution. The gateway does not judge what a tool does; it enforces the policy you set around it. For one-off tools on a server you name in the request, and for Yelno's own MCP server at /v1/mcp, see MCP tools and server.

Connect a server

From the directory. The MCP server directory lists remote servers that platform staff verified against each vendor's own documentation. Connecting one takes its URL, transport, auth type and header name from the entry, so the request only needs directory_entry_id. A connection keeps working if its entry is later disabled.

Custom server. Give a server_url (https, no user info, no fragment) and an auth type. Only the Streamable HTTP transport (streamable_http) is supported; the deprecated HTTP+SSE transport is refused with 400 mcp_transport_unsupported. The slug is derived from the name when you leave it out and must be unique in your organization.

Create a connection
curl https://yelno.ai/api/v1/organizations/$ORG/mcp/connections \
  -H "Authorization: Bearer $SESSION_TOKEN" -H "Content-Type: application/json" \
  -d '{"directory_entry_id": "mcpd_…", "ownership": "organization"}'
auth_typeWhat is sent
noneNo credential. The server is called as-is.
bearerSends Authorization: Bearer {token}. credential is {"token": "…"}.
headerSends {header_name}: {value}. credential is {"header_name": "…", "value": "…"}. Transport headers (Host, Content-*, Mcp-*, X-Yelno-Hop, …) cannot be used.
oauth2Yelno signs in to the server's authorization server for you (see Managed OAuth). No credential in the body; the connection starts needs_auth.
  • A credential is accepted only for an organization connection with bearer or header auth (single line, up to 8 KiB); otherwise 400 mcp_credential_invalid. It is never returned: reads show a hint such as Bearer ••••3f9a.
  • For none, bearer and header organization connections, creating runs a health check straight away. A failed check does not fail the create: the connection comes back unhealthy with status_detail saying why.
  • An oauth2 connection starts needs_auth. Authorize it for the organization, or hand out connect links for an end-user connection.
  • Registering a connection lets the gateway send data to a new host, so it counts as a data-policy relaxation. If your organization has an approval policy for data_policy.relax, the create (and a PATCH that changes server_url) answers 400 approval_required with error.approval_request_id; once that request is approved, resubmit with X-Yelno-Approval: <approval_request_id>.
  • Hosts the platform blocks are refused with 403 mcp_host_blocked. Stored connections do not need the host on the ad-hoc allow-list; that list governs only server_url tools in a request.

Managed OAuth

For an oauth2 connection, Yelno acts as the MCP client and handles the vendor's OAuth for you. Start it with POST …/connections/{id}/oauth/start (the console's Connect button), which returns an authorize_url.

StepWhat happens
DiscoveryYelno sends an unauthenticated MCP request to the server and reads its WWW-Authenticate challenge, or falls back to the server's protected-resource metadata. It then fetches the authorization server's metadata. The issuer in that document must match, and the server must support PKCE S256; otherwise 424 mcp_oauth_discovery_failed.
ClientTried in order: the client id and secret you stored on the connection; a client platform staff registered for the directory entry; a Client ID Metadata Document Yelno publishes, when the authorization server supports it; dynamic client registration, when it offers it. If none works, 424 mcp_oauth_client_required: register an app with the vendor and PATCH its client_id and client_secret into the connection's oauth field.
AuthorizeYou get an authorize_url built with PKCE (S256), a single-use state, the server URL as resource and the requested scopes. Open it and sign in at the vendor. The flow is valid for 15 minutes.
CallbackThe vendor redirects to Yelno's callback, which checks the state and issuer, exchanges the code and stores the tokens sealed on the account. Your browser then lands on the return_to page you gave (a page of the console; anything else is refused with 400), by default /console/mcp/connections/{id}, with ?mcp_oauth=connected or ?mcp_oauth=error&error_code=…. The vendor's own error text is never echoed.
RefreshAutomatic: a worker refreshes tokens that expire within 10 minutes, and a call that finds its token within 60 seconds of expiry refreshes it first. Each refresh sends the same resource the authorization used. Rotated refresh tokens are kept.
RevocationDeleting an account or a connection asks the vendor to revoke its tokens, when the vendor lists a revocation endpoint (best effort).

Re-authorizing. When a refresh fails or the server answers 401, the account turns needs_reauth and the mcp.connection.needs_reauth webhook fires. For an organization connection, start OAuth again from the connection's page in the console. For an end-user account, create a new connect link for that user: a link for a user who already has an account replaces it.

End users

With ownership: "end_user", the connection holds no shared credential. Each of your users connects their own account, keyed by an external_user_id you choose: 1 to 256 characters of [A-Za-z0-9._:@|-].

  • API-key callers pick the account with the X-Yelno-External-User header (on the toolset endpoint), or with external_user_id on a Responses tool (or the same header). It only selects an account inside your key's organization; it never selects a tenant.
  • OAuth callers use their own account: console members who connect from the console are stored as yelno_user:<user id>, and that is the account their OAuth client uses. OAuth callers cannot send X-Yelno-External-User.
  • No matching account means the tool error mcp_account_missing, with a connect_hint pointing to the console. The gateway never falls back to the organization's or another user's account.
  • In tools/list, an end-user connection the caller has not connected still lists its cached tools, with a note in each description to connect first.
  • Set a credential for an end user from the console (PUT …/accounts/{external_user_id}) for bearer or header servers, or send them a connect link.
  • An id outside that pattern is refused with 400 mcp_external_user_invalid.

A connect link is a hosted page you hand to one of your users so they can connect their own account without a Yelno login. Create one in the console, or from your backend with an API key that has the mcp:connect scope. Links exist only for end-user connections with bearer, header or oauth2 auth; an organization connection has one shared account, which you authorize from the console. Anything else is 400 mcp_connect_link_invalid.

POST/v1/mcp/connect-linksmcp:connect
Create a connect link
import os, requests

link = requests.post(
    "https://yelno.ai/v1/mcp/connect-links",
    headers={"Authorization": f"Bearer {os.environ['YELNO_API_KEY']}"},   # scope mcp:connect
    json={
        "connection": "linear",                 # id or slug of an end-user connection
        "external_user_id": "user-42",          # your own id for this user
        "return_url": "https://app.example.com/settings/integrations",
        "expires_in_seconds": 3600,             # 60 to 604800, default 3600
    },
).json()

# Send your user to link["url"]. The token inside it is shown only this once.
print(link["id"], link["url"], link["expires_at"])
201
{ "id": "mcpl_…", "url": "https://yelno.ai/mcp/connect/ymcl_…", "token": "ymcl_…",
  "connection_id": "mcpcn_…", "external_user_id": "user-42", "expires_at": "…" }
  • The token is shown once and stored only as a hash. The link is single use: it is consumed when the account connects.
  • The page lives at /mcp/connect/{token}. For OAuth servers it sends the user to the vendor; for bearer or header servers it asks for the value and checks it with tools/list before saving (400 mcp_credential_rejected when the server refuses it).
  • After an OAuth sign-in the user is sent to your return_url (https; http://localhost in development) with ?mcp_oauth=connected or ?mcp_oauth=error and an error_code. Without a return_url, they land back on the hosted page, /mcp/connect/{token}?mcp_oauth=connected; your users have no Yelno login, so they are never sent to the console. The hosted page keeps showing connected after the link is spent.
  • expires_in_seconds is 60 to 604,800 (7 days), default 3,600. Attempts are limited to 10 per link and 30 per minute per source IP (429 rate_limited).

Toolsets

A toolset groups up to 20 connections (members) under one policy and exposes them as one MCP server at /v1/mcp/toolsets/{id}. Your organization can have up to 50 toolsets (400 mcp_limit_reached beyond that; a 21st member is 400 invalid_request). Each member's tools appear as <namespace>__<tool> (two underscores); the namespace defaults to the connection's slug with dashes turned into underscores and matches [a-z0-9][a-z0-9_-]{0,31}.

POST /api/v1/organizations/{org}/mcp/toolsets
{ "name": "Engineering agents", "slug": "eng-agents",
  "read_only": false,
  "approval_mode": "destructive",
  "members": [
    { "connection_id": "mcpcn_…", "namespace": "github",
      "allowed_tools": null,
      "denied_tools": ["delete_repository"],
      "approval_required": ["create_pull_request"],
      "approval_exempt": ["search_issues"],
      "rate_limit_per_minute": null },
    { "connection_id": "mcpcn_…", "namespace": "linear" } ],
  "rate_limit_per_minute": 600,
  "project_ids": [], "api_key_ids": [],
  "oauth_enabled": true, "include_yelno_tools": false,
  "redact_secrets": true, "flag_prompt_injection": true,
  "elicitation_approvals": true, "elicitation_for_api_keys": false,
  "reason": "Agents for the platform team" }

Per member. allowed_tools (null for every tool, or a list of upstream names), denied_tools, approval_required (always asks), approval_exempt (never asks, the "always allow" list) and an optional rate_limit_per_minute.

Approval rule, first match wins: the tool is in approval_required, so it needs approval; it is in approval_exempt, so it does not; otherwise the toolset's approval_mode decides. Under destructive, a tool needs approval unless it declares readOnlyHint: true or destructiveHint: false. A tool with no annotations is destructive by the MCP specification's defaults, so it needs approval.

SettingWhat it does
read_onlyOnly tools that declare readOnlyHint: true are listed or callable. Others are unlisted, and a call to one is treated like any unknown tool (JSON-RPC -32602).
approval_modedestructive (default): calls need approval unless the tool says it is read-only or not destructive. always: every call. never: no call (unless a member's approval_required names it).
elicitation_approvalsLet a person signed in to a modern MCP client with OAuth approve a call in place (see Approvals). Default on.
elicitation_for_api_keysExtend in-place approval to API-key callers. Default off, because a headless automation holding a key could approve its own calls.
rate_limit_per_minuteCalls per minute per calling principal for the whole toolset. Unset means the platform default, 600.
project_ids · api_key_idsEmpty: any API key of your organization. Otherwise the key must belong to one of these projects or be one of these keys (403 mcp_toolset_key_not_allowed).
include_yelno_toolsAlso list Yelno's own tools (list_models, chat, get_usage, get_balance) to API-key callers, unprefixed and under the key's scopes. chat is charged as an ordinary request. Never listed to OAuth callers.
redact_secretsReplace credentials found in tool output with [REDACTED:SECRET]. Default on.
flag_prompt_injectionFlag (never block) tool output that looks like an injected instruction. Default on.
oauth_enabledLet MCP clients connect to this toolset with OAuth. Off: only API keys work.
enabledYour kill switch for the toolset. Off: every call answers 403 mcp_toolset_disabled.

Tool annotations are hints the upstream server declares. They decide read-only mode and the default approval rule, so review a server's tools in the console before you rely on them.

Connect a client

Point any MCP client that speaks Streamable HTTP at the toolset URL, shown on the toolset's page in the console:

Toolset URL
https://yelno.ai/v1/mcp/toolsets/mcpts_…

Claude and ChatGPT. In claude.ai or Claude Desktop, add a custom connector and paste the toolset URL; in ChatGPT, enable developer mode and add a connector with the URL and OAuth. Both sign in with OAuth: you see Yelno's consent screen, choose allow, and the client gets a token for that one toolset. Claude Desktop can also use an API key through the mcp-remote bridge. Cursor and VS Code take the URL in their config files:

Client configuration
{
  "mcpServers": {
    "yelno-eng": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://yelno.ai/v1/mcp/toolsets/mcpts_…",
               "--header", "Authorization:${AUTH_HEADER}"],
      "env": { "AUTH_HEADER": "Bearer yk_live_…" }
    }
  }
}

From code, use the Responses API on Yelno with a toolset reference, the Anthropic SDK's MCP connector, or plain JSON-RPC:

From code
import os
from openai import OpenAI

client = OpenAI(base_url="https://yelno.ai/v1", api_key=os.environ["YELNO_API_KEY"])
response = client.responses.create(
    model="MODEL",
    input="Find open bugs about the billing page and summarize them.",
    tools=[{"type": "mcp", "server_label": "tools", "toolset": "mcpts_…"}],
)
print(response.output_text)
ClientSign-inHow
Claude (claude.ai, Claude Desktop custom connector)OAuthAdd a custom connector and paste the toolset URL. Claude registers itself and opens Yelno's consent screen.
ChatGPT (developer mode connector)OAuthAdd a connector with the toolset URL and choose OAuth.
Claude Desktop via mcp-remoteAPI keyThe bridge sends your key as a header (config above).
CursorAPI key or OAuthA headers block sends the key; a bare url makes Cursor sign in with OAuth.
VS CodeAPI key or OAuthinputs prompts once for the key and stores it; without headers VS Code can sign in with OAuth.
Responses API on YelnoAPI keyReference the toolset by id or slug; no URL or credentials in the request.
Anthropic SDK, agent frameworks, curlAPI keySend Authorization: Bearer with a key that has mcp:toolset or inference:invoke.

API keys. The key must carry mcp:toolset or inference:invoke, belong to the toolset's organization, and match the toolset's project or key scoping when set. Create a dedicated key with only mcp:toolset for clients that need nothing else.

OAuth. The toolset needs oauth_enabled. The person signing in must be a member of the toolset's organization with the key:create permission, and must keep it: the token stops working when they lose it. Tokens are bound to one toolset URL and are accepted nowhere else, not even by the management API. Access tokens last an hour and refresh for up to 30 days. Revoke a client's access from the organization's grants list (POST …/mcp/oauth-grants/{id}/revoke) or your own (POST /api/v1/mcp/oauth/grants/{id}/revoke); deleting a toolset revokes all of its grants.

Use from the Responses API

POST/v1/responsesinference:invoke

Instead of naming a server_url, a Responses mcp tool can reference a stored connection or a toolset by id or slug. No credentials appear in the request, and they are never sent to the model provider, logged or stored in the trace.

Connection and toolset references
import os
from openai import OpenAI

client = OpenAI(base_url="https://yelno.ai/v1", api_key=os.environ["YELNO_API_KEY"])
response = client.responses.create(
    model="MODEL",
    input="Open a Linear issue for the failing deploy and link the GitHub run.",
    tools=[
        {"type": "mcp", "server_label": "gh", "connection": "github",       # id or slug
         "allowed_tools": ["search_issues"], "require_approval": "never"},
        {"type": "mcp", "server_label": "eng", "toolset": "mcpts_…"},         # id or slug
        {"type": "mcp", "server_label": "mine", "connection": "linear",
         "external_user_id": "user-42"},                                     # an end-user account
    ],
)
print(response.output_text)
  • connection and toolset are mutually exclusive, and neither can be combined with server_url or headers (400 mcp_reference_conflict). Unknown references are 400 mcp_connection_not_found or mcp_toolset_not_found; a disabled or needs_auth connection is 424 mcp_list_tools_failed.
  • With connection, the request's require_approval applies (default "always", as for server_url).
  • With toolset, its members, filters, read-only mode and key scoping apply (a key outside the scoping is 400 mcp_toolset_key_not_allowed), and the model sees <server_label>__<namespace>__<tool>. A call needs approval when the toolset's rule or the request's require_approval says so: a request can only tighten.
  • Approvals use the Responses round trip (mcp_approval_request, then mcp_approval_response; see MCP tools), not the inbox.
  • The external user comes from the tool's external_user_id, else the X-Yelno-External-User header.
  • The org host allow-list is not consulted for stored connections; platform host blocks are.
  • stream: true is 400 mcp_streaming_unsupported. MCP tools are not offered on /v1/chat/completions; use /v1/responses.

Approvals

When the approval rule says a call on the toolset endpoint needs a human decision, the call is not made. A pending approval appears in the console's approvals inbox, where a member with approval:decide can approve, deny, or approve with "always allow" (which adds the tool to the member's approval_exempt list). The mcp.approval.requested webhook fires; it carries ids, the tool name and the approval URL, never the arguments.

People signed in with OAuth to a modern client (protocol 2026-07-28) that supports form elicitation are asked in place, on a toolset with elicitation_approvals on: the person answers in the client and the call runs. API-key callers are asked in place only when the toolset also turns on elicitation_for_api_keys. It is off by default because a headless automation holding a key could approve its own calls. Declining is the tool error mcp_approval_denied. The request state is signed and expires after 10 minutes.

Approval by elicitation
// The gateway answers tools/call with an input request (protocol 2026-07-28):
{ "resultType": "input_required",
  "inputRequests": { "yelno_approval": { "method": "elicitation/create", "params": {
      "mode": "form",
      "message": "Approve github__create_issue on GitHub? Arguments: …",
      "requestedSchema": { "type": "object", "required": ["approve"], "properties": {
          "approve": { "type": "boolean", "title": "Approve this call" },
          "always_allow": { "type": "boolean", "title": "Always allow this tool" } } } } } },
  "requestState": "…" }

// The client retries the same call with the person's answer and the same requestState:
"inputResponses": { "yelno_approval": { "action": "accept", "content": { "approve": true } } }

Every other caller, legacy protocol clients and (by default) API-key callers included, gets a tool error with a link to the inbox. Once someone approves it, call the tool again with the same arguments within 15 minutes and it runs.

mcp_approval_required
{ "isError": true,
  "content": [{ "type": "text", "text": "This call needs approval: https://yelno.ai/console/mcp/approvals/mcpap_…. Once approved, call the tool again with the same arguments within 15 minutes." }],
  "structuredContent": { "error": { "code": "mcp_approval_required", "message": "…",
      "approval_id": "mcpap_…", "approval_url": "https://yelno.ai/console/mcp/approvals/mcpap_…", "expires_at": "…" } } }
  • An approval matches the toolset, the namespaced tool, the exact arguments (by SHA-256) and the caller (the API key plus external user, or the OAuth grant).
  • Each approval runs at most one call. A pending approval expires after 24 hours.
  • The inbox shows the arguments only while your organization captures content for the mcp surface; otherwise it shows their digest.

Safety controls

Connect only servers you trust. The gateway enforces your policy around each call, but it cannot judge what a tool does. A tool can change or delete data, and tool output can carry instructions aimed at the model. Keep toolsets narrow, use read-only mode where you can, and leave approvals on for tools with side effects.

Network guard and size caps. Every outbound request (MCP calls, OAuth discovery, registration, token and revocation requests, metadata document fetches) refuses private, loopback, link-local and metadata addresses at send time and never follows redirects. Caps: upstream MCP response 2 MB; OAuth documents 256 KiB; client metadata documents 16 KiB; each image, audio or embedded resource 1 MB; structuredContent 1 MB; tool descriptions 4,000 characters; 1,000 tools per connection. An oversized media item is replaced by a short text note, and oversized structuredContent is dropped with a note.

Secret redaction (redact_secrets, on by default) scans text items, embedded resource text and strings inside structuredContent, and replaces each match with [REDACTED:SECRET]. This is the only change the gateway makes to tool output. It looks for:

  • provider and platform keys: sk-…, sk-ant-…, sk_live_ and rk_live_, yk_live_ and yk_test_;
  • GitHub tokens: ghp_, gho_, ghu_, ghs_, ghr_, github_pat_;
  • Slack tokens: xox[abprs]-;
  • AWS access key ids AKIA… and ASIA…, Google API keys AIza…;
  • JWTs, -----BEGIN … PRIVATE KEY----- blocks and Bearer <token>.

Prompt-injection flagging (flag_prompt_injection, on by default). A deterministic heuristic (instructions to ignore earlier instructions, role-override markers, chat template tokens, hidden-instruction phrasing), plus your guardrail policy when it scans output, checks tool output text. Hits are flagged only, never altered or blocked: in the call's trace flags, in a guardrail event (finding types and counts only) and in the result's _meta["io.yelno/flags"], so your client can decide.

Rate limits apply in four layers; the first one reached refuses the call with mcp_rate_limited and retry_after_seconds. If the shared limiter is unavailable, conservative local limits apply, never none.

LayerLimit
toolsetrate_limit_per_minute per calling principal (an API key, plus its external user, or an OAuth grant). Default 600.
memberThe member's rate_limit_per_minute, when set.
connectionThe connection's rate_limit_per_minute across every caller, when set.
organizationThe platform ceiling MCP_GATEWAY_ORG_RPM, 1,200 calls per minute per organization by default.

Content capture. Tool arguments and outputs are stored only when your organization's data controls capture the mcp surface and zero data retention or a retention ceiling of 0 does not block it. They are then kept sealed for your capture retention. Otherwise only digests, sizes and metadata are kept.

Kill switches. Turn a connection or a toolset off with enabled: false. Platform staff can turn off your organization's gateway, one connection or one toolset, and can block hosts for everyone. Each switch takes effect on the next call, within 5 seconds.

Audit. Every configuration change is in your audit log, with credentials never in the before or after views: mcp_connection.*, mcp_account.connect and revoke, mcp_connect_link.create, mcp_toolset.*, mcp_approval.* and mcp_oauth_grant.*. Platform staff actions are audited too, including mcp_kill_switch.engage and release.

Observability

Trace. Every exchange through the gateway writes a row you can read at GET /api/v1/organizations/{org}/mcp-calls: the toolset, connection, upstream host, namespaced tool, list or call, external user, API key or OAuth client, status, error code, latency, bytes sent to the server and received from it, approval id, charge and flags. Never arguments, outputs or credentials. Filter by connection, toolset, tool, status, surface and time, and page with cursor and next_cursor.

Statistics. GET …/mcp/stats groups calls by connection, toolset, tool or day over up to 93 days (7 by default) with calls, errors, error rate, p50 and p95 latency and bytes received from upstream servers. Errors count failed and refused calls; listings are not counted.

Health. Each connection is checked every 15 minutes, or every 5 minutes while unhealthy. An organization connection (or one with no auth) runs tools/list with its credential; an end-user connection is probed for reachability, where a 401 counts as reachable. Checks are kept for 30 days and shown on the connection's page; the cached tool list refreshes on each check, after an hour (or the server's own cache time), when a call names a tool it lacks, and on demand.

StatusMeaning
pendingJust created, not checked yet.
healthyThe last tools/list succeeded.
degradedIt succeeded but took longer than 5 seconds, or some accounts need re-authorization.
unhealthyThe last two checks failed.
needs_authOAuth is not authorized yet, or an organization connection has no credential.
needs_reauthA token refresh failed or the server answered 401. Sign in again.
disabledTurned off by your organization (enabled: false) or by platform staff.

Subscribe a webhook endpoint to these events:

EventWhen
mcp.connection.unhealthyA connection became unhealthy.
mcp.connection.needs_reauthA connection or one account needs a new sign-in.
mcp.connection.recoveredAn unhealthy connection is healthy again.
mcp.approval.requestedA call is waiting in the approvals inbox. Never carries the arguments.

Pricing

A gateway tool call is a tools/call forwarded upstream through a stored connection, from the toolset endpoint or from a Responses connection or toolset reference. Its per-call price is 0 (free) unless platform staff set one for the platform or for your organization. Read the price that applies to you at GET /api/v1/organizations/{org}/mcp/pricing (policy:read); it is a final price.

  • When the price is above 0, it is held against your balance before the upstream call. If the hold cannot be placed, the call is not made (insufficient_funds).
  • Gateway calls are paid from prepaid credit only, also for organizations on postpaid billing: a call whose price exceeds the available prepaid balance is refused with insufficient_funds.
  • The hold is captured only when the server returns a successful result. A failure, timeout, tool error, refusal or approval request releases it, and nothing is charged.
  • Model turns in a Responses run are billed as normal inference, at each model's final price.
  • Ad-hoc server_url tools are not gateway calls and are never priced. Yelno's own chat tool is charged as an ordinary request.

Troubleshooting

You seeWhat to do
424 mcp_oauth_discovery_failedThe server does not publish usable OAuth metadata, its issuer does not match, or it lacks PKCE S256. Check the server URL against the vendor's docs; some vendors need a different path for OAuth.
424 mcp_oauth_client_requiredThe vendor does not allow automatic client registration. Register an OAuth app with the vendor (redirect URI: Yelno's callback), then PATCH oauth.client_id and oauth.client_secret on the connection and start OAuth again.
status needs_reauth · tool error mcp_connection_needs_reauthThe refresh token stopped working or the server answered 401. Organization connection: Re-authorize in the console (POST …/oauth/start). End-user account: send that user a new connect link.
status needs_auth · tool error mcp_connection_unavailableOAuth was never completed, or the connection is turned off, staff-disabled or host-blocked. Finish OAuth or turn it back on.
mcp_account_missingAn end-user connection has no account for this caller. Send X-Yelno-External-User (API key) or connect from the console (OAuth). The gateway never falls back to another user's account.
-32602 for a tool you expectedThe tool is filtered out: not in allowed_tools, in denied_tools, or not read-only on a read-only toolset. Filtered tools are unlisted and unknown to the caller.
400 mcp_transport_unsupportedOnly Streamable HTTP is supported. The deprecated HTTP+SSE transport cannot be connected.
400 mcp_external_user_invalidexternal_user_id must be 1 to 256 characters of [A-Za-z0-9._:@|-].
mcp_rate_limitedA rate limit was reached; retry after retry_after_seconds. Raise the toolset, member or connection limit if needed.
mcp_approval_requiredApprove the call in the inbox (the approval_url), then call the tool again with the same arguments within 15 minutes.
mcp_approval_deniedSomeone denied the call, or the client declined the elicitation.
403 mcp_toolset_key_not_allowedThe toolset is scoped to certain projects or keys and yours is not one of them.
403 mcp_toolset_disabledYour organization or platform staff turned the toolset off.
403 mcp_gateway_disabledPlatform staff turned off the MCP gateway for your organization.
403 mcp_host_blockedThe platform refuses to contact that host. It cannot be overridden by an organization.
401 with WWW-AuthenticateNo credential, an invalid key, or an OAuth token for a different toolset. The client should follow resource_metadata and sign in, or send an API key with mcp:toolset or inference:invoke.
404 on the toolset URLThe key belongs to another organization, or the toolset id is wrong.
insufficient_fundsA per-call price applies and the hold could not be placed from prepaid credit. Top up; the call was not made.
mcp_timeout · mcp_server_unreachable · mcp_server_errorThe upstream server was slow, down or failing. Check its health history; raise call_timeout_seconds (1 to 120) if it is just slow.
mcp_response_too_largeThe server's response was over 2 MB.
400 approval_required on createYour organization requires approval for data-policy relaxations. Once the request in error.approval_request_id is approved, resubmit with X-Yelno-Approval: <approval_request_id>.

Every route, shape and error code is in the MCP gateway API reference.