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.
- dataverse_mcp-0.1.0/LICENSE +21 -0
- dataverse_mcp-0.1.0/PKG-INFO +177 -0
- dataverse_mcp-0.1.0/README.md +149 -0
- dataverse_mcp-0.1.0/pyproject.toml +56 -0
- dataverse_mcp-0.1.0/setup.cfg +4 -0
- dataverse_mcp-0.1.0/src/dataverse_mcp/__init__.py +1 -0
- dataverse_mcp-0.1.0/src/dataverse_mcp/_app.py +22 -0
- dataverse_mcp-0.1.0/src/dataverse_mcp/client.py +101 -0
- dataverse_mcp-0.1.0/src/dataverse_mcp/models.py +252 -0
- dataverse_mcp-0.1.0/src/dataverse_mcp/server.py +27 -0
- dataverse_mcp-0.1.0/src/dataverse_mcp/tools/__init__.py +1 -0
- dataverse_mcp-0.1.0/src/dataverse_mcp/tools/metadata.py +147 -0
- dataverse_mcp-0.1.0/src/dataverse_mcp/tools/solutions.py +293 -0
- dataverse_mcp-0.1.0/src/dataverse_mcp/tools/tables.py +141 -0
- dataverse_mcp-0.1.0/src/dataverse_mcp.egg-info/PKG-INFO +177 -0
- dataverse_mcp-0.1.0/src/dataverse_mcp.egg-info/SOURCES.txt +18 -0
- dataverse_mcp-0.1.0/src/dataverse_mcp.egg-info/dependency_links.txt +1 -0
- dataverse_mcp-0.1.0/src/dataverse_mcp.egg-info/entry_points.txt +2 -0
- dataverse_mcp-0.1.0/src/dataverse_mcp.egg-info/requires.txt +4 -0
- dataverse_mcp-0.1.0/src/dataverse_mcp.egg-info/top_level.txt +1 -0
|
@@ -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 @@
|
|
|
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()
|