kervan.yaml reference
A kervan.yaml file declares an MCP server whose tools are HTTP requests. This page lists every field, generated from the spec’s JSON Schema.
Where this fits: Build with YAML. The reference for every field; HTTP tools and the pages after it explain how the fields work together.
On this page
A complete example
specVersion: 1
name: open-meteo
version: 0.1.0
description: Weather and city lookup backed by the public Open-Meteo APIs (no API key needed).
defaults:
http: { timeoutMs: 8000, maxResponseBytes: 262144 }
rateLimit: { perMinute: 30, concurrency: 5 }
tools:
- name: search_city
title: Search city
description: Finds up to 5 places by name and returns their coordinates.
annotations: { readOnlyHint: true }
input:
type: object
properties:
name: { type: string, minLength: 2, maxLength: 100, description: "City name, e.g. Ankara" }
required: [name]
http:
url: https://geocoding-api.open-meteo.com/v1/search
query: { name: "{{input.name}}", count: 5, language: en, format: json }
output:
# Only the fields the model needs; never the whole API response.
select: "results[].{name: name, country: country, latitude: latitude, longitude: longitude}"
- name: get_current_weather
title: Current weather
description: Current temperature (°C), wind speed (km/h) and WMO weather code at a coordinate.
annotations: { readOnlyHint: true }
input:
type: object
properties:
latitude: { type: number, minimum: -90, maximum: 90 }
longitude: { type: number, minimum: -180, maximum: 180 }
required: [latitude, longitude]
http:
url: https://api.open-meteo.com/v1/forecast
query:
latitude: "{{input.latitude}}"
longitude: "{{input.longitude}}"
current: temperature_2m,wind_speed_10m,weather_code
output:
select: "{temperatureC: current.temperature_2m, windKmh: current.wind_speed_10m, weatherCode: current.weather_code}"
schema:
type: object
properties:
temperatureC: { type: number }
windKmh: { type: number }
weatherCode: { type: integer }
required: [temperatureC, windKmh, weatherCode]Two things to know before the table:
specVersionis always1. A breaking change of the format would becomespecVersion: 2.- Unknown fields are errors, and every error names the file, line and column, for example
kervan.yaml:12:5 tools[0].http.url: The URL's host and port cannot be templated.
Fields
tools[].output has two forms: select (with an optional schema), or raw: true. Both are
listed below. tools[].input and output.schema are ordinary JSON Schema (2020-12); their
limits are in input and output.
Generated from the editor schema, Kervan spec (https://getkervan.dev/schema/v1.json), when this site was built.
| Field | Type | Description and limits |
|---|---|---|
defaults | object | |
defaults.http | object | Defaults for every tool’s HTTP request. |
defaults.http.allowInsecureHttp | boolean | Allow plain http:// URLs. Off by default: requests, answers and any secrets would travel unencrypted. |
defaults.http.followRedirects | integer | How many redirects to follow (default 0). Each hop is checked again; secret headers are dropped when the host changes. at least 0; at most 5 |
defaults.http.maxOutputChars | integer | Longest tool output sent to the model; longer output is cut. at least 1; at most 1e+06 |
defaults.http.maxResponseBytes | integer | Largest response body accepted, after decompression. at least 1; at most 5.24288e+07 |
defaults.http.timeoutMs | integer | Request timeout in milliseconds (connect and response). at least 100; at most 120000 |
defaults.rateLimit | object | Per-tool limits on outgoing calls (defaults: 60 per minute, 10 at once). |
defaults.rateLimit.concurrency | integer | at least 1; at most 1000 |
defaults.rateLimit.perMinute | integer | at least 1; at most 100000 |
description | string | |
namerequired | string | 1+ characters; up to 128 characters |
secrets | array of string or object | Secrets this spec may use as {{secrets.NAME}}: a name, or { name, hosts } to allow sending it only to those hosts. Only declared names are resolved. up to 100 items; a name: pattern ^[A-Z][A-Z0-9_]{0,127}$ |
secrets[].hostsrequired | array of string | The only hosts this secret may be sent to, as host or host:port (port 443 when omitted), matched exactly (no subdomains or wildcards), e.g. api.example.com or api.example.com:8443. 1+ items; up to 32 items; each: pattern ^(?:[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)*\.?|\[[0-9A-Fa-f:.]+\])(?::\d{1,5})?$ |
secrets[].namerequired | string | pattern ^[A-Z][A-Z0-9_]{0,127}$ |
specVersionrequired | 1 (constant) | |
toolsrequired | array of object | 1+ items; up to 200 items |
tools[].annotations | object | |
tools[].annotations.destructiveHint | boolean | |
tools[].annotations.idempotentHint | boolean | |
tools[].annotations.openWorldHint | boolean | |
tools[].annotations.readOnlyHint | boolean | |
tools[].annotations.title | string | |
tools[].descriptionrequired | string | What the tool does; the model’s main guidance. 1+ characters |
tools[].httprequired | object | The HTTP request this tool makes. |
tools[].http.allowInsecureHttp | boolean | |
tools[].http.body | any JSON value | JSON request body. String values may contain templates. |
tools[].http.followRedirects | integer | How many redirects to follow (default 0). Each hop is checked again; secret headers are dropped when the host changes. at least 0; at most 5 |
tools[].http.headers | map of string or number or boolean | Request headers. Names are literal; values may contain templates. |
tools[].http.maxResponseBytes | integer | Largest response body accepted, after decompression. at least 1; at most 5.24288e+07 |
tools[].http.method | string | one of GET, POST, PUT, PATCH, DELETE; default GET |
tools[].http.query | map of string or number or boolean or array of string or number or boolean | Query parameters. Values may contain templates. |
tools[].http.timeoutMs | integer | Request timeout in milliseconds (connect and response). at least 100; at most 120000 |
tools[].http.urlrequired | string | Request URL. Scheme, host and port must be literal; {{input.x}} and {{secrets.X}} may appear in the path. Put templated query parameters under query.1+ characters |
tools[].input | map | JSON Schema of the arguments; must have type: object. |
tools[].namerequired | string | Tool name: 1-128 of A-Z a-z 0-9 _ - . pattern ^[A-Za-z0-9_.-]{1,128}$ |
tools[].outputrequired | object | How the response becomes the tool result: select (JMESPath) or raw: true. |
tools[].output.maxOutputChars | integer | Longest tool output sent to the model; longer output is cut. at least 1; at most 1e+06 |
tools[].output.schema | map | JSON Schema of the selected value. When set, the tool returns structured content. |
tools[].output.selectrequired | string | JMESPath expression that picks and reshapes the fields the model sees, e.g. {temp: main.temp}. External API output is untrusted; select only what is needed.1+ characters; up to 1000 characters |
tools[].output.maxOutputChars | integer | Longest tool output sent to the model; longer output is cut. at least 1; at most 1e+06 |
tools[].output.rawrequired | true (constant) | Return the response body as text (cut to maxOutputChars) instead of selecting fields. Prefer select. |
tools[].rateLimit | object | Per-tool limits on outgoing calls (defaults: 60 per minute, 10 at once). |
tools[].rateLimit.concurrency | integer | at least 1; at most 1000 |
tools[].rateLimit.perMinute | integer | at least 1; at most 100000 |
tools[].title | string | |
versionrequired | string | 1+ characters; up to 64 characters |
Templates
Only two kinds of placeholders exist, {{input.field}} (nested: {{input.user.id}}) and
{{secrets.NAME}}. There is no logic, no filter and no expression; anything else is a load
error, and \{{ writes a literal {{. Each value is encoded for where it goes: see
HTTP tools.
Editor support
The schema in the table above ships in @kervan/spec-runtime as
schema/kervan.schema.json. With the YAML extension of VS Code, point the file at it:
# yaml-language-server: $schema=./node_modules/@kervan/spec-runtime/schema/kervan.schema.jsonThe schema’s $id, https://getkervan.dev/schema/v1.json, is also where this site serves the
same file. Loading a spec never fetches anything: validation always uses the copy in the package.