Skip to main content
Glama
BusinessNone

Dataverse Local MCP

by BusinessNone

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

whoami

Verify auth: returns UserId, BusinessUnitId, OrganizationId

get

Raw OData GET relative to /api/data/v9.2/, e.g. accounts?$select=name&$top=5

fetch_xml

Run a FetchXML query — aggregates, link-entity joins, complex filters; returns formatted values

create

Create a record — previews by default, payload validated against cached metadata first

update

Update one record by id or a filter matching exactly one row; optimistic concurrency by default

delete

Delete one record — preview lists its current values first; permanent

associate / disassociate

Relate or unrelate two records through a navigation property

invoke_action

Run a bound or unbound action (WinOpportunity, SetState, Field Service booking actions)

invoke_function

Run a bound or unbound function — side-effect free, so no confirmation needed

list_saved_queries

Browse system and personal saved views — filter by entity, scope, or name substring

get_saved_query

One saved view including its FetchXML, by id or name — run or adapt it with fetch_xml

Schema

Tool

What it does

list_tables

List tables from the local cache — filter by custom/out-of-the-box, name fragment, or solution

describe_table

One table in full: columns, types, required levels, option sets, lookup targets, relationships, annotations, sampled fill rates

find_column

Search cached columns by name fragment or display label, across every table cached in full

lookup_reference

Microsoft Learn documentation for an out-of-the-box table (defers to the Learn MCP when you have it)

refresh_metadata

Rebuild the cache, optionally scoped to named tables

Annotations

Tool

What it does

annotate

Record a local note on a table or column, marked confirmed or inferred

remove_annotation

Delete the local note(s) for one target

export_annotations

Write the annotation markdown to a path you name

import_annotations

Import a markdown file — refused unless its organizationId matches the connected environment

check_drift

Resolve every annotation against the current schema: valid, changed, or orphaned

promote_annotation

Write a confirmed annotation into the Dataverse description itself — maker mode only

Environment

Tool

What it does

environment_info

Cache state: org id, mode, last sync, table counts, sampling settings, drift

set_environment_config

Set friendly name, mode, out-of-the-box allowlist, table cap, and row sampling

set_storage

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. update and delete take an id or a where filter, 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: false to 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.bind navigation 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 refetch

config.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

local

Default. Under ~/.dataverse-mcp/environments/<organizationId>/

git

A repo on disk. Every write is committed, so the documentation carries history and diffs; set autoPush to push each commit

obsidian

Markdown into your vault — defaults to ~/Obsidian, or give an explicit path

onedrive

Into the OneDrive sync folder — $OneDrive or ~/OneDrive

basic-memory

Into the Basic Memory notes directory — defaults to ~/basic-memory

directory

Any other folder you name

notion

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>/.default

This 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.json

The 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 tsbuildinfo

Runtime 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, authority https://login.microsoftonline.com/common

  • Scope <environmentUrl>/.default

  • Token cache persisted to ~/.dataverse-mcp/token-cache.json

  • Silent acquisition from cache first, fall back to interactive (system browser opened via the open package; set DATAVERSE_MCP_NO_OPEN=1 to 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 token

  • Concurrent interactive sign-ins are deduped per environment — parallel requests share one browser window

  • A silentOnly mode 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.com

Expected: 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"]
    }
  }
}
A
license - permissive license
Not graded
quality - not tested
A
maintenance

Maintenance

Maintainers
Response time
Release cycle
1Releases (12mo)
Commit activity

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

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables 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
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables 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
  • A
    license
    A
    quality
    A
    maintenance
    Enables AI agents to query, inspect, and manage Microsoft Dataverse records, metadata, schema, forms, views, and Power Platform environments via the Dataverse OData Web API.
    97
    2
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables AI agents to perform CRUD operations, query data, fetch schemas, and execute custom operations on Microsoft Dynamics 365 CRM entities.
    52
    2
    MIT

View all related MCP servers

Related MCP Connectors

View all MCP Connectors

Latest Blog Posts

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