Skip to content

Plugins

Extend what every Vidmoat agent can do. You host an MCP server; we proxy to it, namespace its tools, and expose them over both MCP and REST.

A plugin is an MCP server you run. Vidmoat never executes your code: it forwards a tools/call to your endpoint and hands the result back to whoever asked. You keep your runtime, your dependencies and your deploy cycle, and we keep the ability to cut a bad integration off without touching anything else.

Submit it: paste the URL#

You do not write a manifest. Give us your endpoint and Vidmoat calls initialize and tools/list and fills in the rest from what your server reports, so what is registered can never disagree with what you serve.

We readFrom
Tool names, descriptions, input schemastools/list (at most 32 tools; descriptions up to 2,000 characters)
Display nameinitialize, serverInfo.title (or .name)
Descriptioninitialize, instructions
Suggested slugDerived from the name; you can change it

The one thing MCP cannot express is producesMedia, a Vidmoat concept. Declare it per tool and your plugin imports with nothing to fill in:

Making your server fully self-describing
{
  "name": "make_thumbnail",
  "description": "Takes a title and an optional face crop and returns a 1280x720 thumbnail.",
  "inputSchema": { "type": "object", "properties": { "title": { "type": "string" } }, "required": ["title"] },
  "_meta": { "vidmoat": { "producesMedia": "image" } }
}

Otherwise tick it in the form. Slug, name and description are editable there too; the tool list is re-read from your server when you submit.

The shape of a call#

Once a user installs your plugin, your tools appear in their agents' tools (through search_agent_capabilities and call_plugin, or directly in tools/list when the client asks for your namespace) and over REST. Both go through the same code path.

Two doors, one plugin
# MCP, for agents
{"method":"tools/call","params":{"name":"my_plugin__do_the_thing","arguments":{...}}}

# REST, for everything else
POST https://api.vidmoat.com/v1/plugins/my-plugin/do_the_thing
Authorization: Bearer vmk_live_...
Content-Type: application/json

{ "arguments": { "subject": "..." }, "projectId": "optional" }

Namespacing#

Your tools are exposed as slug__tool (with - in the slug written as _). Agents pick tools by name, so if a plugin could register render_project, an agent asked to render a video would reach a stranger's server holding a real project id. Slugs are unique across Vidmoat and a plugin claiming one of our tool names is refused, which also means nobody can shadow yours.

What your endpoint receives#

The request your endpoint receives
POST /mcp
X-Vidmoat-Request-Id: 0f0d…            # unique per call
X-Vidmoat-Timestamp: 1756...            # unix seconds
X-Vidmoat-Signature: <hex>              # HMAC-SHA256 of "timestamp.body", Vidmoat's platform key
X-Your-Header: <your secret>            # only if you configured one

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "do_the_thing",             # YOUR name, not the namespaced one
    "arguments": { ... },
    "_meta": { "vidmoat": {
      "requestId": "0f0d…",
      "user": "a1b2…",                  # opaque, stable, per-plugin
      "projectId": "cm…" | null
    }}
  }
}

The user field is an HMAC of the real user id with your plugin id. It is stable, so you can cache per-user work against it, and it is useless to any other plugin, so nobody can follow one person across the catalogue.

Limits, and what happens when you hit them#

LimitValueWhat the caller sees
Response time20 secondsA timeout, counted as a failure
Response body1 MBbody_too_large: return a URL, not inline data
Returned media200 MBThe call fails; nothing is added to the project
RedirectsNot followedredirect: respond directly
Consecutive failures8The plugin is disabled until you fix it and turn it back on

Returning media#

Declare producesMedia on a tool and return a URL. Vidmoat downloads it, checks the content type against what you declared, and copies it into the user's own library before handing back our URL. A plugin's URL written into a saved project is a clip that goes black the day you change hosts; copying it makes plugin output a real asset, rendered exactly like an upload.

A media tool
{
  "name": "rig_character",
  "description": "Rigs a front-facing character illustration and returns a looping idle animation.",
  "inputSchema": { "type": "object", "properties": { "imageUrl": { "type": "string" } }, "required": ["imageUrl"] },
  "_meta": { "vidmoat": { "producesMedia": "video" } }
}

Either of these response shapes is understood:

JSON
{ "content": [{ "type": "text", "text": "{\"url\":\"https://you.dev/out.webm\"}" }] }
JSON
{ "content": { "url": "https://you.dev/out.webm" } }

Writing tool descriptions#

The description is the whole interface. An agent reads it and nothing else when deciding whether to call your tool: never your README or your site. Say what the tool does, what it needs and what it returns, in a sentence someone could act on. Submissions whose descriptions say nothing are held for review.

Visibility#

VisibilityWho can install itReviewedIn the marketplace
privateNobody; only you can call itNoNo
unlistedAnyone you give the slug toYesNo
publicAnyoneYesYes

Unlisted is reviewed like public on purpose: a link gets forwarded, so the moment anybody other than you can install it, somebody has looked at it.

Your slug is taken globally either way, including against plugins you cannot see. Changing private to public or unlisted runs the whole submission check again at that moment, so registering privately and flipping the switch later is not a way around review. Going back to private is immediate; anyone who had installed it keeps the install but loses the tools until you publish again.

Who can use it#

Installing and calling marketplace plugins needs a paid plan (Creator or above). You can always call your own plugins, on any plan. On a Telegram bot you can switch on your own approved plugins for your users.

Review#

You can call your own plugin the moment it is registered. Review gates other people installing it. Most clean submissions publish automatically; a hold always comes with the reason, and the ones that hold are mechanical: an endpoint that does not answer, one that is not an MCP server, or tools the endpoint does not advertise.