Skip to content

The oHallo MCP server

Alongside calling the MCP servers you build, oHallo exposes its own MCP server. You can point an MCP client or an agent at it and work with your oHallo data and actions directly: brands, channels, knowledge base, policies, conversations, contacts, accounts, and agent definitions. It authenticates with a normal API key, and every tool is gated by that key’s scopes, exactly like the REST API.

POST https://api.ohallo.eu/api/mcp

The server speaks JSON-RPC 2.0 over HTTP and implements the standard tools/list and tools/call methods.

Send your API key as a Bearer token:

Authorization: Bearer sf_live_v1_...

The key resolves your account. Each tool checks the key’s scopes and returns a permission_denied error if a required scope is missing, so grant the key the scopes for the tools you intend to call. See Authentication for the scope catalogue.

Terminal window
curl -X POST https://api.ohallo.eu/api/mcp \
-H "Authorization: Bearer sf_live_v1_..." \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/list" }'

The account is taken from your key, so you do not pass it. Workspace-scoped tools take a workspace_id argument (optional when the key is itself scoped to one workspace).

Terminal window
curl -X POST https://api.ohallo.eu/api/mcp \
-H "Authorization: Bearer sf_live_v1_..." \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "search_kb_entries",
"arguments": { "workspace_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "query": "return policy" }
}
}'

Each tool requires the scope shown. This is the same permission model as the REST API, so a key that can read conversations over REST can also call the conversation read tools here.

ToolRequired scopePurpose
get_contextany valid keyReturn the account, brand, and workspace context the key reaches
list_brands, get_brandbrands:readList and read the brands the key reaches
create_brand, update_brandbrands:writeCreate a brand, safe to retry with the same slug and name; update a brand, where null clears an optional field and the slug stays fixed
list_workspacesworkspaces:readList the workspaces the key reaches
get_workspaceany valid keyRead a workspace the key reaches, with its spoken greeting per language and whether each greeting’s AI-disclosure confirmation is valid
list_channels, get_channel, check_channel_setupchannels:readRead a workspace’s channels; check a channel’s setup, and an email channel’s DNS records
create_chat_channel, create_email_channel, update_channelchannels:writeCreate a chat or email channel; change a chat or email channel
list_kb_entries, get_kb_entry, search_kb_entries, list_kb_topicskb:readList, read or search knowledge base entries; list the topics in use
create_kb_entry, update_kb_entry, approve_kb_entry, reject_kb_entrykb:writeManage knowledge base entries
list_policy_entries, get_policy_entry, search_policy_entries, list_policy_topicspolicy:readList, read or search policies; list the topics in use
create_policy_entry, update_policy_entry, approve_policy_entry, reject_policy_entrypolicy:writeManage policies
list_conversations, get_conversation, list_attention_itemsconversations:readRead conversations and attention items
reply_to_conversation, compose_conversation, resolve_attention_itemconversations:writeReply, compose, and resolve attention items
search_contacts, get_contactcontacts:readRead contacts
create_contact, update_contactcontacts:writeCreate and update contacts
search_accounts, get_account, get_account_contactsaccounts:readRead accounts
create_account, update_accountaccounts:writeCreate and update accounts
list_agents, get_agentagents:readRead agent definitions
update_agent_instructionsagents:writeUpdate an agent’s instructions
create_agent, update_agentagents:write, and mcp:write for the agent’s MCP connectionsCreate or change an agent
list_agent_models, check_agent_reachabilityagents:readList the models an agent can use; check whether the assistant can hand tasks to an agent in a workspace
list_mcp_connections, get_mcp_connectionmcp:readRead the MCP connections the key reaches, their tools and each tool’s governance
register_mcp_connection, test_mcp_connectionmcp:writeRegister one of the account’s business systems as a connection; call it again and record its status
set_mcp_connection_instructions, set_mcp_tool_risk_classifications, set_mcp_tool_assurance, set_mcp_tool_scopemcp:writeGovern a connection and its tools
assign_mcp_tools_to_agent, detach_mcp_connection_from_agentmcp:write, agents:writeGive an agent the tools it may call on a connection; remove its access
list_mcp_guardrailsmcp:readList a connection’s guardrails
create_mcp_guardrailmcp:writeAdd a guardrail to a tool

Call tools/list for the authoritative set and each tool’s input schema; the catalogue above is a summary.

list_kb_entries and list_policy_entries return every entry of the workspace as a list, or those with the status given. With limit (1 to 100), cursor or both, they return one page in creation order, { "data": [...], "nextCursor": "..." }, of at most limit entries, 50 when only cursor is sent; pass nextCursor as cursor to read the next page, and the last page carries no nextCursor.

get_kb_entry and get_policy_entry take an entry_id and return that entry. list_kb_topics and list_policy_topics return { "topics": [...] }: the topics of the workspace’s approved entries, each once, sorted by name.

approve_kb_entry and approve_policy_entry approve an entry that awaits review, with status proposed or pending_review. The question, answer and topics sent, or for a policy the topic, rule_text and conditions, are approved in place of the entry’s own, and the fields not sent stay as they are. A proposal that changes an existing entry is applied to that entry: it takes the proposal’s fields, with the edits in place of the ones sent, and a knowledge base entry keeps its own question, and its own topics when the proposal has none and the edits give none; topics given as an empty list clear them. When that entry was edited after the proposal was written, the tool answers validation_error with the entry as it stands, and calling it again with expected_target_version_id set to that entry’s publishedVersionId applies the proposal onto it.

A workspace holds one knowledge base entry per question and one policy per rule text. A create, an update or an approval that would give a second one the same question or rule text answers validation_error naming the entry that holds it, to be changed with update_kb_entry or update_policy_entry instead. When the account reviews changes before they apply, an update is saved for approval in the dashboard, and its question or rule text is checked when it is approved.

Deleting an entry, its version history and restore use the REST API: for knowledge base entries Delete entry and Version history, for policies Delete entry and Version history.

An agent is a specialist the assistant hands a task to when the task matches the agent’s description. The agents created in your account have the type custom, and these tools create and change them. The agents oHallo provides have the type platform: they are listed for reference, and a change to one answers permission_denied.

ToolRequired scopePurpose
list_agentsagents:readList the account’s agents: id, name, description, type, and for a custom agent its MCP connections and tools
get_agentagents:readRead one agent, including its instructions
create_agentagents:write, mcp:writeCreate an agent that uses one or more MCP connections
update_agentagents:write, and mcp:write when mcp_connections is sentChange an agent; only the fields sent change
update_agent_instructionsagents:writeReplace an agent’s instructions; null clears them
list_agent_modelsagents:readList the models an agent can use
check_agent_reachabilityagents:readReport whether the assistant can hand tasks to an agent in a workspace, and if not, why

create_agent takes display_name, description, system_prompt and mcp_connections, the fields of the REST create in snake case, and needs all four. The description tells the assistant when to use the agent, so name the requests it covers. The system prompt is the agent’s instructions, how it does its work: get_agent returns it as systemPrompt, and update_agent_instructions replaces it on its own. mcp_connections is a list of { "connection_id", "allowed_tools" } entries: each connection comes from list_mcp_connections, and allowed_tools names the tools on it the agent may call. The optional model is an id from list_agent_models; without one the agent runs on Automatic. Each call to create_agent creates a new agent, so check list_agents first.

update_agent changes only the fields sent. mcp_connections replaces the agent’s connections and tools as a whole and names at least one connection; detach_mcp_connection_from_agent removes one. "model": null returns the agent to Automatic.

When your account reviews agent changes before they apply, update_agent and update_agent_instructions return { "pending": true, "versionId": "..." } and the change waits for approval in the dashboard. In that mode, an agent’s connections are changed with assign_mcp_tools_to_agent and detach_mcp_connection_from_agent, or the assignment endpoints.

Deleting an agent uses the REST API: Delete custom agent.

  1. list_mcp_connections finds the connection to the business system and the names of its tools. A system that is not connected is registered first, as Registering a connection describes.
  2. list_agent_models lists the models, when the agent should run on a particular one.
  3. create_agent creates the agent with that connection and the tools it needs.
  4. check_agent_reachability, with the workspace the agent serves, confirms the assistant hands matching tasks to the agent there ("reachable": true). Otherwise reasons says what to change: no_mcp_connections means the agent has no connection, and mcp_connection_unavailable means the connections in connectionIds do not serve that workspace: each is scoped to another workspace or another brand, is inactive, or has been removed. A connection serves a workspace when it is scoped to that workspace, to the workspace’s brand or to the whole account.

A key restricted to a brand or workspace gives an agent only connections its restriction covers. A connection outside the restriction answers validation_error, as an unknown one does, and a key restricted to a workspace answers permission_denied for a connection its brand shares across workspaces, as the assignment endpoints do. The key changes an agent only when every connection the agent uses is inside its restriction, and detaches an agent from any connection inside its restriction. Deleting an agent cannot be undone and takes a key without a restriction.

ToolRequired scopePurpose
list_mcp_connectionsmcp:readList the MCP connections the key reaches, with the names of their tools
get_mcp_connectionmcp:readRead one connection: its instructions, and each tool’s name, description and governance
register_mcp_connectionmcp:writeRegister one of the account’s business systems as a connection
test_mcp_connectionmcp:writeCall the connection’s endpoint again and record the result as its status
set_mcp_connection_instructionsmcp:writeReplace the instructions the assistant reads before using the connection
set_mcp_tool_risk_classificationsmcp:writeClassify the risk of the connection’s tools
set_mcp_tool_assurancemcp:writeRaise the assurance a caller must reach before one tool runs
set_mcp_tool_scopemcp:writeDeclare whose record one tool touches
assign_mcp_tools_to_agentmcp:write, agents:writeGive an agent the tools it may call on the connection
detach_mcp_connection_from_agentmcp:write, agents:writeRemove an agent’s access to the connection
list_mcp_guardrailsmcp:readList the connection’s guardrails
create_mcp_guardrailmcp:writeAdd a guardrail to one of the connection’s tools

Each connection carries its id, name, type, status and scope (tenant, brand or workspace, with brandId and workspaceId). A connection with the status active is available to the assistant. list_mcp_connections leaves out oHallo’s own connections (type system) unless include_system is true. A key restricted to a brand or workspace reads and changes the connections its restriction covers, and any other connection answers not_found. Deleting a connection, its version history and restore, replacing its whole guardrail set, and changing or removing one guardrail use the REST API: MCP connections.

register_mcp_connection takes name, endpoint_url, the server’s public https address, and auth_type with the credential that type uses: api_key for a bearer token, headers for a server that authenticates by its own header names, or client_id, client_secret and token_endpoint for oauth2_client. brand_id or workspace_id sets where the connection serves; with neither it serves the whole account, and a key restricted to a brand or workspace registers inside its restriction. oHallo calls tools/list on the endpoint with the credential and registers the connection when that succeeds; otherwise the tool answers validation_error with what the endpoint said, and nothing is stored. The answer is connection_id, name, scope and status. Each call registers a new connection, so check list_mcp_connections first.

oHallo stores the credential in its secret store. A connection can also be registered with the REST API, Register a connection, and governed with the tools here.

An agent calls a tool only once it has a risk classification and a scope declaration: set_mcp_tool_risk_classifications sets the classifications, and each one supplies the default assurance a caller must reach before the tool runs; set_mcp_tool_scope sets each tool’s declaration. get_mcp_connection returns each tool’s governance: its risk, the enforcedAssurance a caller must reach, its default and source, its scope declaration as oHallo enforces it, and callable, false for a tool that has no classification or no declaration.

A key raises assurance and never lowers it. set_mcp_tool_assurance takes a level at or above the tool’s enforcedAssurance; a lower level, a reclassification that lowers what a tool enforces, and an acknowledged argument each answer permission_denied. An owner or admin makes a lowering in the dashboard, where it is acknowledged.

set_mcp_tool_scope takes a declaration in the shape get_mcp_connection returns, which Declare the scope of each tool describes. create_mcp_guardrail adds a rule oHallo applies to the calls of one tool, which Guardrails describes. The instructions and risk classifications of oHallo’s own connections are configured by oHallo; their guardrails, assurance, scope declarations and the agents that call their tools are the account’s to set.

assign_mcp_tools_to_agent takes connection_id, agent_id and allowed_tools, which replaces what the agent may call on that connection. It names at least one tool, each one the connection declares, and an agent created in the account (type custom). detach_mcp_connection_from_agent removes the access of an agent created in the account to a connection. The assistant applies either within five minutes, and check_agent_reachability confirms it. A key restricted to a brand or workspace assigns tools only to an agent whose every connection is inside its restriction, as it does with update_agent, and detaches an agent from any connection inside its restriction.

ToolRequired scopePurpose
list_channelschannels:readList a workspace’s channels: email, chat, voice and WhatsApp
get_channelchannels:readRead one channel; a chat channel carries the parts of its embed code
create_chat_channelchannels:writeCreate a chat channel for a website
create_email_channelchannels:writeCreate an email channel in one of four inbound modes
update_channelchannels:writeRename a chat or email channel, turn it off or on, move it to another workspace, and change a chat channel’s origins and appearance
check_channel_setupchannels:readReport a chat or email channel’s status, and whether an email channel’s DNS records resolve

Each call to create_chat_channel or create_email_channel creates a new channel, so check list_channels first.

create_chat_channel takes display_name and, optionally, allowed_origins (each an origin such as https://www.example.com, which a page’s origin must match exactly), mode (floating, inline or sidebar), primary_color, position and suggested_prompts. The channel is active at once. The result carries embed, the parts of the code that puts the chat on a page:

{
"id": "...",
"channelType": "chat",
"status": "active",
"publicKey": "ck_...",
"embed": {
"script_src": "https://widget.ohallo.eu/chat.js",
"data_key": "ck_...",
"data_api_base": "https://api.ohallo.eu",
"mode": "floating"
}
}

They assemble into the chat’s script tag, as Embedding widgets describes:

<script src="https://widget.ohallo.eu/chat.js"
data-key="ck_..."
data-api-base="https://api.ohallo.eu"
async></script>

For the inline mode, embed also carries data_target: add data-target="ohallo-chat" to the script tag and place <div id="ohallo-chat"></div> where the chat appears. get_channel and check_channel_setup return the same parts for an existing chat channel, which has no records to check: an active, enabled chat channel runs on every page that carries the code and whose origin allowed_origins permits.

create_email_channel takes display_name, inbound_mode and the arguments that mode needs:

inbound_modeHow mail reaches oHalloArguments
subdomainA subdomain given to oHallo receives the mail itselffrom_address, an address on that subdomain. An address on a registrable domain itself, such as support@example.com or support@example.co.uk, answers validation_error, and so does an address on a domain a channel of another oHallo account uses, or on a domain oHallo sends its own mail from
forwardingThe mailbox stays where it is and forwards its mail to an oHallo addressfrom_address; mail_provider: office365, google or other
mail_routing_ruleThe mailbox stays where it is and a rule in its provider redirects its mail to an oHallo address; replies come from from_address itself, signed by three DKIM records on its domain. The MX record stays as it isfrom_address; mail_provider: office365 or google
inbox_apioHallo reads and sends through a Microsoft 365 mailboxmail_provider: microsoft365; from_address, the mailbox, which can also be entered in the dashboard

The result is the channel with setup: inbound_address, where mail is delivered at oHallo (for forwarding and mail_routing_rule, the address the mailbox’s rule forwards or redirects the mail to), and dns_records, the records to publish at the domain’s DNS host, each with its purpose (verification, dkim, mx, spf or dmarc), type, name, value, and priority for MX. Provisioning continues after the call returns.

An inbox_api channel is finished in a browser: open the channel under Settings > Channels in the dashboard and choose Connect, which signs in to the mailbox’s provider. The channel stays in draft until then.

check_channel_setup lists each record with its state: verified, mismatch (with the value found as observed), not_found or pending. It returns the channel’s status, and when the status is error, provisioning_error with the reason when it is known; Retry on the channel’s page in the dashboard provisions it again. complete is true when the records that route and verify mail resolve: the verification TXT, the DKIM CNAMEs and, where the mode has one, the MX. forwarding and inbox_api need none. SPF and DMARC are listed with their state and do not count toward complete: SPF reads verified when it is a v=spf1 record that includes amazonses.com, one merged with your own senders included, and DMARC when it is v=DMARC1 with p=quarantine or p=reject, whatever report address it names. complete means the records resolve, not that mail has arrived. The status turns active once the domain is verified and its MX record resolves for subdomain, when the first message arrives for forwarding, mail_routing_rule and subdomain_with_redirect, and when the mailbox is connected for inbox_api. Each message the channel receives appears as a conversation under Conversations in the dashboard. A channel is checked at most six times a minute; more answer rate_limit.

update_channel changes only the arguments sent. move_to_workspace_id moves the channel to another workspace, and with it under that workspace’s brand. Voice and WhatsApp channels are changed with the REST API or in the dashboard. Deleting a channel uses the REST API: Delete channel.

A key restricted to a brand or workspace lists, reads, creates and changes channels in the workspaces its restriction covers, and moves a channel only between them; any other channel or workspace answers not_found.

For a key restricted to a brand or a workspace, list_brands, list_workspaces and get_context return what the key reaches. A tool that takes a workspace_id acts only in a workspace the key reaches, and the conversation tools act only on conversations, channels and attention items in those workspaces. A workspace, conversation or channel outside the restriction answers not_found. Scoping keys to brand or workspace describes the restrictions.

  • list_workspaces requires workspaces:read. A key created without that scope receives permission_denied from list_workspaces. Create a key that includes workspaces:read in Settings > API Access and move the integration to it.

A tool returns a structured error object on failure:

{ "error_type": "permission_denied", "message": "Missing scopes: kb:write" }

Common error_type values are authentication_error, permission_denied, not_found, validation_error, rate_limit, and server_error.

Use the REST API for ordinary request and response integrations. Use this MCP server when you want an agent or an MCP-aware client to discover and call oHallo capabilities as tools, with the same account scoping and permissions as your API keys.

Connecting an MCP client covers the client side: the claude mcp add command, the mcp.json configuration shape for any Streamable HTTP client, the packaged Claude Desktop extension, and the Context7 library id for this documentation.