# 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 read | From |
| --- | --- |
| Tool names, descriptions, input schemas | `tools/list` (at most 32 tools; descriptions up to 2,000 characters) |
| Display name | `initialize`, `serverInfo.title` (or `.name`) |
| Description | `initialize`, `instructions` |
| Suggested slug | Derived 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:

```json 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.

```text 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

```text 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
    }}
  }
}
```

> **Tip: Authenticate our calls with your own header.** The signature is made with a Vidmoat platform key that is not shared with plugin authors, so you cannot check it yourself. To know a request came from Vidmoat, set a secret header name and value in the plugin form and reject requests without it.

> **Warning: You never receive the caller's Vidmoat credentials.** Not their API key, not their session, not their scopes: there is no mechanism by which you could. Your plugin acts on its own behalf. If it needs data from the user's Vidmoat account, ask them for their own API key through your own flow.

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

| Limit | Value | What the caller sees |
| --- | --- | --- |
| Response time | 20 seconds | A timeout, counted as a failure |
| Response body | 1 MB | `body_too_large`: return a URL, not inline data |
| Returned media | 200 MB | The call fails; nothing is added to the project |
| Redirects | Not followed | `redirect`: respond directly |
| Consecutive failures | 8 | The plugin is disabled until you fix it and turn it back on |

> **Important: Your endpoint must resolve to a public address on every call.** Not just at registration: DNS is checked again each time. Development tunnels (ngrok and friends) are refused for publication; they stop answering the moment you close your laptop. They register fine as a private plugin.

## 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.

```json 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

| Visibility | Who can install it | Reviewed | In the marketplace |
| --- | --- | --- | --- |
| `private` | Nobody; only you can call it | No | No |
| `unlisted` | Anyone you give the slug to | Yes | No |
| `public` | Anyone | Yes | Yes |

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.

> **Warning: Private skips review, and only review.** Everything mechanical still applies: the namespace rules, reserved slugs, the collision check against our own tool names, the public-address check on every call, the request signature, the response caps and the failure breaker.

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](https://developer.vidmoat.com/developer/docs/telegram/behaviour) 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.

- [Submit a plugin](https://developer.vidmoat.com/developer/plugins): Paste your endpoint URL. We read your server and show you what we got before anything is created.
- [Call a plugin tool](https://developer.vidmoat.com/developer/docs/api/plugins/invoke-plugin-tool): The REST reference.

---

Source: https://developer.vidmoat.com/developer/docs/plugins
Previous: [MCP server](https://developer.vidmoat.com/developer/docs/mcp.md)
Next: [Changelog](https://developer.vidmoat.com/developer/docs/changelog.md)
All documentation: https://developer.vidmoat.com/llms-full.txt
