Overview
The Doris MCP server exposes the ontology as tools that AI assistants can call directly. Ask questions about deals, commitments, and stakeholders in natural language — the AI translates your question into the right ontology queries automatically.
Endpoint: https://mcp.meetdoris.com/mcp
Transport: Streamable HTTP
Auth: OAuth 2.1 via your Doris login (Auth0). On first connection, a browser popup handles authentication.
Setup
Claude Code
claude mcp add --transport http doris https://mcp.meetdoris.com/mcp
Claude Desktop
Add to your MCP config (~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"doris": {
"command": "npx",
"args": ["mcp-remote", "https://mcp.meetdoris.com/mcp"]
}
}
}
Cursor
Settings > Features > MCP > Add Server > Streamable HTTP > https://mcp.meetdoris.com/mcp
ChatGPT
Developer Mode > Add Connector > https://mcp.meetdoris.com/mcp
Windsurf
Add to your MCP configuration:
{
"mcpServers": {
"doris": {
"serverUrl": "https://mcp.meetdoris.com/mcp"
}
}
}
The MCP server exposes 10 tools that mirror the REST API — eight read tools and two write tools:
ontology_types
List all entity types or get the schema for a specific type.
| Parameter | Type | Required | Description |
|---|
type_name | string | No | Specific type to get schema for. Omit to list all types. |
Example prompt: “What entity types are available in Doris?“
ontology_resolve
Resolve a single entity by type and ID, with optional expand keys for related data.
| Parameter | Type | Required | Description |
|---|
type_name | string | Yes | Entity type (e.g., deal, meeting, commitment) |
object_id | string | Yes | Entity ID |
expand | string[] | No | Related data to include (e.g., stakeholders, commitments) |
expand_params | object | No | Per-expand-key options, such as transcript search terms or limits |
Available expand keys for deals: stakeholders, commitments, objections, competitors, meetings, strategy, emails, agent_summary, activity, insights, assessments, brief, pipeline_stages
Example prompt: “Show me the Acme deal with its stakeholders and open commitments”
ontology_list
List entities of a specific type with filtering, sorting, and expand support.
| Parameter | Type | Required | Description |
|---|
object_type | string | Yes | Type to list (e.g., deal, commitment) |
filters | object | No | Filter criteria (e.g., {"stage": "Discovery"}) |
sort | string | No | Sort field and direction (e.g., created_at:desc) |
expand | string[] | No | Related data to include |
limit | number | No | Max results (default 25, max 100) |
offset | number | No | Pagination offset |
mine | boolean | No | Restrict to entities owned by or assigned to you |
include_links | boolean | No | Include relationship counts for each result |
changes_since | string | No | Only return entities created or updated after this ISO-8601 timestamp |
cursor | string | No | Keyset pagination cursor from a previous response |
Example prompt: “List all deals in the Negotiation stage with their commitments”
ontology_search
Full-text search across all entity types.
| Parameter | Type | Required | Description |
|---|
query | string | No | Free-text search query |
types | string[] | No | Restrict search to specific types |
filters | object | No | Filter criteria |
sort | string | No | Sort field and direction |
limit | number | No | Max results (default 25, max 100) |
offset | number | No | Pagination offset |
mine | boolean | No | Restrict to entities owned by or assigned to you |
search_fields | string[] | No | Target search zones such as title, content, or body |
include_links | boolean | No | Include relationship counts for each result |
changes_since | string | No | Only return entities created or updated after this ISO-8601 timestamp |
cursor | string | No | Keyset pagination cursor from a previous response |
Example prompt: “Search for anything mentioning ‘pricing comparison’ across commitments”
ontology_batch
Resolve up to 50 entities in a single call.
| Parameter | Type | Required | Description |
|---|
refs | object[] | Yes | List of {"type": "...", "id": "..."} references |
Example prompt: “Get details on deals deal_1, deal_2, and deal_3”
ontology_traverse
Follow relationships from one entity to discover connected entities.
| Parameter | Type | Required | Description |
|---|
source_type | string | Yes | Type of the source entity |
source_id | string | Yes | ID of the source entity |
link_type | string | No | Filter by relationship type |
target_type | string | No | Filter by target entity type |
limit | number | No | Max results (default 50, max 200) |
link_filter | object | No | Filter by link properties, such as stakeholder role |
Example prompt: “What entities are linked to this deal?“
ontology_aggregate
Group-by aggregation over an object type — counts and metrics bucketed by a field.
| Parameter | Type | Required | Description |
|---|
object_type | string | Yes | Type to aggregate (e.g., deal, commitment) |
group_by | string | No | Field to group results by (e.g., stage, owner_name) |
metrics | string[] | No | Metrics to compute per group (e.g., sum:amount); defaults to a per-group count |
filters | object | No | Filter criteria applied before aggregation |
mine | boolean | No | Restrict to entities owned by/assigned to you |
time_bucket | string | No | Bucket results by time period (e.g., month, week) |
time_field | string | No | Date field to bucket on when time_bucket is set |
Example prompt: “How many deals are in each stage, and what’s the total value per stage?“
search_transcripts
Full-text search over meeting transcript turns — find exactly what was said and where.
| Parameter | Type | Required | Description |
|---|
query | string | Yes | Search terms matched against spoken words |
date_from | string | No | ISO-8601 lower bound on meeting start time |
date_to | string | No | ISO-8601 upper bound on meeting start time |
mine | boolean | No | Restrict to your own meetings; defaults to true |
deal_id | string | No | Restrict to meetings linked to this deal |
meeting_id | string | No | Restrict to a single meeting |
meeting_ids | string[] | No | Restrict to several meetings |
speaker | string | No | Filter to turns whose speaker name contains this string |
limit | number | No | Max turns to return (default 25) |
Example prompt: “Every time security came up in the Acme calls, what was said?”
Writes are a deliberately small, self-describing surface — just two tools. ontology_update makes typed field writes; ontology_action runs named domain actions (e.g. completing a commitment, moving a plan card). You never need a new tool per entity: capabilities are discovered live through ontology_types, which advertises each type’s writable_fields and actions — including each action’s name, description, and JSON-Schema params_schema. As new writable types and actions ship, an AI assistant learns them automatically the next time it inspects the schema, with no client changes.
Writes require the ontology:write scope. Reads only need ontology:read, which every token carries; ontology:write is granted only when your token actually holds it. A read-only token that calls a write tool is rejected with a forbidden error before anything is mutated.
ontology_update
Write typed fields on an entity. Only the fields a type lists under writable_fields are accepted — unknown or read-only fields are rejected (no mass-assignment).
| Parameter | Type | Required | Description |
|---|
type_name | string | Yes | The entity type (see ontology_types for its writable_fields) |
object_id | string | Yes | The entity ID to update |
patch | object | Yes | Field → value map; only the type’s writable fields are allowed |
Returns {"ok": true, "id": ...} on success, or an error envelope (e.g. invalid_request, not_found, forbidden).
Example prompt: “Push the due date on this commitment to next Friday”
ontology_action
Run a named domain action on an entity. Each type advertises its actions (and their parameter schemas) via ontology_types.
| Parameter | Type | Required | Description |
|---|
type_name | string | Yes | The entity type |
object_id | string | Yes | The entity ID the action targets |
action | string | Yes | The action name (e.g. complete, move, add_plan_item) |
params | object | No | Action parameters, per the action’s params_schema |
Returns an {"ok": true, ...} envelope on success (shape varies by action), or an error envelope.
First writable types and actions:
| Type | Action | What it does |
|---|
commitment | complete | Mark a commitment completed (no params) |
commitment | reopen | Reopen a completed commitment to pending (no params) |
commitment | update | Edit description, owner_type, or due_date |
roundtable | add_plan_item | Add a card to the mutual action plan |
roundtable | comment | Post a rep comment on a room artifact |
roundtable_plan_item | move | Move a plan card to a board column |
roundtable_plan_item | update | Edit a plan card’s description / owner / due date / seat |
roundtable_plan_item | remind | Nudge the card owner (buyer-facing — see guardrails) |
Example prompt: “Mark the security-review commitment on the Acme deal as done”
Guardrails
- No machine touches the buyer. Actions that could reach a buyer — such as
remind on a plan card — are flagged buyer_facing and are denied to automated/agent callers. MCP calls are treated as agent-initiated, so these actions return forbidden; they only run on a human surface where a person initiates them.
- Tenant-scoped. Every write is scoped to your tenant. The entity is loaded under your company before any mutation, so a request can never touch another tenant’s data — a foreign ID looks identical to a missing one (
not_found).
- Role-aware. Some actions (e.g. all roundtable writes) require the caller to hold the right role at the entity, such as a rep seat at the room.
Example: complete a commitment via JSON-RPC
curl -X POST https://mcp.meetdoris.com/mcp \
-H "Authorization: Bearer <your-token>" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "ontology_action",
"arguments": {
"type_name": "commitment",
"object_id": "commitment_abc123",
"action": "complete"
}
}
}'
Example Conversations
Once connected, you interact naturally:
You: “What commitments are overdue on the Acme deal?”
The AI calls ontology_resolve with type_name="deal", finds the Acme deal, then expands commitments and filters for overdue status.
You: “Prep me for my 2pm with Globex”
The AI calls ontology_search to find the Globex deal, then ontology_resolve with expand=["stakeholders", "commitments", "strategy", "meetings"] to assemble a full prep brief.
You: “Which deals have been in Discovery for more than 30 days?”
The AI calls ontology_list with object_type="deal" and filters={"stage": "Discovery"}, then checks days_in_stage on each result.
Privacy
The MCP server automatically masks personally identifiable information before data reaches your AI tool. This means contact emails, phone numbers, and LinkedIn URLs are never exposed to external LLM providers.
MCP connections always use masked mode. This is enforced server-side and cannot be changed per-request. For full or anonymous access, use the REST API with the ?privacy= query parameter.
| Level | Names | Emails | Phones | LinkedIn |
|---|
| full | Adam Cross | adam.cross@minesoft.com | +44 7700 900123 | Included |
| masked (MCP default) | Adam C. | a***@minesoft.com | +44 *** *** 0123 | Omitted |
| anonymous | Evaluator #1 | Omitted | Omitted | Omitted |
How masking works
- No setup required — Masking is on by default for all MCP consumers.
- Deal owners preserved — Your team’s names and emails on deals remain visible in masked mode, so you always know who owns a deal.
- Expand keys respected — Stakeholder and email expansions are masked. Non-PII data (commitments, objections, competitors, strategy) passes through unchanged.
- Anonymous mode — Stakeholders are replaced with role-based identifiers (e.g., “Champion #1”, “Evaluator #2”) and the email expansion is omitted entirely. Only available via the REST API.
Which fields are masked
| Field | Masked | Anonymous |
|---|
full_name | First name + initials (Adam C.) | Omitted — replaced by role ID |
email | First char + domain (a***@minesoft.com) | Omitted |
phone | Last 4 digits visible (+44 *** *** 0123) | Omitted |
linkedin_url | Omitted | Omitted |
owner_name | Preserved (your team) | Omitted |
owner_email | Preserved (your team) | Omitted |
sender / sender_email | Initialed / masked | Omitted |
When to use each level
| Level | Best for | Available via |
|---|
| full | Internal dashboards, your own analysis | REST API only |
| masked | AI assistants, external LLM tools | MCP (automatic), REST API |
| anonymous | Customer-facing exports, shared reports | REST API only |
Rate Limits
MCP uses the same tenant-level daily quota as the REST API, with endpoint-specific minute limits:
- 10,000 requests/day per tenant
- 300 req/min for resolve, list, and type operations
- 120 req/min for search
- 60 req/min for batch
Local Development
For local testing with MCP_DEV_MODE=true:
# Start the backend with dev mode
MCP_DEV_MODE=true make web
# Connect Claude Code to local server
claude mcp add --transport http doris-local http://localhost:5000/mcp
Dev mode accepts dev-token as a Bearer token without OAuth.