MCP server: One MCP server.

Openverse MCP

@odelMCP server · Native

Search Openverse for openly licensed sound effects, music and images that are safe for commercial use

v1.1.0
Freeno charge per call

One address for any MCP client, signed in with your Odel account.

What it does

4 read1 act

Grouped by the server's hints and tool names.

What it costs

Free

Odel charges nothing per call.

Tools

5 · grouped by the server's hints and tool names

Read · 4Look things up

search-audioSearch openly licensed sound effects and music, safe for commercial use. Returns a playable file URL, duration, licence and a ready-made credit line per result. Made using Openverse (https://openverse.org). Not endorsed by Openverse. Licences are as reported by the source and are not verified: check `landingUrl` before use.6 parameters

Parameters

  • querystringrequired

    What to search for

  • kindstringoptional

    sound_effect (short Freesound clips), music (tracks, mostly Jamendo) or any

    One of sound_effect, music, any

    Default "sound_effect"

  • licencestringoptional

    public-domain (CC0 / public domain mark, no credit needed) or attribution (adds CC BY; use the returned `attribution` line)

    One of public-domain, attribution

    Default "public-domain"

  • maxSecondsnumberoptional

    Only return clips at most this long

  • pageintegeroptional

    Result page, from 1

    Default 1

  • pageSizeintegeroptional

    Results per page, 1–20

    Default 10

Returns

  • successbooleanrequired

    One of true

  • resultsobject[]required
    • idstringrequired
    • titlestring | nullrequired
    • creatorstring | nullrequired
    • urlstringrequired

      The file itself, on the source’s own host

    • fileTypestring | nullrequired
    • licensestringrequired

      cc0, pdm or by

    • licenseVersionstring | nullrequired
    • licenseUrlstring | nullrequired
    • attributionstring | nullrequired

      The credit line to publish with the work; null when none is required

    • landingUrlstring | nullrequired

      The work’s page at its source — check the licence there before use

    • sourcestring | nullrequired
    • tagsstring[]required
    • durationSecondsnumber | nullrequired
  • resultCountintegerrequired
  • pageCountintegerrequired
  • pageintegerrequired
  • nextPageinteger | nullrequired

    Pass as `page` to continue; null when there is nothing more

Input schema as JSON
{
  "type": "object",
  "properties": {
    "query": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200,
      "description": "What to search for"
    },
    "kind": {
      "type": "string",
      "enum": [
        "sound_effect",
        "music",
        "any"
      ],
      "default": "sound_effect",
      "description": "sound_effect (short Freesound clips), music (tracks, mostly Jamendo) or any"
    },
    "licence": {
      "type": "string",
      "enum": [
        "public-domain",
        "attribution"
      ],
      "default": "public-domain",
      "description": "public-domain (CC0 / public domain mark, no credit needed) or attribution (adds CC BY; use the returned `attribution` line)"
    },
    "maxSeconds": {
      "type": "number",
      "exclusiveMinimum": 0,
      "maximum": 3600,
      "description": "Only return clips at most this long"
    },
    "page": {
      "type": "integer",
      "minimum": 1,
      "default": 1,
      "description": "Result page, from 1"
    },
    "pageSize": {
      "type": "integer",
      "minimum": 1,
      "maximum": 20,
      "default": 10,
      "description": "Results per page, 1–20"
    }
  },
  "required": [
    "query"
  ],
  "additionalProperties": false,
  "$schema": "http://json-schema.org/draft-07/schema#"
}
search-imagesSearch openly licensed photos, illustrations and artwork, safe for commercial use. Returns the image URL, a thumbnail, its size, licence and a ready-made credit line per result. Made using Openverse (https://openverse.org). Not endorsed by Openverse. Licences are as reported by the source and are not verified: check `landingUrl` before use.6 parameters

Parameters

  • querystringrequired

    What to search for

  • licencestringoptional

    public-domain (CC0 / public domain mark, no credit needed) or attribution (adds CC BY; use the returned `attribution` line)

    One of public-domain, attribution

    Default "public-domain"

  • categorystringoptional

    Kind of image

    One of photograph, illustration, digitized_artwork

  • aspectRatiostringoptional

    Image shape

    One of tall, wide, square

  • pageintegeroptional

    Result page, from 1

    Default 1

  • pageSizeintegeroptional

    Results per page, 1–20

    Default 10

Returns

  • successbooleanrequired

    One of true

  • resultsobject[]required
    • idstringrequired
    • titlestring | nullrequired
    • creatorstring | nullrequired
    • urlstringrequired

      The file itself, on the source’s own host

    • fileTypestring | nullrequired
    • licensestringrequired

      cc0, pdm or by

    • licenseVersionstring | nullrequired
    • licenseUrlstring | nullrequired
    • attributionstring | nullrequired

      The credit line to publish with the work; null when none is required

    • landingUrlstring | nullrequired

      The work’s page at its source — check the licence there before use

    • sourcestring | nullrequired
    • tagsstring[]required
    • thumbnailUrlstring | nullrequired
    • widthnumber | nullrequired
    • heightnumber | nullrequired
  • resultCountintegerrequired
  • pageCountintegerrequired
  • pageintegerrequired
  • nextPageinteger | nullrequired

    Pass as `page` to continue; null when there is nothing more

Input schema as JSON
{
  "type": "object",
  "properties": {
    "query": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200,
      "description": "What to search for"
    },
    "licence": {
      "type": "string",
      "enum": [
        "public-domain",
        "attribution"
      ],
      "default": "public-domain",
      "description": "public-domain (CC0 / public domain mark, no credit needed) or attribution (adds CC BY; use the returned `attribution` line)"
    },
    "category": {
      "type": "string",
      "enum": [
        "photograph",
        "illustration",
        "digitized_artwork"
      ],
      "description": "Kind of image"
    },
    "aspectRatio": {
      "type": "string",
      "enum": [
        "tall",
        "wide",
        "square"
      ],
      "description": "Image shape"
    },
    "page": {
      "type": "integer",
      "minimum": 1,
      "default": 1,
      "description": "Result page, from 1"
    },
    "pageSize": {
      "type": "integer",
      "minimum": 1,
      "maximum": 20,
      "default": 10,
      "description": "Results per page, 1–20"
    }
  },
  "required": [
    "query"
  ],
  "additionalProperties": false,
  "$schema": "http://json-schema.org/draft-07/schema#"
}
get-mediaLook up one Openverse audio or image result by id, optionally with similar works. Only works under CC0, the public domain mark or CC BY are returned. Made using Openverse (https://openverse.org). Not endorsed by Openverse. Licences are as reported by the source and are not verified: check `landingUrl` before use.3 parameters

Parameters

  • typestringrequired

    Whether the id is an audio or an image result

    One of audio, image

  • idstring · uuidrequired

    The Openverse id from a search result

  • relatedbooleanoptional

    Also return up to 10 similar works

    Default false

Returns

  • successbooleanrequired

    One of true

  • typestringrequired

    One of audio, image

  • itemanyrequired
  • relatedany[]optional
Input schema as JSON
{
  "type": "object",
  "properties": {
    "type": {
      "type": "string",
      "enum": [
        "audio",
        "image"
      ],
      "description": "Whether the id is an audio or an image result"
    },
    "id": {
      "type": "string",
      "format": "uuid",
      "description": "The Openverse id from a search result"
    },
    "related": {
      "type": "boolean",
      "default": false,
      "description": "Also return up to 10 similar works"
    }
  },
  "required": [
    "type",
    "id"
  ],
  "additionalProperties": false,
  "$schema": "http://json-schema.org/draft-07/schema#"
}
client-statusread-onlyopen worldCheck which Openverse key this module uses for the user (their own, Odel’s shared one, or none) and, for a key, whether it is verified, its rate-limit tier and how many requests it has made. Not endorsed by Openverse.No parameters

Parameters

Takes no parameters.

Returns

  • successbooleanrequired

    One of true

  • keySourcestringrequired

    Whose Openverse key your searches use: your own, Odel’s shared one, or none (anonymous)

    One of yours, odel, anonymous

  • verifiedboolean | nullrequired

    Whether that key’s email address is verified (unverified keys get anonymous rate limits); null when anonymous

  • rateLimitModelstring | nullrequired

    standard or enhanced; null when anonymous

  • requestsThisMinuteinteger | nullrequired
  • requestsTodayinteger | nullrequired
Input schema as JSON
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {}
}

Act · 1Change something

register-clientdestructiveopen worldRegister the user’s own free Openverse API client. Only needed if they keep hitting rate limits: without one, this module searches with Odel’s shared key, whose allowance (100 requests a minute, 10,000 a day) every Odel user shares; a verified key of their own gets the same allowance to themselves. Needs no credentials. Only call it when the user asks for a client and gives their own email address. The secret is returned once: tell the user to store both values, verify the client from the email Openverse sends, and then add them as this module’s OPENVERSE_CLIENT_ID and OPENVERSE_CLIENT_SECRET in Odel. Not endorsed by Openverse.3 parameters

Parameters

  • emailstring · emailrequired

    Where Openverse sends the verification link; must be the user’s own address

  • namestringoptional

    A unique name for the client; a random one is picked when omitted

  • descriptionstringoptional

    What the client is for; Openverse asks for detail

Returns

  • successbooleanrequired

    One of true

  • clientIdstringrequired
  • clientSecretstringrequired

    Shown once — Openverse cannot show it again

  • namestringrequired
  • nextStepsstring[]required
Input schema as JSON
{
  "type": "object",
  "properties": {
    "email": {
      "type": "string",
      "format": "email",
      "maxLength": 254,
      "description": "Where Openverse sends the verification link; must be the user’s own address"
    },
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 150,
      "description": "A unique name for the client; a random one is picked when omitted"
    },
    "description": {
      "type": "string",
      "minLength": 1,
      "maxLength": 10000,
      "description": "What the client is for; Openverse asks for detail"
    }
  },
  "required": [
    "email"
  ],
  "additionalProperties": false,
  "$schema": "http://json-schema.org/draft-07/schema#"
}
README4 min read

@Odel/openverse An Odel module that searches Openverse for openly licensed sound effects, music and images, filtered to licences that are safe for commercial use. Built on @odel/module-sdk.

Made using Openverse. Not endorsed by Openverse. Licences are as reported by the source and are not verified by Openverse or Odel — check a result's landingUrl before you use it.

Tools Every tool works without setup: searches run on Odel's shared Openverse key unless you add your own (see Which key a search uses).

Tool Inputs Returns search-audio query · kind: sound_effect (default) | music | any · licence: public-domain (default) | attribution · maxSeconds? · page · pageSize (1–20) results[], resultCount, pageCount, page, nextPage search-images query · licence · category? (photograph | illustration | digitized_artwork) · aspectRatio? (tall | wide | square) · page · pageSize the same get-media type: audio | image · id · related (default false) item, and related[] (up to 10) when asked register-client email · name? (unique; random when omitted) · description? clientId, clientSecret (shown once), name, nextSteps[] client-status — keySource (yours | odel | anonymous), verified, rateLimitModel, requestsThisMinute, requestsToday Every item carries url (the file, on the source's own host), license, licenseUrl, attribution (the credit line to publish, or null when none is needed) and landingUrl. Audio items add durationSeconds; image items add thumbnailUrl, width and height.

Licences. public-domain returns only CC0 and the public domain mark — nothing to credit. attribution adds CC BY, with its credit line in attribution. Share-alike, no-derivatives and non-commercial works are never returned, and results are re-checked after the fetch in case the upstream filter changes. get-media serves the attribution set. Most music on Openverse (Jamendo) is CC BY, so music searches usually want licence: "attribution".

maxSeconds is applied after the fetch, because Openverse only filters by coarse length buckets; a call reads at most 3 upstream pages to fill pageSize. Pass nextPage back as page to continue.

Errors come back as an isError result whose text is { success: false, error, code, metadata? }, with code from the SDK's ErrorCode: 2001 only one of your two secrets set, 2002 your own credentials rejected by Openverse, 1001 bad input, 3004 not found, 4001 rate limited (metadata.retryAfter; on the shared key or anonymous use, metadata.keySource and a pointer to register-client), 3001 Openverse unavailable, 3002 network, 3003 timeout.

Which key a search uses Your own Openverse client, when both of your secrets are set: an allowance to yourself. Odel's shared key otherwise: a verified client Odel registered for this module and holds as Worker secrets. Every Odel user without their own key shares its allowance. Anonymous, when neither is available (a local run without Odel's key, say). Caller Requests a minute Requests a day Results per page A verified client (your own, or Odel's shared one) 100 10,000 up to 20 here An unverified client, or anonymous (per IP) 20 200 20 These are Openverse's published defaults, read from its rate-limit headers; Openverse can raise a client's limits on request.

Secret (optional) What OPENVERSE_CLIENT_ID Your Openverse client id OPENVERSE_CLIENT_SECRET Your Openverse client secret (shown only once) Setting only one of the two is answered 2001, naming the missing one: add it, or remove both to go back to Odel's key.

Getting your own key. Worth it if you keep hitting the shared limit (a 4001 that points at register-client). Ask your assistant to run register-client with your own email address; it needs no account. It returns the client id and secret (the secret is shown once), so store both, say in your Odel vault. Click the link Openverse emails you before adding them: until you verify, Openverse treats the client as anonymous, which is less than the shared key. Then add both on the module's Config tab (how) and run client-status to confirm keySource: "yours" and verified: true.

Registering by hand.

curl -s -X POST https://api.openverse.org/v1/auth_tokens/register/
-H 'Content-Type: application/json'
-d '{"name": "my-odel-openverse", "description": "Searching Openverse from Odel", "email": "you@example.com"}' Save client_id and client_secret from the response — the secret can't be retrieved again — and click the link Openverse emails you. Until you do, the client stays on anonymous rate limits.

Development pnpm install pnpm test # vitest, Openverse replaced by a stubbed fetch pnpm typecheck pnpm dev # wrangler dev; send your own key in params._meta["app.odel/secrets"] Worker bindings:

ODEL_OPENVERSE_CLIENT_ID / ODEL_OPENVERSE_CLIENT_SECRET (secrets): Odel's shared key. Locally, put them in .dev.vars (gitignored); without them local searches go anonymous. OPENVERSE_API_BASE (a variable, default https://api.openverse.org/v1) points the module at another Openverse, such as a local stub. License MIT

Similar modules