ovh-api-mcp
The ovh-api-mcp server is a Model Context Protocol (MCP) server that gives LLMs full programmatic access to the OVH API (v1 and v2) through sandboxed JavaScript execution. It exposes two core tools:
searchtool: Write JavaScript to explore the OVH OpenAPI 3.1 specification — find endpoints by path or keyword, inspect request/response schemas, and understand parameters before making calls.executetool: Write async JavaScript to call any OVH API endpoint (GET,POST,PUT,DELETE) with authentication handled automatically — no manual credential or signature management needed.
Key capabilities:
Manage any OVH service: DNS/domains, email, cloud, VPS, dedicated servers, networking, IAM, and more
All JavaScript runs in a sandboxed QuickJS engine with memory, CPU, and timeout limits for safety
Every API call is validated against the loaded OpenAPI spec before execution, rejecting unknown endpoints or incorrect HTTP methods
Supports OVH API key authentication (application key + secret + consumer key) or OAuth2 service account credentials (client ID + secret)
Multi-region support: Europe (
eu), Canada (ca), or US (us)Operates in HTTP transport mode (for web clients/Docker) or stdio mode (for Claude Desktop, Cursor, MCP inspectors)
Configurable service filtering, API spec caching with TTL, and security measures like path injection prevention and non-root container execution
Deployable from source, via Docker, or using pre-built binaries for macOS and Linux
Provides full access to the OVH API, allowing for the discovery and execution of API endpoints to manage OVHcloud services such as DNS zones, records, and infrastructure resources.
ovh-api-mcp
A native Model Context Protocol (MCP) server that gives LLMs full access to the OVH API (v1 and v2). Built in Rust for minimal footprint (~19 MB Docker image, ~1.2 MiB RAM).
Early Release — Designed for local development use. Security hardening has been applied (sandboxed execution, spec validation, secret protection), but the server has not been battle-tested at scale. Do not expose it to the public internet. Feedback and bug reports are welcome.
How it works
The server exposes two MCP tools:
Tool | Description |
| Explore the OVH OpenAPI spec using JavaScript — find endpoints, inspect schemas, read parameters |
| Call any OVH API endpoint using JavaScript — authentication is handled transparently |
The LLM writes JavaScript that runs inside a sandboxed QuickJS engine with resource limits (memory, CPU timeout, stack size). Every API call is validated against the loaded OpenAPI spec before execution.
The server supports two transport modes:
HTTP (Streamable HTTP) — for web-based clients and Docker deployments
stdio — for direct integration with Claude Desktop, Cursor, and MCP inspectors
OVH credentials are optional at startup: the server starts and exposes its tools even without API keys. Tools return a clear error when called without credentials.
Related MCP server: ChatGPT Codex Bridge
Quick start
With stdio (Claude Desktop / Cursor)
Add to your MCP client configuration:
{
"mcpServers": {
"ovh-api": {
"command": "ovh-api-mcp",
"args": ["--transport", "stdio"],
"env": {
"OVH_APPLICATION_KEY": "your_app_key",
"OVH_APPLICATION_SECRET": "your_app_secret",
"OVH_CONSUMER_KEY": "your_consumer_key"
}
}
}
}With Docker
docker run -d --name ovh-api \
-e OVH_APPLICATION_KEY=your_app_key \
-e OVH_APPLICATION_SECRET=your_app_secret \
-e OVH_CONSUMER_KEY=your_consumer_key \
-p 3104:3104 \
ghcr.io/davidlandais/ovh-api-mcp:latestFrom source
cargo install --git https://github.com/davidlandais/ovh-api-mcp
export OVH_APPLICATION_KEY=your_app_key
export OVH_APPLICATION_SECRET=your_app_secret
export OVH_CONSUMER_KEY=your_consumer_key
ovh-api-mcp --port 3104Pre-built binaries
Download from GitHub Releases — available for macOS (x86_64, aarch64) and Linux (x86_64 musl).
Claude Code configuration (HTTP mode)
{
"mcpServers": {
"ovh-api": {
"type": "http",
"url": "http://localhost:3104/mcp",
"headers": {
"Authorization": "Bearer local"
}
}
}
}The
Authorizationheader is required to bypass Claude Code's OAuth discovery. See claude-code#2831.
OVH credentials
You need three values: an application key, an application secret, and a consumer key.
Go to the token creation page for your region, log in with your OVH account, set the permissions and validity, and you'll get all three keys at once:
Region | URL |
Europe | |
Canada | |
US |
For full API access, set all four methods (GET, POST, PUT, DELETE) with path /*.
OAuth2 authentication (service accounts)
As an alternative to API keys, you can use OVH service accounts with OAuth2 client credentials:
Variable | Description |
| Service account ID |
| Service account secret |
Service accounts are created via the OVH API (POST /me/api/oauth2/client with flow: CLIENT_CREDENTIALS). You must then create an IAM policy (POST /v2/iam/policy) to grant API permissions to the service account. See the OVHcloud documentation for details.
The server auto-detects the auth mode from environment variables. Do not set both API keys and OAuth2 credentials at the same time.
CLI options
Options:
--transport <TRANSPORT> Transport mode: http, stdio [env: OVH_TRANSPORT] [default: http]
--port <PORT> Port to listen on [env: PORT] [default: 3104]
--host <HOST> Host to bind to [default: 127.0.0.1]
--endpoint <ENDPOINT> OVH API endpoint: eu, ca, us [env: OVH_ENDPOINT] [default: eu]
--app-key <APP_KEY> OVH application key [env: OVH_APPLICATION_KEY]
--app-secret <APP_SECRET> OVH application secret [env: OVH_APPLICATION_SECRET]
--consumer-key <CONSUMER_KEY> OVH consumer key [env: OVH_CONSUMER_KEY]
--client-id <CLIENT_ID> OVH OAuth2 client ID [env: OVH_CLIENT_ID]
--client-secret <CLIENT_SECRET> OVH OAuth2 client secret [env: OVH_CLIENT_SECRET]
--services <SERVICES> Services to load, comma-separated or "*" [env: OVH_SERVICES] [default: *]
--cache-dir <PATH> Directory to cache the merged spec [env: OVH_CACHE_DIR]
--cache-ttl <SECONDS> Cache TTL in seconds, 0 to disable [env: OVH_CACHE_TTL] [default: 86400]
--no-cache Disable spec caching entirely
--max-code-size <BYTES> Maximum code input size [env: OVH_MAX_CODE_SIZE] [default: 1048576]Usage examples
Once connected, the LLM can use the tools like this:
Search for DNS endpoints:
// search tool
(spec) => {
const results = [];
for (const [path, methods] of Object.entries(spec.paths)) {
if (path.includes("/domain/zone")) {
for (const [method, op] of Object.entries(methods)) {
results.push({ method: method.toUpperCase(), path, summary: op.summary });
}
}
}
return results;
}List your domain zones:
// execute tool
async () => await ovh.request({ method: "GET", path: "/v1/domain/zone" })Get DNS records for a domain:
// execute tool
async () => {
const records = await ovh.request({
method: "GET",
path: "/v1/domain/zone/example.com/record"
});
const details = [];
for (const id of records.slice(0, 10)) {
details.push(await ovh.request({
method: "GET",
path: `/v1/domain/zone/example.com/record/${id}`
}));
}
return details;
}Security
Sandboxed execution — JavaScript runs in QuickJS with memory limit (64 MiB), stack limit (1 MiB), and execution timeout (10s for search, 30s for execute)
Spec-validated API calls — every
ovh.request()call is matched against the loaded OpenAPI spec; unknown endpoints or wrong HTTP methods are rejectedPath injection prevention — API paths containing
?,#, or..are rejectedSecret protection —
app_secretandconsumer_keyare stored usingsecrecy(zeroized on drop)No HTTP redirects — prevents credential leakage to third-party domains
Non-root container — Docker image runs as unprivileged user
Architecture
src/
main.rs CLI, logging, transport selection (HTTP/stdio), graceful shutdown
tools.rs MCP tool definitions (search, execute) via rmcp macros
sandbox.rs QuickJS sandboxed JS execution with resource limits
auth.rs OVH API client with signature, clock sync, request handling
spec.rs OpenAPI spec fetching, caching, merging, and path validation
types.rs Input types for MCP tool parametersLicense
MIT — David Landais
Available Tools
2 toolsexecuteA
Execute JavaScript against the OVH API. Use 'search' first to find endpoints.
Your code must be an async arrow function: async () => { ... }
Available:
declare const ovh: {
request(options: {
method: "GET" | "POST" | "PUT" | "DELETE";
path: string;
query?: Record<string, string | number | boolean>;
body?: unknown;
}): Promise<any>;
};Authentication is automatic. Errors (HTTP >= 400) throw exceptions. Examples:
// List email domains
async () => await ovh.request({ method: "GET", path: "/email/domain" })// List accounts then get details
async () => {
const accounts = await ovh.request({ method: "GET", path: "/email/domain/example.com/account" });
const details = [];
for (const name of accounts.slice(0, 5)) {
const d = await ovh.request({ method: "GET", path: `/email/domain/example.com/account/${name}` });
details.push(d);
}
return details;
}| Name | Required | Description | Default |
|---|---|---|---|
| code | Yes | JavaScript function to execute. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description fully discloses behavior: authentication is automatic, errors throw for HTTP >= 400, code must be an async arrow function, and the exact API signature is provided. No contradictions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is relatively long but well-structured with clear sections, code blocks, and examples. Each part serves a purpose; however, some repetition could be trimmed. It is appropriately sized for the complexity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the single required parameter, no output schema, and the complexity of executing arbitrary JavaScript against an API, the description covers all necessary aspects: code format, available objects, authentication, error handling, and usage pattern. It is complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The only parameter 'code' has a schema description but the tool description adds significant value: required format (async arrow function), the available `ovh` object with typed request method, and examples. Since schema coverage is 100%, baseline is 3, but the description elevates it to 4.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states 'Execute JavaScript against the OVH API' and distinguishes from the sibling tool 'search' by instructing to use 'search' first to find endpoints. This provides a specific verb-resource pairing and clear differentiation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use (after searching) and provides a detailed code template, available API methods, error handling, and examples. It mentions the alternative tool 'search' and gives practical usage guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
searchA
Search the OVH API OpenAPI 3.1 spec. All configured services are included. The spec is passed as argument to your function.
Your code must be a function that receives spec and returns a value:
(spec) => { ... }
Types available:
interface ParameterObject {
name: string; in: "path" | "query"; required: boolean;
description: string; schema: SchemaObject;
}
interface OperationObject {
summary: string; parameters: ParameterObject[];
requestBody?: { content: { "application/json": { schema: SchemaObject } } };
responses: { "200": { content: { "application/json": { schema: SchemaObject } } } };
}
declare function yourCode(spec: {
paths: Record<string, Record<string, OperationObject>>;
components: { schemas: Record<string, SchemaObject> };
}): any;Examples:
// Find account-related endpoints
(spec) => {
const results = [];
for (const [path, methods] of Object.entries(spec.paths)) {
if (path.includes("/account")) {
for (const [method, op] of Object.entries(methods)) {
results.push({ method: method.toUpperCase(), path, summary: op.summary });
}
}
}
return results;
}// Inspect a model schema
(spec) => spec.components.schemas["email.domain.Account"]// Get request body for creating an account
(spec) => {
const op = spec.paths["/email/domain/{domain}/account"]?.post;
return { summary: op?.summary, requestBody: op?.requestBody, parameters: op?.parameters };
}| Name | Required | Description | Default |
|---|---|---|---|
| code | Yes | JavaScript function to execute. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description must disclose behavioral traits. It explains that the tool executes user-provided JavaScript code against the spec, but it does not mention safety implications, execution limits, or whether modifications are possible. Examples are all read-only, but this is not explicitly stated.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the purpose and then provides necessary details including types and examples. While it is lengthy, the information is essential for correct usage, so it is not overly verbose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description explains how to use the parameter and what the code should do, but it does not describe what the tool returns (e.g., the output format or handling of errors). Given the absence of an output schema, this leaves some ambiguity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema description coverage is 100%, but the description adds extensive detail: the exact function signature, type definitions, and multiple code examples. This far exceeds the schema's simple description of 'JavaScript function to execute.'
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states 'Search the OVH API OpenAPI 3.1 spec' with a specific verb and resource. It distinguishes from the sibling tool 'execute' by focusing on spec search.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description does not explicitly state when to use this tool versus the sibling 'execute' or provide any alternatives. It only mentions that all configured services are included, which is implicit context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
2 tool updates
v0.2.2- First observed
execute - First observed
search
TDQS
Scored across 2 tools
execute and search have entirely different purposes: execute runs API calls, search explores the API spec. No overlap.
Both tools are simple imperative verbs, consistent in style and lowercase. No mixing of conventions.
Only two tools, but the server's design (search + execute) is focused and efficient for its purpose. Slightly minimal but still reasonable.
The combination of search (explore spec) and execute (make any API call) covers the full workflow of using OVH APIs. No obvious gaps.
Maintenance
Related MCP Connectors
An MCP server that gives your AI access to the source code and docs of all public github repos
Hosted MCP server connecting claude.ai, ChatGPT and other AI apps to your own computer
One AI endpoint to search and call 22k+ MCP servers; 50+ hosted tools work instantly, no key.
Related MCP Servers
- AlicenseAqualityDmaintenanceA code-mode MCP server for the Unraid 7.2+ GraphQL API that exposes search and execute tools, allowing LLM agents to introspect and call any GraphQL field via sandboxed JavaScript.21MIT
- AlicenseNot gradedqualityAmaintenanceSelf-hosted MCP server that lets ChatGPT work with your local codebase through explicit tools.MIT
- FlicenseNot gradedqualityDmaintenanceAn MCP server that gives AI agents full access to the Clever Cloud API through three tools: search, execute, and doc, using a code mode pattern to compose API commands.4-
- FlicenseNot gradedqualityBmaintenanceA Code Mode MCP server for the e2b API, giving agents three tools (search, execute_read, execute_write) to write and run JavaScript functions inside sandboxed environments for API discovery and calls.-