Connecting AI Agents with MCP

AI assistants and your own agents can search our people and healthcare data through our MCP server. MCP (Model Context Protocol) is the standard AI assistants use to call outside tools. The server is at https://api.aggknowledgeai.com/mcp/v1.

MCP access is part of your contract. If your organization can't connect, ask your AggKnowledge account team to turn it on.

Two ways to connect#

Who connectsSigns in withSeatWhat it may do
A person, from Claude, ChatGPT, or CursorTheir AggKnowledge sign-inUses their seat, assigned at first sign-in if they don't have oneWhat their role allows in the app
Your own agent or backend jobAn agent credential: a client ID and secretUses no seatOnly the permissions you grant the credential

Connect an AI assistant#

Add the server URL, then sign in when asked. The assistant opens our sign-in page as soon as you add the server. Sign in with your AggKnowledge account, and the assistant acts with your role’s permissions from then on.

Signing in can use a seat. Someone who has never signed in to the app is assigned a seat the first time they connect. If your organization has no seats left, sign-in is refused.

  • Claude: add a custom connector with the server URL.
  • ChatGPT: add a custom MCP server with the server URL. Which ChatGPT plans allow this varies, and a Business or Enterprise workspace admin may need to turn it on first.
  • Cursor: add the server to mcp.json.
mcp.json
{
  "mcpServers": {
    "aggknowledge": {
      "url": "https://api.aggknowledgeai.com/mcp/v1"
    }
  }
}

Connect your own agent#

An agent signs in with a client ID and secret. It uses no person's account: it exchanges them for an access token through OAuth client credentials (the standard sign-in for software acting on its own), then sends that token with every request.

1
Create an agent credential
In the app, open Settings, then Account & Billing, then Agent credentials. Name the credential after the agent and grant the permission each of its tools needs, listed in the table under The tools. You can grant only permissions you hold yourself. The secret is shown once, so store it with the agent.
2
Request a token
Send the client ID and secret to the token URL shown with the secret, as in the example below. A token lasts one hour and can't be refreshed: request a new one when it expires.
3
Call the server
Point any MCP client library at https://api.aggknowledgeai.com/mcp/v1 and send the token as a bearer token in the Authorization header.
Request a token
curl -X POST https://id.aggknowledge.ai/oauth2/token \
  -d grant_type=client_credentials \
  -d client_id="$AGGK_AGENT_CLIENT_ID" \
  -d client_secret="$AGGK_AGENT_SECRET"
Response
{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600
}

Rotate a secret without downtime. Add a new secret, move the agent over to it, then delete the old one. Both secrets work until you delete one.

Permission changes take up to an hour. A token carries the permissions the credential had when it was issued, so a change applies from the agent’s next token.

Deleting a credential cuts the agent off within a minute. New token requests fail straight away, and tokens it already holds stop working shortly after.

The tools#

ToolWhat it doesCostAgent permission
pingChecks that the server is reachable.Not billedNone
list_filter_valuesLists the valid values for filters that take fixed values, such as industry.Not billedNone
discover_countCounts the people a Discover search matches and returns a queryId for it.Not billeddiscover:search
discover_peopleReturns a page of the people a queryId matched, up to 100 per page.1 credit per queryIddiscover:search
enrich_personReturns one person's record, bio included, by person ID or profile URL.1 credit per request that finds the personperson:enrich
match_hcpFinds healthcare professionals (HCPs) by name, identifier, or attributes, best match first.Not billedhealthcare:enrich
get_hcpReturns an HCP's record by HCP ID.1 healthcare credit per HCP per billing periodhealthcare:enrich
get_hcp_focus_areasRanks what an HCP treats, prescribes, performs, researches, or runs trials on, largest first.1 healthcare credit per HCP per billing periodhealthcare:enrich
get_hcp_publicationsReturns an HCP's publications, newest first, up to 100 per page.1 healthcare credit per HCP per billing periodhealthcare:enrich
get_hcp_trialsReturns the clinical trials an HCP is linked to, newest first, up to 100 per page.1 healthcare credit per HCP per billing periodhealthcare:enrich
get_hcp_congressesReturns an HCP's congress presentations, newest first, up to 100 per page.1 healthcare credit per HCP per billing periodhealthcare:enrich
browse_healthcare_taxonomyWalks or searches the diagnosis and procedure category trees.Not billedNone
match_companyFinds companies by name, website, profile URL, or ticker and exchange, best match first.1 credit per request with resultscompany:search
get_companyReturns a company's record by company ID, website, or profile URL.1 credit per request that finds the companycompany:enrich
get_company_headcountReturns a company's monthly employee count, hires, and departures.Not billedcompany:headcount:enrich
get_company_relationshipsLists a company's customers or vendors, up to 500 per page.1 credit per company per billing periodcompany:relationships:enrich
search_product_catalogFinds products by name, or pages through every product or category, for Discover's product filters.Not billedvendorgraphic:company:enrich or vendorgraphic:person:enrich
request_contact_infoRequests work email, personal email, or phone for up to 25 people or HCPs.Charged from your contact balanceThe contact:…:enrich permission for each channel
get_contact_infoReads a contact request's status and results.Not billedcontact:view, or any contact enrich permission
get_person_listsLists your person lists, or one list's members as person IDs.Not billedNone
update_person_listCreates, renames, or deletes a list, or adds or removes members.Not billedNone
enrich_peopleEnriches up to 500 people by person ID or profile URL as one batch.1 credit per person enrichedperson:enrich
get_enriched_peopleReads an enrich_people batch, a page of people at a time.Not billedperson:enrich
submit_deep_matchResolves up to 1,000 rows of partial signals to a person or HCP as one batch.1 credit per row that resolvesperson:deep_match
get_deep_matchReads a submit_deep_match batch, a page of rows at a time.Not billedperson:deep_match

How a search runs#

Count first, then read the people. The filters are the ones Discover search uses. This search finds VPs and C-level leaders in engineering at software companies:

discover_count arguments
{
  "or": [
    {
      "company": { "industry": { "or": ["computer software"] } },
      "workExperience": {
        "seniority": { "or": ["vp", "cxo"] },
        "role": { "or": ["engineering"] }
      }
    }
  ]
}
Result
{
  "count": 3512,
  "isApproximate": false,
  "queryId": "8f14e45fceea167a5a36dedd4bea2543"
}

Pass the queryId to discover_people to read the matches a page at a time. page starts at 0 and size defaults to 25.

discover_people arguments
{
  "queryId": "8f14e45fceea167a5a36dedd4bea2543",
  "page": 0,
  "size": 25
}
  • A queryId expires after 30 minutes unused, and after 24 hours in any case.
  • Only the first 5,000 people of a search can be read. Narrow the filters to reach the rest.
  • Counts above 40,000 are approximate, and isApproximate says so.
  • A filter value we don't recognize is refused. The error lists the closest valid values, if any are close. Call list_filter_values to see them all.

Find an HCP and what they work on#

Match first to get an HCP ID. Your contract needs healthcare enrichment. Pass any of a name, NPI, email, person ID, specialty, organization, city, state, or country, one value each. limit defaults to 5, up to 25.

match_hcp arguments
{
  "name": "Jay Patel",
  "specialty": "cardiology"
}
Result
{
  "hcps": [
    {
      "affiliations": [
        {
          "aggkHcoId": "187892874929524688",
          "name": "Wentworth - Douglass Hospital"
        }
      ],
      "aggkHcpId": "169067854124029421",
      "aggkPersonId": "731209846",
      "designations": ["MD"],
      "firstName": "Jay",
      "lastName": "Patel",
      "matchedOn": ["fullName", "specialties"],
      "npi": "1033479100",
      "score": 41.7,
      "specialties": [{ "name": "Cardiovascular Disease" }]
    }
  ]
}
  • An identifier settles the match. With an NPI, email, or person ID, the other criteria only rank the results. Without one, pass a name, which must match fuzzily.
  • Specialty and place alone don't match. A call with neither a name nor an identifier is refused, so the tool never presents a loose attribute hit as the person you meant.
  • Pass a person ID to find the HCP behind a person from discover_people or enrich_person.

Then rank their focus areas along one axis. get_hcp_focus_areas takes an HCP ID and an axis, and returns the same rows as the matching v3 endpoint, largest first:

axisRanked by
conditions_treatedClaims
procedures_performedClaims
drugs_prescribedPharmacy fills
drugs_administeredAdministrations in the clinic, such as infusions
research_topicsPublications
conditions_investigatedTrials naming the HCP
drugs_investigatedTrials naming the HCP
get_hcp_focus_areas arguments
{
  "aggkHcpId": "169067854124029421",
  "axis": "conditions_treated",
  "fromYear": 2023,
  "limit": 10
}
Result
{
  "aggkHcpId": "169067854124029421",
  "axis": "conditions_treated",
  "results": [
    {
      "bodySystem": "Diseases of the circulatory system",
      "bodySystemCode": "CIR",
      "ccsrCode": "CIR019",
      "claimCount": 1742,
      "indication": "Heart failure",
      "indicationCui": "C0018802",
      "patientMonths": 1311
    }
  ],
  "nextCursor": "bzoxMDA"
}
  • Check both drug axes. drugs_prescribed and drugs_administered return the same drugs, ranked by different counts. A drug given in the clinic, like a cancer infusion, has few pharmacy fills and can fall off the end of drugs_prescribed.
  • fromYear and toYear limit the years counted, and minCount drops rows counted fewer times than that over those years.
  • Per-year counts are left out by default. Set includeYearly to true to get them.
  • A cursor is tied to its filters. Change the axis, years, or minCount and the old nextCursor is refused; start again without one.

Read an HCP's publications#

Pass an HCP ID to get its publications, newest first. Your contract needs healthcare enrichment. limit defaults to 20. Pass the nextCursor (a pointer to the next page) of one page as cursor to read the next.

get_hcp_publications arguments
{
  "aggkHcpId": "169067854124029421",
  "limit": 20
}
Result
{
  "aggkHcpId": "169067854124029421",
  "publications": [
    {
      "citationCount": 47,
      "datePublished": "2024-03-12",
      "doi": "10.1000/182",
      "isFirstAuthor": true,
      "isLastAuthor": true,
      "journal": "Journal of the American College of Cardiology",
      "pubId": "W2126798093",
      "publicationTypes": ["Journal Article"],
      "title": "Cardiovascular Outcomes of GLP-1 Receptor Agonist Therapy in Heart Failure",
      "url": "https://doi.org/10.1000/182"
    }
  ],
  "nextCursor": "W1sibCIsIjE3MTAyMDE2MDAwMDAiXSxbInMiLCJXMjEyNjc5ODA5MyJdXQ"
}
  • Abstracts are left out by default, because they're long. Set includeAbstracts to true to get them.
  • A cursor works only with get_hcp_publications. The REST publications endpoint orders results differently, so its cursors aren't interchangeable.
  • Trials and congresses page the same way, newest first by start date, with get_hcp_trials and get_hcp_congresses. Trial summaries, eligibility criteria, and session descriptions are left out unless you set includeDetails to true.

Enrich a person#

Pass a person ID or a profile URL to get that person's record. Pass one of the two, never both. Your contract needs person enrichment. The record has the same fields as a v3 person enrichment returns (see Persons), with empty fields left out.

enrich_person arguments
{
  "linkedinUrl": "https://www.linkedin.com/in/danielentrup"
}
Result
{
  "person": {
    "aggkPersonId": "590654273",
    "bio": "Co-founder of AggKnowledge, a data management and enrichment business purpose-built for knowledge networks.",
    "dateLastUpdated": "2026-07-05",
    "firstName": "dan",
    "lastName": "entrup",
    "locationCity": "new york",
    "locationCountry": "united states"
  }
}
  • bio is the person's own profile summary, not text we write.
  • A stale record is refreshed before it's returned. dateLastUpdated says when it last was. If a refresh fails, you get the stored record with a warning saying so.

Look up a company#

Match first to get a company ID. Pass a company name, website, profile URL, or a ticker with its exchange (a MIC code such as xnas). country only ranks the results. limit defaults to 5, up to 25.

  • get_company leaves the long lists out by default. Set includeDetails to true for subsidiaries, competitors, products and services offered, and private equity investors.
  • want names what get_company_relationships returns: customers are the companies that buy from this one, vendors the companies it buys from. A cursor from one side is refused on the other.
  • Find product and category IDs with search_product_catalog, then filter a discover_count search on them with aggkProductId or categoryId.

Request contact info#

Request, then read the results with the batch ID. request_contact_info returns contact we already hold straight away, plus a batchId. The rest is looked up and usually ready within minutes: pass the batchId to get_contact_info to read it.

  • Each channel applies to every ID in the request. Ask for workEmail, personalEmail, or phone, for up to 25 person IDs and HCP IDs in total.
  • HCPs have work email only. Ask for personal email or phone for people in a separate request.
  • A request runs as an ordinary contact batch, so it sends your contact webhook when it finishes, if you've set one up.

Manage person lists#

A person sees their own lists; an agent sees the organization's. Someone connected through an AI assistant works with the lists they see in the app, private ones included. An agent credential works with the lists shared with everyone, as an API key does.

  • update_person_list takes an action: create, rename, delete, add_members, or remove_members.
  • Warning: delete can't be undone, and needs no permission beyond access to the server. Grant an agent credential only to agents you trust with your lists.
  • Add members by person ID or profile URL, up to 1,000 per request. Each result says whether the person was added.

Run a batch#

Submit the batch, then read it a page at a time. enrich_people and submit_deep_match return a batchId straight away; a batch usually finishes within minutes. Pass the batchId to get_enriched_people or get_deep_match, which report status and page the results with nextCursor.

  • Pages are small by default: 10 people, up to 50, because each person record is long; 50 Deep Match rows, up to 200.
  • A Deep Match row takes any of fullName, stateOrProvince, country, emails, phones, profileUrl, employerNames, and npis. Every field is scored together, and an email is the strongest signal. Results carry each row's rowIndex, from 0.
  • Each row's evidence is left out by default. Set includeDetails to true on get_deep_match for matched criteria and candidates.
  • A batch runs like one submitted through the API, so it sends your enrichment or Deep Match webhook when it finishes, if you've set one up.

Billing#

A search costs 1 credit, however many pages you read. We bill it on the first page that returns people, and a search that matches nobody isn’t billed. Credits come from the same allowance as Discover search in the app, and we report MCP usage separately.

An HCP costs 1 healthcare credit per billing period, across every HCP tool. We bill on the first request that returns something about them. Every later page, and every later request for the same HCP that period, from any HCP tool, isn't billed. A request that returns nothing isn't billed, and match_hcp is never billed.

Each enrichment that finds the person costs 1 credit, from the same allowance as the person enrichment API. Enriching the same person twice costs 2 credits. A request that finds no one isn't billed.

Company tools bill like their v3 endpoints. A match with results, and a company record found, cost 1 credit each. Relationships cost 1 credit per company per billing period, across both sides and every page.

Contact info is charged from your contact balance, per channel found, as the contact info API is.

Warning: each discover_count returns a new queryId, and reading each one bills 1 credit, even with the same filters. Counting is free, but an expired queryId means counting again, and reading that one bills again.

How the tools change#

We change a tool in place only when existing callers keep working. Adding an optional argument or improving a description ships straight away.

A breaking change ships as a new tool. The new tool takes a new name and runs beside the old one until nobody calls the old one. The /v1 in the server URL changes only for a change every tool must make at once. Both URLs then stay live until a cutover date we publish in advance.

The people records follow the v3 API. Each person discover_people returns has the same fields as a v3 Discover search result, and changes only when that does.