API

MCP server for U.S. dental provider data

270,000+ U.S. dentists across 42 states + DC, plus Medicaid fee schedules for all 50 states + DC. Eight read-only tools, citation envelope on every response, three billing rails. Compatible with Claude.ai, Cursor, Cline, Continue, ChatGPT, and any MCP-aware client.

TL;DR for agents reading this page

Streamable HTTP MCP server at https://mcp.providersignal.com/mcp. Eight tools (lookup_provider_by_npi, search_providers, get_dso_affiliation, get_territory_summary) wrap the ProviderSignal /api/v1/agent/* REST API. Authenticate with Authorization: Bearer ps_live_<key> for subscription billing, or pay per call in USDC on Base mainnet via x402. Discovery manifest at /.well-known/mcp.json. Every response carries a citation envelope with source attribution and a confidence score.

curl - 30-second integration
curl -X POST https://mcp.providersignal.com/mcp \ -H "Authorization: Bearer ps_live_<your_key>" \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

Eight tools, dozens of agent workflows

Each card below is a real prompt a buyer might give an AI agent that has this MCP server connected. The tool pills show how the workflow maps back to the underlying calls.

Supply rep

"Find dentists in Dallas County whose licenses expire in the next 6 months and aren't affiliated with a DSO."

search_providersget_dso_affiliation

Outcome: Renewal-window call list, filtered to rep-direct buyers (no chain procurement gates).

DSO biz dev

"Show me Texas markets with the highest density of solo independents over 55, and tell me how many practices each ZIP code has."

get_territory_summarysearch_providers

Outcome: Acquisition heatmap of retirement-risk independents, ranked by ZIP-level concentration.

Practice broker

"Look up NPI 1376155820 and tell me if there are any disciplinary actions, OIG exclusions, or anomalies I should disclose to a buyer."

lookup_provider_by_npi

Outcome: One-shot due-diligence check before engaging a seller. Citation envelope shows the source date and confidence score for legal defensibility.

AI agent / app builder

"Cross-reference these 200 NPI numbers from a CRM export and flag any that have changed addresses, expired, or moved into a DSO since last quarter."

lookup_provider_by_npiget_dso_affiliation

Outcome: Bulk freshness check that turns a stale CRM into a maintained source of truth. Per-call USDC billing fits batch jobs cleanly.

A 2-tool answer in a real Claude.ai conversation

The flow below is what a connected client renders for the user. The MCP server calls the tools, the agent formats the final answer.

1. User prompt

"How saturated is the dental market in Austin, Texas, and what share of practices belong to a DSO?"

2. Agent calls get_territory_summary

tool call
{ "method": "tools/call", "params": { "name": "get_territory_summary", "arguments": { "state": "TX", "city": "AUSTIN", "top_n": 5 } } }
tool result
{ "data": { "filters": { "state": "TX", "city": "AUSTIN", "zip": null, "county": null, "top_n": 5 }, "total_providers": 1842, "by_affiliation": { "dso": 254, "independent": 1418, "organizations": 170 }, "by_signal": { "has_cms_claims": 211, "has_disciplinary_action": 48, "is_federally_excluded": 2, "retirement_risk_55plus": 587 }, "by_license_status": { "active": 1612, "inactive": 142, "expired": 71, "retired": 17 }, "top_specialties": [ { "specialty": "General Dentist", "providers": 1247, "share_pct": 67.7 }, { "specialty": "Orthodontics", "providers": 162, "share_pct": 8.8 }, { "specialty": "Pediatric Dentist", "providers": 109, "share_pct": 5.9 } ], "top_dsos": [ { "dso_label": "Heartland Dental", "providers": 38, "practices": 6, "states_covered": 1 }, { "dso_label": "Smile Magic", "providers": 21, "practices": 3, "states_covered": 1 } ] }, "meta": { "source_attribution": [ { "table": "providers", "last_refresh": "2026-05-10", "schema_version": "v1", "license": "public" } ] } }

3. Agent calls search_providers (Travis County subset)

tool call
{ "method": "tools/call", "params": { "name": "search_providers", "arguments": { "state": "TX", "city": "AUSTIN", "license_status": "active", "per_page": 25 } } }
tool result (truncated)
{ "data": { "results": [ /* 25 active Austin practices, page 1 of 74 */ ], "page": 1, "per_page": 25, "total": 1842 } }

4. Agent answer to the user

Austin has 1,842 active dental practices. DSO penetration is 13.8% (254 of 1,842). General dentists make up 67.7% of the cohort; orthodontics 8.8%. Heartland Dental is the largest local DSO with 38 providers across 6 practices. Source: ProviderSignal providers table, last refreshed 2026-05-10. To break the share down by ZIP, call get_territory_summary again with a zip prefix.

Two tool calls, ~$2.85 total in per-call USDC (x402 rail) or 3 credits on the subscription rail. Citation envelope provides the freshness date the agent quotes back to the user.

Tools (4 read-only)

Each MCP tool maps to one path under /api/v1/agent. Every response is wrapped in a citation envelope (see Response Shape). Full input schemas (JSON Schema for every parameter) are served by the tools/list JSON-RPC method.

lookup_provider_by_npi(npi)
forwards to /api/v1/agent/lookup-by-npi (no Bearer: /api/v1/free/lookup)

Resolve a 10-digit NPI to a full provider record. Use when an agent has the NPI and needs name, taxonomy, practice address, license status, DSO flag, OIG-exclusion flag, lat/lon, CMS-claims flag, and a 0-100 confidence score (about 30 fields). Works without a Bearer on the free tier (100/day per IP, light fields only).

Sample call & response ›
input
{ "npi": "1376155820" }
output
{ "data": { "npi": "1376155820", "first_name": "JOHN", "last_name": "SMITH", "specialty": "DENTIST - GENERAL PRACTICE", "practice_address": "1234 MAIN ST", "city": "DALLAS", "state": "TX", "zip": "75201", "license_status_normalized": "active", "is_dso": false, "is_federally_excluded": false, "has_cms_claims": true, "co_located_count": 2 }, "meta": { "envelope_version": "1.0", "source_attribution": [ { "table": "providers", "last_refresh": "2026-05-10", "schema_version": "v1", "license": "public" } ], "confidence": { "provider_record": { "score": 92 } } } }
search_providers({state, city, zip, license_status, page, per_page})
forwards to /api/v1/agent/search (no Bearer: /api/v1/free/search)

Filtered list with pagination. Use when an agent needs to find practices by geography or status. Filters compose with AND. per_page caps at 100. Returns the same envelope as lookup with a compact per-row record array. Works without a Bearer on the free tier (10/day per IP, 10 rows/page, identity and location fields only; name search and the license_status filter require a Bearer).

Sample call & response ›
input
{ "state": "TX", "city": "DALLAS", "license_status": "active", "per_page": 25 }
output
{ "data": { "results": [ { "npi": "1376155820", "name": "SMITH, JOHN", "city": "DALLAS", "is_dso": false }, { "npi": "1487266931", "name": "PATEL, RAJ", "city": "DALLAS", "is_dso": true, "dso_label": "HEARTLAND DENTAL" } ], "page": 1, "per_page": 25, "total": 1842 } }
get_dso_affiliation(npi)
forwards to /api/v1/agent/dso/affiliation

Determine whether a provider is part of a Dental Service Organization, which one, and how confident the inference is. Use when buying decisions hinge on chain procurement vs independent rep-direct.

Sample call & response ›
input
{ "npi": "1487266931" }
output
{ "data": { "is_dso": true, "dso_label": "HEARTLAND DENTAL", "signal_type": "address_cascade", "signal_confidence": 0.87, "cluster_size": 14, "sibling_npi_sample": ["1376155820", "1295366042", "1639400138"] } }
get_territory_summary({state, city?, zip?, county?, top_n?})
forwards to /api/v1/agent/territory/rollup (no Bearer: /api/v1/free/territory)

Aggregate market intelligence for a geography. Use when scoping a new sales territory, evaluating an acquisition market, or comparing regions. Returns total providers, DSO/independent/organization split, signal counts (CMS billers, disciplinary action, federally excluded, retirement-risk), license-status breakdown, plus ranked top_specialties and top_dsos arrays (default top 10, max 25 via top_n). Works without a Bearer on the free tier (10/day per IP, state grain only: headline counts, top-3 specialties, DSO share).

Sample call & response ›
input
{ "state": "TX", "top_n": 5 }
output
{ "data": { "filters": { "state": "TX", "city": null, "zip": null, "county": null, "top_n": 5 }, "total_providers": 14988, "by_affiliation": { "dso": 2104, "independent": 10287, "organizations": 8104 }, "by_signal": { "has_cms_claims": 1532, "has_disciplinary_action": 412, "is_federally_excluded": 18, "retirement_risk_55plus": 6431 }, "by_license_status": { "active": 12134, "inactive": 1402, "expired": 891, "retired": 412, "deceased": 78, "disciplined": 41 }, "top_specialties": [ { "specialty": "General Dentist", "providers": 10392, "share_pct": 69.3 }, { "specialty": "Orthodontics", "providers": 1174, "share_pct": 7.8 }, { "specialty": "Pediatric Dentist", "providers": 1015, "share_pct": 6.8 }, { "specialty": "Dental Hygienist", "providers": 680, "share_pct": 4.5 }, { "specialty": "Oral Surgery", "providers": 641, "share_pct": 4.3 } ], "top_dsos": [ { "dso_label": "Heartland Dental", "providers": 124, "practices": 17, "states_covered": 1 }, { "dso_label": "Dental Depot", "providers": 65, "practices": 3, "states_covered": 1 }, { "dso_label": "Network Provider Assoc", "providers": 48, "practices": 2, "states_covered": 1 } ] } }
get_market_positioning({state, zip?})
forwards to /api/v1/agent/market/positioning

Market Multiple Positioning band (Premium/Average/Discount) for a state or ZIP-prefix metro vs the public national dental practice-sale benchmark, with the four driving factors and a 0-100 confidence score. Market context, not a transaction comp or practice-specific valuation. Cheaper single-metric alternative to the positioning block inside get_territory_summary.

Sample call & response ›
input
{ "state": "TX", "zip": "770" }
output
{ "data": { "scope": { "state": "TX", "zip_prefix": "770", "grain": "zip_prefix" }, "band": "Average", "score": 50, "modifier": 1.0, "confidence": 100, "factors": { "dso_competition": 60, "demographics": 40, "scarcity": 40, "supply_pressure": 60 }, "disclaimer": "Market-attractiveness positioning vs the public national benchmark. Not a transaction comp and not a practice-specific valuation." } }
get_medicaid_rates({state, code?})
forwards to /api/v1/agent/medicaid/rates (no Bearer: /api/v1/free/medicaid-rates)

What a state's Medicaid pays dentists, by procedure. Returns a fixed common-procedure basket (exam, adult and child cleaning, filling, stainless steel crown, extraction) with sanity-bounded rates, the state's national rank on that basket, locality structure (adult/pediatric or geographic schedules), and the latest effective year. Optional code arg adds the raw published rate(s) for one CDT D-code. Public fee-schedule amounts, not eligibility. All 50 states + DC. Works without a Bearer on the free tier (25/day per IP, basket summary; the per-code lookup requires a Bearer).

Sample call & response ›
input
{ "state": "CT" }
output
{ "data": { "state": "CT", "state_name": "Connecticut", "national_rank": 21, "total_ranked": 51, "basket_index": 70.5, "effective_year": 2026, "has_pediatric_split": true, "basket": [ { "code": "D1110", "procedure": "Adult cleaning", "rate": 62.72, "pediatric": false }, { "code": "D1120", "procedure": "Child cleaning", "rate": 64.00, "pediatric": true } ], "localities": [ { "locality": "CT Medicaid Dental Adult", "kind": "adult", "code_count": 233 }, { "locality": "CT Medicaid Dental Pediatric", "kind": "pediatric", "code_count": 258 } ], "disclaimer": "Published Medicaid fee-for-service schedule amounts, not a coverage or eligibility guarantee." } }
score_providers({state?, specialty?, sole_prop?, is_dso?, enriched_only?, has_cms?, min_score?, sort?, order?, page?, per_page?})
forwards to /api/v1/agent/scoring

Rank dentists by acquisition-readiness (0-100) with a per-factor breakdown. The 6-factor model weights solo/independent status, practice vintage, retirement risk, practice size, a clean disciplinary record, and license freshness. Use to build a prioritized acquisition or outreach cohort. Individuals only; paginated; sort by score (default DESC) or another column.

Sample call & response ›
input
{ "state": "TX", "min_score": 70, "sort": "score", "per_page": 25 }
output
{ "data": [ { "npi": "1699733048", "first_name": "EUGENE", "last_name": "AARON", "city": "AUSTIN", "state": "TX", "score": 87, "score_factors": { "solo_independent": 25, "practice_vintage": 14, "retirement_risk": 18, "practice_size": 12, "clean_record": 10, "license_freshness": 8 }, "confidence": { "score": 92 } } ], "meta": { "envelope_version": "1.0", "total": 542, "page": 1, "per_page": 25 } }
get_license_events({npi, event_type?, limit?})
forwards to /api/v1/agent/license/events/historical

Dated license-event history for one provider, newest first: status changes, disciplinary actions, board orders, and address changes, each with old/new values, the reporting source and state, and when our pipeline first observed it. The highest-fidelity longitudinal signal we ship. Optional event_type filter; limit 1-500 (default 100).

Sample call & response ›
input
{ "npi": "1376155820", "event_type": "STATUS_CHANGE", "limit": 100 }
output
{ "data": { "npi": "1376155820", "total_events": 4, "events": [ { "event_type": "STATUS_CHANGE", "event_date": "2025-10-31", "old_value": "Active", "new_value": "Expired", "source": "tsbde", "state": "TX", "created_at": "2025-11-01T03:14:22Z" } ] } }

Pick a billing rail

Both rails authenticate against the same backend. Choose based on your traffic profile.

Rail 1 · Default for MCP today

Bearer-token subscription

Pass an existing ProviderSignal API key on every request. Per-call usage rides your subscription tier's rate limit.

header
Authorization: Bearer ps_live_<your-key>
  • API Starter: 60 req/min
  • Team: 200 req/min
  • API Pro: 500 req/min
  • Enterprise: 1,000+ req/min
  • Dashboard Rep tier doesn't include API; add API Starter or upgrade to Team.
  • Get a key at /settings › API Keys
Rail 2 · No signup required

x402 per-call USDC

Skip the subscription. Call /api/v1/agent/* directly. The first request returns 402 with PaymentRequirements; sign a USDC transfer on Base mainnet and retry with X-PAYMENT.

  • $0.50 to $2.50 per call (see pricing below)
  • No signup, no credit card
  • Settles on Base mainnet (chain id 8453)
  • Full flow: /docs/agent-payments

Native x402 challenge inside an MCP tool call is a v1.1 deferred item. Most MCP clients today don't handle the tool-call payment-challenge loop. Until they do, x402 is reachable outside MCP via the direct /api/v1/agent/* endpoints.

Per-call USDC pricing

Subscription becomes more economical above roughly $30/mo of per-call spend. For batch jobs and infrequent calls, x402 wins on simplicity (no key management) and cost.

EndpointTierUSD/callSubscription breakeven
/api/v1/agent/lookup-by-npibasic$0.50~198 calls/mo vs $99 Starter
/api/v1/agent/searchbasic$0.50~198 calls/mo vs $99 Starter
/api/v1/agent/dso/affiliationmoat$1.00~99 calls/mo vs $99 Starter
/api/v1/agent/scoringmoat$1.00~99 calls/mo vs $99 Starter
/api/v1/agent/license/events/historicalmoat$1.50~66 calls/mo vs $99 Starter
/api/v1/agent/territory/rollupmoat$2.50~40 calls/mo vs $99 Starter
/api/v1/agent/market/positioningmoat$0.50~198 calls/mo vs $99 Starter
/api/v1/agent/medicaid/ratesbasic$0.50~198 calls/mo vs $99 Starter
/api/v1/free/lookupfree$0.00100/day per IP
/api/v1/free/medicaid-ratesfree$0.0025/day per IP
/api/v1/free/searchfree$0.0010/day per IP
/api/v1/free/territoryfree$0.0010/day per IP

basic tier endpoints (lookup, search) are priced near commodity because the underlying data is largely derivable from public NPI sources. moat tier endpoints (DSO inference, scoring, license events history, territory rollup) encode work that takes months to replicate from raw sources, and are priced 10-25x basic.

Endpoint

Streamable HTTP transport per the Model Context Protocol spec. POST JSON-RPC 2.0 messages to:

endpoint
POST https://mcp.providersignal.com/mcp

Discovery manifest (no auth required):

manifest
GET https://mcp.providersignal.com/.well-known/mcp.json

MCP protocol version implemented: 2025-06-18. Notifications return HTTP 204; batched JSON-RPC requests are supported.

Response Shape

Every tool returns a JSON-RPC result with a single content array carrying the citation envelope as text.

Initialize

json-rpc
{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2025-06-18" } }

List tools

json-rpc
{ "jsonrpc": "2.0", "id": 2, "method": "tools/list" }

Call a tool

json-rpc
{ "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "lookup_provider_by_npi", "arguments": { "npi": "1376155820" } } }

Tool response (envelope)

json-rpc
{ "jsonrpc": "2.0", "id": 3, "result": { "content": [ { "type": "text", "text": "{ \"data\": {...}, \"error\": null, \"meta\": { \"envelope_version\": \"1.0\", \"source_attribution\": [...], \"confidence\": {...}, \"request\": {...} } }" } ], "isError": false } }

The envelope inside content[0].text carries:

  • data: provider record, search results, or aggregate
  • meta.source_attribution: one entry per data table touched, with last_refresh, schema_version, and license posture
  • meta.confidence.provider_record: 0-100 score with breakdown (completeness, multi-source, freshness)
  • meta.request: request id, endpoint, billed credits, billing method (subscription or per_query)

The full field glossary lives at /docs/fields.

Input Validation

Tool arguments are validated at the edge before any database call. Validation errors return JSON-RPC -32602 (invalid params) and never burn rate-limit quota. Defense in depth: the main API re-validates everything server side.

  • npi: ^\d{10}$
  • state: ^[A-Z]{2}$
  • zip: ^\d{5}(-\d{4})?$ (5- or 9-digit)
  • license_status: enum (active, inactive, expired, retired, deceased)
  • page: integer 1 to 10000
  • per_page: integer 1 to 100
  • strings: 200 chars max (city: 80 max)

Rate Limits & Body Cap

Three guard rails:

  • 60 req/min per IP at the MCP edge, enforced by the Cloudflare-native rate limiter. Fires before any auth or parse work, so anonymous probes cost almost nothing.
  • 16 KB body cap via Content-Length. Larger requests return HTTP 413 before parse.
  • Per-key tier limit from your subscription (API Starter 60/min, Team 200/min, API Pro 500/min, Enterprise 1,000+/min). Enforced after key validation.

When you hit a 429, the response includes a retry_after_seconds hint plus standard X-RateLimit-* headers.

Discovery Manifest

The .well-known/mcp.json manifest is the 2026 MCP-discovery convention. MCP-aware clients fetch it on connect and render the results in their connector picker.

  • Server name, version, protocol version, transport
  • Primary auth: Bearer; alternateBilling block with the full x402 price ladder
  • Tool list with names and short descriptions
curl
curl -s https://mcp.providersignal.com/.well-known/mcp.json

Connect a Client

Claude.ai (Pro / Team / Enterprise)

  1. Settings › Connectors › Add custom connector
  2. URL: https://mcp.providersignal.com/mcp
  3. Bearer token: paste your ps_live_ key
  4. Save. Claude probes the manifest and registers all eight tools.

Cursor

Settings › MCP › Add new MCP server. Same URL + Bearer token. Cursor lists the tools in its agent panel and makes them available in any chat.

Cline (VS Code)

~/.config/cline/mcp.json
{ "mcpServers": { "providersignal": { "transport": "streamable-http", "url": "https://mcp.providersignal.com/mcp", "headers": { "Authorization": "Bearer ps_live_<your_key>" } } } }

Continue (JetBrains / VS Code)

Add to your config.json under experimental.modelContextProtocolServers with the same URL and Authorization header.

ChatGPT

Custom connector via the developer settings. URL and Bearer token same as above.

Custom agent code

Open a POST stream to the endpoint. Send initialize, then tools/list, then tools/call per the MCP spec. JSON-RPC 2.0 batches are supported.

curl
curl -X POST https://mcp.providersignal.com/mcp \ -H "Authorization: Bearer ps_live_<your_key>" \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

Frequently asked questions

How do I connect to the ProviderSignal MCP server from Claude.ai?

Open Claude.ai Settings, then Connectors, then Add custom connector. Paste https://mcp.providersignal.com/mcp as the URL and your ProviderSignal API key (ps_live_<...>) as the Bearer token. Save. Claude probes the discovery manifest at /.well-known/mcp.json and registers all eight tools automatically.

What tools does the MCP server expose?

Eight read-only tools: lookup_provider_by_npi (full provider record by NPI), search_providers (filtered list by state/city/zip/license_status), get_dso_affiliation (Dental Service Organization chain inference), get_territory_summary (aggregate market intelligence for a geography), get_market_positioning (market-multiple positioning band vs the national benchmark), get_medicaid_rates (state Medicaid dental fee schedule by procedure), score_providers (acquisition-readiness ranking with per-factor breakdown), and get_license_events (dated license-event history for one NPI).

What does each call cost?

Three billing rails. On the Bearer-token subscription rail, calls ride your existing tier rate limit (API Starter 60 req/min, Team 200, API Pro 500, Enterprise 1,000+). On the two per-call rails, prices range from $0.50 (basic lookup) to $2.50 (territory rollup): pay in USDC on Base mainnet via x402, or by credit card via MPP (a Stripe Shared Payment Token, so agents without a crypto wallet can still pay per call). Subscription becomes more economical above ~$30/mo of per-call spend.

Is there a free tier?

Yes, four endpoints work with no authentication: NPI lookup (100/day per IP), Medicaid dental rates (25/day), provider search (10/day, directory-grade fields), and state territory summary (10/day). The MCP tools lookup_provider_by_npi, get_medicaid_rates, search_providers, and get_territory_summary fall back to these automatically when called without a Bearer token. Use them to evaluate data quality before committing to a subscription or x402 wallet setup. See /docs/free-tier.

What data sources back the responses?

NPPES (HHS National Plan and Provider Enumeration System), state dental boards (42 states + DC enriched), CMS Medicare Part B billing data, NPDB malpractice and adverse-action data, OIG LEIE federal exclusion list, and Medicaid fee schedules for all 50 states + DC. Every response carries a meta.source_attribution array listing the tables touched and their last refresh dates.

What is the citation envelope?

Every API response wraps the data payload in a meta object containing source_attribution (which tables provided the data), confidence (a 0-100 score with completeness/multi-source/freshness breakdown), and request (id, endpoint, billed credits, billing method). The envelope is the canonical legal-defensibility surface and lets agents cite their work back to a buyer.

Which MCP clients work with this server?

Anything that speaks the MCP Streamable HTTP transport: Claude.ai (custom connectors), Cursor (Settings > MCP), Cline (VS Code, via mcp.json), Continue (JetBrains/VS Code), ChatGPT (custom connector in developer settings), and any agent code that POSTs JSON-RPC 2.0 to the /mcp endpoint with a Bearer header.

Why is x402 not yet exposed inside MCP tool calls?

Most MCP clients in 2026 don't yet implement the tool-call payment-challenge loop required to handle a 402 returned mid-tool-call. Until they do, the per-call rails (x402 USDC and MPP credit card) are reachable outside MCP via direct calls to /api/v1/agent/*. The MCP server itself uses Bearer-token subscription auth; native per-call payment inside MCP is a v1.1 deferred item.

References