MCP
The server exposes an MCP endpoint at /mcp (stateless streamable HTTP: POST only, no sessions, no SSE) that
requires the same bearer token as the Web API. Every tool call runs through the Core as the authenticated user, with
that user's privileges.
Tools: list_tables, describe_table (secured columns carry "secured": true), query (FetchXML or OData, paged), create_record, update_record,
delete_record, list_solutions, export_solution, get_plugin_traces, get_async_jobs, get_audit_history (the audit history of one record, newest first; read-only, needs the audit privileges), list_custom_apis (read-only, optional name; see below), list_glossary_terms (read-only; see Knowledge for agents) and apply_schema.
apply_schema needs the System Administrator role and dryRun defaults to true: it only plans unless the caller
passes dryRun: false; with dryRun: false, rejections are a tool error (isError) carrying the plan. Resources: xrmverse://tables/{name} and xrmverse://schema{?publisher} (the latter is
admin-only).
Clients that only speak stdio use the bridge xrmd mcp, which forwards tools and resources to /mcp with the
profile's token (stdout is the MCP channel; nothing else is written to it). A desktop MCP client configuration:
{
"mcpServers": {
"xrmverse": {
"command": "xrmd",
"args": ["mcp", "--profile", "default"]
}
}
}
The profile must already exist (xrmd auth login); with dev mode the client's environment must provide the signing
key variable.
list_custom_apis returns { customApis: [...] }, each entry with uniqueName, name, displayName, description, binding (global, entity:<table> or collection:<table>), kind (action or function), private, pluginType, parameters (uniqueName, type, optional, table) and properties (uniqueName, type, table). It reads as the caller and pages through every result. The caller needs read access to custom APIs and to plug-in types: without the plug-in type privilege the tool fails with the server's refusal naming it. The tool is read-only: create custom APIs with the CLI, the SDK or the Web API (see Custom APIs).
Knowledge for agents
The tables of Glossary and synonyms give agents the business meaning of a table. Everything is read as the caller, so depth, sharing and field security apply.
describe_tableand the resourcexrmverse://tables/{name}keep their fields and add:description(the table's description), per columndescriptionandsynonyms(a list of strings), andglossary(a list of{ term, description? }for the table). Each is left out when empty.list_glossary_terms(optionaltable, the logical name of a table, andsource, the name of a knowledge source; read-only, idempotent) returns{ terms: [{ term, description?, sources }] }, ordered by term without regard to case. Terms are grouped by term and description, so a term with two descriptions appears twice. The list is not paged.
Which rows count:
- Only active rows (
statecode0). - A term or synonym counts only when its knowledge source is active and the caller can read it.
- A caller who cannot read the knowledge tables gets empty results, not an error.
- The synonyms string is split on
,and;and trimmed; empty parts are dropped. - In
describe_table, synonyms rows that name a column the table does not have are skipped.list_glossary_termswithtabledoes not check that the table exists: an unknown name just matches no source.
Workflow tools
Four tools author and manage classic workflows, business rules and actions written as YAML (see
Workflow authoring as code). They run as the caller with no role requirement: the Dataverse
privileges on workflow decide, so a process author who is not an administrator can use them.
apply_workflow(yaml, optionalsolution,activate,dryRun) applies a document: it creates the process, or updates the one with the same identity.dryRundefaults to true: it only reports what would change; passdryRun: falseto write. The result is{ id, name, action (created | updated | unchanged), state (draft | activated), stateChanged, assumedShapes, errors }. A refused document is a tool error (isError) whose text holds the result with every error and its line and column; nothing is written then. Every apply, a dry run too, needsprvReadWorkflow.export_workflow(nameOrId,formatyaml or xaml) returns{ format, text }. A process with no YAML form is refused with the reasons.list_workflows(optionalkind,entity) returns{ workflows: [...] }withid,name,kind,entity,mode,stateandunsupportedSteps.set_workflow_state(nameOrId,activated) activates or deactivates a process and returns its summary; it needs the activation privileges (Security).
A name that matches several processes is a 400 that lists their ids.