@briefgate/mcp
BriefGate is an MCP server for collecting files, text, choices, and credentials from a client or other outside person through a branded emailed portal, then consuming the typed results in an agent workflow.
Create client intakes (
define_intake) with typed items: text, longtext, file, file_list, image, color_list, select, multiselect, boolean, url, secret, and structured JSON-schema data.Send invitations and automate chasing — BriefGate emails the client, sends reminders on a configurable schedule, and can notify via webhook when items arrive or the intake completes.
Decisions for the account holder — add owner-assignee select/multiselect items with proposed answers that only a human can confirm in the dashboard.
Check progress (
get_intake_status) — see per-item status, chase history, when the client last opened the portal, and whether the intake is draft, sent, in_progress, completed, or archived.Retrieve results (
get_intake_results) — get typed values, signed 24-hour file URLs, and one-time plaintext reveals for secret items.Request revisions (
request_revision) — ask the client to resubmit an item with a note explaining what is wrong.Send manual reminders (
send_chase) — nudge the client outside the automatic schedule.Manage intakes after sending — add items (
add_items), edit item definitions (update_item), and update settings like due date, cadence, quiet hours, folder, or client brief (update_intake).Manage recipients (
manage_recipients) — add, remove, or reinstate people who receive the invite and reminders.Organize with folders (
list_folders,create_folder) — group intakes by client or project.Webhooks (
manage_webhook) — register endpoints to receive events such asintake.completed,item.submitted,client.viewed,chase.bounced,intake.overdue, andintake.stalled.Authentication tools —
loginandlogoutprovide device-authorization sign-in without manually pasting an API key.
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 — from your browser, or from your AI coding agent.
Your agent can build the website. BriefGate gets the missing things from the client.
No agent? You don't need one. Everything below can be done by hand at app.briefgate.dev: build the request, send the link, watch answers arrive, download the results as one ZIP — no code, no API key, no MCP client. This package is for the other way of working, where an agent does it for you. See the dashboard quickstart.
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
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.
By default gemini extensions install fetches the repo's latest GitHub Release rather than the main branch; if that release predates the current gemini-extension.json and the command reports a missing configuration file, install from main directly instead: gemini extensions install --ref main https://github.com/sekera-radim/briefgate-mcp.
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.
Secrets are shown only on the first retrieval.
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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| items | No | |
| status | No | |
| folder_id | No | |
| follow_up | No | How to learn this intake is done — present unless a webhook already covers it. Mirrors FollowUpAdvice in client.ts. |
| intake_id | No | |
| portal_url | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations indicate readOnlyHint=false (mutation), destructiveHint=false (not destructive), and idempotentHint=false. The description adds important behavioral details: the client is notified, existing items are untouched, and returns the updated intake. It doesn't mention potential side effects like rate limits, but the client notification is a key behavioral trait not captured in 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 short paragraphs: one for purpose with examples, one for behavioral notes, and one for validation rules. It is front-loaded with the core purpose, and every sentence adds value without unnecessary fluff.
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 2 parameters and an output schema, the description is mostly complete. It specifies the items schema references another tool, mentions notification and non-impact on existing items, and returns the updated intake. The only minor gap is not detailing validation failure behavior (e.g., partial additions), but the output schema and schema coverage mitigate this.
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 fully documents the parameters and their properties. The description adds minimal extra meaning beyond referencing 'same rules as define_intake' and the snake_case key requirement, but since the schema covers it, a 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 verb ('Add') and resource ('items to an intake'), and specifies the exact use case: adding items after an intake has been sent, with examples. It distinguishes itself from siblings like update_intake and define_intake by focusing on post-send additions.
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 use this tool: when you need to add items to an already-sent intake, and gives concrete examples. It also implicitly differentiates from define_intake (for initial definition) and update_item (for modifying existing items) by stating existing items are not affected.
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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | |
| name | No | |
| created_at | No | |
| sort_order | No | |
| intake_count | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations, the description discloses the duplicate-name error condition, the uniqueness requirement, and the exact return payload. This gives the agent clear expectations about side effects and failure behavior.
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?
Every sentence earns its place: purpose, usage guidance, error condition, and return value. The most important routing instruction is front-loaded, and the description is compact without being vague.
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 with annotations and an output schema, the description covers the essential context: when to create, what happens on duplicates, and what the response contains. Nothing needed to invoke it correctly 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?
The schema already documents the 'name' parameter with uniqueness and examples, so coverage is 100%. The description adds the grouping use case but does not materially improve parameter understanding 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 clearly states the tool creates a new folder to group intakes, with a concrete example ('one per client'). It distinguishes this tool from list_folders by framing creation as the fallback when no existing folder fits.
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 the agent to call list_folders first, reuse a matching folder, and only create when none matches. It also names the duplicate error and directs the agent back to list_folders, leaving no ambiguity about when to use this tool.
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; the value is shown only on the first retrieval), structured (requires schema with JSON Schema).
DECISIONS — questions for the account holder, not the client. An item with assignee="owner" and type select/multiselect is a question only the account holder can answer ("does the discounted plan cost 19 or 29?"). It can carry an optional "proposed" answer, e.g. proposed = { value: "19", rationale: "matches the competitor we benchmarked" }, which is stored separately from the account holder's answer and clearly labelled as a proposal.
get_intake_results returns the current answer with meta..decided_by: "owner" when the account holder answered in the dashboard, "agent_proposal" while only the proposal exists. A proposal cannot be confirmed through this API; only the account holder answers it. 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| status | No | |
| notices | No | Cadence caveats, present only when chase_schedule="custom" makes them relevant. |
| follow_up | No | How to learn this intake is done — present unless a webhook already covers it. Mirrors FollowUpAdvice in client.ts. |
| intake_id | No | |
| portal_url | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond annotations, the description reveals important side effects: the invite email is sent, BriefGate chases the client automatically, and nothing pushes back because MCP is request/response. It also warns about the common failure of never checking, explains the follow_up block, and even flags the blank-name pitfall ('Hello,' to someone being asked for their admin password).
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 length is largely justified by 18 parameters, nested objects, and complex lifecycle behavior. It is front-loaded with the core purpose, then uses titled blocks and a concrete example; some content slightly repeats schema details, so it is not perfectly lean.
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?
Covers creation, the returned contract, follow-up setup, item and assignee semantics, naming constraints, and failure modes. An output schema exists, so the description does not need to enumerate return fields beyond the key { intake_id, portal_url, status, follow_up } shape. Nothing an agent needs to decide whether, when, or how to call it 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?
The schema already covers 100% of parameters, but the description adds substantial meaning beyond it: a full example intake, snake_case key rule, semantics for each item type, assignee='owner' behavior, proposal/decided_by flow, retention nuances, and chase cadence details. This materially improves an agent's ability to construct correct requests.
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 'Create a new client intake request — a branded portal where the client submits logos, copy, files, credentials, and other assets' with a specific verb, resource, and outcome. The create-vs-manage distinction is clear from 'Call this once at the start of a project' and from the sibling tool names like update_intake and add_items.
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 says to call this once at the project start after the needed assets are known, and gives detailed post-create guidance on setting up follow-up via webhook or schedule. It does not explicitly name alternatives like update_intake for modifying an existing intake, so exclusion guidance is implied rather than fully stated.
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 download URLs that expire after 24 hours.
Secrets (type=secret, e.g. passwords, API keys) are decrypted and included only in the first retrieval. Later calls return first_reveal: false in meta and omit the value, so the user should be ready to receive a secret before this tool is called on an intake that contains one.
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 item (assignee=owner, type select/multiselect), results holds the current answer and meta..decided_by is "owner" (answered by the account holder) or "agent_proposal" (only a proposal exists). Proposals are returned even without include_pending. A proposal does not bump revision, so only_new returns decisions the account holder has answered or changed since the last call.
| 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| meta | Yes | Keyed the same as results. |
| status | Yes | |
| results | Yes | Keyed by this intake's own item keys. A value's shape depends on that item's type. |
| intake_id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations flag destructiveHint=true, readOnlyHint=false, and idempotentHint=false, and the description explains exactly why: secrets are decrypted and included only on the first call, later calls omit the value with first_reveal: false; files expire after 24 hours; and proposals are returned without include_pending yet don't bump revision, so they don't trigger only_new. This is rich behavioral disclosure fully consistent with 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 lengthy but well-structured: the purpose is front-loaded, and each paragraph addresses one distinct concern (files, secrets, flags, return shape, decision items). The explicit 'Returns { results ... }' block is partially redundant given the stated output schema, but every sentence carries distinct, non-fluff information that an agent needs.
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 tool with this much behavioral nuance — one-time secret reveal, signed URL expiry, and the interplay of proposals with only_new — the description covers the full calling surface: return shape, parameter effects, side effects, and edge cases like decided_by being 'owner' versus 'agent_proposal.' Nothing an agent needs to call it correctly 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 a baseline of 3 applies. The description adds real meaning beyond the schema: it explains only_new's 'since the last call' semantics and the revision/proposal nuance that affects it, and clarifies include_pending as partial-progress inclusion. intake_id is already self-explanatory from the schema, so the added value is concentrated but meaningful.
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 line 'Retrieve the typed submitted values from a client intake' uses a specific verb and resource, and it clearly distinguishes the tool from siblings like get_intake_status (status vs. values) and list_intakes. The remainder of the description reinforces exactly what data the tool returns (results plus meta), leaving no ambiguity about its core function.
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 concrete usage context: only_new is positioned 'useful in webhook-driven workflows,' include_pending is for 'partial progress checks,' and a precondition warns the user to be ready to receive a secret. However, it never names an alternative sibling or states when not to use this tool (e.g., versus get_intake_status), so explicit tool-routing guidance is missing.
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"). |
Output Schema
| Name | Required | Description |
|---|---|---|
| items | Yes | |
| chases | Yes | |
| status | Yes | |
| due_date | No | |
| progress | Yes | |
| intake_id | No | |
| client_brief | No | The brief shown to the client above the requested items, if one is set. |
| client_last_seen | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false, so the safety profile is covered. The description adds meaningful context beyond that: it mentions the tool returns chase history and last-open time, and it specifies the scheduled-check behavior ('This is also the call a scheduled check should make when no webhook is registered'). This enriches the agent's understanding without contradicting 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 efficiently written, with the core purpose in the first sentence and usage guidance following. It is front-loaded and each sentence earns its place, though there is slight redundancy in restating 'Returns per-item status and chase history' after already listing those items. Overall, it is compact and well-organized.
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 tool with a single parameter and an output schema, the description covers purpose, usage scenarios, and the scheduled-check exception. It references the defining tool (define_intake) and the result tool (get_intake_results), giving the agent a clear workflow. The only minor gap is that it doesn't describe pagination or volume limits, but that is likely covered by the output schema and the tool's 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?
Schema description coverage is 100%, and the description does not add new parameter information. It only restates that intake_id comes from define_intake, which is already in the schema. The baseline of 3 for high coverage applies, as the description does not need to compensate for missing schema documentation.
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 ('Check the completion status of a client intake') and enumerates the exact information returned (submitted/pending/revision status, chase email history, last portal open). It also names sibling tools to route selection, distinguishing it from get_intake_results, send_chase, and request_revision.
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 states when to use this tool versus alternatives: 'Use this to decide whether to send a manual reminder (send_chase), request a revision (request_revision), or fetch results (get_intake_results).' It also provides a specific scheduled-check scenario with the cadence source ('follow_up in the define_intake response') and the follow-up action when status completes. No ambiguity.
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 | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| folders | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, and destructiveHint, so the safety profile is covered. The description adds the return structure (folders array with fields) and the reuse context, which goes beyond annotations. No contradictions. A 4 is appropriate because the description contributes useful behavioral context on top of 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 sentences, front-loaded with the purpose and immediately followed by usage guidance and return format. No fluff, every sentence earns its place. The structure is efficient and easy to scan.
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, parameterless read-only tool with an output schema, the description covers the purpose, usage timing, return structure, and even provides a reason to call it. Nothing an agent needs to invoke it correctly is missing. The description is complete and self-sufficient.
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 to describe. Schema coverage is trivially 100%. The baseline for 0 params is 4, and the description does not need to add parameter information since none exist. It correctly omits any param 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 clearly states it lists folders used for grouping intakes by client/project, with a specific verb and resource. It distinguishes itself from siblings by naming when to call it (before create_folder, etc.) and why (reuse existing folder). This fully clarifies the tool's purpose and differentiates it from 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?
The description explicitly instructs when to use the tool: 'Call this before create_folder or before setting folder_id on define_intake, update_intake, or list_intakes.' It also provides an anti-pattern (avoid creating duplicates) and names the alternative action (reuse existing folder). This is exemplary usage guidance.
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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| total | Yes | |
| intakes | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already carry readOnlyHint, openWorldHint, idempotentHint, and destructiveHint, so the description does not need to repeat those traits. It adds useful behavioral context by specifying account scope, optional filtering, and the exact response envelope with the fields included in each intake.
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 and front-loaded: operation and filters first, then use cases, then return shape. The use-case sentence is slightly longer than strictly necessary, but each listed scenario earns its place by clarifying when an agent should call this tool.
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 list tool with six optional filters and zero required parameters, this description is complete: it states the operation, gives decision triggers, names the related list_folders tool, and documents the response shape. Annotations cover the safety and idempotency profile, so the agent has enough to call 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 input schema has 100% parameter description coverage, including details like q substring matching, limit range, folder_id 'none' semantics, and default values. The description only summarizes the filter categories and adds no new parameter-level meaning, so the baseline of 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 first sentence clearly states the operation ('List all intakes in your account') and the filter dimensions available ('status, client email, folder, or a text search'). The return-shape sentence reinforces that this is a list-level tool, distinguishing it from single-intake siblings like get_intake_status and get_intake_results.
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: overviewing active projects, finding an intake by client email when intake_id is lost, counting in-progress intakes, and inspecting a folder from list_folders. It does not explicitly tell the agent when not to use this tool or route to single-intake alternatives, but the context is clear enough to guide selection.
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. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description reveals important side effects beyond the annotations: it stops future reminders, clears the bounce flag, re-plans the schedule if the reinstated address was the only one being chased, and fails under specific conditions. This gives the agent a much clearer picture of what mutating the recipient list entails.
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 overall purpose, then unpacks each action in turn, and ends with failure conditions. There is no filler or repetition of schema text; every sentence contributes a distinct fact needed to choose and invoke an action.
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?
With an output schema present and annotations already signaling mutation, destructiveness, and idempotency, the description covers all operational essentials: action semantics, parameter roles, error conditions, and schedule re-planning. An agent has enough to call the tool 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 100%, so parameters are already documented. The description adds useful semantic detail by explaining what each action actually does to the recipient and when name is relevant, which goes beyond the schema's brief field 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 opening sentence names the resource (intake recipients) and the three operations (add, remove, reinstate), making the tool's purpose specific. It also distinguishes this from creation-time recipient setup by referencing 'also_notify does at define_intake time', so an agent understands this manages recipients after intake creation.
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?
Each action is tied to a concrete scenario: add invites, remove stops reminders, and reinstate clears a wrong bounce. The reference to define_intake's also_notify gives context for when this tool is the right post-creation choice, though it stops short of explicitly saying 'do not use for initial recipient setup'.
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 signing "secret" exactly once. The receiving service needs it to verify the signature on every delivery (verifyWebhookSignature from @briefgate/mcp/webhook), and it cannot be shown 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". |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes well beyond the annotations. It discloses that the signing secret is returned exactly once and cannot be shown again, that there is no rotation, and that the remedy is to delete and recreate. It also explains event semantics, including that intake.stalled only fires when max_reminders is set. This is rich behavioral context that annotations alone do not provide.
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 with clear paragraphs for usage, secret handling, and events. It is longer than average, but every sentence carries important information. The front-loading of the core purpose and the explicit usage condition is effective. Slight deduction for the event list being somewhat dense, but it earns its place.
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 (5 params, 3 actions, 6 events, security implications), the description covers all critical aspects: when to use, what the secret is, event semantics, and action-specific parameter requirements. The output schema exists, so return values don't need explanation. Nothing essential is missing for an agent to invoke this tool 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%, so the baseline is 3. The description adds value by explaining the action-specific requirements (url required for create, webhook_id for delete), the meaning of the events array with a concrete example (["intake.completed"]), and the format options. It doesn't fully document every parameter's edge cases, but it meaningfully supplements 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 opens with a specific verb and resource ('Register, list, or remove a webhook endpoint') and immediately states the purpose: pushing intake events instead of polling. It clearly distinguishes itself from sibling tools like get_intake_status by naming the alternative and the condition that selects it.
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 use this tool ('ONLY if you control a service that can receive public HTTPS requests') and when not to ('An agent running in a terminal cannot — for that case do not register anything and check on a schedule with get_intake_status instead'). It also warns about the consequence of misuse (failing deliveries and false impression). This is exemplary usage guidance.
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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| status | No | |
| item_key | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses the key behavioral side effects: the client is notified automatically and the item status moves to needs_revision, and it returns a specific object { status: 'revision_requested', item_key }. These details go beyond the annotations, which only indicate non-read-only, non-idempotent, non-destructive, and open-world behavior. The description also implies a write operation that cannot be idempotent, which aligns 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 brief and front-loaded: the first sentence states the core action, the second gives usage context with examples, and the third reports the return value. No extraneous content; every sentence carries essential information.
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 description covers purpose, when to use, side effects, and return value. With the output schema already available and all parameters documented, this is sufficient for an agent to correctly invoke the tool in the expected workflow.
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 input schema already describes all three required parameters (intake_id, item_key, note) with explanations, achieving 100% coverage. The description's examples (e.g., blurry logo, copy too long) illustrate usage but do not add new semantic constraints beyond what the schema provides, so it earns the baseline score.
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's action: asking the client to resubmit a specific item with an explanatory note. It provides concrete examples of what constitutes a revision-worthy item (blurry logo, overlong copy, broken URL), which helps the agent understand the intent. However, it does not explicitly differentiate from sibling tools like update_item, relying on the distinct verb 'request revision' rather than naming 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?
The description explicitly states when to use the tool: after reviewing get_intake_results and finding an item that does not meet requirements. It also notes the automatic client notification and status change to needs_revision. This gives clear usage context, but it doesn't provide when-not conditions or mention alternative tools such as update_item for direct edits.
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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| sent | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With annotations indicating it is not read-only (readOnlyHint=false), not idempotent, and not destructive, the description adds value by noting that the automatic schedule continues after this call, preventing the agent from thinking it disables automation. It also discloses the return value, though the output schema already provides that. The description doesn't mention potential side effects like duplicate reminders, but with the annotations, the bar is lower, and this is adequate.
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 with three sentences, front-loading the core purpose and usage context. It includes the return value in the last sentence, which is efficiently placed. No redundant information; every sentence contributes to understanding.
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 moderate complexity, the description covers purpose, usage, behavioral notes, and return value. The output schema already documents parameters and return type, so no further detail is needed. It could mention that channel is always 'email' but that's in the schema; the description is complete enough.
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 schema already documents both parameters: channel is an enum limited to 'email', and intake_id references define_intake. The description does not add additional meaning beyond that, but since schema coverage is 100%, the baseline of 3 is appropriate. No further explanation is needed.
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 verb (send), the resource (reminder), and the context (manual nudge outside automatic schedule). It distinguishes itself from automatic reminders and from sibling tools like update_intake or get_intake_status, so an agent can confidently select it for this specific action.
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 specifies when to use: when a deadline is approaching and the client has not responded, or when SMS is needed after email failures. It also clarifies that the automatic schedule continues, so the agent knows this is an extra nudge, not a replacement. This directly guides decision-making among alternatives.
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. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations, it discloses critical side effects: including certain fields cancels all pending reminders and re-plans the schedule, raising max_reminders reactivates a stalled intake, and folder_id never touches the chase schedule. It also explains the email limitation and document attachment route, giving an agent accurate expectations.
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 every sentence carries operational value, and the main purpose is front-loaded before the caveats. It efficiently packs usage guidance, exclusions, side effects, and failure conditions without repetition.
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 tool with 13 parameters, nested objects, and significant side effects, the description covers input requirements, when to use alternatives, error conditions, and special cases like archived intakes and stalled reminders. The output schema exists, so not describing the return value is acceptable.
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 baseline is 3, but the description adds cross-parameter behavior not visible in the schema: the reminder re-planning trigger, max_reminders reactivation semantics, and null-clearing behavior for folder_id and client_brief. This adds genuine meaning beyond the individual 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 uses a specific verb and resource ('Change settings on an intake that has already been sent') and enumerates exactly which fields can be modified. It clearly distinguishes itself from siblings like define_intake and manage_recipients by stating what this tool can and cannot do.
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 instead of deleting and recreating an intake when deadlines or chase cadence change, and it names manage_recipients as the alternative for changing the client email. It also gives constraints: fails if archived and at least one field must be provided.
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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| item | No | |
| discarded_submitted_value | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations' destructiveHint, the description discloses critical behavior: item keys are immutable because results are keyed by them, invalidating a submitted answer fails the call without side effects, and only discard_submitted_value: true clears the answer. This gives the agent a clear mental model of failure modes and idempotency.
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, then builds context through usage scenarios, a hard constraint, and failure semantics. Every paragraph serves a distinct function with no filler.
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 destructive 10-parameter mutation tool, the description covers the essential operational context: when to use it, what cannot change, how invalidations behave, and how to force a resubmission. The output schema covers return values, so nothing critical 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?
With 60% schema coverage, the description adds meaningful semantics for type switching, required flags, accepted formats, and the discard_submitted_value escape hatch. It does not elaborate on options or pattern, but the description significantly strengthens understanding of the key mutation parameters.
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 and resource: 'Change one item on an intake that is already with the client' and enumerates exactly which attributes can change (type, label, hint, required, accepted formats). This clearly distinguishes it from siblings like add_items and update_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?
It gives concrete when-to-use guidance ('Reach for this when the field turns out to be the wrong shape'), explains the alternative approach ('Add a new item instead' when the key must change), and details the discard_submitted_value workflow after a failed call. No ambiguity remains about when this tool is appropriate.
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.
13 tool updates
v0.10.5- Changed
add_items1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "folder_id": { + "type": [ + "string", + "null" + ] + }, + "follow_up": { + "description": "How to learn this intake is done — present unless a webhook already covers it. Mirrors FollowUpAdvice in client.ts.", + "properties": { + "reason": { + "type": "string" + }, + "recommended": { + "enum": [ + "webhook", + "schedule" + ], + "type": "string" + }, + "schedule": { + "properties": { + "check_with": { + "type": "string" + }, + "every_hours": { + "type": "number" + }, + "until": { + "type": "string" + } + }, + "type": "object" + }, + "webhook": { + "properties": { + "active_endpoints": { + "type": "number" + }, + "events": { + "items": { + "type": "string" + }, + "type": "array" + }, + "register_with": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "intake_id": { + "type": "string" + }, + "items": { + "items": { + "properties": { + "key": { + "type": "string" + }, + "status": { + "enum": [ + "pending", + "submitted", + "needs_revision", + "approved" + ], + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + }, + "portal_url": { + "type": "string" + }, + "status": { + "enum": [ + "draft", + "sent", + "in_progress", + "completed", + "archived" + ], + "type": "string" + } + }, + "type": "object" +}
- Changed
create_folder1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "created_at": { + "type": [ + "string", + "null" + ] + }, + "id": { + "type": "string" + }, + "intake_count": { + "type": "number" + }, + "name": { + "type": "string" + }, + "sort_order": { + "type": "number" + } + }, + "type": "object" +}
- Changed
define_intake1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "follow_up": { + "description": "How to learn this intake is done — present unless a webhook already covers it. Mirrors FollowUpAdvice in client.ts.", + "properties": { + "reason": { + "type": "string" + }, + "recommended": { + "enum": [ + "webhook", + "schedule" + ], + "type": "string" + }, + "schedule": { + "properties": { + "check_with": { + "type": "string" + }, + "every_hours": { + "type": "number" + }, + "until": { + "type": "string" + } + }, + "type": "object" + }, + "webhook": { + "properties": { + "active_endpoints": { + "type": "number" + }, + "events": { + "items": { + "type": "string" + }, + "type": "array" + }, + "register_with": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "intake_id": { + "type": "string" + }, + "notices": { + "description": "Cadence caveats, present only when chase_schedule=\"custom\" makes them relevant.", + "items": { + "type": "string" + }, + "type": "array" + }, + "portal_url": { + "type": "string" + }, + "status": { + "enum": [ + "draft", + "sent", + "in_progress", + "completed", + "archived" + ], + "type": "string" + } + }, + "type": "object" +}
- Changed
get_intake_results1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "intake_id": { + "type": "string" + }, + "meta": { + "additionalProperties": { + "additionalProperties": true, + "properties": { + "decided_by": { + "description": "Only present for an assignee=owner decision item.", + "enum": [ + "owner", + "agent_proposal" + ], + "type": "string" + }, + "first_reveal": { + "description": "Only present for type=secret: true on the call that reveals the plaintext value, false after.", + "type": "boolean" + }, + "status": { + "enum": [ + "pending", + "submitted", + "needs_revision", + "approved" + ], + "type": "string" + }, + "submitted_at": { + "type": [ + "string", + "null" + ] + }, + "type": { + "enum": [ + "text", + "longtext", + "file", + "file_list", + "image", + "color_list", + "select", + "multiselect", + "boolean", + "url", + "secret", + "structured" + ], + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "description": "Keyed the same as results.", + "type": "object" + }, + "results": { + "additionalProperties": true, + "description": "Keyed by this intake's own item keys. A value's shape depends on that item's type.", + "type": "object" + }, + "status": { + "enum": [ + "draft", + "sent", + "in_progress", + "completed", + "archived" + ], + "type": "string" + } + }, + "required": [ + "intake_id", + "status", + "results", + "meta" + ], + "type": "object" +}
- Changed
get_intake_status1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "chases": { + "items": { + "properties": { + "attempt_no": { + "type": "number" + }, + "channel": { + "type": "string" + }, + "sent_at": { + "type": [ + "string", + "null" + ] + }, + "status": { + "type": "string" + } + }, + "required": [ + "channel", + "status", + "attempt_no" + ], + "type": "object" + }, + "type": "array" + }, + "client_brief": { + "description": "The brief shown to the client above the requested items, if one is set.", + "type": "string" + }, + "client_last_seen": { + "type": [ + "string", + "null" + ] + }, + "due_date": { + "type": [ + "string", + "null" + ] + }, + "intake_id": { + "type": "string" + }, + "items": { + "items": { + "properties": { + "key": { + "type": "string" + }, + "label": { + "type": "string" + }, + "status": { + "enum": [ + "pending", + "submitted", + "needs_revision", + "approved" + ], + "type": "string" + }, + "submitted_at": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "key", + "status", + "label" + ], + "type": "object" + }, + "type": "array" + }, + "progress": { + "properties": { + "submitted": { + "type": "number" + }, + "total": { + "type": "number" + } + }, + "required": [ + "submitted", + "total" + ], + "type": "object" + }, + "status": { + "enum": [ + "draft", + "sent", + "in_progress", + "completed", + "archived" + ], + "type": "string" + } + }, + "required": [ + "status", + "progress", + "items", + "chases" + ], + "type": "object" +}
- Changed
list_folders1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "folders": { + "items": { + "properties": { + "created_at": { + "type": [ + "string", + "null" + ] + }, + "id": { + "type": "string" + }, + "intake_count": { + "type": "number" + }, + "name": { + "type": "string" + }, + "sort_order": { + "type": "number" + } + }, + "required": [ + "id", + "name", + "sort_order", + "intake_count", + "created_at" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "folders" + ], + "type": "object" +}
- Changed
list_intakes1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "intakes": { + "items": { + "properties": { + "client_email": { + "type": "string" + }, + "created_at": { + "type": [ + "string", + "null" + ] + }, + "due_date": { + "type": [ + "string", + "null" + ] + }, + "folder_id": { + "type": [ + "string", + "null" + ] + }, + "intake_id": { + "type": "string" + }, + "portal_url": { + "type": "string" + }, + "project_name": { + "type": "string" + }, + "status": { + "enum": [ + "draft", + "sent", + "in_progress", + "completed", + "archived" + ], + "type": "string" + } + }, + "required": [ + "intake_id", + "project_name", + "status", + "created_at", + "portal_url" + ], + "type": "object" + }, + "type": "array" + }, + "total": { + "type": "number" + } + }, + "required": [ + "intakes", + "total" + ], + "type": "object" +}
- Changed
manage_recipients1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "properties": { + "bounced_at": { + "type": "null" + }, + "email": { + "type": "string" + }, + "still_chasing": { + "type": "boolean" + } + }, + "type": "object" + } + ], + "type": "object" +}
- Changed
manage_webhook1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "anyOf": [ + { + "properties": { + "active": { + "type": "boolean" + }, + "created_at": { + "type": "string" + }, + "events": { + "items": { + "type": "string" + }, + "type": "array" + }, + "format": { + "type": "string" + }, + "id": { + "type": "string" + }, + "note": { + "type": "string" + }, + "secret": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object" + }, + { + "properties": { + "webhooks": { + "items": { + "properties": { + "active": { + "type": "boolean" + }, + "created_at": { + "type": "string" + }, + "events": { + "items": { + "type": "string" + }, + "type": "array" + }, + "format": { + "type": "string" + }, + "id": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + }, + { + "properties": { + "deleted": { + "enum": [ + true + ], + "type": "boolean" + } + }, + "type": "object" + } + ], + "type": "object" +}
- Changed
request_revision1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "item_key": { + "type": "string" + }, + "status": { + "enum": [ + "revision_requested" + ], + "type": "string" + } + }, + "type": "object" +}
- Changed
send_chase1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "sent": { + "enum": [ + true + ], + "type": "boolean" + } + }, + "type": "object" +}
- Changed
update_intake1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": true, + "minProperties": 1, + "type": "object" +}
- Changed
update_item1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "discarded_submitted_value": { + "type": "boolean" + }, + "item": { + "additionalProperties": true, + "type": "object" + } + }, + "type": "object" +}
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 and action: intake creation, status, results, revisions, reminders, item management, recipients, webhooks, folders, and auth are all clearly separated. The only close neighbors—update_intake, update_item, and add_items—are disambiguated by operating on different objects and having different effects. No two tools appear to do the same thing.
All tools use lowercase snake_case verb_noun naming, with get_, list_, create_, update_, manage_, and send_ prefixes applied predictably. The pattern is consistent across intake, item, folder, recipient, webhook, and auth operations. This makes the toolset easy for an agent to guess and select from.
Fifteen tools is at the upper end of the ideal range but is justified by the server's scope: intake lifecycle, item management, revisions, reminders, recipients, webhooks, folders, and authentication each need dedicated tooling. None of the tools feel redundant, and the set is not bloated for the domain it covers.
The core intake workflow is well covered: create an intake, add and update items, monitor status, fetch results, request revisions, send manual chases, manage recipients, and receive webhook events. Minor gaps exist—there is no archive/delete tool for finished or cancelled intakes, and folder management is limited to list/create—but agents can work around these reasonably well.
Maintenance
Related MCP Connectors
- 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.
Give AI coding agents access to your Vynix visual feedback, bug reports, and AI diagnosis.
- vibsyncOAuthcom.vibsync
One shared brain for your AI coding agents: team memory, agent Q&A, tasks, and file claims.
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