Every change Wellknown observed on this MCP server, newest first, with what it was before and what it became. Tool-surface changes carry the definition diff. Nothing here is edited after the fact.
Authorization not required, issuer https://mcp.growthkit.tools
Authorization not required, issuer https://mcp.growthkit.tools
Authorization not required, issuer https://mcp.growthkit.tools
Authorization not required, issuer https://mcp.growthkit.tools
Authorization not required, issuer https://mcp.growthkit.tools
Certificate changed, valid to 2027-01-07
Authorization not required, issuer https://mcp.growthkit.tools
Authorization not required, issuer https://mcp.growthkit.tools
Authorization not required, issuer https://mcp.growthkit.tools
Authorization not required, issuer https://mcp.growthkit.tools
Authorization not required, issuer https://mcp.growthkit.tools
Authorization not required, issuer https://mcp.growthkit.tools
Authorization not required, issuer https://mcp.growthkit.tools
Changed the definition of "pipelineRun"
⟨15 unchanged words⟩ resolve → score → signals → reveal →rescore.rescore; rerank follows reveal and re-judges the contacts that are already there. ALWAYS call with dry_run=true first and show the ⟨44 unchanged words⟩ so resolve needs confirm_credits too, not just reveal. rerank calls no provider at all: it reads the stored candidate lists, and the only credit it can spend is one per contact it actually swaps (the new address is verified). That number is not knowable before the run, so its estimate is 0 — use rerank instead of a second reveal whenever the contacts are there but the wrong people. A stage with 0 candidates is not an ⟨116 unchanged words⟩
⟨15 unchanged words⟩ for a paid stage when dry_run is false — reveal and resolve
Authorization not required, issuer https://mcp.growthkit.tools
Changed the definition of "pipelineRun"
⟨97 unchanged words⟩ ,"en"],"type":"string"},"lead_ids":{"description":"Optional: run only on these leads instead of letting the stage choose. Use it when the user names specific companies, for a first careful run on one lead, or to pick up after an aborted run. These are `leads.id` — the `lead_id` from this tool's own dry-run candidates, from pipelineStatus top_10 or from listLeadSignals. It is NOT the `id` from listCampaignLeads: that is the campaign membership (campaign_leads.id), and passing it is not refused — it simply matches nothing, and 0 candidates reads like 'nothing left to do'. Naming leads only NARROWS: whoever the gate excludes stays excluded. One exception, and it is the point of a second attempt: in the resolve stage a named lead is taken up again even inside the two retry windows.","items":{"type":"string"},"type":"array"},"limit":{"description":"How many candidates to ⟨163 unchanged words⟩
Added "getCampaignLead"; changed the definition of "listCampaignLeads" (73 tools before, 74 now)
⟨35 unchanged words⟩ what you need to rank and to decide. The compact rows carry no contact. fields=full adds the contact (contact_name, contact_role, contact_email, contact_phone), enrichment_provider, enriched_at and enrichment_persona: why the enrichment picked this contact — persona_naehe (closeness of the contact's role to the campaign persona, 0 to 1), persona_treffer, gewaehlt, the alternativen it passed over, and getauscht when it replaced a contact who was already there; that previous contact is kept in metadata.vorheriger_kontakt. enrichment_persona is null when the lead was never enriched with a persona. For one lead's complete recorduse—getCampaignLeadFieldsincluding kandidaten_liste,oreveryonepassthefields=full.enrichment found — call getCampaignLead with the row's id. The fit gate decides on `score_fit` against `fit_gate_min`
Changed the definition of "enrichCompany", "enrichPerson", "findContacts" and 6 more
{"properties":{"campaign_lead_id":{"description":"Optional. The `id` of a lead from listCampaignLeads (that is campaign_leads.id). Pass it whenever you enrich a lead that is already in a campaign: the result is then written back onto that lead. Contact fields are only filled where they are empty — a hand-curated e-mail is never overwritten, the rival candidate is kept in enrichment_data — while provider, timestamp and a short raw extract are always recorded, so `enriched_at` afterwards means 'this lead was enriched' instead of being a guess. Without it nothing is stored and you only get the result back, which is the right thing for a company that is not a lead yet. ⚠ This is NOT the `lead_id` from pipelineStatus' top_10 or listLeadSignals: that one names the company row, and the write fails with lead_not_found. The reply carries `enrichment_write` with what happened.","type":"string"},"company_name":{"description":"DEPRECATED — use `name`. ⟨94 unchanged words⟩
Changed the definition of "updateCampaign"
⟨97 unchanged words⟩ campaign back on the default.","type":["integer","null"]},"icp_snapshot":{"description":"Frozen ICP ⟨432 unchanged words⟩
Changed the definition of "updateCampaign"
⟨33 unchanged words⟩ = open-ended.","type":"string"},"fit_gate_min_score":{"description":"Fit-gate threshold on score_fit for THIS campaign, 0 to 100. Default is 60 when unset — only change it when the user asks. Lower it when a documented signal decides suitability rather than the score: reveal then stops dropping leads that sit just below the default, which is the case this exists for. Pass null to put the campaign back on the default.","type":"integer"},"icp_snapshot":{"description":"Frozen ICP for this ⟨430 unchanged words⟩
Authorization not required, issuer https://mcp.growthkit.tools
Authorization not required, issuer https://mcp.growthkit.tools
Changed the definition of "listLeadSignals"
⟨6 unchanged words⟩ one lead or for a whole campaign: `id` (the signal_id that updateLeadSignal and deleteLeadSignal take), type (funding, hiring, job_change, leadership, acquisition, expansion, tech_change, regulatory, event, inbound, other), the one-sentence signal, ⟨69 unchanged words⟩
Added "deleteLeadSignal" and "updateLeadSignal" (71 tools before, 73 now)
Campaign: Delete Lead Signal — Permanently delete ONE why-now signal, e.g. one recorded on the wrong company. Always confirm with the user first: name the company, the signal sentence and its source, and wait for an explicit yes. There is no undo and no version history. signal_id is the `id` of a signal from listLeadSignals (active_only=false also lists expired ones). Only this signal is removed — the lead's other signals stay untouched. If the signal is right and only a detail is wrong, use updateLeadSignal instead. 404 signal_not_found means the id is unknown or not in this workspace.
Campaign: Update Lead Signal — Correct ONE stored why-now signal in place. signal_id is the `id` of a signal from listLeadSignals — call that with active_only=false to also see expired signals. updates carries only the fields to change: type, signal, source_url, source_label, observed_at, last_confirmed_at. Anything else is ignored and named in `ignored_fields`. The signal stays on its lead: lead_id is not writable, so a signal recorded on the wrong company is removed with deleteLeadSignal, not moved. Changing type, observed_at or last_confirmed_at makes the database recompute expires_at — the reply says so in `expires_at_…
Authorization not required, issuer https://mcp.growthkit.tools
Authorization not required, issuer https://mcp.growthkit.tools
Authorization not required, issuer https://mcp.growthkit.tools
Certificate recorded, valid to 2026-11-09
Authorization not required, issuer https://mcp.growthkit.tools
First tool surface recorded: 71 tools (server version 1.12.2)
Showing the latest 30 events. The API returns up to 500 and filters by kind: ?kind=tool_surface_changed
⟨27 unchanged words⟩ description":"compact for lists and ranking; fullonlywhen you need contacts or theuserenrichmentaskstrailforacross the list. For aspecificsinglelead'sleaddetails.prefer getCampaignLead. Default compact.","enum":["compact"," ⟨32 unchanged words⟩
Campaign: Get Lead — One campaign lead with everything stored about it: contact, company, score, lifecycle_stage, metadata and the complete enrichment_data. That is where you answer why a contact was chosen and who else was found: enrichment_data.persona holds the trail (persona_naehe, persona_treffer, gewaehlt, alternativen, getauscht), enrichment_data.kandidaten_liste the people the enrichment found, and metadata.vorheriger_kontakt the contact this one replaced. Read-only, spends no credits. ⚠ campaign_lead_id is the `id` of a row from listCampaignLeads (campaign_leads.id). It is NOT the `lead_id` from pipeline…
{"properties":{"campaign_lead_id":{"description":"Optional. The `id` of a lead from listCampaignLeads (that is campaign_leads.id). Pass it whenever you enrich a lead that is already in a campaign: the result is then written back onto that lead. Contact fields are only filled where they are empty — a hand-curated e-mail is never overwritten, the rival candidate is kept in enrichment_data — while provider, timestamp and a short raw extract are always recorded, so `enriched_at` afterwards means 'this lead was enriched' instead of being a guess. Without it nothing is stored and you only get the result back, which is the right thing for a company that is not a lead yet. ⚠ This is NOT the `lead_id` from pipelineStatus' top_10 or listLeadSignals: that one names the company row, and the write fails with lead_not_found. The reply carries `enrichment_write` with what happened.","type":"string"},"company":{"description":"Company name (if ⟨41 unchanged words⟩
{"properties":{"campaign_lead_id":{"description":"Optional. The `id` of a lead from listCampaignLeads (that is campaign_leads.id). Pass it whenever you enrich a lead that is already in a campaign: the result is then written back onto that lead. Contact fields are only filled where they are empty — a hand-curated e-mail is never overwritten, the rival candidate is kept in enrichment_data — while provider, timestamp and a short raw extract are always recorded, so `enriched_at` afterwards means 'this lead was enriched' instead of being a guess. Without it nothing is stored and you only get the result back, which is the right thing for a company that is not a lead yet. ⚠ This is NOT the `lead_id` from pipelineStatus' top_10 or listLeadSignals: that one names the company row, and the write fails with lead_not_found. The reply carries `enrichment_write` with what happened.","type":"string"},"company":{"description":"Company name (if ⟨44 unchanged words⟩
⟨102 unchanged words⟩ icp_version_hash in the response to detect staleness). The fit gate decides on `score_fit` against `fit_gate_min` (default 60), never on `score`: `score` is the composite that ranks leads INSIDE the gate and is structurally low for cold leads, so comparing it with the threshold is always wrong.
⟨45 unchanged words⟩ lead's complete record use getCampaignLeadFields, or pass fields=full. The fit gate decides on `score_fit` against `fit_gate_min` (default 60), never on `score`: `score` is the composite that ranks leads INSIDE the gate and is structurally low for cold leads, so comparing it with the threshold is always wrong.
⟨161 unchanged words⟩ next-step suggestion on it, never on chat history. The fit gate decides on `score_fit` against `fit_gate_min` (default 60), never on `score`: `score` is the composite that ranks leads INSIDE the gate and is structurally low for cold leads, so comparing it with the threshold is always wrong.
⟨157 unchanged words⟩ who is there but in the wrong role. The fit gate decides on `score_fit` against `fit_gate_min` (default 60), never on `score`: `score` is the composite that ranks leads INSIDE the gate and is structurally low for cold leads, so comparing it with the threshold is always wrong.
⟨93 unchanged words⟩ ", "welche Leads kann ich anrufen"). The fit gate decides on `score_fit` against `fit_gate_min` (default 60), never on `score`: `score` is the composite that ranks leads INSIDE the gate and is structurally low for cold leads, so comparing it with the threshold is always wrong.