B
Documentation

Bredgio MCP Server · Specification

Status — design only. The bredgio-mcp npm 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.json without 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 taskId so the assistant can poll status with get_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..."
}

Elicitationdispatch_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:

  1. 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.jpg
    

    The response includes url, a relative path under /api/media-proxy/…. That URL is stable and reusable.

  2. dispatch_post consumes that url verbatim in media[]:

    {
      "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 on get_task once 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. Calls list_agents then create_schedule.
  • triage_failed_tasks — pulls the last 24 h of failed tasks via list_tasks, groups by errorCode, 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:

  1. Should dispatch_post block 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.
  2. Should Team-plan owners be able to mint PATs on behalf of invited members, or restrict to per-seat minting?
  3. Do we need a per-tool dryRun: true mode so the assistant can preview an action without executing it?

Have an opinion? Open an issue with the mcp label.