
Dv Connect
- 72 installs
- 191 repo stars
- Updated July 31, 2026
- microsoft/dataverse-skills
dv-connect is an agent skill for one-step Dataverse workspace setup including tools, auth, MCP registration, and .env configuration.
About
The dv-connect skill performs one-step Dataverse environment connection for a workspace. It idempotently installs Python, Node, PAC CLI, Dataverse CLI, .NET SDK, and Azure CLI, upgrades the PowerPlatform Dataverse Client SDK, and front-loads both dataverse auth and pac auth so later skills never prompt mid-flow. Step zero detects an existing setup via complete .env values, registered MCP server, matching auth profiles, and importable Python SDK, jumping straight to verification when all checks pass. Otherwise it discovers or creates environments, writes DATAVERSE_URL and TENANT_ID, registers the dataverse MCP proxy, and confirms connectivity with dataverse auth who and pac org who. The skill enforces environment-first metadata rules: create components in Dynamics via API, then pull into the repo, never hand-edit solution XML. It supports device code auth for headless sessions, admin consent links, and region-aware profile selection. Use when starting a new Dataverse project, switching environments, fixing authentication, or troubleshooting MCP connections that fail to start.
- Step zero short-circuits when .env, MCP, auth, and Python SDK already match.
- Front-loads dataverse auth and pac auth to avoid mid-workflow prompts.
- Installs and upgrades Dataverse CLI, PAC CLI, and Python SDK dependencies.
- Registers MCP server and writes workspace .env with environment URLs.
- Enforces environment-first metadata creation instead of hand-edited solution XML.
Dv Connect by the numbers
- 72 all-time installs (skills.sh)
- +8 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #3,076 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Security screen: HIGH risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
dv-connect capabilities & compatibility
- Capabilities
- tool installation and sdk upgrade checks · dual auth setup for dataverse cli and pac cli · mcp server registration for claude and cursor · environment discovery and .env writing · idempotent verification only short circuit
- Works with
- azure
- Use cases
- api development · orchestration
What dv-connect says it does
One-step connection to Dataverse. Handles tool installation, authentication, environment selection, workspace initialization, MCP configuration, and verification
npx skills add https://github.com/microsoft/dataverse-skills --skill dv-connectAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 72 |
|---|---|
| repo stars | ★ 191 |
| Security audit | 2 / 3 scanners passed |
| Last updated | July 31, 2026 |
| Repository | microsoft/dataverse-skills ↗ |
How do I connect a new project to Dataverse with auth, MCP, and required CLI tools in one flow?
One-step Dataverse workspace setup with tool install, dual auth, MCP registration, .env writing, and verification.
Who is it for?
Developers starting or reconnecting Dataverse projects who need idempotent environment and MCP setup.
Skip if: Skip for ALM solution packaging, plugin authoring, or workspaces already fully connected without verification needs.
When should I use this skill?
User starts a Dataverse project, switches environments, fixes auth, or troubleshoots MCP connection failures.
What you get
A verified workspace with .env, MCP server, matching auth profiles, and importable Dataverse Python SDK.
Files
Skill: Connect
One-step connection to Dataverse. Handles tool installation, authentication, environment selection, workspace initialization, MCP configuration, and verification — all idempotently. Each step checks if it's already done and skips if so.
Environment-First Rule — All metadata (solutions, columns, tables, forms, views) and plugin registrations are created in the Dynamics environment via API or scripts, then pulled into the repo. Never write or edit solution XML by hand to create new components.
Execute every step in order. Do not skip ahead, even if a later step appears more relevant to the user's immediate goal. Exception: Step 0 below can short-circuit the entire flow if the workspace is already set up.
---
Step 0: Detect existing setup (run this first)
Before touching anything, check whether this workspace is already connected to a Dataverse environment. This matters a lot on claude --continue or any re-run — repeating it on an already-configured workspace overwrites .env, re-registers MCP, and wastes time.
Run these checks in order. If all four pass, skip straight to Step 7 (final verification) and stop there.
1. `.env` is present and complete — file exists at the workspace root and contains non-empty values for DATAVERSE_URL, TENANT_ID, and MCP_CLIENT_ID 2. MCP is registered — .mcp.json (Claude Code) or the equivalent Copilot / Cursor config file has a dataverse-* server entry pointing at the DATAVERSE_URL from .env 3. Both auth surfaces match `.env` — dataverse auth who shows a profile whose Environment Url matches DATAVERSE_URL, AND pac org who against a PAC profile for the same URL succeeds. (DV CLI auth covers Connect / Data / Query / Metadata / MCP / Python; PAC auth covers dv-solution and dv-admin. Both are front-loaded at connect time so neither prompts later.) 4. Python SDK is importable and current — python -c "from PowerPlatform.Dataverse.client import DataverseClient; import pandas; from importlib.metadata import version; v=version('PowerPlatform-Dataverse-Client'); assert v>='0.1.0b9', f'SDK {v} is outdated, need >=0.1.0b9'" exits 0
If all pass: Tell the user you detected an existing setup, list what you found (URL, profile name, MCP server name), then jump to Step 7. Do not rewrite .env, do not re-register MCP, do not re-run pip install.
Example: "Detected existing Dataverse setup at{DATAVERSE_URL}(auth profile:{PROFILE}, MCP server:dataverse-{orgid}). Running verification only."
If any check fails: Proceed through the normal flow (Steps 1–7), but still use each step's own skip condition. A partially-configured workspace doesn't need a full redo — e.g., if only .env and MCP are missing but tools and auth are fine, start at Step 2 or Step 3.
---
Step 1: Ensure tools are installed
Check each tool independently — do not use fail-fast parallel execution. If one tool check fails, continue checking the others so you can report all missing tools at once. See tools-setup.md for installation commands and platform-specific notes.
| Tool | Check |
|---|---|
| Python 3 | python --version |
| Git | git --version |
| Node.js | node --version |
| PAC CLI | pac (prints version banner; note: pac --version is not a valid command and returns a non-zero exit code) (see tools-setup.md for Windows path discovery if not in PATH) |
| Dataverse CLI | npm list -g @microsoft/dataverse (prints @microsoft/dataverse@<version> if installed globally; prints (empty) if not) |
| .NET SDK | dotnet --version |
| Azure CLI | az --version |
.NET SDK is needed for PAC CLI but NOT for the Dataverse CLI (the npm package bundles its own runtime). Node.js powers the Dataverse CLI npm package (@microsoft/dataverse), which is used as the MCP proxy and for scripted data plane actions. Azure CLI is used as a fallback for environment discovery when PAC CLI isn't available (see mcp-configuration.md Step 3b). GitHub CLI is not needed for connecting — it's used later for ALM/CI/CD scenarios (see dv-solution).
If any tool is missing, install it (see tools-setup.md), then verify. If winget installs a tool but it's not in PATH, ask the user to restart the terminal.
After Python is confirmed:
pip install --upgrade azure-identity requests PowerPlatform-Dataverse-Client pandas msal msal-extensionsmsal + msal-extensions let scripts/auth.py reuse the dataverse auth create cache \u2014 one sign-in for CLI, MCP, Python.
After Node.js is confirmed, install or upgrade the Dataverse CLI to the latest version (run on each connect to keep it current):
npm install -g @microsoft/dataverse@latestSkip condition: All tools present, Python SDK installed, and pandas importable (python -c "import pandas").
---
Step 2: Discover and select the environment
Before asking the user for a URL, check what's already available.
Auth tool choice. Two tools, two AAD apps, two caches — front-load both at connect:
>
1. `dataverse auth create` (app 0c412cc3-…) covers DV CLI + MCP + Python.2. `pac auth create` (PAC's own app) coversdv-solution+dv-admin.
Check for an existing DV CLI profile first, then fall back to PAC for environment discovery if needed:
dataverse auth list
dataverse auth who
pac auth list # PAC profiles are still useful for env discovery / pac org listIf `dataverse auth who` shows a profile and its environment matches the user's target:
- Reuse it. Set
DATAVERSE_URLandTENANT_IDfrom the profile.
If no DV CLI profile exists (or it points at the wrong environment):
- Ask: "Do you want to connect to an existing environment or create a new one?"
Before selecting, check for tenant/region mismatch. If the target environment URL uses a different region (e.g., crm10.dynamics.com = APAC) than the currently authenticated account's environments, create a new profile for the correct tenant rather than trying to reuse the old one:
dataverse auth create --environment <url> # interactive (WAM broker on Windows → no browser tab)
dataverse auth create --environment <url> --deviceCode # headless / remote / SSHOn first run in a tenant, AAD may prompt for admin consent for app 0c412cc3-0dd6-449b-987f-05b053db9457. If the user lacks consent rights, ask an admin to visit:
https://login.microsoftonline.com/<tenant-id>/adminconsent?client_id=0c412cc3-0dd6-449b-987f-05b053db9457To switch between existing DV CLI profiles:
dataverse auth select --name <profile-name>To create a new environment (requires admin permissions):
pac admin create --name "<name>" --type "<type>" --region "<region>"If this fails with permissions error, guide the user to Power Platform Admin Center to create it, then connect.
Confirm connection:
dataverse auth who
dataverse org who # or: pac org whoParse the output to extract DATAVERSE_URL and TENANT_ID.
If neither command shows a tenant ID, fall back to:
curl -sI https://<org>.crm.dynamics.com/api/data/v9.2/ \
| grep -i "WWW-Authenticate" \
| sed -n 's|.*login\.microsoftonline\.com/\([^/]*\).*|\1|p'Step 2b: Front-load PAC CLI auth for the same environment
PAC uses its own AAD app, so a separate sign-in is required for dv-solution and dv-admin. Do it now — user signs in twice back-to-back, no later surprises.
pac auth list # skip if a profile for $DATAVERSE_URL exists
pac auth create --name <orgid> --environment <DATAVERSE_URL>Use the same account as Step 2. If PAC CLI is not installed, skip with a note that dv-solution / dv-admin will need it later.
---
Step 3: Create .env
Present authentication options:
How would you like to authenticate with Dataverse?
1. Interactive login (recommended) — Sign in via browser. No app registration needed. Token stays cached across sessions.
2. Service principal (for CI/CD) — Uses CLIENT_ID and CLIENT_SECRET from an Azure app registration.
Write .env directly — do not instruct the user to create it:
Detect the current tool (Claude or Copilot) from context and set MCP_CLIENT_ID automatically:
- Claude (CLI or VSCode extension):
0c412cc3-0dd6-449b-987f-05b053db9457 - GitHub Copilot:
aebc6443-996d-45c2-90f0-388ff96faa56
Also set plugin attribution variables for User-Agent tagging. Fill in the two literals below from your own context — you (the agent) loaded this plugin, so you already know both values:
PLUGIN_VERSION— theversionfield of your loaded plugin manifest (e.g."1.5.0"). At runtime,auth.pyre-reads this from the live manifest via host env vars; this.enventry is a fallback for offline cases.AGENT— your host identity, one of:claude-code,copilot,cursor,codex, orunknown. Must match an entry in_ALLOWED_AGENTSinauth.py— if you don't recognize your host, useunknown.
# Substitute these two literals from your loaded plugin context.
# Do NOT leave the angle-bracket placeholders — replace with real values.
plugin_version = "<plugin manifest version, e.g. 1.5.0>"
agent_host = "<your host name: claude-code | copilot | cursor | codex | unknown>"
with open(".env", "w") as f:
f.write(f"DATAVERSE_URL={dataverse_url}\n")
f.write(f"TENANT_ID={tenant_id}\n")
f.write(f"MCP_CLIENT_ID={mcp_client_id}\n")
f.write(f"DATAVERSE_PLUGIN_VERSION={plugin_version}\n")
f.write(f"DATAVERSE_PLUGIN_AGENT={agent_host}\n")
f.write(f"SOLUTION_NAME={solution_name}\n")
f.write(f"PUBLISHER_PREFIX=\n") # filled in when solution is created
f.write(f"PAC_AUTH_PROFILE=nonprod\n")
if client_id:
f.write(f"CLIENT_ID={client_id}\n")
if client_secret:
f.write(f"CLIENT_SECRET={client_secret}\n")Multi-environment repos: If the team deploys to multiple environments from the same repo, each developer's.envrepresents their current target. Consider.env.dev,.env.staging, etc., with a pattern likecp .env.dev .envto switch targets.
Ensure .env is in .gitignore:
import os
GITIGNORE_ENTRIES = [
".env", ".vscode/settings.json", ".claude/mcp_settings.json",
".token_cache.bin", "*.snk", "__pycache__/", "*.pyc",
"solutions/*.zip", "plugins/**/bin/", "plugins/**/obj/",
]
gitignore = open(".gitignore").read() if os.path.exists(".gitignore") else ""
missing = [e for e in GITIGNORE_ENTRIES if e not in gitignore]
if missing:
with open(".gitignore", "a") as f:
f.write("\n" + "\n".join(missing) + "\n")Skip condition: .env already exists with all required values.
---
Step 4: Set up project structure (new projects only)
If this is a new project (no scripts/ directory):
mkdir -p solutions plugins scriptsCopy plugin scripts:
cp .github/plugins/dataverse/scripts/auth.py scripts/Copy templates/CLAUDE.md to the repo root if it doesn't exist. Replace placeholders ({{DATAVERSE_URL}}, {{SOLUTION_NAME}}, {{PUBLISHER_PREFIX}}) with values from .env.
Skip condition: scripts/auth.py exists.
---
Step 5: Verify the connection
dataverse auth who
pac org who
python scripts/auth.pyAll three must resolve the same user/environment. They prove the DV CLI cache, the PAC profile (Step 2b), and Python's silent reuse of the DV CLI cache are all wired.
If any fail:
dataverse auth whofails → re-run Step 2.pac org whofails → re-run Step 2b.python scripts/auth.pyprints a device-code URL → DV CLI cache missing/wrong tenant; re-run Step 2 and confirmmsal+msal-extensionsare installed (pip show msal msal-extensions).- Other Python error → check SDK install and
.env.
---
Step 6: Configure MCP server
Skip this step if MCP is already configured:
.mcp.jsonor~/.copilot/mcp-config.jsonor~/.cursor/mcp.jsonor~/.codex/config.tomlcontains a Dataverse server entryclaude mcp listshows adataverse-*server registered
If MCP is not configured, follow mcp-configuration.md:
1. Detect which tool the user is running (Copilot, Claude, Cursor, or Codex) from context 2. Set MCP_CLIENT_ID based on tool choice 3. Get environment URL from .env 4. Default to GA endpoint (/api/mcp) 5. Register the MCP server per host (see the per-host blocks below) 6. Handle admin consent and allowlist — prefer dataverse mcp allow <MCP_CLIENT_ID> over the portal (one-time per tenant/environment)
Plugin attribution for MCP: This plugin uses the stdio proxy transport (npx @microsoft/dataverse mcp <url>) — the CLI runs as a local subprocess and proxies requests to the Dataverse MCP HTTP endpoint. When registering it, include DATAVERSE_OPERATION_CONTEXT in the env block so the CLI appends it to its User-Agent on outbound requests to /api/mcp. Build the value from .env:
DATAVERSE_OPERATION_CONTEXT=app=dataverse-skills/{DATAVERSE_PLUGIN_VERSION};skill=mcp-direct;agent={DATAVERSE_PLUGIN_AGENT}For Claude Code (claude mcp add -t stdio), pass it via -e DATAVERSE_OPERATION_CONTEXT=.... For Copilot/Cursor JSON configs, add it to the "env" object in the stdio server entry; for Codex, add it to its [mcp_servers.<name>.env] table.
Important: MCP configuration requires an editor/CLI restart.
For Copilot: Write the JSON config, then:
✅ Dataverse MCP server configured. Restart your editor for changes to take effect.
For Claude: Run the claude mcp add command, then warn the user about the auth popup that will appear on next launch:
✅ Dataverse MCP server registered. Restart Claude Code to enable MCP tools.
Remember to use `claude --continue` to resume the session without losing context.
>
On restart, a browser window will open asking you to sign in to your Dataverse environment. This is the MCP proxy authenticating on your behalf — sign in with the same account you used fordataverse auth create(or your active DV CLI profile, e.g.,{username}). This only happens once; the token is cached for future sessions, anddataverse auth createpopulates the same cache so the popup is skipped if you've already run it.
For Cursor: Write the JSON config, then:
✅ Dataverse MCP serverdataverse-{orgid}configured in~/.cursor/mcp.json. Reload the Cursor window (Ctrl+Shift+P → "Developer: Reload Window") for the new MCP server to appear under Settings → Tools & MCPs.
>
On first use, thenpx @microsoft/dataverseproxy starts a device-code sign-in in your browser. Sign in with the same account you used fordataverse auth create; the token is cached in your OS credential store for future sessions. If you've already rundataverse auth create, the proxy reuses that cache silently — no device code.
For Codex: Write the TOML config to ~/.codex/config.toml. Codex loads MCP tools only at startup, so don't claim they're callable until the user restarts. Tell the user:
✅ Dataverse MCP serverdataverse-{orgid}configured in~/.codex/config.toml. Restart Codex (CLI) or reload the Codex IDE to load the MCP tools.
---
Step 7: Final verification
After the editor/CLI restarts, both of these must succeed before declaring the setup complete:
Check 1: `claude mcp list` (or Copilot equivalent) shows ✓ Connected
claude mcp listThis proves the MCP server process starts and speaks the MCP protocol. It does NOT by itself prove that data operations work — authentication, environment allowlisting, and endpoint reachability are only exercised on the first real tool call.
Check 2: Agent successfully calls `list_tables` and returns data
"List the tables in my Dataverse environment."
This proves end-to-end wiring: auth, tenant consent, environment allowlist, and endpoint reachability are all correct. If the agent falls back to PAC CLI or Web API, see mcp-configuration.md troubleshooting.
Only when both checks pass is the setup verified.
Interpreting failures:
- If Check 1 fails (server not ✓ Connected): the MCP server itself cannot start. Re-run Step 6 and check that
npx/Node.js are installed and the MCP registration succeeded. - If Check 1 passes but Check 2 fails (server starts but
list_tableserrors): the server can speak MCP but cannot reach or read Dataverse. Run--validatebelow to diagnose.
Diagnostic — `--validate` (for failure investigation only):
npx @microsoft/dataverse mcp {DATAVERSE_URL} --validateThis exercises two Dataverse MCP endpoints with a fresh authentication handshake and reports detailed errors (auth, allowlist, consent, endpoint reachability):
- GA / Production endpoint —
{DATAVERSE_URL}/api/mcp. This is the one the plugin actually uses at runtime. - Preview endpoint —
{DATAVERSE_URL}/api/mcp_preview. Opt-in per environment; not used by the plugin.
Do not use `--validate` as a success gate on first-time setup. On a freshly configured workspace, the token cache hasn't warmed up, so --validate can fail with MsalClientException or 403 while MCP is actually working fine on subsequent real calls. Reserve --validate for diagnosing a confirmed failure in Check 1 or Check 2.
How to read `--validate` output:
- Look at the GA / Production endpoint (`/api/mcp`) result first. If this passes, MCP will work for normal plugin usage regardless of what the Preview endpoint reports.
- A `403 Forbidden` on the Preview endpoint (`/api/mcp_preview`) is expected for most environments. Preview is opt-in per environment; if your environment hasn't enabled it, the Preview check will always fail. This does not indicate a broken setup.
- Ignore the overall exit code and the `⚠ Partial success` warning in this case. The validator returns exit code
1(failure) unless BOTH/api/mcpand/api/mcp_previewpass. Because most environments don't enable the Preview endpoint,--validatewill exit1even when MCP is fully functional via the GA endpoint. Focus on per-endpoint results, not the aggregate status. - If the GA endpoint (`/api/mcp`) fails: that's the real signal to investigate — auth, tenant consent, environment allowlist, or endpoint reachability.
MCP Server Capabilities
| Task | Use |
|---|---|
| Create/read/update/delete data records | MCP server |
| Create a new table | MCP server |
| Explore what tables/columns exist | MCP server (list_tables, describe_table) |
| Add a column to an existing table | MCP server (update_table) for basic columns; SDK or Web API (see dv-metadata) for advanced options (choice columns, lookups, relationships) |
| Create a relationship / lookup | SDK (see dv-metadata) |
| Create or modify a form | Web API (see dv-metadata) |
| Create or modify a view | Web API (see dv-metadata) |
After verifying MCP works, tell the user:
✅ Connected to Dataverse at {DATAVERSE_URL}. Tools installed, authenticated, MCP live.>
You can now:
- Create tables, columns, and relationships (dv-metadata)- Write and import data (dv-data)- Query and analyze data (dv-query)- Export and promote solutions (dv-solution)>
To create your first solution, see the dv-solution skill.To load sample data (accounts, contacts, opportunities), ask: "Load demo data into my Dataverse environment."
---
Supported Agents
This plugin's skill files are natively loaded by both GitHub Copilot CLI and Claude Code CLI when installed as a plugin. No manual context-loading is needed — both agents discover and invoke skills automatically.
The PAC CLI commands, Python scripts, and XML templates work identically in both environments.
MCP Server Configuration Reference
Detailed instructions for configuring the Dataverse MCP server for GitHub Copilot, Claude Code, Cursor, or Codex.
The environment URL should already be known from the dv-connect flow (stored in DATAVERSE_URL in .env). If it's not set, go back to Step 2 of the dv-connect skill to discover and select the environment first.
The parameters for the MCP server should be determined from context or environment variables where possible, and interactive prompts should only be used when it cannot be done.
---
0. Determine which tool to configure
Determine whether to configure MCP for GitHub Copilot, Claude Code, Cursor, or Codex:
- If explicitly mentioned in prompt, use that.
- Otherwise, determine which tool the user is running from the context.
- Only if choosing based on the context is impossible, ask the user:
Which tool would you like to configure the Dataverse MCP server for?
1. GitHub Copilot
2. Claude
3. Cursor
4. Codex
Based on the result, set the TOOL_TYPE variable to copilot, claude, cursor, or codex. Store this for use in all subsequent steps.
Set the MCP_CLIENT_ID variable in .env based on the tool choice:
- If
copilot:MCP_CLIENT_ID=aebc6443-996d-45c2-90f0-388ff96faa56 - If
claude,cursor, orcodex:MCP_CLIENT_ID=0c412cc3-0dd6-449b-987f-05b053db9457(all use the@microsoft/dataversenpx stdio proxy, which authenticates as the Dataverse CLI app) - If
claudeand the VSCode extension is used: set it to the same value asCLIENT_IDif already set, otherwise offer to create a new app registration following the auth setup in thedv-connectskill.
---
1. Determine the MCP scope
Choose the configuration scope based on the tool. Use the scope explicitly mentioned by the user, or choose the default without asking to confirm it.
If TOOL_TYPE is `copilot`:
The options are: 1. Globally (default, available in all projects) 2. Project-only (available only in this project)
Based on the scope, set the CONFIG_PATH variable:
- Global:
~/.copilot/mcp-config.json(use the user's home directory) - Project:
.mcp.json(relative to the current working directory)
Store this path for use in steps 2 and 5.
If TOOL_TYPE is `claude`:
The options are: 1. User (available in all projects for this user) 2. Project (default, available only in this project) 3. Local (scoped to current project directory)
Based on the scope, set the CLAUDE_SCOPE variable:
- User:
CLAUDE_SCOPE=user - Project:
CLAUDE_SCOPE=project - Local:
CLAUDE_SCOPE=local
Store this value for use in step 5.
If TOOL_TYPE is `cursor`:
The options are: 1. Globally (default, available in all projects) 2. Project-only (available only in this project)
Based on the scope, set the CONFIG_PATH variable:
- Global:
~/.cursor/mcp.json(use the user's home directory) - Project:
.cursor/mcp.json(relative to the current working directory)
Store this path for use in steps 2 and 5.
If TOOL_TYPE is `codex`:
Codex stores MCP servers in a config.toml file. The options are: 1. Globally (default, available in all projects) 2. Project-only (trusted projects only)
Based on the scope, set the CONFIG_PATH variable:
- Global:
~/.codex/config.toml(use the user's home directory) - Project:
.codex/config.toml(relative to the current working directory)
Store this path for use in steps 2 and 5.
---
2. Check already-configured MCP servers
If TOOL_TYPE is `copilot`:
Read the MCP configuration file at CONFIG_PATH (determined in step 1) to check for already-configured servers.
The configuration file is a JSON file with the following structure:
{
"mcpServers": {
"ServerName1": {
"type": "http",
"url": "https://example.com/api/mcp"
}
}
}Or it may use "servers" instead of "mcpServers" as the top-level key.
Extract all url values from the configured servers and store them as CONFIGURED_URLS. For example:
["https://orgfbb52bb7.crm.dynamics.com/api/mcp"]If the file doesn't exist or is empty, treat CONFIGURED_URLS as empty ([]). This step must never block the skill.
If the environment URL from .env is already in CONFIGURED_URLS, the MCP server is already configured. Confirm with the user whether they want to re-register it (e.g. to change the endpoint type) before proceeding. If not, skip to the end.
If TOOL_TYPE is `claude`:
Skip this step — Claude uses CLI commands to manage MCP servers, so we don't need to check existing configuration.
If TOOL_TYPE is `cursor`:
Read the MCP configuration file at CONFIG_PATH (determined in step 1) to check for already-configured servers. Same logic as Copilot: parse mcpServers (or servers) keys, extract URLs, store as CONFIGURED_URLS. If the file doesn't exist or is empty, treat CONFIGURED_URLS as empty ([]).
If the environment URL from .env is already in CONFIGURED_URLS, the MCP server is already configured. Confirm with the user whether they want to re-register it before proceeding. If not, skip to the end.
If TOOL_TYPE is `codex`:
Read the config.toml file at CONFIG_PATH (determined in step 1) to check for already-configured servers. The file is TOML — look for [mcp_servers.<name>] tables and extract the environment URL from each table's args array (the URL is the argument that follows "mcp"). Store the URLs as CONFIGURED_URLS. If the file doesn't exist or has no [mcp_servers.*] tables, treat CONFIGURED_URLS as empty ([]).
If the environment URL from .env is already in CONFIGURED_URLS, the MCP server is already configured. Confirm with the user whether they want to re-register it before proceeding. If not, skip to the end.
---
3. Determine the environment URL
If the user provided a URL via command parameters it is: '$ARGUMENTS'. If the user mentioned the URL in the prompt, use it. Otherwise, take the URL from the DATAVERSE_URL variable in .env. If you have the URL, skip to step 4.
If the file or the variable doesn't exist, the environment URL must be discovered. Try the dv-connect skill's Step 2 first. If that's not possible (e.g., this reference is being used standalone), use the auto-discovery priority order below — try each method in order, stop at the first that succeeds:
1. PAC CLI (preferred) → step 3a 2. Azure CLI (fallback) → step 3b 3. Manual entry (last resort) → step 3c
3a. Auto-discover via PAC CLI (preferred)
Check if PAC CLI is available:
pac --versionIf available, check auth and list environments:
pac auth list
pac org who
pac env listIf PAC CLI is authenticated and pac env list returns results, present the environments to the user:
I found the following Dataverse environments via PAC CLI. Which one would you like to configure MCP for?
>
1. My Dev Org — https://orgfbb52bb7.crm.dynamics.com2. Another Env — https://orgabc123.crm.dynamics.com>
Or type a URL manually.
If PAC CLI is not installed or not authenticated, fall back to step 3b.
3b. Auto-discover via Azure CLI (fallback)
Check prerequisites:
- Verify Azure CLI (
az) is installed (check withwhich azorwhere azon Windows) - If not installed, inform the user and fall back to step 3c
Make the API call:
1. Check if the user is logged into Azure CLI:
az account showIf this fails, prompt the user to log in:
az login2. Get an access token for the Power Apps API:
az account get-access-token --resource https://service.powerapps.com/ --query accessToken --output tsv3. Call the Power Apps API to list environments:
GET https://api.powerapps.com/providers/Microsoft.PowerApps/environments?api-version=2016-11-01
Authorization: Bearer {token}
Accept: application/json4. Parse the JSON response and filter for environments where properties?.linkedEnvironmentMetadata?.instanceUrl is not null.
5. For each matching environment, extract:
properties.displayNameasdisplayNameproperties.linkedEnvironmentMetadata.instanceUrl(remove trailing slash) asinstanceUrl
6. Create a list of environments in this format:
[
{ "displayName": "My Org (default)", "instanceUrl": "https://orgfbb52bb7.crm.dynamics.com" },
{ "displayName": "Another Env", "instanceUrl": "https://orgabc123.crm.dynamics.com" }
]If the API call succeeds, present the environments as a numbered list. For each environment, check whether any URL in CONFIGURED_URLS starts with that environment's instanceUrl — if so, append (already configured) to the line.
I found the following Dataverse environments on your account. Which one would you like to configure?
>
1. My Org (default) — https://orgfbb52bb7.crm.dynamics.com (already configured)2. Another Env — https://orgabc123.crm.dynamics.com>
Enter the number of your choice, or type "manual" to enter a URL yourself.
If the user selects an already-configured environment, confirm that they want to re-register it (e.g. to change the endpoint type) before proceeding.
If the user types "manual", fall back to step 3c.
If the API call fails (user not logged in, network error, no environments found, or any other error), tell the user what went wrong and fall back to step 3c.
3c. Manual entry — ask for the URL
Ask the user to provide their environment URL directly:
Please enter your Dataverse environment URL.
>
Example: https://myorg.crm10.dynamics.com>
You can find this in the Power Platform Admin Center under Environments.
3d. Remember the selected URL
Take the URL determined above (from context, .env, manual entry, or instanceUrl from discovery) and strip any trailing slash. This is USER_URL for the remainder of this reference.
---
4. Decide whether to use the "Preview" or "Generally Available (GA)" endpoint
Determine from the context which of these options the user wants to use. If they did not mention either, default to GA:
- If Generally Available (GA): set
MCP_URLto{USER_URL}/api/mcp - If Preview: set
MCP_URLto{USER_URL}/api/mcp_preview
---
5. Register the MCP server
If TOOL_TYPE is `copilot`:
Update the MCP configuration file at CONFIG_PATH (determined in step 1) to add the new server.
Generate a unique server name from the USER_URL: 1. Extract the subdomain (organization identifier) from the URL
- Example:
https://orgbc9a965c.crm10.dynamics.com→orgbc9a965c
2. Prepend DataverseMcp to create the server name
- Example:
DataverseMcporgbc9a965c
This is the SERVER_NAME.
Update the configuration file:
1. Read the existing configuration file at CONFIG_PATH, or create a new empty config if it doesn't exist:
{}2. Determine which top-level key to use:
- If the config already has
"servers", use that - Otherwise, use
"mcpServers"
3. Add or update the server entry:
{
"mcpServers": {
"{SERVER_NAME}": {
"type": "http",
"url": "{MCP_URL}"
}
}
}4. Write the updated configuration back to CONFIG_PATH with proper JSON formatting (2-space indentation).
Important notes:
- Do NOT overwrite other entries in the configuration file
- Preserve the existing structure and formatting
- If
SERVER_NAMEalready exists, update it with the newMCP_URL
If TOOL_TYPE is `claude`:
Generate the CLI command. Do NOT edit any configuration files.
IMPORTANT: Always use `-t stdio` transport with the npx proxy. Never use --transport http or --transport sse for Claude — the Dataverse MCP endpoint requires authentication that only the npx proxy handles. Using HTTP transport directly will fail with connection errors.
Generate a unique server name from the USER_URL: 1. Extract the subdomain (organization identifier) from the URL
- Example:
https://orgbc9a965c.crm10.dynamics.com→orgbc9a965c
2. Use lowercase format: dataverse-{orgid}
- Example:
dataverse-orgbc9a965c
This is the SERVER_NAME.
Build the command:
Construct the command based on CLAUDE_SCOPE and whether the user chose GA or Preview endpoint. Always pass `-e DATAVERSE_OPERATION_CONTEXT="…"` so the stdio proxy attaches plugin attribution to outbound requests (same role as the env block in the Copilot / Cursor JSON configs):
claude mcp add --scope {CLAUDE_SCOPE} {SERVER_NAME} -t stdio -e DATAVERSE_OPERATION_CONTEXT="app=dataverse-skills/{DATAVERSE_PLUGIN_VERSION};skill=mcp-direct;agent=claude-code" -- npx -y @microsoft/dataverse@latest mcp "{USER_URL}" {ENDPOINT_FLAG}When running on Windows without WSL, wrap the npx call into cmd //c and omit the quotes around the URL:
claude mcp add --scope {CLAUDE_SCOPE} {SERVER_NAME} -t stdio -e DATAVERSE_OPERATION_CONTEXT="app=dataverse-skills/{DATAVERSE_PLUGIN_VERSION};skill=mcp-direct;agent=claude-code" -- cmd //c "npx -y @microsoft/dataverse@latest mcp {USER_URL} {ENDPOINT_FLAG}"Where:
{CLAUDE_SCOPE}isuser,project, orlocal(from step 1){SERVER_NAME}is the generated server name (e.g.,dataverse-orgbc9a965c){USER_URL}is the base environment URL (e.g.,https://orgbc9a965c.crm10.dynamics.com){ENDPOINT_FLAG}is--previewif the user chose Preview endpoint in step 4, otherwise omit this flag{DATAVERSE_PLUGIN_VERSION}comes from.env(set in dv-connect Step 3)
Example commands:
- GA endpoint with user scope:
claude mcp add --scope user dataverse-orgbc9a965c -t stdio -e DATAVERSE_OPERATION_CONTEXT="app=dataverse-skills/1.5.0;skill=mcp-direct;agent=claude-code" -- npx -y @microsoft/dataverse@latest mcp "https://orgbc9a965c.crm10.dynamics.com" - Preview endpoint with project scope:
claude mcp add --scope project dataverse-orgbc9a965c -t stdio -e DATAVERSE_OPERATION_CONTEXT="app=dataverse-skills/1.5.0;skill=mcp-direct;agent=claude-code" -- npx -y @microsoft/dataverse@latest mcp "https://orgbc9a965c.crm10.dynamics.com" --preview - GA endpoint on Windows with project scope:
claude mcp add --scope project dataverse-orgbc9a965c -t stdio -e DATAVERSE_OPERATION_CONTEXT="app=dataverse-skills/1.5.0;skill=mcp-direct;agent=claude-code" -- cmd //c "npx -y @microsoft/dataverse@latest mcp https://orgbc9a965c.crm10.dynamics.com"
Store this command as CLAUDE_COMMAND for use in step 8.
If TOOL_TYPE is `cursor`:
Update the MCP configuration file at CONFIG_PATH (determined in step 1) to add the new server.
IMPORTANT: Always use the stdio transport via the npx proxy. Do not configure a direct url to /api/mcp — the Dataverse MCP HTTP endpoint requires the npx proxy to handle authentication. The proxy is @microsoft/dataverse@latest mcp <url>.
Generate a unique server name from the USER_URL: 1. Extract the subdomain (organization identifier) from the URL
- Example:
https://orgbc9a965c.crm10.dynamics.com→orgbc9a965c
2. Use lowercase format: dataverse-{orgid}
- Example:
dataverse-orgbc9a965c
This is the SERVER_NAME.
Update the configuration file:
1. If CONFIG_PATH is for a project-scoped configuration (.cursor/mcp.json), ensure the .cursor directory exists first:
mkdir -p .cursor2. Read the existing configuration file at CONFIG_PATH, or create a new empty config if it doesn't exist:
{ "mcpServers": {} }3. Add or update the server entry under mcpServers:
{
"mcpServers": {
"{SERVER_NAME}": {
"command": "npx",
"args": ["-y", "@microsoft/dataverse@latest", "mcp", "{USER_URL}"],
"env": {
"DATAVERSE_OPERATION_CONTEXT": "app=dataverse-skills/{DATAVERSE_PLUGIN_VERSION};skill=mcp-direct;agent=cursor"
}
}
}
}Append "--preview" to the args array if the user chose the Preview endpoint in step 4.
4. Write the updated configuration back to CONFIG_PATH with proper JSON formatting (2-space indentation).
Important notes:
- Do NOT overwrite other entries in the configuration file — preserve sibling
mcpServersentries - If
SERVER_NAMEalready exists, update it with the new args - After writing, ask the user to reload the Cursor window (Ctrl+Shift+P → "Developer: Reload Window") for the new MCP server to appear
If TOOL_TYPE is `codex`:
Update the config.toml file at CONFIG_PATH (determined in step 1) to add the new server.
IMPORTANT: Always use the stdio transport via the npx proxy. Do not configure a direct url to /api/mcp — the Dataverse MCP HTTP endpoint requires the npx proxy to handle authentication. The proxy is @microsoft/dataverse@latest mcp <url>.
Generate a unique server name from the USER_URL: 1. Extract the subdomain (organization identifier) from the URL
- Example:
https://orgbc9a965c.crm10.dynamics.com→orgbc9a965c
2. Use lowercase format: dataverse-{orgid}
- Example:
dataverse-orgbc9a965c
This is the SERVER_NAME.
Update the configuration file:
1. If CONFIG_PATH is for a project-scoped configuration (.codex/config.toml), ensure the .codex directory exists first:
mkdir -p .codex2. Read the existing config.toml at CONFIG_PATH, or treat it as empty if it doesn't exist. Preserve all existing content (other settings and [mcp_servers.*] tables).
3. Add the server as a [mcp_servers.{SERVER_NAME}] table with an env sub-table:
[mcp_servers.{SERVER_NAME}]
command = "npx"
args = ["-y", "@microsoft/dataverse@latest", "mcp", "{USER_URL}"]
[mcp_servers.{SERVER_NAME}.env]
DATAVERSE_OPERATION_CONTEXT = "app=dataverse-skills/{DATAVERSE_PLUGIN_VERSION};skill=mcp-direct;agent=codex"Append "--preview" to the args array if the user chose the Preview endpoint in step 4.
Where:
{SERVER_NAME}is the generated server name (e.g.,dataverse-orgbc9a965c){USER_URL}is the base environment URL (e.g.,https://orgbc9a965c.crm10.dynamics.com){DATAVERSE_PLUGIN_VERSION}comes from.env(set in dv-connect Step 3)
Important notes:
- Do NOT overwrite other entries in the file — preserve sibling
[mcp_servers.*]tables and any other settings - If
[mcp_servers.{SERVER_NAME}]already exists, replace that table (and its.envsub-table) with the new values; otherwise append the new tables - After writing, ask the user to restart Codex for the new MCP server to load
---
6. Ensure tenant-level admin consent (one-time per tenant)
The MCP client app registration must be granted admin consent on the Azure AD tenant. This is a one-time action per tenant — once done, it applies to all Dataverse environments in that tenant. It requires an Azure AD Global Admin or Privileged Role Admin.
List out the parameters chosen in previous steps:
- Tool type (Copilot, Claude, Cursor, or Codex) from step 0
- Scope from step 1
- Environment URL from step 3
- Endpoint (GA or Preview) from step 4
- MCP Client ID from step 0
Ask the user if admin consent has already been granted for this tenant. If not, provide the consent URL:
Tenant-level admin consent is required for the MCP client app. This is a one-time action per Azure AD tenant — once granted, it covers all environments in the tenant.
>
An Azure AD Global Admin or Privileged Role Admin must open this URL and click Accept:
```
https://login.microsoftonline.com/{TENANT_ID}/adminconsent?client_id={MCP_CLIENT_ID}
```
>
If you don't have admin permissions, send this URL to your Azure AD administrator.
Wait for the user to confirm this is done (or was already done previously) before proceeding.
---
7. Add the MCP client to the environment's allowed list (one-time per environment)
Separately from tenant-level consent, each Dataverse environment must explicitly allow the MCP client. This is a one-time action per environment and does NOT require Azure AD admin permissions — any user with Environment Admin or System Administrator role in the environment can do it.
One sign-in for CLI, MCP, and Python. When the user runsdataverse auth create(seedv-connectStep 2) the token cache is written to a path / OS keychain entry that the@microsoft/dataversestdio MCP proxy andscripts/auth.pyboth read silently. As a result, the allowlisted MCP client ID (0c412cc3-…for the Claude / Cursor stdio proxy, oraebc6443-…for Copilot HTTP) is exercised exactly once per environment — there is no separate Python device-code sign-in for the same user/env. If a script does prompt for a device code, the shared cache is missing or stale; re-rundataverse auth create --environment <url>.
Present the methods in priority order. Always attempt Method A first — it is a single command, needs no portal navigation, and is the most reliable path.
Method A (preferred): Dataverse CLI `mcp allow`
>
Run the first-class CLI command — it ensures the MCP client app is in the environment's allowed list using the active auth profile's environment:
>
```
dataverse mcp allow {MCP_CLIENT_ID}
```
>
Any of these outputs means success — continue to validation:
- Client {MCP_CLIENT_ID} is already enabled. No changes needed.- Client exists but is disabled. Enabling... Done.- Client not found. Creating... Done.>
Requires the signed-in user to have Dataverse admin rights (Environment Admin or System Administrator) on the target environment. No Azure AD admin needed.
Method B (fallback): Power Platform Admin Center
>
Use this only if dataverse mcp allow is unavailable (CLI not installed) or fails on permissions:>
1. Go to Power Platform Admin Center
2. Select Environments in the left navigation
3. Click on your environment (e.g., the one matching {USER_URL})4. Click Settings in the top toolbar
5. Expand Product and click Features
6. Scroll down to the MCP Server section
7. Toggle Enable MCP Server to On (if not already)
8. Under Allowed clients, click Add client
9. Paste the MCP Client ID: {MCP_CLIENT_ID}10. Click Save
Method C (fallback): Programmatic via script
>
Run scripts/enable-mcp-client.py to add the client ID to the allowed list via the Dataverse API. Useful in non-interactive environments where the CLI isn't present.Do not send the user to the portal (Method B) before attempting Method A. Run dataverse mcp allow {MCP_CLIENT_ID} yourself first; fall back to Method B or C only if it fails (CLI missing, or the user lacks Dataverse admin rights).
Then validate the endpoint:
npx -y @microsoft/dataverse@latest mcp {USER_URL} --validateTreat GA endpoint is valid, but Preview endpoint is not configured as success for a GA registration (the default). Only revisit enablement if the GA endpoint still returns 403 Forbidden after mcp allow reported success.
---
8. Confirm success and provide next steps
If TOOL_TYPE is `copilot`:
Tell the user:
✅ Dataverse MCP server configured for GitHub Copilot at {MCP_URL}.>
Configuration saved to: {CONFIG_PATH}>
IMPORTANT: You must restart your editor for the changes to take effect.
>
Restart your editor or reload the window, then you will be able to:
- List all tables in your Dataverse environment
- Query records from any table
- Create, update, or delete records
- Explore your schema and relationships
Pause and give the user a chance to restart their editor before proceeding. Do not perform any subsequent or parallel operations until the user responds — they need MCP tools to be active first.
If TOOL_TYPE is `claude`:
Run {CLAUDE_COMMAND} to install the Dataverse MCP server, then tell the user:
✅ Dataverse MCP server registered. Restart Claude Code to enable MCP tools.
Remember to use `claude --continue` to resume the session without losing context.
>
On restart, a browser window will open asking you to sign in to your Dataverse environment. This is the MCP proxy (@microsoft/dataverse) authenticating on your behalf. Sign in with the same account you used earlier. This only happens once — the token is cached for future sessions.>
After signing in, you will be able to:
- List all tables in your Dataverse environment
- Query records from any table
- Create, update, or delete records
- Explore your schema and relationships
Pause and give the user a chance to restart the session to enable it before proceeding. Do not perform any subsequent or parallel operations until the user responds.
If TOOL_TYPE is `codex`:
Tell the user:
✅ Dataverse MCP server{SERVER_NAME}written to{CONFIG_PATH}.
>
IMPORTANT: Codex loads MCP tools at startup. Fully restart Codex (CLI) or reload the Codex IDE for the Dataverse tools to appear — the current session cannot call them yet.
>
After restart, you will be able to:
- List all tables in your Dataverse environment
- Query records from any table
- Create, update, or delete records
- Explore your schema and relationships
Do not claim the Dataverse MCP tools are callable in the current session. They become available only after a restart, once Codex discovers the dataverse-* server. If the user asks you to run an MCP query before restarting, explain that the tools load on restart — do not spin up a separate npx @microsoft/dataverse mcp stdio proxy as a workaround, and if the user explicitly required MCP, do not silently fall back to the SDK or Web API. Surface the restart requirement instead.
Pause and give the user a chance to restart Codex before proceeding.
---
9. Troubleshooting
If something goes wrong, help the user check:
- The URL format is correct (
https://<org>.<region>.dynamics.com) - They have access to the Dataverse environment
- The environment URL matches what's shown in the Power Platform Admin Center
- Tenant-level admin consent has been granted for the MCP client app. This is a one-time per-tenant action requiring an Azure AD admin. Without it, authentication succeeds but the app is denied access. Use the admin consent URL from step 6.
- Org-level allowed clients — the MCP client ID has been added to the environment's allowed list. The quickest fix is the CLI command
dataverse mcp allow {MCP_CLIENT_ID}(Step 7, Method A). To check or fix it via the portal instead:
1. Go to Power Platform Admin Center > Environments > your environment > Settings > Product > Features 2. Verify MCP Server is toggled On 3. Verify the MCP Client ID appears under Allowed clients
- If using the Preview endpoint, verify that the Preview MCP endpoint is also enabled in the same Features page
- If TOOL_TYPE is `copilot`:
- For project-scoped configuration, ensure the
.mcp.jsonfile was created successfully - For global configuration, check permissions on the
~/.copilot/directory - If TOOL_TYPE is `claude`:
- Ensure the
claudeCLI is installed and available in their PATH - If the command fails, check that
npxandnpmare installed - After running the command, they must restart Claude Code for the changes to take effect (remind them: "Remember to use `claude --continue` to resume the session without losing context")
- They can verify the installation with
claude mcp list - If the MCP proxy version seems outdated or behaves unexpectedly, clear the npx cache and retry:
npx clear-npx-cache- To validate authentication independently, run:
npx -y @microsoft/dataverse@latest mcp "{USER_URL}" --validateThis checks credentials and prints error details if issues are found.
- If TOOL_TYPE is `codex`:
- The Dataverse MCP tools load only on a Codex restart after
~/.codex/config.tomlis written — the session that wrote the config cannot see them. Do not treat their absence in the current session as a failure or build a workaround proxy. - If
--validatereturns 403 Forbidden on the GA endpoint, the client isn't allowlisted yet — rundataverse mcp allow {MCP_CLIENT_ID}(Step 7, Method A), then re-validate. - Confirm the server entry exists: look for
[mcp_servers.{SERVER_NAME}]in~/.codex/config.toml(global) or.codex/config.toml(project). - If
npxcan't be found when Codex launches the server, ensure Node.js 18+ is on PATH; on Windows the proxy command may needcmd /cwrapping (see Step 5).
Tool Installation & Authentication Reference
Required Tools
Check all in parallel. Install any that are missing.
| Tool | Check | Install |
|---|---|---|
| PAC CLI | pac (prints version banner; pac --version is not valid and returns non-zero) | winget install Microsoft.PowerAppsCLI |
| GitHub CLI | gh --version | winget install GitHub.cli |
| Azure CLI | az --version | winget install Microsoft.AzureCLI |
| .NET SDK | dotnet --version | winget install Microsoft.DotNet.SDK.9 |
| Python 3 | python --version | winget install Python.Python.3.12 |
| Node.js | node --version | winget install OpenJS.NodeJS.LTS |
| Dataverse CLI | npm list -g @microsoft/dataverse (shows @microsoft/dataverse@<version> if installed; (empty) if not) | npm install -g @microsoft/dataverse@latest (always upgrades to latest — mirrors pip install --upgrade for the Python SDK) |
| Git | git --version | winget install Git.Git |
After any winget install, the new tool may not be in PATH until the shell is restarted. If a tool is not found immediately after install, ask the user to close and reopen the terminal (if running in Claude Code, remind them to resume the session correctly: "Remember to use `claude --continue` to resume the session without losing context"), then proceed.
PAC CLI on Windows Git Bash
PAC CLI is a .cmd wrapper. In Git Bash (used by Claude Code), pac alone may fail or hang. Use the PowerShell wrapper:
powershell -Command "& 'C:\Users\$USER\AppData\Local\Microsoft\PowerAppsCLI\pac.cmd' help"Or, if installed via dotnet tool install --global:
powershell -Command "& pac help"To avoid repeating this, add an alias to ~/.bashrc:
echo 'alias pac="powershell -Command \"& pac.cmd\""' >> ~/.bashrc
source ~/.bashrcIf pac works directly in your shell, skip the PowerShell wrapper — it's only needed when Git Bash can't execute .cmd files.
Python SDK
After Python is confirmed available:
pip install --upgrade azure-identity requests PowerPlatform-Dataverse-Client pandasIf winget is unavailable
- PAC CLI:
dotnet tool install --global Microsoft.PowerApps.CLI.Tool - GitHub CLI: download from https://cli.github.com
- Azure CLI: download from https://aka.ms/installazurecliwindows
---
Authentication
Multi-environment note: Pro developers typically work across multiple environments (dev, test, staging, prod) and maintain one named PAC auth profile per environment. Before any environment operation, always runpac auth list+pac org whoto confirm which profile is active, and ask the user which environment they intend to target. Never assume the currently active profile is correct.
Identify your tenant ID first
If working in a non-production or separate tenant (different from your corporate AAD), you need that tenant's ID before authenticating. Options:
# If you already have PAC CLI authenticated to any environment:
pac org who
# Or: run this after az login (see below) and check the tenantId field
az account showThe tenant ID is a GUID like xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. Set it in .env as TENANT_ID before running any scripts.
---
PAC CLI
Recommended for non-prod / separate tenants: service principal auth (non-interactive)
pac auth create \
--name nonprod \
--applicationId <CLIENT_ID> \
--clientSecret <CLIENT_SECRET> \
--tenant <TENANT_ID>This requires a service principal in your dev tenant. Once created, record CLIENT_ID, CLIENT_SECRET, and TENANT_ID in .env. With service principal auth, no browser is ever needed.
Interactive user auth (corporate tenant or if no service principal yet)
pac auth create --name devThis opens a browser. If you use a non-corporate tenant, ensure you are logged out of your corporate Microsoft account in the browser before the prompt opens — otherwise the browser will auto-complete with corporate credentials. Use an InPrivate/Incognito window if needed.
Verify the correct auth is active:
pac auth list
pac org whoTo switch between profiles:
pac auth activate --name <profile-name>Name profiles to reflect the environment they target (e.g., dev, staging, prod, contoso-dev).
When starting any deployment task: run pac auth list and pac org who, show the output to the user, and confirm this is the environment they want to target before proceeding.
---
GitHub CLI
gh auth statusIf not authenticated:
gh auth loginIf you have multiple GitHub accounts (corporate + personal), verify the correct one is active:
gh api user --jq .login---
Azure CLI (needed for CI/CD setup only — skip until needed)
For a non-prod or separate tenant, always specify the tenant explicitly:
az login --tenant <TENANT_ID>
az account show --query '{tenant:tenantId, subscription:name}' -o tableConfirm the tenant ID in the output matches your dev tenant before proceeding.
---
PAC CLI PATH setup
If pac is not in PATH, check these common Windows install locations in order (fastest first):
# 1. winget install location (most common)
ls "/c/Users/$USER/AppData/Local/Microsoft/PowerAppsCLI/pac.exe" 2>/dev/null
# 2. dotnet tool install location
ls "/c/Users/$USER/.dotnet/tools/pac.exe" 2>/dev/null
# 3. nuget global packages (if installed via Microsoft.PowerApps.CLI nuget package)
ls /c/Users/$USER/.nuget/packages/microsoft.powerapps.cli/*/tools/pac.exe 2>/dev/nullDo NOT use find or recursive search — it's slow and unnecessary when the install locations are known.
Once found, add to ~/.bashrc (for Git Bash / Claude Code):
# Use the directory where pac.exe was found, e.g.:
echo 'export PATH="$PATH:/c/Users/$USER/AppData/Local/Microsoft/PowerAppsCLI"' >> ~/.bashrc
source ~/.bashrcRelated skills
FAQ
What does dv-connect check before rewriting setup?
Complete .env, registered MCP entry, matching auth profiles, and importable PowerPlatform Dataverse Client SDK.
When should I use dv-connect?
When connecting a workspace to Dataverse for the first time or repairing broken auth and MCP configuration.
Is dv-connect safe to install?
Review the Security Audits panel on this page before installing in production.