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
Related MCP Servers
- Alicense-qualityAmaintenanceMCP server providing token-efficient access to OpenAPI/Swagger specs via MCP Resources for client-side exploration.Last updated26876MIT
- AlicenseAqualityDmaintenanceU301 URL Shortener MCP ServerLast updated133JavaScriptMIT
- AlicenseAqualityBmaintenanceMCP server for managing deep links, analytics, and app configuration on Grovs. Works with Claude Code, Cursor, Windsurf, and any MCP-compatible client.Last updated162MIT

@linklee/mcp-serverofficial
AlicenseAqualityDmaintenanceMCP server for Linklee that enables creating short links, listing links, getting click analytics, and managing workspace tags.Last updated429MIT
Related MCP Connectors
MCP server for URL shortening and management
Official PicSee MCP server for short links, link management, click analytics, and optional OAuth.
Official in.bio MCP server: shorten URLs, styled QR codes, and click analytics. No API key needed.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- 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