AI assistant connector (MCP)
Let an AI assistant plan, draft and report in ViralDashboard for you. The connector speaks the Model Context Protocol and runs every action through the same public API, with the same permissions, plan limits and credits.
Beta. The connector is enabled workspace by workspace while it is in beta. If a correctly
configured key gets 403 “not enabled for this workspace yet”, write to
[email protected] to join.
What it is
An MCP server is a single HTTPS endpoint an AI client can call to discover and use tools (actions), resources (read-only context) and prompts (ready-made workflows). ViralDashboard's server never works around the API: each tool call is one or more public API requests made as your key, so it can only do what that key could do over the API.
| Endpoint | https://viraldashboard.io/mcp |
| Method | POST only. GET and DELETE answer 405. |
| Protocol | MCP 2026-07-28 (stateless, no session). Clients still on the 2025-11-25 handshake are served too. |
| Authentication | Authorization: Bearer vd_live_… (or vd_test_…) |
Who can use it
- Workspaces on the Creator, Pro, Business, Agency and White-Label plans, while admitted to the beta.
- You need an API key. Mint one in Settings → API & Webhooks → API Keys, and pick the scopes for the tools you want (table below). The secret is shown once.
- The key acts as the person who minted it: it sees only the brands that person can access.
Connecting
Point any MCP client that supports the Streamable HTTP transport at the endpoint and send your key as a bearer token. A typical client configuration:
{
"mcpServers": {
"viraldashboard": {
"type": "http",
"url": "https://viraldashboard.io/mcp",
"headers": { "Authorization": "Bearer vd_live_your_key_here" }
}
}
}
To check a key by hand, ask the server to describe itself:
curl https://viraldashboard.io/mcp \
-H "Authorization: Bearer $VD_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "MCP-Protocol-Version: 2026-07-28" \
-H "Mcp-Method: server/discover" \
-d '{"jsonrpc":"2.0","id":1,"method":"server/discover","params":{"_meta":{
"io.modelcontextprotocol/protocolVersion":"2026-07-28",
"io.modelcontextprotocol/clientCapabilities":{}}}}'
Every request carries MCP-Protocol-Version and Mcp-Method, plus Mcp-Name
on tools/call, resources/read and prompts/get; a header that disagrees with
the body is rejected with 400. MCP clients set these for you.
Coming soon: one-click OAuth connectors for Claude and ChatGPT (sign in with ViralDashboard instead of pasting a key), and listings in their connector directories. Until then, connect with a key as above.
Tools and the scopes they need
A tool is listed only when the key holds all of its scopes. A key without them simply doesn't see the tool; mint a key with the scope to add it.
| Tool | Scopes | Kind | What it does |
|---|---|---|---|
vd_list_workspace |
brands:read accounts:read |
Read | List brands and connected accounts |
vd_get_calendar |
posts:read |
Read | Get the content calendar |
vd_get_post |
posts:read |
Read | Get one post |
vd_get_performance |
analytics:read |
Read | Get analytics performance |
vd_list_inbox |
inbox:read |
Read | List inbox conversations |
vd_get_conversation |
inbox:read |
Read | Get an inbox conversation |
vd_list_media |
media:read |
Read | List the media library |
vd_list_feed_articles |
feeds:read |
Read | List RSS feeds and their latest articles |
vd_list_content_plans |
content:read |
Read | List content plans |
vd_list_reports |
reports:read |
Read | List reports and schedules |
vd_create_post |
posts:write |
Write | Create a post (draft, schedule or publish now) |
vd_update_post |
posts:write |
Write | Edit a draft or scheduled post |
vd_submit_post_for_approval |
posts:write |
Write | Submit a draft for approval |
vd_import_media_from_url |
media:write |
Write | Import an image into the media library from a URL |
vd_write_caption |
ai:write |
Write | Write captions in the brand voice |
vd_generate_image |
ai:write |
Write | Generate an image into the media library |
vd_reply_to_conversation |
inbox:write inbox:read |
Write | Reply to an inbox conversation |
vd_triage_conversation |
inbox:write |
Write | Triage an inbox conversation |
vd_schedule_plan_item |
content:write |
Write | Turn a content-plan item into a post |
vd_create_report |
reports:write |
Write | Create an analytics report |
vd_export_report |
reports:write |
Write | Export a report |
vd_refresh_account |
accounts:write |
Write | Refresh a social account's login |
vd_get_connect_link |
accounts:write |
Write | Get a link to connect a social account |
vd_get_job_status |
Read | Check background work | |
vd_delete_post |
posts:write |
Destructive | Delete a post from ViralDashboard |
vd_disconnect_account |
accounts:write |
Destructive | Disconnect a social account from a brand |
vd_delete_webhook |
webhooks:write |
Destructive | Delete a webhook endpoint |
vd_hide_comment |
inbox:write inbox:read |
Destructive | Hide a comment on the platform |
vd_unhide_comment |
inbox:write |
Write | Unhide a comment on the platform |
vd_moderation_rules |
inbox:read |
Write | Inbox moderation rules: list, audit, create (test mode) |
vd_search_mentions |
listening:read |
Read | Search social-listening mentions |
vd_manage_bio_page |
linkinbio:write linkinbio:read |
Write | Update a link-in-bio page |
vd_automation_rules |
automation:read |
Destructive | Posting automation rules: list, create, edit, pause, delete, run history |
vd_evergreen |
automation:read |
Destructive | Evergreen queue: recycle posts on a schedule |
vd_bulk_schedule |
automation:read |
Write | Bulk schedule posts from a CSV (preview first) |
Resources
Read-only context an assistant can load before it acts. Read with resources/read.
| URI | Scope | Contents |
|---|---|---|
vd://brands/{brand_id}/voice |
brands:read |
How a brand sounds: industry, target audience, content topics, values, products and services, and its voice profile (at most 2000 characters). This is the context ViralDashboard's own AI writes with; use it to keep anything you draft on-voice. Brand ids come from vd_list_workspace. |
vd://brands/{brand_id}/kit |
brands:read |
A brand's visual identity from its brand kit: colours as {name, hex}, fonts, and logo URLs. `configured` is false when the brand has no kit yet. Brand ids come from vd_list_workspace. |
vd://account/plan |
account:read |
The workspace's plan and usage, read-only: plans held, enabled features, usage and remaining room for every plan limit (including posts this month), the AI credit balance, AI capacity pools, and X posts used vs. included. Check it before suggesting work that needs a feature or uses credits. It can't be changed from here: never offer to upgrade or buy; an owner does that in Billing. |
vd://capabilities |
any key | What ViralDashboard can do on each social platform right now: connect, publish (post and media types), analytics, inbox read, comment and DM replies, private replies, hiding and deleting comments, and listening. Every unsupported cell carries the reason. Only plan or propose actions a platform supports, and tell the user the reason when it doesn't. |
Prompts
| Prompt | Arguments | What it does |
|---|---|---|
weekly_plan |
brand_id (required)week_startgoals |
Plans a week of on-brand posts for one brand from its voice, kit and recent performance, only in formats each connected platform supports, and saves them as drafts. Nothing is published. |
client_report |
brand_id (required)period (required)format |
Writes a client-ready performance summary for one brand and period: totals compared with the previous period, insights, top posts and a short narrative. Can also export a formatted report file. |
inbox_triage |
brand_id (required)limit |
Goes through a brand's open inbox conversations: classifies intent and urgency, proposes status, assignee, tags and priority, and drafts replies in brand voice. Replies are only sent after the user confirms each one, and never where the platform can't take a reply. |
The confirm step
Anything that reaches the outside world or can't be undone — publishing now, replying to someone, hiding a comment, deleting or disconnecting — is never done on the first call. The first call runs every check, changes nothing, and returns:
{
"status": "confirmation_required",
"preview": { "…": "what will happen" },
"confirm_token": "…",
"expires_in_seconds": 300
}
The assistant shows the preview to you, and only if you agree calls the tool again with the
same arguments plus confirm_token. A token works once, for those exact arguments,
for the key that asked for it, and expires after 5 minutes. Anything else
returns confirmation_invalid. Drafts never need confirming.
Test keys are dry runs
With a vd_test_… key, reads return live data and every write is a dry run: it passes
through every scope, brand, plan and validation check and then stops, returning
"status": "dry_run" with what would have happened. Nothing is created, sent or charged. Use a test key
to try a workflow safely, and a live key to do it.
Retries are safe
Every write carries an idempotency key.
If the assistant repeats a call with identical arguments (after a dropped connection, say), it gets the original result
back instead of a second post or a second charge. To do the same thing twice on purpose, pass a new
idempotency_key argument.
Errors
Connection-level
These come back as an HTTP status with a JSON-RPC error, before any tool runs:
| Status | Means | Fix |
|---|---|---|
401 | Missing, invalid, revoked or expired key (with a WWW-Authenticate header). | Check the key, or mint a new one. |
403 | The workspace isn't admitted to the beta yet, its plan doesn't include the connector, or a browser Origin isn't allowed. | Ask to join the beta, or an owner can upgrade in Billing. |
404 | The connector is switched off. | Try again later. |
400 | Unsupported protocol version, or headers that don't match the body. | Update the client. |
429 | Too many requests (see rate limits). | Wait for Retry-After seconds. |
503 | The ViralDashboard API is switched off, so tools can't run. | Try again later. |
Tool errors
A tool that can't do what was asked returns a normal result with isError: true. Its
structuredContent.error carries the public API's code, type and
message unchanged, plus a remediation written for the assistant and whether a
retryable retry can help:
{
"isError": true,
"structuredContent": { "error": {
"code": "limit_reached", "type": "invalid_request_error", "http_status": 402,
"message": "…", "remediation": "…", "retryable": false, "details": { }
} }
}
A resource that can't be read returns a JSON-RPC error with the same object in data.error. A resource
that doesn't exist, or that you can't access, is -32602.
Codes
| Code | What the assistant is told to do | Retry? |
|---|---|---|
unauthorized |
Reconnect the connector, or check the API key. | No |
insufficient_scope |
This key is missing a scope this tool needs. Mint a key with that scope in Settings → API keys. | No |
test_mode_write_forbidden |
Test keys are read-only. Use a live key to write. | No |
brand_forbidden |
You don't have access to this brand. Call vd_list_workspace for the brands you can use. | No |
forbidden |
Not allowed on this resource (for example, the conversation is not in this brand). | No |
feature_not_available |
The workspace plan doesn't include this feature. An owner can upgrade in Billing; don't offer to do it here. | No |
limit_reached |
A plan limit is reached. Free up capacity, wait for the reset (posts reset monthly), or an owner can upgrade in Billing. | No |
insufficient_credits |
Not enough AI credits. An owner can top up in Billing. | No |
storage_limit_exceeded |
Media storage is full. Delete media in the app or upgrade storage. | No |
payload_too_large |
The file is over the size limit (10 MB images, 50 MB video). | No |
unsupported_media_type |
Use JPEG, PNG, GIF or WebP images, or MP4/MOV video. | No |
invalid_media_url |
The URL must be public HTTPS and reachable; private and internal addresses are blocked. | No |
invalid_feed_url |
The URL must be public HTTPS and reachable; private and internal addresses are blocked. | No |
invalid_webhook_url |
The URL must be public HTTPS and reachable; private and internal addresses are blocked. | No |
invalid_site_url |
The URL must be public HTTPS and reachable; private and internal addresses are blocked. | No |
invalid_account |
Pick accounts connected to this brand (see vd_list_workspace). | No |
invalid_credentials |
Connect this platform in the app (Settings → Social accounts). | No |
oauth_required |
Connect this platform in the app (Settings → Social accounts). | No |
not_oauth_platform |
This platform is connected with credentials in the app, not with a link. | No |
interactive_setup_required |
This platform needs interactive setup in the app (Settings → Social accounts). | No |
platform_unavailable |
This platform isn't open for connection right now. | No |
authorize_failed |
The platform refused to start authorization. Try again later. | Yes |
reconnect_required |
The account's login expired. The user must reconnect it (vd_get_connect_link). | No |
platform_capability_unavailable |
The platform can't do this right now; the message says why. Don't retry. | No |
moderation_paused |
Automation is switched off for this brand, so comments can't be hidden. A person can switch it back on in the app (Automation → Auto-Replies); don't retry until then. | No |
platform_error |
The platform refused the action; the message is its reason. Fix what it says (often a missing permission: reconnect the account), then try once more. | No |
idempotency_conflict |
The same request is still running. Wait a few seconds and retry with the same arguments. | Yes |
idempotency_error |
This idempotency_key was already used with different arguments. Use a new key. | No |
rate_limited |
Rate limit reached. Retry after the number of seconds in retry_after. | Yes |
invalid_request |
Fix the fields listed in details.errors and call again. | No |
not_found |
It doesn't exist or isn't visible to you. | No |
post_not_editable |
Published posts can't be edited. Create a new post instead. | No |
post_not_draft |
Only drafts can be submitted for approval. | No |
post_not_pending |
Only posts pending approval can be approved or rejected. | No |
generation_failed |
The AI returned nothing and the credits were refunded. To try again, call with a new idempotency_key (an identical call replays this failure). | No |
evergreen_max_reached |
The entry has republished as many times as max_republishes allows. Raise max_republishes (action "update"), then resume it. | No |
confirmation_invalid |
Call the tool again without confirm_token to get a fresh preview, and ask the user again. | No |
handle_expired |
That job handle is unknown or expired. Start the action again if needed. | No |
internal_error |
Something failed on our side and it has been logged. Retry once, then stop. | Yes |
The API's own codes are described in Errors.
Rate limits
- The connector accepts up to 120 requests a minute per key. Over that, it answers
429withRetry-After. - Each tool call also counts against the key's hourly API rate limit, once for every API request it makes (a tool that lists brands and their accounts makes one request per brand). A tool that hits that limit returns
rate_limitedwithretry_after. - AI tools spend the workspace's AI credits, exactly as the API does.
Support
Questions, beta access or a bug: [email protected].