@briefgate/mcp
BriefGate MCP lets AI coding agents collect files, text, credentials, and structured data from clients or other humans outside the conversation via email-invited intake portals, then consume the typed results — while BriefGate automatically chases the client until everything is submitted.
Create intakes (
define_intake): define a branded client portal with typed items — images, files, text/longtext, structured JSON-Schema data, secrets (one-time reveal), color lists, URLs, booleans, selects/multiselects — plus due dates, branding, chase schedules, client briefs, and automatic reminder emails.Monitor progress (
get_intake_status): see per-item status (submitted/pending/revision), automated chase history, and when the client last opened the portal.Collect results (
get_intake_results): retrieve typed values, signed file URLs (valid 24h), and one-time secrets; filter withonly_newandinclude_pending.Request revisions (
request_revision): ask the client to resubmit an item with a clear note.Modify intakes mid-flight (
add_items,update_item,update_intake,manage_recipients): add items, change an item's type/constraints, update deadline/cadence/folder/brief, and manage additional recipients.Send manual reminders (
send_chase): nudge the client outside the automatic schedule.Organize and search (
list_intakes,list_folders,create_folder): filter by status, client email, folder, or text; group intakes into folders.Webhook integration (
manage_webhook): push events such asintake.completed,item.submitted,client.viewed,chase.bounced,intake.overdue, andintake.stalledto your own HTTPS endpoint.Ask the developer decisions (
assignee: owner): pose questions only the account holder can answer, record a proposed answer, and keep building.Authentication (
login/logout): two-phase device-authorization sign-in without pasting API keys; API keys and OAuth are also supported.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@@briefgate/mcpSet up a client intake for logo, copy, and WordPress credentials."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
BriefGate
Client intake for AI coding agents.
Your agent can build the website. BriefGate gets the missing things from the client.
Claude Code / Cursor / Codex → BriefGate → Client portal
→ Files · copy · credentials · structured data → Agent continues building
Watch as MP4 (25 s) · Full 47 s walkthrough
Website · MCP reference · llms.txt · Guides and checklists
Not working with an agent? The same intakes can be created from the browser dashboard — see the dashboard quickstart.
The problem
Agents are fast. The bottleneck is the human on the other side of the project.
Somewhere in the middle of building, the agent needs something only the client has: a logo, homepage copy, brand colors, opening hours, hosting credentials, an API key, a piece of structured data like a price list. None of that exists in the chat, and none of it can be guessed.
The usual move is to stop and ask the developer to go chase the client by email. Instead, the agent creates a BriefGate intake. BriefGate emails the client, collects what comes back, chases automatically when it doesn't, and returns typed results the agent can use directly. The agent keeps building in the meantime.
Related MCP server: Business Bridge MCP Server
Quickstart
Claude Code — hosted, no key to manage:
claude mcp add --transport http briefgate https://mcp.briefgate.dev/mcpThen run /mcp in Claude Code, pick briefgate, and choose Authenticate.
Claude Code — local package:
claude mcp add briefgate -- npx -y @briefgate/mcp
npx -y @briefgate/mcp loginPrefer to skip sign-in entirely? Get a key at briefgate.dev (free tier, no card) and pass it as BRIEFGATE_API_KEY.
Cursor — add to .cursor/mcp.json:
{
"mcpServers": {
"briefgate": {
"command": "npx",
"args": ["-y", "@briefgate/mcp"]
}
}
}Then run npx -y @briefgate/mcp login, or ask the agent to call the login tool.
Codex:
codex mcp add briefgate --env BRIEFGATE_API_KEY=bg_live_xxxxx -- npx -y @briefgate/mcpGemini CLI — installs as an extension from this repo's gemini-extension.json, pointed at the hosted endpoint:
gemini extensions install https://github.com/sekera-radim/briefgate-mcpIt authenticates the same way as the other hosted clients above — via OAuth, on first use. The extension also ships GEMINI.md, a context file that tells the model what BriefGate is and when to reach for it.
Claude Desktop — one-click install as a Desktop Extension (.mcpb), running the local @briefgate/mcp package instead of the hosted endpoint. Build details: see MCPB bundle below.
Cursor — as a plugin from this repo's .cursor-plugin/plugin.json, which bundles the same MCP config together with the collect-from-client skill below.
Any Agent Skills-compatible tool (OpenAI Codex, GitHub Copilot / VS Code, Cursor, Gemini CLI, and other clients on that standard) — install the collect-from-client skill with the skills CLI:
npx skills add https://github.com/sekera-radim/briefgate-mcp/tree/main/skills/collect-from-clientThe bare owner/repo shorthand the CLI's own docs show does not work against this layout as of skills@1.5.26 — it either 404s against its registry or, given the plain repo URL, clones the whole repo and reports "No SKILL.md found" because ours lives a directory deeper than the root. The full /tree/main/skills/<name> URL above is what actually installs it; verified by running it in an empty scratch directory before documenting it. This copies SKILL.md into that tool's local skills directory; the skill still assumes the briefgate MCP server (see the client-specific instructions above) is configured separately.
Full setup details, manual config, and API-key precedence: see Reference below.
For agents reading project instructions (AGENTS.md, Copilot, Cursor rules)
If your agent reads a project instructions file instead of (or in addition to) MCP tool descriptions or Agent Skills, add this block to your AGENTS.md, .github/copilot-instructions.md, or .cursor/rules:
## BriefGate
BriefGate (MCP server `briefgate`, or `npx @briefgate/mcp`) collects files,
text, choices, or credentials from a human who is not in this conversation
(a client, colleague, or customer) — it emails them a no-account portal link
and chases them automatically until everything is submitted.
Use it when a task is blocked on input only that outside person can supply,
especially if they may take days and need reminders. Do not use it when the
information is already available, or when the person who should answer is
the user you're talking to.
Flow: `define_intake` (with items) -> tell the user it was sent ->
`get_intake_status` later ("not ready" is normal, not an error) ->
`get_intake_results` once completed. `request_revision` for a bad item.
Secret items are revealed in plaintext exactly once.Example: building a client's website
An agent is building a website for a restaurant. It has the layout and the booking flow, but it still needs the logo, a hero photo, the opening hours, a short description of the restaurant, the social media links, and admin access to the client's WordPress install. It calls define_intake:
{
"project_name": "Website for Trattoria Bella",
"client": { "email": "owner@trattoriabella.example", "name": "Marco", "language": "en" },
"items": [
{ "key": "logo", "type": "image", "label": "Restaurant logo",
"constraints": { "formats": ["svg", "png"], "min_width": 512 } },
{ "key": "hero_image", "type": "image", "label": "Hero photo for the homepage" },
{ "key": "opening_hours", "type": "structured", "label": "Opening hours",
"schema": { "type": "object", "properties": { "mon_fri": { "type": "string" }, "sat": { "type": "string" }, "sun": { "type": "string" } } } },
{ "key": "about_copy", "type": "longtext", "label": "Short description of the restaurant" },
{ "key": "social_links", "type": "structured", "label": "Social media links" },
{ "key": "wp_admin", "type": "secret", "label": "WordPress admin credentials" }
]
}From there, BriefGate (1) creates a branded portal, (2) emails the client, (3) validates each asset as it comes in, (4) chases the client automatically until everything is submitted, and (5) notifies the agent when it's done.
The agent keeps building the layout, the booking flow, and everything else that doesn't depend on this — then calls get_intake_results(intake_id) and gets back typed data and signed URLs for the files, plus a one-time reveal of the WordPress credentials. It stores the secret and continues.
Why not a form?
Generic form | BriefGate |
Human creates the form | Agent declares what it needs |
Human reads results | Agent consumes typed results |
Generic answers | Typed items |
Manual follow-up | Automatic chasing |
Spreadsheet mindset | API / MCP workflow |
Credentials are awkward | Secret item + controlled reveal |
Human workflow | Agent workflow |
BriefGate is not trying to replace every form builder. It is designed for the point where an AI agent needs information from a human.
Free tier, no card required. BriefGate is a hosted service — this repository is the open-source MCP client, MIT licensed. Sign up at briefgate.dev.
Reference
Everything below is unchanged technical detail: manual setup, environment variables, HTTP/OAuth mode, the full tool reference, webhooks, pricing, and legal.
Claude Code: manual setup and API keys
Paste an API key (for CI, scripts, or if you'd rather manage the key yourself). Get one at briefgate.dev (free tier available, no card required):
claude mcp add briefgate \
-e BRIEFGATE_API_KEY=bg_live_... \
-- npx -y @briefgate/mcpOr add manually to ~/.claude/settings.json:
{
"mcpServers": {
"briefgate": {
"command": "npx",
"args": ["-y", "@briefgate/mcp"],
"env": {
"BRIEFGATE_API_KEY": "bg_live_..."
}
}
}
}BRIEFGATE_API_KEY (or --api-key on the command line), if set, always takes precedence over a key login stored locally — running login while one is configured just says so instead of doing anything.
Verify it loaded — run /mcp in Claude Code and look for briefgate with 15 tools.
The same local-package and API-key setup works for any MCP client that runs the package locally (Cursor, Codex, others) — register it with no key at all and run login, or paste BRIEFGATE_API_KEY into that client's own MCP config the same way.
Sign in without an API key
Two ways to get a key onto this machine without pasting one — both run the same device-authorization flow (RFC 8628) against the same credential file, so pick whichever fits how you're using the package.
From a terminal — the login / logout subcommands:
npx -y @briefgate/mcp login # prints a code + URL, waits for approval, saves the key
npx -y @briefgate/mcp logout # removes the local key, best-effort revokes it remotelylogin blocks until you approve it (or it times out at 10 minutes), then prints Signed in as <account_name> and exits 0 — or prints why it didn't work (denied, expired, an error) and exits 1. logout always removes the local copy; it also sends DELETE /v1/keys/current using that same key to revoke it server-side, and if that call fails (no network, API unreachable) it says so and points at the BriefGate dashboard instead of leaving you unsure whether the key is still live.
From an agent — the login / logout tools (see Tools):
Same flow, for a client that can't block a terminal on your click. login is two-phase because a tool call can't sit open for minutes:
The first call starts the flow and returns immediately with the code and URL. A browser is opened automatically where possible.
Call
loginagain — any time, or once you've approved it — to check progress. While it's still waiting, it says so; once approved, that same call reports success and the key is saved. No restart needed: the very next tool call is signed in.
logout as a tool does exactly what the subcommand does, including the best-effort remote revoke.
Either way, the key lands in ~/.briefgate/credentials.json (directory mode 0700, file mode 0600; override the path with BRIEFGATE_CREDENTIALS_FILE), keyed by which BriefGate server it's for so a staging BRIEFGATE_BASE_URL and production never collide. An explicit key always wins over a stored one — --api-key, then BRIEFGATE_API_KEY, then whatever login last saved — and login says so instead of running the flow when one of those is already set. Neither the subcommands nor the tools apply to the shared hosted endpoint (mcp.briefgate.dev) — see Hosted endpoint + OAuth, where connecting a client triggers real OAuth instead.
Environment variables
Variable | Required | Default | Description |
| No | — | API key ( |
| No |
| Override for staging or local development. |
| No |
| Where |
| No | unset | Set to |
| No | — | Set to |
| No |
| Port for HTTP mode. |
| No | — | Publishes the server as a shared, multi-customer OAuth endpoint. See Hosted endpoint + OAuth. |
| No |
| The OAuth authorization server advertised to clients in published mode. Defaults to |
--api-key bg_live_... is also accepted on the command line, ahead of BRIEFGATE_API_KEY in priority. login and logout are also accepted as the first command-line argument (npx @briefgate/mcp login), instead of --http/no flag.
HTTP (Streamable HTTP) mode
For remote or multi-session deployments, start the server in HTTP mode:
BRIEFGATE_API_KEY=bg_live_... npx @briefgate/mcp --http --port 3000The server binds to 127.0.0.1 only and includes DNS-rebinding protection. Behind a reverse proxy, terminate TLS there and forward to the local port — do not expose the port directly.
Hosted endpoint + OAuth
Set BRIEFGATE_MCP_PUBLIC_HOST to the hostname the server is published under and
it becomes a shared, multi-customer endpoint: each caller sends its own key as
Authorization: Bearer bg_live_... (an OAuth access token, for this API, is
that same key — see below), and the server speaks to the BriefGate API as that
caller. The public instance is https://mcp.briefgate.dev/mcp.
BRIEFGATE_MCP_PUBLIC_HOST=mcp.example.com npx @briefgate/mcp --http --port 3000Several things change, on purpose:
the listener binds
0.0.0.0and the Host guard accepts that name, because a server behind a reverse proxy is reached by its public name;the
BRIEFGATE_API_KEYfallback and the locallogincredential are both switched off. Leaving either on would let an anonymous caller spend the operator's key, or read whatever the machine's ownloginlast stored;login/logout, tools and subcommands alike, are unavailable — connecting a client triggers real OAuth instead, described below;the server becomes an OAuth 2.1 resource server, per the MCP authorization spec, so an OAuth-aware client can add it with nothing but the URL. This package never runs the authorization flow itself — it only advertises where to find it and enforces that a request carries a token:
it serves
GET /.well-known/oauth-protected-resource(RFC 9728), and the same content again under/.well-known/oauth-protected-resource/mcp(the resource-scoped path the MCP spec also has clients try), both with open CORS and naming the BriefGate API as the authorization server — seeBRIEFGATE_MCP_AUTH_SERVERabove;every MCP request now needs a Bearer token — including
initializeandtools/list, which used to work without one so a registry could introspect the tool list. One with no token gets HTTP401and aWWW-Authenticate: Bearer resource_metadata="https://<host>/.well-known/oauth-protected-resource"header, which is the signal an OAuth client uses to start signing in;if a tool call's key turns out to be expired or revoked (the API answers
401), the response is rewritten into a real HTTP401with the same header pluserror="invalid_token", rather than an ordinary tool error — so the client knows to refresh rather than just reporting the call failed.
What a connecting client actually does, against the authorization server named
in that metadata: standard OAuth 2.1 discovery
(GET /.well-known/oauth-authorization-server), dynamic client registration
(POST /v1/oauth/register), then an authorization-code exchange with PKCE
(S256) at POST /v1/oauth/token — no client secret, since MCP clients are
public clients — and POST /v1/oauth/revoke to end a session. None of that is
this package's concern; it only has to be a correct resource server pointing
at it. The access token that comes out the other end is a bg_live_... key
like any other, with a one-hour expiry the API enforces.
None of this applies without BRIEFGATE_MCP_PUBLIC_HOST: a local --http run
keeps behaving exactly as before, including an absent key reaching
initialize/tools/list and a plain Authorization: Bearer ... header
working with no OAuth involved.
Tools
define_intake
Create a new client intake — a branded portal where the client submits the assets you need. BriefGate sends the invite email and chases the client automatically until everything is collected.
project_name: "Website for John Finance"
client: { email: "john@example.com", name: "John", language: "cs" }
// also_notify: [{ email: "jane@example.com", name: "Jane" }]
// Others at the client who get the same link and the same reminders — either of
// them can supply the material. Each gets their own email; nobody sees the rest.
due_date: "2026-08-15"
branding: { accent_color: "#1B2A4A", sender_name: "Radim" }
chase_schedule: "default" // default | gentle | aggressive | custom | off
// chase_interval: 5, chase_interval_unit: "minutes" // only with "custom"; omit for every 3 days
// respect_quiet_hours: false, max_reminders: 12 // for a deliberately rapid cadence
items:
- { key: "logo", type: "image", label: "Company logo",
constraints: { formats: ["svg","png"], min_width: 512 } }
- { key: "hero_copy", type: "longtext", label: "Homepage headline",
constraints: { max_chars: 400 } }
- { key: "brand_colors", type: "color_list", label: "Brand colors", required: false }
- { key: "ga4_id", type: "text", label: "Google Analytics ID",
pattern: "^G-[A-Z0-9]+$", required: false }
- { 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 } }
- { key: "opening_hours", type: "structured", label: "Opening hours",
schema: { type: "object", properties: { mon_fri: { type: "string" }, sat: { type: "string" } } } }
- { key: "has_existing_site", type: "boolean", label: "Does the client have an existing website?" }
- { key: "website_url", type: "url", label: "Current website URL", required: false }
- { key: "service_tier", type: "select", label: "Service package",
options: [{ value: "basic", label: "Basic" }, { value: "pro", label: "Pro" }] }
// folder_id: "fld_1"
// Put the intake straight into an existing folder from list_folders instead
// of leaving it unfiled.
// client_brief: "Here's the offer we agreed on, plus a few notes on scope..."
// Free text shown to the client above the requested items — information from
// you to them, not another thing you're asking them for. Up to 5000 characters.
// Documents go through POST /v1/intakes/:id/brief/files (dashboard or REST,
// not through MCP).Item key rules: must be snake_case (e.g. logo, hero_copy, ga4_id). Keys become property names in get_intake_results — no uppercase, no spaces, no hyphens.
Returns { intake_id, portal_url, status }. Save intake_id for all follow-up calls.
get_intake_status
Check which items are submitted, pending, or need revision. Includes the history of automated chase emails and when the client last opened the portal.
intake_id: "in_8f3k"Returns per-item status and a full chase history.
get_intake_results
Retrieve typed submitted values. Files are signed URLs (valid 24 hours). Secrets are one-time — decrypted and returned on the first call only; store them before moving on.
intake_id: "in_8f3k"
only_new: true // only items new since last call
include_pending: false // omit unsubmitted itemsReturns { results: { logo: "https://signed...", hero_copy: "text...", wp_admin: "s3cr3t" }, meta: { ... } }.
request_revision
Ask the client to resubmit an item with a note explaining what is wrong.
intake_id: "in_8f3k"
item_key: "logo"
note: "Logo is blurry — we need at least 512 px wide in SVG or PNG with a transparent background"Returns { status: "revision_requested", item_key }.
send_chase
Send a manual reminder outside the automatic schedule. Use when a deadline is approaching or email attempts have failed.
intake_id: "in_8f3k"Returns { sent: true }.
list_intakes
List all intakes across projects, optionally filtered by status, client email, folder, or a text search.
status: "in_progress" // draft | sent | in_progress | completed | archived
client_email: "john@example.com"
folder_id: "fld_1" // or "none" for intakes not in any folder
q: "Finance" // substring match on project name, client name, or client email
limit: 20
offset: 0Returns { intakes: [...], total }.
add_items
Add new items to an already-sent intake — for example a favicon you forgot, or additional credentials needed mid-project.
intake_id: "in_8f3k"
items:
- { key: "favicon", type: "image", label: "Favicon (32×32 PNG or ICO)" }Returns the updated intake.
update_item
Change an item's definition after the intake was sent — the type, label, help text or constraints. Use this when you asked for the wrong thing, e.g. you requested an image but the client has a PDF.
intake_id: "in_8f3k"
item_key: "logo"
type: "file" // was "image"
constraints: { formats: ["pdf","ai","svg"] }
discard_submitted_value: false // true is required if the change invalidates what the client already sentReturns the updated item. If the client already submitted a value that the new definition would reject, the call fails with item_answer_would_be_discarded until you pass discard_submitted_value: true.
update_intake
Change settings on an already-sent intake — project name, due date, reminder cadence, quiet hours, the client brief, or the client's name, phone, language, and timezone. Use this instead of deleting and recreating the intake, which would re-send the invite.
intake_id: "in_8f3k"
due_date: "2026-12-01"
chase_schedule: "gentle" // was "default"
max_reminders: "unlimited" // reactivates a stalled intake if it had hit its cap
// folder_id: "fld_1" // move it into a folder; null removes it from any folder
// client_brief: "Updated offer..." // replaces the brief shown above the items; null clears itIf any chase-related field changes (chase_schedule, chase_interval, chase_interval_unit, chase_at_time, max_reminders, respect_quiet_hours, due_date, client.timezone) on a sent intake, every pending reminder is cancelled and re-planned from now — reminders already sent still count toward max_reminders. folder_id never touches the chase schedule.
The client's e-mail address cannot be changed here — the portal link and login are bound to it. Use manage_recipients for that. Fails if the intake is archived. Returns the full, updated intake object.
manage_recipients
Add, remove, or reinstate a person who receives an intake's invite and reminders, alongside or instead of the primary client.
intake_id: "in_8f3k"
action: "reinstate" // add | remove | reinstate
email: "extra@example.com"
name: "Petr" // only used with action="add"action="add" invites another address the same way also_notify does at define_intake time. action="remove" stops future reminders to that address. action="reinstate" is for a bounce that was wrong — the person did get the e-mail — it clears the bounce flag so reminders resume, and re-plans the chase schedule from now if that address was the only one still being chased.
manage_webhook
Register, list or remove a webhook endpoint so events are pushed to your service instead of you polling.
action: "create" // create | list | delete
url: "https://your.service/hooks/briefgate"
events: ["intake.completed", "intake.overdue"]
format: "raw" // raw | slack | discordaction: "create" returns a secret once — store it, it verifies every delivery signature and cannot be retrieved again. Remove with action: "delete" and webhook_id.
Because an agent receives the secret in a tool result, it can come to rest wherever that conversation is stored. There is no rotation endpoint: if a transcript leaks, delete the endpoint and create a new one to get a fresh secret.
Only register an endpoint you can actually receive on. An agent running in a terminal has no public HTTPS address; for that case register nothing and check on a schedule instead (see below).
list_folders
List the folders in your account, used to group intakes by client or project. Takes no arguments.
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_folder
Create a new folder to group intakes, e.g. one per client.
name: "Acme Inc"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. Returns the created folder.
login
Sign in without an API key — see Sign in without an API key. Takes no arguments.
Call it whenever another tool reports "Not signed in" or that the stored key was revoked or expired. The first call starts a device-authorization flow and returns a URL and a short code immediately; call it again (any time) to check whether it's been approved yet. Has no effect — it says so instead — if --api-key or BRIEFGATE_API_KEY already supplies a key. Not available on the hosted endpoint. Same flow as running npx @briefgate/mcp login from a terminal (which blocks until approved instead of needing a second call) — see Sign in without an API key.
logout
Removes the API key login stored locally for this BriefGate server, and best-effort revokes it on the server too. Takes no arguments.
If the revoke call fails — no network, the API unreachable — the local copy is still removed; the response says so and points at the BriefGate dashboard to revoke it there instead. Not available on the hosted endpoint. Same effect as running npx @briefgate/mcp logout from a terminal — see Sign in without an API key.
Decisions — questions for the developer
An agent building something hits things only the account holder can settle: does the discounted plan cost $19 or $29? Stopping to wait wastes the run; picking silently buries the assumption. A decision is the third option — pose the question, record the answer you are proceeding on, keep building.
{ "key": "discount_price", "type": "select", "assignee": "owner",
"label": "What does the discounted subscription cost?",
"options": [ { "value": "19", "label": "$19/month" },
{ "value": "29", "label": "$29/month" } ],
"proposed": { "value": "19", "rationale": "matches the competitor we benchmarked" } }type: "multiselect" takes several answers, bounded by constraints.min_count / max_count.
The proposal is stored apart from the real answer, so it can never be mistaken for one the developer gave — and it survives being overruled, which is the point: in three months you can still see that $19 was assumed, not agreed. Read it back from get_intake_results:
"results": { "discount_price": "19" },
"meta": { "discount_price": { "decided_by": "agent_proposal", "proposed_value": "19" } }decided_by is "owner" once a person has settled it and "agent_proposal" while it is still your own pick. A proposed decision comes back even without include_pending — you need the assumption you are building on. It does not bump revision, so an only_new read surfaces exactly the decisions someone has since answered.
You cannot answer your own question. The answer endpoint takes a dashboard session, not an API key: if the agent could confirm its own proposal and have it recorded as the developer's, the distinction would be worth nothing. Decisions are answered in the BriefGate dashboard.
Owner items never reach the client portal, never appear in a reminder, and never hold up completion — the intake is finished when the client is finished.
Knowing when the client is done
Nothing pushes to an MCP client on its own — MCP is request/response, so the server cannot wake your agent when the client finishes. define_intake therefore returns a follow_up block naming the mechanism that fits your setup:
"follow_up": {
"recommended": "schedule", // or "webhook" when an endpoint already exists
"webhook": { "active_endpoints": 0, "events": ["intake.completed", "item.submitted"],
"register_with": "manage_webhook" },
"schedule": { "check_with": "get_intake_status", "every_hours": 24,
"until": "2026-10-01T08:00:00.000Z" }
}You run a service → register a webhook with
manage_webhookand act onintake.completed.You are an agent in a terminal → set up a recurring check that calls
get_intake_statuseveryevery_hourshours untiluntil. A cron entry, a systemd timer, or your agent host's own scheduler all work.
Events worth acting on: intake.completed (everything is in) and intake.overdue (the deadline passed with required items missing — the project is blocked and the client needs a human, not another reminder).
The cadence tightens near the deadline (24h normally, 12h inside a week, 6h inside two days) and is not tied to the reminder schedule: a client can submit everything at 2am having never opened a reminder.
End-to-end example
# System prompt excerpt
You are a web development agent. When you need client assets:
1. Call define_intake with all assets needed for this project.
Use type=secret for passwords/credentials.
The chase engine runs automatically — do not poll more often than once per day.
2. Read follow_up in the response and set up how you will hear back:
register a webhook with manage_webhook if you have an HTTPS endpoint,
otherwise schedule a get_intake_status check at follow_up.schedule.every_hours.
3. When intake.completed arrives (or the scheduled check reports "completed"),
call get_intake_results. Download file URLs within 24 hours.
Store secrets immediately — they are one-time.
4. If a submitted asset does not meet requirements (blurry logo, broken URL),
call request_revision with a clear note for the client.
If the client has the asset in another form, call update_item to change the type.
5. If the client is still unresponsive after 9 days, call send_chase for an
extra nudge outside the automatic schedule, or tell the developer the intake
is stuck and let them pick up the phone.Verifying webhooks
BriefGate signs every webhook with HMAC-SHA256 to prevent forgery and replay attacks. The @briefgate/mcp package exports a ready-made helper:
import { verifyWebhookSignature, parseWebhookEvent } from "@briefgate/mcp/webhook";The signature lives in the X-BriefGate-Signature header as t=<unix>,v1=<hex>:
Fastify (recommended)
import Fastify from "fastify";
import { verifyWebhookSignature, parseWebhookEvent } from "@briefgate/mcp/webhook";
const app = Fastify();
// Parse body as raw string — JSON-parsing before verification breaks the HMAC.
app.addContentTypeParser("application/json", { parseAs: "string" }, (req, body, done) => {
done(null, body);
});
app.post("/briefgate/webhook", (request, reply) => {
const rawBody = request.body as string;
const ok = verifyWebhookSignature(
process.env.BRIEFGATE_WEBHOOK_SECRET!,
request.headers["x-briefgate-signature"] as string,
rawBody,
// { toleranceSec: 300 } ← default; increase for slow networks
);
if (!ok) {
return reply.status(401).send({ error: "Invalid signature" });
}
const event = parseWebhookEvent(rawBody);
console.log("BriefGate event:", event.event, event.intake_id);
reply.send({ ok: true });
});Express
import express from "express";
import { verifyWebhookSignature, parseWebhookEvent } from "@briefgate/mcp/webhook";
const app = express();
// raw body parser — must come before express.json()
app.post(
"/briefgate/webhook",
express.raw({ type: "application/json" }),
(req, res) => {
const rawBody = Buffer.isBuffer(req.body)
? req.body.toString("utf8")
: String(req.body);
const ok = verifyWebhookSignature(
process.env.BRIEFGATE_WEBHOOK_SECRET!,
req.headers["x-briefgate-signature"] as string,
rawBody,
);
if (!ok) return res.status(401).json({ error: "Invalid signature" });
const event = parseWebhookEvent(rawBody);
console.log("BriefGate event:", event.event, event.intake_id);
res.sendStatus(200);
},
);Webhook events
Event | When | Key fields |
| Client submits an item |
|
| All required items approved | — |
| Client opens the portal |
|
| A reminder bounced |
|
| 3 reminders sent, no response |
|
Pricing
Launch offer: code LAUNCH20 gives 20% off Solo and Agency for the lifetime of the subscription, valid until 4 October 2026 (new customers, plans only).
Free | Solo — $29/mo | Agency — $79/mo | |
Active intakes | 1 | 15 | 60 |
Items per intake | 10 | unlimited | unlimited |
Storage | 1 GB | 25 GB | 100 GB |
Branding | "powered by" | custom logo + colors | + custom sending domain |
Chase | email, default | email, all schedules | email, all schedules |
Secrets vault | — | yes | yes |
Webhooks + REST + MCP | yes | yes | yes |
Full pricing at GET https://api.briefgate.dev/pricing.json (no auth required — agents can read it directly).
Data residency
BriefGate is hosted in the EU: application servers at netcup GmbH in Nuremberg, Germany; files in Cloudflare R2 under EU jurisdiction. See the GDPR notes and the DPA.
Privacy Policy
This package is a thin client: it holds no data of its own and sends nothing
anywhere except to the BriefGate API at api.briefgate.dev, using the API key
you configure. It writes no telemetry and no analytics.
What BriefGate itself collects, how long it keeps it, who it is shared with and how to have it deleted is covered in full here:
Privacy Policy — https://briefgate.dev/docs/privacy
Security — https://briefgate.dev/docs/security
Data Processing Agreement — https://briefgate.dev/docs/dpa
Contact for privacy requests: privacy@briefgate.dev
MCPB bundle (Claude Desktop Extension)
manifest.json at the repo root packages the local
@briefgate/mcp package as a one-click Claude Desktop install
(MCPB spec). It runs dist/index.js
locally and prompts for an optional API key at install time — the same
login/BRIEFGATE_API_KEY setup documented above, not the hosted
endpoint's OAuth flow.
Build the bundle (production dependencies only, packed in a throwaway
staging directory so it never touches this repo's own node_modules):
npm run package:mcpbThis produces briefgate.mcpb at the repo root (gitignored — install it
locally to test, don't commit it). Not yet submitted anywhere; see
scripts/build-mcpb.mjs for what the command does.
Contributing
This repository is the BriefGate MCP client only — a thin wrapper over the public BriefGate REST API. The BriefGate service itself is closed source.
npm install
npm run typecheck # TypeScript check
npm run lint # ESLint
npm run test # Vitest
npm run check # all three
npm run build # compile to dist/License
MIT — use freely in commercial projects.
Made by Radim Sekera. Related project: impri.dev — human-in-the-loop approval inbox for AI agents.
Available Tools
15 toolsadd_itemsAdd items to an intakeA
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).
| Name | Required | Description | Default |
|---|---|---|---|
| items | Yes | New items to add. Same schema as define_intake items. | |
| intake_id | Yes | Intake ID returned by define_intake. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond what the annotations provide (readOnlyHint=false, destructiveHint=false), the description discloses that the client is notified, existing items and submitted values are unaffected, and the updated intake object is returned. These are meaningful behavioral details that help an agent anticipate side effects without contradicting the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is tightly structured: first sentence states the core action with examples, second covers behavioral side effects, third gives the key constraint reference. No fluff or redundancy, and important information is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter tool with fully documented schema properties, this description is complete. It covers when to use it, what happens on execution, return value, and item format constraints. An agent has enough context to invoke it correctly without additional inference.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is high and both parameters are described in the schema. The description adds value by explicitly tying items to define_intake rules (snake_case keys, type-specific constraints) and giving practical examples of item types like favicon or credentials. This supplements the schema's generic descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the operation: adding new items to an already-sent intake. It distinguishes itself from sibling tools like define_intake (creating an intake) and update_item (modifying an existing item) by focusing specifically on post-submission additions, reinforced by concrete examples.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives a clear usage scenario: adding items mid-project after an intake has already been sent. It implies the alternative is define_intake for initial definitions, and even cross-references its item rules. It lacks an explicit exclusion statement, but the context is strong enough for an agent to select this tool appropriately.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_folderCreate a 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 }.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Folder name, e.g. the client's or project's name. Must be unique in your account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already signal that this is a write operation (readOnlyHint=false, idempotentHint=false). The description adds useful behavior beyond annotations by disclosing the folder_exists failure condition and the exact return shape, including fields like id, sort_order, and intake_count. It does not discuss permission requirements, but that is not a significant gap given the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded: it opens with the core purpose, immediately gives usage guidance with a sibling alternative, and closes with the failure mode and return shape. Every sentence adds value, and there is no redundant or filler content.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter creation tool, the description is complete: it covers purpose, when to use, failure behavior, and return format. The output schema is absent, but the description compensates by listing the returned object fields. Annotations cover safety and world-openness, and no critical information is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and the single 'name' parameter is well documented in the schema ('Must be unique in your account'). The description reinforces this with 'e.g. one per client' and mentions uniqueness implicitly via folder_exists, but it does not add substantial new parameter meaning beyond the schema. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific action ('Create a new folder'), a clear resource (folders for grouping intakes), and a concrete use case ('one per client'). It is clearly distinct from sibling tools like list_folders, and the title/description align without being tautological.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit guidance is provided: 'Call list_folders first and reuse a matching folder — only create one when none of the existing folders fits.' It also explains the failure case ('Fails with folder_exists... use list_folders to find it instead'), giving an agent clear decision rules for when to invoke this tool versus its sibling.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
define_intakeCreate client intakeAIdempotent
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:
follow_up.recommended = "webhook" — the account already has an endpoint; deliveries will arrive there and you need do nothing further.
follow_up.recommended = "schedule" — no endpoint is registered. If you control a service that can receive public HTTPS, register one with manage_webhook. Otherwise tell the user to set up a recurring check (cron, a systemd timer, a scheduled task in their agent host) that calls get_intake_status every follow_up.schedule.every_hours hours until follow_up.schedule.until, and offer to configure it for them.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| send | No | Whether to send the invite email immediately. Default: true. Set to false to create a draft and call /v1/intakes/:id/send later. | |
| items | Yes | List of assets to collect. Each item has key, type, label, and optional constraints. | |
| client | Yes | Client contact details. | |
| branding | No | Override account-level branding for this intake. | |
| due_date | No | Deadline in YYYY-MM-DD format. Shown in the portal and used to escalate chase cadence. | |
| template | No | Template slug to pre-populate items (e.g. "restaurant-website", "consulting-firm"). | |
| folder_id | No | Put this intake in an existing folder from list_folders instead of leaving it unfiled. Folders group intakes by client or project — reuse one for a returning client rather than creating a duplicate with create_folder. | |
| retention | No | How long BriefGate keeps this intake after it is finished. Default: purged 90 days after the client completes. Use mode "on_delivery" when the intake holds anything sensitive (credentials, personal photos): the contents are then removed shortly after YOU collect them with get_intake_results, because at that point you already have the files and there is no reason for a copy to sit on our server. An intake you never collect still expires on the day count, so this can only ever delete data earlier, never later. Example: { "mode": "on_delivery" } — or { "mode": "days", "days": 7 } to just shorten the window. | |
| email_copy | No | Your own subject and intro lines, overriding the built-in translation for this intake. Placeholders: {sender}, {project}, {client}, {count}, {minutes}, {due}. An unknown placeholder is rejected rather than rendered literally to the client. Layout, button and footer stay as they are. | |
| client_brief | No | Free-text brief shown to the client at the top of the portal, above the requested items — information from you to them: an offer, instructions, or context for why you are asking for these items. Up to 5000 characters. Documents attached to the brief go through the REST endpoint POST /v1/intakes/:id/brief/files (dashboard or REST — not available through this MCP tool set). | |
| project_name | Yes | Human-readable project name shown in the invite email and portal heading. | |
| chase_at_time | No | Local time of day to send reminders at, "HH:MM" in the client's timezone (e.g. "07:00"). Anchors the cadence to a clock time instead of counting from the invite, and needs an interval measured in whole days. Naming a time deliberately overrides quiet hours, so "07:00" stays 07:00. | |
| max_reminders | No | Reminders to send before the intake is marked stalled and handed back to you (default 3). An integer from 1 to 1000, or the string "unlimited" to keep reminding until the client finishes. Raise it for a rapid cadence, which would otherwise exhaust three attempts in minutes. A bounce or spam complaint always cancels the remaining reminders, whatever this is set to. | |
| chase_interval | No | How often to remind, only with chase_schedule="custom". Pair with chase_interval_unit. Defaults to every 3 days when omitted. The interval must work out to at least 5 minutes and at most 90 days. | |
| chase_schedule | No | Automated reminder cadence. default=T+2d,T+5d,T+9d,weekly. gentle=T+3d,T+8d,biweekly. aggressive=T+1d,T+3d,T+5d,every-other-day. custom=every chase_interval chase_interval_unit. off=no auto reminders. | |
| auto_approve_hours | No | Hours after submission before an item is auto-approved without agent review. Default: 72. Set to 0 to require explicit approval. | |
| chase_interval_unit | No | Unit for chase_interval. Defaults to "days". | |
| respect_quiet_hours | No | Hold reminders to the client's 08:00-19:00 local window (default true). A cadence of minutes or hours pauses overnight and resumes in the morning; set false to send around the clock. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
It discloses several non-obvious behaviors: BriefGate automatically sends the invite and chases the client, nothing pushes to the agent, follow_up.recommended selects the completion mechanism, secrets are revealed exactly once, and owner items never block completion. These go well beyond the annotations, which only cover read-only/destructive/idempotency hints.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long, but the tool is complex and the length is mostly purposeful, with clear sections for the example, item types, and decisions. It is front-loaded with the core action and the most critical follow-up warning, though some content could be trimmed without losing value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given 18 parameters, nested objects, and no output schema, the description supplies what the schema cannot: the return shape, follow-up workflow, failure mode, and parameter interaction rules. An agent has enough context to call this tool and wire up the resulting intake correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, and the description adds substantial meaning on top: item type semantics, owner vs client assignee behavior, the proposed-answer concept for developer decisions, and a full worked example. It clarifies how 'secret' items behave and when 'structured' requires a JSON Schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence states a specific action ('Create a new client intake request') and describes the resource (a branded portal where the client submits assets). It also distinguishes this creation tool from follow-up siblings by noting the returned intake_id is needed for all follow-up calls.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly says to call it once at the start of a project after knowing the assets needed. It then tells the agent how to learn completion, naming get_intake_status and manage_webhook as the relevant alternatives, and warns against creating an intake and never checking again.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_intake_resultsCollect intake resultsADestructive
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.
| Name | Required | Description | Default |
|---|---|---|---|
| only_new | No | Return only items submitted or updated since the previous get_intake_results call. Default: false. | |
| intake_id | Yes | Intake ID returned by define_intake. | |
| include_pending | No | Include items not yet submitted (useful for partial progress checks). Default: false. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already signal non-read-only and destructive behavior, and the description richly explains why: secrets are decrypted and revealed in plaintext only on the first call, then marked read with first_reveal:false and the value omitted. It also discloses the 24-hour signed-URL validity, the decision semantics (decided_by owner vs. agent_proposal), and the fact that reads do not bump revision. This goes well beyond the annotation hints and matches them consistently (no contradiction).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The definition is long (five paragraphs) but every section earns its place: base retrieval, signed URLs, the one-time secret reveal, the parameter flags, and the subtle decision behavior. The core purpose is front-loaded in the first sentence. The decision paragraph is dense but the underlying semantics are nuanced enough to warrant the detail; it could be tightened slightly without loss.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description correctly compensates by spelling out the return shape ({ results: {...}, meta: {...} }) including the meta sub-fields type/status/submitted_at/first_reveal and the decided_by field. Combined with the schema's parameter documentation, an agent has what it needs to call correctly. Minor gaps (no explicit error conditions or behavior when intake_id is invalid) keep it from a 5.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds meaningful extra semantics beyond the schema: only_new's interaction with revision (a read 'does not bump revision', so only_new surfaces exactly the decisions a person has since answered or changed) and the relationship between include_pending and proposed decisions. This adds real signal beyond the bare property definitions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence 'Retrieve the typed submitted values from a client intake' states a specific verb, resource, and scope. It is clearly distinct from siblings like get_intake_status (status vs. values) and list_intakes (listing vs. retrieving a specific intake's submitted data). An agent can separate it from alternatives without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides concrete usage context: only_new=true for webhook-driven workflows, include_pending=true for partial progress, and an explicit explanation of when a decision result is returned even without include_pending ('you need back the assumption you are building on'). It does not explicitly name alternative tools to avoid, but the purpose is sufficiently distinct that selection is unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_intake_statusCheck intake progressARead-onlyIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| intake_id | Yes | Intake ID returned by define_intake (e.g. "in_8f3k"). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false, covering the safety profile. The description adds meaningful behavioral context beyond these: it explains the return content (per-item status, chase history, last opened) and how the tool fits into a workflow (scheduled checks, follow-up routing). It does not contradict annotations; it complements them.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured: the first sentence states the core purpose and key outputs, the second explains routing to alternatives, and the third adds the scheduled-check workflow. Every sentence contributes meaning without redundancy, and the most critical information is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter read-only tool with no output schema, the description provides a solid overview of return data and usage scenarios. While it does not specify the exact structure of per-item status or chase history, this is a minor gap given the absence of an output schema; the description still enables an agent to call it correctly and understand the next steps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% for the single parameter intake_id, which already includes a clear description. The description does not elaborate on parameter usage or format beyond what the schema states, so it adds no extra semantic value. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool checks the completion status of a client intake and enumerates the specific data it returns (per-item status, chase history, last portal open). It explicitly differentiates itself from sibling tools like send_chase, request_revision, and get_intake_results, so an agent can immediately understand its role.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit when-to-use guidance: it is the tool to consult before deciding between send_chase, request_revision, or get_intake_results. It also specifies the scheduled-check use case when no webhook is registered, and instructs to fetch results via get_intake_results once status becomes 'completed'. This is thorough and actionable.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_foldersList foldersARead-onlyIdempotent
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 }] }.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint and idempotentHint, so the read-only nature is covered. The description adds the return shape (folders array with fields) and clarifies it's a listing operation, providing useful context beyond annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three concise sentences, each serving a distinct purpose: purpose, usage guidance, and return format. No fluff or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a parameterless, read-only tool with no output schema, the description covers everything an agent needs: what it returns, when to call it, and why. It's complete for its simplicity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There are zero parameters, so the schema fully covers all inputs. With no parameters to document, the baseline of 4 applies; the description doesn't need to add parameter details.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb 'List' and resource 'folders in your account', plus explains the purpose 'group intakes by client or project'. This clearly differentiates it from sibling tools like create_folder, and the mention of 'List' makes it obvious it's a read operation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly instructs to call before create_folder or before setting folder_id on other tools, and advises reusing existing folders to avoid duplicates. This is direct when-to-use guidance with a clear rationale.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_intakesList intakesARead-onlyIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| q | No | Free-text search: matches a substring of project name, client name, or client email. | |
| limit | No | Maximum number of results (1–100). Default: 20. | |
| offset | No | Pagination offset. Default: 0. | |
| status | No | Filter by intake status. Omit to return all. | |
| folder_id | No | Filter by folder, using an id from list_folders. Pass the literal string "none" to see only intakes that are not in any folder. | |
| client_email | No | Filter by client email address. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already carry readOnlyHint, openWorldHint, idempotentHint, and destructiveHint false, so the safety profile is fully covered. The description adds the response shape and the folder 'none' convention, but pagination, ordering, and other runtime behaviors are not disclosed beyond what the schema already states. With annotations covering safety, this is adequate but not rich.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three paragraphs with no waste: purpose, use cases, and return format. The core verb and scope are front-loaded in the first sentence. Each paragraph serves a distinct function, and the description remains compact despite covering multiple optional filters.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Although there is no output schema, the description explicitly states the response shape and the fields each intake includes. With 100% parameter schema coverage, rich read-only annotations, and sibling context, an agent has everything needed to invoke the tool correctly. No critical gap is evident.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, with each of the 6 parameters already fully described. The description's filter summary (status, client email, folder, text search) merely restates schema dimensions without adding new semantics, constraints, or usage nuances. The baseline of 3 applies because the schema carries the parameter documentation burden.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description begins with 'List all intakes in your account, optionally filtered by status, client email, folder, or a text search,' which states a specific verb, resource, scope, and filtering options. It clearly distinguishes this multi-intake listing tool from siblings like get_intake_status, which presumably targets a single intake.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The second paragraph gives concrete use cases: overview of active projects, finding an intake by client email when intake_id is lost, counting in-progress intakes, and viewing folder contents. It references list_folders as the source for folder_id, which helps route preparation steps. It does not explicitly state when not to use this tool or name alternatives to avoid, so it stops short of full exclusion guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
loginSign in to BriefGateA
Sign in without pasting an API key, the same way snyk_auth works: this opens a browser page where a human approves this device, then stores the issued key locally.
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:
The first call starts the sign-in and returns immediately with a URL and a short code (e.g. "WDJB-MJHT"). Tell the user to open the URL and confirm the code; a browser is also opened automatically when possible.
Call
loginagain (no arguments change) to check progress. While the human hasn't approved yet, it replies that it's still waiting. Once approved, the same call reports success and the key is saved — no further action needed, other tools start working immediately.
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 --api-key flag or the BRIEFGATE_API_KEY environment variable — those always take priority over a locally stored one, so this tool says so instead of running the flow. Not available when this server is running as the shared hosted endpoint (mcp.briefgate.dev): there, connecting a client already triggers OAuth automatically.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond annotations, the description explains that this is a two-phase flow, that the first call returns a URL and code, that re-calling checks approval status, and that the key is stored locally once approved. It also discloses that it has no effect when env/flag auth exists and that the hosted endpoint auto-triggers OAuth, giving a very complete behavioral picture.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Although the description is long, it is necessary for a non-trivial two-phase flow. It is well-structured with a summary, trigger conditions, numbered phases, and explicit exclusions. Every sentence carries operational information, and the key trigger condition is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a multi-phase tool with no output schema, the description covers the full flow: what happens on first call, what the agent should tell the user, what happens on subsequent calls, and when the tool is not applicable. An agent has enough information to call it correctly and to guide the user through approval.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so the input schema leaves nothing to document; the baseline for no parameters is 4. The description reinforces this by saying repeated calls use 'no arguments change,' which is helpful for agents deciding how to invoke the second phase.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states the specific action ('Sign in without pasting an API key') and the mechanism (browser-based human approval, then local key storage). It is clearly distinct from siblings like logout and the rest of the toolset, and even references a comparable flow (snyk_auth) to anchor meaning.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly says when to call this tool: 'whenever a tool reports “Not signed in” or that the stored key was revoked or expired.' It also gives exclusions: if an API key flag or environment variable is present, and if running on the shared hosted endpoint, the tool should not run the flow.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
logoutSign out of BriefGateADestructiveIdempotent
Remove the API key login stored locally for this BriefGate server, and best-effort revoke it on the server too (a DELETE /v1/keys/current call using that same key). If the revoke call fails — no network, the API is unreachable — the local copy is still removed; the response says so and points at the BriefGate dashboard to revoke it there instead. Not available when this server is running as the shared hosted endpoint.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes well beyond the annotations: it explains the local removal, the server-side DELETE call, the best-effort revocation behavior, what happens when the revoke fails, and where to revoke manually if needed. This aligns with and enriches destructiveHint and readOnlyHint rather than merely repeating them.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three compact sentences pack in the action, the HTTP detail, the failure mode, the fallback guidance, and the availability constraint. Every sentence earns its place and there is no redundant wording.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter, no-output-schema tool, the description is complete: it covers what happens, how it happens, what happens on failure, and when it cannot be used. No critical information is missing for an agent to invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so there is nothing for the description to add beyond the schema. Baseline 4 is appropriate because the description correctly focuses on behavior instead of inventing parameter details.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific action: remove the locally stored API key and best-effort revoke it on the server. It also distinguishes itself by explaining exactly what is modified, so an agent can clearly tell logout from the sibling login tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear behavioral context and an explicit when-not: the tool is unavailable on the shared hosted endpoint. It does not explicitly contrast with alternatives, but for a logout operation the purpose is unambiguous and the failure mode is fully explained.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
manage_recipientsManage intake recipientsADestructive
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.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | Their name, used to address their copy. Only used with action="add". | |
| Yes | The recipient's e-mail address. | ||
| action | Yes | What to do with the address. | |
| intake_id | Yes | Intake ID returned by define_intake. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations, the description discloses meaningful side effects: remove stops future reminders, reinstate clears a bounce flag and may re-plan the schedule, and add creates its own message and bounce state. It also explicitly states failure conditions for non-member addresses and reinstate on non-bounced addresses.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core purpose and then organized by action, with each sentence earning its place. It is dense but not bloated, and the failure conditions are placed at the end as a natural close.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a mutation tool with no output schema, the description covers the three actions, side effects, scheduling implications, and failure cases. It does not describe the success return value, but that is a minor gap given the detail present for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real value by explaining the semantics of each action value, noting that name is only used with add, and clarifying how email/intake_id relate to failure conditions. This goes beyond the schema's terse property descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb set — add, remove, or reinstate — and names the resource (a person receiving an intake's invite and reminders). It clearly distinguishes this tool from siblings like define_intake and update_intake by framing it as managing recipients alongside or instead of the primary client.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear context for each action and even references define_intake's also_notify behavior to explain the add case. It does not explicitly list alternatives or say 'use this instead of X', but the action-specific guidance makes the intended use obvious enough.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
manage_webhookManage webhook endpointsADestructive
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).
| Name | Required | Description | Default |
|---|---|---|---|
| url | No | HTTPS endpoint to deliver to. Required for action="create". | |
| action | Yes | What to do. "list" needs no other argument. | |
| events | No | Events to receive. Required for action="create". For "tell me when the client is done", this is ["intake.completed"]. | |
| format | No | Payload shape. "raw" (default) is the signed BriefGate envelope; "slack" and "discord" post a message those services render directly. | |
| webhook_id | No | Endpoint to remove. Required for action="delete". |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already signal destructive/non-read-only behavior, but the description adds substantial context: the create action returns a one-time secret, the secret cannot be retrieved again, there is no rotation, and unreachable endpoints create false delivery confidence. It also documents conditional event behavior such as intake.stalled only firing with max_reminders set.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but tightly organized: purpose, usage restriction, secret lifecycle, then event definitions. Every sentence carries operational weight, and the most important usage constraint is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a webhook manager with no output schema, this is highly complete: it covers when to use the tool, when not to, secret handling, event semantics, format options, and action-specific parameters. Nothing necessary for correct invocation is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3; the schema already describes url, action, events, format, and webhook_id. The description adds some operational meaning, like the secret-on-create behavior and an events example, but it does not need to carry much of the parameter-semantics burden.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb-and-resource relationship: register, list, or remove webhook endpoints so BriefGate pushes intake events instead of polling. This clearly distinguishes the tool from the polling-based sibling get_intake_status.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly says this tool should only be used when the agent controls a public HTTPS-receiving service, and warns that terminal agents must instead poll with get_intake_status. It also gives concrete event-selection guidance, such as using ["intake.completed"] for client-completion scenarios.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
request_revisionRequest a 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 }.
| Name | Required | Description | Default |
|---|---|---|---|
| note | Yes | Plain-language explanation shown to the client (e.g. "Logo is blurry — we need at least 512 px wide in SVG or PNG with a transparent background"). | |
| item_key | Yes | The key of the item to revise (e.g. "logo", "hero_copy"). | |
| intake_id | Yes | Intake ID returned by define_intake. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond annotations, the description discloses that the client is notified automatically, the item status moves to needs_revision, and the return value is { status: "revision_requested", item_key }. This gives the agent a full picture of side effects and outcome without contradicting the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded: the primary action comes first, followed by the triggering condition with examples, then the side effect and return value. Every sentence contributes necessary information without redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has no output schema, but the description supplies the return shape. It also covers prerequisites via the workflow reference to get_intake_results, and the schema links intake_id to define_intake. Nothing essential is missing for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all three parameters. The description reinforces that the note explains what is wrong but adds little semantic value beyond the schema's own parameter descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the action: ask the client to resubmit a specific item with a note. It includes concrete examples (blurry logo, long copy, broken URL) and describes the resulting status change, making the tool's role unambiguous even without naming sibling alternatives.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly says to use this after reviewing get_intake_results and finding an item that does not meet requirements. It does not discuss when-not-to-use or contrast with update_item or send_chase, but the workflow context is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
send_chaseSend a reminderA
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 }.
| Name | Required | Description | Default |
|---|---|---|---|
| channel | No | Delivery channel. Email is the only one offered. | |
| intake_id | Yes | Intake ID returned by define_intake. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds useful behavioral context beyond the annotations: the automatic chase schedule continues after this call, and the call returns { sent: true }. It does not warn about duplicate sends, but idempotentHint=false already covers that. The SMS mention conflicts with the schema's email-only channel, which slightly weakens transparency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise and front-loaded with the main action, followed by use cases and the return value. Every sentence contributes something, though the SMS sentence is confusing and could be removed or corrected. Overall it is efficient and well-structured.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple two-parameter tool, the description covers the main behavior, use cases, and return value. The missing piece is the contradiction between the SMS mention and the email-only channel, which is significant because it affects how the agent should invoke the tool. The schema covers the intake_id prerequisite, so no major gap there.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema already documents both parameters well. However, the description says an SMS can be sent after email failures, while the channel parameter only allows 'email'. This actively misleads the agent about valid parameter values, so the description detracts from rather than adds to parameter semantics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the core action: sending a manual reminder outside the automatic schedule. It differentiates the tool from sibling intake/list tools and from the automatic chase flow, though it does not name a specific sibling alternative. The mention of sending an SMS after email failures introduces some ambiguity about the tool's actual delivery scope.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit conditions for use: when a deadline is approaching, when automatic reminders got no response, or after email attempts failed. It also clarifies that this is an extra nudge and not a replacement for the automatic schedule. It does not explicitly list when not to use it, but the provided context is actionable.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_intakeEdit intake settingsA
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.
| Name | Required | Description | Default |
|---|---|---|---|
| client | No | Client fields to change. Email cannot be changed here — use manage_recipients. | |
| due_date | No | Deadline in YYYY-MM-DD format. null clears it. | |
| folder_id | No | Move this intake to a different folder, using an id from list_folders. null removes it from any folder. | |
| intake_id | Yes | Intake ID returned by define_intake. | |
| owner_note | No | Private note, never shown to the client. null clears it. | |
| client_brief | No | Free-text brief shown to the client at the top of the portal, above the requested items — information from you to them: an offer, instructions, or context. Up to 5000 characters. null clears it. Documents attached to the brief go through the REST endpoint POST /v1/intakes/:id/brief/files (dashboard or REST — not available through this MCP tool set). | |
| project_name | No | Human-readable project name shown to the client. | |
| chase_at_time | No | Anchor reminders to this 24-hour local time in the client's timezone (e.g. "07:00"), overriding quiet hours. null clears it. | |
| max_reminders | No | Cap on reminder attempts (1-1000), or "unlimited". Raising this above the number already sent reactivates a stalled intake. | |
| chase_interval | No | How often to remind, only meaningful with chase_schedule="custom". Pair with chase_interval_unit. | |
| chase_schedule | No | Automated reminder cadence. default=T+2d,T+5d,T+9d,weekly. gentle=T+3d,T+8d,biweekly. aggressive=T+1d,T+3d,T+5d,every-other-day. custom=every chase_interval chase_interval_unit. off=no auto reminders. | |
| chase_interval_unit | No | Unit for chase_interval. | |
| respect_quiet_hours | No | Whether reminders pause outside 08:00-19:00 in the client's timezone. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (readOnlyHint=false, destructiveHint=false, idempotentHint=false), the description discloses the key side-effect: including any of eight listed fields cancels all pending reminders and replans the schedule from now, with already-sent reminders still counting toward max_reminders. It also documents the stalled-intake reactivation behavior, that folder_id 'never touches the chase schedule', and the archived-intake failure condition. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Eight sentences, each earning its place: purpose, use case, side effects, exclusions, per-field notes, constraints, return value. Despite its length, nothing is redundant with the schema, and the most decision-relevant information (replanning trigger, email exclusion) appears early and prominently.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 13-parameter tool with nested objects and no output schema, the description covers all invocation requirements: required intake_id, the at-least-one-field rule, failure conditions ('Fails if the intake is archived'), return shape, and side-effect semantics. An agent can invoke it correctly using only this text plus the schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description earns a 4 by adding cross-parameter semantics the per-field schema entries don't convey: the exact list of fields that trigger reminder replanning, the interaction between raising max_reminders and reminders already sent, and the guarantee that folder_id is independent of the chase schedule.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence names a specific verb ('Change') and resource ('an intake that has already been sent') and enumerates the editable fields. It is further differentiated from sibling tools like define_intake (creating), manage_recipients (email), and add_items/update_item (items), so an agent can select it unambiguously.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly says 'Use this instead of deleting and recreating the intake when a deadline moves or the chase cadence needs to change' and names manage_recipients as the alternative for email changes. It also tells the agent what not to use it for (brief documents go through REST/dashboard) and states failure preconditions (archived intakes).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_itemEdit an itemADestructive
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.
| Name | Required | Description | Default |
|---|---|---|---|
| help | No | Hint under the label. null clears it. | |
| type | No | ||
| label | No | Human-readable label shown to the client. | |
| options | No | ||
| pattern | No | ||
| item_key | Yes | Key of the item to change. | |
| required | No | ||
| intake_id | Yes | Intake ID returned by define_intake. | |
| constraints | No | Same shape as define_intake, e.g. { "formats": ["svg","png","pdf"] }. null clears all constraints. | |
| discard_submitted_value | No | Go ahead even though it throws away what the client already sent. Only set this after the call has failed once for that reason. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare destructiveHint=true and readOnlyHint=false. The description goes far beyond by detailing the exact failure mode (invalidating a submitted answer), the effect of discard_submitted_value, and the guarantee that nothing is touched on failure. It also clarifies that a non-invalidating change never discards answers. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three paragraphs but every sentence carries purpose. It is front-loaded with the core action, then usage, constraints, and failure handling. It is slightly longer than strictly necessary but not bloated, and the structure is logical.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (10 parameters, mutation with destructive hint, failure modes), the description covers all critical aspects: what it does, when to use, key constraints (immutable key), failure handling, and parameter semantics for the most important parameters. No output schema exists, so return values need not be described. The description is sufficient for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 60%, so the description must compensate. It explains the semantics of key parameters: type, label, help (hint), required, constraints (file formats), and discard_submitted_value in detail. However, it does not clarify pattern or options, which remain under-documented. Overall it adds meaningful meaning beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a clear verb ('Change') and specific resource ('one item on an intake') and enumerates the editable fields (type, label, hint, required, file formats). It also distinguishes from sibling tools by noting it edits existing items already with the client, versus add_items or define_intake. The purpose is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when-to-use guidance is given with concrete examples ('field turns out to be the wrong shape'), plus a clear alternative ('Add a new item instead') for changing the item key. It also explains the failure condition (client already answered and change invalidates their answer) and how to proceed (discard_submitted_value). This fully routes an agent.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
12 tool updates
v0.9.0- Changed
add_items1 field changed- changed
Input schema / properties / items / items / properties / type / enumPrevious value: -[ - "text", - "longtext", - "file", - "file_list", - "image", - "color_list", - "select", - "boolean", - "url", - "secret", - "structured" -]New value: +[ + "text", + "longtext", + "file", + "file_list", + "image", + "color_list", + "select", + "multiselect", + "boolean", + "url", + "secret", + "structured" +]
- Added
create_folder - Changed
define_intake7 fields changed- added
Input schema / properties / client / properties / also_notifyAdded value: +{ + "description": "Other people who should receive the same invitation and the same reminders, through the same portal link — two directors of one company, say, where it does not matter which of them supplies the material. Each address gets its own message (nobody sees the others) and its own bounce state, so one dead address does not stop the rest being chased. At most 4, on top of the primary client.", + "items": { + "properties": { + "email": { + "description": "Their email address.", + "type": "string" + }, + "name": { + "description": "Their name, used to address their copy.", + "type": "string" + } + }, + "required": [ + "email" + ], + "type": "object" + }, + "type": "array" +} - changed
Input schema / properties / client / properties / name / descriptionPrevious value: -"Client name for personalised emails."New value: +"Client name (required). Every email opens by addressing them by name, so a blank one sends \"Hello,\" to someone being asked for their admin password." - changed
Input schema / properties / client / requiredPrevious value: -[ - "email" -]New value: +[ + "email", + "name" +] - added
Input schema / properties / client_briefAdded value: +{ + "description": "Free-text brief shown to the client at the top of the portal, above the requested items — information from you to them: an offer, instructions, or context for why you are asking for these items. Up to 5000 characters. Documents attached to the brief go through the REST endpoint POST /v1/intakes/:id/brief/files (dashboard or REST — not available through this MCP tool set).", + "maxLength": 5000, + "type": "string" +} - added
Input schema / properties / folder_idAdded value: +{ + "description": "Put this intake in an existing folder from list_folders instead of leaving it unfiled. Folders group intakes by client or project — reuse one for a returning client rather than creating a duplicate with create_folder.", + "type": "string" +} - added
Input schema / properties / items / items / properties / assigneeAdded value: +{ + "description": "Who owes this. \"client\" (the default) is something the client fills in through the portal. \"owner\" is a private to-do for the account holder, e.g. \"call the client\": it never appears in the client portal, is never mentioned in a reminder, and never holds up completion of the intake. Use type \"boolean\" for a plain tick-off task. Owner items cannot use type file, file_list, image or secret.", + "enum": [ + "client", + "owner" + ], + "type": "string" +} - changed
Input schema / properties / items / items / properties / type / enumPrevious value: -[ - "text", - "longtext", - "file", - "file_list", - "image", - "color_list", - "select", - "boolean", - "url", - "secret", - "structured" -]New value: +[ + "text", + "longtext", + "file", + "file_list", + "image", + "color_list", + "select", + "multiselect", + "boolean", + "url", + "secret", + "structured" +]
- Added
list_folders - Changed
list_intakes2 fields changed- added
Input schema / properties / folder_idAdded value: +{ + "description": "Filter by folder, using an id from list_folders. Pass the literal string \"none\" to see only intakes that are not in any folder.", + "type": "string" +} - added
Input schema / properties / qAdded value: +{ + "description": "Free-text search: matches a substring of project name, client name, or client email.", + "type": "string" +}
- Added
login - Added
logout - Added
manage_recipients - Added
manage_webhook - Changed
send_chase2 fields changed- changed
Input schema / properties / channel / descriptionPrevious value: -"Delivery channel. Default: email. Use \"sms\" only if the client provided a phone number and has not responded to emails."New value: +"Delivery channel. Email is the only one offered." - changed
Input schema / properties / channel / enumPrevious value: -[ - "email", - "sms" -]New value: +[ + "email" +]
- Added
update_intake - Added
update_item
7 tool updates
v0.2.1- First observed
add_items - First observed
define_intake - First observed
get_intake_results - First observed
get_intake_status - First observed
list_intakes - First observed
request_revision - First observed
send_chase
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.
Maintenance
Related MCP Connectors
Client intake for AI agents: request files, texts, choices and credentials from a client, chase missing items automatically, get notified when complete. Hosted Streamable HTTP endpoint with OAuth 2.1 (PKCE, dynamic client registration); the same tools as the @briefgate/mcp npm package.
- mcp-serverOAuthcom.make
Give your AI agents the tools to build, manage, and run automation workflows.
Build and send email, SMS, and push straight from your AI agent.
Private projects, preflight, decisions, and handoffs that keep work continuous across AI tools.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceIntegrates with the AgentCraft framework to enable secure communication and data exchange between AI agents, supporting both premade and custom enterprise AI agents.3-
- AlicenseAqualityDmaintenanceEnables AI agents to connect and interact with common business platforms like Shopify, WordPress, and Calendly via pre-built connectors for tasks such as product management, content operations, and scheduling.13MIT
- FlicenseNot gradedqualityCmaintenanceEnables AI coding agents to plan, build, and review websites and product interfaces with a persistent, user-led process, including design direction, component contracts, and implementation review.-
- AlicenseNot gradedqualityAmaintenanceEnables AI coding agents to collaborate on the same project by sharing session briefs and reading each other's native transcripts, memories, and instructions in place, with zero-copy, across different agent tools.58 npmMIT