Skip to main content
Glama
mgcrea

mcp-ovh-api

by mgcrea

@mgcrea/mcp-ovh

npm version GHCR

A Model Context Protocol server for the OVHcloud API, focused on Object Storage: buckets, objects, project users, S3 credentials and the storage policies that tie them together.

The server is read-only by default. Mutating tools are not merely refused when writes are off — they are never registered, so an agent cannot call them at all.

Features

  • Curated tools over OVHcloud's /1.0 API with descriptions that spell out its traps (see Traps worth knowing).

  • Read-only by default. OVH_ALLOW_WRITES=1 adds the write tools; the destructive ones then additionally require an explicit confirm: true on every call.

  • All three OVH auth methods, picked automatically from whichever env vars are present: OAuth2 service account (recommended), application key + consumer key (SHA1-signed, with automatic clock-drift correction), or a static access token.

  • Policy presets — including write-only, which OVH's own role shortcut does not offer.

  • List results are summarized, and OVH's deprecated per-bucket objects[] array (which embeds every object in the bucket) is suppressed on both ends.

  • X-Ovh-QueryID is surfaced on every error, because that is the first thing OVH support asks for.

  • An ovh_request escape hatch for the rest of the API (GET-only unless writes are enabled).

  • Native fetch, no runtime dependencies beyond the MCP SDK and Zod.

Related MCP server: saveformedearai

Install

pnpm install
pnpm build

Configure

Pick one auth method.

  1. Create an IAM service account at https://www.ovh.com/manager/#/iam/service-account.

  2. Attach an IAM policy granting it your public cloud project (for object storage: publicCloudProject:apiovh:* on the project resource).

  3. Copy the client id and secret into .env.

Tokens last an hour and are cached and refreshed ahead of expiry.

(B) Application key + consumer key

Create the triplet in one shot at https://eu.api.ovh.com/createToken/. The access rules you list there are fixed forever — a consumer key cannot be widened afterwards, so grant what you need up front:

GET    /cloud/project/*
POST   /cloud/project/*
PUT    /cloud/project/*
DELETE /cloud/project/*
GET    /me

Requests are SHA1-signed over secret+consumerKey+METHOD+URL+BODY+TIMESTAMP. A clock more than ~30s off OVH's fails every call with a misleading Invalid signature, so the server probes /auth/time once at startup and corrects for the delta.

(C) Static access token

Set OVH_ACCESS_TOKEN and it is sent as Authorization: Bearer.

cp .env.example .env

Variable

Required

Description

OVH_ENDPOINT

no

ovh-eu (default), ovh-ca, ovh-us, kimsufi-*, soyoustart-*.

OVH_CLIENT_ID / OVH_CLIENT_SECRET

(A)

IAM service account. Their presence selects OAuth2.

OVH_APPLICATION_KEY / _SECRET

(B)

Application key pair.

OVH_CONSUMER_KEY

(B)

Consumer key issued alongside them.

OVH_ACCESS_TOKEN

(C)

Pre-minted bearer token.

OVH_AUTH_METHOD

no

Force oauth2, signature or accessToken. Otherwise inferred.

OVH_CLOUD_PROJECT

no

Default project — the 32-char hex serviceName, not the display name.

OVH_REGION

no

Default storage region, upper-case (GRA, SBG, DE, UK).

OVH_ALLOW_WRITES

no

Set to 1 to register the write tools. Off by default.

OVH_API_URL

no

Override the API base URL entirely.

OVH_MAX_RETRIES

no

Retry budget for 401 / 429 / 5xx. Defaults to 3.

OVH_REFRESH_SKEW_SECONDS

no

Refresh the OAuth2 token this long before expiry. Defaults to 60.

OVH_DEBUG

no

Set to 1 to log debug output to stderr.

Run

pnpm start   # speaks JSON-RPC over stdio

Wire into Claude Code

Add to .mcp.json (project) or ~/.claude.json (global):

{
  "mcpServers": {
    "ovh": {
      "command": "node",
      "args": ["/absolute/path/to/mcp-ovh/dist/cli.js"],
      "env": {
        "OVH_CLIENT_ID": "...",
        "OVH_CLIENT_SECRET": "...",
        "OVH_CLOUD_PROJECT": "abcdef0123456789abcdef0123456789",
        "OVH_REGION": "UK"
      }
    }
  }
}

Inspect the tools

npx @modelcontextprotocol/inspector node dist/cli.js

Traps worth knowing

All are baked into the tool descriptions, but they explain the shape of this server:

  1. OVH has no bucket policies — only user policies. One raw JSON document per project user, and that document is the entire access-control surface. Setting a policy replaces everything that user could previously do, across all buckets.

  2. A policy cannot restrict the bucket's owner. OVH falls back to ACLs and the owner holds FULL_CONTROL: "if the user is the bucket owner and even if there is no explicit allow in the policy file, the user will be authorized." A restricted key must therefore belong to a new project user that did not create the bucket. ovh_provision_s3_user checks the bucket's ownerId and refuses when you point it at the owner.

  3. The same fallback applies per object. Whoever uploads an object owns it and gets FULL_CONTROL on it through the object ACL. So merely omitting s3:GetObject does not stop an upload-only key from reading back everything it wrote — verified against the live API, where a bare allow-list policy happily served the key its own uploads while correctly denying every object someone else had uploaded. An explicit Deny is required, and it does beat the ACL. That is why the write-only preset ships a Deny statement rather than a bare allow-list.

Two smaller ones. s3:PutObject alone still permits blind overwrite of existing keys inside the allowed prefix — a "write-only" key is not an append-only key, which is a good reason to enable versioning on the bucket. And policy changes take up to ~30 seconds to propagate: a probe run five seconds after ovh_set_storage_policy still shows the old behaviour, which reads exactly like a policy that silently failed.

Tools

Every project-scoped tool takes an optional project, and every storage tool an optional region, overriding OVH_CLOUD_PROJECT / OVH_REGION per call. Tools marked W exist only when OVH_ALLOW_WRITES=1; those marked ⚠️ are destructive and additionally require confirm: true.

Start with ovh_whoami. It reports which auth method is live, which account you are, and the clock delta against OVH — which is what a 401 on the signature method is nearly always about.

Area

Tools

Meta

ovh_whoami, ovh_list_projects, ovh_get_project, ovh_list_regions, ovh_get_region

Buckets

ovh_list_buckets, ovh_get_bucket, ovh_get_bucket_lifecycle · W ovh_create_bucket, ovh_update_bucket, ovh_set_bucket_lifecycle, ⚠️ ovh_delete_bucket_lifecycle, ⚠️ ovh_delete_bucket

Objects

ovh_list_objects, ovh_get_object, ovh_list_object_versions, ovh_presign_object · W ovh_copy_object, ⚠️ ovh_delete_object, ⚠️ ovh_delete_object_version, ⚠️ ovh_bulk_delete_objects

Users & keys

ovh_list_project_users, ovh_get_project_user, ovh_list_s3_credentials · W ovh_create_project_user, ovh_create_s3_credentials, ovh_reveal_s3_secret, ⚠️ ovh_delete_s3_credentials, ⚠️ ovh_delete_project_user

Policies

ovh_get_storage_policy, ovh_preview_policy · W ⚠️ ovh_set_storage_policy, ⚠️ ovh_grant_bucket_access, ⚠️ ovh_provision_s3_user

Escape hatch

ovh_request — any /1.0 path, GET-only unless writes are enabled

ovh_presign_object is the only way bytes move: the server never proxies object content, it mints a time-limited presigned S3 URL instead. With writes off it signs GET only.

Policy presets

ovh_preview_policy, ovh_set_storage_policy and ovh_provision_s3_user share three presets, all scopable to a key prefix:

Preset

Grants

write-only

Allow s3:PutObject, s3:AbortMultipartUpload, s3:ListMultipartUploadParts on the prefix — plus an explicit Deny on s3:GetObject / s3:GetObjectAcl bucket-wide

read-only

s3:ListBucket + s3:GetBucketLocation on the bucket, s3:GetObject on the objects

read-write

both, plus s3:DeleteObject

OVH's built-in roles (admin, deny, readOnly, readWrite, via ovh_grant_bucket_access) have no write-only equivalent — that is why the raw-policy path exists. The multipart pair is included deliberately: every S3 SDK auto-switches to multipart above ~8-16MB, and without abort/list a failed upload orphans parts the key holder cannot clean up and keeps paying for.

OVH validates policy actions against a fixed enum and rejects the whole document with a 400 if one is unknown — s3:GetObjectVersion and s3:DeleteObjectVersion exist in AWS but not there. The presets use only accepted actions, and a test pins that.

Handing out a write-only upload key

The motivating case: an app embeds an S3 key in a shipped binary, so the key must be able to upload and nothing else, while the read/write key stays with the developer.

ovh_get_bucket           bucket=dev-rgis-ar          → note ownerId
ovh_preview_policy       bucket=dev-rgis-ar preset=write-only prefix=uploads/
ovh_provision_s3_user    bucket=dev-rgis-ar preset=write-only prefix=uploads/ \
                         description=ar-app-uploader confirm=true

That creates a new project user (never the bucket owner), applies the policy, and only then mints credentials — a key that exists before its policy is a key that briefly had whatever the default allows. The secret is returned once.

Verify against the real S3 API before handing it over — a policy that reads correctly can still be shadowed by ownership, and wait ~30s after setting it or you will be probing the previous policy:

export AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=...
# An array, not a string: zsh does not word-split an unquoted $var, so the
# `S3='aws ...'` form you would write in bash silently becomes "command not found".
S3=(aws --endpoint-url https://s3.uk.io.cloud.ovh.net --region uk s3api)
"${S3[@]}" put-object      --bucket dev-rgis-ar --key uploads/probe.txt --body /dev/null   # 200
"${S3[@]}" get-object      --bucket dev-rgis-ar --key uploads/probe.txt /dev/null          # 403
"${S3[@]}" list-objects-v2 --bucket dev-rgis-ar                                            # 403
"${S3[@]}" delete-object   --bucket dev-rgis-ar --key uploads/probe.txt                    # 403
"${S3[@]}" put-object      --bucket dev-rgis-ar --key elsewhere/probe.txt --body /dev/null # 403

The get-object line is the one that matters: it is the check that catches trap 3, and it passes only because of the preset's Deny.

Develop

pnpm dev            # tsdown --watch
pnpm test           # vitest
pnpm typecheck
pnpm lint
pnpm format

License

MIT

Available Tools

1 tool
ovh_auth_statusOVHcloud: Auth StatusA
Read-only

Report whether this server has working OVHcloud credentials, which auth method and endpoint it uses, the default project and region, whether writes are enabled, and — when something is missing — exactly what to set. Call this first when a tool you expected is not listed: an absent tool here means missing configuration, not a bug.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The readOnlyHint annotation already marks this as safe, and the description adds useful behavioral context by stating it reports credential validity, auth method, endpoint, project/region, and write status. It also says missing credentials explain absent tools, which clarifies what the status check means. It doesn't explicitly describe network/read behavior, but the annotation plus 'report' wording make the safety profile clear.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences carry a full purpose statement, a detailed list of outputs, and a usage rule. The key diagnostic trigger ('Call this first when a tool you expected is not listed') is placed second and is memorable. No word is wasted.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with no parameters, no siblings, and no output schema, the description is self-sufficient: it tells the agent what information the tool produces and when to invoke it. The only omitted detail, the exact configuration values to set, is precisely what the tool's output is described as providing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There are zero parameters, so there is nothing to document beyond the empty schema. The description still clarifies the kind of status data returned, which is consistent with a no-input diagnostic tool. Baseline 4 is appropriate for a 0-parameter definition.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Report whether this server has working OVHcloud credentials,' then enumerates exactly what is reported (auth method, endpoint, default project/region, write enablement). This is unambiguous and fully distinguishes the tool from any conceivable alternative, even though no siblings are listed.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives an explicit call heuristic: 'Call this first when a tool you expected is not listed,' and even frames the diagnostic interpretation ('an absent tool here means missing configuration, not a bug'). This tells an agent not only when to run it but how to interpret the result.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

TDQS

A4.2/5.0
Disambiguation5/5

With only one tool, there is no possibility of confusion between tools. The single tool's purpose is clearly defined and distinct.

Naming Consistency5/5

The lone tool name follows a clean snake_case verb_noun pattern. With only one tool, there are no inconsistencies to evaluate.

Tool Count1/5

A single status-check tool is drastically insufficient for a server named 'mcp-ovh-api' covering the OVH cloud API. The count represents an extreme mismatch between the server's implied scope and its actual surface.

Completeness1/5

The server exposes no operations beyond an authentication status check. Any actual OVH API functionality is absent, making the tool surface severely incomplete for the stated domain.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    MCP server for uploading, listing, and retrieving files on S3-compatible storage (AWS S3, DigitalOcean Spaces) with public/private access and temporary URLs.
    15
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    MCP server for Oracle Cloud Infrastructure (OCI) that provides tools to manage Compute, Object Storage, Block Storage, Networking, Autonomous Database, and IAM via the official OCI SDK.
    23
    67
    MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mgcrea/mcp-ovh'

If you have feedback or need assistance with the MCP directory API, please join our Discord server