Dataverse Local MCP
Allows using Git repositories as a storage backend for the MCP server's documentation annotations and metadata cache, enabling versioning and collaboration.
Allows using Notion as a storage backend for documentation annotations and metadata cache.
Allows using an Obsidian vault as a storage backend for documentation annotations and metadata cache.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Dataverse Local MCPlist my saved views on account"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Dataverse Local MCP
Connect Claude (or any MCP client) to your Microsoft Dataverse / Dynamics 365 environment and work with your data in plain English — query records, run your saved views, explore tables and columns, create and update rows.
Sign in like you always do — your own Microsoft work account, in your browser, with the same trusted sign-in XrmToolBox uses. Works out of the box: no app registration, no API keys, no admin setup.
Fast from the second call — your environment's schema and saved views are prefetched and cached locally, so metadata questions answer instantly.
Full toolbox — OData queries, FetchXML (aggregates and joins), entity CRUD, and metadata discovery in one server.
Guides: Setup · User guide
Getting started
1. Install Node.js 18 or newer if you don't have it.
2. Install the server from the npm install page:
npm install -g dataverse-local-mcp(Or skip the install and use npx -y dataverse-local-mcp as the command below.)
3. Add it to your MCP client — for Claude Desktop, add this to claude_desktop_config.json, replacing the URL with your environment's:
{
"mcpServers": {
"dataverse": {
"command": "dataverse-local-mcp",
"args": ["https://yourorg.crm.dynamics.com"]
}
}
}4. Sign in once. The first time a tool runs, your browser opens a Microsoft sign-in — pick your work account for that environment. The token is cached at ~/.dataverse-mcp/token-cache.json, so you won't be asked again until it expires. If the browser signs you in with the wrong account, an account picker is always shown so you can switch.
Try it: ask your MCP client to run the whoami tool — it should return your Dataverse UserId and OrganizationId. Then try "list my saved views on account" or "show me the top 5 accounts by name".
Related MCP server: Dataverse MCP Server
Tools
Data
Tool | What it does |
| Verify auth: returns |
| Raw OData GET relative to |
| Run a FetchXML query — aggregates, link-entity joins, complex filters; returns formatted values |
| Create a record — previews by default, payload validated against cached metadata first |
| Update one record by id or a filter matching exactly one row; optimistic concurrency by default |
| Delete one record — preview lists its current values first; permanent |
| Relate or unrelate two records through a navigation property |
| Run a bound or unbound action ( |
| Run a bound or unbound function — side-effect free, so no confirmation needed |
| Browse system and personal saved views — filter by entity, scope, or name substring |
| One saved view including its FetchXML, by id or name — run or adapt it with |
Schema
Tool | What it does |
| List tables from the local cache — filter by custom/out-of-the-box, name fragment, or solution |
| One table in full: columns, types, required levels, option sets, lookup targets, relationships, annotations, sampled fill rates |
| Search cached columns by name fragment or display label, across every table cached in full |
| Microsoft Learn documentation for an out-of-the-box table (defers to the Learn MCP when you have it) |
| Rebuild the cache, optionally scoped to named tables |
Annotations
Tool | What it does |
| Record a local note on a table or column, marked |
| Delete the local note(s) for one target |
| Write the annotation markdown to a path you name |
| Import a markdown file — refused unless its |
| Resolve every annotation against the current schema: valid, changed, or orphaned |
| Write a confirmed annotation into the Dataverse description itself — maker mode only |
Environment
Tool | What it does |
| Cache state: org id, mode, last sync, table counts, sampling settings, drift |
| Set friendly name, mode, out-of-the-box allowlist, table cap, and row sampling |
| Choose where documentation and the metadata cache live — local, git, Obsidian, OneDrive, Basic Memory, Notion, or any folder |
Writing safely
Every mutating tool previews by default. Called without confirm: true it describes exactly what would change — naming the resolved record by its primary name — and calls nothing. delete additionally lists the record's current field values, so you can see what is about to be lost.
One record at a time.
updateanddeletetake an id or awherefilter, and anything matching more than one record is refused with the candidates listed rather than fanning out.Optimistic concurrency by default. Updates and deletes carry the record's ETag, so a write against a record that changed after you read it fails instead of silently overwriting someone else's work. Pass
concurrency: falseto opt out.Payloads are checked before they are sent. Unknown columns, columns not valid for the operation, out-of-range option set values, and unknown
@odata.bindnavigation properties all fail locally with a message naming the problem — not an opaque platform 400.Actions cascade. Much of the real work in Dataverse happens through actions rather than table writes, and their effects reach further than the call suggests. The preview shows the call, not its consequences: those cannot be known without running it.
Errors surface the Dataverse hex code and message, with recognised shapes (privilege denied, duplicate detection, business rule or plugin rejection, concurrency, unknown column) prefixed by a plain-language line. Anything unrecognised passes through verbatim rather than being guessed at.
Resources
The environment's full OData $metadata (CSDL/EDMX) schema is exposed as an MCP resource at dataverse://metadata (application/xml, often several MB).
How the cache works
Right after the stdio handshake the server builds its caches in the background. It never opens a browser at startup: the prefetch uses silent auth only, so with no cached token it waits and retries after your first tool call signs in. Nothing blocks on it — a cold start still works, just slower on the first call.
Everything is keyed by OrganizationId, not environment URL, because URLs change and org ids don't:
~/.dataverse-mcp/
token-cache.json
environments/
index.json # host -> organizationId, so a warm start needs no network
<organizationId>/
config.json # url, friendly name, mode, storage, scope, sampling
schema.json # cached metadata } these two follow
schema.fingerprint # hash for drift detection } your storage choice
annotations.md # your documentation }
metadata.xml # the $metadata resource } always local:
saved-queries.json # } large, derived, cheap to refetchconfig.json and index.json always stay local — they hold the storage settings themselves, so they can't live inside the backend they describe.
Scope. Every table gets a cheap name-level summary. Full column and relationship detail is cached for all custom tables plus an allowlist of out-of-the-box ones (Field Service and core sales/service by default), capped at maxFullTables. Anything else is fetched lazily and merged the first time a tool touches it.
Row sampling is off by default. Enable it per environment and the cache also records, per column, the fill rate and up to five example values from at most 20 rows — the single most useful signal for tables whose descriptions are blank. It reads real data, so it stays opt-in, and it never samples columns whose type or format suggests personal data unless you explicitly allow it.
Annotations
Dataverse descriptions are frequently blank. The environment's own shape carries most of the meaning; the rest is human knowledge worth accumulating instead of re-deriving every session. Annotations live in plain markdown at environments/<organizationId>/annotations.md — human-editable, diffable, and safe to check into an engagement repo.
## rsm_cipscenariocandidate
Candidate records for capital improvement plan scenario modelling. Populated by
the scenario engine, not by users directly.
_author: ben.vollmer_ · _added: 2026-08-20_ · _confidence: confirmed_ · _provenance: human_
### rsm_scenariotype
Picklist. 1 = replacement, 2 = rehabilitation, 3 = deferral.
_author: ben.vollmer_ · _added: 2026-08-20_ · _confidence: inferred_ · _provenance: human_Each annotation carries two independent fields. Confidence is confirmed only when a human stated it; anything a model or tool worked out is inferred — which is exactly why annotations are never written back into Dataverse descriptions by default. Provenance is human, preflight, velocity or model, and governs overwrite behaviour on re-scan: a writer may replace its own earlier note freely, and a human replaces anything, but nothing else overwrites. Where a re-scan contradicts a person's note, both are kept and flagged for a human to settle rather than one silently winning.
Tool-authored notes are rendered as blockquotes so you can tell at a glance what came from a person:
### rsm_scenariotype
> No plugins are registered on this column.
_author: preflight_ · _added: 2026-08-20_ · _confidence: confirmed_ · _provenance: preflight_Sharing. export_annotations writes the file anywhere you like; import_annotations reads one back. Front matter carries the organizationId, and an import into a different org is refused, never merged. Where both sides annotate the same target with different text, both are kept and flagged rather than one silently winning.
Drift. A fingerprint over the schema is captured at cache time. When it moves, each annotation resolves to valid, changed (the type or option set moved underneath the note) or orphaned (the target is gone). A short summary is logged on connect, the specific warning repeats inline in describe_table, and nothing is ever deleted automatically.
Modes. Set explicitly per environment, never inferred from privileges — privileges are usually broader than intent. consumer (the default) keeps annotations local and never writes metadata. maker will additionally allow promoting a confirmed annotation into the Dataverse description itself.
Promoting documentation into Dataverse
If you own an environment's schema, a confirmed annotation can become the real Dataverse description. This is deliberately awkward and the friction should not be reduced for convenience: it needs maker mode, a confirmed annotation (inferences are refused), no unresolved conflict on it, one target per call, and an explicit confirmation after reading the preview.
The written text is prefixed with a [dataverse-mcp] marker plus provenance and date. That marker is the point — without it a promoted note becomes indistinguishable from a human-authored description six months later, and something that read as a reasonable guess starts reading as fact.
Promotion is a metadata write: it creates an unmanaged customization in the active solution layer, which can mask later updates to a managed component, and publishing may be required before the description appears in the UI. The preview says all of this before you confirm, and the tool cannot undo it.
Where your documentation lives
By default everything sits under ~/.dataverse-mcp. Point it somewhere else with set_storage and both the annotations and the metadata cache follow — they always travel together, per environment.
Kind | What it does |
| Default. Under |
| A repo on disk. Every write is committed, so the documentation carries history and diffs; set |
| Markdown into your vault — defaults to |
| Into the OneDrive sync folder — |
| Into the Basic Memory notes directory — defaults to |
| Any other folder you name |
| The annotation document as a page under a parent page you choose |
The file-backed kinds are one implementation: an Obsidian vault, a OneDrive sync folder and a Basic Memory directory are all just folders, and git adds a commit step. Each environment gets its own subfolder (dataverse-mcp/<friendlyName>-<orgId prefix>) so a shared vault or repo can hold several without collision.
Notion needs an internal integration token in a NOTION_TOKEN environment variable — set it in your MCP client config, not in a file — and a notionPageId for the parent page, which must be shared with your integration. Each markdown line becomes one paragraph block, so the document round-trips exactly and stays readable and editable in Notion. Because Notion is a document store rather than a file store, the schema cache stays on local disk when Notion is selected; the annotations live in Notion.
Switching storage does not copy what you already have — run export_annotations first if you want to carry it across.
Upgrading from 0.3.x
list_entities and describe_entity are replaced by list_tables and describe_table, which read the new cache and fold in annotations and drift warnings. The 0.3.x cache directory ~/.dataverse-mcp/cache/<host>/ is no longer read and can be deleted; the new cache rebuilds itself on first connect. Your token cache is untouched, so no new sign-in is needed.
Build Spec (for contributors)
Goal
Build a standalone MCP server that talks directly to the Dataverse Web API. Going straight to the Web API keeps the server small and dependency-light, and lets it use the sign-in flow that works most broadly across machines and tenants — including tenants with strict Conditional Access policies. TypeScript, local Node host, no new app registration required.
Why this auth approach
This server uses the same proven auth pattern as XrmToolBox and Microsoft's own XRM Tooling samples: a Microsoft-provided, pre-consented public client with a loopback redirect, driven as a standard MSAL auth-code-plus-PKCE flow. It's the ordinary browser sign-in your tenant already trusts — it runs on every OS, satisfies Conditional Access policies that stop device-code flows, and needs no OS-level broker. Anywhere XrmToolBox connects, this connects.
Client ID: 51f81489-12ee-4a9e-aaae-a2591f45987d
Redirect URI: http://localhost
Authority: https://login.microsoftonline.com/common
Scope: <environmentUrl>/.defaultThis is a Microsoft multi-tenant sample app with user_impersonation delegated permission, no admin consent required. If XrmToolBox already connects successfully in your tenant, this same client ID is proven to already clear Conditional Access there.
Non-goals for v1
No custom Entra app registration (use the well-known client ID above)
No service principal / CI auth (interactive user auth only)
Repo layout
packages/
core/ @dataverse-platform/core — shared library, private
src/
index.ts public surface
auth.ts MSAL interactive + silent acquisition
cache.ts atomic read/write helpers
paths.ts ~/.dataverse-mcp layout
environment.ts per-environment config, OrganizationId resolution
dataverseClient.ts Web API calls
writes.ts preview/confirm, validation, single-record resolution
promotion.ts annotation -> Dataverse description, maker mode only
errors.ts Dataverse error translation
store.ts $metadata + saved-view warm cache
metadata/ schema cache: types, fingerprint, build, sampling
annotations/ markdown model, store, drift detection
storage/ backends: directory/git presets, Notion
mcp-server/ dataverse-local-mcp — published to npm
src/
server.ts MCP wiring
tools/ tool definitions and formatters
build.mjs esbuild bundle (inlines core)
prepack.mjs stages README/LICENSE for packing
package.json npm workspaces root
tsconfig.base.jsonThe assessment tools (powerpreflight, velocity) join as further packages/*, calling core directly as a library rather than going through the MCP server.
Dependencies
npm install # installs every workspace
npm run typecheck # tsc -b across packages
npm run build # core via tsc, mcp-server bundled via esbuild
npm run clean # removes dist and tsbuildinfoRuntime dependencies are @azure/msal-node, @modelcontextprotocol/sdk and open. HTTP calls use Node's built-in global fetch (hence the Node ≥ 18 requirement) — no HTTP client dependency.
Step 1 — Auth module (src/auth.ts)
Acquire and cache a token using acquireTokenInteractive, which spins up its own loopback listener, no manual HTTP server needed.
Client ID
51f81489-12ee-4a9e-aaae-a2591f45987d, authorityhttps://login.microsoftonline.com/commonScope
<environmentUrl>/.defaultToken cache persisted to
~/.dataverse-mcp/token-cache.jsonSilent acquisition from cache first, fall back to interactive (system browser opened via the
openpackage; setDATAVERSE_MCP_NO_OPEN=1to print the URL instead)Interactive sign-in always shows the account picker (
prompt: select_account) so browser SSO can't silently hand back the wrong account's tokenConcurrent interactive sign-ins are deduped per environment — parallel requests share one browser window
A
silentOnlymode backs the cache prefetch: it throws instead of opening a browser, so background work never interrupts client startup
Step 2 — Dataverse Web API client (src/dataverseClient.ts)
Thin wrapper over the Dataverse Web API (/api/data/v9.2/) sending Authorization: Bearer, OData-MaxVersion: 4.0, OData-Version: 4.0 headers, retrying 429/503 on Retry-After so a bulk metadata build survives service protection limits. Covers whoAmI(), generic get(), record create/update/delete (PATCH sends If-Match: * so updates never silently upsert), FetchXML queries, saved views (savedquery + userquery, following @odata.nextLink), the raw $metadata EDMX, and metadata reads over EntityDefinitions.
Two Dataverse constraints shape the metadata calls: EntityDefinitions rejects $top and $orderby (it accepts $select and $filter), and DisplayName/Description/RequiredLevel come back as objects rather than scalars, so labels are extracted from UserLocalizedLabel.Label. Option sets need a cast — the client tries the EnumAttributeMetadata base cast (one call for picklist, state, status and multiselect) and falls back to the concrete casts where that isn't supported.
Step 3 — MCP server entry (src/server.ts)
Registers the tools listed in the Tools section above plus the dataverse://metadata resource, and kicks off the background cache prefetch after the transport connects. Uses the standard @modelcontextprotocol/sdk Server class with stdio transport, matching how @microsoft/dataverse mcp itself runs. The environment URL is passed as the first CLI argument.
Step 4 — First test
npm run build
node dist/server.js https://yourorg.crm.dynamics.comExpected: system browser opens once for interactive sign-in, token caches to ~/.dataverse-mcp/token-cache.json, subsequent runs reuse the cached token silently. Confirm success by calling the whoami tool and checking the returned UserId/BusinessUnitId. After the first sign-in, the background prefetch fills ~/.dataverse-mcp/cache/<org-host>/ with metadata.xml, entities.json, and saved-queries.json; later launches serve metadata and saved-view tools from that cache.
Step 5 — Claude Desktop config
{
"mcpServers": {
"dataverse": {
"command": "node",
"args": ["/full/path/to/DataVerseLocalMCP/dist/server.js", "https://yourorg.crm.dynamics.com"]
}
}
}This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables comprehensive management of Microsoft Dataverse environments, including schema operations for tables, columns, and relationships through the Dataverse Web API. It also supports solution management, security role configuration, and the generation of WebAPI calls and Mermaid ERD diagrams.MIT
- AlicenseNot gradedqualityDmaintenanceEnables comprehensive schema and solution management for Microsoft Dataverse, including operations for tables, columns, relationships, and security roles via the Dataverse Web API. It also supports PowerPages configuration, automated WebAPI call generation, and schema visualization through Mermaid ERD diagrams.MIT
- AlicenseAqualityAmaintenanceEnables AI agents to query, inspect, and manage Microsoft Dataverse records, metadata, schema, forms, views, and Power Platform environments via the Dataverse OData Web API.972MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to perform CRUD operations, query data, fetch schemas, and execute custom operations on Microsoft Dynamics 365 CRM entities.522MIT
Related MCP Connectors
Official Microsoft MCP Server to query Microsoft Entra data using natural language
Run SOQL queries to explore and retrieve Salesforce data. Access accounts, contacts, opportunities…
Query PostgreSQL databases in plain English — LLM-generated, safety-validated SQL.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/BusinessNone/DataVerseLocalMCP'
If you have feedback or need assistance with the MCP directory API, please join our Discord server