MCP Tools, One Weekend: Stress-Testing an AI-Accessible Form Builder

java dev.to

I have 20 MCP tools in formlm-cli. On paper they cover the full lifecycle: auth, app management, field CRUD, and sharing. But "covers the lifecycle" and "actually works when you throw a complex real-world task at an AI agent" are different things.

Update (v0.2.0): The architecture has been redesigned from 20 flat tools to 6 layered tools + 6 knowledge resources. The formlm_generate tool now handles app creation + field generation + scoring + publishing in a single call via the server-side AssessAgent pipeline. The formlm_exec tool provides direct access to all whitelisted CLI commands. The individual tools described in this article (app_create, field_add, share_publish, etc.) are now accessed through formlm_exec. The "current app" state issue mentioned below has been fixed via server-side SessionContext.

So I spent a weekend trying to build a genuinely complex form — an enterprise onboarding assessment with multiple field types, conditional logic, and scoring — using only Claude Desktop and the MCP tools. Here's what worked, what didn't, and what surprised me.

The Task

Build an "Enterprise Onboarding Readiness Assessment" with:

  • 5 text/input fields (employee name, department, role, manager, start date)
  • 2 radio fields with scoring (technical proficiency, communication level)
  • 3 scale fields (1-10) for confidence dimensions
  • 1 textarea for open feedback
  • Field ordering (specific sequence)
  • Published with a shareable URL

Not the most complex form in the world, but complex enough to exercise most of the toolset. I wanted to hit at least 15 of the 20 tools in a single session.

Saturday: Building the Form

Tools used: app_create, field_schema, field_config, field_add (×11)

I started with a prompt describing the full form. Claude's first move was app_create — clean, no issues.

Then it did something I've come to expect: it called field_schema before adding any fields. It wanted to see the available types. The response listed input, textarea, radio, scale, date, and others. Claude picked date for the start date field instead of input, which was smart — it means the form will show a date picker instead of a text box.

The field_config --type radio call came next. Claude wanted to know what properties a radio field accepts. The response showed options (comma-separated with label:score format) and required. Claude then added the radio fields with properly scored options:

field_add --app <appId> --id tech --type radio --options "Beginner:1,Intermediate:2,Advanced:3,Expert:4" --required
field_add --app <appId> --id comm --type radio --options "Needs improvement:1,Meets expectations:2,Exceeds expectations:3" --required
Enter fullscreen mode Exit fullscreen mode

The scale fields came next:

field_add --app <appId> --id conf_tools --type scale --min 1 --max 10 --title "How confident are you with the tools?"
field_add --app <appId> --id conf_team --type scale --min 1 --max 10 --title "How confident are you with your team?"
field_add --app <appId> --id conf_process --type scale --min 1 --max 10 --title "How confident are you with the processes?"
Enter fullscreen mode Exit fullscreen mode

Eleven field_add calls. All succeeded. No issues.

Tools used: field_list, field_move

Here's where I hit my first snag. The fields were added in the right order, but I wanted the textarea (open feedback) to appear last, after the scale fields. Claude had added it in the middle.

Claude called field_list to see the current ordering:

field_list --app <appId>
# Response: [name, department, role, manager, start_date, tech, comm, conf_tools, conf_team, conf_process, feedback]
Enter fullscreen mode Exit fullscreen mode

Then field_move:

field_move --app <appId> --id feedback --pos 11
Enter fullscreen mode Exit fullscreen mode

Worked. The feedback field moved to the end.

This was a good test of field_move — it's a tool that seems simple but has edge cases. What if the position is out of range? What if the field doesn't exist? I tested both scenarios manually afterward. Out-of-range position silently clamps to the end. Non-existent field ID returns an error. Reasonable behavior.

Tools used: field_find

I asked Claude to check the scoring on the tech field. It called field_find:

field_find--app<appId>--idtech#Response:{id:"tech",type:"radio",options:[{label:"Beginner",score:1},...]}
Enter fullscreen mode Exit fullscreen mode

The find response was clean and structured. Claude could read the scores and confirm they were correct.

One thing I noticed: the field_find response includes a lot of metadata — field type, validation rules, placeholder text, etc. For a human reading the JSON, it's fine. For an AI parsing it, the extra fields add tokens but don't cause confusion. Claude had no trouble extracting what it needed.

Sunday: Publishing and Cleanup

Tools used: share_publish, share_url, share_query

Publishing was the easiest part. One share_publish call, one share_url call. Done.

I tested share_query to check the publish status:

share_query--app<appId>#Response:{published:true,formType:"all",formPerm:1,formDay:3650000}
Enter fullscreen mode Exit fullscreen mode

The formDay: 3650000 is the "never expires" default I hardcoded in the MCP tool. That's intentional — the AI doesn't need to decide expiry dates for public forms. Safe defaults matter.

Tools used: share_unpublish, app_remove

After testing, I unpublished and deleted the app to clean up. Both worked without issue. The app_remove is permanent — no confirmation prompt, no undo. That's slightly terrifying for an AI-accessible tool, but the whitelist on the backend would block a clear command (which deletes ALL apps). Individual deletes are safe as long as the AI targets the right app ID.

What I Didn't Use

Out of 20 tools, I used 11 in this session:

  • auth_login, auth_status — skipped, I was already logged in
  • app_list, app_get, app_update — didn't need to list or update apps
  • field_update, field_remove, field_set_property — didn't need to modify or remove fields

That's fine. Not every form build needs all 20 tools. The test was whether the tools I did need worked correctly when chained together by an AI agent. They did.

What Broke

Two things went wrong over the weekend:

1. Claude forgot the app ID mid-session. After adding about eight fields, Claude lost track of the app ID it had created at the start. It tried to use field_add without the --app parameter. The error message was clear ("Missing required option: --app"), and Claude recovered by calling app_list to find the app. But it cost three extra API calls and broke the flow.

Fix idea: The MCP server could maintain a "current app" state, similar to how git remembers your current branch. app_get could set the active app, and subsequent field commands could default to it.

Update (v0.2.0): This has been implemented. The server-side SessionContext now maintains the current app ID. When an app is created or used, the session automatically tracks it, and subsequent commands default to the active app.

2. The --filter parameter on field_find returned too many results. When Claude searched for "confidence" (trying to find the scale fields), the filter matched on field titles and returned all three confidence fields plus the textarea (which mentioned "confident" in its placeholder text). Not wrong, but noisier than expected. Claude handled it by reading the response and picking the right fields, but the filter could be more precise — maybe matching on field ID or key instead of title text.

The Scorecard

Tool Category Tools Used This Weekend Worked?
Auth 2 0 N/A (already logged in)
App 5 2 (create, delete)
Field 9 9 (all of them, across the session) ✅ with minor issues
Share 4 4 (all of them)
Total 20 15 15/15 passed

Every tool I used worked. The issues were with Claude's context management (forgetting the app ID) and search precision (filter returning too many results), not with the tools themselves.

The takeaway: 20 tools was enough coverage for a complex form build. In v0.2.0, the architecture was streamlined to 6 layered tools — formlm_generate handles the entire build in one call, while formlm_exec provides direct access for fine-grained control. The gaps aren't in the toolset — they're in the workflow ergonomics (state management, search precision). Those are fixable without adding new tools.


All MCP tools are available in formlm-cli. Open source at github.com/formlm/cli — try the platform at formlm.me.

Source: dev.to

arrow_back Back to Tutorials