@briefgate/mcp
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| BRIEFGATE_API_KEY | Yes | API key (`bg_live_...` or `bg_test_...`). Obtain from the BriefGate dashboard. | |
| BRIEFGATE_BASE_URL | No | Override for staging or local development. | https://api.briefgate.dev |
| BRIEFGATE_MCP_HTTP | No | Set to `1` to start Streamable HTTP instead of stdio. | |
| BRIEFGATE_MCP_PORT | No | Port for HTTP mode. | 3000 |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| define_intakeA | Create a new client intake request — a branded portal where the client submits logos, copy, files, credentials, and other assets. BriefGate sends the invite email and chases the client automatically until all items are collected. Call this once at the start of a project, after you know what assets you need. Returns { intake_id, portal_url, status, follow_up }. Save intake_id — you need it for all follow-up calls. AFTER CREATING AN INTAKE, SET UP HOW YOU WILL LEARN IT IS DONE. Nothing pushes to you on its own: MCP is request/response, so the server cannot wake you when the client finishes. Creating the intake and never checking again is the common failure — the completed work then sits in the portal until a human happens to look. The returned follow_up block tells you which of the two mechanisms applies:
Example: { "project_name": "Website for John Finance", "client": { "email": "john@example.com", "name": "John", "language": "cs" }, "due_date": "2026-08-15", "branding": { "accent_color": "#1B2A4A", "sender_name": "Radim" }, "items": [ { "key": "logo", "type": "image", "label": "Company logo", "constraints": { "formats": ["svg","png"], "min_width": 512 } }, { "key": "hero_copy", "type": "longtext", "label": "Homepage headline (2–3 sentences)", "constraints": { "max_chars": 400 } }, { "key": "wp_admin", "type": "secret", "label": "WordPress admin credentials" }, { "key": "photos", "type": "file_list", "label": "Photos (5–10 images)", "constraints": { "formats": ["jpg","png","heic"], "min_count": 5, "max_count": 15 } } ] } Item types: text, longtext, file, file_list, image, color_list, select (one of options[]), multiselect (several of options[]; min_count/max_count in constraints), boolean, url, secret (encrypted; revealed exactly once — store the value on the first read), structured (requires schema with JSON Schema). DECISIONS — questions for the developer, not the client. An item with assignee="owner" and type select/multiselect is a question only the account holder can settle ("does the discounted plan cost 19 or 29?"). Never stop and wait for one: give it a "proposed" answer and carry on building. proposed = { value: "19", rationale: "matches the competitor we benchmarked" } records what you went with and why; it is stored separately from the real answer, so it can never be mistaken for one the developer gave. Read the answer back from get_intake_results. meta..decided_by tells you which it is: "owner" means a person settled it, "agent_proposal" means the build is still standing on your own pick and may yet be overruled. You cannot confirm your own proposal — answering is the developer's, through the dashboard. Item keys must be snake_case (e.g. "logo", "hero_copy", "ga4_id") — they become property names in get_intake_results. |
| get_intake_statusA | Check the completion status of a client intake — which items are submitted, pending, or need revision; the history of automated chase emails sent; and when the client last opened the portal. Use this to decide whether to send a manual reminder (send_chase), request a revision (request_revision), or fetch results (get_intake_results). Returns per-item status and chase history. This is also the call a scheduled check should make when no webhook is registered — see follow_up in the define_intake response for the cadence. When status becomes "completed", fetch the results with get_intake_results and carry on with the work that was waiting on them. |
| get_intake_resultsA | Retrieve the typed submitted values from a client intake. Files are returned as signed URLs valid for 24 hours — download them promptly or store the URL for reuse within that window. Secrets (type=secret, e.g. passwords, API keys) are decrypted and returned in plaintext on the FIRST call only. After the first retrieval the secret is marked as read: subsequent calls return first_reveal: false in meta and omit the value. Store secrets immediately before proceeding — you cannot retrieve them again. Use only_new=true to get only items submitted since the last call (useful in webhook-driven workflows). Use include_pending=true to also return partially filled items. Returns { results: { : }, meta: { : { type, status, submitted_at, first_reveal? } } }. For a DECISION (assignee=owner, type select/multiselect) results holds the answer currently standing and meta..decided_by says whose it is: "owner" once a person has settled it, "agent_proposal" while it is still your own pick. A proposed decision is returned even without include_pending — you need back the assumption you are building on. It does not bump revision, so an only_new read surfaces exactly the decisions a person has since answered or changed. |
| request_revisionA | Ask the client to resubmit a specific item with a note explaining what is wrong. Use this after reviewing get_intake_results and finding an item that does not meet requirements — for example a blurry logo, copy that is too long, or a broken URL. The client is notified automatically and the item status moves to needs_revision. Returns { status: "revision_requested", item_key }. |
| send_chaseA | Send a manual reminder to the client outside the automatic schedule. Use when a deadline is approaching and the client has not responded to automatic reminders, or when you want to send an SMS after email attempts have failed. The automatic chase schedule continues after this call — this is an extra nudge, not a replacement. Returns { sent: true }. |
| list_intakesA | List all intakes in your account, optionally filtered by status, client email, folder, or a text search. Use this to get an overview of active projects, find a specific intake by the client's email when you have lost the intake_id, check how many intakes are currently in progress, or see what's in a folder from list_folders. Returns { intakes: [...], total } where each intake includes intake_id, project_name, status, created_at, due_date, folder_id, and portal_url. |
| add_itemsA | Add new items to an already-sent intake — for example, when you realise mid-project that you also need a favicon, social media assets, or additional credentials. The client is notified about the new items. Existing items and their submitted values are not affected. Returns the updated intake object. Items must follow the same key/type/label rules as define_intake (snake_case keys, type-specific constraints). |
| update_itemA | Change one item on an intake that is already with the client — its type, label, hint, whether it is required, and which file formats it accepts. Reach for this when the field turns out to be the wrong shape: you asked for an image and the client only has their logo as a PDF, or what you asked for as a line of text is really a file. Widening the accepted formats or switching the type unblocks them without adding a duplicate item and waiving the original. The item key cannot be changed — results come back under it, so renaming would break whatever reads them. Add a new item instead. If the client has already answered and the change would make their answer invalid, the call fails and nothing is touched. Repeat it with discard_submitted_value: true to clear the answer and ask them again. A change that leaves their answer valid (a new label, a wider limit) never discards anything. |
| update_intakeA | Change settings on an intake that has already been sent — project name, due date, reminder cadence, quiet hours, which folder it's in, the client brief, or the client's name, phone, language, and timezone. Use this instead of deleting and recreating the intake when a deadline moves or the chase cadence needs to change. If any of chase_schedule, chase_interval, chase_interval_unit, chase_at_time, max_reminders, respect_quiet_hours, due_date, or client.timezone is included, every pending reminder is cancelled and the schedule is re-planned from now — reminders already sent still count toward max_reminders. Raising max_reminders (or setting it to "unlimited") past the number already sent on a stalled intake reactivates it and resumes chasing. The client's e-mail address cannot be changed here — the portal link and login are bound to it. Use manage_recipients to add, remove, or reinstate an address. folder_id moves the intake to a different folder (an id from list_folders); set it to null to remove the intake from any folder. It never touches the chase schedule. client_brief replaces the free-text brief shown to the client above the requested items; set it to null to clear it. Documents attached to the brief are managed via the dashboard or the REST endpoint POST /v1/intakes/:id/brief/files, not through this tool. Fails if the intake is archived. At least one field must be given. Returns the full, updated intake object. |
| manage_recipientsA | Add, remove, or reinstate a person who receives an intake's invite and reminders, alongside or instead of the primary client. action="add" invites another address the same way also_notify does at define_intake time — its own message, its own bounce state; pass name to address it by name. action="remove" stops future reminders to that address. action="reinstate" is for a bounce that was wrong — the person did get the e-mail — and clears the bounce flag so reminders resume; if that address was the only one still being chased, the schedule is re-planned from now. Fails if the address is not on the intake, or — for reinstate — if it never bounced in the first place. |
| manage_webhookA | Register, list, or remove a webhook endpoint so BriefGate pushes intake events to your service instead of you polling for them. Use this ONLY if you control a service that can receive public HTTPS requests. An agent running in a terminal cannot — for that case do not register anything and check on a schedule with get_intake_status instead. A registered endpoint that cannot receive produces failing deliveries and a false impression that the work is being watched. action="create" returns a "secret" exactly once. Store it somewhere durable outside this conversation: it is needed to verify the signature on every delivery (use verifyWebhookSignature from @briefgate/mcp/webhook) and it cannot be retrieved again. If it is ever exposed, there is no rotation in place — delete the endpoint and create a new one, which issues a fresh secret. Events: intake.completed (all required items in — the one to act on), item.submitted (a single item arrived), client.viewed (the client opened the portal), chase.bounced (a reminder failed to deliver), intake.overdue (the due date passed with required items outstanding — the one to act on when work is blocked), intake.stalled (fires only when the intake sets max_reminders; without it this event never arrives). |
| list_foldersA | List the folders in your account, used to group intakes by client or project. Call this before create_folder or before setting folder_id on define_intake, update_intake, or list_intakes — reuse an existing folder for a returning client instead of creating a duplicate. Returns { folders: [{ id, name, sort_order, intake_count, created_at }] }. |
| create_folderA | Create a new folder to group intakes, e.g. one per client. Call list_folders first and reuse a matching folder — only create one when none of the existing folders fits. Fails with folder_exists if a folder with this name already exists; use list_folders to find it instead. Returns the created folder { id, name, sort_order, intake_count, created_at }. |
| loginA | Sign in without pasting an API key, the same way Call this whenever a tool reports "Not signed in" or that the stored key was revoked or expired. This is a TWO-PHASE tool because approval can take minutes — longer than a single tool call should block for:
Do not wait silently for minutes on one call — call this tool again after telling the user to approve, and again if they say they've clicked Allow. Has no effect if a key is already supplied via the |
| logoutA | Remove the API key |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 15 tools
Each tool targets a distinct resource-action pair: intakes, items, folders, recipients, webhooks, and auth are cleanly separated. Even get_intake_status vs get_intake_results are clearly differentiated as progress tracking vs value retrieval. No two tools have overlapping purposes that would cause misselection.
The set mostly follows a consistent verb_noun snake_case pattern (list_folders, get_intake_results, add_items, update_item, manage_webhook). Minor deviations exist: 'login'/'logout' lack an object, and 'define_intake' uses a different create verb than 'create_folder', but the overall pattern remains predictable.
15 tools is well within the appropriate range for a dedicated intake-management product. Each tool serves a necessary role across auth, folders, intake lifecycle, item management, recipients, and webhooks, with no redundancy or bloat.
The core lifecycle is well covered: create, read status and results, update intakes/items, add items, request revisions, send chases, manage recipients and webhooks. The main gap is the lack of an explicit archive or delete tool for intakes (the archived state is referenced but not actionable via MCP), and no direct remove-item operation, though these are workaroundable via the dashboard.