Skip to main content
Glama

Server Details

Form backend an agent runs end to end: provision forms, snippets, spam, signed webhooks.

Ownership verified
Status
Healthy
Uptime
96.9% over 61 days
Last Tested
Transport
Streamable HTTP · MCP 2025-11-25
URL
Repository
useburrow/furrow-forms
GitHub Stars
1
Server Listing
furrow-forms

TDQS

A4/5.0

Scored across 29 tools

Disambiguation4/5

Each tool targets a clear resource+action pair, and descriptions explicitly cross-reference siblings ("Do not use this to list forms — call list_forms"), which removes most overlap. Minor confusion remains between update_project and upsert_project_webhook (the webhook URL can also be set via update_project), and between create_project and bootstrap_site, though the composite nature of bootstrap_site is spelled out.

Naming Consistency4/5

Names follow a predictable verb_noun snake_case pattern (list_*, get_*, create_*, update_*, archive_*, test_*), with special cases like bootstrap_site, move_client, rotate_project_inbox, and upsert_project_webhook still fitting that shape. The only drift is terminology: 'site' in bootstrap_site versus 'project' everywhere else, and 'website' used interchangeably in descriptions.

Tool Count3/5

29 tools exceeds the comfortable range and is heavy for an agent to scan, though the domain genuinely spans clients, projects, forms, submissions, teams/account, billing, webhooks, snippets, and diagnostics. Several tools could plausibly be folded (e.g., archive_* variants, upsert_project_webhook into update_project), so it sits at the over-scoped borderline rather than being unjustified.

Completeness3/5

Core lifecycle coverage is solid: full create/read/list/update/archive for clients, projects, and forms, plus submission inspection, snippets, webhook testing, and billing links. Notable gaps: no unarchive (explicitly acknowledged as a v1 omission), no submission delete/export/resend, and hard-delete absent for every resource, which limits recovery and cleanup workflows.

Available Tools

29 tools
archive_clientArchive clientA
DestructiveIdempotent
Inspect

Use to archive a client. Do not use this as a hard delete. There is no unarchive tool in v1.

NameRequiredDescriptionDefault
client_idYesClient id from list_clients or create_client.

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already indicate destructiveHint=true and idempotentHint=true, and the description adds value by clarifying that this is not a hard delete and that archiving is irreversible in v1. This contextualizes the destructive behavior beyond what the annotations alone convey. No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two short sentences with no filler. The primary action is front-loaded, and the critical caveats about hard delete and lack of unarchive follow immediately. Every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter tool with annotations covering safety and idempotency, the description covers the core action and the key irreversibility constraint. It does not detail post-archive effects or return values, but the tool is simple and has no output schema, so the information is reasonably complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, and the only parameter, client_id, already has a clear schema description including its source ('Client id from list_clients or create_client'). The tool description adds no additional 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.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: 'archive a client.' It further distinguishes the operation from a hard delete and notes that no unarchive tool exists in v1, so an agent can clearly tell what this tool does and what it does not do.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives clear usage context: use it to archive, and do not treat it as a hard delete. It also warns that there is no unarchive tool in v1, signaling irreversibility. It does not explicitly name alternative tools or conditions for choosing them, but the exclusions are clear enough.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

archive_formArchive formA
DestructiveIdempotent
Inspect

Use to archive a form so public submit returns 404. Do not use this as a hard delete.

NameRequiredDescriptionDefault
form_idYesForm id from list_forms, create_form, or bootstrap_site.

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already indicate readOnlyHint=false, idempotentHint=true, and destructiveHint=true. The description adds meaningful behavioral context by explaining the specific effect on public submissions (404) and clarifying that this is not a hard delete, which refines what 'destructive' means in this context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences with no filler. The primary purpose is front-loaded in the first sentence, and the cautionary note about hard deletion is brief and directly relevant.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-parameter tool with annotations and no output schema, the description gives the core functional behavior and a key exclusion. It does not explain side effects beyond the 404 outcome, but given the simplicity and annotations, it is sufficient for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has 100% coverage: form_id is described with a clear description and format. The description itself does not add parameter-level information, so the baseline of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('archive'), the resource ('a form'), and the concrete outcome ('public submit returns 404'). It clearly distinguishes from siblings like archive_client and archive_project by focusing on forms, and from hard delete via the explicit 'Do not use this as a hard delete' note.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly says when to use the tool ('Use to archive a form so public submit returns 404') and when not to use it ('Do not use this as a hard delete'). This gives the agent clear positive and negative usage conditions without needing to infer them.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

archive_projectArchive projectA
DestructiveIdempotent
Inspect

Use to archive a website. Archived projects reject public submit. Do not use this as a hard delete. There is no unarchive tool in v1.

NameRequiredDescriptionDefault
project_idYesProject (website) id from list_projects, create_project, or bootstrap_site.

TDQS

A4.3/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already signal destructive and non-readonly behavior, and the description adds valuable context beyond that: archiving blocks public submissions and cannot be undone in v1. This directly informs the agent about irreversible consequences, which is exactly the kind of behavioral detail needed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences with no filler: purpose, side effect, and irreversibility warning are all front-loaded. Every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter tool with a well-described schema and annotations already covering safety and idempotency, the description is complete. It tells the agent what happens, what not to use it for, and what cannot be done afterward.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema already fully documents project_id with format, pattern, and valid sources (list_projects, create_project, bootstrap_site). The description adds no parameter-specific detail, so the baseline 3 applies because the schema carries the parameter semantics.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a concrete verb and resource ('archive a website') and gives the key consequence ('Archived projects reject public submit'), so an agent can recognize it as the project-archiving action. It does not explicitly differentiate from sibling tools like archive_client or archive_form, but 'website' and 'projects' make the intended resource reasonably clear.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It provides clear when-to-use context: use it to archive a website, and it explicitly warns not to treat it as a hard delete. It also notes the critical v1 limitation that no unarchive tool exists. It stops short of naming alternative tools or specifying when to prefer update_project or archive_client, but the guidance is still actionable.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

bootstrap_siteBootstrap siteA
Idempotent
Inspect

Use this to stand up a client, one website (project), and its forms in a single call. Before calling, ask the human who should receive notification emails (notify_emails has no default; empty means nobody is emailed), thank-you page vs on-page message, and whether to add Turnstile — see furrow://docs/workflow. name/slug are the client; optional project_name/project_slug default to the same. Free accounts cannot create a fourth active project — call create_upgrade_link if bootstrap_site returns plan_limit. Operator tokens (all workspaces) must pass team_id from list_teams. Team-scoped tokens may omit team_id and cannot target another workspace. Idempotent on client, project, and form slugs. Site settings (domains, Turnstile, notify emails, webhook, email templates, uploads) live on the project. Email templates support {{project.name}}, {{project.slug}}, {{form.name}}, {{form.slug}}, {{count}} (1-based clean submissions on this form, including the current one), and any submission field such as {{name}} or {{email}}. Use a fallback with {{name | "New submission"}}. Missing fields render empty and never error. Do not pass HTML; Furrow renders one owned layout. Plans are capacity, not a feature ladder. Free: one workspace, 100 clean submissions/month pooled on the team, 3 active (non-archived) projects, 30-day submission retention, and email.footer_mode / branding.mode stay furrow (other values return plan_limit). Yearly ($199/year): extra workspaces, 10,000 submissions/month, unlimited projects, 730-day retention, branding.mode furrow | off | agency. Expansion packs are $99/year each and add 5,000 submissions/month on a yearly account. MCP, webhooks, snippets, inheritance, Turnstile, and email templates stay available on free. Spam, honeypot, and rejected posts do not count. Use create_upgrade_link to hand a human a Stripe Checkout (or Customer Portal) URL. Pass product=expansion on a yearly team to add a pack. Checkbox groups, select multiple, and other multi-value HTML fields must use a [] suffix on the name (name="services[]") or only the last checked value is stored. Put services[] on field_contract.name too. JSON/fetch should send a real array without brackets ({ "services": ["a", "b"] }) and must not use Object.fromEntries(formData) — use formData.getAll("services"). Single checkbox, select, or combobox: no brackets. File inputs use multipart POST (name="resume[]" for multiple). Enable uploads on the project first. Downloads are the project inbox at files_url, with the form slug as a folder — never put that URL in a public snippet. Do not use when you only need to patch an existing form — call update_form.

NameRequiredDescriptionDefault
nameYesClient (company) display name.
slugYesLowercase hyphenated identifier, unique per team. Used for idempotent re-runs.
emailNoNotification email template for the project: subject, from_name, intro, footer_mode, include_meta.
formsNoForms to create or update on the site, matched by slug. Re-running is safe.
team_idNoTeam id from list_teams. Required for operator tokens and OAuth logins; team-scoped frw_ tokens may omit it.
from_nameNoLegacy From display name. Prefer email.from_name.
notify_ccNoCc'd on every notification email (max 10). Copies only: an email still needs at least one notify_emails address, and an address on more than one list is sent once (To, then Cc, then Bcc). Copies get [Potential spam] emails too.
notify_bccNoBcc'd on every notification email (max 10), hidden from the other recipients — use it when an agency wants a silent copy of a client's leads. Copies only: an email still needs at least one notify_emails address, and an address on more than one list is sent once (To, then Cc, then Bcc). Copies get [Potential spam] emails too.
webhook_urlNoPublic https endpoint that receives a signed JSON POST per clean submission. Null disables it.
project_nameNoWebsite display name. Defaults to the client name.
project_slugNoWebsite slug. Defaults to the client slug.
notify_emailsNoAddresses that receive each clean submission (max 10). Inherited by every form. Required unless webhook_url is set — ask the human which inbox should get submissions.
notify_phonesNoE.164 numbers that get a short "new lead, check your email" text per clean submission (max 5). Inherited by every form. Never sent for spam. Text messages need the team's own Twilio account first (update_account sms).
honeypot_fieldNoName of the hidden honeypot input the snippet emits. Defaults to _gotcha.
webhook_secretNoHMAC-SHA256 secret for the X-Furrow-Signature header. Omit to have one generated and returned.
allowed_domainsNoHostnames allowed to post to this project's forms (example.com, www.example.com). Empty accepts any origin.
uploads_enabledNoAccept multipart file fields on this project's forms.
turnstile_secretNoCloudflare Turnstile secret used to verify tokens server-side. Null removes it.
upload_max_filesNoMax files per submission (up to 20).
rate_limit_per_ipNoMax submissions per IP within rate_limit_window_s.
require_turnstileNoReject submissions that lack a valid Turnstile token.
turnstile_site_keyNoCloudflare Turnstile site key for the frontend widget. Null removes it.
rate_limit_window_sNoRate-limit window in seconds (max 86400).
default_redirect_urlNoWhere classic HTML posts redirect after success. Null returns JSON/inline success instead.
upload_allowed_typesNoAllowed upload MIME types, e.g. application/pdf or image/*.
upload_max_file_bytesNoMax bytes per file (up to 25 MB).
upload_max_total_bytesNoMax total upload bytes per submission (up to 50 MB).

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond readOnlyHint=false and idempotentHint=true, it discloses that idempotency is keyed on client/project/form slugs, that notify_emails has no default and an empty value means nobody is emailed, that free accounts hit a hard active-project ceiling with a plan_limit error, and that missing template fields render empty rather than erroring. These are exactly the mutation-side behaviors an agent cannot infer from annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The purpose, pre-call checklist, and the update_form exclusion are correctly front-loaded, but the middle is a dense wall of pricing tiers, expansion-pack pricing, and general HTML/JS integration advice for services[] that is tangential to invoking this tool. It earns much of its length given 27 parameters, but specific dollar figures and Object.fromEntries warnings could be trimmed.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 27-parameter nested mutation tool with no output schema, it covers prerequisites, plan constraints, error routing, and the update_form alternative thoroughly. It is slightly short on what a successful call returns (ids, generated webhook_secret), though the schema notes the secret is generated and returned when omitted.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema already carries the baseline, but the description adds real meaning: name/slug are the client while project_name/project_slug default to the same values, team_id requirements differ by token type, and the [] suffix convention for multi-value fields must be mirrored on field_contract.name. Some of that guidance is scattered mid-paragraph rather than attached to clear parameter groups.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The first sentence names a specific composite verb+resource set: stand up a client, one website (project), and its forms in a single call. That immediately differentiates it from the granular siblings create_client, create_project, and create_form, which each do only one of those things.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives explicit when-not guidance ("Do not use when you only need to patch an existing form — call update_form") and routes the plan_limit failure to create_upgrade_link. It also states the pre-call questions to ask the human and the operator-token vs team-scoped-token rules for team_id.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

create_clientCreate clientA
Idempotent
Inspect

Use to add a client (company) by slug. Idempotent on slug unless strict is true. Operator tokens (all workspaces) must pass team_id from list_teams. Team-scoped tokens may omit team_id and cannot target another workspace. Prefer bootstrap_site when also creating a website and forms. Do not put Turnstile or webhooks on a client — those live on the project.

NameRequiredDescriptionDefault
nameYesClient (company) display name.
slugYesLowercase hyphenated identifier, unique per team. Used for idempotent re-runs.
strictNoWhen true, fail if the slug already exists instead of returning the existing record.
team_idNoTeam id from list_teams. Required for operator tokens and OAuth logins; team-scoped frw_ tokens may omit it.

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations supply idempotentHint=true, and the description elaborates on the strict flag and slug-based idempotency. It adds operational context about operator vs team-scoped token behavior and the client/project boundary, which goes beyond the structured hints.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Five sentences, each carrying distinct operational information with no filler. Core purpose and alternatives are front-loaded, and the caution about Turnstile/webhooks closes the definition without redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 4-parameter creation tool with no output schema, the description covers idempotency, auth requirements, cross-tool selection, and data placement. It does not describe the return value, but the rich schema and annotations already handle most invocation details.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds meaning by tying slug to idempotency ('Idempotent on slug unless strict is true'), explaining team_id comes from list_teams, and clarifying that team-scoped tokens cannot target another workspace.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with 'Use to add a client (company) by slug', naming a specific verb and resource, and immediately distinguishes this from bootstrap_site. It clearly states the tool's place relative to siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly says 'Prefer bootstrap_site when also creating a website and forms', providing a direct alternative condition. It also gives token-scoping rules for when team_id is required, and tells the agent not to put Turnstile/webhooks on a client because those belong on the project.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

create_formCreate formA
Idempotent
Inspect

Use to add a form under an existing project_id. Idempotent on slug unless strict is true. Overrides are nullable; omit them to inherit project notify emails, webhook, redirect, require_turnstile, and email templates. Pass email.subject / email.intro to override notification copy for this form only. Pass webhook_url_override to send this form to a different URL. Field contract labels (label) are used on notification emails. Checkbox groups, select multiple, and other multi-value HTML fields must use a [] suffix on the name (name="services[]") or only the last checked value is stored. Put services[] on field_contract.name too. JSON/fetch should send a real array without brackets ({ "services": ["a", "b"] }) and must not use Object.fromEntries(formData) — use formData.getAll("services"). Single checkbox, select, or combobox: no brackets. File inputs use multipart POST (name="resume[]" for multiple). Enable uploads on the project first. Downloads are the project inbox at files_url, with the form slug as a folder — never put that URL in a public snippet.

NameRequiredDescriptionDefault
nameYesForm display name, e.g. Contact or Quote request.
slugYesLowercase hyphenated identifier, unique per project. Used for idempotent re-runs.
emailNoPer-form subject and intro overrides for the notification email.
strictNoWhen true, fail if the slug already exists instead of returning the existing record.
project_idYesProject (website) id from list_projects, create_project, or bootstrap_site.
field_contractNoDeclared fields (name, type, label, report). Drives get_snippet and the email layout. Null clears it.
notify_cc_overrideNoCc for this form only. Empty array sends no Cc. Null inherits the project's notify_cc.
notify_bcc_overrideNoBcc for this form only. Empty array sends no Bcc. Null inherits the project's notify_bcc.
webhook_url_overrideNoWebhook endpoint for this form only. Null inherits the project webhook.
redirect_url_overrideNoSuccess redirect for this form only. Null inherits default_redirect_url.
notify_emails_overrideNoRecipients for this form only. Null inherits the project's notify_emails.
notify_phones_overrideNoText-alert numbers for this form only (E.164). Empty array silences texts for this form. Null inherits the project's notify_phones. Text messages need the team's own Twilio account first (update_account sms).
webhook_secret_overrideNoHMAC secret for this form's webhook. Null inherits the project secret.
require_turnstile_overrideNoForce Turnstile on or off for this form. Null inherits the project setting.

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description reinforces the idempotentHint with concrete semantics ('Idempotent on slug unless strict is true') and discloses inheritance behavior — omitting overrides inherits project notify emails, webhook, redirect, require_turnstile, and email templates. It also surfaces a real prerequisite ('Enable uploads on the project first') and a warning not to expose files_url publicly. Much of the override mechanics are also in the schema, so it isn't fully additive.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose and idempotency are correctly front-loaded, but a large block is devoted to client-side HTML/JS integration (bracket suffixes, fetch arrays, multipart, files_url) that is tangential to *creating* a form. The density is high and several sentences about the download inbox sit far from the tool's core action.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 14-parameter tool with nested objects and no output schema, the description covers the essentials: prerequisites, inheritance/null semantics, field contract conventions, and what happens on slug collision ('returning the existing record'). It stops short of clarifying the return payload shape, but the idempotency note hints at it.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema already carries per-parameter meaning, making 3 the baseline. The description adds cross-cutting semantics that a single field description cannot convey: the omit-to-inherit rule for all overrides, the [] name-suffix convention shared across field_contract and HTML, and the JSON/array guidance (formData.getAll, no Object.fromEntries).

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb + resource + scope: 'Use to add a form under an existing project_id.' That clearly distinguishes it from update_form/archive_form in the sibling set. It stops short of naming a sibling explicitly, but the create action is unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

'Use to add a form under an existing project_id' gives a clear precondition (an existing project) and the idempotency clause ('Idempotent on slug unless strict is true') tells the agent how re-runs behave. No explicit when-not guidance or named alternative for editing an existing form, so it falls short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

create_projectCreate projectA
Idempotent
Inspect

Use to add one website under an existing client_id. Idempotent on slug unless strict is true. Free accounts are limited to 3 active (non-archived) projects; yearly is unlimited. If create_project returns plan_limit, call create_upgrade_link and hand the human the Stripe URL. Prefer bootstrap_site when the client does not exist yet. Optional email sets notification templates (subject, from_name, intro, footer_mode, include_meta). Free accounts keep footer_mode furrow; other values return plan_limit. Set uploads_enabled on the project to accept files. Email templates support {{project.name}}, {{project.slug}}, {{form.name}}, {{form.slug}}, {{count}} (1-based clean submissions on this form, including the current one), and any submission field such as {{name}} or {{email}}. Use a fallback with {{name | "New submission"}}. Missing fields render empty and never error. Do not pass HTML; Furrow renders one owned layout. Plans are capacity, not a feature ladder. Free: one workspace, 100 clean submissions/month pooled on the team, 3 active (non-archived) projects, 30-day submission retention, and email.footer_mode / branding.mode stay furrow (other values return plan_limit). Yearly ($199/year): extra workspaces, 10,000 submissions/month, unlimited projects, 730-day retention, branding.mode furrow | off | agency. Expansion packs are $99/year each and add 5,000 submissions/month on a yearly account. MCP, webhooks, snippets, inheritance, Turnstile, and email templates stay available on free. Spam, honeypot, and rejected posts do not count. Use create_upgrade_link to hand a human a Stripe Checkout (or Customer Portal) URL. Pass product=expansion on a yearly team to add a pack. File inputs use multipart POST (name="resume[]" for multiple). Enable uploads on the project first. Downloads are the project inbox at files_url, with the form slug as a folder — never put that URL in a public snippet. Do not put Turnstile keys on a form.

NameRequiredDescriptionDefault
nameYesProject (website) display name.
slugYesLowercase hyphenated identifier, unique per client. Used for idempotent re-runs.
emailNoNotification email template for the project: subject, from_name, intro, footer_mode, include_meta.
strictNoWhen true, fail if the slug already exists instead of returning the existing record.
client_idYesClient id from list_clients or create_client.
from_nameNoLegacy From display name. Prefer email.from_name.
notify_ccNoCc'd on every notification email (max 10). Copies only: an email still needs at least one notify_emails address, and an address on more than one list is sent once (To, then Cc, then Bcc). Copies get [Potential spam] emails too.
notify_bccNoBcc'd on every notification email (max 10), hidden from the other recipients — use it when an agency wants a silent copy of a client's leads. Copies only: an email still needs at least one notify_emails address, and an address on more than one list is sent once (To, then Cc, then Bcc). Copies get [Potential spam] emails too.
webhook_urlNoPublic https endpoint that receives a signed JSON POST per clean submission. Null disables it.
notify_emailsNoAddresses that receive each clean submission (max 10). Inherited by every form. Required unless webhook_url is set — ask the human which inbox should get submissions.
notify_phonesNoE.164 numbers that get a short "new lead, check your email" text per clean submission (max 5). Inherited by every form. Never sent for spam. Text messages need the team's own Twilio account first (update_account sms).
honeypot_fieldNoName of the hidden honeypot input the snippet emits. Defaults to _gotcha.
webhook_secretNoHMAC-SHA256 secret for the X-Furrow-Signature header. Omit to have one generated and returned.
allowed_domainsNoHostnames allowed to post to this project's forms (example.com, www.example.com). Empty accepts any origin.
uploads_enabledNoAccept multipart file fields on this project's forms.
turnstile_secretNoCloudflare Turnstile secret used to verify tokens server-side. Null removes it.
upload_max_filesNoMax files per submission (up to 20).
rate_limit_per_ipNoMax submissions per IP within rate_limit_window_s.
require_turnstileNoReject submissions that lack a valid Turnstile token.
turnstile_site_keyNoCloudflare Turnstile site key for the frontend widget. Null removes it.
rate_limit_window_sNoRate-limit window in seconds (max 86400).
default_redirect_urlNoWhere classic HTML posts redirect after success. Null returns JSON/inline success instead.
upload_allowed_typesNoAllowed upload MIME types, e.g. application/pdf or image/*.
upload_max_file_bytesNoMax bytes per file (up to 25 MB).
upload_max_total_bytesNoMax total upload bytes per submission (up to 50 MB).

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the idempotent annotation, the description explains idempotency on slug unless strict is true, free-account project limits, plan_limit behavior, footer_mode constraints on free accounts, and that uploads must be enabled before file inputs work. It also discloses template rendering behavior and warns not to put the project inbox URL in a public snippet.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness2/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The purpose and idempotency behavior are front-loaded, but the description then expands into pricing tiers, expansion packs, monthly submission capacity, retention periods, and MCP/webhook availability. Much of that is product documentation rather than guidance needed to create a project, so the definition is substantially bloated.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 25-parameter tool with a nested email object and no output schema, the description covers the important invocation context: client existence, idempotency, plan-limit handling, email token behavior, and upload prerequisites. It does not describe the normal return shape beyond special cases like plan_limit and generated webhook_secret, which is the main remaining gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds meaningful semantics for the optional email object, token interpolation and fallback syntax, footer_mode limitations, and enabling uploads. It still leaves many of the 25 parameters to the schema, which is reasonable given the full schema descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence states a specific verb and resource: add one website under an existing client_id. It also distinguishes itself from bootstrap_site for the case where the client does not yet exist, so an agent can tell the two apart without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly says when to prefer bootstrap_site and what to do when create_project returns plan_limit by routing to create_upgrade_link. These are concrete alternatives and error-routing instructions, not merely implied usage.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_accountGet accountA
Read-onlyIdempotent
Inspect

Use to read the team's plan, usage this month, limits, expansion_packs, retention_days, branding, and AI spam filter (ai.typesafe_key_set, ai.notify_suspected_spam), and text alerts (sms.configured, provider, account_sid, from). The TypeSafe key and Twilio Auth Token are never returned. Plans are capacity, not a feature ladder. Free: one workspace, 100 clean submissions/month pooled on the team, 3 active (non-archived) projects, 30-day submission retention, and email.footer_mode / branding.mode stay furrow (other values return plan_limit). Yearly ($199/year): extra workspaces, 10,000 submissions/month, unlimited projects, 730-day retention, branding.mode furrow | off | agency. Expansion packs are $99/year each and add 5,000 submissions/month on a yearly account. MCP, webhooks, snippets, inheritance, Turnstile, and email templates stay available on free. Spam, honeypot, and rejected posts do not count. Use create_upgrade_link to hand a human a Stripe Checkout (or Customer Portal) URL. Pass product=expansion on a yearly team to add a pack. Operator tokens (all workspaces) must pass team_id from list_teams. Team-scoped tokens may omit team_id and cannot target another workspace. Do not use this to list projects — call list_projects.

NameRequiredDescriptionDefault
team_idNoTeam id from list_teams. Required for operator tokens and OAuth logins; team-scoped frw_ tokens may omit it.

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already establish readOnly and idempotent, so the description wisely focuses on what annotations cannot express: secrets are never returned (TypeSafe key, Twilio Auth Token), plan-capped fields return plan_limit, and error/limit behavior per plan tier. That is genuine added context; the remaining plan pricing detail is domain lore rather than behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded correctly with purpose and exclusions, but the middle is a wall of plan-tier minutiae ($199/year, $99/year packs, 730-day retention, branding mode values) that an agent only needs occasionally. Several sentences could be condensed without losing call-correctness.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, and the description compensates by enumerating return fields down to nested paths (ai.typesafe_key_set, sms.configured) plus authentication constraints. What's missing is a sense of response shape/structure and whether usage figures are cumulative or period-bounded.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Only one parameter and the schema already covers it at 100%, so the baseline is 3. The description earns above-baseline by explaining the conditional requirement of team_id — mandatory for operator tokens/OAuth, optional for team-scoped frw_ tokens, and never usable to target another workspace.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb and resource ('Use to read the team's plan, usage this month, limits...') and enumerates the exact fields returned, so an agent knows precisely what it gets. It distinguishes itself from the closest lookalike by name ('Do not use this to list projects — call list_projects'), though the dense mid-sentence field paths slightly blur the headline purpose.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly routes two adjacent tasks away: create_upgrade_link for Stripe Checkout/Customer Portal, and list_projects for project listings. It also gives token-scoping rules (operator tokens must pass team_id from list_teams; team-scoped tokens may omit it). It stops short of stating when an agent should prefer this over, say, get_client or get_form, but those are clearly different resources.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_clientGet clientA
Read-onlyIdempotent
Inspect

Use to inspect one client. Do not use to list that client's sites — call list_projects with client_id.

NameRequiredDescriptionDefault
client_idYesClient id from list_clients or create_client.

TDQS

A4.3/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint and idempotentHint, covering the safety profile. The description adds only the singular-object scope and the exclusion about listing sites; it does not describe return shape or error behavior, but with annotations present this is acceptable.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences with no filler. The core action is front-loaded, and the important exclusion/alternative is immediately stated.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool is simple: one required parameter, fully documented by the schema, with read-only and idempotent annotations. The description supplies essential cross-tool guidance. Nothing needed to call this tool correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has 100% coverage for the single parameter, including UUID format and provenance ('Client id from list_clients or create_client.'). The description only references client_id in the routing note and adds no meaning beyond the schema, so baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: 'inspect one client.' It clearly distinguishes itself from list_projects by saying it should not be used to list a client's sites, and the singular 'one client' implies contrast with list_clients.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly says when to use the tool ('Use to inspect one client') and provides a direct exclusion with the alternative tool and parameter: 'Do not use to list that client's sites — call list_projects with client_id.' This gives clear routing guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_formGet formA
Read-onlyIdempotent
Inspect

Use to read a form plus resolved and inherited settings, including resolved.email and resolved.uploads. files_url is the private inbox folder for this form under the project inbox (do not put it in a public snippet). Turnstile always inherits from the project (inherited.turnstile is true). Form webhook_url_override is nullable; null means inherit the project webhook. Forms may override email.subject and email.intro only. Do not use to generate embed code — call get_snippet.

NameRequiredDescriptionDefault
form_idYesForm id from list_forms, create_form, or bootstrap_site.

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Even though annotations already indicate readOnly and idempotent, the description adds important non-obvious behavior not derivable from annotations or schema: files_url is private and should not appear in public snippets, turnstile always inherits from the project, webhook_url_override is nullable with inheritance semantics, and only email.subject and email.intro can be overridden. This is rich, useful behavioral disclosure with no contradiction.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core purpose and then packs high-value details into a compact paragraph. Every sentence contributes distinct information: scope, privacy-sensitive fields, inheritance behavior, nullable webhook semantics, and the alternative tool. There is no filler or redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the simple single-parameter schema, no output schema, and read/idempotent annotations, the description compensates well by clarifying key response semantics (resolved vs inherited settings, turnstile inheritance, nullable webhook override) and by explicitly preventing misuse through get_snippet. An agent has enough context to invoke it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The only parameter, form_id, is already fully documented in the schema (100% coverage) with a clear source hint ('Form id from list_forms, create_form, or bootstrap_site'). The description does not need to add parameter syntax and does not repeat it, which is appropriate; the baseline of 3 applies because the schema carries the burden.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific operation: 'read a form plus resolved and inherited settings,' and then clarifies its boundaries by saying 'Do not use to generate embed code — call get_snippet.' This distinguishes the tool from at least one sibling (get_snippet) and conveys exactly what resource and behavior is involved.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly frames when to use the tool ('Use to read a form...') and when not to use it ('Do not use to generate embed code — call get_snippet'). It also names the alternative tool directly, giving the agent a clear routing decision without needing to inspect other definitions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_projectGet projectA
Read-onlyIdempotent
Inspect

Use to inspect one website's settings, domains, webhook URL, email templates, upload policy, plan, usage this month, limits, retention_days, and branding. Returns stored values plus resolved.email. Secrets are never returned. Plans are capacity, not a feature ladder. Free: one workspace, 100 clean submissions/month pooled on the team, 3 active (non-archived) projects, 30-day submission retention, and email.footer_mode / branding.mode stay furrow (other values return plan_limit). Yearly ($199/year): extra workspaces, 10,000 submissions/month, unlimited projects, 730-day retention, branding.mode furrow | off | agency. Expansion packs are $99/year each and add 5,000 submissions/month on a yearly account. MCP, webhooks, snippets, inheritance, Turnstile, and email templates stay available on free. Spam, honeypot, and rejected posts do not count. Use create_upgrade_link to hand a human a Stripe Checkout (or Customer Portal) URL. Pass product=expansion on a yearly team to add a pack. Do not use to list forms — call list_forms.

NameRequiredDescriptionDefault
project_idYesProject (website) id from list_projects, create_project, or bootstrap_site.

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint and idempotentHint, so safety is covered; the description adds real value beyond that by stating that stored values are returned alongside resolved.email, that secrets are never returned, and that plan values are capacity rather than a feature ladder. It omits anything about response shape or error behavior, but the security and semantic caveats are meaningful additions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The core purpose and the sibling exclusion are correctly front-loaded, but roughly half the text is a pricing tier catalogue ($199/year, $99 expansion packs, free-tier quotas) that belongs to the billing/upgrade tools rather than to a read tool. The retention and plan_limit details justify some of it, yet the volume dilutes the actionable content.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter read tool with no output schema, the description covers what is returned, what is deliberately withheld (secrets), and why plan fields return plan_limit values. Only the lack of guidance on error cases or ordering keeps it from being fully complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With a single parameter at 100% schema description coverage, the schema already explains that project_id comes from list_projects, create_project, or bootstrap_site. The description adds nothing about format or edge cases for project_id, so the baseline applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb and resource ('inspect one website's settings') and enumerates the exact fields returned (domains, webhook URL, email templates, plan, usage, retention_days, branding). It also explicitly distinguishes itself from a sibling by ruling out list_forms, so an agent can route without reading either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives a clear when-to-use (inspect a single site's config) plus an explicit when-not/alternative ('Do not use to list forms — call list_forms'), and redirects upgrade flows to create_upgrade_link. It does not distinguish from get_account or get_client, which the field list only implies, so it falls short of full alternative coverage.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_snippetGet form snippetA
Read-onlyIdempotent
Inspect

Use after the form exists to get copy-paste html, astro, or next code that posts to /s/:publicKey. Do not invent a submit URL — this tool returns it. Snippets are single-value inputs; write checkbox groups and multi-selects yourself. Checkbox groups, select multiple, and other multi-value HTML fields must use a [] suffix on the name (name="services[]") or only the last checked value is stored. Put services[] on field_contract.name too. JSON/fetch should send a real array without brackets ({ "services": ["a", "b"] }) and must not use Object.fromEntries(formData) — use formData.getAll("services"). Single checkbox, select, or combobox: no brackets. File inputs use multipart POST (name="resume[]" for multiple). Enable uploads on the project first. Downloads are the project inbox at files_url, with the form slug as a folder — never put that URL in a public snippet. After dropping the snippet, add visitor-facing success UI: a thank-you page plus default_redirect_url for classic HTML POST, or an on-page message when using fetch/{ok:true}. Do not leave visitors looking at JSON. See furrow://docs/submit-success. Hardcode submit_url as returned; the public key is not a secret. Do not move it into .env / NEXT_PUBLIC_* / PUBLIC_* or ask the human to set environment variables. See furrow://docs/testing.

NameRequiredDescriptionDefault
form_idYesForm id from list_forms, create_form, or bootstrap_site.
frameworkYesSnippet flavor: html (plain form), astro (component), or next (React client component).

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

readOnlyHint=true and idempotentHint=true are consistent with a code-generation read. Beyond annotations it discloses real behavior: the returned submit_url must be hardcoded (not moved into .env/NEXT_PUBLIC_*), the public key is not a secret, files_url points at the project inbox and must never appear in a public snippet, and file inputs require multipart with uploads enabled. That is substantial context the annotations cannot convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness2/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with purpose and the 'do not invent a submit URL' constraint, but the single dense paragraph packs in post-invocation integration rules (bracket suffixes, fetch arrays, multipart, inbox URLs, success UI, env-var policy) and repeats the [] suffix rule twice. For a two-parameter read tool this is over-stuffed and would benefit from splitting invocation-critical guidance from downstream implementation notes.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description carries the burden of describing what comes back (html/astro/next snippet, submit_url, files_url) and the constraints on using it safely. Nothing an agent needs to call this correctly, or to avoid the common failure of inventing a submit URL, appears to be missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the enum for framework is already documented in-schema as html/astro/next flavors. The description adds only a light constraint that form_id must reference an existing form ('after the form exists'); the long multi-value/file-input text describes the returned snippet, not the two input parameters. Baseline 3 is appropriate when the schema does the lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource: returns copy-paste html/astro/next code that posts to /s/:publicKey. That output is unlike anything any sibling produces (all siblings are CRUD on clients/forms/projects/submissions), so an agent can route to it without opening the schema. The first sentence also names the precondition 'after the form exists', which further pins what it does.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit about when to use it ('Use after the form exists'), when to hand-write instead ('Snippets are single-value inputs; write checkbox groups and multi-selects yourself'), and prerequisites ('Enable uploads on the project first'). It also covers what to do after receiving the snippet (add success UI, don't leave visitors looking at JSON) and routes to furrow://docs/submit-success and furrow://docs/testing for depth.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_submissionGet submissionA
Read-onlyIdempotent
Inspect

Use to read one stored submission by id, including file metadata and files_url (the form inbox). Do not use this for live submit tests — POST to /s/:publicKey instead. Do not expect signed bucket URLs — download from the inbox.

NameRequiredDescriptionDefault
submission_idYesSubmission id from list_submissions or a webhook payload.

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With readOnlyHint and idempotentHint already present, the description adds valuable behavioral context beyond annotations: it clarifies the response includes files_url and that URLs should not be expected to be signed bucket URLs. This prevents a common failure mode.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences deliver purpose, exclusions, and a critical caveat without any filler. The main intent is front-loaded and each sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter read operation with safety annotations, the description provides enough context to invoke the tool correctly and understand the return characteristics. No output schema is needed because the description states the important return components.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema fully documents submission_id, including format, pattern, and source. The description does not need to add much parameter detail, so the baseline of 3 applies given 100% schema coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool reads one stored submission by id and specifies what it includes (file metadata and files_url). This distinguishes it from list_submissions and from the live submit endpoint.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly says not to use this for live submit tests and directs the agent to POST to /s/:publicKey instead. It also provides guidance on how to handle returned URLs by downloading from the inbox.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_clientsList clientsA
Read-onlyIdempotent
Inspect

Use to browse clients (companies). Operator tokens (all workspaces) must pass team_id from list_teams. Team-scoped tokens may omit team_id and cannot target another workspace. Do not use to list websites — call list_projects with client_id. Archived clients are hidden unless status=all or archived.

NameRequiredDescriptionDefault
limitNoPage size, max 100.
cursorNoOpaque cursor from the previous page's next_cursor.
statusNoFilter by archive state. Defaults to active.
team_idNoTeam id from list_teams. Required for operator tokens and OAuth logins; team-scoped frw_ tokens may omit it.

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare the tool read-only and idempotent, so the safety profile is covered. The description adds meaningful behavioral detail beyond the schema: operator tokens must pass team_id, team-scoped tokens cannot target another workspace, and archived clients are hidden unless status is set to 'all' or 'archived'. This adds useful context without contradicting the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is three sentences, all information-dense, with the core purpose front-loaded and the exclusionary guidance placed efficiently. There is no filler or repeated schema content; every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool has no output schema and four parameters, the description covers the essential operational context: what the resource is, which sibling to use instead, token-based team_id requirements, and archive filtering behavior. This is complete enough for an agent to call the tool correctly in most situations.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

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. The description goes further by clarifying the nuanced semantics of team_id for different token types and explaining the practical consequence of the status default. This adds value beyond what the parameter descriptions already state.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb-resource pair ('browse clients') and explicitly identifies clients as companies, removing ambiguity about the resource type. It also names a sibling tool (list_projects) and differentiates this tool by saying it is not for listing websites, which makes the purpose unmistakable.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit when-to-use guidance and a clear exclusion: 'Do not use to list websites — call list_projects with client_id.' It also provides contextual usage rules for token scoping, explaining when team_id is required and when it may be omit, plus the archive-filter behavior. This is strong routing guidance with no reliance on inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_formsList formsA
Read-onlyIdempotent
Inspect

Use to list forms. Pass project_id for one website or client_id for one company (team is inferred). Otherwise Operator tokens (all workspaces) must pass team_id from list_teams. Team-scoped tokens may omit team_id and cannot target another workspace. Do not use to read resolved inheritance — call get_form.

NameRequiredDescriptionDefault
limitNoPage size, max 100.
cursorNoOpaque cursor from the previous page's next_cursor.
statusNoFilter by archive state. Defaults to active.
team_idNoTeam id from list_teams. Required for operator tokens and OAuth logins; team-scoped frw_ tokens may omit it.
client_idNoOnly forms under this client's projects.
project_idNoOnly forms in this project.

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already mark the tool as read-only and idempotent, so the bar is lower. The description adds valuable behavior beyond annotations: token-type requirements, workspace targeting restrictions, and team inference when client_id is passed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is four sentences with no waste: it front-loads the action, explains parameter selection, covers token behavior, and routes to the correct alternative. Every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a list tool with no output schema, the description covers all calling contexts, auth modes, and the key sibling distinction. It does not describe the response shape, but pagination parameters are already documented in the schema and the main invocation guidance is complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3. The description adds meaningful selection logic beyond the schema by tying project_id, client_id, and team_id to specific auth contexts and scoping rules.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states an explicit verb and resource: 'Use to list forms.' It also distinguishes itself from the sibling get_form with 'Do not use to read resolved inheritance — call get_form,' making the scope clear.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit when-to-use guidance by parameter: pass project_id for one website, client_id for one company, or team_id for operator tokens. It also states exclusions and names the alternative, get_form, for inheritance reads.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_projectsList projectsA
Read-onlyIdempotent
Inspect

Use to browse websites. Pass client_id to list one client's sites (team is inferred). Without client_id, Operator tokens (all workspaces) must pass team_id from list_teams. Team-scoped tokens may omit team_id and cannot target another workspace. Do not use to read one project's settings — call get_project. Archived projects are hidden unless status=all or archived.

NameRequiredDescriptionDefault
limitNoPage size, max 100.
cursorNoOpaque cursor from the previous page's next_cursor.
statusNoFilter by archive state. Defaults to active.
team_idNoTeam id from list_teams. Required for operator tokens and OAuth logins; team-scoped frw_ tokens may omit it.
client_idNoOnly projects under this client.

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already mark the tool as read-only and idempotent. The description adds useful behavior beyond those annotations: archived projects are hidden unless status=all or archived, team inference happens when client_id is passed, and team-scoped tokens cannot target another workspace.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four compact sentences carry purpose, exclusions, token rules, and archive filtering without wasted words. The primary use is front-loaded and every sentence adds a distinct operational rule.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a five-parameter list tool with no output schema, the description is complete enough: it covers purpose, parameter selection, token scoping, archive default, and the alternative tool. Pagination mechanics are already covered by the cursor and limit schema descriptions.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

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 meaning by explaining the relationship between client_id and team_id, the token-type requirements, and the status default behavior. This goes beyond the schema's field-level descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool lists projects/sites for browsing and explicitly contrasts itself with get_project for reading a single project's settings. This distinguishes it from a sibling tool without requiring the agent to open schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives concrete guidance: pass client_id for one client's sites, use team_id from list_teams for operator tokens, and let team-scoped tokens omit team_id. It also states an explicit when-not: 'Do not use to read one project's settings — call get_project.'

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_submissionsList submissionsA
Read-onlyIdempotent
Inspect

Use to inspect recent posts. Available on free and yearly. Pass project_id or form_id to infer the team; omit both and Operator tokens (all workspaces) must pass team_id from list_teams. Team-scoped tokens may omit team_id and cannot target another workspace. Free teams prune submissions after 30 days; yearly after 730 days. Do not use to read a single submission — call get_submission.

NameRequiredDescriptionDefault
spamNoWhich submissions to return. Defaults to clean.clean
limitNoPage size, max 100.
sinceNoISO 8601 timestamp; only submissions created after this instant.
cursorNoOpaque cursor from the previous page's next_cursor.
form_idNoOnly submissions to this form.
team_idNoTeam id from list_teams. Required for operator tokens and OAuth logins; team-scoped frw_ tokens may omit it.
project_idNoOnly submissions in this project.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the readOnly/idempotent annotations, it discloses important behavior: data retention/pruning periods (30 days free, 730 days yearly), team inference from project_id/form_id, and workspace-scoping restrictions for team-scoped tokens. No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact, front-loaded with the core purpose, and every sentence contributes decision-relevant information: plan availability, token scoping, retention, and the single-submission alternative. No filler or redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

It covers purpose, auth/scoping, retention, and alternative routing, which is strong for a list tool with no output schema. The only gap is that it does not describe the return shape, default ordering, or default page size beyond what the schema hints through cursor and limit.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Even though schema coverage is 100%, the description adds cross-parameter meaning: project_id or form_id can infer the team, and team_id from list_teams is only required for Operator tokens/OAuth when no scoping parameter is provided. This is meaningful semantic context beyond the schema field descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with 'Use to inspect recent posts,' a clear verb+resource statement, reinforced by the name and title 'List submissions.' It explicitly distinguishes itself from get_submission with 'Do not use to read a single submission — call get_submission.'

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly states when not to use the tool and names the alternative: get_submission. It also gives actionable token-scoping guidance, telling Operator tokens when team_id is required and team-scoped tokens when it may be omitted.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_teamsList teamsA
Read-onlyIdempotent
Inspect

Use to see workspaces this token can act on. A team-scoped frw_ token returns that team only. An operator token or an MCP OAuth login returns every team you belong to — pass team_id on list_clients, create_client, and bootstrap_site. Creating another workspace is dashboard-only after signing in and requires a yearly plan; free accounts stay on one team. Do not use this to list clients — call list_clients.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already indicate readOnlyHint and idempotentHint, so the safety profile is covered. The description adds useful behavioral context: team-scoped tokens get one team, while operator/OAuth tokens get all belonging teams, and it mentions the one-team limitation for free accounts. It does not describe response shape, but that is not required for a read-only list tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core purpose and remains compact. The sentence about creating another workspace is slightly tangential to listing teams, but it provides relevant context about why the returned team set may be limited. Overall, no word is wasted.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter, read-only, idempotent tool, the description is complete: it explains token-based visibility, points to the correct sibling for clients, and even notes team_id usage for downstream calls. There is no output schema, so lack of return-value detail is not a gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters and an empty input schema, so schema coverage is trivially 100%. No parameter documentation is needed; the description correctly focuses on behavior and output semantics instead.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific action ('see workspaces this token can act on') and resource ('teams'), and explicitly differentiates from list_clients in the final sentence. It clarifies token-dependent scope, leaving no ambiguity about what the tool returns.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit when-to-use guidance based on token type, and directly names list_clients as the alternative for listing clients. It also notes workspace creation is dashboard-only, which helps an agent avoid attempting an unsupported action.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

move_clientMove client to another teamAInspect

Use when the human asks to move a client (company) and all its projects, forms, submissions, and files into another workspace they own. Public keys, snippets, inbox links, and webhook secrets stay the same, so the live site needs no change. Needs an owner of both teams (dashboard session or operator token; team-scoped tokens cannot). Refused when the destination has a client with the same slug (pass slug), when a free destination would pass 3 projects, or when the client has text alert numbers and the destination has no Twilio. Confirm the destination team name with the human first. Do not use this to rename or archive.

NameRequiredDescriptionDefault
slugNoNew client slug, only when the destination team already has a client with this one.
team_idYesDestination team id from list_teams. You must own it and the client's current team.
client_idYesClient id from list_clients or create_client.

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well past the annotations: it discloses the authorization requirement (owner of both teams; team-scoped tokens cannot), what is preserved (public keys, snippets, inbox links, webhook secrets), and three concrete refusal conditions (slug collision, free-plan project cap, Twilio requirement for text alert numbers). The destructiveHint=false annotation is consistent with a move that preserves all identifiers, and the description supplies the human-confirmation step the annotation layer cannot express.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the triggering condition, then invariants, auth, and refusals in a compact sequence with zero filler sentences. Slightly dense in the refusal clause ('when a free destination would pass 3 projects'), which reads tersely and could be clearer, but nothing is wasted.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, and the description never says what a successful move returns or whether it is synchronous, which is the main remaining gap for an operation touching many resources. Auth, side effects, failure modes, and confirmation requirements are otherwise fully covered for a tool of this complexity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and both the 'pass slug' hint and the 'you must own it' ownership note already appear in the schema property descriptions, so the description adds little parameter-level meaning. Baseline 3 is appropriate since the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('move a client (company) and all its projects, forms, submissions, and files') and names the destination scope ('another workspace they own'). The final sentence explicitly disambiguates from the sibling operations archive_client and update_client ('Do not use this to rename or archive').

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives a triggering condition ('Use when the human asks to move a client'), an explicit exclusion ('Do not use this to rename or archive'), and a procedural precondition ('Confirm the destination team name with the human first'). An agent knows when to select this tool and when to route elsewhere without reading any other definition.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

rotate_project_inboxRotate project inbox linkA
Destructive
Inspect

Use when a project's private inbox URL may have leaked. Issues a new inbox key; every previously shared inbox link stops working immediately. get_project returns the new files_url.

NameRequiredDescriptionDefault
project_idYesProject (website) id from list_projects, create_project, or bootstrap_site.

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description clearly discloses the destructive consequence—every previously shared inbox link stops working immediately—and provides recovery guidance by noting that get_project returns the new files_url. This goes beyond the destructiveHint annotation by explaining exactly what is destroyed and how to obtain the replacement.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and front-loaded with the triggering condition, followed by the key behavioral fact and the recovery path. Every sentence contributes essential information with no redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-parameter tool with a well-documented schema, clear annotations, and a description that covers the purpose, behavior, and post-condition, the context is complete. An agent has everything it needs to invoke the tool correctly and understand the impact.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema already fully documents project_id with a description and format restriction, so the description does not need to add parameter-level detail. The schema carries the semantic load here, and the description adds no parameter information, matching the baseline for complete schema coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description specifies a concrete action—rotating a project's private inbox URL—and clearly distinguishes it from generic updates by explaining the exact effect: issuing a new inbox key and invalidating previously shared links. This makes the tool's purpose immediately identifiable and distinct from siblings like update_project.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

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: when a project's private inbox URL may have leaked. It does not name alternative tools or explicitly state when not to use it, but the use case is clear and specific enough for an agent to select it appropriately.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

test_smsTest SMSAInspect

Use after setting notify_phones to send one test text to each of the project's numbers through the team's Twilio account. Returns per-number results with Twilio's error when one fails. Fails if the team has no Twilio account (update_account sms) or the project has no notify_phones. A successful result means Twilio accepted it; ask the human to confirm it arrived, since unregistered US senders are filtered by carriers after Twilio accepts. Error codes and fixes are in furrow://docs/text-alerts. Do not use this to test email or the webhook — call test_webhook for the webhook.

NameRequiredDescriptionDefault
project_idYesProject (website) id from list_projects, create_project, or bootstrap_site.

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only say readOnlyHint=false and idempotentHint=false; the description adds the important non-obvious behavior: it sends one real text per number, returns per-number Twilio errors, and 'success' only means Twilio accepted, not delivered, due to carrier filtering of unregistered US senders. It also points to a doc resource for error codes, which annotations cannot convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the action and prerequisite, and every sentence contributes distinct information (preconditions, return shape, success semantics, doc pointer, sibling exclusion). It is slightly dense at five sentences, but nothing is filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, yet the description describes the return value (per-number results with Twilio errors) and the success caveat. With one 100%-covered parameter and explicit preconditions, an agent has everything needed to call it and interpret the result.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the single project_id parameter is already fully documented with a format and source tools (list_projects, create_project, bootstrap_site). The description only indirectly references the parameter via 'the project's numbers' and adds no new syntax or sourcing guidance, so the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific verb (send one test text), the exact resource (each of the project's numbers via the team's Twilio account), and explicitly distinguishes itself from the sibling test_webhook. An agent can tell what it does without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives an explicit precondition ('Use after setting notify_phones'), failure conditions (no Twilio account, no notify_phones), and a named alternative with the exclusion ('Do not use this to test email or the webhook — call test_webhook'). This is the when/when-not/alternatives pattern at full strength.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

test_webhookTest webhookAInspect

Use to POST a signed webhook.test event to the project's webhook URL. Available on free and yearly. Do not use this to inspect past deliveries — that is REST GET /api/projects/:id/webhook-deliveries. Fails if no webhook URL and secret are set.

NameRequiredDescriptionDefault
project_idYesProject (website) id from list_projects, create_project, or bootstrap_site.

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only say the operation is not read-only and not idempotent. The description adds meaningful behavior beyond that: the event is signed, the tool fails if no webhook URL and secret are configured, and it is available on free and yearly plans. This gives useful operational context without needing to list every side effect.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four short sentences, each carrying distinct information: the action, plan availability, an exclusion, and a failure condition. The most important action is front-loaded and there is no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple single-parameter tool, the description covers the action, the precondition for failure, and an alternative. It does not describe the success response, but the absence of an output schema and the simplicity of the operation keep this from being a significant gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There is only one parameter, project_id, and the schema already fully documents it with a clear description referencing where the id comes from. The tool description does not need to add parameter detail, so the high schema coverage sets a baseline of 3.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb and resource: 'POST a signed webhook.test event to the project's webhook URL.' It also clearly distinguishes itself from webhook-delivery inspection, so an agent can tell this tool apart from related operations.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

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 ('Use to POST...') and when not to use it ('Do not use this to inspect past deliveries — that is REST GET...'). It also notes the failure precondition, which helps the agent decide if 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.

update_accountUpdate accountA
Idempotent
Inspect

Use to rename the workspace, set branding, configure the AI spam filter, or connect the team's own Twilio account for new-lead text alerts. Pass sms { account_sid, auth_token, from } to save Twilio (auth_token is write-only; omit it later to keep the saved one; sms: null disconnects). Numbers to text are notify_phones on the project. US senders must finish A2P 10DLC or toll-free verification in Twilio or carriers drop the texts. To set it up with a human who has never used Twilio, follow furrow://docs/text-alerts. Pass ai.typesafe_api_key to save this team's TypeSafe key (null clears it; the key is write-only). ai.notify_suspected_spam (default true) emails notify addresses with a Potential spam label for every spam catch — rule-based or Jev — except exact duplicates; set it false to silence both. Spam never sends a webhook. Free accounts keep branding.mode and email.footer_mode at furrow; asking for anything else returns plan_limit — tell the human the custom footer is a Yearly feature and offer create_upgrade_link. Yearly may set branding.mode to furrow, off, or agency (name + optional link + optional logo; Furrow keeps the layout, no raw HTML). Plans are capacity, not a feature ladder. Free: one workspace, 100 clean submissions/month pooled on the team, 3 active (non-archived) projects, 30-day submission retention, and email.footer_mode / branding.mode stay furrow (other values return plan_limit). Yearly ($199/year): extra workspaces, 10,000 submissions/month, unlimited projects, 730-day retention, branding.mode furrow | off | agency. Expansion packs are $99/year each and add 5,000 submissions/month on a yearly account. MCP, webhooks, snippets, inheritance, Turnstile, and email templates stay available on free. Spam, honeypot, and rejected posts do not count. Use create_upgrade_link to hand a human a Stripe Checkout (or Customer Portal) URL. Pass product=expansion on a yearly team to add a pack. Operator tokens (all workspaces) must pass team_id from list_teams. Team-scoped tokens may omit team_id and cannot target another workspace. Do not use this to change the plan — call create_upgrade_link.

NameRequiredDescriptionDefault
aiNoAI spam filter. Off until this team saves its own TypeSafe key. One Jev call per heuristics-clean submission.
smsNoThe team's own Twilio account for new-lead text alerts. Null removes it. Numbers to text live on the project (notify_phones).
nameNoNew workspace display name.
team_idNoTeam id from list_teams. Required for operator tokens and OAuth logins; team-scoped frw_ tokens may omit it.
brandingNoAccount-wide branding for notification emails and hosted pages.

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond readOnlyHint=false/idempotentHint=true: discloses that free plans return plan_limit for non-furrow branding, that spam never fires a webhook, that secrets are write-only, and that sms: null disconnects. Loses a point because the plan/capacity paragraph is padded and partially restates the branding constraint already stated earlier.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with concrete actions, which is good, but the plan-limit rule appears twice (once as 'Free accounts keep branding.mode and email.footer_mode at furrow' and again in the Free/Yearly tier breakdown), and the pricing paragraph is longer than the calling agent needs.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 5-parameter mutation tool with nested objects and no output schema, the description covers error paths (plan_limit), permission/token scoping, null semantics for every nested field, and where the plan lives instead. 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.

Parameters4/5

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 still adds operational meaning — omit auth_token later to keep the saved one, null clears the TypeSafe key, notify_suspected_spam defaults true and affects both email and webhooks, and notify_phones live on the project not here.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb and enumerates the exact resources it changes: workspace name, branding, AI spam filter, Twilio SMS. An agent can distinguish it from update_team and update_project without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly routes away from itself: 'Do not use this to change the plan — call create_upgrade_link,' and names list_teams as the source of team_id for operator tokens. It also names the escalation path (furrow://docs/text-alerts, create_upgrade_link) for the human-facing cases.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

update_clientUpdate clientA
Idempotent
Inspect

Use to rename a client. Do not use this to change domains, Turnstile, or webhooks — call update_project.

NameRequiredDescriptionDefault
nameNoNew client display name.
client_idYesClient id from list_clients or create_client.

TDQS

A4.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=false and idempotentHint=true, indicating a mutating but idempotent operation. The description adds scope context by narrowing the tool to rename-only, but does not disclose additional behavioral details such as side effects or response behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences deliver the core purpose and the key exclusion with no filler. The primary function is front-loaded, and the alternative tool is mentioned immediately.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple rename operation with full schema coverage and helpful annotations, the description is complete enough for correct invocation. It does not describe return values, but no output schema exists and the operation is straightforward.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents both client_id and name. The description's 'rename' wording loosely maps to the name parameter but adds no new parameter-level meaning beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('rename a client') and distinguishes itself from update_project by listing what it is not for. An agent can tell exactly what update_client does without needing to inspect the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says when not to use the tool (for domains, Turnstile, or webhooks) and names the alternative tool, update_project. This gives the agent clear routing guidance and prevents misuse.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

update_formUpdate formA
Idempotent
Inspect

Use to patch form overrides. Pass null on an override to inherit again. Set email.subject or email.intro to override project notification copy for this form only; from_name, footer_mode, and include_meta stay on the project. Email templates support {{project.name}}, {{project.slug}}, {{form.name}}, {{form.slug}}, {{count}} (1-based clean submissions on this form, including the current one), and any submission field such as {{name}} or {{email}}. Use a fallback with {{name | "New submission"}}. Missing fields render empty and never error. Do not pass HTML; Furrow renders one owned layout. Set webhook_url_override to override the project webhook for this form. notify_cc_override / notify_bcc_override replace the project's copies ([] sends none). notify_phones_override replaces the project's text-alert numbers ([] silences texts for this form). Do not use this to change Turnstile keys — those live on the project. Checkbox groups, select multiple, and other multi-value HTML fields must use a [] suffix on the name (name="services[]") or only the last checked value is stored. Put services[] on field_contract.name too. JSON/fetch should send a real array without brackets ({ "services": ["a", "b"] }) and must not use Object.fromEntries(formData) — use formData.getAll("services"). Single checkbox, select, or combobox: no brackets. File inputs use multipart POST (name="resume[]" for multiple). Enable uploads on the project first. Downloads are the project inbox at files_url, with the form slug as a folder — never put that URL in a public snippet.

NameRequiredDescriptionDefault
nameNoNew form display name.
emailNoPer-form subject and intro overrides for the notification email.
form_idYesForm id from list_forms, create_form, or bootstrap_site.
field_contractNoDeclared fields (name, type, label, report). Drives get_snippet and the email layout. Null clears it.
notify_cc_overrideNoCc for this form only. Empty array sends no Cc. Null inherits the project's notify_cc.
notify_bcc_overrideNoBcc for this form only. Empty array sends no Bcc. Null inherits the project's notify_bcc.
webhook_url_overrideNoWebhook endpoint for this form only. Null inherits the project webhook.
redirect_url_overrideNoSuccess redirect for this form only. Null inherits default_redirect_url.
notify_emails_overrideNoRecipients for this form only. Null inherits the project's notify_emails.
notify_phones_overrideNoText-alert numbers for this form only (E.164). Empty array silences texts for this form. Null inherits the project's notify_phones. Text messages need the team's own Twilio account first (update_account sms).
webhook_secret_overrideNoHMAC secret for this form's webhook. Null inherits the project secret.
require_turnstile_overrideNoForce Turnstile on or off for this form. Null inherits the project setting.

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the readOnly=false and idempotent=true annotations, the description discloses meaningful behavior: null inheritance, [] meaning no Cc/Bcc or silenced texts, missing template fields rendering empty, and no HTML allowed. Some of the behavioral detail is about snippet and inbox usage rather than update_form itself, which keeps it from being a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness2/5

Is the description appropriately sized, front-loaded, and free of redundancy?

It starts well with the override-patching purpose and null semantics, but then becomes an overlong mix of email templating, raw HTML field naming, fetch/multipart instructions, and public snippet warnings. Much of that does not help an agent invoke update_form and buries the relevant parameter guidance.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 12-parameter mutation with no output schema, the description covers the tricky inheritance and array semantics well. It omits return/confirmation behavior and does not mention several parameters such as name, redirect_url_override, and webhook_secret_override, but the schema documents those directly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

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. The description adds useful semantics for override parameters, especially null inheritance, [] behavior, template tokens, and the [] suffix convention for multi-value field_contract names.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description gives a specific verb and resource: patching form overrides. It also draws a clear boundary from project-level settings, but it never states that update_form can rename the form or change field_contract as a whole, so the stated purpose is narrower than the actual tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It clearly says when to use this tool and when not to, e.g. null re-inherits, Turnstile keys belong to the project, and SMS needs Twilio configured first. It does not name update_project as the alternative, but the operational boundary is easy to infer.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

update_projectUpdate projectA
Idempotent
Inspect

Use to patch website settings. Nested email sets notification templates: email.subject, email.from_name, email.intro, email.footer_mode (furrow | minimal | off), email.include_meta. Optional branding updates the team (mode furrow | off | agency on yearly only; other modes return plan_limit on free). Set uploads_enabled and upload_allowed_types here — forms inherit them. notify_cc and notify_bcc copy every notification email (Bcc stays hidden from the client — use it for an agency's quiet copy); forms inherit them. notify_phones (E.164) get a short new-lead text per clean submission once the team has Twilio set up (update_account sms); forms inherit them. Email templates support {{project.name}}, {{project.slug}}, {{form.name}}, {{form.slug}}, {{count}} (1-based clean submissions on this form, including the current one), and any submission field such as {{name}} or {{email}}. Use a fallback with {{name | "New submission"}}. Missing fields render empty and never error. Do not pass HTML; Furrow renders one owned layout. Plans are capacity, not a feature ladder. Free: one workspace, 100 clean submissions/month pooled on the team, 3 active (non-archived) projects, 30-day submission retention, and email.footer_mode / branding.mode stay furrow (other values return plan_limit). Yearly ($199/year): extra workspaces, 10,000 submissions/month, unlimited projects, 730-day retention, branding.mode furrow | off | agency. Expansion packs are $99/year each and add 5,000 submissions/month on a yearly account. MCP, webhooks, snippets, inheritance, Turnstile, and email templates stay available on free. Spam, honeypot, and rejected posts do not count. Use create_upgrade_link to hand a human a Stripe Checkout (or Customer Portal) URL. Pass product=expansion on a yearly team to add a pack. File inputs use multipart POST (name="resume[]" for multiple). Enable uploads on the project first. Downloads are the project inbox at files_url, with the form slug as a folder — never put that URL in a public snippet. Null Turnstile or webhook fields clear them. Do not use this to create forms. Updating project keys changes resolved settings on existing forms unless a form override is set.

NameRequiredDescriptionDefault
nameNoNew project display name.
emailNoNotification email template for the project: subject, from_name, intro, footer_mode, include_meta.
brandingNoPer-project branding override for notification emails and hosted pages.
from_nameNoLegacy From display name. Prefer email.from_name.
notify_ccNoCc'd on every notification email (max 10). Copies only: an email still needs at least one notify_emails address, and an address on more than one list is sent once (To, then Cc, then Bcc). Copies get [Potential spam] emails too.
notify_bccNoBcc'd on every notification email (max 10), hidden from the other recipients — use it when an agency wants a silent copy of a client's leads. Copies only: an email still needs at least one notify_emails address, and an address on more than one list is sent once (To, then Cc, then Bcc). Copies get [Potential spam] emails too.
project_idYesProject (website) id from list_projects, create_project, or bootstrap_site.
webhook_urlNoPublic https endpoint that receives a signed JSON POST per clean submission. Null disables it.
notify_emailsNoReplace the notification recipients (max 10). Cannot be emptied while the project has no webhook_url.
notify_phonesNoReplace the text-alert numbers (E.164, max 5). Empty array turns texts off. Text messages need the team's own Twilio account first (update_account sms).
honeypot_fieldNoName of the hidden honeypot input the snippet emits. Defaults to _gotcha.
webhook_secretNoHMAC-SHA256 secret for the X-Furrow-Signature header. Omit to have one generated and returned.
allowed_domainsNoReplace the allowed hostnames. Empty array accepts any origin.
uploads_enabledNoAccept multipart file fields on this project's forms.
turnstile_secretNoCloudflare Turnstile secret used to verify tokens server-side. Null removes it.
upload_max_filesNoMax files per submission (up to 20).
rate_limit_per_ipNoMax submissions per IP within rate_limit_window_s.
require_turnstileNoReject submissions that lack a valid Turnstile token.
turnstile_site_keyNoCloudflare Turnstile site key for the frontend widget. Null removes it.
rate_limit_window_sNoRate-limit window in seconds (max 86400).
default_redirect_urlNoWhere classic HTML posts redirect after success. Null returns JSON/inline success instead.
upload_allowed_typesNoAllowed upload MIME types, e.g. application/pdf or image/*.
upload_max_file_bytesNoMax bytes per file (up to 25 MB).
upload_max_total_bytesNoMax total upload bytes per submission (up to 50 MB).

TDQS

A3.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description adds substantial behavior beyond the annotations: plan limits that return plan_limit, token rendering and fallback behavior, inheritance by forms, null-clearing semantics for Turnstile and webhook fields, and the side effect that updating project keys changes resolved settings on existing forms. These details materially help an agent predict the tool's effects.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness2/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is very long and mixes update_project behavior with pricing details, create_upgrade_link usage, multipart submission instructions, download inbox warnings, and other tangential context. The purpose is front-loaded, but many sentences do not directly help an agent invoke this specific update tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 24-parameter nested update tool with annotations but no output schema, the description covers the most important behavior: plan constraints, inheritance, token rendering, null clearing, upload prerequisites, and the prohibition on using it to create forms. It is not perfectly focused, but it is complete enough for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema already carries most parameter semantics. The description still adds useful meaning beyond it, especially token syntax and fallbacks for email templates, inheritance rules for uploads and notification fields, and null-clearing behavior for some fields.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with 'Use to patch website settings,' giving a clear verb and resource, and it enumerates many project-level fields. It also says 'Do not use this to create forms,' which distinguishes it from creation siblings, though it does not explicitly contrast update_form or update_team.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives some usage context, such as setting uploads and notification settings here because forms inherit them, and it explicitly warns against using the tool to create forms. However, it does not clearly say when to choose update_project over update_form or update_team, and the create_upgrade_link guidance is about a sibling tool rather than this one.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

update_teamUpdate teamA
Idempotent
Inspect

Use to rename a team. Operator tokens (all workspaces) must pass team_id from list_teams. Team-scoped tokens may omit team_id and cannot target another workspace. Do not use this to create another workspace — that is dashboard-only and requires yearly. For plan, usage, retention, and branding, call get_account / update_account. Do not use this to change clients or projects.

NameRequiredDescriptionDefault
nameYesNew team (workspace) display name.
team_idNoTeam id from list_teams. Required for operator tokens and OAuth logins; team-scoped frw_ tokens may omit it.

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The annotations only indicate readOnly=false and idempotentHint=true. The description adds meaningful behavioral context about authentication scoping: operator tokens must pass team_id, while team-scoped tokens may omit it and cannot target another workspace. This goes beyond what the annotations convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and front-loaded with the primary purpose, then delivers token constraints and exclusions in efficient sentences. Every sentence adds necessary guidance; there is no filler or redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-parameter mutation tool with no output schema, the description is complete enough for correct selection and invocation. It covers the action, token behavior, required parameter provenance, and explicitly rules out adjacent use cases.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

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 extra semantics for team_id by clarifying operator-token requirements and the 'cannot target another workspace' restriction for team-scoped tokens, which is not fully captured in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

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: 'Use to rename a team.' It then explicitly distinguishes itself from creating a workspace, changing account-level settings, and changing clients/projects, so an agent can differentiate it from siblings like update_account, update_client, and update_project.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit when-to-use and when-not-to-use guidance. It names get_account/update_account as the correct tools for plan, usage, retention, and branding, and warns against using this tool to create workspaces or change clients/projects. It also explains token-specific requirements for team_id.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

upsert_project_webhookUpsert project webhookA
Idempotent
Inspect

Use to set or replace the project webhook URL. Available on free and yearly. A signing secret is generated once if the project has none. Forms inherit this unless webhook_url_override is set. Do not use this for a one-off delivery — call test_webhook.

NameRequiredDescriptionDefault
project_idYesProject (website) id from list_projects, create_project, or bootstrap_site.
webhook_urlYesPublic https endpoint that receives a signed JSON POST per clean submission. Null disables it.
webhook_secretNoHMAC-SHA256 secret for X-Furrow-Signature. Omit to have one generated and returned.

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the annotations, the description reveals important side effects: a signing secret is generated only once if absent, and forms inherit it unless webhook_url_override is set. These behaviors are not visible in the schema description and help predict the call's consequences. It does not contradict the readOnlyHint=false or idempotentHint=true annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is three tight sentences, with the core purpose front-loaded and the exclusion placed immediately after. Every sentence contributes either to correct invocation, plan eligibility, or behavioral context. No filler or redundant restatement of the title.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 3-parameter tool with a fully documented schema, the description covers what the tool does, when to use it, the main alternative, plan availability, and a non-obvious side effect. The schema already documents the parameters, so the description is complete enough for correct tool selection and invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

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 useful meaning around webhook_secret by explaining auto-generation and form inheritance, which helps an agent decide whether to omit or provide that parameter. It does not fully explain every edge case, such as rotating an existing secret, but it adds value beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

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: 'set or replace the project webhook URL.' It also explicitly contrasts the tool with test_webhook, so an agent can distinguish it from the most relevant sibling without inspecting schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives a direct 'Use to...' instruction, states the plan availability, and provides an explicit exclusion: 'Do not use this for a one-off delivery — call test_webhook.' This is concrete when-to-use and when-not-to-use guidance.

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.

  1. 5 tool updates
    • Changedbootstrap_site8 fields changed
      • changedInput schema / properties / forms / items / properties / notify_bcc_override / anyOf
        Previous value: -[
        -  {
        -    "items": {
        -      "format": "email",
        -      "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        -      "type": "string"
        -    },
        -    "maxItems": 10,
        -    "type": "array"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "items": {
        +      "format": "email",
        +      "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        +      "type": "string"
        +    },
        +    "maxItems": 10,
        +    "type": "array"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • changedInput schema / properties / forms / items / properties / notify_cc_override / anyOf
        Previous value: -[
        -  {
        -    "items": {
        -      "format": "email",
        -      "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        -      "type": "string"
        -    },
        -    "maxItems": 10,
        -    "type": "array"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "items": {
        +      "format": "email",
        +      "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        +      "type": "string"
        +    },
        +    "maxItems": 10,
        +    "type": "array"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • changedInput schema / properties / forms / items / properties / notify_emails_override / anyOf
        Previous value: -[
        -  {
        -    "items": {
        -      "format": "email",
        -      "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        -      "type": "string"
        -    },
        -    "maxItems": 10,
        -    "type": "array"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "items": {
        +      "format": "email",
        +      "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        +      "type": "string"
        +    },
        +    "maxItems": 10,
        +    "type": "array"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • removedInput schema / properties / forms / items / properties / require_turnstile_override / anyOf
        Removed value: -[
        -  {
        -    "type": "boolean"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedInput schema / properties / forms / items / properties / require_turnstile_override / type
        Added value: +[
        +  "boolean",
        +  "null"
        +]
      • changedInput schema / properties / notify_bcc / items / pattern
        Previous value: -"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"New value: +"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
      • changedInput schema / properties / notify_cc / items / pattern
        Previous value: -"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"New value: +"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
      • changedInput schema / properties / notify_emails / items / pattern
        Previous value: -"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"New value: +"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
    • Changedcreate_form5 fields changed
      • changedInput schema / properties / notify_bcc_override / anyOf
        Previous value: -[
        -  {
        -    "items": {
        -      "format": "email",
        -      "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        -      "type": "string"
        -    },
        -    "maxItems": 10,
        -    "type": "array"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "items": {
        +      "format": "email",
        +      "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        +      "type": "string"
        +    },
        +    "maxItems": 10,
        +    "type": "array"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • changedInput schema / properties / notify_cc_override / anyOf
        Previous value: -[
        -  {
        -    "items": {
        -      "format": "email",
        -      "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        -      "type": "string"
        -    },
        -    "maxItems": 10,
        -    "type": "array"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "items": {
        +      "format": "email",
        +      "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        +      "type": "string"
        +    },
        +    "maxItems": 10,
        +    "type": "array"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • changedInput schema / properties / notify_emails_override / anyOf
        Previous value: -[
        -  {
        -    "items": {
        -      "format": "email",
        -      "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        -      "type": "string"
        -    },
        -    "maxItems": 10,
        -    "type": "array"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "items": {
        +      "format": "email",
        +      "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        +      "type": "string"
        +    },
        +    "maxItems": 10,
        +    "type": "array"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • removedInput schema / properties / require_turnstile_override / anyOf
        Removed value: -[
        -  {
        -    "type": "boolean"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedInput schema / properties / require_turnstile_override / type
        Added value: +[
        +  "boolean",
        +  "null"
        +]
    • Changedcreate_project3 fields changed
      • changedInput schema / properties / notify_bcc / items / pattern
        Previous value: -"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"New value: +"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
      • changedInput schema / properties / notify_cc / items / pattern
        Previous value: -"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"New value: +"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
      • changedInput schema / properties / notify_emails / items / pattern
        Previous value: -"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"New value: +"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
    • Changedupdate_form5 fields changed
      • changedInput schema / properties / notify_bcc_override / anyOf
        Previous value: -[
        -  {
        -    "items": {
        -      "format": "email",
        -      "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        -      "type": "string"
        -    },
        -    "maxItems": 10,
        -    "type": "array"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "items": {
        +      "format": "email",
        +      "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        +      "type": "string"
        +    },
        +    "maxItems": 10,
        +    "type": "array"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • changedInput schema / properties / notify_cc_override / anyOf
        Previous value: -[
        -  {
        -    "items": {
        -      "format": "email",
        -      "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        -      "type": "string"
        -    },
        -    "maxItems": 10,
        -    "type": "array"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "items": {
        +      "format": "email",
        +      "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        +      "type": "string"
        +    },
        +    "maxItems": 10,
        +    "type": "array"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • changedInput schema / properties / notify_emails_override / anyOf
        Previous value: -[
        -  {
        -    "items": {
        -      "format": "email",
        -      "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        -      "type": "string"
        -    },
        -    "maxItems": 10,
        -    "type": "array"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "items": {
        +      "format": "email",
        +      "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        +      "type": "string"
        +    },
        +    "maxItems": 10,
        +    "type": "array"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • removedInput schema / properties / require_turnstile_override / anyOf
        Removed value: -[
        -  {
        -    "type": "boolean"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedInput schema / properties / require_turnstile_override / type
        Added value: +[
        +  "boolean",
        +  "null"
        +]
    • Changedupdate_project3 fields changed
      • changedInput schema / properties / notify_bcc / items / pattern
        Previous value: -"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"New value: +"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
      • changedInput schema / properties / notify_cc / items / pattern
        Previous value: -"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"New value: +"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
      • changedInput schema / properties / notify_emails / items / pattern
        Previous value: -"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"New value: +"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
  2. 5 tool updates
    • Changedbootstrap_site4 fields changed
      • addedInput schema / properties / forms / items / properties / notify_bcc_override
        Added value: +{
        +  "anyOf": [
        +    {
        +      "items": {
        +        "format": "email",
        +        "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        +        "type": "string"
        +      },
        +      "maxItems": 10,
        +      "type": "array"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Bcc for this form only. Empty array sends no Bcc. Null inherits the project's notify_bcc."
        +}
      • addedInput schema / properties / forms / items / properties / notify_cc_override
        Added value: +{
        +  "anyOf": [
        +    {
        +      "items": {
        +        "format": "email",
        +        "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        +        "type": "string"
        +      },
        +      "maxItems": 10,
        +      "type": "array"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Cc for this form only. Empty array sends no Cc. Null inherits the project's notify_cc."
        +}
      • addedInput schema / properties / notify_bcc
        Added value: +{
        +  "description": "Bcc'd on every notification email (max 10), hidden from the other recipients — use it when an agency wants a silent copy of a client's leads. Copies only: an email still needs at least one notify_emails address, and an address on more than one list is sent once (To, then Cc, then Bcc). Copies get [Potential spam] emails too.",
        +  "items": {
        +    "format": "email",
        +    "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        +    "type": "string"
        +  },
        +  "maxItems": 10,
        +  "type": "array"
        +}
      • addedInput schema / properties / notify_cc
        Added value: +{
        +  "description": "Cc'd on every notification email (max 10). Copies only: an email still needs at least one notify_emails address, and an address on more than one list is sent once (To, then Cc, then Bcc). Copies get [Potential spam] emails too.",
        +  "items": {
        +    "format": "email",
        +    "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        +    "type": "string"
        +  },
        +  "maxItems": 10,
        +  "type": "array"
        +}
    • Changedcreate_form2 fields changed
      • addedInput schema / properties / notify_bcc_override
        Added value: +{
        +  "anyOf": [
        +    {
        +      "items": {
        +        "format": "email",
        +        "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        +        "type": "string"
        +      },
        +      "maxItems": 10,
        +      "type": "array"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Bcc for this form only. Empty array sends no Bcc. Null inherits the project's notify_bcc."
        +}
      • addedInput schema / properties / notify_cc_override
        Added value: +{
        +  "anyOf": [
        +    {
        +      "items": {
        +        "format": "email",
        +        "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        +        "type": "string"
        +      },
        +      "maxItems": 10,
        +      "type": "array"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Cc for this form only. Empty array sends no Cc. Null inherits the project's notify_cc."
        +}
    • Changedcreate_project2 fields changed
      • addedInput schema / properties / notify_bcc
        Added value: +{
        +  "description": "Bcc'd on every notification email (max 10), hidden from the other recipients — use it when an agency wants a silent copy of a client's leads. Copies only: an email still needs at least one notify_emails address, and an address on more than one list is sent once (To, then Cc, then Bcc). Copies get [Potential spam] emails too.",
        +  "items": {
        +    "format": "email",
        +    "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        +    "type": "string"
        +  },
        +  "maxItems": 10,
        +  "type": "array"
        +}
      • addedInput schema / properties / notify_cc
        Added value: +{
        +  "description": "Cc'd on every notification email (max 10). Copies only: an email still needs at least one notify_emails address, and an address on more than one list is sent once (To, then Cc, then Bcc). Copies get [Potential spam] emails too.",
        +  "items": {
        +    "format": "email",
        +    "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        +    "type": "string"
        +  },
        +  "maxItems": 10,
        +  "type": "array"
        +}
    • Changedupdate_form2 fields changed
      • addedInput schema / properties / notify_bcc_override
        Added value: +{
        +  "anyOf": [
        +    {
        +      "items": {
        +        "format": "email",
        +        "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        +        "type": "string"
        +      },
        +      "maxItems": 10,
        +      "type": "array"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Bcc for this form only. Empty array sends no Bcc. Null inherits the project's notify_bcc."
        +}
      • addedInput schema / properties / notify_cc_override
        Added value: +{
        +  "anyOf": [
        +    {
        +      "items": {
        +        "format": "email",
        +        "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        +        "type": "string"
        +      },
        +      "maxItems": 10,
        +      "type": "array"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Cc for this form only. Empty array sends no Cc. Null inherits the project's notify_cc."
        +}
    • Changedupdate_project2 fields changed
      • addedInput schema / properties / notify_bcc
        Added value: +{
        +  "description": "Bcc'd on every notification email (max 10), hidden from the other recipients — use it when an agency wants a silent copy of a client's leads. Copies only: an email still needs at least one notify_emails address, and an address on more than one list is sent once (To, then Cc, then Bcc). Copies get [Potential spam] emails too.",
        +  "items": {
        +    "format": "email",
        +    "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        +    "type": "string"
        +  },
        +  "maxItems": 10,
        +  "type": "array"
        +}
      • addedInput schema / properties / notify_cc
        Added value: +{
        +  "description": "Cc'd on every notification email (max 10). Copies only: an email still needs at least one notify_emails address, and an address on more than one list is sent once (To, then Cc, then Bcc). Copies get [Potential spam] emails too.",
        +  "items": {
        +    "format": "email",
        +    "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
        +    "type": "string"
        +  },
        +  "maxItems": 10,
        +  "type": "array"
        +}
  3. 1 tool update
    • Addedmove_client
  4. 7 tool updates
    • Changedbootstrap_site6 fields changed
      • removedInput schema / properties / forms / default
        Removed value: -[]
      • changedInput schema / properties / forms / items / properties / field_contract / anyOf
        Previous value: -[
        -  {
        -    "items": {
        -      "properties": {
        -        "label": {
        -          "anyOf": [
        -            {
        -              "maxLength": 120,
        -              "minLength": 1,
        -              "type": "string"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ],
        -          "description": "Human label used in the snippet and notification email. Defaults to the field name."
        -        },
        -        "name": {
        -          "description": "Posted field name. HTML checkbox groups / multi-select must end in [] (services[]) or only the last value is stored. JSON arrays omit the brackets.",
        -          "maxLength": 80,
        -          "minLength": 1,
        -          "type": "string"
        -        },
        -        "report": {
        -          "default": false,
        -          "description": "Include this field in dashboard summaries and reports.",
        -          "type": "boolean"
        -        },
        -        "type": {
        -          "description": "text, email, textarea, file, etc. type=file emits a file input in get_snippet.",
        -          "maxLength": 40,
        -          "minLength": 1,
        -          "type": "string"
        -        }
        -      },
        -      "required": [
        -        "name",
        -        "type"
        -      ],
        -      "type": "object"
        -    },
        -    "type": "array"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "items": {
        +      "properties": {
        +        "label": {
        +          "anyOf": [
        +            {
        +              "maxLength": 120,
        +              "minLength": 1,
        +              "type": "string"
        +            },
        +            {
        +              "type": "null"
        +            }
        +          ],
        +          "description": "Human label used in the snippet and notification email. Defaults to the field name."
        +        },
        +        "name": {
        +          "description": "Posted field name. HTML checkbox groups / multi-select must end in [] (services[]) or only the last value is stored. JSON arrays omit the brackets.",
        +          "maxLength": 80,
        +          "minLength": 1,
        +          "type": "string"
        +        },
        +        "report": {
        +          "default": false,
        +          "description": "Include this field in dashboard summaries and reports.",
        +          "type": "boolean"
        +        },
        +        "required": {
        +          "description": "Emit the HTML required attribute in get_snippet so the browser blocks an empty submit. Set it on the fields a lead is useless without, usually email and message.",
        +          "type": "boolean"
        +        },
        +        "type": {
        +          "description": "text, email, textarea, file, etc. type=file emits a file input in get_snippet.",
        +          "maxLength": 40,
        +          "minLength": 1,
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "name",
        +        "type"
        +      ],
        +      "type": "object"
        +    },
        +    "type": "array"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • addedInput schema / properties / forms / items / properties / fields / items / properties / required
        Added value: +{
        +  "description": "Emit the HTML required attribute in get_snippet so the browser blocks an empty submit. Set it on the fields a lead is useless without, usually email and message.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / forms / items / properties / notify_phones_override
        Added value: +{
        +  "anyOf": [
        +    {
        +      "items": {
        +        "maxLength": 32,
        +        "type": "string"
        +      },
        +      "maxItems": 5,
        +      "type": "array"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Text-alert numbers for this form only (E.164). Empty array silences texts for this form. Null inherits the project's notify_phones. Text messages need the team's own Twilio account first (update_account sms)."
        +}
      • changedInput schema / properties / notify_emails / description
        Previous value: -"Addresses that receive each clean submission (max 10). Inherited by every form."New value: +"Addresses that receive each clean submission (max 10). Inherited by every form. Required unless webhook_url is set — ask the human which inbox should get submissions."
      • addedInput schema / properties / notify_phones
        Added value: +{
        +  "description": "E.164 numbers that get a short \"new lead, check your email\" text per clean submission (max 5). Inherited by every form. Never sent for spam. Text messages need the team's own Twilio account first (update_account sms).",
        +  "items": {
        +    "maxLength": 32,
        +    "type": "string"
        +  },
        +  "maxItems": 5,
        +  "type": "array"
        +}
    • Changedcreate_form2 fields changed
      • changedInput schema / properties / field_contract / anyOf
        Previous value: -[
        -  {
        -    "items": {
        -      "properties": {
        -        "label": {
        -          "anyOf": [
        -            {
        -              "maxLength": 120,
        -              "minLength": 1,
        -              "type": "string"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ],
        -          "description": "Human label used in the snippet and notification email. Defaults to the field name."
        -        },
        -        "name": {
        -          "description": "Posted field name. HTML checkbox groups / multi-select must end in [] (services[]) or only the last value is stored. JSON arrays omit the brackets.",
        -          "maxLength": 80,
        -          "minLength": 1,
        -          "type": "string"
        -        },
        -        "report": {
        -          "default": false,
        -          "description": "Include this field in dashboard summaries and reports.",
        -          "type": "boolean"
        -        },
        -        "type": {
        -          "description": "text, email, textarea, file, etc. type=file emits a file input in get_snippet.",
        -          "maxLength": 40,
        -          "minLength": 1,
        -          "type": "string"
        -        }
        -      },
        -      "required": [
        -        "name",
        -        "type"
        -      ],
        -      "type": "object"
        -    },
        -    "type": "array"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "items": {
        +      "properties": {
        +        "label": {
        +          "anyOf": [
        +            {
        +              "maxLength": 120,
        +              "minLength": 1,
        +              "type": "string"
        +            },
        +            {
        +              "type": "null"
        +            }
        +          ],
        +          "description": "Human label used in the snippet and notification email. Defaults to the field name."
        +        },
        +        "name": {
        +          "description": "Posted field name. HTML checkbox groups / multi-select must end in [] (services[]) or only the last value is stored. JSON arrays omit the brackets.",
        +          "maxLength": 80,
        +          "minLength": 1,
        +          "type": "string"
        +        },
        +        "report": {
        +          "default": false,
        +          "description": "Include this field in dashboard summaries and reports.",
        +          "type": "boolean"
        +        },
        +        "required": {
        +          "description": "Emit the HTML required attribute in get_snippet so the browser blocks an empty submit. Set it on the fields a lead is useless without, usually email and message.",
        +          "type": "boolean"
        +        },
        +        "type": {
        +          "description": "text, email, textarea, file, etc. type=file emits a file input in get_snippet.",
        +          "maxLength": 40,
        +          "minLength": 1,
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "name",
        +        "type"
        +      ],
        +      "type": "object"
        +    },
        +    "type": "array"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • addedInput schema / properties / notify_phones_override
        Added value: +{
        +  "anyOf": [
        +    {
        +      "items": {
        +        "maxLength": 32,
        +        "type": "string"
        +      },
        +      "maxItems": 5,
        +      "type": "array"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Text-alert numbers for this form only (E.164). Empty array silences texts for this form. Null inherits the project's notify_phones. Text messages need the team's own Twilio account first (update_account sms)."
        +}
    • Changedcreate_project2 fields changed
      • changedInput schema / properties / notify_emails / description
        Previous value: -"Addresses that receive each clean submission (max 10). Inherited by every form."New value: +"Addresses that receive each clean submission (max 10). Inherited by every form. Required unless webhook_url is set — ask the human which inbox should get submissions."
      • addedInput schema / properties / notify_phones
        Added value: +{
        +  "description": "E.164 numbers that get a short \"new lead, check your email\" text per clean submission (max 5). Inherited by every form. Never sent for spam. Text messages need the team's own Twilio account first (update_account sms).",
        +  "items": {
        +    "maxLength": 32,
        +    "type": "string"
        +  },
        +  "maxItems": 5,
        +  "type": "array"
        +}
    • Addedtest_sms
    • Changedupdate_account1 field changed
      • addedInput schema / properties / sms
        Added value: +{
        +  "anyOf": [
        +    {
        +      "properties": {
        +        "account_sid": {
        +          "description": "Twilio Account SID (AC…).",
        +          "pattern": "^AC[0-9a-fA-F]{32}$",
        +          "type": "string"
        +        },
        +        "auth_token": {
        +          "description": "Twilio Auth Token. Write-only, stored encrypted, never returned. Required the first time; omit later to keep the saved one.",
        +          "maxLength": 200,
        +          "minLength": 1,
        +          "type": "string"
        +        },
        +        "from": {
        +          "description": "Sending Twilio number in E.164, or a Messaging Service SID (MG…). US senders need A2P 10DLC or toll-free verification done in Twilio first.",
        +          "maxLength": 64,
        +          "type": "string"
        +        },
        +        "provider": {
        +          "default": "twilio",
        +          "description": "SMS provider. Only twilio today.",
        +          "enum": [
        +            "twilio"
        +          ],
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "account_sid",
        +        "from"
        +      ],
        +      "type": "object"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "The team's own Twilio account for new-lead text alerts. Null removes it. Numbers to text live on the project (notify_phones)."
        +}
    • Changedupdate_form2 fields changed
      • changedInput schema / properties / field_contract / anyOf
        Previous value: -[
        -  {
        -    "items": {
        -      "properties": {
        -        "label": {
        -          "anyOf": [
        -            {
        -              "maxLength": 120,
        -              "minLength": 1,
        -              "type": "string"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ],
        -          "description": "Human label used in the snippet and notification email. Defaults to the field name."
        -        },
        -        "name": {
        -          "description": "Posted field name. HTML checkbox groups / multi-select must end in [] (services[]) or only the last value is stored. JSON arrays omit the brackets.",
        -          "maxLength": 80,
        -          "minLength": 1,
        -          "type": "string"
        -        },
        -        "report": {
        -          "default": false,
        -          "description": "Include this field in dashboard summaries and reports.",
        -          "type": "boolean"
        -        },
        -        "type": {
        -          "description": "text, email, textarea, file, etc. type=file emits a file input in get_snippet.",
        -          "maxLength": 40,
        -          "minLength": 1,
        -          "type": "string"
        -        }
        -      },
        -      "required": [
        -        "name",
        -        "type"
        -      ],
        -      "type": "object"
        -    },
        -    "type": "array"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "items": {
        +      "properties": {
        +        "label": {
        +          "anyOf": [
        +            {
        +              "maxLength": 120,
        +              "minLength": 1,
        +              "type": "string"
        +            },
        +            {
        +              "type": "null"
        +            }
        +          ],
        +          "description": "Human label used in the snippet and notification email. Defaults to the field name."
        +        },
        +        "name": {
        +          "description": "Posted field name. HTML checkbox groups / multi-select must end in [] (services[]) or only the last value is stored. JSON arrays omit the brackets.",
        +          "maxLength": 80,
        +          "minLength": 1,
        +          "type": "string"
        +        },
        +        "report": {
        +          "default": false,
        +          "description": "Include this field in dashboard summaries and reports.",
        +          "type": "boolean"
        +        },
        +        "required": {
        +          "description": "Emit the HTML required attribute in get_snippet so the browser blocks an empty submit. Set it on the fields a lead is useless without, usually email and message.",
        +          "type": "boolean"
        +        },
        +        "type": {
        +          "description": "text, email, textarea, file, etc. type=file emits a file input in get_snippet.",
        +          "maxLength": 40,
        +          "minLength": 1,
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "name",
        +        "type"
        +      ],
        +      "type": "object"
        +    },
        +    "type": "array"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • addedInput schema / properties / notify_phones_override
        Added value: +{
        +  "anyOf": [
        +    {
        +      "items": {
        +        "maxLength": 32,
        +        "type": "string"
        +      },
        +      "maxItems": 5,
        +      "type": "array"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Text-alert numbers for this form only (E.164). Empty array silences texts for this form. Null inherits the project's notify_phones. Text messages need the team's own Twilio account first (update_account sms)."
        +}
    • Changedupdate_project2 fields changed
      • changedInput schema / properties / notify_emails / description
        Previous value: -"Replace the notification recipients (max 10)."New value: +"Replace the notification recipients (max 10). Cannot be emptied while the project has no webhook_url."
      • addedInput schema / properties / notify_phones
        Added value: +{
        +  "description": "Replace the text-alert numbers (E.164, max 5). Empty array turns texts off. Text messages need the team's own Twilio account first (update_account sms).",
        +  "items": {
        +    "maxLength": 32,
        +    "type": "string"
        +  },
        +  "maxItems": 5,
        +  "type": "array"
        +}
  5. 1 tool update
    • Changedupdate_account1 field changed
      • addedInput schema / properties / ai
        Added value: +{
        +  "description": "AI spam filter. Off until this team saves its own TypeSafe key. One Jev call per heuristics-clean submission.",
        +  "properties": {
        +    "notify_suspected_spam": {
        +      "description": "When true (the default), submissions the AI flags as spam still email notify addresses with a Potential spam label and send no webhook. Set false to silence those emails.",
        +      "type": "boolean"
        +    },
        +    "typesafe_api_key": {
        +      "anyOf": [
        +        {
        +          "maxLength": 500,
        +          "type": "string"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "description": "TypeSafe API key for the AI spam filter. Send null or an empty string to remove it. The key is write-only and never returned."
        +    }
        +  },
        +  "type": "object"
        +}
  6. 27 tool updates
    • First observedarchive_client
    • First observedarchive_form
    • First observedarchive_project
    • First observedbootstrap_site
    • First observedcreate_client
    • First observedcreate_form
    • First observedcreate_project
    • First observedcreate_upgrade_link
    • First observedget_account
    • First observedget_client
    • First observedget_form
    • First observedget_project
    • First observedget_snippet
    • First observedget_submission
    • First observedlist_clients
    • First observedlist_forms
    • First observedlist_projects
    • First observedlist_submissions
    • First observedlist_teams
    • First observedrotate_project_inbox
    • First observedtest_webhook
    • First observedupdate_account
    • First observedupdate_client
    • First observedupdate_form
    • First observedupdate_project
    • First observedupdate_team
    • First observedupsert_project_webhook

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    Enables AI agents to submit and triage leads via email, check endpoint health, and generate production-ready form code across multiple frameworks without requiring API keys.
    4
    7 npm
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI agents to build and operate production-ready forms, quizzes, surveys, and workflows, including creation, publishing, submission management, and integration with webhooks and analytics.
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Formward as an MCP server for your coding agent. Pair once from the Formward dashboard, then let Claude Code, Codex, Cursor or any other MCP client create forms, fetch the paste-ready snippet, add webhooks and confirm a test submission against your workspace. Formward is a form backend hosted in the EU (Sweden). This package talks to it over HTTPS with a key that is pinned to one workspace and ca
    8
    12 npm
    MIT
Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.