Bredgio MCP Server · Specification
Status — design only. The
bredgio-mcpnpm package described here is not yet published. This document is the contract we're building against. Once we ship, this file will be updated with the live install instructions.
What it is
bredgio-mcp is a Model Context Protocol
server that wraps the Bredgio HTTP API and surfaces a small,
opinionated tool set for AI assistants — Claude Desktop, Cursor, Cline,
ChatGPT with MCP, and anything else that speaks MCP.
After installing the server, an end-user can say things like:
"List my Bredgio agents that are online, then schedule a daily Facebook post at 9 AM Taipei time on the marketing seat saying 'Good morning ☀️'."
…and the assistant will resolve which agent to use, validate the schedule shape, and call the API end-to-end without the user touching curl.
Why MCP instead of just exposing the API
- Zero glue code. Users don't write Python scripts to call our endpoints — the assistant does.
- Discoverability. Tool schemas teach the assistant what's possible without us writing prose for it to read.
- Safety boundary. The server holds the API key locally and pre-validates inputs (enum values, future timestamps, ownership) before any HTTP call leaves the user's machine.
- Token rotation friendliness. Re-running the install prompt swaps the credential in
claude_desktop_config.jsonwithout manual JSON editing.
Target deliverable
| Item | Detail |
|---|---|
| Package name | bredgio-mcp |
| Runtime | Node ≥ 20, ESM |
| Distribution | npm (run via npx -y bredgio-mcp) |
| Transport | stdio (default) — JSON-RPC over stdin/stdout, the MCP standard |
| License | MIT |
| Source of truth | This repo, under mcp/ (TBD) |
Authentication model
The user obtains a Personal Access Token from the Bredgio dashboard (Settings → API Keys → New PAT). The token is scoped to one account and carries every project the user owns or is a member of.
Plan gate — paid plans only
PAT issuance is a paid feature. Free-plan accounts can use the
dashboard and operate agents normally, but cannot mint a PAT and
therefore cannot run bredgio-mcp.
| Plan | MCP server | PAT issuance |
|---|---|---|
| Free | ❌ | ❌ |
| Pro | ✅ | ✅ |
| Team / Enterprise | ✅ | ✅ (multi-seat) |
If bredgio-mcp boots with an invalid or missing PAT — including one
that was auto-revoked because the user downgraded — it exits early with
a clear message routed back to the assistant:
[bredgio-mcp] Authentication failed: plan_upgrade_required
Upgrade at https://bredgio.letmesee.cc/dashboard/billing to enable API access.
See API.md → Plan gate for the full plan matrix and error envelope.
Configuration
The server reads the token from an environment variable:
{
"mcpServers": {
"bredgio": {
"command": "npx",
"args": ["-y", "bredgio-mcp"],
"env": {
"BREDGIO_API_KEY": "bk_pat_..."
}
}
}
}
No tokens are persisted on disk by the server itself. Removing the entry from the MCP config revokes the assistant's access locally; calling Revoke in the dashboard revokes it globally.
The server also accepts a BREDGIO_BASE_URL override for staging /
self-hosted deployments. Default is https://bredgio.letmesee.cc.
Tool catalogue
Nine tools. Each maps cleanly to one or two endpoints in the HTTP API.
| Tool | Wraps | Purpose |
|---|---|---|
list_agents |
GET /api/agents |
Inventory of agents and their online state. |
get_agent |
GET /api/agents/:id |
Drill into one agent, including recent tasks. |
dispatch_post |
POST /api/agents/:id/command |
Publish a post (immediately or scheduled once, with optional images). |
dispatch_group_post |
POST /api/agents/:id/command (action: post-to-groups) |
Search FB public groups by topic and post into the ones the agent has joined. |
list_tasks |
GET /api/tasks |
Browse task history. |
get_task |
GET /api/tasks/:id |
Inspect a single task including its step log. |
create_schedule |
POST /api/schedules |
Set up daily / weekly / cron posting. |
list_schedules |
GET /api/schedules |
Review what's scheduled. |
update_schedule |
PATCH /api/schedules/:id |
Pause, resume, edit content, change timing. |
Media (image) upload is not exposed as an MCP tool — the MCP stdio
transport isn't a good fit for shipping file bytes. Call the REST
endpoint POST /api/media/upload (same PAT works) and pass the returned
url into dispatch_post.media[]. See
Attaching media.
Tools that write (dispatch_post, create_schedule, update_schedule)
emit MCP "elicitation" messages when destructive — e.g. asking the user
to confirm before pausing every schedule — so the assistant can surface a
yes/no prompt.
Out of scope on purpose. Agent creation, token regeneration, login, and password operations are not exposed. Those are sensitive enough that we route them through the dashboard's UI, not through an AI-driven tool surface.
list_agents
List Bredgio agents the user owns. Optionally filter to online ones only.
Input schema
{
"type": "object",
"properties": {
"onlyOnline": {
"type": "boolean",
"description": "If true, return only agents currently connected to the WS hub.",
"default": false
}
}
}
Output
{
"agents": [
{
"agentId": "65fc...",
"name": "Marketing seat #1",
"status": "online", // online · offline · paused
"platforms": ["facebook", "twitter"],
"lastSeenAt": "2026-05-18T03:14:15Z",
"todayCount": 7 // tasks dispatched in last 24h
}
]
}
get_agent
Inspect one agent's config and recent activity.
Input
{
"type": "object",
"required": ["agentId"],
"properties": { "agentId": { "type": "string" } }
}
Output — { agent, recentTasks[20], todayCount }. See GET /api/agents/:id.
dispatch_post
Publish a single post on a platform via the named agent. Returns the
taskIdso the assistant can poll status withget_task.
Input schema
{
"type": "object",
"required": ["agentId", "platform", "content"],
"properties": {
"agentId": { "type": "string" },
"platform": { "type": "string", "enum": ["facebook", "twitter"] },
"content": { "type": "string", "minLength": 1, "maxLength": 60000,
"description": "Post body. Newlines (\\n) are preserved. X / Twitter rejects posts over 280 characters." },
"media": {
"type": "array", "maxItems": 10,
"description": "Optional images. Array order is the order they appear on Facebook.",
"items": {
"type": "object",
"required": ["url"],
"properties": {
"url": { "type": "string",
"description": "Publicly fetchable image URL. Prefer the value returned by POST /api/media/upload — pass it back unchanged." },
"type": { "type": "string",
"enum": ["image/jpeg", "image/png", "image/webp", "image/gif"] }
}
}
},
"scheduledFor": {
"type": "string", "format": "date-time",
"description": "Optional ISO 8601 timestamp in the future. If omitted, dispatch immediately."
}
}
}
platform currently accepts "facebook" and "twitter" (covers x.com);
the enum will grow as more flows ship server-side. Video and reels are
not supported. X rejects posts over 280 characters. See
Attaching media for how to populate media[].
Output
{
"taskId": "65fc...",
"status": "pending", // pending · dispatched
"dispatchedAt": "2026-05-18T03:14:15Z",
"watchUrl": "https://bredgio.letmesee.cc/dashboard/tasks/65fc..."
}
Elicitation — dispatch_post MUST confirm with the user before
posting when scheduledFor is omitted and the agent has already
dispatched ≥ agent.config.dailyOutreachLimit tasks in the last 24 h.
This prevents accidental over-posting via runaway agent loops.
Attaching media
There is intentionally no upload_media tool — the MCP stdio transport
isn't a good fit for moving file bytes. The recommended pattern:
The MCP client / agent runtime uploads the image via REST:
curl -X POST https://bredgio.letmesee.cc/api/media/upload \ -H "Authorization: Bearer $BREDGIO_PAT" \ -F file=@./photo.jpgThe response includes
url, a relative path under/api/media-proxy/…. That URL is stable and reusable.dispatch_postconsumes thaturlverbatim inmedia[]:{ "name": "dispatch_post", "arguments": { "agentId": "ag_…", "platform": "facebook", "content": "Today's new menu 🍣", "media": [ { "url": "/api/media-proxy/bredgio/usr_…/abc.jpg", "type": "image/jpeg" } ] } }
Full upload contract (size limits, supported types, errors) lives in
API.md POST /api/media/upload.
dispatch_group_post
Search Facebook public groups by topic and post the same content into the first N groups the agent is already a member of. Non-member groups are recorded in the task log and skipped (we never request to join). Returns a
taskId; the per-group breakdown surfaces onget_taskonce the run completes.
Input schema
{
"type": "object",
"required": ["agentId", "topic", "content"],
"properties": {
"agentId": { "type": "string" },
"topic": { "type": "string", "minLength": 1,
"description": "Keyword used to search FB public groups, e.g. 'coffee tips' or 'real estate investing'." },
"content": { "type": "string", "minLength": 1, "maxLength": 60000,
"description": "The post body sent to each group (same text everywhere for now)." },
"maxGroups": { "type": "integer", "minimum": 1, "maximum": 20, "default": 5,
"description": "Cap on how many member groups to post into." },
"intervalSec": { "type": "integer", "minimum": 30, "maximum": 3600, "default": 300,
"description": "Delay between posts. Short intervals dramatically raise FB anti-spam risk." }
}
}
Facebook only (platform is implicit — "facebook").
Output
{
"taskId": "65fc...",
"status": "pending",
"dispatchedAt": "2026-05-30T03:14:15Z",
"watchUrl": "https://bredgio.letmesee.cc/dashboard/tasks/65fc..."
}
Once the task finishes, get_task(taskId).result adds two fields:
groupResults: [{ slug, url, name, status: "posted" | "failed", error? }]groupPostSummary: { attempted, posted, failed, memberGroupsFound }
Elicitation — Required when maxGroups > 10 or intervalSec < 120.
High fan-out or short intervals make FB spam detection much more likely
to fire, so the assistant MUST confirm with the user before dispatching.
list_tasks
Browse task history, optionally filtered.
Input
{
"type": "object",
"properties": {
"agentId": { "type": "string" },
"status": { "type": "string",
"enum": ["pending", "dispatched", "running", "completed", "failed", "cancelled"] },
"limit": { "type": "integer", "minimum": 1, "maximum": 200, "default": 50 }
}
}
Output — { tasks[] }. See task shape in API.md.
get_task
Fetch a single task including its step-by-step execution log.
Input — { "taskId": "string" }.
Output — full task object with log[]. See API.md.
Use this to surface execution details to the user — "the task failed
on click_compose with fb_login_required; ask them to log in".
create_schedule
Set up a recurring (or one-off) scheduled post.
Input schema
{
"type": "object",
"required": ["agentId", "platform", "content", "frequency"],
"properties": {
"agentId": { "type": "string" },
"platform": { "type": "string", "enum": ["facebook", "twitter"] },
"content": { "type": "string", "minLength": 1 },
"frequency": { "type": "string", "enum": ["oneoff", "daily", "weekly", "cron"] },
"runAt": { "type": "string", "format": "date-time",
"description": "Required when frequency=oneoff." },
"timeMinutesUTC": { "type": "integer", "minimum": 0, "maximum": 1439,
"description": "Required when frequency=daily or weekly. Minutes past 00:00 UTC." },
"dayOfWeek": { "type": "integer", "minimum": 0, "maximum": 6,
"description": "Required when frequency=weekly. 0=Sunday." },
"cronExpr": { "type": "string",
"description": "Required when frequency=cron. Standard 5-field cron." },
"timezone": { "type": "string",
"description": "Informational IANA timezone. The server stores everything in UTC." }
}
}
The MCP server pre-validates the conditional requirements before
calling the API so the assistant gets a clean error message instead of
a generic 400.
Output — full schedule object including the computed nextRunAt.
Elicitation — confirm before creating a schedule whose first run
is more than 30 days out, or whose frequency is cron with a fan-out
of more than 30 fires per day (cron expressions like * * * * *).
list_schedules
Show currently active and historical schedules.
Input
{
"type": "object",
"properties": {
"agentId": { "type": "string" },
"status": { "type": "string", "enum": ["active", "paused", "completed", "cancelled"] }
}
}
Output — { schedules[] }.
update_schedule
Pause, resume, edit content, or change timing on an existing schedule.
Input
{
"type": "object",
"required": ["scheduleId"],
"properties": {
"scheduleId": { "type": "string" },
"content": { "type": "string" },
"status": { "type": "string", "enum": ["active", "paused", "cancelled"] },
"frequency": { "type": "string", "enum": ["oneoff", "daily", "weekly", "cron"] },
"runAt": { "type": "string", "format": "date-time" },
"timeMinutesUTC": { "type": "integer", "minimum": 0, "maximum": 1439 },
"dayOfWeek": { "type": "integer", "minimum": 0, "maximum": 6 },
"cronExpr": { "type": "string" }
}
}
Touch any timing field and you must send a coherent group — same rule as the underlying PATCH endpoint.
Output — updated schedule.
Elicitation — confirm status: "cancelled" since cancellation is
not undoable through this tool surface.
Resources
In addition to tools, the server exposes one MCP resource:
| URI | Content | Notes |
|---|---|---|
bredgio://agents |
Plain-text directory of agentId name status |
Lets assistants ground "the marketing seat" → an agentId without calling a tool. Refreshed on every read. |
Future candidates (not in scope for v0): bredgio://schedules,
bredgio://recent-failures.
Prompts
The server ships two MCP prompts — pre-baked workflows the user can invoke from the assistant UI:
schedule_daily_post— guided flow: pick agent → write content → pick time-of-day → confirm. Callslist_agentsthencreate_schedule.triage_failed_tasks— pulls the last 24 h offailedtasks vialist_tasks, groups byerrorCode, and proposes next actions (retry, rotate token, manual login).
Error handling
The server propagates the underlying API error envelope through MCP's
isError flag with a structured content block:
{
"isError": true,
"content": [{
"type": "text",
"text": "Bredgio API rejected request: scheduledFor must be in the future"
}],
"_meta": {
"httpStatus": 400,
"errorCode": null
}
}
errorCode is populated when the underlying response includes one — see
API.md → Error codes for the full list.
Retry policy: the server transparently retries idempotent reads
(list_*, get_*) once on 5xx. Writes are never auto-retried.
Configuration reference
| Env var | Required | Default | Purpose |
|---|---|---|---|
BREDGIO_API_KEY |
yes | — | Personal Access Token. |
BREDGIO_BASE_URL |
no | https://bredgio.letmesee.cc |
Override for staging or self-hosted. |
BREDGIO_LOG_LEVEL |
no | warn |
error · warn · info · debug. |
BREDGIO_TIMEOUT_MS |
no | 30000 |
Per-request HTTP timeout. |
Versioning and stability
bredgio-mcp will follow semver against the published tool schemas,
not the underlying API. A backwards-incompatible tool schema change
forces a major version bump. The MCP server itself pins a minimum API
version it expects via a startup check; if the server discovers it's
talking to an incompatible API, it logs a clear error and refuses to
register the affected tools.
Roadmap
| Version | Adds |
|---|---|
| v0.1 (target) | Eight tools above, stdio transport only, Facebook posting. |
| v0.2 | comment, smart-post, smart-comment once the corresponding flows ship server-side. |
| v0.3 | HTTP transport for shared-team deployments, fine-grained scopes on PATs. |
| v1.0 | Stable tool schemas, frozen for one calendar year minimum. |
Open questions
These are deliberately unresolved and will be answered before v0.1 publishes:
- Should
dispatch_postblock until the task reaches a terminal state, or return immediately as currently specified? Blocking gives assistants a cleaner "did it work?" answer; non-blocking matches REST norms. - Should Team-plan owners be able to mint PATs on behalf of invited members, or restrict to per-seat minting?
- Do we need a per-tool
dryRun: truemode so the assistant can preview an action without executing it?
Have an opinion? Open an issue with the mcp label.