Agent MCP server
Connect an AI agent to Doctronic's free AI consult over MCP without credentials, collect intake and terms acceptance, show Doctronic's safety and emergency text verbatim, and hand the person off to doctronic.ai for care.
The Doctronic agent MCP server lets an AI agent run Doctronic's AI consult for the person it's helping. It's the same free AI consult a person starts on doctronic.ai without signing in. The agent connects without an account, API token, or sign-in, and the person stays anonymous. The consult is for adults 18 and over in the United States.
Private beta
The agent MCP server, its endpoints, and its directory listings are in private beta. The production endpoint isn't open yet. Build and test against the staging endpoint.
The rest of these docs cover the Doctronic API, which your backend calls with an API token for your organization's patients. The agent MCP server is separate. It has its own endpoint and doesn't go through the Doctronic API:
| Agent MCP server | Doctronic API | |
|---|---|---|
| Caller | An AI agent acting for one person | Your backend, for your organization's patients |
| Credentials | None | An API token for your organization |
| The person's identity | Anonymous until they continue on doctronic.ai | Mapped to a user ID by your backend |
| The consult | An anonymous doctronic.ai consult | A conversation for one of your users |
| Care after the consult | A one-time link to doctronic.ai | Appointments, enabled per organization |
| Protocol | MCP over Streamable HTTP | REST and Server-Sent Events |
To run consultations for your own patients inside your product, use the Doctronic API. See get access.
Connect
| Setting | Value |
|---|---|
| Endpoint | https://mcp.doctronic.ai/ (private beta, not open yet) |
| Staging endpoint | https://staging-mcp.doctronic.ai/, for building and testing. Its limits can differ from production. |
| Transport | Streamable HTTP, with JSON responses |
| Authentication | None |
| Sessions | None. The server keeps no state between requests. The intake_token and consult_id carry it. |
The server is the whole host, so the endpoint is the root URL with nothing after it. Add it to
your MCP client as a remote HTTP server with no authentication. To use staging, put
https://staging-mcp.doctronic.ai/ in place of the production URL.
claude mcp add --transport http doctronic https://mcp.doctronic.ai/{
"mcpServers": {
"doctronic": {
"url": "https://mcp.doctronic.ai/"
}
}
}{
"mcpServers": {
"doctronic": {
"type": "http",
"url": "https://mcp.doctronic.ai/"
}
}
}- Claude on the web, desktop, or mobile: add a custom connector in Claude's connector settings, with the endpoint as its remote MCP server URL. Leave the OAuth fields empty.
- ChatGPT: turn on developer mode, then add the endpoint as a remote MCP server with no authentication.
Client settings screens vary and change over time. Look for the option to add a remote MCP server by URL.
To check the connection from a terminal, list the tools:
curl --fail-with-body --request POST "https://mcp.doctronic.ai/" \
--header "Content-Type: application/json" \
--header "Accept: application/json, text/event-stream" \
--header "User-Agent: ExampleAgent/1.0 (agent)" \
--data '{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}'Name your agent in the User-Agent header. Clients that don't identify as an agent get a lower
limit on new consults. See limits.
Run a consult
A consult starts in two steps. start_consult returns the intake questions and the terms. Your
agent asks the questions and shows the terms. submit_intake sends the answers and starts the
consult.
- Call
describe_service. Show the person what Doctronic offers. - Ask the person which US state they're in. Call
start_consultwith that state. - Show the person
disclosureonce, with the intake questions. Ask each question inquestionsone at a time, using itsprompt. Use the person's answers, not values inferred from other context. - Show the person
terms.textword for word, with the terms of service and privacy policy links. Whenweb_consent_requiredisfalse, ask whether they accept. When it'strue, don't ask in chat. Doctronic asks on its own page. See terms. - Call
submit_intakewith theintake_token, the person's answers, and whether they were shown and accepted the terms. If the person has already described their concern, send it asinitial_message. Keep theconsult_idit returns. - If
statusisconsent_pending, the person accepts the terms on doctronic.ai first. See web consent. - Relay the assistant's
messageto the person. Send each reply withcontinue_consultuntilstatusisassessment_readyorended. - When
statusisassessment_ready, callget_assessmentand share the result. - Call
get_care_options. If the person wants care from a Doctronic physician, follow hand off for care.
If the person describes a possible emergency before Doctronic has replied, show them
emergency_text first. See emergency text.
The AI consult is free. describe_service and get_care_options return physician visit prices.
Talk to the person
The server instructions, the tool descriptions, and each next_step carry these rules. The server
can't enforce them, so your agent has to follow them.
- One question at a time. When the person's app can present a question with choices or a short
form, use it for the intake questions and for any Doctronic question that offers options.
Otherwise ask in plain text. The intake questions are age, sex, and accepting the terms when
web_consent_requiredisfalse. - The person answers. Never answer a question or accept the terms on the person's behalf.
- Doctronic's words only. Relay Doctronic's questions and replies in Doctronic's words. Add no
medical assessment, advice, or emergency instructions of your own. When the person needs safety
guidance, the result carries it in
safety.text. - Verbatim text stays verbatim. Show
terms.text,emergency_text, andsafety.textwhensafety.display_verbatimistrueexactly as written. - The disclosure once. Show
disclosureonce, with the intake questions. - Identifiers stay in tool calls. Never show or read
intake_tokenorconsult_idto the person. Thecodefromsubmit_intakeorcreate_handoffis the one identifier the person sees, with its link. - Terms acceptance in chat only without web consent. When
web_consent_requiredistrue, the person accepts on a Doctronic page through anaccept_termslink. An answer in chat doesn't count.
Tools
| Tool | Purpose | Read-only |
|---|---|---|
describe_service | Describes what Doctronic offers, who it's for, prices, limits, the terms and privacy links, Doctronic's emergency text, and how to start. | Yes |
start_consult | Checks the person's state and returns the intake questions, the terms to show, and an intake token. Creates no consult. | No |
submit_intake | Sends the person's answers and terms acceptance, starts the consult, and returns its consult_id. | No |
continue_consult | Sends the person's next message and returns the assistant's reply. | No |
get_assessment | Returns the consult's status, title, differential, SOAP note, and summaries. | Yes |
get_care_options | Lists care options with prices, availability, and the handoff each one needs. | Yes |
create_handoff | Creates a one-time link and code that move the consult to doctronic.ai, or a new consent link while consent is pending. A book_visit link needs a finished assessment. | No |
get_handoff_status | Reports whether the person has used a consent link or a handoff link. | Yes |
Read-only tools carry the MCP readOnlyHint annotation. start_consult creates no consult, but
it counts toward the new-consult limit, so it isn't read-only. Calling it again returns a new
intake token. Errors come back as MCP tool errors with a plain-language message.
start_consult
| Argument | Required | Notes |
|---|---|---|
state | Yes | The person's two-letter US state code, such as NY. DC is accepted. |
timezone | No | An IANA timezone, such as America/New_York. Defaults to UTC. Send the same value to submit_intake. |
The call returns a tool error when state isn't a US state code, timezone isn't an IANA
timezone name, or the new-consult limit is reached.
| Result field | Meaning |
|---|---|
intake_token | Pass it to submit_intake. It works once. Treat it as opaque, and never show it to the person. |
expires_at | When the intake token expires, as Unix seconds (UTC). It lasts 15 minutes. |
questions | The intake questions. Each has a field and a prompt to ask. The sex question also has options. |
terms | The terms to show the person. See terms. |
web_consent_required | true when Doctronic requires the person to accept the terms on doctronic.ai. See web consent. |
emergency_text | Doctronic's 911 and 988 text, for the intake. See emergency text. |
{
"intake_token": "dct_int_...",
"questions": [
{ "field": "age", "prompt": "How old are you?" },
{ "field": "sex", "prompt": "What is your sex? (female or male)", "options": ["female", "male"] }
],
"terms": {
"text": "The exact terms text to show the person.",
"version": "...",
"terms_url": "https://www.doctronic.ai/terms-of-service/",
"privacy_url": "https://www.doctronic.ai/privacy-policy/",
"display_verbatim": true
},
"web_consent_required": false,
"emergency_text": "If this may be a medical emergency, call 911 or go to the nearest emergency room now. If you are thinking about harming yourself, call or text 988 to reach the Suicide and Crisis Lifeline."
}Terms
terms has the text to show, its version, the terms_url and privacy_url links, and
display_verbatim, which is always true.
Show the terms verbatim
Show terms.text to the person word for word, with links to terms_url and privacy_url.
Don't summarize, shorten, or reword it. Use the text from the current result, not a stored copy.
Only the person can accept the terms. Never accept them on the person's behalf.
When web_consent_required is false, ask the person whether they accept, and report their
answer in submit_intake. When it's true, show the terms but don't ask for acceptance in chat.
The person accepts on a doctronic.ai page instead. See web consent.
Doctronic records that the person was shown the terms and accepted them, with the terms version.
submit_intake
| Argument | Required | Notes |
|---|---|---|
intake_token | Yes | The token from start_consult. |
age | Yes | The person's age in years. Must be 18 or older. |
sex | Yes | female or male. |
person_shown_terms | Yes | true only after you showed the person terms.text word for word. |
person_accepted_terms | Yes | true only when the person told you they accept the terms. |
initial_message | No | The person's first message, up to the message length limit. |
timezone | No | An IANA timezone. Defaults to UTC. |
The call returns a tool error when:
- The intake token is invalid, expired, or already used.
- The person is under 18, or
ageisn't an age in years. person_shown_termsisfalse.initial_messageis too long, ortimezoneisn't an IANA timezone name.- The daily consult limit is reached.
If the person declines the terms, don't call submit_intake. The consult can't run without them.
To let the person read and accept the terms on doctronic.ai instead, set person_accepted_terms
to false.
The result has these fields. Which ones are set depends on status.
| Result field | Meaning |
|---|---|
consult_id | The handle for this consult. Pass it to every other tool. Never show it to the person. |
expires_at | When the consult_id expires, as Unix seconds (UTC). |
status | consent_pending while the person still has to accept the terms on doctronic.ai. Otherwise gathering, assessment_ready, or ended. See consult status. |
message | The assistant's reply to initial_message, when one was sent. |
cta | A suggested next step. See calls to action. |
conversation_ended | true when the assistant ended the conversation. |
off_topic | true when Doctronic treated the message as outside the consult. |
consent_url | The doctronic.ai page where the person accepts the terms. null unless status is consent_pending. |
code | The consent link's code. Pass it to get_handoff_status. null unless status is consent_pending. |
consent_expires_at | When the consent link expires, as Unix seconds (UTC). null unless status is consent_pending. |
emergency_text | Doctronic's 911 and 988 text, for the wait before the person accepts. null unless status is consent_pending. See emergency text. |
When the person accepted the terms and Doctronic doesn't require web consent, the consult starts
right away. If you sent initial_message, the result carries the assistant's reply. If that first
message fails, you still get the consult_id, and next_step tells you to send the message again
with continue_consult.
Otherwise, status is consent_pending. See web consent.
Web consent
Doctronic can require the person to accept the terms on a doctronic.ai page instead of in your
agent's conversation. When it does, web_consent_required is true, and submit_intake returns
consent_pending whatever person_accepted_terms says. You still show the terms and set
person_shown_terms, but you don't ask the person to accept them in chat.
- Show the person the
consent_urlandcode. The page shows Doctronic's terms and asks them to accept. - When the person says they're done, call
get_handoff_statuswith theconsult_idandcode. Astatusofclaimedmeans they accepted. - Send the person's first message with
continue_consult.submit_intakedoesn't sendinitial_messagewhile consent is pending.
If the person describes a possible emergency while they wait, show them emergency_text from the
consent_pending result exactly as written, before anything else. Send what they said with
continue_consult once they've accepted.
Until the person accepts, continue_consult returns a tool error. Accepting the terms doesn't
move the consult, so your consult_id keeps working.
Only the doctronic.ai page can claim a consent code. The page runs Doctronic's bot protection before it records the acceptance, so your agent can't accept the terms for the person by sending the code to the page's web API.
A consent link expires at consent_expires_at, 15 minutes after it's created. If it expires
before the person accepts, call create_handoff with purpose set to accept_terms for a new
link. Its continue_url is the new consent page. Once the person has accepted, create_handoff
refuses that purpose.
continue_consult
| Argument | Required | Notes |
|---|---|---|
consult_id | Yes | The handle from submit_intake. |
message | Yes | The person's next message in their own words, up to the message length limit. |
timezone | No | An IANA timezone. Defaults to UTC. |
The result has consult_id, status, message, cta, conversation_ended, and off_topic,
as above.
The call returns a tool error when:
- The person hasn't accepted the terms yet.
- The consult has ended, moved to doctronic.ai, or doesn't exist.
- The consult has used all its turns. The error points you to
get_assessmentandcreate_handoff. messageis empty or too long.- Doctronic couldn't finish the reply. The error asks you to try again.
A call refused because consent is pending, or because the consult moved to doctronic.ai, doesn't count as a turn.
Send one message at a time and wait for its result. When a tool error asks you to try again, wait a moment and send the message again. Don't resend a message automatically after a call times out with no result: Doctronic may already have processed it.
get_assessment
The result has status, title, differential, soap_note_markdown, summary, and
detailed_summary_markdown.
detailed_summary_markdownis the consult's detailed summary for the person. On a symptom consult it lists the likely conditions in rank order, with the tests and treatments to discuss with a physician. This is where the likely conditions are on most consults.differentialis the same list in structured form, with a likelihood and the findings for and against each condition. Doctronic only fills it when the consult ran on a flow that publishes one; otherwise it'snull, and the detailed summary carries the list. Present either as possibilities to discuss with a clinician, not as a diagnosis.summaryis the consult's short summary for the person.summaryanddetailed_summary_markdownarenullwhilestatusisgathering.- Before the consult is complete, most fields are
null.
An ended consult's assessment stays readable until its consult_id expires.
get_care_options
The result has options and an insurance note. Each option has option, label,
description, price_usd, availability, requires_handoff, and handoff_purpose.
option | handoff_purpose | What it is |
|---|---|---|
video_visit | book_visit | A video visit with a licensed Doctronic physician, who reviews the consult first. Bookable once the assessment is ready. |
prescription_refill | refill | A refill of an existing prescription after physician review, in states that allow asynchronous telehealth visits. Controlled substances aren't prescribed. |
save_consult | save_consult | Keeps the consult on doctronic.ai. Free. |
Show the person the prices and availability from the result. insurance says which options take
insurance. Doctronic confirms coverage on doctronic.ai.
Consult status
status | Meaning | What to do |
|---|---|---|
consent_pending | The person hasn't accepted the terms on doctronic.ai yet. | Show the consent_url. Continue once get_handoff_status reports claimed. |
gathering | The assistant is still asking questions. | Relay its message and send the person's answer with continue_consult. |
assessment_ready | The consult is complete. | Call get_assessment, then get_care_options. |
ended | The conversation is over, for example after a safety event. | Show any safety text first. Send no more messages for this consult. get_assessment still returns what it produced. If the person wants to go on, start a new consult. |
What every result carries
Every tool result includes three fields:
| Field | Contents | What your agent does |
|---|---|---|
disclosure | A fixed statement that Doctronic is an AI health tool, not a physician, that the consult isn't a diagnosis, and to call 911 in an emergency. | Show it to the person once, with the intake questions. |
safety | The safety state of this result. See safety. | Check it before anything else in the result. |
next_step | One sentence about what to do next. | Use it to choose your next call. It's written for your agent, not for the person. |
Safety
{
"level": "emergency",
"text": "If this may be a medical emergency, call 911 or go to the nearest emergency room now. If you are thinking about harming yourself, call or text 988 to reach the Suicide and Crisis Lifeline.",
"display_verbatim": true
}level | Meaning | text |
|---|---|---|
none | No safety condition. | null |
caution | Doctronic flagged a safety concern. | A message for the person. |
emergency | The person may need emergency care. | The 911 and 988 instructions above, sometimes after a message about the person's situation. |
Show safety text verbatim
When display_verbatim is true, your agent displays text to the person word for word, before
anything else from that result. Don't summarize, shorten, reword, or leave it out.
When level is emergency:
- Show the safety text first, and stop the routine flow. Don't offer care options, booking, or other next steps in the same reply.
- Don't send another message for the person automatically.
The server doesn't contact emergency services. The safety text tells the person how to get help.
Decide from the safety field, not from the assistant's wording.
Emergency text
Before Doctronic has replied to the person, no result carries safety.text. For that stretch,
these results carry emergency_text, the same 911 and 988 text an emergency safety result
ends with:
| Result | emergency_text |
|---|---|
describe_service | Always set, so your agent has it before it asks the person anything. |
start_consult | Always set. |
submit_intake | Set when status is consent_pending. Otherwise null. |
If the person describes a possible emergency during the intake, or while they wait to accept the
terms, show them emergency_text exactly as written, before anything else. Then go on:
- During the intake, send what they said as
initial_messagewithsubmit_intake. - While they wait to accept the terms, send what they said with
continue_consultonce they've accepted. The intake is already spent.
Once Doctronic has replied, the safety field carries any emergency guidance. Add none of your
own.
Calls to action
cta has a kind and an optional text label. kind is null when there's no call to action.
kind | Meaning |
|---|---|
see_doctor | A visit with a physician may help. Offer a book_visit handoff once status is assessment_ready. |
see_doctor_in_person | An in-person visit may help. Show text when present. |
emergency_room | Emergency care may be needed. Treat it like an emergency safety result. |
refill | A prescription refill may fit. Offer a refill handoff. |
save | Offer a save_consult handoff. |
other | Another next step. Show text when present. |
When safety.level is emergency, the safety text takes precedence over any call to action.
The consult_id
submit_intake returns a consult_id that begins with dct_cons_. Pass it to every other tool.
- It's state, not identity. It names one anonymous consult. It doesn't identify the person, and it isn't a credential for anything else at Doctronic.
- Treat it as opaque. Its contents are encrypted. Don't parse it or build one.
- Keep it in tool calls. Never show or read it to the person. The same goes for the
intake_token. - Keep it with the person's conversation. Anyone who holds it can continue and read that consult. Use one per person, keep it out of logs, and don't share it.
- It expires after 24 hours. After that, tools return an error, and the person needs a new
consult. To keep the consult, create a
save_consulthandoff before it expires. - A claimed handoff ends agent access. Once the person confirms a
book_visit,refill, orsave_consulthandoff, the consult belongs to their Doctronic account. Theconsult_idstops working for it.get_handoff_statusstill answers. Accepting the terms at a consent link doesn't end agent access.
Hand off for care
The MCP server doesn't book visits, take payment, or handle prescriptions. doctronic.ai does, after a one-time handoff.
- Call
get_care_optionsand show the person the options, their prices, and their availability. - When the person picks one, call
create_handoffwith itshandoff_purpose. - Show the person the
continue_urlandcodefrom the result. Tell them the page will ask them to sign in to Doctronic, or create a free account, and to confirm the consult is theirs. The result also has the code'sexpires_atandinstructionsfor your agent. - The person opens the link, signs in, and confirms. Doctronic moves the consult to their account
and opens it on doctronic.ai, and your
consult_idstops working. - To see whether they've used the link, call
get_handoff_status. It returnspending,claimed, orexpired.
purpose | Where the person lands on doctronic.ai |
|---|---|
book_visit | The booking flow, with this consult, to book a video visit with a licensed Doctronic physician. If booking can't start, the consult opens instead. |
refill | The consult, where they can request a prescription refill. |
save_consult | The consult, saved to their account. |
Booking needs a finished assessment
create_handoff refuses book_visit until the consult's status is assessment_ready, because
booking a visit needs the consult's SOAP note. The refusal creates no code and tells your agent
to keep the consult going with continue_consult. If the person wants to see a doctor now, send
that in their words, so Doctronic can wrap up. If the consult ended before the assessment was
ready, the person needs a new consult to book from.
A handoff code works like a ticket:
- One use. The first person to open the link and confirm claims the consult. Show the link only to the person in the consult.
- Short-lived. An unused code expires after 15 minutes. If it expires, create a new one.
- No identity. The code doesn't identify the person, and it grants nothing once claimed or expired.
- For the person only. The doctronic.ai page completes a bot check before it claims a code. Your agent can't claim a code itself, whether to continue a consult or to accept the terms.
Every expires_at in a tool result is in Unix seconds (UTC). The doctronic.ai pages behind
consent_url and continue_url read the code through their own web API, which reports times in
ISO 8601. Your agent doesn't need that API. It can't claim a code through it either, because a
claim needs the page's bot check.
Limits
| Limit | Current value | When it's reached |
|---|---|---|
| Requests per client IP address | 120 per minute | HTTP 429 with a Retry-After header |
| New consults per client IP address | 10 per hour, or 3 per hour for clients that don't identify as an agent | start_consult returns a tool error |
| New consults per day, across all agents | 2,000 per UTC day | submit_intake returns a tool error until the next UTC day |
| Intake token | One use, 15 minutes | submit_intake returns a tool error. Call start_consult again. |
| Turns per consult | 60, including a first message sent with submit_intake | continue_consult returns a tool error. Hand off or start a new consult. |
| Message length | 1,152 characters. describe_service reports the current value. | The tool returns a tool error |
consult_id lifetime | 24 hours | Tools return a tool error. Start a new consult. |
| Unused consent or handoff code lifetime | 15 minutes | get_handoff_status returns expired. Create a new code. |
These are the production defaults. Staging can differ, and limits can change during the private
beta. Wait for Retry-After before you retry a 429. Its body is a JSON-RPC error with code
-32002.
A refused call comes back as a tool result with isError: true and a short reason. Some MCP
clients prefix that text with Error executing tool <name>:. Show the reason to the person when it
asks for something only they can do, such as accepting the terms.
When the server is turned off, every request returns HTTP 503 with JSON-RPC error code -32001:
{
"jsonrpc": "2.0",
"error": {
"code": -32001,
"message": "The Doctronic agent surface is not enabled."
},
"id": null
}What the server doesn't do
- No booking, payment, or prescriptions. These need a handoff to doctronic.ai.
- No account access. The server can't sign anyone in or read a Doctronic account, past consults, or records.
- No identity. The tools take no name, date of birth, or contact details. Leave them out of consult messages too.
- No consent on the person's behalf. The person answers the intake questions and accepts the terms. Your agent relays what they said.
- No emergency dispatch. The server doesn't contact emergency services. It returns emergency text for your agent to display verbatim.
- No consults for minors or for people outside the United States.
- No access to your organization's data. It runs its own anonymous consults and can't reach the users or chats you create with the Doctronic API.