MCP HeartbeatTHE MCP OBSERVATORY
Public workspace

Explore

Evidence for your next connection.

Independent measurements of remote MCP servers. Every observation has a timestamp.

Read-only probes

MCP server · Other

Prosp

Find B2B companies that fit your buyer, with their contacts and register IDs, and write outreach.

Checks passed
12 / 22

2 failed · 8 not measured

Median response
852 ms

Median of successful probes over 7 days

30-day uptime
Not measured

Share of countable probes over 30 days, including daily rollups. Auth challenges are excluded.

Discovered tools
23

Tool definitions returned by this server

Independent monitoring

Health & checks

Status history

Responded

The server completed its last measured handshake. This is an observation, not a guarantee.

Last checked
Check summary by test group
Test areaPassedFailedUnmeasured
ConnectionCan a client connect and discover tools?
500
Reliability & speedDoes it respond consistently and quickly?
004
Access & authenticationWhat is required to use this server?
102
Tool qualityAre the tool definitions usable by an agent?
310
Publisher identityCan the publisher be traced to its source?
312

Unmeasured means there is not enough evidence to decide. How these checks work

Inspect individual checks 22 recorded

Core

  • Pass
    Reachable

    Latest probe returned HTTP 200 in 852 ms.

  • Pass
    Valid handshake

    protocolVersion 2025-11-25, serverInfo.name Prosp Prospect Discovery.

  • Pass
    Lists tools

    tools/list returned 23 tools.

  • Pass
    Current protocol

    Negotiated 2025-11-25. The bar is 2025-06-18 or newer.

  • Pass
    HTTPS

    The endpoint URL is https.

Performance

  • Not measured
    Fast (p50)

    Median latency 852 ms over 1 samples in 7 days. The bar is 1000 ms. Need at least 5 samples.

  • Not measured
    Consistent (p95)

    95th percentile 852 ms over 1 samples in 7 days. The bar is 3000 ms. Need at least 20 samples.

  • Not measured
    Reliable (uptime)

    1 of 1 attempts returned a tool list in 30 days (100%). The bar is 99%. Need at least 30 samples.

  • Not measured
    Stable tool list

    Fewer than two successful probes in 30 days, so tool-list stability is not measured.

Access

  • Pass
    Open access

    tools/list succeeded with no credential, and the record declares none.

  • Not measured
    Standard auth

    Auth is not required, so the OAuth metadata check does not apply.

  • Not measured
    Free tier cited

    No publisher quote has been checked.

Tools

  • Pass
    Tools described

    Every tool description is at least 20 characters.

  • Pass
    Valid input schemas

    Every inputSchema is a JSON Schema object.

  • Fail
    Parameters documented

    50 of 67 parameters have a description (75%). The bar is 80%.

  • Pass
    Safety hints

    Every tool sets readOnlyHint or destructiveHint.

Publisher

  • Pass
    Verified namespace

    Listed in the Official MCP Registry.

  • Pass
    Website matches publisher

    The website host matches the reverse-domain namespace.

  • Fail
    Source repo linked

    No repository URL is on file.

  • Not measured
    Repo owner matches

    This namespace is not an io.github login, so the owner comparison does not apply.

  • Pass
    Recently updated

    Updated 2026-11-08T08:07:23.56485Z, within 180 days.

  • Not measured
    Open-source licence

    No GitHub repository to ask about a licence.

Start using Prosp

Connect to this server

Choose your client, copy the configuration, then enable the server in your client’s MCP settings.

Run this command in your terminal, then restart Claude Code.

Claude Code

claude mcp add --transport http prosp https://api.prosp.cc/mcp-rpc

Capabilities

Tools 23

acknowledge_prospects

Mark one or more company domains as delivered so they are not returned again in future discoveries or searches. Pass the `domains` list. Job-blind: each domain is acknowledged across ALL your discoveries. Idempotent — an unknown or already-acknowledged domain is a harmless no-op that contributes 0 to the returned count. Does not cost credits.

Discovered
cancel_discovery

Cancel a running discovery job by its jobId, freeing its slot so a new find_prospects can start. Credits are refunded ONLY when the run had delivered zero prospects at cancel time; if prospects were already delivered you keep them and are not refunded (they stay retrievable via get_discovery_results). Only the job's owner may cancel; cancelling an already-finished job is a harmless no-op that returns its current state.

Discovered
cancel_message_generation

Moves a `queued`, `enriching` or `composing` generation to the terminal state `canceled` with `canceled_reason` = `caller_request`. **Refund rule:** the debit is returned if composition has not started (`status` is `queued` or `enriching`); once `composing` has started the cancel is accepted but the credits are not returned. Canceling an already `canceled` generation is idempotent and returns it unchanged. Canceling a `succeeded` or `failed` generation is **`409 not_cancelable`**. An expired id is `410 generation_expired`.

Discovered
delete_seller_profile

Removes the profile. Idempotent: deleting when no profile is set also returns `204`. **No credit cost.**

Discovered
disconnect_crm

Disconnect your Odoo CRM. Future discovered prospects will no longer sync to it. Idempotent — safe to call even if nothing is connected.

Discovered
find_prospects

Start a B2B prospect-discovery job. Debits 100 credits and returns a jobId immediately; the job runs asynchronously (~5-25 min). Poll get_discovery_status with the jobId, then call get_discovery_results when it reaches a terminal state.

Discovered
generate_message

Registers one message generation and returns immediately with `202 Accepted`. Poll the status resource named in `Location` (or honour `Retry-After`) until `status` is terminal; the finished message is embedded in that resource. The API composes text; it does **not** send it, and it does not replace the recipient's WhatsApp opt-in. ## What the message may say Every candidate fact about the recipient and the sender carries a tier. The message **asserts only facts of tier `verified`**. `reported` and `estimated` facts may influence which angle is chosen but are never stated as fact; `unknown` facts are never mentioned. The tier of each field is derived from the provenance the record already carries (field by field, from its source and verification evidence), not chosen by the model. Personal data - contacts, officers, phone numbers, e-mail addresses, social handles, and, for a sole trader, the person's own name and address - is **never** asserted. After generation, code re-checks that each claim in the text maps to `fact_ids` of `verified` facts; an unsupported claim is removed, or the request fails - it is never shipped. ## Language The message is written in the language of the **recipient company's website main page**. There is no caller override. The decision is reported on the result as `language` (ISO 639-1) and `language_source`. If the homepage language cannot be determined the request fails with error code `language_undetermined` and the credits are returned; the language is never guessed. ## Unknown companies If the recipient or sender company is not yet known, the status passes through `enriching`, which runs the existing discovery/validation pipeline for that single domain, then `composing`. ## Cost and balance There is exactly **one debit, at `POST`**, for the whole generation including any enrichment. Enrichment never debits again and never changes the balance mid-job; a generation that ends `failed` for any reason below, or `canceled` before composition starts, returns that one debit. `402 insufficient_credits` is therefore only ever returned by `POST`. ## Jurisdiction Checked at `POST` against what is already known: `422 jurisdiction_excluded`. A company whose jurisdiction only becomes known **after** enrichment is excluded late: the generation ends `failed` with `error.error` = `jurisdiction_excluded` and the credits are returned. The text is never composed for an excluded jurisdiction. ## Validation done before `202` Invalid body or hostname: `400 invalid_request`. `sender.company_domain` is the sender's own company domain: the service reads the sender's facts from the knowledge base by it and does not verify that the caller controls it. When `sender` or `sender.company_domain` is omitted, the caller's seller profile website is the sender; with no profile website either, the request is `400 invalid_request` (`sender.company_domain is required when no seller profile website is set`). Recipient or sender in a policy-excluded jurisdiction: `422 jurisdiction_excluded`. Insufficient credits: `402 insufficient_credits`. `constraints.max_characters` that no message can meet: `422 max_characters_unmeetable`; when this only shows during composition the generation ends `failed` with the same code and the credits are returned. `callback_url` must be an `https` URL on a public host: a URL resolving to a private, loopback, link-local, reserved, multicast or cloud-metadata address is rejected with `422 unsafe_url` (the same rule as `odoo_url` on the tenant Odoo endpoint). The service being temporarily unable to accept work: `503 service_unavailable` with `Retry-After`; nothing is debited. ## Error codes Every error uses the surface's single `Error` shape `{error, message, request_id}`. `error` is a snake_case code: `invalid_request`, `missing_idempotency_key`, `idempotency_key_reused`, `request_in_flight`, `jurisdiction_excluded`, `insufficient_credits`, `unsafe_url`, `max_characters_unmeetable`, `not_cancelable`, `generation_expired`, `service_unavailable`, and, on a `failed` generation only, the `MessageFailureCode` values: `insufficient_verified_facts` (too few verified facts exist to write anything true), `enrichment_failed`, `language_undetermined`, `language_failed`, `claim_verification_failed`, `jurisdiction_excluded`, `max_characters_unmeetable`, `compose_failed`, `compose_timeout` and `timed_out`. A `failed` generation always returns its credits. ## Idempotency (draft-ietf-httpapi-idempotency-key-header-07) `Idempotency-Key` is **required**. Missing: `400 missing_idempotency_key`. Same key with a different payload: `422 idempotency_key_reused`. Same key while the original request is still being processed: `409 request_in_flight`. Same key with the same payload: **`200`** (not `202`) with the EXISTING resource in its current state (same `id`, no second debit). A key is remembered for **24 hours** from its first use; after that the same key starts a new generation.

Discovered
get_credit_balance

Return your remaining credit balance and the per-job cost (100 credits).

Discovered
get_credit_ledger

Show your credit transaction history: every debit and refund behind the current balance, most recent first. Each entry has a `type` ('debit' or 'refund'), the `job_id` it belongs to, the `amount`, a `reason` and a `timestamp`. Use this to explain WHY the balance is what it is — e.g. a job that debited 100 credits and was then refunded on exhaustion/failure shows up as a matching debit + refund pair, so the balance returning to its prior value is a legitimate refund, not the debit having failed to apply. Does not run a new discovery and does not cost credits. Pass `limit` to bound how many recent transactions are returned (default 50, max 200).

Discovered
get_crm_connection

Show your currently configured Odoo CRM connection (Odoo URL, database, user id and status). Never shows the API key. Returns a 'not connected' error if you have not called set_odoo_credentials yet.

Discovered
get_discovery_results

Fetch the result summary + discovered companies (with verified contacts) for a jobId. Call once get_discovery_status reports a terminal state.

Discovered
get_discovery_status

Poll a discovery job by its jobId. Returns the lifecycle state (queued, processing, completed, failed, exhausted), `total_prospects` (the canonical count of validated prospects delivered so far — the SAME field name get_discovery_results carries, so you can group/count across both; `prospectsFound` is a deprecated alias of it) and `target` counts, and `found`: the prospect DOMAINS discovered so far — which accumulate PROGRESSIVELY while the job runs, so you see results before it finishes. For incremental polling, pass the previous response's `next_cursor` as `cursor` to receive only the domains added since your last poll (`has_more` stays true until the job reaches a terminal state; omit `cursor` to get the full list so far). Call get_prospect(domain) — or get_discovery_results once terminal — for full detail on any domain in `found`.

Discovered
get_message_generation

Always `200` for a known id owned by the caller; a non-terminal state is a status, not an error. An unknown id, or one owned by another tenant, is `404`. While `status` is non-terminal the response carries `Retry-After`. When `succeeded`, `result` is embedded in this response (there is no redirect to a second resource). When `failed`, `error` is the surface's `Error` shape with a snake_case code. When `canceled`, `canceled_reason` says why. The resource is retained for **7 days after it reaches a terminal state**; `expires_at` says when, and the response carries a matching `Expires` header. After that the id is **`410 Gone`** (`generation_expired`), distinct from `404` for an id that never existed or is not yours.

Discovered
get_prospect

Fetch the FULL record for one already-discovered company by its `domain` — the detail behind a `search_prospects` summary or a `found[]` domain. Returns every field (contacts, personal + company emails, registry data, address, social profiles, scores, description), not the lean summary. Does not run a new discovery and does not cost credits. A domain you have not discovered comes back as a structured `not_found` status rather than an error.

Discovered
get_seller_profile

The one seller profile the caller keeps: the business description and website used to ground outreach messages. **No credit cost.** `derivation_status` says whether an offering was derived from the website (`derived`), is still being derived (`pending`), or there is no website (`none`). `404 not_found` when no profile is set.

Discovered
list_circular_opportunities

List circular-economy opportunities inferred from the customer's OWN stored business profile: for each, the flow, asset, spare capacity or capability it rests on and the kind of company that completes it. Does not run a discovery and does not cost credits. The list is dated by `computed_at` — when it was DERIVED, not when you asked — and is never padded, so an empty list means nothing is well-grounded. READ `speech_act` BEFORE YOU USE A LABEL. Each of `resource` and `counterpart` carries one, graded independently. `ASSERT` means the customer told us this and you may state it back. `ASK` means WE INFERRED IT, so refer to it only as a question ('do you have spare kiln capacity?'), never as a fact ('your spare kiln capacity'). The two elements are NOT the same kind of claim. `resource` is about the customer's OWN business; `counterpart` is a hypothesis about which kind of company would want it, and it is `ASK` on nearly every entry — that is normal and does not make the entry unusable. `outreach_draft_eligible` answers ONE question: may this entry ground a draft at all. It turns on the `resource`, because that is what a message would be ABOUT. `false` means do not compose outreach from it in any form, not even as a question. `true` means you may write about the resource — it does NOT license asserting the `counterpart`; that element's own `speech_act` still governs it, so write the market as an open question, not as a need you have established. Do not re-order the list: the order is server-authoritative and shared with every other surface these opportunities appear on. Pass an entry's `handle` to run_circular_opportunity to search for its counterparts.

Discovered
list_icps

List your saved ICPs (Ideal Customer Profiles) — reusable discovery criteria you can re-run by id. Each entry carries its `icpId`, optional `name`, and stored `criteria`. Pass an `icpId` to run_icp to start a discovery from it. Does not cost credits.

Discovered
run_circular_opportunity

Start a counterpart search for one circular opportunity, by the `handle` from list_circular_opportunities. Debits credits and returns a jobId immediately; poll get_discovery_status then get_discovery_results exactly as for find_prospects. What is searched for is derived server-side from the graded opportunity — you pass a handle, not criteria. When the entry has `requires_resource_confirmation: true`, its resource is something WE INFERRED rather than something the customer stated, and this call is REFUSED (404 `resource_confirmation_required`, no charge) unless you pass `confirmed_resource`. ASK THE CUSTOMER FIRST and send THEIR WORDING — do not echo our label back and do not invent one. Their answer is retained as a stated fact about their business and informs every later inference, so a guess becomes a durable error. An unknown handle is refused with a refund.

Discovered
run_icp

Start a discovery from a saved ICP by its `icpId` (get one from list_icps or save_icp). Debits 100 credits and returns a jobId immediately; the job runs asynchronously (~5-25 min) — poll get_discovery_status, then get_discovery_results, exactly as for find_prospects. An unknown icpId is rejected and the debit is refunded.

Discovered
save_icp

Save a reusable ICP (Ideal Customer Profile) from an optional display `name` plus discovery `criteria` (same shape as find_prospects). Returns the stored ICP with a server-assigned `icpId` you can later pass to run_icp. Saving does NOT run a discovery and does not cost credits.

Discovered
search_prospects

Semantic free-text search over the companies you have already discovered. Does not run a new discovery and does not cost credits. Returns LEAN prospect SUMMARIES (domain, name, industry, size, location, employee_count, logo, a short description and a semantic_score) — not the full record. To get every field for one company (contacts, emails, registry data, social profiles, ...), call get_prospect(domain) with the summary's `domain`. Pagination is progressive: the response carries `has_more` and an opaque `next_cursor`; pass that cursor back to fetch the next page. When `has_more` is false / `next_cursor` is null you have seen everything above the relevance floor — stop paging.

Discovered
set_odoo_credentials

Connect your own Odoo CRM so your discovered prospects sync into it. Provide your Odoo URL, database name, API-user id and API key (generate one under Preferences -> Account Security -> New API Key). The connection is validated live against your Odoo before it is saved; your API key is stored encrypted and is NEVER shown back — present a masked confirmation (e.g. odoo_api_key: ****) to the user. Calling this again updates the existing connection.

Discovered
set_seller_profile

Replaces the whole profile with the body. **No credit cost.** Send at least one of `description` or `website`. A `description` longer than 1500 characters is truncated to 1500 characters, the same cap the `/profile` bot command applies, so both surfaces store the same record. A `website` is a URL or a bare domain; the site is crawled into the shared knowledge base in the background, and `derived_offering` fills in later (`derivation_status` is `pending` until then). The offering is kept while the website is unchanged and cleared when the website changes.

Discovered

Recent measurements

Probe history

View status
Latest stored probes. Raw probe rows are kept for 14 days.
CheckedResultResponseTools
Connected852 ms23