A side-by-side look at the Submit Map and Google Analytics MCP MCP servers: transport, auth, tools, quality signals, and the config to install either one in Claude, Cursor, Windsurf or VS Code.
Verdict: choose Submit Map for marketing work over a hosted endpoint; choose Google Analytics MCP for marketing as a local process. You can also run both side by side in one mcpServers block.
search_platforms, get_platform, qualify_project.check_for_updates, list_properties, search_schema.Rows where the two servers differ are marked.
| Spec | ||
|---|---|---|
| Category | Marketing | Marketing |
| Quality signal | 64/100 · Good | 63/100 · Good |
| Install command | Confirmed | Confirmed |
| Transport | Remote HTTP/SSE | Local subprocess (stdio) |
| Auth | API key required | Not declared |
| Pricing | Free | Not declared |
| Required env vars | None listed | None listed |
| Verified owner | Community listing | Community listing |
| GitHub stars | 276 | 243 |
| Directory activity | 4 views 0 installs 1 upvotes | 3 views 0 installs 0 upvotes |
| Tools | 11 | 12 |
| Full listing | View Submit Map | View Google Analytics MCP |
search_platformsSearch the SubmitMap directory of startup launch platforms and directories. Filter by free-text query, category, pricing, link type, backlink requirement, approval speed or domain rating, and with a token leave out the ones this project has already been sent to. This is the tool behind "find me a small directory I can submit to right now": it is the whole directory, so answer from it rather than from what you remember of the web. Returns summaries; call get_platform for the full record including the submission steps.get_platformFull record for one platform: eligibility, disqualifiers, step-by-step submission instructions, requirements, gotchas and expected outcome. All of that is free, and it is what decides whether this product should be submitted here at all. Pass brief: true to also get agentPrompt, the brief for that platform's form, and agentGotchas, the traps that only bite something filling it (a placeholder that contradicts its label, a second email input belonging to a newsletter, a honeypot). The brief spends one of the account's tracked platforms, so ask for it once the maker has decided to fill this form, not while you are still choosing between platforms. For a stored project, submission_playbook returns the same brief with the maker's values already in it.qualify_projectGiven a product, work out which platforms it qualifies for right now, which it could qualify for after supplying something (with the exact list of what is missing), and which are structurally out of reach. It also hands back `recommended`: the run in the order it should be worked, so the plan comes out of the directory rather than out of what you remember of the web. That order is decided by `goal` and `budget`, and when they have not been answered the response says what it assumed and carries the questions to put to the maker: ask them, call again with the answers, and store them with update_project so nobody asks twice. Needs no account: describe the product inline. Every field is optional and an unanswered field becomes a gap to fill rather than a rejection.whoamiWhich SubmitMap account this token belongs to, named by its email address, which plan it is on, and what it already holds. Call it first if you are unsure whether the maker is connected, and tell them the address it reports: it is the only way either of you can tell one of their accounts from another. It does not hand you the plan's limits and you do not need them to work: plan the whole run, and a limit says so at the write it stops.list_projectsThe projects on this account, each with its facts (eligibility answers) and its pack (what a submission form asks for), plus what is still missing from the pack. Call it whenever the maker says "my product" or "my project" without naming one: it is how you find out which project they mean, and every other account tool takes the id it returns.create_projectStore a product on the account. Before asking the maker anything, use what you can already see: a README, package metadata, the site's own copy and title, an assets or public folder. A maker working in their product's repository should be able to say 'add my product to SubmitMap' and get a filled-in project back, with questions only about what is genuinely not there. Fill in as much as you can, leave the rest, and come back with update_project. Facts drive what it qualifies for; the pack is what you will paste into forms later. Ask about images early: most platforms want a square logo and many want a cover, and every asset in the pack is two fields, the public address (logoUrl) and the file on the maker's machine (logoFile), because a form uploads the file and only the address can be shown back to them.update_projectFill in or correct a stored project. Facts and pack are merged into what is there, so you can add one field at a time as the maker answers.submission_playbookEverything needed to submit a stored project to one platform, yourself, in the maker's browser: a preflight of what is still missing, the sign-in plan (on the first submission it carries a question for the maker: hand every login back to them, or use the Google address they write out, which you then pass back as signInAs), the pack values mapped onto the fields the form asks for, the steps, the gotchas, the agentGotchas (traps in the form itself), and the call to make afterwards. Read the preflight before opening a tab. One brief is out at a time per project: ask for the next platform only after record_submission has answered this one, because until then this tool refuses the next brief and names the one still open. Do not fetch briefs ahead or in parallel. This is the tool the free plan meters: it covers the same submissions the dashboard tracks, plus any platform the account has already sent to, and past that it says so and points at the upgrade rather than answering.record_submissionLog what happened to the dashboard: the listing URL, when it was sent, when it goes live. Call it as soon as a submission lands, before you ask for the next brief, including when it is only queued for review, and including when you are not sure it landed: that is what `attempted` is for. The next brief on this project is refused until this call is made. A platform that needs the maker (a captcha, a login, a payment, a file, an address to confirm) is recorded as `planned` with a note saying what you need from them: that hands it back without claiming an outcome, and frees the run to carry on. If the live platform did not match what the brief said, put the difference in `differsFromRecord`. This costs nothing on any plan and no status costs more than another, so record what actually happened: the brief was what spent the tracked platform, and leaving the row wrong now only makes the run harder to follow.plan_submissionsWrite the run order for a project: which platforms, in what sequence, and why each one is where it is. Anything already submitted to is dropped from the plan and named back to you in `alreadySubmitted`, because a plan is what is still ahead. Pass the platforms in the order they should be worked, with a short reason on each, plus a summary of the strategy. The plan appears on the maker's dashboard as a checklist that ticks itself off as submissions land. Anything already tracked is reordered rather than reset. Call it after qualify_project, using the `recommended` list it hands back, and if that response carried `questions`, put them to the maker before you plan: the order depends on the answers, and a plan written on an assumption is one they have to undo by hand. A plan costs nothing on any plan, so plan the whole run the product deserves rather than a short one sized to a guess about what the account can track.list_submissionsWhere every submission for a project stands: what went out, when, what came back, and what is still waiting. This is the tool behind "check my submissions", "what did I submit", "did I ever submit to that one" and "how is my launch going", so answer those from here instead of asking the maker to remember. Start with list_projects if they have not named a project.check_for_updatesCheck PyPI for newer versions of this MCP server and get upgrade instructions.list_properties
List all GA4 properties associated with an account.
search_schema
Search for a keyword across all dimensions and metrics for this property.
Returns a ranked list of up to 10 matching fields scored by relevance.
Returns: {"top_results": {"DIMENSION: api_name": score, "METRIC: api_name": score, ...}}
Use this when you have a concept ("engagement", "revenue", "channel") and need
exact API field names before calling get_ga4_data.
Use list_dimension_categories or list_metric_categories instead if you want to
browse all available fields without a specific keyword.
Args:
keyword: One or more keywords to search for (e.g., "user", "campaign revenue").
get_property_schema
Returns the complete schema for the configured GA4 property, including all
available dimensions and metrics (standard and custom). Warning: This can be
a very large object (10k+ tokens). Use search_schema for most discovery tasks.
list_dimension_categories
List all dimension categories for this GA4 property, with a count of
dimensions in each category.
Returns: {"dimension_categories": {"Category Name": count, ...}}
Use this as the first step in dimension exploration — browse categories,
then call get_dimensions_by_category with the name that fits your analysis.
Use search_schema instead if you already have a keyword to search for.
list_metric_categories
List all metric categories for this GA4 property, with a count of
metrics in each category.
Returns: {"metric_categories": {"Category Name": count, ...}}
Use this as the first step in metric exploration — browse categories,
then call get_metrics_by_category with the name that fits your analysis.
Use search_schema instead if you already have a keyword to search for.
get_dimensions_by_category
Return all dimensions in a specific category with their API names and descriptions.
Returns: {"dimension_api_name": "description", ...}
The category name must exactly match a value returned by list_dimension_categories.
Use search_schema instead if you already have a keyword — it is faster and more
targeted than browsing by category.
Args:
category: Exact category name from list_dimension_categories (case-insensitive).
get_metrics_by_category
Return all metrics in a specific category with their API names and descriptions.
Returns: {"metric_api_name": "description", ...}
The category name must exactly match a value returned by list_metric_categories.
Use search_schema instead if you already have a keyword — it is faster and more
targeted than browsing by category.
Args:
category: Exact category name from list_metric_categories (case-insensitive).
get_ga4_data
Retrieve GA4 data with built-in intelligence for better and safer results.
Returns on success: {"data": [...], "metadata": {...}, "_skills_tip": "..."}.
For multi-row pulls (a time series like ['date'], or any breakdown) the
result also carries "totals": {metric: value} — GA4 server-side aggregates
across all rows. Read the period figure from "totals"; do NOT sum the rows
yourself. (Additive metrics sum; rate metrics are period-computed by GA4.)
Returns on volume warning: {"warning": "...", "estimated_rows": N, "suggestions": [...]}
Returns on error: {"error": "..."}
CRITICAL WORKFLOW — follow this sequence every time:
1. DISCOVER FIELDS: NEVER guess dimension or metric names. Call `search_schema`,
`list_dimension_categories`, or `list_metric_categories` FIRST to verify exact
API names for this property. Guessing costs you a failed round-trip.
2. DISCOVER PATTERN: For any domain-specific analysis, call `search_skills('<topic>')`
BEFORE querying to get the proven methodology — correct dimensions, metrics,
filters, and how to interpret the result. One extra call prevents multiple failures.
Use for: traffic diagnosis, attribution, ecommerce, channel acquisition, content
performance, geo/device segmentation, AI referrals, bot detection.
3. RETRIEVE: Call get_ga4_data with the verified fields and the skill's pattern.
4. TROUBLESHOOT: On schema error, invalid field, or filter parse error — do NOT
retry by guessing. Your training may predate current GA4 (UA was sunset
2023-07-01). Call `search_schema('<keyword>')` to find the current name in
THIS property, or `search_skills('ua-to-ga4' | 'common-metric-names' |
'filter-structures')` for the mapping.
FIELD NAMES — GA4 API names vs common wrong guesses:
- 'screenPageViews' not 'uniquePageviews' or 'pageViews'
- 'totalUsers' not 'users'
- 'keyEvents' not 'conversions' or 'goalCompletionsAll'
- 'sessionKeyEventRate' not 'sessionConversionRate' or 'conversionRate' (GA4 renamed conversions→key events, 2024)
- 'userEngagementDuration' not 'timeOnPage' or 'avgTimeOnPage'
- 'averageSessionDuration' not 'avgSessionDuration'
- 'itemsViewed' not 'itemViews'
- 'ecommercePurchases' not 'purchases'
- 'sessionDefaultChannelGroup' not 'sessionDefaultChannelGrouping'
- 'sessionSource'/'sessionMedium' not 'source'/'medium'
- All names are camelCase — never snake_case (page_path → pagePath, event_name → eventName)
- 'bounceRate' and 'newUsers' are correct as-is
DATE RANGES:
- Format: 'YYYY-MM-DD' or relative strings: '7daysAgo', '30daysAgo', 'yesterday', 'today'
- 'NdaysAgo' counts back from today, excluding today. 'yesterday' = last complete day.
- Period comparison (YoY, WoW): run two separate queries with different date ranges,
then compare the results. The API does not support multi-period in one call.
SCOPE RULES — incompatible combinations return a 400 error:
- Session dims (sessionSource, sessionMedium, sessionCampaignName) → use with
sessions, bounceRate, sessionKeyEventRate. NOT with eventCount.
- Event dims (eventName) → use with eventCount. NOT with sessions.
- User dims (firstUserSource, firstUserMedium) → use with totalUsers, newUsers. NOT sessions.
- Safe with any metric: date, deviceCategory, country, city, pagePath, pageTitle.
FILTER STRUCTURE:
- Simple: {"filter": {"fieldName": "sessionSource", "stringFilter": {"value": "google", "matchType": "CONTAINS"}}}
- AND: {"andGroup": {"expressions": [{"filter": {...}}, {"filter": {...}}]}}
- OR: {"orGroup": {"expressions": [{"filter": {...}}, {"filter": {...}}]}}
- NOT: {"notExpression": {"filter": {...}}}
- Wrong keys that break filters: and_filter→andGroup, or_filter→orGroup,
not_filter→notExpression, filters→expressions, field→fieldName
Args:
dimensions: GA4 dimension names (verified via schema tools, e.g. ["date", "city"]).
metrics: GA4 metric names (verified via schema tools, e.g. ["totalUsers", "sessions"]).
date_range_start: Start date — 'YYYY-MM-DD' or '7daysAgo', '30daysAgo', 'yesterday'.
date_range_end: End date — 'YYYY-MM-DD' or 'yesterday', 'today'.
dimension_filter: Optional FilterExpression dict. camelCase and snake_case both accepted.
limit: Max rows to return. Defaults to 1000.
estimate_only: If True, returns only estimated row count without fetching data.
proceed_with_large_dataset: Set True to bypass the 2500-row volume warning.
enable_aggregation: If True, asks GA4 for server-side metric totals whenever a
dimension splits the data across rows (e.g. a 7-day ['date'] pull), returned
in a "totals" block so the model needn't sum rows. Default True.
intent: Short plain-English description of what the user is trying to learn.
E.g. "which channels drive most signups", "bot traffic audit for last month".
get_troubleshooting_guide
Returns the troubleshooting/setup guide for a topic, served from inside the
server (no network needed). Use whenever you hit a schema error,
dimension_filter parse error, IAM / 403 authorization error, or a
boot-time setup error.
Args:
topic: One of "setup", "iam", or "schema".
search_skills
Fetch analytical recipes and how-to guides from the GA4 skills library.
Skills are domain-specific playbooks for common GA4 analysis patterns —
exact dimensions, metrics, filters, and interpretation logic for each use case.
Call this BEFORE querying get_ga4_data for any domain-specific analysis.
Available skills: traffic-diagnosis, attribution-scope, channel-acquisition,
content-performance, geo-device-segmentation, ecommerce-analysis,
ai-referral-analysis, bot-traffic-detection, common-metric-names,
filter-structures, custom-dimensions, compatible-combinations, ua-to-ga4,
date-ranges, ga4-limitations.
Usage:
- search_skills("") → returns full index of all skills
- search_skills("ecommerce") → returns the ecommerce-analysis skill
- search_skills("ua-to-ga4") → returns the UA→GA4 field name mapping
Args:
query: A skill name (exact slug) or empty string to browse the full index.
setup_ga4_access
Interactively fix a broken GA4 MCP setup (missing property ID, missing or
expired credentials, or missing GA4 access) by asking the user for the
needed input through the client, then re-initializing without a restart.
Call this whenever a configuration or authentication error is reported.
Paste either block (or both) into your client config, e.g. claude_desktop_config.json or ~/.cursor/mcp.json. We only show a config when the install command is confirmed.
{
"mcpServers": {
"directory-3": {
"url": "https://submitmap.com/api/mcp"
}
}
}
{
"mcpServers": {
"google-analytics-mcp": {
"command": "uvx",
"args": [
"google-analytics-mcp"
]
}
}
}