Open Dental MCP by usefulapi

Read appointments, open slots, providers, procedure codes and recalls in Open Dental.

Should I use this

Quality & Safety

A
Description quality
98%
Schema completeness
88%
Naming quality
96%
Poisoning risk
100%
Permission match
100%
Protocol compliance
100%

Findings (3)

  • LOWTool 'opendental_list_asap_appointments' name length outside 3-30 rangein opendental_list_asap_appointments
  • LOWTool 'opendental_list_appointment_types' name length outside 3-30 rangein opendental_list_appointment_types
  • LOWTool 'opendental_list_procedure_codes' name length outside 3-30 rangein opendental_list_procedure_codes

Based on automated analysis of tool definitions and protocol compliance.

Context Cost

~4,285Tokens (tool definitions)
~1.9 KBTypical response size
Significant attention impact (3.35% of 128k context)

This is the approximate number of tokens consumed each time the server's tools are loaded into a model's context. Higher counts reduce the attention available for other tasks.

Install

One-Click Install

Add this to your `claude_desktop_config.json` file:

{
  "mcpServers": {
    "open-dental": {
      "url": "https://open-dental.usefulapi.io/mcp"
    }
  }
}

Remote endpoints

https://open-dental.usefulapi.io/mcpstreamable-http

What it can do

Tool inventory

Tools (17)

🟢 Read-only🟡 Write🔴 Delete⚪ Unknown
🟢opendental_get_api_permissions

The API permissions this customer key has (ReadAll, Appointments, Patients, ...), each true or false. The tools here only read, so ReadAll is the one they need. Use it to explain why a call is refused. GET /securityperms.

Input Schema

{
  "type": "object",
  "properties": {},
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🟢opendental_list_patients(last_name, first_name, status, birthdate, guarantor, ...)

List patients with the fast search (name, birthdate, status, guarantor, primary provider, clinic, changed since). Names match partially, case-insensitive. Returns demographics, balances and contact data (protected health information); SSNs are removed. Page with limit/offset. GET /patients/Simple.

Input Schema

{
  "type": "object",
  "properties": {
    "last_name": {
      "description": "Last name or the start of it.",
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "first_name": {
      "description": "First name or the start of it.",
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "status": {
      "description": "Patient status. Omit for all.",
      "type": "string",
      "enum": [
        "Patient",
        "NonPatient",
        "Inactive",
        "Archived",
        "Deceased",
        "Prospective"
      ]
    },
    "birthdate": {
      "type": "string",
      "description": "A date, yyyy-MM-dd."
    },
    "guarantor": {
      "description": "PatNum of the guarantor (the person responsible for the account): lists a family.",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991
    },
    "primary_provider": {
      "description": "ProvNum of the primary provider.",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991
    },
    "clinic": {
      "description": "ClinicNum. Omit for all clinics.",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    },
    "changed_since": {
      "description": "Only items changed after this time, yyyy-MM-dd HH:mm:ss (DateTStamp).",
      "type": "string"
    },
    "limit": {
      "description": "Results per request, 1-100 (default 50).",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 100
    },
    "offset": {
      "description": "Items to skip (default 0). Pass the previous reply's next_offset.",
      "type": "integer",
      "minimum": 0,
      "maximum": 10000000
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🟢opendental_search_patients(last_name, first_name, phone, email, address, ...)

Search patients the way Open Dental's Patient Select window does: by name, phone, email, address, city, state, chart number, birthdate or insurance subscriber id. Slower than opendental_list_patients on large databases; use it for phone, email and address lookups. Returns the Patient Select columns only (use opendental_get_patient for the full record). GET /patients.

Input Schema

{
  "type": "object",
  "properties": {
    "last_name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "first_name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "phone": {
      "description": "Any of the patient's phone numbers.",
      "type": "string",
      "minLength": 1,
      "maxLength": 30
    },
    "email": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "address": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "city": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "state": {
      "type": "string",
      "minLength": 1,
      "maxLength": 50
    },
    "chart_number": {
      "type": "string",
      "minLength": 1,
      "maxLength": 50
    },
    "birthdate": {
      "type": "string",
      "description": "A date, yyyy-MM-dd."
    },
    "subscriber_id": {
      "description": "Insurance subscriber id.",
      "type": "string",
      "minLength": 1,
      "maxLength": 50
    },
    "hide_inactive": {
      "description": "Hide inactive patients.",
      "type": "boolean"
    },
    "guarantors_only": {
      "description": "Only guarantors.",
      "type": "boolean"
    },
    "limit": {
      "description": "Results per request, 1-100 (default 50).",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 100
    },
    "offset": {
      "description": "Items to skip (default 0). Pass the previous reply's next_offset.",
      "type": "integer",
      "minimum": 0,
      "maximum": 10000000
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🟢opendental_get_patient(patient_id)

One patient's full record by PatNum: name, status, birthdate, contact details, balances, providers, preferred contact methods, urgent notes. SSN is removed. GET /patients/{PatNum}.

Input Schema

{
  "type": "object",
  "properties": {
    "patient_id": {
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991,
      "description": "PatNum."
    }
  },
  "required": [
    "patient_id"
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🟢opendental_get_patient_notes(patient_id)

A patient's note fields: family financial, medical, service, medical complete, treatment (odontogram) notes and the emergency contact (ICE name and phone). GET /patientnotes/{PatNum}.

Input Schema

{
  "type": "object",
  "properties": {
    "patient_id": {
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991,
      "description": "PatNum."
    }
  },
  "required": [
    "patient_id"
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🟢opendental_list_appointments(patient_id, status, operatory, date, date_start, ...)

List appointments, filtered by patient, status, operatory, a day or a date range, clinic, appointment type, or changed since. Each has AptNum, PatNum, status, AptDateTime, pattern, provider, operatory, note and procedures. Use `date` for one day (e.g. today's schedule) or date_start/date_end for a range. GET /appointments.

Input Schema

{
  "type": "object",
  "properties": {
    "patient_id": {
      "description": "PatNum.",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991
    },
    "status": {
      "type": "string",
      "enum": [
        "Scheduled",
        "Complete",
        "UnschedList",
        "ASAP",
        "Broken",
        "Planned",
        "PtNote",
        "PtNoteCompleted"
      ]
    },
    "operatory": {
      "description": "OperatoryNum.",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991
    },
    "date": {
      "description": "One day.",
      "type": "string"
    },
    "date_start": {
      "description": "Range start, inclusive.",
      "type": "string"
    },
    "date_end": {
      "description": "Range end, inclusive.",
      "type": "string"
    },
    "clinic": {
      "description": "ClinicNum; 0 = appointments not attached to a clinic.",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    },
    "appointment_type": {
      "description": "AppointmentTypeNum.",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991
    },
    "changed_since": {
      "description": "Only items changed after this time, yyyy-MM-dd HH:mm:ss (DateTStamp).",
      "type": "string"
    },
    "limit": {
      "description": "Results per request, 1-100 (default 50).",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 100
    },
    "offset": {
      "description": "Items to skip (default 0). Pass the previous reply's next_offset.",
      "type": "integer",
      "minimum": 0,
      "maximum": 10000000
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🟢opendental_get_appointment(appointment_id)

One appointment by AptNum, with its patient, time, provider, operatory, status, confirmation, note and procedures. GET /appointments/{AptNum}.

Input Schema

{
  "type": "object",
  "properties": {
    "appointment_id": {
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991,
      "description": "AptNum."
    }
  },
  "required": [
    "appointment_id"
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🟢opendental_list_asap_appointments(clinic, provider, limit, offset)

The ASAP list: appointments whose priority is ASAP (patients who want an earlier time). ClinicNum is required when the office uses clinics (use 0 for no clinic). GET /appointments/ASAP.

Input Schema

{
  "type": "object",
  "properties": {
    "clinic": {
      "description": "ClinicNum; 0 = not attached to a clinic. Required if clinics are enabled.",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    },
    "provider": {
      "description": "ProvNum.",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991
    },
    "limit": {
      "description": "Results per request, 1-100 (default 50).",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 100
    },
    "offset": {
      "description": "Items to skip (default 0). Pass the previous reply's next_offset.",
      "type": "integer",
      "minimum": 0,
      "maximum": 10000000
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🟢opendental_find_open_slots(date, date_start, date_end, length_minutes, provider, ...)

Open schedule slots for a provider and operatory (like the Appointments module search): each has DateTimeStart, DateTimeEnd, ProvNum and OpNum. Dates must be today or later; the default is the next 14 days. GET /appointments/Slots.

Input Schema

{
  "type": "object",
  "properties": {
    "date": {
      "description": "One day (today or later).",
      "type": "string"
    },
    "date_start": {
      "type": "string",
      "description": "A date, yyyy-MM-dd."
    },
    "date_end": {
      "type": "string",
      "description": "A date, yyyy-MM-dd."
    },
    "length_minutes": {
      "description": "Skip slots shorter than this many minutes.",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 1440
    },
    "provider": {
      "description": "ProvNum (default: the practice default provider).",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991
    },
    "operatory": {
      "description": "OperatoryNum the provider is assigned to.",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🟢opendental_list_providers(clinic, changed_since, limit, offset)

Providers (dentists, hygienists, billing entities) with ProvNum, abbreviation, name, specialty, status and hidden flags. SSN/TIN is removed. Optionally only providers usable at one clinic, or only those changed since a time (not both). GET /providers.

Input Schema

{
  "type": "object",
  "properties": {
    "clinic": {
      "description": "ClinicNum: non-hidden providers not restricted to another clinic.",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991
    },
    "changed_since": {
      "description": "Only items changed after this time, yyyy-MM-dd HH:mm:ss (DateTStamp).",
      "type": "string"
    },
    "limit": {
      "description": "Results per request, 1-100 (default 50).",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 100
    },
    "offset": {
      "description": "Items to skip (default 0). Pass the previous reply's next_offset.",
      "type": "integer",
      "minimum": 0,
      "maximum": 10000000
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🟢opendental_list_operatories(clinic, limit, offset)

Operatories (treatment rooms) with OperatoryNum, name, abbreviation, assigned dentist and hygienist, clinic and hidden flag. GET /operatories.

Input Schema

{
  "type": "object",
  "properties": {
    "clinic": {
      "description": "ClinicNum.",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991
    },
    "limit": {
      "description": "Results per request, 1-100 (default 50).",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 100
    },
    "offset": {
      "description": "Items to skip (default 0). Pass the previous reply's next_offset.",
      "type": "integer",
      "minimum": 0,
      "maximum": 10000000
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🟢opendental_list_clinics(limit, offset)

All non-hidden clinics (locations) with address, billing address and phone. An empty list means the office does not use the Clinics feature. GET /clinics.

Input Schema

{
  "type": "object",
  "properties": {
    "limit": {
      "description": "Results per request, 1-100 (default 50).",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 100
    },
    "offset": {
      "description": "Items to skip (default 0). Pass the previous reply's next_offset.",
      "type": "integer",
      "minimum": 0,
      "maximum": 10000000
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🟢opendental_list_appointment_types(limit, offset)

Appointment types (AppointmentTypeNum, name, default pattern, required procedure codes, hidden flag). GET /appointmenttypes.

Input Schema

{
  "type": "object",
  "properties": {
    "limit": {
      "description": "Results per request, 1-100 (default 50).",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 100
    },
    "offset": {
      "description": "Items to skip (default 0). Pass the previous reply's next_offset.",
      "type": "integer",
      "minimum": 0,
      "maximum": 10000000
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🟢opendental_list_procedure_codes(changed_since, limit, offset)

The office's procedure codes (CodeNum, ADA code, description, category, treatment area, hygiene flag). Use CodeNum to filter procedure logs. Page with limit/offset. GET /procedurecodes.

Input Schema

{
  "type": "object",
  "properties": {
    "changed_since": {
      "description": "Only items changed after this time, yyyy-MM-dd HH:mm:ss (DateTStamp).",
      "type": "string"
    },
    "limit": {
      "description": "Results per request, 1-100 (default 50).",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 100
    },
    "offset": {
      "description": "Items to skip (default 0). Pass the previous reply's next_offset.",
      "type": "integer",
      "minimum": 0,
      "maximum": 10000000
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🟢opendental_list_procedure_logs(patient_id, appointment_id, procedure_date, status, clinic, ...)

Procedure log entries (treatment-planned, complete, existing, referred, deleted) filtered by patient, appointment, date, status, clinic, procedure code or creation time. Each has ProcNum, PatNum, ProcDate, fee, tooth, surface, status, provider and code. Page with limit/offset. GET /procedurelogs.

Input Schema

{
  "type": "object",
  "properties": {
    "patient_id": {
      "description": "PatNum.",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991
    },
    "appointment_id": {
      "description": "AptNum.",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991
    },
    "procedure_date": {
      "type": "string",
      "description": "A date, yyyy-MM-dd."
    },
    "status": {
      "description": "TP treatment plan, C complete, EC/EO existing, R referred out, D deleted, Cn condition, TPi inactive TP.",
      "type": "string",
      "enum": [
        "TP",
        "C",
        "EC",
        "EO",
        "R",
        "D",
        "Cn",
        "TPi"
      ]
    },
    "clinic": {
      "description": "ClinicNum.",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    },
    "code_num": {
      "description": "CodeNum from opendental_list_procedure_codes.",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991
    },
    "created_since": {
      "description": "Procedures created on or after this time.",
      "type": "string"
    },
    "limit": {
      "description": "Results per request, 1-100 (default 50).",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 100
    },
    "offset": {
      "description": "Items to skip (default 0). Pass the previous reply's next_offset.",
      "type": "integer",
      "minimum": 0,
      "maximum": 10000000
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🟢opendental_list_commlogs(patient_id, changed_since, limit, offset)

Communication log entries (calls, texts, emails, letters) for a patient or changed since a time: CommlogNum, PatNum, date, mode, sent or received, note. Page with limit/offset. GET /commlogs.

Input Schema

{
  "type": "object",
  "properties": {
    "patient_id": {
      "description": "PatNum.",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991
    },
    "changed_since": {
      "description": "Only items changed after this time, yyyy-MM-dd HH:mm:ss (DateTStamp).",
      "type": "string"
    },
    "limit": {
      "description": "Results per request, 1-100 (default 50).",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 100
    },
    "offset": {
      "description": "Items to skip (default 0). Pass the previous reply's next_offset.",
      "type": "integer",
      "minimum": 0,
      "maximum": 10000000
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🟢opendental_list_recalls_due(date_start, date_end, provider, clinic, recall_type, ...)

The Recall List as in Open Dental's Appointment module: patients due for recall (prophy, perio) with due date, interval, reminders sent, last reminder, preferred contact and status. Defaults follow the office's recall preferences. Page with limit/offset. GET /recalls/List.

Input Schema

{
  "type": "object",
  "properties": {
    "date_start": {
      "description": "Due date range start.",
      "type": "string"
    },
    "date_end": {
      "description": "Due date range end.",
      "type": "string"
    },
    "provider": {
      "description": "ProvNum (primary or secondary provider).",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991
    },
    "clinic": {
      "description": "ClinicNum; 0 = unassigned.",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    },
    "recall_type": {
      "description": "Typically \"Prophy\" or \"Perio\".",
      "type": "string",
      "minLength": 1,
      "maxLength": 50
    },
    "include_reminded": {
      "description": "Include patients who already received a reminder (default false).",
      "type": "boolean"
    },
    "limit": {
      "description": "Results per request, 1-100 (default 50).",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 100
    },
    "offset": {
      "description": "Items to skip (default 0). Pass the previous reply's next_offset.",
      "type": "integer",
      "minimum": 0,
      "maximum": 10000000
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}

Community

Rate this Server

Evidence

Recent observations

verifiedversion not recorded17 tools