Skip to content
verify mcp Beta VerifyMCP is currently in beta. If you notice any issues, get in touch and we’ll put it right.

GTM Otto

REMOTE · AGENT.GTMOTTO.COM · SCANNED SEP 23

Self-driving LinkedIn prospecting: it sources, warms and invites daily. You approve every message.

75 Trust /100
Trust breakdown (7 categories)

How this component scores in each security and reliability category. Every signal is checked automatically against the live server, and we only credit what we can confirm. How we score → Why this is hard to score →

Endpoint Security81
Transport & Reachability100
Schema Quality & AI Usability80
  • 100% of prompts and resources have a non-trivial description (not blank, and not just the item's name).Pass
  • AI-judged instruction clarity (excellent).Pass
  • Context-footprint check failed: tool/resource definitions use about 4949 tokens (~224/item across 22 items; 21 tools + 1 resources), over budget; trim descriptions and params. See how to fix → Fail
  • Usage-examples check failed: none of the tools include examples. See how to fix → Fail
Stability & Change Management3
  • Stability observed for 1 of 30 days with no destabilising changes; credit accrues until the full window elapses.Partial
Tool Coverage95
  • 100% of tools have a non-trivial description (not blank, and not just the tool's name).Pass
  • 84% of tool parameters carry a description.Partial
Tool Safety100
  • No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.Pass
  • We read all 21 captured tool definition(s), and no name or description among them implies an irreversible operation.Pass
  • An AI judge read all 23 captured unit(s) of tool text and found none that tries to manipulate the model reading it.Pass
Capabilities100
  • Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.Pass
Install

How do I install the GTM Otto MCP server?

GTM Otto is a hosted endpoint at https://agent.gtmotto.com/mcp, so there is nothing to install locally. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

remote · agent.gtmotto.com

# add to Claude Code
claude mcp add --transport http com-gtmotto-linkedin 'https://agent.gtmotto.com/mcp'
// .cursor/mcp.json
{
  "mcpServers": {
    "com-gtmotto-linkedin": {
      "url": "https://agent.gtmotto.com/mcp"
    }
  }
}
// .vscode/mcp.json
{
  "servers": {
    "com-gtmotto-linkedin": {
      "type": "http",
      "url": "https://agent.gtmotto.com/mcp"
    }
  }
}
# ~/.codex/config.toml
[mcp_servers.com-gtmotto-linkedin]
url = "https://agent.gtmotto.com/mcp"
// opencode.json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "com-gtmotto-linkedin": {
      "type": "remote",
      "url": "https://agent.gtmotto.com/mcp",
      "enabled": true
    }
  }
}
# add to OpenClaw
openclaw mcp add com-gtmotto-linkedin --url 'https://agent.gtmotto.com/mcp' --transport streamable-http
# ~/.hermes/config.yaml
mcp_servers:
  com-gtmotto-linkedin:
    url: "https://agent.gtmotto.com/mcp"
// ~/.netclaw/config/netclaw.json
{
  "McpServers": {
    "com-gtmotto-linkedin": {
      "Transport": "http",
      "Url": "https://agent.gtmotto.com/mcp"
    }
  }
}
# add to Vellum
assistant mcp add com-gtmotto-linkedin -t streamable-http -u 'https://agent.gtmotto.com/mcp'
// mcp.json
{
  "mcpServers": {
    "com-gtmotto-linkedin": {
      "type": "http",
      "url": "https://agent.gtmotto.com/mcp"
    }
  }
}

The mcpServers block is a cross-client convention. Remote transports vary, so check your client's docs.

Changelog

Every change we have recorded for this component, newest first. Security-relevant changes are always shown. ▲ marks a change for the better, ▼ a change for the worse; unmarked changes are neutral.

  • 23 Sept 26 +1
    • Stability: unverified → 0.03 functional
  • 22 Sept 26 74

    First indexed and scored.

Diagnostics

Diagnostic detail from the automated scan of this channel: what the scanner observed at each step, so you can see exactly where a check passed or failed. It is informational only and never changes the trust score.

Captured 23 Sept 2026 · Probed https://agent.gtmotto.com/mcp

TLS valid

Negotiated TLS 1.3 with TLS_AES_128_GCM_SHA256 .

Subject Issuer Valid from Valid until Key Signature Serial
CN=gtmotto.com CN=WE1,O=Google Trust Services,C=US 7 Sept 2026 6 Dec 2026 ECDSA 256 ECDSA-SHA256 99eb1bec8ee62ca00e9649dbccd7c594
SANs: gtmotto.com, agent.gtmotto.com, *.agent.gtmotto.com
CN=WE1,O=Google Trust Services,C=US (CA) CN=GTS Root R4,O=Google Trust Services LLC,C=US 13 Dec 2023 20 Feb 2029 ECDSA 256 ECDSA-SHA384 7ff31977972c224a76155d13b6d685e3
CN=GTS Root R4,O=Google Trust Services LLC,C=US (CA) CN=GlobalSign Root CA,OU=Root CA,O=GlobalSign nv-sa,C=BE 15 Nov 2023 28 Jan 2028 ECDSA 384 SHA256-RSA 7fe530bf331343bedd821610493d8a1b

Background: What to check on a remote MCP endpoint →

DNSSEC insecure

Validation of agent.gtmotto.com. Not signed

Zone DS Keys Algorithms Outcome
. trust_anchor 20326, 38696 8, 8 Verified
com. present 19718 13 Verified
gtmotto.com. absent Unsigned (proven) parent-signed NSEC/NSEC3 proves an unsigned delegation
Authentication Enforced and verified

The endpoint asked for a token and published valid RFC 9728 metadata describing how to get one.

Result Enforced and verified
Enforced On tool calls
HTTP status 200

WWW-Authenticate challenge Bearer error="invalid_token", error_description="Sign in to call __verifymcp_auth_probe_c152681fb183ef65__", resource_metadata="https://agent.gtmotto.com/.well-known/oauth-protected-resource/mcp", scope="profile email"

Bearer error="invalid_token", error_description="Sign in to call __verifymcp_auth_probe_c152681fb183ef65__", resource_metadata="https://agent.gtmotto.com/.well-known/oauth-protected-resource/mcp", scope="profile email"

Protected resource metadata

Document https://agent.gtmotto.com/.well-known/oauth-protected-resource/mcp
Retrieved Yes
Resource https://agent.gtmotto.com/mcp
Authorisation server https://clerk.gtmotto.com

Background: How OAuth 2.1 works in the 2026 MCP spec →

Transports 2 probes
Transport URL Outcome Status Location
streamable-http https://agent.gtmotto.com/mcp Verified 200
http (plaintext) http://agent.gtmotto.com/mcp Inconclusive 406
MCP tools · 21 exposed · ~4,474 tokens

The tools this component advertises to a client, with an estimated token cost for each. Expand a tool to see its parameters and schema. The per-tool counts are indicative and are not scored directly; the schema's total context footprint is one signal in Schema Quality & AI Usability. A tool's description is untrusted text the model reads on every call, which is what makes this list a security surface and not just an inventory: how tool poisoning works →

Tool Tokens
gtm_approve ~96

Approve ONE drafted action by its approvalId (from gtm_list_approvals). It then leaves on the seat's next slot, inside LinkedIn's limits — this is the human's tap, moved from the cockpit to the chat, so ONLY call it after showing them the draft and getting a yes in this conversation. An already-decided card is refused.

NameTypeReqDescription
approvalIdstringyesThe card to approve.

No output schema declared.

No examples provided.

gtm_configure_source ~444

Write a lane's settings — upload an account list, set the Post-discovery intent and anchors, change the job keywords, move a gate. MERGE semantics: only the fields you send change. Lists (companies, postUrls, competitors) REPLACE by default; pass listMode 'append' to add to what is there (deduped, case-insensitive). Caps: 100 companies, 25 posts, 25 anchors — what did not fit comes back in `truncated`. Unknown fields are refused. Saving creates the lane's row if it never had one (OFF, because configuring is not switching on), makes the lane due again immediately and resets its retry backoff; on account_list it also forgets the cached misses so unfound companies are looked up again. Pass `on: true` to switch it on in the same call — refused, with nothing saved, if the lane would still have nothing to run on. Nothing runs inside this call: an ON lane on an ACTIVE play runs on the next tick (≤5 min); otherwise use gtm_run_source. Read gtm_sources_reference for each field's meaning.

NameTypeReqDescription
configobjectyesThe fields to change. See gtm_sources_reference.
keystringyesWhich lane. people_search: LinkedIn people search compiled from the play's ICP (+ optional free-text keywords). | competitor_posts: Post discovery: Otto searches LinkedIn daily for posts matching an…
listModestringHow list fields combine with the current value. Default replace.
onbooleanAlso switch the lane on/off. Omit to leave the switch alone.
playIdstringyesThe play, from gtm_list_plays.

No output schema declared.

No examples provided.

gtm_connect_linkedin ~123

The link a human opens to connect (or reconnect) their LinkedIn account to a play — the one cockpit step the play cannot run without. Hand the URL to the human, tell them to say when it is done, then call gtm_linkedin_status: the seat reads `classic` or `sales_navigator` once bound. The link expires in 15 minutes; call again for a fresh one. Nothing is sent, nothing is read from their account by this call.

NameTypeReqDescription
playIdstringyesThe play to bind the seat to.

No output schema declared.

No examples provided.

gtm_create_play ~494

Create a fully-configured play in one call: the product (an existing productId, or a websiteUrl which seeds one and kicks its research), the play itself, the ICP facets, which sourcing lanes are on, the autonomy ladder and the caps. Three rules are enforced server-side and cannot be talked around. (1) `activate` DEFAULTS TO FALSE — turning a play on spends the month's lead quota and takes real actions from the user's own LinkedIn account, so hand the human the returned cockpit link and let them press go. (2) A play with no live LinkedIn seat stays a draft whatever you pass. (3) Autonomy rules are written ONLY for the steps you name; every step you omit keeps gtmotto's own default, which is 'wait for a human'. Lanes that need their own seeds (post_engagers, competitor_posts, account_list) are refused if switched on without them.

NameTypeReqDescription
activatebooleanDefault false. True only starts it if a live seat is already connected.
autonomyarrayOnly the rungs you name are written. Omitted rungs wait for a human.
dailyCapnumberActions per day, all kinds summed. Defaults to the seat's full LinkedIn allowance (145), so pass it only to throttle. ACCOUNT-level: sets the ceiling every play on this LinkedIn account shares, not a…
goalstringWhat a conversation from this play should reach.
icpobjectThe audience, as plain strings — the same seven fields the cockpit's ICP screen fills. gtmotto compiles them into a LinkedIn search and normalises odd labels itself ('Swiss' → Switzerland, 'Fintech'…
namestringyesThe play's name.
productIdstringAn existing product. Omit to create one.
productNamestringDefaults to the website's hostname.
sourcesarrayThe sourcing lanes to configure.
voicestringHow the messages should sound.
websiteUrlstringSeeds a NEW product from this website and kicks its research.
workingHoursobjecte.g. { start: '09:00', end: '18:00', tz: 'Europe/Paris' }. ACCOUNT-level, like dailyCap.

No output schema declared.

No examples provided.

gtm_estimate_icp ~167

How big an audience a CANDIDATE ICP would reach — '≈ N companies match' — before committing to it. Writes nothing: no play, no ICP row, no cache. Use it to iterate (propose facets → see 40 → widen the locations → see 900 → then create the play). Returns null when the data provider has no key configured, which is not an error. Needs a write-scoped key only because it rides the gateway's action channel.

NameTypeReqDescription
industriesarraye.g. ['SaaS', 'Fintech']
locationsarraye.g. ['France', 'Benelux']
sizesarrayHeadcount bands, e.g. ['11-50', '51-200']

No output schema declared.

No examples provided.

gtm_forget_harvested_posts ~280

The tuning escape hatch for the post lanes. A post lane remembers every post it already paid to harvest so it never buys the same engagers twice — which makes it impossible to TUNE: change the intent or a gate, run, and the answer is 'no new posts since last time'. This clears that memory so the next run (or gtm_run_source) judges today's posts against the new settings. It COSTS: every forgotten post is a fresh engager fetch. Call it only after a deliberate settings change the human wants re-tested. Only competitor_posts and post_engagers have this memory.

NameTypeReqDescription
keystringyesWhich lane. people_search: LinkedIn people search compiled from the play's ICP (+ optional free-text keywords). | competitor_posts: Post discovery: Otto searches LinkedIn daily for posts matching an…
playIdstringyesThe play, from gtm_list_plays.

No output schema declared.

No examples provided.

gtm_linkedin_status ~122

The play's LinkedIn seat and today's headroom against the LinkedIn limits. States: `no_seat` (nothing connected — the play cannot act), `seat_broken` (the account was disconnected or deleted; the queue is parked until a human reconnects), `pending`, `classic`, or `sales_navigator` (which search backend the ICP compiles to). Read-only. Connecting a seat is a browser flow — link the human to the cockpit for it.

NameTypeReqDescription
playIdstringyesThe play whose seat to check.

No output schema declared.

No examples provided.

gtm_list_approvals ~111

Every drafted action that waits for the human — an invite note, a first message, a follow-up, a reply or a comment the planner wrote — with the draft, the 'why now', the lead, and a cockpit link. One play, or the whole workspace when playId is omitted. Show each draft to the human before gtm_approve / gtm_reject; never decide for them. Read-only.

NameTypeReqDescription
playIdstringOne play; omit for all.

No output schema declared.

No examples provided.

gtm_list_plays ~74

Every play in the workspace with enough context to route on: which product it sells, whether it is active or paused, whether a LinkedIn seat is actually connected (`seatConnected: false` means the play cannot act, whatever its status says), and how its leads are spread across the pipeline stages. Read-only.

Input schema present but exposes no named parameters.

No output schema declared.

No examples provided.

gtm_play_stats ~102

The play's Stats screen: the pipeline (leads sourced → warmed → invited → new connections → conversations started) over four windows — all time, last 30 days, last 7 days, last 24 hours — each windowed count paired with the window before it as a percentage delta. 'Conversations started' counts real replies, never the openers Otto sent. Read-only.

NameTypeReqDescription
playIdstringyesThe play to report on.

No output schema declared.

No examples provided.

gtm_preview_source ~345

Dry-run ONE lane: '≈ N people match' and a 10-person sample, WITHOUT inserting a lead, spending the day's budget or moving the run clock. Same query, same gates, same scorers as a real run, on up to 50 people — so what it shows is what a run would keep. `size.precision` is honest: 'exact' (the query ran out inside the probe), 'atLeast' (the probe filled up; LinkedIn reports no total) or 'unavailable' (no seat, no config, paused play — `skipped` says which). Reports the ICP facets LinkedIn could not filter on (`unmapped`), facet values it had no id for (`unresolved`), which sampled people are `alreadyALead`, and on account_list what each company resolved to. Costs provider calls and, when a gate is on, model tokens. Refused on a paused play.

NameTypeReqDescription
keystringyesWhich lane. people_search: LinkedIn people search compiled from the play's ICP (+ optional free-text keywords). | competitor_posts: Post discovery: Otto searches LinkedIn daily for posts matching an…
playIdstringyesThe play, from gtm_list_plays.

No output schema declared.

No examples provided.

gtm_reject ~69

Reject (skip) ONE drafted action by its approvalId. Nothing is sent; the planner records the decline and re-plans around it instead of proposing the same step again. Ask the human first, as for gtm_approve.

NameTypeReqDescription
approvalIdstringyesThe card to reject.

No output schema declared.

No examples provided.

gtm_run_source ~331

'Run once now' — run ONE lane immediately through the same code the sourcing tick runs, with force: an OFF lane still runs (that is what run-once means while configuring), a DRAFT play still runs (how you try a lane before launch), a PAUSED play does not. REAL: provider calls are made, leads are inserted, the play's daily budget and the org's monthly lead quota are spent. Only call it when the human asked for a run in this conversation; to look without spending, use gtm_preview_source. Returns the run's funnel (scanned / dropped / droppedIntent / added / duplicates / capped), the unmapped ICP facets, and `skipped` + `reason` when it could not run (no_seat, needs_config, icp_empty, daily_cap, lead_quota, no_results, provider_error).

NameTypeReqDescription
keystringyesWhich lane. people_search: LinkedIn people search compiled from the play's ICP (+ optional free-text keywords). | competitor_posts: Post discovery: Otto searches LinkedIn daily for posts matching an…
playIdstringyesThe play, from gtm_list_plays.

No output schema declared.

No examples provided.

gtm_set_source ~270

Switch a lane on or off — the toggle on the Sources list. Switching ON a lane that has nothing to run on (no companies, no intent/anchors/posts, no role) is refused: configure it first. An empty ICP is not a refusal — people_search may go on ahead of the ICP and comes back `blocked: icp_empty` until gtm_update_icp fills it. Switching OFF never fails and creates nothing. The lane then runs (or stops running) on the daily cadence; nothing runs inside this call.

NameTypeReqDescription
keystringyesWhich lane. people_search: LinkedIn people search compiled from the play's ICP (+ optional free-text keywords). | competitor_posts: Post discovery: Otto searches LinkedIn daily for posts matching an…
onbooleanyes
playIdstringyesThe play, from gtm_list_plays.

No output schema declared.

No examples provided.

gtm_show_play ~125

ONE play in full — everything needed to judge it or copy it into a better one: goal and voice, the account's daily cap and working hours (shared by every play on the seat), the ICP facets, the five sourcing lanes with their config and last run, the autonomy ladder (which rungs run alone and which wait for a human), the LinkedIn seat, and today's used/cap for the seat plus this play's share. Read-only. Pass a playId from gtm_list_plays.

NameTypeReqDescription
playIdstringyesThe play to inspect.

No output schema declared.

No examples provided.

gtm_show_sources ~249

All five sourcing lanes of a play, configured or not — the read to make before touching any of them. Per lane: `configured`, `on`, the parsed `config` (defaults filled), the cockpit's `summary` (null = needs setup), `blocked` (why it cannot run, judged without running: needs_config / icp_empty / null), last/next run and the last run's funnel, `addedToday`, `failures`, and the lane's memory — `accounts[]` with `found: true|false` per typed company on account_list, `harvestedPosts` on the post lanes, the cached `searches` the model wrote on Post discovery. Play level: `state` is the same verdict the cockpit shows when the Leads table is empty (no_sources / paused / draft / no_seat / seat_broken / icp_empty / needs_config / lead_quota / never_ran / no_results / provider_error / added_nothing / ok), plus provider, seat, ICP facet count and the month's remaining lead quota. Read-only.

NameTypeReqDescription
playIdstringyesThe play, from gtm_list_plays.

No output schema declared.

No examples provided.

gtm_source_runs ~259

The run history of ONE lane, newest first — the funnel over time (scanned → dropped by fit → dropped by intent → added / duplicates / capped), the ICP facets LinkedIn could not filter on, and the provider's status and message when it refused. Use it to tell 'the query returns nobody' from 'everyone was already a lead' from 'LinkedIn is rate-limiting the seat'. Read-only.

NameTypeReqDescription
keystringyesWhich lane. people_search: LinkedIn people search compiled from the play's ICP (+ optional free-text keywords). | competitor_posts: Post discovery: Otto searches LinkedIn daily for posts matching an…
limitnumberRuns to return (default 10, max 50).
playIdstringyesThe play, from gtm_list_plays.

No output schema declared.

No examples provided.

gtm_sources_reference ~134

THE REFERENCE for configuring sourcing lanes — read it before your first gtm_configure_source / gtm_set_source / gtm_run_source call. Every lane (people_search, competitor_posts a.k.a. Post discovery, job_offers, account_list, and the retired post_engagers), every config field with its meaning, default and cap, the fit/intent/post gates, what makes a lane runnable, how the 5-minute tick and the 24h cadence work, what a preview costs versus a run, and what this surface deliberately will not do. No backend call; safe to call any time.

Input schema present but exposes no named parameters.

No output schema declared.

No examples provided.

gtm_update_icp ~231

Change the ICP a play runs — the query behind people_search and the fit gate on every other lane. Seven string arrays, plain words (gtmotto normalises 'Swiss' → Switzerland, 'Fintech' → Financial Services itself): titles, seniority, languages, industries, sizes (headcount bands like '11-50'), locations, exclude ('never contact' — absolute). ONLY the facets you name change; the first write on a play forks its own ICP row from the product baseline (or a sibling play's row), so no other play's targeting is rewritten. Filling an empty ICP unblocks lanes parked on icp_empty and kicks the play's due lanes if it is active. Size a candidate first with gtm_estimate_icp.

NameTypeReqDescription
excludearray
industriesarray
languagesarray
locationsarray
playIdstringyesThe play, from gtm_list_plays.
seniorityarray
sizesarray
titlesarray

No output schema declared.

No examples provided.

gtm_update_play ~364

Change a play's own knobs — name, goal, voice — its two switches, and the ACCOUNT's pacing (daily action cap, working hours: shared by every play on the LinkedIn account, not per play). `paused: true` is the human's stop button (nothing runs, nothing is sent); `paused: false` resumes. `status: 'active'` LAUNCHES a draft; `status: 'draft'` takes it back off the road. Seat rule, enforced server-side: no live LinkedIn seat ⇒ neither launch nor resume makes it active — it stays/falls back to draft and the note says so. Launching or resuming kicks the play's due lanes at once. Only launch, resume or raise the cap when the human asked for it in this conversation: an active play spends the month's lead quota and acts from the human's own LinkedIn account. Does not touch the autonomy ladder.

NameTypeReqDescription
dailyCapnumberActions per day, all kinds summed. ACCOUNT-level: the ceiling every play on this LinkedIn account shares. Defaults to the seat's full LinkedIn allowance (145).
goalstringWhat a conversation from this play should reach.
namestring
pausedbooleantrue = stop everything; false = resume.
playIdstringyesThe play, from gtm_list_plays.
statusstring'active' launches; 'draft' un-launches.
voicestringHow the messages should sound.
workingHoursobjecte.g. { start: '09:00', end: '18:00', tz: 'Europe/Paris' }. ACCOUNT-level, like dailyCap.

No output schema declared.

No examples provided.

gtm_whoami ~84

Who this key belongs to, and what the workspace may do: org name, plan, subscription status, how many products and plays exist, and the LEAD METER (leads added this calendar month, the plan's cap, and what is left). Start here — the remaining lead allowance is the one number that changes what is worth proposing. Read-only.

Input schema present but exposes no named parameters.

No output schema declared.

No examples provided.

Common questions

What is the GTM Otto MCP server?

GTM Otto is an MCP server listed in the public MCP registry as com.gtmotto/linkedin. Self-driving LinkedIn prospecting: it sources, warms and invites daily. You approve every message. This page covers its hosted endpoint (https://agent.gtmotto.com/mcp).

Is the GTM Otto MCP server safe to use?

GTM Otto scores 75 out of 100 on VerifyMCP. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

What tools does the GTM Otto MCP server expose?

GTM Otto exposes 21 tools: gtm_whoami, gtm_list_plays, gtm_show_play, gtm_play_stats, gtm_linkedin_status, and 16 more. Their descriptions and schemas cost roughly 4,474 tokens of context every time the server is loaded.

Does the GTM Otto MCP server require authentication?

Yes. GTM Otto asked us for credentials when we connected, so you will need to authorise it in your MCP client before it can do anything.

Is the GTM Otto MCP server still maintained?

GTM Otto is still listed as active in the MCP registry. We last reached this channel on 23 September 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.