npm.io
1.3.1 • Published 17h agoCLI

leadval-mcp

Licence
UNLICENSED
Version
1.3.1
Deps
2
Size
25 kB
Vulns
0
Weekly
0

LeadVal MCP server

An MCP server that lets any MCP-capable AI — Claude Desktop, Cursor, or your own agent — use LeadVal directly: notify LeadVal that a session is ready, poll its status, or do both and wait.

LeadVal's integration model is notify-then-poll: your AI Agents write a finished conversation to LeadVal's raw lead storage, then this server notifies LeadVal to score it, and you poll for the outcome. The conversation itself must already exist under the sessionId you pass — this server triggers scoring, it does not upload transcripts.

There are two LeadVal MCP servers. This is the writing one.

They do opposite jobs and authenticate differently, so pick by direction:

This package (leadval-mcp) The hosted endpoint
Direction Submits sessions for scoring Reads scored leads, and registers your tools
Runs Locally, next to your agent On your LeadVal host, at POST /mcp
Auth A val_key__… ingestion key you hold OAuth 2.1 — the customer approves your app
Scope Your own workspace Whichever workspace the person authorised
Install npx -y leadval-mcp Point any MCP client at https://<your-host>/mcp

Use this package for your own workspace's pipeline. Use the hosted endpoint when you are building something other LeadVal customers install, so you never hold their API key — see Connect an application in <your-host>/docs.

Nothing stops you running both: submit with this one, read with that one.

What the hosted endpoint offers (for comparison)

This package's tools are listed below. The hosted POST /mcp endpoint is a separate, OAuth-authorized surface with its own tool set.

Readingget_workspace, search_leads, get_lead, get_lead_brief, get_lead_crm_status, list_lead_tags, list_crm_agents and get_customer_profile.

Recognising a returning customer, offered only to a client the customer approved for the identity:lookup scope, and only where LeadVal has enabled the Identity Resolution API for that workspace — identity_lookup. You send the identifying signals you have for the person in front of you; you receive pointers to matching conversations and never their details.

Whether you may use a name is one field, on both surfaces. Every match row carries nameUsePERMITTED or WITHHOLD — with nameUseReason when it is withheld, and get_customer_profile on the hosted endpoint returns the same field with the same two values on each conversation. Read it on the row you are acting on rather than deriving permission from verdict, band or corroboratedBy, and note that the response's top-level constraintsFired unions every candidate scored, including near misses — it can name a clamp that a different person's row fired. Each row carries its own.

Three other answers mislead if read quickly: retrieval.candidatesConsidered: 0 means nobody was retrieved to compare against rather than that the person is new, retrieval.truncated: true means an absent match is not evidence of absence, and corroboratedBy: [] means the match rests on device signals alone — what a shared computer looks like. corroboratedBy is evidence and tone, never permission: ["contact-phone"] alongside SHARED_CONTACT_NO_NAME is one handset and one browser shared by a household, because a phone identifies an address where an email identifies a person. decision.activeGuards lists the protections in force.

Registering your own tools, offered only to a client the customer approved for the tools:write scope:

Tool What it does
submit_tool Describe one of your tools — its parameters, and which LeadVal fact each maps to — so LeadVal reads its calls as graded evidence on that workspace's leads. Returns a submission id and status: "PENDING". Idempotent on the contract, so re-sending an unchanged definition is free.
verify_tool Does the tool you are shipping still match what was approved? Returns which field names and types differ — never a value. Run it at start-up and after any schema change. Read compared_against: on a tool still awaiting review it compares against what you submitted and returns "SUBMITTED", so matches: true alone does not mean live.
list_tools Your workspace's registrations and where each one stands, including the reviewer's note on anything declined.

A submission is a request, not a change. Nothing you submit affects a single lead until a LeadVal administrator reviews and approves it — approving one decides how every future lead in that workspace is read into evidence, which is why it is not yours to do. Approval also applies only to leads processed afterwards; a lead already scored is never re-read.

Which tools a client sees depends on the scopes the customer approved, so tools/list is the authority rather than any list in a README. Full reference: Developers → MCP → Hosted server in <your-host>/docs.

Tools

Tool What it does
leadval_notify_session Queue a finished session for scoring (sessionId, processor). Idempotent.
leadval_get_session_status Poll one session's conformance + processing state. Never returns PII/score/price.
leadval_process_and_wait Notify, then poll until the session reaches a terminal state (or a timeout).
leadval_identity_lookup Mid-conversation: does this workspace already know the person you are talking to? Returns pointers (leadId, customerProfileId), never customer data. Read nameUse on the row you act on (PERMITTED/WITHHOLD, with nameUseReason) — corroboratedBy is evidence and tone, never permission.

Configuration

Two environment variables:

  • LEADVAL_BASE_URL — your LeadVal host, e.g. https://app.your-workspace.com
  • LEADVAL_API_KEY — a val_key__… ingestion key. Mint one in Settings → API Playground. Keep it secret.
Claude Desktop

Add to claude_desktop_config.json:

{
  "mcpServers": {
    "leadval": {
      "command": "npx",
      "args": ["-y", "leadval-mcp"],
      "env": {
        "LEADVAL_BASE_URL": "https://app.your-workspace.com",
        "LEADVAL_API_KEY": "val_key__your-key"
      }
    }
  }
}
Cursor

Add to .cursor/mcp.json (or Cursor Settings → MCP):

{
  "mcpServers": {
    "leadval": {
      "command": "npx",
      "args": ["-y", "leadval-mcp"],
      "env": {
        "LEADVAL_BASE_URL": "https://app.your-workspace.com",
        "LEADVAL_API_KEY": "val_key__your-key"
      }
    }
  }
}
From source
npm install
npm run build
LEADVAL_BASE_URL=https://app.your-workspace.com LEADVAL_API_KEY=val_key__… node dist/index.js

Notes

  • Published to npm since 1.3.0. npx -y leadval-mcp resolves, so the client configs above use it directly and nothing has to be built or path-wired. The "From source" section below is still correct and is what you want when you are changing the server itself.

  • Production only. This server authenticates with an ingestion key, so it submits real sessions. The sandbox needs a logged-in session token as well — for sandbox testing, use the API Playground UI in the app.

  • This server notifies; it does not carry your data. The transcript and its metadata go to the document store; these tools tell LeadVal a session is finished and report on it. What you put in that metadata is what your customer can later use.

  • Extra metadata keys are useful, not ignored — and you are their only source. Your agent is the only thing in the conversation, so a fact that is not in the transcript and not in the metadata does not exist downstream. Anything you send beyond the required contract can be surfaced by the workspace itself, two ways over the same key:

    • Custom Signals — the value shown on the lead as a field or badge, or totalled in the workspace's Analytics Hub. Evidence their team can read.
    • Lead Tags — a rule on the same field, which is what alerts, CRM sync, AI automations and lead visibility all key off. This is the one that makes a lead do something.

    Shape them to be reachable: shallow paths (≤ 5 segments), stringified once, scalar leaves, and stable values a rule can match with equals"PC-4471" works, "campaign PC-4471 (spring)" does not.

    "metadata": {
      "sessionId": "…", "started": "…", "embedding": "…",   // the standard contract
      "partnerCampaignId": "PC-4471"                        // yours
    }

    Four rules decide whether it is usable on the other side:

    Send leaves, not trees {"tier":"vip"} stringified once is reachable as preferences.tier; a deep tree is not (5 segments max).
    Keep values matchable Rules match with equals. Stable identifiers, not prose.
    Keep names and types stable Mapping is by path — renaming silently stops a configured signal from finding anything.
    Omit optional keys, don't blank them A rule can test presence alone, and an empty string still counts as present — so a key you always send but sometimes leave blank matches every lead. Omit it or send null.

    None of it is scored or priced: extraction runs after scoring, so sending more cannot make a lead worth more. Full detail in §6.11 of the Ingestion Specification, and on the Custom metadata page under Developers in <your-host>/docs.

  • Your tool calls are evidence — and they outrank the transcript. If your agent calls tools during the conversation, LeadVal turns those calls into graded facts and rebuilds the customer's Website Journey timeline from them. A conversation says what someone claimed; a tool call records what they did, in a structured field, so it is believed more.

    What you control and what you don't is deliberate:

    You decide LeadVal decides
    Which tools exist and what they take Whether a tool is recognised, and what its parameters mean
    When to call them, and what values to send How strongly the source is believed
    Emitting the result beside the call Whether a value is private and withheld before purchase
    Declaring the toolset the agent has Which values feed scoring, and which are recorded only

    Sending a field called tier or pii changes nothing — those come from the registry, never from your payload. Which is exactly why a buyer trusts them.

    Four habits materially change what a session is worth:

    Emit both sides, back to back Send the call and its result as two consecutive turns naming the same tool, with nothing between them — that adjacency is what joins them. The result carries your system's own truth (price, availability, a qualification outcome), the more credible half. Do not try to join them with a correlation id: no such field survives the trip, and tool_call_id renders as a stamp inside the tool body.
    Declare your full toolset metadata.function.tools — every tool the agent has, not only the ones it used. A tool turn asserts your agent did something and the transcript alone cannot prove it, so this is what lets LeadVal refuse turns your agent never made. Declare nothing and that check is off entirely. Any common shape is read: OpenAI chat or Responses, Anthropic, Bedrock, Gemini, or a bare name.
    Send raw values 450000, not "$450k". "+13055550142", not "(305) 555-0142 (mobile)". Formatting is lossy.
    Distinct tools for distinct actions Separate tools read as a real journey. One catch-all log_event(name, payload) reads as nothing and cannot be mapped.
    Keep tool names exactly stable Matching is case-sensitive. A rename or a capitalisation change silently ends the evidence — no error, calls still retained, facts simply stop.

    Unregistered tools are never dropped: the calls are kept verbatim and surfaced for registration, so ship first and have them mapped after.

    You can register them yourself. If your application is connected over the hosted endpoint with the tools:write scope, call submit_tool with the parameter schema and your parameter→fact mappings; list_tools tells you where each one stands and carries the reviewer's note on anything declined. Otherwise send the platform operator your tool's JSON Schema plus a real sample of one call and one response — samples make the mapping far more accurate than a schema alone. Either way a registration takes effect only after LeadVal approves it, and only for leads processed after that.

    And unlike custom metadata, this DOES move the number. Where a captured parameter is wired to scoring, it feeds the lead's quality index and therefore its price. Measured against the live calibration on one mid-strength lead: with no tool evidence it scored below the pricing floor and was unsellable; a single tool-captured budget lifted it over the floor to $51.83, and adding a target property and an intent signal took it to $59.76. A tool-captured phone number matters even more — remove every contact vector and reachability is zero, which zeroes the lead outright.

    Only some fact keys are wired that way (a contact vector, a budget corroboration, a target property, an intent signal); the rest are recorded faithfully and change nothing. Which is which is registry configuration, so ask your operator what your parameters are wired to.

    None of this is real-estate specific: the event labels and captured fields are configuration, so the same machinery carries a clinic booking appointments or a SaaS company starting trials. Full detail on the Tool calls as evidence page under Developers in <your-host>/docs, and the admin-side Tool Calls & Journey page under Guides.

  • Status responses are PII/score/price-free by design — safe to hand back to an AI.

  • Errors come back as the LeadVal RFC 9457 problem+json body with a stable code.

  • Full API reference: <your-host>/docs and the machine-readable spec at <your-host>/openapi.yaml.

  • Advanced identity resolution and the evidence schema: contact EVAS at support@evas.live.

Keywords