MCP & agents / MCP

# MCP tools and server

Let a model call tools on your own MCP servers through the Responses API, or connect an MCP client to Yelno and use the gateway as tools.

Off until your organization allows a server. The gateway connects to an MCP server only when its host is on your organization's allow-list, which is empty by default. An administrator adds hosts under [Governance → MCP servers](https://yelno.ai/governance/mcp-servers); a request naming any other host is refused before anything is contacted.

## Remote MCP tools

`POST` `/v1/responses` `inference:invoke`

Add a `{"type": "mcp"}` tool to a Responses request and the gateway lists that server's tools, offers them to the model as functions, calls the tools the model asks for, feeds the results back and runs the model again until it answers. The MCP server sees only the tool calls; the model provider never sees the server's address or your headers.

Remote MCP tools

```
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="What changed in the billing docs this week?",
    tools=[{
        "type": "mcp",
        "server_label": "docs",
        "server_url": "https://mcp.example.com/mcp",
        "allowed_tools": ["search", "fetch"],
        "require_approval": "never",          # default: "always"
        "headers": {"Authorization": f"Bearer {os.environ['DOCS_MCP_TOKEN']}"},
    }],
)
for item in response.output:
    print(item.type)                          # mcp_list_tools, mcp_call, ..., message
print(response.output_text)
```

- `server_label` names the server in the output (letters, digits, - and _); up to 8 servers per request.
- `allowed_tools` limits which of the server's tools the model may use.
- `headers` are sent to that server only, for example its access token. They are not logged, stored or returned.
- The server must speak MCP over Streamable HTTP at an https address that resolves to a public IP. Redirects are not followed.
- Function tools can sit beside MCP tools; when the model calls one of yours, the response ends with a `function_call` item for you to run.

## Approval

Tool calls need approval unless you say otherwise: `require_approval` is `"always"` by default, `"never"` runs every call, and `{"never": {"tool_names": [...]}}` skips approval for the tools you list. A call that needs approval is not made; the response ends with an `mcp_approval_request`. Resend the conversation with your decision to run or decline it. The gateway keeps no conversation state, so include the request item itself. An approval covers exactly the arguments it was issued for and runs its call once: when you continue the conversation, keep the returned `mcp_call` item and the call is replayed to the model, not made again.

Approve a call

```
// 1. The response stops at the call that needs approval:
{"type": "mcp_approval_request", "id": "mcpc_…", "server_label": "docs", "name": "delete_page",
 "arguments": "{\"page\": 42}"}

// 2. Resend the conversation with that item and your decision:
"input": [
  {"role": "user", "content": "Remove the outdated page"},
  {"type": "mcp_approval_request", "id": "mcpc_…", "server_label": "docs", "name": "delete_page", "arguments": "{\"page\": 42}"},
  {"type": "mcp_approval_response", "approval_request_id": "mcpc_…", "approve": true}
]
```

## Output and charges

Response

```
{
  "id": "req_…",
  "status": "completed",
  "output": [
    {"type": "mcp_list_tools", "id": "mcpc_…", "server_label": "docs", "tools": [{"name": "search", "input_schema": {…}}]},
    {"type": "mcp_call", "id": "mcpc_…", "server_label": "docs", "name": "search",
     "arguments": "{\"q\": \"billing\"}", "output": "…", "error": null},
    {"type": "message", "role": "assistant", "content": [{"type": "output_text", "text": "…"}]}
  ],
  "yelno": {
    "turns": [{"request_id": "req_…", "cost": {…}}, {"request_id": "req_…", "cost": {…}}],
    "cost": {"state": "final", "currency": "USD", "customer_nanos": 1840000},
    "mcp": {"calls": 1, "max_calls_per_request": 8, "servers": [{"server_label": "docs", "host": "mcp.example.com", "tools": 2}]}
  }
}
```

Each time the model runs is its own request, routed under your policy, held against your balance and charged at the model's final price, exactly like a single call. `yelno.turns` lists them and `yelno.cost` adds them up. Tool calls themselves are not charged by Yelno.

## Limits

- At most `max_calls_per_request` tool calls (8 unless your organization sets 1 to 32). A call over the limit is not made and the response is `incomplete` with reason `max_tool_calls`.
- At most 10 model turns per request (reason `max_turns`).
- Each exchange with the server must finish within the organization's timeout (20 seconds unless set from 1 to 120); a response over 2 MB is refused; tool output given to the model is cut at 100,000 characters.
- Streaming is not available with MCP tools, and `previous_response_id` is not supported: send the conversation in `input`.

| Code | Status | Meaning |
| --- | --- | --- |
| mcp_server_not_allowed | 403 | The server's host is not on your organization's MCP allow-list. Nothing was contacted. |
| mcp_list_tools_failed | 424 | The server could not be reached, refused the request, timed out, or resolves to a private address. No model ran. |
| mcp_approval_unknown | 400 | An approval response does not match an approval request the gateway issued to your organization for that tool. |
| mcp_approval_used | 400 | That approval already ran its call. Resend the mcp_call item you received for it instead. |
| mcp_streaming_unsupported | 400 | stream: true is not available with MCP tools. |
| tool_type_unsupported | 400 | Only function and mcp tools can be combined in one request. |
| mcp_server_url_invalid | 400 | server_url must be https, name a host and carry no credentials. |

## What is recorded

Every exchange with an MCP server is recorded on the request: the server, the tool, the outcome and how long it took. The arguments, the tool's output and your headers are never stored. Read the record with `GET /api/v1/organizations/{org}/mcp-calls?response_id=…`.

You are responsible for your MCP servers. The gateway enforces the allow-list, the limits and approval, but it cannot judge what a tool does. A tool can change data, and tool output can carry instructions aimed at the model. Allow only servers you trust, keep `allowed_tools` narrow, and leave approval on for tools with side effects.

## Yelno as an MCP server

`POST` `/v1/mcp` `per tool`

Point an MCP client at `https://yelno.ai/v1/mcp` with your API key as a bearer token to use the gateway as tools. The server is stateless (no session id), answers JSON and lists only the tools your key's scopes allow.

| Tool | Scope | What it does |
| --- | --- | --- |
| list_models | catalog:read or inference:invoke | Models your organization may use, with operations, context window and final price per million tokens. |
| chat | inference:invoke | One chat completion through the gateway, charged to your organization at the model's final price. |
| get_usage | usage:read | This key's requests, tokens and charges in a window of up to 93 days. |
| get_balance | wallet:read | Your organization's available balance and the amount held for requests in flight. |

Client configuration

```
{
  "mcpServers": {
    "yelno": {
      "url": "https://yelno.ai/v1/mcp",
      "headers": { "Authorization": "Bearer YELNO_API_KEY" }
    }
  }
}
```

Protocol versions 2025-06-18, 2025-03-26 and 2024-11-05. A tool that fails returns a result with `isError: true` and the [error envelope](https://yelno.ai/docs/errors) in `structuredContent.error`.

Source: https://yelno.ai/docs/mcp
