Developers
MCP (Cliedesk Pro)
This guide covers a feature of Cliedesk Pro. The free plugin is a complete help desk; see what Pro adds.
§6.7, §16. Tests: tests/phpunit/pro/mcp/, cliedesk-pro/app/__tests__/Mcp.test.tsx.
Like the Public API, every MCP endpoint exists only while Cliedesk Pro runs with a valid licence.
Otherwise WordPress answers 404 rest_no_route. With MCP switched off (pro_mcp.enabled), every MCP route is 404.
Transport
All three endpoints speak MCP Streamable HTTP, protocol 2025-11-25 (also 2025-06-18 and 2025-03-26):
POSTwithContent-Type: application/json; the answer is always JSON (no SSE).GETandDELETEanswer405.- Stateless: no
Mcp-Session-Id. Every request is authenticated on its own. - JSON-RPC 2.0, batches of at most 10 messages. Notifications get no answer; a body of only notifications answers
202with no body. initializeanswers the client’s version when supported, else the newest. Other requests may sendMCP-Protocol-Version; an unsupported value answers400witherror.data.supported.- Methods:
initialize,ping,tools/list,tools/call. Capabilities:tools(listChanged: false) only. - An
Originheader must be the site (same scheme, host and port) or an origin inpro_api.oauth_allowed_origins(DNS-rebinding protection), else403 FORBIDDEN_ORIGIN. - Tool results have
structuredContent(the Public API representation: snake_case, prefixed ids such asconv_…) and a text block with the same JSON. Errors are results withisError: trueandstructuredContent.error.code(the Public API error codes). An unknown tool is JSON-RPC error-32602.
Team server: POST /wp-json/cliedesk-api/v1/mcp
- Credentials: an OAuth 2.1 access token (
cdk_oat_…, the person who consented) or an API key, inAuthorization: Bearer. Scopesmcp:read(read tools) andmcp:write(write tools). The owner needs the capabilitycliedesk_use_mcp(Admin, Supervisor and Agent by default). Without a credential:401withWWW-Authenticate: Bearer resource_metadata="…/oauth/protected-resource". - Tools (
CliedeskPro\Modules\Mcp\Tools) are Public API endpoints run throughApiServer::dispatch, so the person’s capabilities, product scope, record rules, PII masking and money limits apply as on the API. Default set:me,search,conversations_list,conversation_get,conversation_messages,conversation_reply,conversation_note_add,conversation_status_set,conversation_priority_set,conversation_assign,conversation_tag,contact_search,contact_get,contact_conversations,contact_records(record sources such aswoo_order),contact_update,article_search,article_get,article_create_draft,article_update_draft(drafts only),saved_reply_search,saved_reply_render,macro_run,report_metrics,report_overview,report_series,approval_get,job_get. - Add-on Actions flagged
mcpwith anapi_scopebecome tools named like the Action (.→_), off until an admin switches them on. Settings, keys, roles, approval decisions, erasure, licence and vault never become tools. tools/listshows only what the credential can use: switched on, the owner may run the Action,mcp:writeand read-only off for write tools. Write tools in read-only mode answerREAD_ONLY; withoutmcp:writeINSUFFICIENT_SCOPE.- Approvals (§7.4, MCP column): tools whose Action is
customer_visible,destructiveortouches_moneycreate an approval through thecliedesk_action_gatefilter instead of running. The result is{"status": "approval_pending", "approval_id": "appr_…"}; the Action runs once, as the person, when someone with Answer approvals approves. Governance can set a tool toauto; money tools always ask. Test keys (cdk_test_) run writes as dry runs and create no approval. - Steps (Actions a tool call starts on the side, such as macro steps): a
macro_run(or a note with macro steps) whose steps would need approval becomes one approval for the whole macro. Inside a tool call, any other Action that needs approval is refused (cliedesk_mcp_approval_required). Steps need approval when they arecustomer_visible,destructiveortouches_money, and alsocsat.send,conversation.cc.add,conversation.requester.setand everyconnector.*Action. Tool overrides never apply to steps. article_update_draftrefuses a scheduled (future) article or a scheduled update draft: it would go live without review.- Every tool result except
me,report_*,approval_getandjob_getcarries"untrusted": trueat the top, and every object in it (messages, conversations, contacts, search hits) carries"untrusted": true: their text can be written by customers. - Limits: the credential’s Public API buckets plus per client (
oauth:{client}orkey:{id}) 120 tool calls and 20 writes per minute (pro_mcp.client_calls_per_min,client_writes_per_min, overrides inpro_mcp.clients). A suspended client answers403 CLIENT_SUSPENDED.
Public knowledge: POST /wp-json/cliedesk-api/v1/mcp/public
Off by default (pro_mcp.public_enabled; off answers 404). No credential. Tools help_search {query, limit} and
help_article_get {id} over published articles whose effective visibility is public (an article in an internal or
signed-in category is not public). 30 calls per minute per IP and 600 for the site (public_ip_per_min,
public_site_per_min); over the limit: HTTP 429 with Retry-After. Answers are cached 5 minutes.
Customer server: POST /wp-json/cliedesk-api/v1/mcp/customer/{brand_slug}
Off by default, switched per brand (pro_mcp.customer_enabled, layers workspace and brand; off answers 404).
- Sign-in: each brand has its own issuer
…/mcp/customer/{brand_slug}with…/protected-resource,…/metadata(also…/.well-known/oauth-authorization-serverbelow the issuer and the RFC 8414 path-inserted form) and…/authorize. Token, registration and revocation are the shared/oauth/*endpoints. The authorize endpoint sends the browser to a consent page on the site (/?cliedesk_mcp_consent={brand_slug}&…); the customer signs in with their WordPress account. Scopescustomer:read,customer:write;resourcemust be the brand’s customer URL. - The grant is bound to the customer’s contact and the brand (
cliedesk_pro_mcp_customer_grants). Only such tokens work here, only for their brand; team tokens and API keys answer 401. Customer tokens never open the team server or the Public API. - Contact link: a WordPress account’s email is not proven. The account’s contact is used only when its address is
verified there, Cliedesk made the contact for this account (user meta
cliedesk_pro_mcp_contact), or the contact does not hold the account’s address. A contact linked only by an unverified address (Free links on sign-up and sign-in) is refused withaccess_denied; the team can verify the address. With no linked contact, a contact that holds the address unverified is not used: the account gets a new contact without the address. - Tools:
my_conversations_list,my_conversation_get,request_create(a conversation on channel and source API, with normal triage, routing, SLA and escalation; an optionalsummaryfrom the assistant of what it tried becomes a team note marked as the assistant’s),message_add,my_orders_listandorder_status(thewoo_orderrecord source, by the account and its verified emails),help_search. Every lookup is by the token’s contact; another customer’s id answersNOT_FOUND. 60 calls per minute per contact.
MCP client (AI tools from other MCP servers)
Servers and McpClient: admins connect servers (Settings → Developer → MCP servers for AI). Auth none, bearer,
basic or OAuth (access token with optional refresh token, token URL and client id; refreshed once on 401). Secrets
are in the vault (pro_mcp.srv.{uid}.token|password|refresh|signing). Changing the URL or the token URL removes
every saved secret that is not sent again in the same change, so secrets never go to a new address. Optional signing header
Cliedesk-Signature: t={unix},v1={hex HMAC-SHA256(secret, t + "." + body)} and X-Cliedesk-Conversation-Id.
Requests go through SafeHttp (SSRF guard, HTTPS, no redirects), timeout 30 s (at most 60 s), answers up to 256 KiB,
JSON or SSE.
Import calls initialize, notifications/initialized and tools/list (with Mcp-Session-Id when the server sets
one). Tools are stored in cliedesk_pro_mcp_tools switched off, autonomy off, kind write, approval on, no
exposure; the server’s annotations are kept as hints only. Switching a tool on approves its definition hash; a later
import with another hash switches it off (changed). The daily job cliedesk_pro_mcp_refresh imports again. After 5
failures in a row a server’s tools are hidden from the AI for 15 minutes. The AI agent calling these tools through
the AI gateway comes with the AI gateway (M5) and the AI tool registry.
Governance, log and CLI
Settings of pro_mcp (versioned and audited): enabled (on), read_only (on), public_enabled (off),
customer_enabled (off), limits, tools (per-tool enabled and autonomy), clients (per-client calls,
writes, suspended), log_days (30). OAuth registration and the client limit stay pro_api.oauth_registration
and pro_api.oauth_client_limit; POST cliedesk/v1/mcp-governance/oauth sets them and purges unused clients.
The call log cliedesk_pro_mcp_calls keeps per request method: surface, client, credential, user or contact, tool,
result (ok, error, denied, approval, rate_limited), error code, duration, request id; never arguments,
answers or tokens. Purged daily (cliedesk_pro_mcp_purge). Each tool call is also an audit entry
mcp.tool_called (via mcp).
wp cliedesk mcp status [--format=json]
wp cliedesk mcp on | off
wp cliedesk mcp read-only on|off
wp cliedesk mcp tools [--enabled] [--format=json]
wp cliedesk mcp refresh-servers