Formfurrow

io.github.CloudCorpRecordsPublished by GitHub account CloudCorpRecordsv0.1.0Updated Nov 7, 2026

Run a form backend end to end: teams, clients, projects, forms, submissions, snippets, webhooks.

Overview

AI-generated overview

Local bridge to the hosted FurrowForms form backend, letting an assistant manage teams, clients, projects, forms, submissions, snippets and webhooks.

What you can do
  • List and update teams and account settings, including branding and spam-filter options
  • Browse, create, rename and archive clients (companies)
  • Manage websites (projects): create, patch settings, rotate the private inbox, archive
  • Create and update forms, read resolved settings, and fetch embed snippets for a chosen framework
  • List and read stored submissions, including file metadata and inbox location
  • Register or replace a project webhook and send a signed test event; bootstrap a client, site and forms in one call
When to use it
Useful when you run forms on FurrowForms and want an assistant to set up clients, sites and forms, inspect incoming submissions, or wire webhooks without using the dashboard. It is a thin local bridge, so it only makes sense if you already have a FurrowForms account and API key.
Requirements
Runs locally via npx (Node.js) or the Docker image, as a stdio MCP server. It forwards to the hosted upstream endpoint, so network access is needed. Optional environment variables: FURROWFORMS_MCP_URL to override the endpoint and FURROWFORMS_API_KEY, sent as a Bearer Authorization header; without the key no auth header is sent.
Before you install
Most tools write or change data: creating and updating clients, projects and forms, archiving (no unarchive in v1), rotating a project inbox (invalidates previously shared inbox links), and setting webhooks. update_account accepts a write-only TypeSafe API key. The API key travels to the upstream service, and the endpoint can be redirected via FURROWFORMS_MCP_URL. The plan-upgrade tool was deliberately omitted, so no payment tool is exposed.

Installation

In SourceWeft

  1. Open Formfurrow in the dashboard and add it to a workspace.
  2. Enable the server for the chats that should use its tools.

Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.

Other MCP clients

Follow the launch instructions in the repository.

README

formfurrow

formfurrow is a small local bridge to the hosted FurrowForms MCP server, a form backend you can run end to end: organise work into teams, clients and projects, build forms under those projects, read the submissions that come in, pull an embed snippet for a form in your framework of choice, and wire a project's webhook so new submissions are pushed to your own endpoint as they land.

The bridge keeps no form data of its own. It serves the upstream server's tool list locally, exactly as captured from the live endpoint, and forwards each tool call upstream, returning the result untouched. The captured surface here is a labelled subset: the upstream's plan-upgrade funnel tool (create_upgrade_link) was omitted on purpose, so there is no payment or plan-upgrade tool in this package. Everything that remains is workspace management on the caller's own form backend.

Tools

Account and teams:

  • list_teams — list the caller's teams. No required arguments.
  • update_team — update a team. Required: name.
  • get_account — read the caller's account. No required arguments.
  • update_account — update the account. No required arguments.

Clients:

  • list_clients — list clients. No required arguments.
  • get_client — read one client. Required: client_id.
  • create_client — create a client. Required: name, slug.
  • update_client — update a client. Required: client_id.
  • archive_client — archive a client. Required: client_id.

Projects:

  • list_projects — list projects. No required arguments.
  • get_project — read one project. Required: project_id.
  • create_project — create a project. Required: name, slug, client_id.
  • update_project — update a project. Required: project_id.
  • rotate_project_inbox — rotate a project's inbox. Required: project_id.
  • archive_project — archive a project. Required: project_id.

Forms and submissions:

  • list_forms — list forms. No required arguments.
  • get_form — read one form. Required: form_id.
  • create_form — create a form. Required: name, slug, project_id.
  • update_form — update a form. Required: form_id.
  • archive_form — archive a form. Required: form_id.
  • list_submissions — list submissions. No required arguments.
  • get_submission — read one submission. Required: submission_id.
  • get_snippet — get a form's embed snippet. Required: form_id, framework.

Webhooks and setup:

  • upsert_project_webhook — register or replace a project's webhook. Required: webhook_url, project_id.
  • test_webhook — send a test webhook for a project. Required: project_id.
  • bootstrap_site — bootstrap a site. Required: name, slug.

Each tool's full input schema, including its optional fields, is served verbatim in the MCP tools/list response.

Configuration

  • FURROWFORMS_MCP_URL (optional) — override the upstream endpoint. Defaults to https://api.furrowforms.com/mcp. (The override hook is this rebuild's own labelled reconstruction.)
  • FURROWFORMS_API_KEY (optional) — when set, every upstream request carries an Authorization: Bearer <key> header. When it is not set, no key header is sent. (The Authorization header name is pinned by the registry record; the Bearer scheme and this forwarding are labelled reconstructions, and the original surface was captured keyless.)

Forwarding mechanics

On the first tool call the client opens a streamable HTTP session with the upstream: a JSON-RPC initialize request (protocolVersion 2025-03-26, clientInfo name formfurrow), then a notifications/initialized notification, then the tools/call request carrying the caller's arguments unchanged. When the upstream answers initialize with an Mcp-Session-Id header, that value rides on every later request of the session, which is reused for the life of the process. Replies may arrive as plain JSON or as server-sent events; both are read.

Successful upstream results pass through verbatim, including any isError flag the upstream sets. Failures — a JSON-RPC error, a non-2xx status, an unreadable response, or a request that cannot be completed — come back as an isError result whose text starts with formfurrow error: (this rendering is this rebuild's own labelled reconstruction).

CLI

Each tool is also a subcommand taking one JSON argument string:

bash
npx @reneturcios/formfurrow list_teamsnpx @reneturcios/formfurrow create_client '{"name":"Acme","slug":"acme"}'npx @reneturcios/formfurrow get_snippet '{"form_id":"frm_1","framework":"react"}'npx @reneturcios/formfurrow --version

Exit codes: 0 on success, 1 on an isError result, 2 on a usage error.

MCP server

bash
npx -y @reneturcios/formfurrow mcp

MCP client config:

json
{  "mcpServers": {    "formfurrow": {      "command": "npx",      "args": ["-y", "@reneturcios/formfurrow", "mcp"]    }  }}

Docker

bash
docker run -i ghcr.io/cloudcorprecords/formfurrowdocker run --rm ghcr.io/cloudcorprecords/formfurrow --version

The container's default command is mcp, so the first form is a stdio MCP server; any other arguments reach the CLI normally.

Development

bash
npm installnpm run buildnpm testnpm run dev

The test suite runs entirely against a stubbed JSON-RPC upstream — no live network calls.

License

MIT — see LICENSE.

Source: README.md at commit d2b4b80

Tools

26

list_teams

List teams

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.

Takes no parameters.

update_team

Update team

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.

Parametersnamerequiredteam_id

get_account

Get account

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). The TypeSafe key itself is 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. 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.

Parametersteam_id

update_account

Update account

Use to rename the workspace, set branding, or configure the AI spam filter. 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 when Jev flags a submission; no webhook is sent either way. Heuristic spam stays silent. Free accounts cannot set branding.mode or email.footer_mode to anything but furrow — those writes are ignored. 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. 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.

Parametersainameteam_idbranding

list_clients

List clients

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.

Parameterslimitcursorstatusteam_id

get_client

Get client

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

Parametersclient_idrequired

create_client

Create client

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.

Parametersnamerequiredslugrequiredstrictteam_id

update_client

Update client

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

Parametersnameclient_idrequired

archive_client

Archive client

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

Parametersclient_idrequired

list_projects

List projects

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.

Parameterslimitcursorstatusteam_idclient_id

get_project

Get project

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

Parametersproject_idrequired

list_forms

List forms

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.

Parameterslimitcursorstatusteam_idclient_idproject_id

get_form

Get form

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.

Parametersform_idrequired

list_submissions

List submissions

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.

Parametersspamlimitsincecursorform_idteam_idproject_id

get_submission

Get submission

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.

Parameterssubmission_idrequired

get_snippet

Get form snippet

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.

Parametersform_idrequiredframeworkrequired

create_project

Create project

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 cannot set footer_mode off. 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. 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.

Parametersnamerequiredslugrequiredemailstrictclient_idrequiredfrom_namewebhook_urlnotify_emailshoneypot_fieldwebhook_secretallowed_domainsuploads_enabled+10 more

update_project

Update project

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; ignored on free except furrow). Set uploads_enabled and upload_allowed_types here — 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. 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.

Parametersnameemailbrandingfrom_nameproject_idrequiredwebhook_urlnotify_emailshoneypot_fieldwebhook_secretallowed_domainsuploads_enabledturnstile_secret+9 more

rotate_project_inbox

Rotate project inbox link

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.

Parametersproject_idrequired

archive_project

Archive project

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.

Parametersproject_idrequired

create_form

Create form

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.

Parametersnamerequiredslugrequiredemailstrictproject_idrequiredfield_contractwebhook_url_overrideredirect_url_overridenotify_emails_overridewebhook_secret_overriderequire_turnstile_override

update_form

Update form

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

Parametersnameemailform_idrequiredfield_contractwebhook_url_overrideredirect_url_overridenotify_emails_overridewebhook_secret_overriderequire_turnstile_override

archive_form

Archive form

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

Parametersform_idrequired

upsert_project_webhook

Upsert project webhook

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.

Parametersproject_idrequiredwebhook_urlrequiredwebhook_secret

test_webhook

Test webhook

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.

Parametersproject_idrequired

bootstrap_site

Bootstrap site

Use this to stand up a client, one website (project), and its forms in a single call. 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. 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.fromEn

Parametersnamerequiredslugrequiredemailformsteam_idfrom_namewebhook_urlproject_nameproject_slugnotify_emailshoneypot_fieldwebhook_secret+12 more

Version history

1
  1. v0.1.0LatestNov 4, 2026

Score

90% of the applicable checks met

How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.

  • Reached in the last 30 days25 pts
  • Auth declared10 pts

    Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.

  • Tools listedRequired15 pts
  • DescriptionRequired15 pts
  • Logo10 pts
  • Documentation15 pts
  • License10 pts