Skip to content

[DEVPL-4067] Add data_custom_code_tool for freeform head/footer custom code#131

Closed
bertenator wants to merge 1 commit into
mainfrom
albertchang/freeform-custom-code-mcp-tool
Closed

[DEVPL-4067] Add data_custom_code_tool for freeform head/footer custom code#131
bertenator wants to merge 1 commit into
mainfrom
albertchang/freeform-custom-code-mcp-tool

Conversation

@bertenator

@bertenator bertenator commented Jun 8, 2026

Copy link
Copy Markdown
Member

Summary

Adds a data_custom_code_tool that exposes Webflow's freeform custom code (raw <head> / footer code blocks on a site or page) through the MCP server. The Data API endpoints /v2/{sites|pages}/:id/custom_code/freeform[/:location] are live, but no MCP tool wrapped them, so MCP clients had no way to read or write freeform head/footer code.

This is distinct from the existing data_scripts_tool, which manages registered scripts applied to a site/page. Freeform code is an arbitrary code block stored directly on the site or page.

What's added

One data_custom_code_tool (actions array, consistent with the other data tools):

  • get_site_freeform_code — GET a site's head/footer freeform code (omit location for both)
  • set_site_freeform_code — PUT (atomic replace) a site's head or footer
  • get_page_freeform_code — GET a page's head/footer freeform code (optional locale_id)
  • set_page_freeform_code — PUT (atomic replace) a page's head or footer

How it works

Mirrors the existing data_workflows_tool: a thin authenticated fetch to the Data API using the caller's OAuth token. All access control — read/write scopes, the Custom Code entitlement, and the feature gate — is enforced API-side, so this stays a thin wrapper with no SDK change.

How to test

Prereqs: connect the MCP server to a client (Claude Desktop / Cursor / MCP Inspector) with a token that has sites:read|write + pages:read|write and a workspace/site with the Custom Code entitlement. Grab a SITE_ID and a PAGE_ID (e.g. via data_sites_tool / data_pages_tool).

Natural-language prompts:

  1. Read the freeform head and footer custom code on page PAGE_ID.
  2. Add <meta name="robots" content="noindex"> to the head custom code on page PAGE_ID, then read it back to confirm.
  3. Put a small console.log script in the footer custom code of page PAGE_ID.
  4. Show the site-wide freeform head and footer code for site SITE_ID.
  5. Add an Inter Google Font <link> tag to the site-wide head code for site SITE_ID.
  6. Clear the footer code on page PAGE_ID by setting it to an empty string.

Direct tool calls (MCP Inspector):

{ "actions": [ { "get_page_freeform_code": { "page_id": "PAGE_ID" } } ] }
{ "actions": [ { "set_page_freeform_code": { "page_id": "PAGE_ID", "location": "head", "content": "<meta name=\"robots\" content=\"noindex\">" } } ] }
{ "actions": [ { "set_site_freeform_code": { "site_id": "SITE_ID", "location": "head", "content": "<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Inter&display=swap\">" } } ] }
// batch: write then read back in one call
{ "actions": [
  { "set_page_freeform_code": { "page_id": "PAGE_ID", "location": "footer", "content": "<script>console.log('hello')</script>" } },
  { "get_page_freeform_code": { "page_id": "PAGE_ID", "location": "footer" } }
] }

What to verify:

  • Round-trip: a set_* followed by a get_* returns the same content.
  • get_* with no location returns both head and footer; with location returns just that block.
  • Writes are atomic replaces (the previous content for that location is fully replaced).
  • Error surfacing (returned cleanly via the tool's error handler, not a crash):
    • token missing *:write scope → scope/authorization error
    • site/workspace without the Custom Code entitlement → 402/forbidden
    • set_page_freeform_code with a non-primary locale_id → validation error (page freeform code is single-locale today)
    • content over the custom-code character limit → validation error
  • At least one valid action per call is required (the schema rejects an empty action object).

Testing (build)

  • npx tsc --noEmit — passes, no type errors
  • npm run build (tsup) — succeeds

(No test suite exists in this repo; verification is typecheck + build.)

Follow-up

  • The AI tool-reference doc that currently lists custom code as "Not Supported" lives outside this repo and should be updated separately.

Jira

🤖 Generated with Claude Code

Wrap the freeform Custom Code Data API
(/v2/{sites|pages}/:id/custom_code/freeform[/:location]) behind a new
data_custom_code_tool with get/set actions for site and page head/footer
code. Mirrors the existing authenticated-fetch pattern (workflows.ts); the
API enforces scopes, the Custom Code entitlement, and the feature gate, so
the tool is a thin token-forwarding wrapper.

The underlying Data API (DEVPL-4351, rolled out to 100%) was live but had no
MCP tool exposing it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@bertenator bertenator self-assigned this Jun 8, 2026
@bertenator bertenator marked this pull request as ready for review June 8, 2026 17:34
@bertenator bertenator requested a review from a team as a code owner June 8, 2026 17:34
@bertenator bertenator requested review from denisemauldin, tterb and viratatwebflow and removed request for a team June 8, 2026 17:34

@viratatwebflow viratatwebflow left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bertenator bertenator closed this Jun 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants