dataverse-mcp 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ryan James
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,177 @@
1
+ Metadata-Version: 2.4
2
+ Name: dataverse-mcp
3
+ Version: 0.1.0
4
+ Summary: A read-only MCP server for querying Microsoft Dataverse environments during development
5
+ Author: Ryan James
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/ryanmichaeljames/dataverse-mcp
8
+ Project-URL: Repository, https://github.com/ryanmichaeljames/dataverse-mcp
9
+ Project-URL: Issues, https://github.com/ryanmichaeljames/dataverse-mcp/issues
10
+ Project-URL: Changelog, https://github.com/ryanmichaeljames/dataverse-mcp/blob/main/CHANGELOG.md
11
+ Keywords: mcp,model-context-protocol,dataverse,power-platform,copilot,llm
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: azure-identity>=1.25.3
24
+ Requires-Dist: httpx<1.0,>=0.20.0
25
+ Requires-Dist: mcp[cli]>=1.27.0
26
+ Requires-Dist: powerplatform-dataverse-client>=0.1.0b7
27
+ Dynamic: license-file
28
+
29
+ # Dataverse MCP Server
30
+
31
+ An [MCP](https://modelcontextprotocol.io/) server for interacting with Microsoft Dataverse environments. Built with [FastMCP](https://github.com/modelcontextprotocol/python-sdk) and the official [PowerPlatform-Dataverse-Client](https://pypi.org/project/PowerPlatform-Dataverse-Client/) Python SDK.
32
+
33
+ ## Features
34
+
35
+ - **Solution inspection** — list solutions, get solution details, browse solution components
36
+ - **Table querying** — flexible OData-style queries against any Dataverse table
37
+ - **Schema exploration** — list tables, inspect table metadata (primary key, name attribute)
38
+ - **Agent-friendly** — rich tool descriptions designed for AI agent discoverability
39
+ - **Secure** — Pydantic v2 input validation, GUID format enforcement, OData injection prevention
40
+
41
+ ## Prerequisites
42
+
43
+ - [uv](https://docs.astral.sh/uv/) — install from [docs.astral.sh/uv](https://docs.astral.sh/uv/getting-started/installation/)
44
+ - Access to a Microsoft Dataverse environment
45
+ - Azure CLI (`az login`) or a registered app for authentication
46
+
47
+ ## Installation
48
+
49
+ No install required — run directly from PyPI using `uvx`:
50
+
51
+ ```bash
52
+ uvx dataverse-mcp
53
+ ```
54
+
55
+ `uvx` downloads and runs the package in an isolated environment. No cloning, no virtual env setup.
56
+
57
+ ## Configuration
58
+
59
+ Copy the example environment file and fill in your values:
60
+
61
+ ```bash
62
+ cp .env.example .env
63
+ ```
64
+
65
+ | Variable | Required | Default | Description |
66
+ |----------|----------|---------|-------------|
67
+ | `DATAVERSE_URL` | Yes | — | Your Dataverse org URL (e.g., `https://yourorg.crm.dynamics.com`) |
68
+ | `DATAVERSE_AUTH_TYPE` | No | `azure_cli` | Auth method: `interactive`, `client_secret`, or `azure_cli` |
69
+ | `AZURE_TENANT_ID` | For `client_secret` | — | Azure AD tenant ID |
70
+ | `AZURE_CLIENT_ID` | For `client_secret` | — | App registration client ID |
71
+ | `AZURE_CLIENT_SECRET` | For `client_secret` | — | App registration client secret |
72
+
73
+ ### Authentication Methods
74
+
75
+ - **`azure_cli`** (default) — Uses your existing `az login` session. Best for local development.
76
+ - **`interactive`** — Opens a browser window for interactive sign-in.
77
+ - **`client_secret`** — Uses a service principal. Requires `AZURE_TENANT_ID`, `AZURE_CLIENT_ID`, and `AZURE_CLIENT_SECRET`.
78
+
79
+ ## Usage
80
+
81
+ This server communicates over stdio and works with any MCP-compatible client.
82
+
83
+ ### VS Code
84
+
85
+ Add the server to your VS Code MCP configuration (`.vscode/mcp.json`):
86
+
87
+ ```json
88
+ {
89
+ "servers": {
90
+ "dataverse-mcp": {
91
+ "type": "stdio",
92
+ "command": "uvx",
93
+ "args": ["dataverse-mcp"],
94
+ "env": {
95
+ "DATAVERSE_URL": "https://yourorg.crm.dynamics.com",
96
+ "DATAVERSE_AUTH_TYPE": "azure_cli"
97
+ }
98
+ }
99
+ }
100
+ }
101
+ ```
102
+
103
+ To connect to multiple environments, add one entry per environment with a unique key:
104
+
105
+ ```json
106
+ {
107
+ "servers": {
108
+ "dataverse-mcp-dev": {
109
+ "type": "stdio",
110
+ "command": "uvx",
111
+ "args": ["dataverse-mcp"],
112
+ "env": {
113
+ "DATAVERSE_URL": "https://yourorg-dev.crm.dynamics.com",
114
+ "DATAVERSE_AUTH_TYPE": "azure_cli"
115
+ }
116
+ },
117
+ "dataverse-mcp-test": {
118
+ "type": "stdio",
119
+ "command": "uvx",
120
+ "args": ["dataverse-mcp"],
121
+ "env": {
122
+ "DATAVERSE_URL": "https://yourorg-test.crm.dynamics.com",
123
+ "DATAVERSE_AUTH_TYPE": "azure_cli"
124
+ }
125
+ }
126
+ }
127
+ }
128
+ ```
129
+
130
+ ## Tools
131
+
132
+ | Tool | Description |
133
+ |------|-------------|
134
+ | `dataverse_list_solutions` | List solutions with optional OData filter, select, and top |
135
+ | `dataverse_get_solution` | Get a single solution by unique name or GUID |
136
+ | `dataverse_list_solution_components` | List components in a solution with optional type filter |
137
+ | `dataverse_query_table` | Query records from any table with filter, select, orderby, expand, top |
138
+ | `dataverse_get_record` | Get a single record by table name and GUID |
139
+ | `dataverse_list_tables` | List available tables/entities with optional filter |
140
+ | `dataverse_get_table_metadata` | Get schema details for a specific table |
141
+
142
+ ## Project Structure
143
+
144
+ ```
145
+ src/dataverse_mcp/
146
+ ├── __init__.py # Package init
147
+ ├── _app.py # FastMCP instance (avoids circular imports)
148
+ ├── server.py # Entry point, logging setup, tool registration
149
+ ├── client.py # DataverseClient wrapper (auth, lifecycle)
150
+ ├── models.py # Pydantic v2 input models for all tools
151
+ └── tools/
152
+ ├── __init__.py # Tools package init
153
+ ├── solutions.py # Solution query tools
154
+ ├── tables.py # Table record query tools
155
+ └── metadata.py # Table/column metadata tools
156
+ ```
157
+
158
+ ## Development
159
+
160
+ ```bash
161
+ # Clone the repo
162
+ git clone https://github.com/ryanmichaeljames/dataverse-mcp.git
163
+ cd dataverse-mcp
164
+
165
+ # Install dependencies
166
+ uv sync
167
+
168
+ # Run the MCP inspector for testing
169
+ uv run mcp dev src/dataverse_mcp/server.py
170
+
171
+ # Compile check all modules
172
+ uv run python -m py_compile src/dataverse_mcp/server.py
173
+ ```
174
+
175
+ ## License
176
+
177
+ MIT
@@ -0,0 +1,149 @@
1
+ # Dataverse MCP Server
2
+
3
+ An [MCP](https://modelcontextprotocol.io/) server for interacting with Microsoft Dataverse environments. Built with [FastMCP](https://github.com/modelcontextprotocol/python-sdk) and the official [PowerPlatform-Dataverse-Client](https://pypi.org/project/PowerPlatform-Dataverse-Client/) Python SDK.
4
+
5
+ ## Features
6
+
7
+ - **Solution inspection** — list solutions, get solution details, browse solution components
8
+ - **Table querying** — flexible OData-style queries against any Dataverse table
9
+ - **Schema exploration** — list tables, inspect table metadata (primary key, name attribute)
10
+ - **Agent-friendly** — rich tool descriptions designed for AI agent discoverability
11
+ - **Secure** — Pydantic v2 input validation, GUID format enforcement, OData injection prevention
12
+
13
+ ## Prerequisites
14
+
15
+ - [uv](https://docs.astral.sh/uv/) — install from [docs.astral.sh/uv](https://docs.astral.sh/uv/getting-started/installation/)
16
+ - Access to a Microsoft Dataverse environment
17
+ - Azure CLI (`az login`) or a registered app for authentication
18
+
19
+ ## Installation
20
+
21
+ No install required — run directly from PyPI using `uvx`:
22
+
23
+ ```bash
24
+ uvx dataverse-mcp
25
+ ```
26
+
27
+ `uvx` downloads and runs the package in an isolated environment. No cloning, no virtual env setup.
28
+
29
+ ## Configuration
30
+
31
+ Copy the example environment file and fill in your values:
32
+
33
+ ```bash
34
+ cp .env.example .env
35
+ ```
36
+
37
+ | Variable | Required | Default | Description |
38
+ |----------|----------|---------|-------------|
39
+ | `DATAVERSE_URL` | Yes | — | Your Dataverse org URL (e.g., `https://yourorg.crm.dynamics.com`) |
40
+ | `DATAVERSE_AUTH_TYPE` | No | `azure_cli` | Auth method: `interactive`, `client_secret`, or `azure_cli` |
41
+ | `AZURE_TENANT_ID` | For `client_secret` | — | Azure AD tenant ID |
42
+ | `AZURE_CLIENT_ID` | For `client_secret` | — | App registration client ID |
43
+ | `AZURE_CLIENT_SECRET` | For `client_secret` | — | App registration client secret |
44
+
45
+ ### Authentication Methods
46
+
47
+ - **`azure_cli`** (default) — Uses your existing `az login` session. Best for local development.
48
+ - **`interactive`** — Opens a browser window for interactive sign-in.
49
+ - **`client_secret`** — Uses a service principal. Requires `AZURE_TENANT_ID`, `AZURE_CLIENT_ID`, and `AZURE_CLIENT_SECRET`.
50
+
51
+ ## Usage
52
+
53
+ This server communicates over stdio and works with any MCP-compatible client.
54
+
55
+ ### VS Code
56
+
57
+ Add the server to your VS Code MCP configuration (`.vscode/mcp.json`):
58
+
59
+ ```json
60
+ {
61
+ "servers": {
62
+ "dataverse-mcp": {
63
+ "type": "stdio",
64
+ "command": "uvx",
65
+ "args": ["dataverse-mcp"],
66
+ "env": {
67
+ "DATAVERSE_URL": "https://yourorg.crm.dynamics.com",
68
+ "DATAVERSE_AUTH_TYPE": "azure_cli"
69
+ }
70
+ }
71
+ }
72
+ }
73
+ ```
74
+
75
+ To connect to multiple environments, add one entry per environment with a unique key:
76
+
77
+ ```json
78
+ {
79
+ "servers": {
80
+ "dataverse-mcp-dev": {
81
+ "type": "stdio",
82
+ "command": "uvx",
83
+ "args": ["dataverse-mcp"],
84
+ "env": {
85
+ "DATAVERSE_URL": "https://yourorg-dev.crm.dynamics.com",
86
+ "DATAVERSE_AUTH_TYPE": "azure_cli"
87
+ }
88
+ },
89
+ "dataverse-mcp-test": {
90
+ "type": "stdio",
91
+ "command": "uvx",
92
+ "args": ["dataverse-mcp"],
93
+ "env": {
94
+ "DATAVERSE_URL": "https://yourorg-test.crm.dynamics.com",
95
+ "DATAVERSE_AUTH_TYPE": "azure_cli"
96
+ }
97
+ }
98
+ }
99
+ }
100
+ ```
101
+
102
+ ## Tools
103
+
104
+ | Tool | Description |
105
+ |------|-------------|
106
+ | `dataverse_list_solutions` | List solutions with optional OData filter, select, and top |
107
+ | `dataverse_get_solution` | Get a single solution by unique name or GUID |
108
+ | `dataverse_list_solution_components` | List components in a solution with optional type filter |
109
+ | `dataverse_query_table` | Query records from any table with filter, select, orderby, expand, top |
110
+ | `dataverse_get_record` | Get a single record by table name and GUID |
111
+ | `dataverse_list_tables` | List available tables/entities with optional filter |
112
+ | `dataverse_get_table_metadata` | Get schema details for a specific table |
113
+
114
+ ## Project Structure
115
+
116
+ ```
117
+ src/dataverse_mcp/
118
+ ├── __init__.py # Package init
119
+ ├── _app.py # FastMCP instance (avoids circular imports)
120
+ ├── server.py # Entry point, logging setup, tool registration
121
+ ├── client.py # DataverseClient wrapper (auth, lifecycle)
122
+ ├── models.py # Pydantic v2 input models for all tools
123
+ └── tools/
124
+ ├── __init__.py # Tools package init
125
+ ├── solutions.py # Solution query tools
126
+ ├── tables.py # Table record query tools
127
+ └── metadata.py # Table/column metadata tools
128
+ ```
129
+
130
+ ## Development
131
+
132
+ ```bash
133
+ # Clone the repo
134
+ git clone https://github.com/ryanmichaeljames/dataverse-mcp.git
135
+ cd dataverse-mcp
136
+
137
+ # Install dependencies
138
+ uv sync
139
+
140
+ # Run the MCP inspector for testing
141
+ uv run mcp dev src/dataverse_mcp/server.py
142
+
143
+ # Compile check all modules
144
+ uv run python -m py_compile src/dataverse_mcp/server.py
145
+ ```
146
+
147
+ ## License
148
+
149
+ MIT
@@ -0,0 +1,56 @@
1
+ [project]
2
+ name = "dataverse-mcp"
3
+ version = "0.1.0"
4
+ description = "A read-only MCP server for querying Microsoft Dataverse environments during development"
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ authors = [
8
+ {name = "Ryan James"},
9
+ ]
10
+ license = "MIT"
11
+ keywords = [
12
+ "mcp",
13
+ "model-context-protocol",
14
+ "dataverse",
15
+ "power-platform",
16
+ "copilot",
17
+ "llm",
18
+ ]
19
+ classifiers = [
20
+ "Development Status :: 5 - Production/Stable",
21
+ "Intended Audience :: Developers",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3.10",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Topic :: Software Development :: Libraries :: Python Modules",
27
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
28
+ ]
29
+ dependencies = [
30
+ "azure-identity>=1.25.3",
31
+ "httpx>=0.20.0,<1.0",
32
+ "mcp[cli]>=1.27.0",
33
+ "powerplatform-dataverse-client>=0.1.0b7",
34
+ ]
35
+
36
+ [project.urls]
37
+ Homepage = "https://github.com/ryanmichaeljames/dataverse-mcp"
38
+ Repository = "https://github.com/ryanmichaeljames/dataverse-mcp"
39
+ Issues = "https://github.com/ryanmichaeljames/dataverse-mcp/issues"
40
+ Changelog = "https://github.com/ryanmichaeljames/dataverse-mcp/blob/main/CHANGELOG.md"
41
+
42
+ [project.scripts]
43
+ dataverse-mcp = "dataverse_mcp.server:main"
44
+
45
+ [build-system]
46
+ requires = ["setuptools>=61.0"]
47
+ build-backend = "setuptools.build_meta"
48
+
49
+ [tool.setuptools.packages.find]
50
+ where = ["src"]
51
+
52
+ [dependency-groups]
53
+ dev = [
54
+ "build>=1.0",
55
+ "twine>=5.0",
56
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ """Dataverse MCP Server — read-only MCP tools for querying Microsoft Dataverse."""
@@ -0,0 +1,22 @@
1
+ """FastMCP application instance.
2
+
3
+ This module exists to avoid circular imports between server.py and tool
4
+ modules. Tool modules import ``mcp`` from here; server.py imports ``mcp``
5
+ from here and registers tool modules.
6
+ """
7
+
8
+ from mcp.server.fastmcp import FastMCP
9
+
10
+ from dataverse_mcp.client import dataverse_lifespan
11
+
12
+ mcp = FastMCP(
13
+ "dataverse_mcp",
14
+ instructions=(
15
+ "Dataverse MCP server for interacting with Microsoft Dataverse "
16
+ "environments. Use dataverse_list_solutions to discover solutions, "
17
+ "dataverse_query_table to search records, and "
18
+ "dataverse_list_tables / dataverse_get_table_metadata for schema "
19
+ "exploration."
20
+ ),
21
+ lifespan=dataverse_lifespan,
22
+ )
@@ -0,0 +1,101 @@
1
+ """DataverseClient wrapper with authentication factory and lifecycle management."""
2
+
3
+ import logging
4
+ import os
5
+ from collections.abc import AsyncIterator
6
+ from contextlib import asynccontextmanager
7
+ from dataclasses import dataclass
8
+
9
+ from azure.identity import (
10
+ AzureCliCredential,
11
+ ClientSecretCredential,
12
+ InteractiveBrowserCredential,
13
+ )
14
+ from PowerPlatform.Dataverse.client import DataverseClient
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+ SUPPORTED_AUTH_TYPES = ("interactive", "client_secret", "azure_cli")
19
+
20
+
21
+ def _build_credential(auth_type: str):
22
+ """Build an Azure TokenCredential based on the configured auth type.
23
+
24
+ Args:
25
+ auth_type: One of 'interactive', 'client_secret', or 'azure_cli'.
26
+
27
+ Returns:
28
+ A TokenCredential instance for authenticating with Dataverse.
29
+
30
+ Raises:
31
+ ValueError: If the auth type is not supported or required env vars are missing.
32
+ """
33
+ if auth_type == "interactive":
34
+ logger.info("Using InteractiveBrowserCredential for authentication")
35
+ return InteractiveBrowserCredential()
36
+
37
+ if auth_type == "client_secret":
38
+ tenant_id = os.environ.get("AZURE_TENANT_ID")
39
+ client_id = os.environ.get("AZURE_CLIENT_ID")
40
+ client_secret = os.environ.get("AZURE_CLIENT_SECRET")
41
+ if not all([tenant_id, client_id, client_secret]):
42
+ raise ValueError(
43
+ "client_secret auth requires AZURE_TENANT_ID, "
44
+ "AZURE_CLIENT_ID, and AZURE_CLIENT_SECRET environment variables"
45
+ )
46
+ logger.info("Using ClientSecretCredential for authentication")
47
+ return ClientSecretCredential(
48
+ tenant_id=tenant_id,
49
+ client_id=client_id,
50
+ client_secret=client_secret,
51
+ )
52
+
53
+ if auth_type == "azure_cli":
54
+ logger.info("Using AzureCliCredential for authentication")
55
+ return AzureCliCredential()
56
+
57
+ raise ValueError(
58
+ f"Unsupported DATAVERSE_AUTH_TYPE: '{auth_type}'. "
59
+ f"Supported values: {', '.join(SUPPORTED_AUTH_TYPES)}"
60
+ )
61
+
62
+
63
+ @dataclass
64
+ class AppContext:
65
+ """Application context holding the initialized DataverseClient."""
66
+
67
+ client: DataverseClient
68
+
69
+
70
+ @asynccontextmanager
71
+ async def dataverse_lifespan(server) -> AsyncIterator[AppContext]:
72
+ """FastMCP lifespan that initializes and cleans up the DataverseClient.
73
+
74
+ Reads configuration from environment variables:
75
+ - DATAVERSE_URL: The Dataverse organization URL (required)
76
+ - DATAVERSE_AUTH_TYPE: Authentication method (default: 'azure_cli')
77
+
78
+ Yields:
79
+ AppContext containing the initialized DataverseClient.
80
+ """
81
+ dataverse_url = os.environ.get("DATAVERSE_URL")
82
+ if not dataverse_url:
83
+ raise ValueError(
84
+ "DATAVERSE_URL environment variable is required "
85
+ "(e.g., 'https://yourorg.crm.dynamics.com')"
86
+ )
87
+
88
+ auth_type = os.environ.get("DATAVERSE_AUTH_TYPE", "azure_cli").lower().strip()
89
+ logger.info(
90
+ "Initializing DataverseClient for %s (auth: %s)", dataverse_url, auth_type
91
+ )
92
+
93
+ credential = _build_credential(auth_type)
94
+ client = DataverseClient(dataverse_url, credential)
95
+
96
+ try:
97
+ logger.info("DataverseClient initialized successfully")
98
+ yield AppContext(client=client)
99
+ finally:
100
+ logger.info("Shutting down DataverseClient")
101
+ client.close()