utm-helper-mcp
This server helps teams create and validate consistent UTM-tagged links by centralizing an authoritative, version-controlled UTM spec and campaign registry. It exposes three MCP tools:
get_guidelines(): Fetches the human-readable UTM tagging guide (GUIDE.md) from the configured Git spec source. Fails loudly if the source is unreachable rather than falling back to any built-in content.validate_url(url): Normalizes and validates a URL's UTM parameters against the spec, returning:The normalized URL with UTM parameters corrected (e.g., lowercase, hyphen-separated)
A changelog of all fixups made
Soft nudges for valid-but-unregistered values
Hard errors for invalid
utm_mediumvalues, malformedutm_campaignnames, or missing required parameters
list_campaigns(): Returns known recurring campaigns by reading fresh data from a Google Sheet registry on every call (no caching), with schema validation on each read to ensure data integrity.
Key behaviors: enforces consistent UTM formatting, distinguishes between open enums (utm_source), closed enums (utm_medium), and free-form fields (utm_content, utm_term), and fails loudly on misconfigurations rather than silently serving incorrect content.
Fetches UTM spec files (GUIDE.md, utm-spec.yaml) from a GitHub raw URL to define and validate UTM parameters.
Fetches UTM spec files (GUIDE.md, utm-spec.yaml) from a GitLab raw URL to define and validate UTM parameters.
Reads campaign registry from a Google Sheet to validate utm_campaign parameter values.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@utm-helper-mcpgenerate a UTM link for the product launch campaign"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
UTM Helper MCP server
An Arcade MCP server that helps teams produce consistent UTM-tagged links by reading an authoritative, version-controlled UTM spec and exposing it through MCP tools.
This repo is both the spec source of truth (GUIDE.md,
utm-spec.yaml) and the server that serves it. See the
PRD for the full design.
Status: in progress.
get_guidelines(),validate_url(), andlist_campaigns()are implemented.build_urlandadd_campaignland in later issues.
Tools
Tool | Description |
| Returns the human-readable UTM guide ( |
| The mandatory last hop: normalizes a URL's UTM parameters against the spec and validates them, returning the normalized URL, a changelog of fixups, and any soft nudges. |
| Returns the known recurring campaigns, read fresh from the campaign registry Google Sheet (via a shared service credential). Validates the Sheet's column schema on every read. |
validate_url — normalize + validate (the last hop)
validate_url reads the structured spec (utm-spec.yaml) from the configured
Git source and applies it end-to-end to a URL:
Normalize every UTM value deterministically (lowercase, hyphen-separated, no spaces; the structural
_inutm_campaignis preserved). Each change is reported in achangelog.Validate, with per-field behavior asymmetry straight from the spec:
Param
On unknown value
utm_source(open enum)Emits the normalized link + a nudge to add the source to the spec repo.
utm_medium(closed enum)Hard refuse (raises): shows the valid set + closest suggestion; no link is emitted.
utm_campaign(sheet)Must match the
YYYY-qN_kebab-slugtemplate; a malformed name hard-errors. Registry membership is checked once the campaign registry lands.utm_content/utm_term(free)Always shape-normalized; never refused.
A missing required parameter (utm_source, utm_medium, utm_campaign)
also hard-refuses. Non-UTM query params are passed through untouched.
On success it returns { "url", "changelog": [{param, from, to}, ...], "nudges": [...] }.
Hard refusals and spec-source failures surface as loud tool errors — it never
falls back to a built-in spec.
Related MCP server: U301 URL Shortener MCP Server
Setup (admin)
Requires Python ≥ 3.12 and uv.
uv sync --extra dev # install runtime + dev deps into .venvConfiguration — pointers only, via an Arcade secret
Configuration only says where to look for the spec; it never encodes behavior. All behavior (enums, shape rules, casing) lives in the spec repo and changes by reviewed PR.
The single pointer is provided as the Arcade tool secret
UTM_SPEC_SOURCE_URL — it is not hard-coded in the server. The tool reads it
from its injected Context at call time (context.get_secret(...)).
Secret | Meaning |
| Raw base URL of the Git spec source. The server appends file names (e.g. |
| The campaign registry Google Sheet ID (the token in the Sheet URL between |
| A1 tab/range to read. Optional; defaults to |
| The full Google service-account key JSON. The shared server-side credential used to read the Sheet — so end users need no direct Sheet access. Required for |
Campaign Sheet setup (admin)
The utm_campaign registry lives in a Google Sheet, read with a shared
service-account credential so end users need no direct Sheet/Google access:
Create a Google Cloud service account and download its key JSON → set as
UTM_CAMPAIGN_SA_JSON.Enable the Google Sheets API for that project.
Share the registry Sheet (Viewer) with the service account's email.
The Sheet's first row must be exactly:
campaign | description | added_by | added_at.
The schema is validated on every read; a mismatched header or malformed row fails loud with a row-numbered, actionable error (see Failure behavior).
Set it where it belongs for your environment:
Production: configure it on the Arcade dashboard, or with
arcade secret set UTM_SPEC_SOURCE_URL <url>.Local dev: put it in
.env(copy.env.example) — Arcade discovers.envand injects the secret into the toolContext.
First run / no config: if the secret is unset, the tool uses the opinionated
default seed shipped in this repo
(https://raw.githubusercontent.com/ArcadeAI/utm-helper-mcp/main/) and logs a
warning. This is the only case where a default is used — see Failure
behavior below.
Run
# stdio (Claude Desktop, CLI clients)
uv run src/utm_server/server.py
# http (Cursor, VS Code)
uv run src/utm_server/server.py http # serves on http://127.0.0.1:8000/mcp/Caching
Successful spec reads are cached in memory for 5 minutes
(SPEC_CACHE_TTL_SECONDS in sources.py). The
spec is slow-changing (reviewed-PR cadence), so this trades a little staleness
for far fewer network round-trips. The cache is per-process and per-file; a
restart clears it. The TTL is a code constant, not configuration, to keep config
strictly pointers-only.
The campaign Sheet is the opposite: it churns constantly, so list_campaigns()
reads it fresh on every call (no result cache) — newly added campaigns appear
immediately. Only the CampaignRegistry object (HTTP client + minted token) is
reused across calls.
Failure behavior — fail loud, never silently default
If the spec source is unreachable, returns a non-2xx status, or the file is missing, the tool raises a specific error naming the URL and failure and telling the user to contact the admin. It never falls back to a built-in or stale guide — a misconfigured deploy must look broken, not quietly serve the wrong content. The single announced exception is the first-run default seed source above (which is still fetched over the network like any other source).
The campaign Sheet is human-editable, so list_campaigns() defensively
validates its schema on every read: the header row must be exactly
campaign | description | added_by | added_at, and each data row is checked. On
a mismatch (wrong headers, extra columns, a row missing its campaign name) it
raises a specific, row-numbered error ("…headers don't match… fix the header
row (row 1)…", "…row 7 is missing a campaign name…") and returns no campaigns —
it never silently guesses what a column means.
Development
uv run pytest # tests
uv run ruff check . # lint
uv run mypy src # type-checkMaintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Tools
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/ArcadeAI/utm-helper-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server