MCP Server
The @voxburst/mcp-server package lets AI assistants (Claude, Cursor, Windsurf, and others) manage your VoxBurst workspace through the Model Context Protocol (MCP) .
Installation
npm install -g @voxburst/mcp-serverOr run directly without installing:
npx @voxburst/mcp-serverConfiguration
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
VOXBURST_API_KEY | Yes | — | Your VoxBurst API key (vb_live_…) |
VOXBURST_API_URL | No | https://api.voxburst.io | API base URL (override for self-hosted) |
Get your API key from VoxBurst Settings . Use a key with the scopes your agent needs (at minimum posts:write and accounts:read).
Pass your API key via the env block in the MCP config — never hard-code it in the config file, which may be committed to source control.
Claude Desktop
Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"voxburst": {
"command": "npx",
"args": ["@voxburst/mcp-server"],
"env": {
"VOXBURST_API_KEY": "vb_live_xxxxxxxxxxxxx"
}
}
}
}Restart Claude Desktop after saving the config.
Cursor
Add to your Cursor MCP settings (.cursor/mcp.json or the global MCP config):
{
"mcpServers": {
"voxburst": {
"command": "npx",
"args": ["@voxburst/mcp-server"],
"env": {
"VOXBURST_API_KEY": "vb_live_xxxxxxxxxxxxx"
}
}
}
}Windsurf / VS Code with MCP Extension
{
"mcp.servers": {
"voxburst": {
"command": "npx",
"args": ["@voxburst/mcp-server"],
"env": {
"VOXBURST_API_KEY": "vb_live_xxxxxxxxxxxxx"
}
}
}
}Available Tools
Post Management
create_post
Create a new post or schedule it for later.
| Parameter | Type | Required | Description |
|---|---|---|---|
content | string | Yes | Post content |
accountIds | string[] | Yes | Target account IDs |
scheduledFor | string | No | ISO 8601 datetime for scheduling |
mediaUrls | string[] | No | Public HTTPS URLs for images or videos to attach. Each URL is automatically registered with VoxBurst. Local file paths are not supported. |
mediaIds | string[] | No | VoxBurst media IDs for files already uploaded via POST /v1/media/upload. Use this when you have pre-uploaded media. |
saveAsDraft | boolean | No | Save as draft without publishing |
contentType | string | No | IMAGE, VIDEO, CAROUSEL, REEL, STORY, TEXT, or THREAD. Required for Instagram — omitting it will cause the post to be rejected. |
firstComment | string | No | Text to auto-post as a comment immediately after publishing (max 2,200 chars). Supported on Instagram, LinkedIn, YouTube. Facebook support is pending Meta API approval — posts with this field set will publish normally, but the comment is silently skipped on Facebook until approval is granted. |
firstCommentDelay | integer | No | Seconds to wait before posting the first comment (0–3600, default 0) |
Media: Use mediaUrls for publicly accessible HTTPS image/video URLs — the MCP server registers them with VoxBurst automatically. Use mediaIds if you have already uploaded files to VoxBurst and have their IDs. You can pass both in the same request. Local file paths are not accepted.
list_posts
List recent posts from the workspace.
| Parameter | Type | Required | Description |
|---|---|---|---|
status | string | No | Filter by status: draft, approved, scheduled, publishing, published, failed, partial, cancelled, unpublished, archived, or all |
accountId | string | No | Filter posts by a specific account ID |
limit | integer | No | Number of posts to return (1–100, default: 20) |
cursor | string | No | Pagination cursor from the previous response for fetching the next page |
get_post
Get a single post by ID.
delete_post
Delete a post by ID. Only draft and scheduled posts can be deleted — published posts cannot be deleted through this tool.
update_post
Update a draft or scheduled post.
| Parameter | Type | Required | Description |
|---|---|---|---|
postId | string | Yes | Post ID to update |
content | string | No | New post text |
scheduledFor | string | No | New ISO 8601 scheduled time |
accountIds | string[] | No | Replace target accounts |
contentType | string | No | New content type (IMAGE, VIDEO, CAROUSEL, REEL, STORY, TEXT, THREAD) |
firstComment | string | No | New first comment text |
firstCommentDelay | integer | No | Seconds before first comment (0–3600) |
media | string[] | No | Replace attached media — pass VoxBurst media IDs |
platformOverrides | object | No | Per-platform content overrides, e.g. { "INSTAGRAM": { "content": "..." } } |
cancel_post
Cancel a scheduled or draft post by postId. Cannot cancel posts that are already published or being published.
retry_post
Retry a post by postId. Works on posts with status failed (all platforms failed) or partial (some platforms failed, others succeeded). Successfully-published platforms are never re-posted — only failed platforms are re-queued.
validate_content
Validate content against platform-specific rules before posting.
| Parameter | Type | Required | Description |
|---|---|---|---|
content | string | Yes | Content to validate |
platforms | string[] | Yes | Platform constants to validate against — e.g. ["TWITTER", "INSTAGRAM"]. Not account IDs. |
Example response:
{
"valid": false,
"platforms": {
"TWITTER": {
"valid": false,
"errors": [
{ "code": "CONTENT_TOO_LONG", "message": "Content exceeds Twitter's 280 character limit (current: 312)" }
],
"warnings": []
},
"INSTAGRAM": {
"valid": true,
"errors": [],
"warnings": [
{ "code": "NO_MEDIA", "message": "Instagram feed posts require at least one image or video" }
]
}
}
}The top-level valid is false if any platform has errors. warnings do not fail validation but indicate potential issues. Check per-platform errors to know exactly what to fix before posting.
Account Management
list_accounts
List all connected social media accounts. No parameters required.
get_account
Get details of a specific account by accountId.
Analytics
get_post_metrics
Get engagement metrics for a post (impressions, likes, shares, comments, clicks).
get_account_metrics
Get metrics for an account (followers, following, total posts, average engagement rate).
Available Resources
Resources provide read-only access to VoxBurst data via URI:
| URI | Description |
|---|---|
accounts://list | All connected accounts |
accounts://{accountId} | Single account details |
posts://recent | Last 20 posts |
posts://{postId} | Single post details |
Example Usage
Once configured, you can ask your AI assistant to:
- “Post ‘Just shipped a new feature!’ to my Twitter and LinkedIn accounts.”
- “Schedule a post for next Monday at 10 AM: ‘Monday motivation…’”
- “How did my last post perform?”
- “Show me all my scheduled posts.”
- “Delete the draft post I created earlier.”
The MCP server handles translating these requests into VoxBurst API calls automatically.
Instagram requires media on every post and a contentType to specify the post format. Always call list_accounts first to get the account ID — Instagram account IDs start with acc_.
Instagram feed posts require at least one image or video. Text-only posts will be rejected. Always specify contentType.
Single image post
create_post(
content: "Behind the scenes 📸",
accountIds: ["acc_instagram_123"],
contentType: "IMAGE",
mediaUrls: ["https://cdn.example.com/photo.jpg"]
)Carousel (2–10 images or videos)
create_post(
content: "Swipe to see more →",
accountIds: ["acc_instagram_123"],
contentType: "CAROUSEL",
mediaUrls: [
"https://cdn.example.com/slide-1.jpg",
"https://cdn.example.com/slide-2.jpg",
"https://cdn.example.com/slide-3.jpg"
]
)Reel (short-form video)
create_post(
content: "Our latest drop 🔥",
accountIds: ["acc_instagram_123"],
contentType: "REEL",
mediaUrls: ["https://cdn.example.com/reel.mp4"],
firstComment: "#reels #newdrop #launch"
)Scheduled post
create_post(
content: "Launch day. Shipping now.",
accountIds: ["acc_instagram_123"],
contentType: "CAROUSEL",
mediaUrls: [
"https://cdn.example.com/launch-1.jpg",
"https://cdn.example.com/launch-2.jpg"
],
scheduledFor: "2026-06-01T15:00:00Z"
)First comment with delay
create_post(
content: "New product just dropped.",
accountIds: ["acc_instagram_123"],
contentType: "IMAGE",
mediaUrls: ["https://cdn.example.com/product.jpg"],
firstComment: "Shop the link in bio 👆",
firstCommentDelay: 30
)firstCommentDelay is in seconds. The example above posts the comment 30 seconds after the photo publishes.
Post Status Lifecycle
Understanding how post status flows helps you know which tool actions are valid at each point.
draft ──────────────→ scheduled ──→ publishing ──→ published
↑ ↑ ↓ ↓
│ │ failed partial
│ │ ↓ ↓
└── update_post └── (after retry retry
└── cancel_post unschedule) ↓ ↓
└── delete_post published publishedWhich tools are available from each status:
| Status | update_post | cancel_post | retry_post | delete_post |
|---|---|---|---|---|
draft | ✅ | ✅ | ✗ | ✅ |
approved | ✅ | ✅ | ✗ | ✅ |
scheduled | ✅ | ✅ | ✗ | ✅ |
publishing | ✗ | ✗ | ✗ | ✗ |
published | ✗ | ✗ | ✗ | ✗ |
failed | ✅ | ✗ | ✅ | ✅ |
partial | ✅ | ✗ | ✅ | ✗ |
cancelled | ✗ | ✗ | ✗ | ✅ |
archived | ✗ | ✗ | ✗ | ✗ |
retry_post re-queues only platforms with status: "failed". Platforms that already published are never re-posted — it is safe to call even on a partial post.
Security Notes
- API keys are passed via environment variables and never logged
- All API communication uses HTTPS
- The server has no persistent state between calls
- Runs in stdio mode — no network port is opened
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
VOXBURST_API_KEY environment variable is required | Key not set in env block | Add VOXBURST_API_KEY to the env section of your MCP config |
VoxBurst API Error (401): Invalid API key | Key invalid or expired | Generate a new API key in VoxBurst Settings |
VoxBurst API Error (429): Rate limit exceeded | Too many requests | Wait and retry; the server does not auto-retry rate limits |
VoxBurst API Error (400): Content exceeds platform limit | Post too long | Shorten the content or use platform overrides |
Tool Response Shapes
What each tool returns so your agent knows what to expect.
list_accounts
[
{
"id": "acc_instagram_abc123",
"platform": "instagram",
"username": "mybrand",
"displayName": "My Brand",
"connected": true,
"lastSync": "2026-05-30T10:00:00Z"
},
{
"id": "acc_twitter_xyz456",
"platform": "twitter",
"username": "mybrand_x",
"displayName": "My Brand X",
"connected": true
}
]create_post — success
{
"id": "post_abc123",
"content": "Launch day. Shipping now.",
"status": "scheduled",
"scheduledFor": "2026-06-01T15:00:00Z",
"contentType": "CAROUSEL",
"platforms": [
{
"platform": "instagram",
"accountId": "acc_instagram_abc123",
"status": "pending",
"platformPostId": null,
"platformPostUrl": null,
"publishedAt": null,
"error": null
}
],
"createdAt": "2026-05-30T10:05:00Z"
}get_post — after publishing
{
"id": "post_abc123",
"content": "Launch day. Shipping now.",
"status": "published",
"publishedAt": "2026-06-01T15:00:05Z",
"platforms": [
{
"platform": "instagram",
"status": "published",
"platformPostUrl": "https://www.instagram.com/p/ABC123/",
"publishedAt": "2026-06-01T15:00:05Z",
"error": null
}
]
}get_post — partial failure
{
"id": "post_abc123",
"status": "partial",
"platforms": [
{
"platform": "instagram",
"status": "published",
"platformPostUrl": "https://www.instagram.com/p/ABC123/",
"error": null
},
{
"platform": "twitter",
"status": "failed",
"platformPostUrl": null,
"error": "Content exceeds Twitter's 280 character limit",
"retryCount": 1
}
]
}list_posts
{
"data": [
{
"id": "post_abc123",
"content": "Launch day.",
"status": "published",
"publishedAt": "2026-06-01T15:00:05Z"
},
{
"id": "post_def456",
"content": "Coming soon...",
"status": "scheduled",
"scheduledFor": "2026-06-03T10:00:00Z"
}
],
"pagination": {
"has_more": true,
"next_cursor": "eyJpZCI6InBvc3RfZGVmNDU2In0",
"limit": 20
}
}validate_content — with errors
{
"valid": false,
"platforms": {
"TWITTER": {
"valid": false,
"errors": [
{ "code": "CONTENT_TOO_LONG", "message": "Content exceeds Twitter's 280 character limit (current: 312)" }
],
"warnings": []
},
"INSTAGRAM": {
"valid": true,
"errors": [],
"warnings": [
{ "code": "NO_MEDIA", "message": "Instagram feed posts require at least one image or video" }
]
}
}
}valid: false at the top level means at least one platform has errors. Warnings are advisory — they do not block posting.
retry_post
{
"id": "post_abc123",
"status": "publishing",
"retriedPlatforms": [
{ "platform": "twitter", "retryCount": 2, "nextRetryAt": "2026-06-01T15:04:00Z" }
],
"skippedPlatforms": []
}If a platform has exhausted all 3 retries, it appears in skippedPlatforms with reason: "max_retries_exceeded". Use the REST API /v1/posts/:id/platforms/:platformId/fix to reset and resubmit.
Field Mapping Across Surfaces
Use this table when switching between MCP, REST API, TypeScript SDK, and Go SDK to avoid confusion.
| Concept | MCP | REST API | TypeScript SDK | Go SDK |
|---|---|---|---|---|
| Target accounts | accountIds | accountIds | accountIds | AccountIds |
| Public URL media | mediaUrls | register via /media/register, then media | register, then media | Media.RequestUploadURL |
| Pre-uploaded media | mediaIds | media | media | Media |
| Post format | contentType | contentType | contentType | ContentType |
| Platform overrides | platformOverrides | platformOverrides | platformOverrides | PlatformOverrides |
| First comment | firstComment | firstComment | firstComment | FirstComment |
| Delay before comment | firstCommentDelay | firstCommentDelay | firstCommentDelay | FirstCommentDelay |
| Schedule time | scheduledFor | scheduledFor | scheduledFor | ScheduledFor |
| Save without publishing | saveAsDraft: true | saveAsDraft: true | saveAsDraft: true | SaveAsDraft: true |
| Validate content | validate_content(content, platforms) | POST /v1/posts/validate | client.posts.validate() | REST API directly |
| Retry failed post | retry_post(postId) | POST /v1/posts/:id/retry | client.posts.retry(id) | client.Posts.Retry(ctx, id) |
Key differences to remember:
- MCP
mediaUrlsaccepts public HTTPS URLs and registers them automatically. The REST API has nomediaUrlsfield — you must register or upload first, then pass the resultingmediaIdasmedia. accountIdsis always an array, even when posting to a single account:["acc_abc123"].- Platform constants are uppercase:
"INSTAGRAM","TWITTER". Theaccount.platformfield in responses is lowercase:"instagram","twitter".
Common Failures Cookbook
Post fails immediately (validation error)
Your agent receives a 400 error before the post is created. Check validate_content first:
validate_content(
content: "your caption",
platforms: ["INSTAGRAM"]
)Common causes: caption too long, wrong contentType, missing media for Instagram.
Post is created but status stays pending for a long time
Normal — posts go through a scheduling queue. Use get_post to poll. If status is still pending after 5 minutes, check the VoxBurst dashboard for queue status.
Post status is failed
get_post(postId: "post_abc123")Read platforms[].error for the specific failure reason per platform. Common Instagram failures:
platforms[].error | Meaning | Fix |
|---|---|---|
MEDIA_REQUIRED | No media on an Instagram post | Add mediaUrls or mediaIds |
ASPECT_RATIO_INVALID | Image ratio outside 4:5–1.91:1 | Re-crop image |
CONTENT_TYPE_NOT_SUPPORTED | STORY used for Instagram | Change to IMAGE, VIDEO, CAROUSEL, or REEL |
ACCOUNT_DISCONNECTED | OAuth token expired | Re-connect account in VoxBurst settings |
| Error code 9004 | Instagram rejected the image bytes | VoxBurst auto-normalizes; use /fix with a fresh media URL |
Then retry:
retry_post(postId: "post_abc123")Post status is partial
Some platforms published, others failed. retry_post re-queues only the failed platforms — it will not re-post to platforms that already succeeded.
Platform has retryCount: 3 and is skipped by retry
The platform has exhausted automatic retries. Use the REST API directly:
curl -X POST https://api.voxburst.io/v1/posts/post_abc123/platforms/pp_xyz789/fix \
-H "Authorization: Bearer vb_live_xxxxxxxxxxxxx" \
-d '{ "mediaUrl": "https://cdn.example.com/corrected-image.jpg" }'Replace pp_xyz789 with the postPlatformId from get_post’s platforms array.
Consistency Reference
The MCP server, REST API, TypeScript SDK, and Go SDK all interact with the same VoxBurst backend. Each surface uses the same field semantics — only the naming conventions differ slightly.
If you are using the MCP server: Follow MCP field names only (accountIds, mediaUrls, mediaIds, contentType). The server handles the REST API translation.
If you are using the REST API directly: Use accountIds, media (for IDs), contentType. Register external URLs via POST /v1/media/register first.
If you are using the TypeScript SDK: Same field names as REST (accountIds, media, contentType). Use client.posts.create(), client.media.requestUploadURL().
If you are using the Go SDK: Same concepts, Go-idiomatic capitalization (AccountIds, Media, ContentType). Use client.Posts.Create(), client.Media.RequestUploadURL().
A post created via MCP, checked via REST, and monitored via the TypeScript SDK will have the same post.id and the same status values throughout.