mcp-dataverse 0.3.7 → 0.3.9
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.
- package/CAPABILITIES.md +1039 -1039
- package/LICENSE +21 -21
- package/README.md +114 -113
- package/dist/auth-provider.factory-MSMLSOX3.js +1 -0
- package/dist/chunk-24RDOMG4.js +29 -0
- package/dist/chunk-OXKMMPM3.js +37 -0
- package/dist/chunk-PAX4NW5B.js +1 -0
- package/dist/chunk-SUDI4JM6.js +3 -0
- package/dist/config.loader-VTIKUDN7.js +1 -0
- package/dist/dataverse-client-advanced-T5ZJMRLK.js +1 -0
- package/dist/doctor.js +2 -102
- package/dist/http-server.js +3 -61
- package/dist/install.js +8 -233
- package/dist/server.js +43 -202
- package/dist/setup-auth.js +18 -41
- package/package.json +95 -94
- package/server.json +51 -50
- package/dist/auth/auth-provider.factory.d.ts +0 -4
- package/dist/auth/auth-provider.factory.d.ts.map +0 -1
- package/dist/auth/auth-provider.factory.js +0 -5
- package/dist/auth/auth-provider.factory.js.map +0 -1
- package/dist/auth/auth-provider.interface.d.ts +0 -21
- package/dist/auth/auth-provider.interface.d.ts.map +0 -1
- package/dist/auth/auth-provider.interface.js +0 -2
- package/dist/auth/auth-provider.interface.js.map +0 -1
- package/dist/auth/device-code-auth-provider.d.ts +0 -18
- package/dist/auth/device-code-auth-provider.d.ts.map +0 -1
- package/dist/auth/device-code-auth-provider.js +0 -167
- package/dist/auth/device-code-auth-provider.js.map +0 -1
- package/dist/config/config.loader.d.ts +0 -3
- package/dist/config/config.loader.d.ts.map +0 -1
- package/dist/config/config.loader.js +0 -39
- package/dist/config/config.loader.js.map +0 -1
- package/dist/config/config.schema.d.ts +0 -16
- package/dist/config/config.schema.d.ts.map +0 -1
- package/dist/config/config.schema.js +0 -20
- package/dist/config/config.schema.js.map +0 -1
- package/dist/dataverse/dataverse-client-advanced.d.ts +0 -53
- package/dist/dataverse/dataverse-client-advanced.d.ts.map +0 -1
- package/dist/dataverse/dataverse-client-advanced.js +0 -199
- package/dist/dataverse/dataverse-client-advanced.js.map +0 -1
- package/dist/dataverse/dataverse-client.actions.d.ts +0 -11
- package/dist/dataverse/dataverse-client.actions.d.ts.map +0 -1
- package/dist/dataverse/dataverse-client.actions.js +0 -25
- package/dist/dataverse/dataverse-client.actions.js.map +0 -1
- package/dist/dataverse/dataverse-client.batch.d.ts +0 -10
- package/dist/dataverse/dataverse-client.batch.d.ts.map +0 -1
- package/dist/dataverse/dataverse-client.batch.js +0 -74
- package/dist/dataverse/dataverse-client.batch.js.map +0 -1
- package/dist/dataverse/dataverse-client.d.ts +0 -46
- package/dist/dataverse/dataverse-client.d.ts.map +0 -1
- package/dist/dataverse/dataverse-client.js +0 -275
- package/dist/dataverse/dataverse-client.js.map +0 -1
- package/dist/dataverse/dataverse-client.metadata.d.ts +0 -41
- package/dist/dataverse/dataverse-client.metadata.d.ts.map +0 -1
- package/dist/dataverse/dataverse-client.metadata.js +0 -124
- package/dist/dataverse/dataverse-client.metadata.js.map +0 -1
- package/dist/dataverse/dataverse-client.utils.d.ts +0 -14
- package/dist/dataverse/dataverse-client.utils.d.ts.map +0 -1
- package/dist/dataverse/dataverse-client.utils.js +0 -65
- package/dist/dataverse/dataverse-client.utils.js.map +0 -1
- package/dist/dataverse/http-client.d.ts +0 -38
- package/dist/dataverse/http-client.d.ts.map +0 -1
- package/dist/dataverse/http-client.js +0 -111
- package/dist/dataverse/http-client.js.map +0 -1
- package/dist/dataverse/types.d.ts +0 -68
- package/dist/dataverse/types.d.ts.map +0 -1
- package/dist/dataverse/types.js +0 -2
- package/dist/dataverse/types.js.map +0 -1
- package/dist/doctor.d.ts +0 -7
- package/dist/doctor.d.ts.map +0 -1
- package/dist/doctor.js.map +0 -1
- package/dist/http-server.d.ts +0 -3
- package/dist/http-server.d.ts.map +0 -1
- package/dist/http-server.js.map +0 -1
- package/dist/install.d.ts +0 -3
- package/dist/install.d.ts.map +0 -1
- package/dist/install.js.map +0 -1
- package/dist/resources/resource-provider.d.ts +0 -11
- package/dist/resources/resource-provider.d.ts.map +0 -1
- package/dist/resources/resource-provider.js +0 -79
- package/dist/resources/resource-provider.js.map +0 -1
- package/dist/server.d.ts +0 -3
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/setup-auth.d.ts +0 -3
- package/dist/setup-auth.d.ts.map +0 -1
- package/dist/setup-auth.js.map +0 -1
- package/dist/tools/actions.tools.d.ts +0 -206
- package/dist/tools/actions.tools.d.ts.map +0 -1
- package/dist/tools/actions.tools.js +0 -256
- package/dist/tools/actions.tools.js.map +0 -1
- package/dist/tools/annotations.tools.d.ts +0 -94
- package/dist/tools/annotations.tools.d.ts.map +0 -1
- package/dist/tools/annotations.tools.js +0 -225
- package/dist/tools/annotations.tools.js.map +0 -1
- package/dist/tools/audit.tools.d.ts +0 -51
- package/dist/tools/audit.tools.d.ts.map +0 -1
- package/dist/tools/audit.tools.js +0 -170
- package/dist/tools/audit.tools.js.map +0 -1
- package/dist/tools/auth.tools.d.ts +0 -23
- package/dist/tools/auth.tools.d.ts.map +0 -1
- package/dist/tools/auth.tools.js +0 -36
- package/dist/tools/auth.tools.js.map +0 -1
- package/dist/tools/batch.tools.d.ts +0 -52
- package/dist/tools/batch.tools.d.ts.map +0 -1
- package/dist/tools/batch.tools.js +0 -89
- package/dist/tools/batch.tools.js.map +0 -1
- package/dist/tools/crud.tools.d.ts +0 -260
- package/dist/tools/crud.tools.d.ts.map +0 -1
- package/dist/tools/crud.tools.js +0 -290
- package/dist/tools/crud.tools.js.map +0 -1
- package/dist/tools/customization.tools.d.ts +0 -127
- package/dist/tools/customization.tools.d.ts.map +0 -1
- package/dist/tools/customization.tools.js +0 -285
- package/dist/tools/customization.tools.js.map +0 -1
- package/dist/tools/environment.tools.d.ts +0 -106
- package/dist/tools/environment.tools.d.ts.map +0 -1
- package/dist/tools/environment.tools.js +0 -274
- package/dist/tools/environment.tools.js.map +0 -1
- package/dist/tools/file.tools.d.ts +0 -73
- package/dist/tools/file.tools.d.ts.map +0 -1
- package/dist/tools/file.tools.js +0 -160
- package/dist/tools/file.tools.js.map +0 -1
- package/dist/tools/guardrails.d.ts +0 -22
- package/dist/tools/guardrails.d.ts.map +0 -1
- package/dist/tools/guardrails.js +0 -56
- package/dist/tools/guardrails.js.map +0 -1
- package/dist/tools/impersonate.tools.d.ts +0 -44
- package/dist/tools/impersonate.tools.d.ts.map +0 -1
- package/dist/tools/impersonate.tools.js +0 -87
- package/dist/tools/impersonate.tools.js.map +0 -1
- package/dist/tools/metadata.tools.d.ts +0 -279
- package/dist/tools/metadata.tools.d.ts.map +0 -1
- package/dist/tools/metadata.tools.js +0 -400
- package/dist/tools/metadata.tools.js.map +0 -1
- package/dist/tools/org.tools.d.ts +0 -32
- package/dist/tools/org.tools.d.ts.map +0 -1
- package/dist/tools/org.tools.js +0 -65
- package/dist/tools/org.tools.js.map +0 -1
- package/dist/tools/output.utils.d.ts +0 -63
- package/dist/tools/output.utils.d.ts.map +0 -1
- package/dist/tools/output.utils.js +0 -78
- package/dist/tools/output.utils.js.map +0 -1
- package/dist/tools/progress.d.ts +0 -15
- package/dist/tools/progress.d.ts.map +0 -1
- package/dist/tools/progress.js +0 -29
- package/dist/tools/progress.js.map +0 -1
- package/dist/tools/quality.tools.d.ts +0 -36
- package/dist/tools/quality.tools.d.ts.map +0 -1
- package/dist/tools/quality.tools.js +0 -97
- package/dist/tools/quality.tools.js.map +0 -1
- package/dist/tools/query.tools.d.ts +0 -151
- package/dist/tools/query.tools.d.ts.map +0 -1
- package/dist/tools/query.tools.js +0 -293
- package/dist/tools/query.tools.js.map +0 -1
- package/dist/tools/relations.tools.d.ts +0 -77
- package/dist/tools/relations.tools.d.ts.map +0 -1
- package/dist/tools/relations.tools.js +0 -96
- package/dist/tools/relations.tools.js.map +0 -1
- package/dist/tools/router.tools.d.ts +0 -5
- package/dist/tools/router.tools.d.ts.map +0 -1
- package/dist/tools/router.tools.js +0 -247
- package/dist/tools/router.tools.js.map +0 -1
- package/dist/tools/search.tools.d.ts +0 -74
- package/dist/tools/search.tools.d.ts.map +0 -1
- package/dist/tools/search.tools.js +0 -142
- package/dist/tools/search.tools.js.map +0 -1
- package/dist/tools/solution.tools.d.ts +0 -113
- package/dist/tools/solution.tools.d.ts.map +0 -1
- package/dist/tools/solution.tools.js +0 -176
- package/dist/tools/solution.tools.js.map +0 -1
- package/dist/tools/teams.tools.d.ts +0 -65
- package/dist/tools/teams.tools.d.ts.map +0 -1
- package/dist/tools/teams.tools.js +0 -127
- package/dist/tools/teams.tools.js.map +0 -1
- package/dist/tools/tool-registry.d.ts +0 -35
- package/dist/tools/tool-registry.d.ts.map +0 -1
- package/dist/tools/tool-registry.js +0 -31
- package/dist/tools/tool-registry.js.map +0 -1
- package/dist/tools/trace.tools.d.ts +0 -75
- package/dist/tools/trace.tools.d.ts.map +0 -1
- package/dist/tools/trace.tools.js +0 -233
- package/dist/tools/trace.tools.js.map +0 -1
- package/dist/tools/tracking.tools.d.ts +0 -41
- package/dist/tools/tracking.tools.d.ts.map +0 -1
- package/dist/tools/tracking.tools.js +0 -76
- package/dist/tools/tracking.tools.js.map +0 -1
- package/dist/tools/users.tools.d.ts +0 -141
- package/dist/tools/users.tools.d.ts.map +0 -1
- package/dist/tools/users.tools.js +0 -321
- package/dist/tools/users.tools.js.map +0 -1
- package/dist/tools/validation.utils.d.ts +0 -6
- package/dist/tools/validation.utils.d.ts.map +0 -1
- package/dist/tools/validation.utils.js +0 -14
- package/dist/tools/validation.utils.js.map +0 -1
- package/dist/tools/views.tools.d.ts +0 -36
- package/dist/tools/views.tools.d.ts.map +0 -1
- package/dist/tools/views.tools.js +0 -92
- package/dist/tools/views.tools.js.map +0 -1
- package/dist/tools/workflow.tools.d.ts +0 -111
- package/dist/tools/workflow.tools.d.ts.map +0 -1
- package/dist/tools/workflow.tools.js +0 -449
- package/dist/tools/workflow.tools.js.map +0 -1
- package/dist/transport.d.ts +0 -6
- package/dist/transport.d.ts.map +0 -1
- package/dist/transport.js +0 -21
- package/dist/transport.js.map +0 -1
package/dist/server.js
CHANGED
|
@@ -1,203 +1,44 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
import { createAuthProvider } from "./auth/auth-provider.factory.js";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
{ tools: relationTools, handler: handleRelationTool },
|
|
46
|
-
{ tools: actionTools, handler: handleActionTool },
|
|
47
|
-
{ tools: batchTools, handler: handleBatchTool },
|
|
48
|
-
{ tools: trackingTools, handler: handleTrackingTool },
|
|
49
|
-
{ tools: solutionTools, handler: handleSolutionTool },
|
|
50
|
-
{ tools: customizationTools, handler: handleCustomizationTool },
|
|
51
|
-
{ tools: environmentTools, handler: handleEnvironmentTool },
|
|
52
|
-
{ tools: traceTools, handler: handleTraceTool },
|
|
53
|
-
{ tools: searchTools, handler: handleSearchTool },
|
|
54
|
-
{ tools: auditTools, handler: handleAuditTool },
|
|
55
|
-
{ tools: qualityTools, handler: handleQualityTool },
|
|
56
|
-
{ tools: annotationTools, handler: handleAnnotationTool },
|
|
57
|
-
{ tools: userTools, handler: handleUserTool },
|
|
58
|
-
{ tools: viewTools, handler: handleViewTool },
|
|
59
|
-
{ tools: orgTools, handler: handleOrgTool },
|
|
60
|
-
{ tools: fileTools, handler: handleFileTool },
|
|
61
|
-
{ tools: teamTools, handler: handleTeamTool },
|
|
62
|
-
{ tools: workflowTools, handler: handleWorkflowTool },
|
|
63
|
-
{ tools: routerTools, handler: handleRouterTool },
|
|
64
|
-
]);
|
|
65
|
-
// Impersonate is registered separately — it has a special dispatch signature
|
|
66
|
-
const IMPERSONATE_NAMES = new Set(impersonateTools.map((t) => t.name));
|
|
67
|
-
// Read version from package.json so server.ts never drifts out of sync
|
|
68
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
69
|
-
const SERVER_VERSION = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8")).version;
|
|
70
|
-
// ── Global MCP Instructions ──────────────────────────────────────────────────
|
|
71
|
-
const SERVER_INSTRUCTIONS = `You are connected to a Microsoft Dataverse environment via the mcp-dataverse server.
|
|
72
|
-
|
|
73
|
-
## Recommended Workflow
|
|
74
|
-
1. Start with dataverse_whoami to verify authentication.
|
|
75
|
-
2. Use dataverse_list_tables to discover available tables (custom tables only by default).
|
|
76
|
-
3. Use dataverse_get_table_metadata to inspect column names, types, and required fields before querying or writing.
|
|
77
|
-
4. Query data with dataverse_query (OData) or dataverse_execute_fetchxml (complex joins/aggregations).
|
|
78
|
-
5. Use dataverse_create / dataverse_update / dataverse_delete for record operations.
|
|
79
|
-
|
|
80
|
-
## Best Practices
|
|
81
|
-
- Always specify $select to minimize payload size — never fetch all columns.
|
|
82
|
-
- Use $top to limit results (default 50, max 5000 per page).
|
|
83
|
-
- Prefer dataverse_query for simple reads; use dataverse_execute_fetchxml for aggregations, multi-table joins, or many-to-many traversal.
|
|
84
|
-
- Use dataverse_get_table_metadata before creating/updating records to confirm correct logical field names.
|
|
85
|
-
- For bulk operations (>5 records), use dataverse_batch_execute to reduce HTTP round-trips.
|
|
86
|
-
- Use dataverse_search for full-text search across multiple tables when you don't know which table contains the data.
|
|
87
|
-
- Check dataverse_get_relationships before building FetchXML joins or using dataverse_associate/dataverse_disassociate.
|
|
88
|
-
|
|
89
|
-
## Safety
|
|
90
|
-
- dataverse_delete is irreversible — always confirm with the user first.
|
|
91
|
-
- Use optimistic concurrency (etag parameter) on updates to prevent lost changes.
|
|
92
|
-
- Impersonation requires explicit privilege and is denied for System Administrator users.
|
|
93
|
-
|
|
94
|
-
## Performance
|
|
95
|
-
- Avoid retrieving more than 5000 records unless explicitly needed (use dataverse_retrieve_multiple_with_paging with maxTotal).
|
|
96
|
-
- Use server-side filtering ($filter / FetchXML conditions) instead of client-side filtering.
|
|
97
|
-
- Cache table metadata — it rarely changes during a session.
|
|
98
|
-
`;
|
|
99
|
-
/**
|
|
100
|
-
* Routes a tool call to its handler via the registry Map.
|
|
101
|
-
* Used directly by the request handler and passed as the dispatch
|
|
102
|
-
* function to handleImpersonateTool.
|
|
103
|
-
*/
|
|
104
|
-
function dispatchTool(name, args, client, progress) {
|
|
105
|
-
const handler = registry.getHandler(name);
|
|
106
|
-
if (!handler)
|
|
107
|
-
throw new Error(`Unknown tool: ${name}`);
|
|
108
|
-
return handler(name, args, client, progress);
|
|
109
|
-
}
|
|
110
|
-
async function main() {
|
|
111
|
-
const config = loadConfig();
|
|
112
|
-
const authProvider = createAuthProvider(config);
|
|
113
|
-
const client = new DataverseAdvancedClient(authProvider, config.maxRetries, config.requestTimeoutMs);
|
|
114
|
-
const server = new Server({ name: "mcp-dataverse", version: SERVER_VERSION }, {
|
|
115
|
-
capabilities: { tools: {}, resources: {} },
|
|
116
|
-
instructions: SERVER_INSTRUCTIONS,
|
|
117
|
-
});
|
|
118
|
-
// Combine registry definitions with impersonate tools
|
|
119
|
-
const allToolDefs = [...registry.getAllDefinitions(), ...impersonateTools];
|
|
120
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
121
|
-
tools: allToolDefs,
|
|
122
|
-
}));
|
|
123
|
-
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
124
|
-
const { name, arguments: args } = request.params;
|
|
125
|
-
const progressToken = request.params._meta?.progressToken;
|
|
126
|
-
const progress = new ProgressReporter(progressToken != null ? server : undefined, progressToken);
|
|
127
|
-
try {
|
|
128
|
-
if (IMPERSONATE_NAMES.has(name)) {
|
|
129
|
-
return handleImpersonateTool(name, args, client, dispatchTool);
|
|
130
|
-
}
|
|
131
|
-
return await dispatchTool(name, args, client, progress);
|
|
132
|
-
}
|
|
133
|
-
catch (error) {
|
|
134
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
135
|
-
return {
|
|
136
|
-
content: [{ type: "text", text: `Error: ${message}` }],
|
|
137
|
-
isError: true,
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
// ── Resource Handlers ────────────────────────────────────────────────────
|
|
142
|
-
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
143
|
-
resources: listResources(),
|
|
144
|
-
}));
|
|
145
|
-
server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
|
|
146
|
-
resourceTemplates: listResourceTemplates(),
|
|
147
|
-
}));
|
|
148
|
-
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
149
|
-
const contents = await readResource(request.params.uri, client, SERVER_INSTRUCTIONS);
|
|
150
|
-
return { contents: [contents] };
|
|
151
|
-
});
|
|
152
|
-
const transportArgs = parseTransportArgs();
|
|
153
|
-
if (transportArgs.transport === "http") {
|
|
154
|
-
const { startHttpTransport } = await import("./http-server.js");
|
|
155
|
-
const toolCount = allToolDefs.length;
|
|
156
|
-
process.stderr.write(`Starting HTTP transport on port ${transportArgs.port}...\n`);
|
|
157
|
-
// Trigger auth before accepting requests
|
|
158
|
-
try {
|
|
159
|
-
await authProvider.getToken();
|
|
160
|
-
process.stderr.write("[mcp-dataverse] Authenticated ✓\n");
|
|
161
|
-
}
|
|
162
|
-
catch (err) {
|
|
163
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
164
|
-
process.stderr.write(`[mcp-dataverse] Authentication failed: ${msg}\n`);
|
|
165
|
-
}
|
|
166
|
-
await startHttpTransport(server, transportArgs.port, SERVER_VERSION, toolCount);
|
|
167
|
-
}
|
|
168
|
-
else {
|
|
169
|
-
const transport = new StdioServerTransport();
|
|
170
|
-
await server.connect(transport);
|
|
171
|
-
// Do not log to stdout — stdio transport uses stdout for protocol messages
|
|
172
|
-
process.stderr.write("MCP Dataverse server started\n");
|
|
173
|
-
// Proactively trigger authentication at startup so the device-code prompt
|
|
174
|
-
// appears immediately in the VS Code MCP Output panel (View → Output → MCP),
|
|
175
|
-
// rather than only on the first tool call.
|
|
176
|
-
authProvider
|
|
177
|
-
.getToken()
|
|
178
|
-
.then(() => {
|
|
179
|
-
process.stderr.write("[mcp-dataverse] Authenticated ✓\n");
|
|
180
|
-
})
|
|
181
|
-
.catch((err) => {
|
|
182
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
183
|
-
process.stderr.write(`[mcp-dataverse] Authentication failed: ${msg}\n`);
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
async function entry() {
|
|
188
|
-
if (process.argv.includes("install")) {
|
|
189
|
-
const { runInstall } = await import("./install.js");
|
|
190
|
-
await runInstall();
|
|
191
|
-
process.exit(0);
|
|
192
|
-
}
|
|
193
|
-
if (process.argv.includes("doctor")) {
|
|
194
|
-
const { runDoctor } = await import("./doctor.js");
|
|
195
|
-
await runDoctor();
|
|
196
|
-
}
|
|
197
|
-
await main();
|
|
198
|
-
}
|
|
199
|
-
entry().catch((error) => {
|
|
200
|
-
process.stderr.write(`Fatal error: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
201
|
-
process.exit(1);
|
|
202
|
-
});
|
|
203
|
-
//# sourceMappingURL=server.js.map
|
|
2
|
+
import{a as le}from"./chunk-SUDI4JM6.js";import{a as de}from"./chunk-PAX4NW5B.js";import"./chunk-24RDOMG4.js";import{a as v,b as ce}from"./chunk-OXKMMPM3.js";import{Server as dn}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as cn}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as un,ListToolsRequestSchema as pn,ListResourcesRequestSchema as mn,ListResourceTemplatesRequestSchema as fn,ReadResourceRequestSchema as gn}from"@modelcontextprotocol/sdk/types.js";import{readFileSync as yn}from"fs";import{join as vn,dirname as hn}from"path";import{fileURLToPath as bn}from"url";function ue(){let a=process.argv.slice(2),r="stdio",i=3e3;for(let e=0;e<a.length;e++){if(a[e]==="--transport"&&a[e+1]){let t=a[e+1];(t==="http"||t==="stdio")&&(r=t),e++}if(a[e]==="--port"&&a[e+1]){let t=parseInt(a[e+1],10);!isNaN(t)&&t>0&&t<65536&&(i=t),e++}}return{transport:r,port:i}}var Y=class{map=new Map;register(r){for(let i of r.tools){if(this.map.has(i.name))throw new Error(`Duplicate tool name: ${i.name}`);this.map.set(i.name,{definition:i,handler:r.handler})}}getHandler(r){return this.map.get(r)?.handler}getAllDefinitions(){return Array.from(this.map.values()).map(r=>r.definition)}has(r){return this.map.has(r)}get size(){return this.map.size}};function pe(a){let r=new Y;for(let i of a)r.register(i);return r}var F=class{server;token;constructor(r,i){this.server=r??null,this.token=i??null}async report(r,i){if(!(!this.server||this.token==null))try{await this.server.notification({method:"notifications/progress",params:{progressToken:this.token,progress:r,total:i}})}catch{}}},Rn=new F;function Z(a){return{content:[{type:"text",text:JSON.stringify(a,null,2)}]}}function u(a,r,i,e){return Z({summary:a,data:r,suggestions:i,warnings:e})}function C(a,r,i){return Z({summary:`${r.length} ${a} found.`,data:r,suggestions:i})}function $(a){return Z({summary:`Blocked: ${a.cannotProceedBecause}`,data:null,prerequisite:a})}var J="@OData.Community.Display.V1.FormattedValue";function W(a){return a.map(r=>{if(typeof r!="object"||r===null)return r;let i=r,e={};for(let n of Object.keys(i))if(n.endsWith(J)){let o=n.slice(0,-J.length);e[o]=i[n]}if(Object.keys(e).length===0)return r;let t={};for(let n of Object.keys(i))n.endsWith(J)||(Object.prototype.hasOwnProperty.call(e,n)?t[n]={value:i[n],label:e[n]}:t[n]=i[n]);return t})}var me=[{name:"dataverse_whoami",description:"Returns the current authenticated user context from Dataverse WhoAmI: userId, businessUnitId, organizationId, organizationName, and environmentUrl. Use this to verify authentication is working, retrieve the current user context, or obtain IDs needed for subsequent operations. WHEN TO USE: Verifying authentication, getting current user/org context, or obtaining IDs for downstream operations. BEST PRACTICES: Call first to confirm connectivity before other tools. WORKFLOW: query_data.",inputSchema:{type:"object",properties:{},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}];async function fe(a,r,i){if(a==="dataverse_whoami"){let e=await i.whoAmI(),t={userId:e.UserId,businessUnitId:e.BusinessUnitId,organizationId:e.OrganizationId,organizationName:e.OrganizationName,environmentUrl:e.EnvironmentUrl};return u(`Authenticated as ${e.OrganizationName}`,t,["Use dataverse_list_tables to discover available tables","Use dataverse_get_table_metadata to inspect a table schema"])}throw new Error(`Unknown auth tool: ${a}`)}import{z as S}from"zod";var ee=[{name:"dataverse_list_tables",description:"Lists all Dataverse tables. By default returns ONLY custom (non-system) tables. Set includeSystemTables=true to include all ~1700+ system tables. WHEN TO USE: Discovering which tables exist in the environment. BEST PRACTICES: Start without includeSystemTables to focus on custom tables; use dataverse_get_table_metadata for column details. WORKFLOW: explore_schema.",inputSchema:{type:"object",properties:{includeSystemTables:{type:"boolean",description:"Include system tables. Default false = custom tables only."}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_get_table_metadata",description:"Returns full schema metadata for a Dataverse table: all attribute (column) logical names, display names, data types, required levels, and lookup target entities. Use this before writing queries or creating/updating records to confirm correct field names and types. Set includeAttributes=false if you only need table-level metadata without column details. WHEN TO USE: Before building queries or creating/updating records to validate field names, types, and required levels. BEST PRACTICES: Set includeAttributes=false for table-level info only; call once and reuse results. WORKFLOW: explore_schema.",inputSchema:{type:"object",properties:{logicalName:{type:"string",description:'The logical name of the table (e.g., "account", "contact", "new_mytable")'},includeAttributes:{type:"boolean",description:"Include attribute (column) definitions. Default: true"}},required:["logicalName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_get_relationships",description:"Returns all relationship definitions (1:N, N:1, N:N) for a Dataverse table, including relationship schema names, referenced/referencing entity names, and lookup attribute names. Use to determine the correct relationshipName for dataverse_associate/dataverse_disassociate, or to map lookup fields before building FetchXML joins. Use relationshipType to filter results. WHEN TO USE: Finding relationship schema names for associate/disassociate, or mapping lookups for FetchXML joins. BEST PRACTICES: Filter by relationshipType to reduce output size. WORKFLOW: explore_schema.",inputSchema:{type:"object",properties:{logicalName:{type:"string",description:"The logical name of the table"},relationshipType:{type:"string",enum:["OneToMany","ManyToOne","ManyToMany","All"],description:"Filter by relationship type. Default: All"}},required:["logicalName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_list_global_option_sets",description:"Lists all global (shared) option sets defined in the Dataverse environment, returning their names and metadata IDs. Use this to discover available option sets before calling dataverse_get_option_set to retrieve their values. Prefer this over dataverse_get_table_metadata when you need to find option sets that are reused across multiple tables. WHEN TO USE: Discovering shared option sets reused across multiple tables. BEST PRACTICES: Follow up with dataverse_get_option_set to retrieve individual option values. WORKFLOW: explore_schema.",inputSchema:{type:"object",properties:{},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_get_option_set",description:"Returns all option labels and their integer values for a named global option set. Use this to look up the numeric code for a picklist value before filtering records (e.g., statecode or statuscode equivalents), or to populate dropdowns with correct option values. WHEN TO USE: Looking up numeric codes for picklist filtering or record creation. BEST PRACTICES: Use the integer values in $filter expressions, not the label text. WORKFLOW: explore_schema.",inputSchema:{type:"object",properties:{name:{type:"string",description:"The name of the global option set"}},required:["name"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_get_entity_key",description:"Returns all alternate key definitions for a Dataverse table. Useful before using dataverse_upsert to know which fields serve as alternate keys, their index status (Active/InProgress/Failed), and whether they are customizable. WHEN TO USE: Before using dataverse_upsert to verify which fields serve as alternate keys. BEST PRACTICES: Check that key index status is Active before relying on a key. WORKFLOW: explore_schema.",inputSchema:{type:"object",properties:{tableName:{type:"string",description:'Logical name of the table (e.g., "account", "contact")'}},required:["tableName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_get_attribute_option_set",description:"Returns all option labels and integer values for a table-specific attribute (Picklist, Status, or State field). Use to look up the numeric codes for a column's choices before filtering or updating records. WHEN TO USE: Looking up option values for a specific table's picklist, status, or state column. BEST PRACTICES: Use the integer values in $filter and when setting fields in create/update. WORKFLOW: explore_schema.",inputSchema:{type:"object",properties:{entityLogicalName:{type:"string",description:'Logical name of the table (e.g., "account", "contact")'},attributeLogicalName:{type:"string",description:'Logical name of the attribute (e.g., "statuscode", "industrycode")'}},required:["entityLogicalName","attributeLogicalName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],Ot=S.object({logicalName:S.string().min(1),includeAttributes:S.boolean().optional().default(!0)}),It=S.object({logicalName:S.string().min(1),relationshipType:S.enum(["OneToMany","ManyToOne","ManyToMany","All"]).optional()}),Ct=S.object({includeSystemTables:S.boolean().default(!1).optional().describe("Include system tables. Default false = custom tables only.")}),Ut=S.object({name:S.string().min(1)}),Ht=S.object({tableName:S.string().min(1).describe('Logical name of the table (e.g., "account", "contact")')}),ge=/^[a-z_][a-z0-9_]*$/,qt=S.object({entityLogicalName:S.string().min(1).regex(ge,"Invalid Dataverse logical name"),attributeLogicalName:S.string().min(1).regex(ge,"Invalid Dataverse logical name")});async function ye(a,r,i){switch(a){case"dataverse_list_tables":{let{includeSystemTables:e=!1}=Ct.parse(r??{}),t=!e,n=await i.listTables(t),o=Array.isArray(n)?n:[];return C(`tables (${t?"custom only":"including system"})`,o,["Use dataverse_get_table_metadata to inspect a specific table's columns and types"])}case"dataverse_get_table_metadata":{let{logicalName:e,includeAttributes:t}=Ot.parse(r),n=await i.getTableMetadata(e,t),o=Array.isArray(n?.Attributes)?n.Attributes.length:0;return u(`Metadata for ${e}: ${o} attributes`,n,["Use dataverse_query to read records from this table","Use dataverse_get_relationships to see related tables"])}case"dataverse_get_relationships":{let{logicalName:e,relationshipType:t}=It.parse(r),n=await i.getRelationships(e),o=e.toLowerCase(),s=n.filter(p=>p.RelationshipType==="OneToManyRelationship"&&p.ReferencedEntity?.toLowerCase()===o),l=n.filter(p=>p.RelationshipType==="OneToManyRelationship"&&p.ReferencingEntity?.toLowerCase()===o),c=n.filter(p=>p.RelationshipType==="ManyToManyRelationship"),d=!t||t==="All",m=[...d||t==="OneToMany"?s:[],...d||t==="ManyToOne"?l:[],...d||t==="ManyToMany"?c:[]];return u(`${m.length} relationships found for ${e}`,{tableName:e,oneToMany:d||t==="OneToMany"?s:void 0,manyToOne:d||t==="ManyToOne"?l:void 0,manyToMany:d||t==="ManyToMany"?c:void 0},["Use dataverse_associate or dataverse_disassociate to manage N:N relationships"])}case"dataverse_list_global_option_sets":{let e=await i.listGlobalOptionSets(),t=Array.isArray(e)?e:[];return C("global option sets",t,["Use dataverse_get_option_set with a name to see the values"])}case"dataverse_get_option_set":{let{name:e}=Ut.parse(r),t=await i.getOptionSet(e),n=Array.isArray(t?.Options)?t.Options:[];return u(`Option set '${e}': ${n.length} options`,t,["Use these values in $filter expressions or when creating/updating records"])}case"dataverse_get_entity_key":{let{tableName:e}=Ht.parse(r),t=await i.getEntityKeys(e);return u(`${t.length} alternate keys for ${e}`,{tableName:e,keys:t,count:t.length},["Use dataverse_upsert with these keys for create-or-update operations"])}case"dataverse_get_attribute_option_set":{let{entityLogicalName:e,attributeLogicalName:t}=qt.parse(r),n=await i.getAttributeOptionSet(e,t),o=Array.isArray(n?.options)?n.options:[];return u(`Attribute '${t}' on '${e}': ${o.length} options`,n,["Use these integer values in $filter or when creating/updating records"])}default:throw new Error(`Unknown metadata-read tool: ${a}`)}}import{z as P}from"zod";var Lt=new Set(["dataverse_delete","dataverse_delete_file","dataverse_update_entity","dataverse_delete_attribute"]);function te(a){let r=[];return a.top!==void 0&&a.top>5e3&&r.push({code:"LARGE_RESULT_SET",message:`Large result set requested (top=${a.top}). Consider reducing $top or using paging.`,severity:"warning"}),(!a.select||a.select.length===0)&&r.push({code:"NO_SELECT",message:"No $select specified \u2014 all columns will be returned. Specify columns to reduce payload size.",severity:"warning"}),a.filter||r.push({code:"NO_FILTER",message:"No filter \u2014 consider adding one to reduce results and improve performance.",severity:"info"}),r}function U(a){let r=[];return Lt.has(a.toolName)&&r.push({code:"DESTRUCTIVE_OP",message:"Destructive operation \u2014 confirm with user before proceeding. This action cannot be undone.",severity:"warning"}),a.toolName==="dataverse_batch_execute"&&r.push({code:"BATCH_OP",message:"Batch operations may modify multiple records in a single transaction.",severity:"info"}),r}var ve=[{name:"dataverse_update_entity",description:"Updates configuration flags on an existing Dataverse entity definition \u2014 enables or disables Notes (HasNotes), Change Tracking, and Audit. Requires System Customizer or System Administrator privileges. WHEN TO USE: Enabling notes/attachments support, change tracking, or audit on a table. BEST PRACTICES: Set autoPublish=true (default) to make changes visible immediately. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{entityLogicalName:{type:"string",description:"Logical name of the entity to update (e.g. 'account', 'new_mytable')"},hasNotes:{type:"boolean",description:"Enable or disable the Notes/Attachments feature for this entity"},changeTrackingEnabled:{type:"boolean",description:"Enable or disable change tracking (required for delta sync)"},isAuditEnabled:{type:"boolean",description:"Enable or disable auditing on this entity"},autoPublish:{type:"boolean",description:"Publish the customization after update (default: true). Set false to defer publishing."},confirm:{type:"boolean",description:"Must be true \u2014 confirms intentional schema modification to Dataverse entity metadata"}},required:["entityLogicalName","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],Pt=P.object({entityLogicalName:P.string().min(1).regex(/^[a-z_][a-z0-9_]*$/,"Invalid logical name"),confirm:P.literal(!0,{errorMap:()=>({message:"Set confirm: true to modify entity metadata"})}),hasNotes:P.boolean().optional(),changeTrackingEnabled:P.boolean().optional(),isAuditEnabled:P.boolean().optional(),autoPublish:P.boolean().optional().default(!0)}).refine(a=>a.hasNotes!==void 0||a.changeTrackingEnabled!==void 0||a.isAuditEnabled!==void 0,{message:"At least one of hasNotes, changeTrackingEnabled, or isAuditEnabled must be provided"});async function he(a,r,i){switch(a){case"dataverse_update_entity":{let{entityLogicalName:e,hasNotes:t,changeTrackingEnabled:n,isAuditEnabled:o,autoPublish:s}=Pt.parse(r),l=U({toolName:"dataverse_update_entity",entitySetName:e}).map(g=>`[${g.severity.toUpperCase()}] ${g.code}: ${g.message}`),c={};t!==void 0&&(c.HasNotes=t),n!==void 0&&(c.ChangeTrackingEnabled=n),o!==void 0&&(c.IsAuditEnabled=o);let d={"@odata.type":"#Microsoft.Dynamics.CRM.EntityMetadata"};t!==void 0&&(d.HasNotes=t),n!==void 0&&(d.ChangeTrackingEnabled=n),o!==void 0&&(d.IsAuditEnabled={Value:o});try{await i.updateEntityDefinition(e,d)}catch(g){if((g instanceof Error?g.message:String(g)).includes("0x80060888"))return u(`Cannot update entity '${e}': operation not supported by Dataverse.`,{error:"0x80060888",entityLogicalName:e,requestedChanges:c},["IsAuditEnabled requires organization-level auditing to be enabled first: Power Platform admin center > Settings > Audit and logs > Start Auditing","ChangeTrackingEnabled may be blocked if the entity is part of a managed solution","HasNotes=true/false should work for custom (unmanaged) entities"]);throw g}let m=Object.entries(c).map(([g,I])=>`${g}=${String(I)}`).join(", "),p="autoPublish=false (skipped)";return s&&(await i.publishCustomizations({entities:[e]}),p="published successfully"),u(`Entity '${e}' updated: ${m}. ${p}.`,{entityLogicalName:e,changes:c,published:s,...l.length>0&&{warnings:l}},["Use dataverse_get_table_metadata to verify the changes","Enable HasNotes=true before using dataverse_create_annotation"])}default:throw new Error(`Unknown metadata tool: ${a}`)}}var be=[...ee,...ve];async function _e(a,r,i){return new Set(ee.map(t=>t.name)).has(a)?ye(a,r,i):he(a,r,i)}import{z as _}from"zod";import{z as we}from"zod";var Te=/^[a-zA-Z_][a-zA-Z0-9_]*$/,b=we.string().min(1).regex(Te,"entitySetName must contain only letters, digits, or underscores"),ae=we.string().min(1).regex(Te,"relationshipName must contain only letters, digits, or underscores");var $t={opportunity:"opportunities",territory:"territories",category:"categories",activityparty:"activityparties",activitymimeattachment:"activitymimeattachments",queue:"queues",queueitem:"queueitems",dependency:"dependencies",salesliteratureitem:"salesliteratureitems",contractdetail:"contractdetails",discounttype:"discounttypes",entitlementtemplate:"entitlementtemplates",pricelevel:"pricelevels"},Se=[{name:"dataverse_query",description:"Queries a Dataverse table using OData ($filter, $select, $orderby, $top, $expand, $count). Use for simple to moderate reads on a single table or with shallow $expand for related fields. Always specify $select to minimize payload. For complex aggregations (count, sum, avg), multi-entity joins, many-to-many traversal, or advanced FetchXML-only operators, use dataverse_execute_fetchxml instead. WHEN TO USE: Single-table reads, simple filters, shallow expands, or server-side aggregation via $apply. BEST PRACTICES: Always pass $select; cap with $top; use $apply for server-side counts/grouping. WORKFLOW: query_data.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:'The OData entity set name (e.g., "accounts", "contacts", "new_mytables")'},select:{type:"array",items:{type:"string"},description:"Columns to return. Always specify to minimize payload."},filter:{type:"string",description:'OData $filter expression (e.g., "statecode eq 0 and new_amount gt 1000")'},orderby:{type:"string",description:'OData $orderby expression (e.g., "createdon desc")'},top:{type:"number",description:"Maximum number of records to return (default: 50)"},expand:{type:"string",description:'OData $expand for related entities (e.g., "parentaccountid($select=name)")'},count:{type:"boolean",description:"Include total record count in response"},apply:{type:"string",description:'OData $apply for server-side aggregation (e.g., "groupby((statuscode),aggregate($count as count))")'},formattedValues:{type:"boolean",description:"When true, includes human-readable labels for picklist fields alongside raw integer codes (e.g., { value: 1, label: 'Active' }). Uses OData formatted-value annotations."}},required:["entitySetName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_execute_fetchxml",description:'Executes a FetchXML query against Dataverse \u2014 use for complex scenarios requiring aggregations (count, sum, avg, min, max with grouping), linked-entity joins across multiple tables, many-to-many relationship traversal, or advanced filtering not expressible in OData. Returns a typed array of records. entitySetName is optional \u2014 if omitted it is extracted from the <entity name="..."> element in the FetchXML. WHEN TO USE: Multi-table joins, aggregations with groupby, N:N traversal, or filtering not supported by OData. BEST PRACTICES: Add page/count attributes for large result sets; prefer dataverse_query for simple reads. WORKFLOW: query_data.',inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:'OData entity set name of the root entity (e.g., "accounts"). If omitted, extracted from the <entity name="..."> element in the FetchXML.'},fetchXml:{type:"string",description:"The complete FetchXML query string"},formattedValues:{type:"boolean",description:"When true, includes human-readable labels for picklist fields alongside raw integer codes."}},required:["fetchXml"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_retrieve_multiple_with_paging",description:"Retrieves ALL records matching a query by automatically following OData nextLink pages. Use instead of dataverse_query when you need more than 5000 records or all records in a table. Returns totalRetrieved count. Set maxTotal to cap retrieval (default 5000, max 50000) to avoid overwhelming the context. WHEN TO USE: You need all matching records beyond the 5000-row OData page limit or a full table export. BEST PRACTICES: Always set $select; use maxTotal to cap results and avoid context overflow. WORKFLOW: query_data.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:'OData entity set name (e.g., "accounts")'},select:{type:"array",items:{type:"string"}},filter:{type:"string"},orderby:{type:"string"},expand:{type:"string"},maxTotal:{type:"number",description:"Maximum records to retrieve (default: 5000, max: 50000)"},formattedValues:{type:"boolean",description:"Include formatted label annotations."}},required:["entitySetName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],Wt=_.object({entitySetName:b,select:_.array(_.string()).optional(),filter:_.string().optional(),orderby:_.string().optional(),top:_.number().positive().max(5e3).optional().default(50),expand:_.string().optional(),count:_.boolean().optional(),apply:_.string().optional(),formattedValues:_.boolean().optional()}),jt=_.object({fetchXml:_.string().min(1).describe("Complete FetchXML query string"),entitySetName:_.string().optional().describe('OData entity set name (e.g., "accounts"). If omitted, extracted from the <entity name="..."> element in the FetchXML.'),formattedValues:_.boolean().optional()}),Mt=_.object({entitySetName:b,select:_.array(_.string()).optional(),filter:_.string().optional(),orderby:_.string().optional(),expand:_.string().optional(),maxTotal:_.number().positive().max(5e4).optional(),formattedValues:_.boolean().optional()});async function Ne(a,r,i,e){switch(a){case"dataverse_query":{let t=Wt.parse(r),n={};t.select!==void 0&&(n.select=t.select),t.filter!==void 0&&(n.filter=t.filter),t.orderby!==void 0&&(n.orderby=t.orderby),t.top!==void 0&&(n.top=t.top),t.expand!==void 0&&(n.expand=t.expand),t.count!==void 0&&(n.count=t.count),t.apply!==void 0&&(n.apply=t.apply),t.formattedValues!==void 0&&(n.formattedValues=t.formattedValues);let o=await i.query(t.entitySetName,n),s=Array.isArray(o?.value)?o.value:[],l=o["@odata.count"],c=l!==void 0?l===s.length?" (showing all results)":` (total in dataset: ${l})`:"",d=t.formattedValues?W(s):s,m=t.formattedValues&&Array.isArray(o?.value)?{...o,value:d}:o,p=te({...t.top!==void 0?{top:t.top}:{},...t.select!==void 0?{select:t.select}:{},...t.filter!==void 0?{filter:t.filter}:{},entitySetName:t.entitySetName}).map(g=>`[${g.severity.toUpperCase()}] ${g.code}: ${g.message}`);return u(`${s.length} records returned from ${t.entitySetName}${c}`,m,["Use dataverse_execute_fetchxml for complex joins or aggregations","Add $select to minimize payload"],p.length>0?p:void 0)}case"dataverse_execute_fetchxml":{let t=jt.parse(r),n=t.entitySetName,{fetchXml:o}=t;if(!n){let d=o.match(/<entity\s+name=["']([^"']+)["']/i);if(!d)return{content:[{type:"text",text:JSON.stringify({isError:!0,error:"entitySetName is required when not present in FetchXML <entity> element"})}]};let m=d[1];n=$t[m]??m+"s"}let s=await i.executeFetchXml(n,o,t.formattedValues),l=Array.isArray(s)?s:Array.isArray(s?.value)?s.value:[],c=t.formattedValues?W(l):l;return u(`${c.length} records returned via FetchXML`,t.formattedValues&&Array.isArray(s?.value)?{...s,value:c}:s,["Use dataverse_query for simple OData reads","Add page/count attributes for large result sets"])}case"dataverse_retrieve_multiple_with_paging":{let t=Mt.parse(r),n={};t.select!==void 0&&(n.select=t.select),t.filter!==void 0&&(n.filter=t.filter),t.orderby!==void 0&&(n.orderby=t.orderby),t.expand!==void 0&&(n.expand=t.expand),t.maxTotal!==void 0&&(n.maxTotal=t.maxTotal),await e?.report(0,1);let o=await i.queryWithPaging(t.entitySetName,n),s=o?.totalRetrieved??(Array.isArray(o?.value)?o.value.length:0),l=o?.pageCount??1;await e?.report(1,1);let c=Array.isArray(o?.records)?o.records:[],d=t.formattedValues&&c.length>0?{...o,records:W(c)}:o,m=te({...t.select!==void 0?{select:t.select}:{},...t.filter!==void 0?{filter:t.filter}:{},entitySetName:t.entitySetName}).map(p=>`[${p.severity.toUpperCase()}] ${p.code}: ${p.message}`);return u(`${s} records retrieved across ${l} pages from ${t.entitySetName}`,d,["Set maxTotal to limit retrieval","Use $select to minimize payload size"],m.length>0?m:void 0)}default:throw new Error(`Unknown query tool: ${a}`)}}import{z as y}from"zod";var xe=[{name:"dataverse_get",description:"Retrieves a single Dataverse record by its GUID. Use when you already know the exact record ID and want specific fields \u2014 faster and more precise than dataverse_query with a GUID filter. Specify select to limit returned columns and reduce payload size. WHEN TO USE: You have the exact record GUID and want specific fields. BEST PRACTICES: Always specify select; use the returned etag for subsequent optimistic-concurrency updates. WORKFLOW: query_data.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:"OData entity set name"},id:{type:"string",description:"Record GUID"},select:{type:"array",items:{type:"string"},description:"Columns to return"},expand:{type:"string",description:'OData $expand for related entities (e.g., "parentaccountid($select=name)")'},formattedValues:{type:"boolean",description:"When true, annotates picklist fields with human-readable labels. Note: dataverse_get always requests all OData annotations."}},required:["entitySetName","id"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_create",description:`Creates a new record in a Dataverse table and returns the new record's GUID. Use dataverse_get_table_metadata first to confirm correct logical field names and required fields. For setting lookup fields, use the format "_fieldname_value" with the related record GUID. For bulk creation of multiple records, consider dataverse_batch_execute to reduce HTTP round-trips. WHEN TO USE: Creating a single new record in a known table. BEST PRACTICES: Validate field names via dataverse_get_table_metadata first; use dataverse_batch_execute for bulk inserts. WORKFLOW: create_record.`,inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:"OData entity set name"},data:{type:"object",description:"Record data as key-value pairs using logical names"}},required:["entitySetName","data"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}},{name:"dataverse_update",description:"Updates an existing Dataverse record using PATCH semantics \u2014 only the fields provided in data are changed, all other fields remain unchanged. Requires the record GUID. Use dataverse_upsert instead if you want to create-or-update using an alternate key without knowing the GUID upfront. WHEN TO USE: Modifying specific fields on an existing record with a known GUID. BEST PRACTICES: Pass etag for optimistic concurrency; include only changed fields in data. WORKFLOW: update_record.",inputSchema:{type:"object",properties:{entitySetName:{type:"string"},id:{type:"string",description:"Record GUID"},data:{type:"object",description:"Fields to update"},etag:{type:"string",description:"ETag value from a prior dataverse_get response. When provided, the update only succeeds if the record has not been modified since (optimistic concurrency). Prevents lost updates."}},required:["entitySetName","id","data"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_delete",description:"Permanently deletes a Dataverse record by its GUID. This operation is irreversible \u2014 you MUST set confirm=true to proceed. Use dataverse_list_dependencies to check if the record is referenced by workflows, plugins, or other components before deleting shared or configuration records. WHEN TO USE: Permanently removing a record you are certain should be deleted. BEST PRACTICES: Call dataverse_list_dependencies first for shared records; always set confirm=true. WORKFLOW: delete_record.",inputSchema:{type:"object",properties:{entitySetName:{type:"string"},id:{type:"string",description:"Record GUID"},confirm:{type:"boolean",description:"Must be explicitly true to proceed with deletion"}},required:["entitySetName","id","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_upsert",description:'Creates or updates a Dataverse record using an alternate key (no GUID needed). Returns operation="created" or "updated". Use mode="createOnly" to fail with an error if the record already exists, or mode="updateOnly" to fail if the record does not exist. Default mode="upsert" creates or updates. Supports compound alternate keys via the alternateKeys parameter. WHEN TO USE: Create-or-update when you have an alternate key but no GUID. BEST PRACTICES: Verify alternate keys with dataverse_get_entity_key first; use mode to control behavior. WORKFLOW: create_record.',inputSchema:{type:"object",properties:{entitySetName:{type:"string"},alternateKey:{type:"string",description:"Alternate key attribute name (for single key)"},alternateKeyValue:{type:"string",description:"Alternate key value (for single key)"},alternateKeys:{type:"object",description:'Compound alternate key as key-value map (e.g., {"key1":"val1","key2":"val2"}). Use instead of alternateKey/alternateKeyValue for multi-field keys.'},data:{type:"object",description:"Record data"},mode:{type:"string",enum:["upsert","createOnly","updateOnly"],description:"upsert=create or update (default), createOnly=fail if exists, updateOnly=fail if not found"}},required:["entitySetName","data"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_assign",description:"Assigns a Dataverse record to a different user or team owner. Sets the ownerid lookup field using the OData bind syntax. WHEN TO USE: Changing the owner of a record to a different user or team. BEST PRACTICES: Use dataverse_list_users or dataverse_list_teams to find valid owner GUIDs first. WORKFLOW: update_record.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:"OData entity set name of the record to assign"},id:{type:"string",description:"Record GUID"},ownerType:{type:"string",enum:["systemuser","team"],description:'Type of the new owner: "systemuser" for a user, "team" for a team'},ownerId:{type:"string",description:"GUID of the user or team to assign the record to"}},required:["entitySetName","id","ownerType","ownerId"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],Ft=y.object({entitySetName:b,id:y.string().uuid(),select:y.array(y.string()).optional(),expand:y.string().optional(),formattedValues:y.boolean().optional()}),Bt=y.object({entitySetName:b,data:y.record(y.unknown())}),Gt=y.object({entitySetName:b,id:y.string().uuid(),data:y.record(y.unknown()),etag:y.string().optional()}),zt=y.object({entitySetName:b,id:y.string().uuid(),confirm:y.boolean()}),Vt=y.object({entitySetName:b,alternateKey:y.string().min(1).optional(),alternateKeyValue:y.string().min(1).optional(),alternateKeys:y.record(y.string()).optional(),data:y.record(y.unknown()),mode:y.enum(["upsert","createOnly","updateOnly"]).default("upsert").optional().describe("upsert=create or update (default), createOnly=fail if exists, updateOnly=fail if not found")}).refine(a=>a.alternateKey&&a.alternateKeyValue||a.alternateKeys&&Object.keys(a.alternateKeys).length>0,{message:"Provide either (alternateKey + alternateKeyValue) or alternateKeys"}),Kt=y.object({entitySetName:b,id:y.string().uuid(),ownerType:y.enum(["systemuser","team"]),ownerId:y.string().uuid()});async function Re(a,r,i){switch(a){case"dataverse_get":{let{entitySetName:e,id:t,select:n,expand:o,formattedValues:s}=Ft.parse(r),{record:l,etag:c}=await i.getRecord(e,t,n,o),d=s?W([l])[0]??l:l;return u(`Retrieved record ${t} from ${e}`,{id:t,record:d,etag:c},["Use dataverse_update to modify this record","Use dataverse_get_relationships to explore related records"])}case"dataverse_create":{let{entitySetName:e,data:t}=Bt.parse(r),n=await i.createRecord(e,t);return u(`Created record ${n} in ${e}`,{id:n,message:"Record created successfully"},["Use dataverse_get to retrieve the full record","Use dataverse_associate to link related records"])}case"dataverse_update":{let{entitySetName:e,id:t,data:n,etag:o}=Gt.parse(r);return await i.updateRecord(e,t,n,o),u(`Updated record ${t} in ${e}`,{message:"Record updated successfully"},["Use dataverse_get to verify the update","Use etag parameter for optimistic concurrency"])}case"dataverse_delete":{let{entitySetName:e,id:t,confirm:n}=zt.parse(r);return n?(await i.deleteRecord(e,t),u(`Deleted record ${t} from ${e}`,{message:"Record deleted successfully"},["This operation is irreversible"])):{content:[{type:"text",text:JSON.stringify({message:`Deletion not performed. Set 'confirm: true' to delete record '${t}' from '${e}'.`})}]}}case"dataverse_upsert":{let e=Vt.parse(r),{entitySetName:t,alternateKey:n,alternateKeyValue:o,alternateKeys:s,data:l,mode:c="upsert"}=e,d;s&&Object.keys(s).length>0&&(d=Object.entries(s).map(([p,g])=>`${v(p)}='${v(g)}'`).join(","));let m=await i.upsertRecord(t,n??"",o??"",l,c,d);return u(`Upsert ${m.operation}: record in ${t}`,{operation:m.operation,id:m.id,message:`Record ${m.operation} successfully`},["Use dataverse_get_entity_key to verify alternate key definitions"])}case"dataverse_assign":{let{entitySetName:e,id:t,ownerType:n,ownerId:o}=Kt.parse(r),s=n==="systemuser"?"systemusers":"teams";return await i.updateRecord(e,t,{"ownerid@odata.bind":`/${s}(${o})`}),u(`Assigned record ${t} in ${e} to new owner`,{message:"Record assigned successfully"},["Use dataverse_list_users or dataverse_list_teams to find valid owners"])}default:throw new Error(`Unknown CRUD tool: ${a}`)}}import{z as j}from"zod";var Ae=[{name:"dataverse_associate",description:"Creates an association between two Dataverse records via a named N:N or 1:N relationship. Requires the relationship schema name obtainable from dataverse_get_relationships. Use for N:N relationships or to link records without modifying a lookup field directly \u2014 for simple 1:N lookups, setting the lookup field in dataverse_update is simpler. WHEN TO USE: Linking two records via an N:N relationship or 1:N navigation property. BEST PRACTICES: Get the relationship schema name from dataverse_get_relationships first; for simple 1:N lookups use dataverse_update. WORKFLOW: update_record.",inputSchema:{type:"object",properties:{entitySetName:{type:"string"},id:{type:"string",description:"Source record GUID"},relationshipName:{type:"string",description:"Relationship schema name"},relatedEntitySetName:{type:"string",description:"Related entity set name"},relatedId:{type:"string",description:"Related record GUID"}},required:["entitySetName","id","relationshipName","relatedEntitySetName","relatedId"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_disassociate",description:"Removes an existing association between two Dataverse records on a named relationship. For N:N relationships, provide relatedId and relatedEntitySetName to build the correct $id URL. For 1:N relationships, relatedId and relatedEntitySetName are optional. Use dataverse_get_relationships to find the correct relationship schema name. WHEN TO USE: Removing an N:N or 1:N link between two records without deleting either record. BEST PRACTICES: Get the relationship schema name from dataverse_get_relationships; relatedId is required for N:N. WORKFLOW: update_record.",inputSchema:{type:"object",properties:{entitySetName:{type:"string"},id:{type:"string"},relationshipName:{type:"string"},relatedId:{type:"string",description:"Required for N:N relationships"},relatedEntitySetName:{type:"string",description:'Entity set name of the related record (required for N:N). E.g., "contacts"'}},required:["entitySetName","id","relationshipName"]},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!0,openWorldHint:!0}}],Xt=j.object({entitySetName:b,id:j.string().uuid(),relationshipName:ae,relatedEntitySetName:b,relatedId:j.string().uuid()}),Qt=j.object({entitySetName:b,id:j.string().uuid(),relationshipName:ae,relatedId:j.string().uuid().optional(),relatedEntitySetName:b.optional()});async function Ee(a,r,i){switch(a){case"dataverse_associate":{let{entitySetName:e,id:t,relationshipName:n,relatedEntitySetName:o,relatedId:s}=Xt.parse(r);return await i.associate(e,t,n,o,s),u(`Associated ${e}(${t}) with ${o}(${s}) via ${n}`,{message:"Records associated successfully"},["Use dataverse_get_relationships to verify relationship names"])}case"dataverse_disassociate":{let{entitySetName:e,id:t,relationshipName:n,relatedId:o,relatedEntitySetName:s}=Qt.parse(r);return await i.disassociate(e,t,n,o,s),u(`Disassociated records via ${n}`,{message:"Records disassociated successfully"},["This removes the N:N link but does not delete records"])}default:throw new Error(`Unknown relation tool: ${a}`)}}import{z as h}from"zod";var ke=[{name:"dataverse_execute_action",description:"Executes a global (unbound) Dataverse action that is not tied to a specific record \u2014 for example WinOpportunity, SendEmail, or custom process actions. Use dataverse_execute_bound_action when the action must operate on a particular record. Actions differ from functions in that they are state-changing operations; for read-only operations use dataverse_execute_function. WHEN TO USE: Invoking global Dataverse actions or custom process actions like WinOpportunity or SendEmail. BEST PRACTICES: Check action parameters via metadata or docs; use dataverse_execute_bound_action for record-scoped actions. WORKFLOW: update_record.",inputSchema:{type:"object",properties:{actionName:{type:"string",description:'Action logical name (e.g., "WinOpportunity")'},parameters:{type:"object",description:"Action parameters"}},required:["actionName"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}},{name:"dataverse_execute_function",description:"Executes a global (unbound) Dataverse OData function that is read-only and returns data without side effects \u2014 for example RetrieveTotalRecordCount or InitializeFrom. Use dataverse_execute_action for state-changing operations. Use dataverse_execute_bound_action when the function/action requires a specific record context. WHEN TO USE: Calling global read-only OData functions like RetrieveTotalRecordCount or InitializeFrom. BEST PRACTICES: Functions are side-effect-free and safe to retry; use dataverse_execute_action for mutations. WORKFLOW: query_data.",inputSchema:{type:"object",properties:{functionName:{type:"string",description:'Function name (e.g., "WhoAmI", "RetrieveTotalRecordCount")'},parameters:{type:"object",description:"Function parameters as string key-value pairs"}},required:["functionName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_execute_bound_action",description:"Executes a Dataverse action bound to a specific record instance, passing the entity set name and record GUID as context (e.g., QualifyLead on a lead, or a custom action scoped to an account). The actionName should not include the Microsoft.Dynamics.CRM namespace prefix. Use dataverse_execute_action for global unbound actions that do not require a record context. WHEN TO USE: Running an action scoped to a specific record, e.g. QualifyLead on a lead. BEST PRACTICES: Omit the Microsoft.Dynamics.CRM namespace prefix; ensure the record exists first. WORKFLOW: update_record.",inputSchema:{type:"object",properties:{entitySetName:{type:"string"},id:{type:"string",description:"Record GUID"},actionName:{type:"string",description:"Action name (without Microsoft.Dynamics.CRM prefix)"},parameters:{type:"object",description:"Action parameters"}},required:["entitySetName","id","actionName"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}},{name:"dataverse_retrieve_dependencies_for_delete",description:"Checks what solution components would block deletion of a specific component. Provide the Dataverse component type code (1=Entity, 2=Attribute, 26=SavedQuery, 29=Workflow, 92=PluginAssembly) and the component GUID. Use before deleting shared Dataverse customization components. WHEN TO USE: Before deleting a solution component to check for blocking dependencies. BEST PRACTICES: Resolve all dependencies before attempting deletion. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{componentType:{type:"number",description:"Dataverse component type code (1=Entity, 2=Attribute, 29=Workflow, 92=PluginAssembly)"},objectId:{type:"string",description:"Component GUID"}},required:["componentType","objectId"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_execute_bound_function",description:"Executes a Dataverse function bound to a specific record (e.g., CalculateRollupField, GetQuantityAvailable). Use for read-only computed operations on a single record. Unlike bound actions, bound functions do not modify data. WHEN TO USE: Calling a read-only computed function on a specific record, e.g. CalculateRollupField. BEST PRACTICES: Functions are side-effect-free and safe to call repeatedly; pass parameters as string key-value pairs. WORKFLOW: query_data.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:'OData entity set name of the table (e.g., "accounts")'},id:{type:"string",description:"GUID of the record"},functionName:{type:"string",description:'Name of the bound function (e.g., "CalculateRollupField")'},parameters:{type:"object",description:"Function parameters as key-value pairs of strings",additionalProperties:{type:"string"}}},required:["entitySetName","id","functionName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_list_dependencies",description:"Lists workflows, Power Automate flows, Business Rules, and custom actions that reference a given table. Use to detect hidden dependencies before modifying or removing a table. Returns component name, type, state (Active/Draft), trigger event (Create/Update/Delete), and count. WHEN TO USE: Before modifying or removing a table, to discover workflows, flows, and plugins that reference it. BEST PRACTICES: Filter by componentType to focus on specific dependency kinds. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{tableName:{type:"string",description:'Logical name of the table to check (e.g., "account", "contact")'},componentType:{type:"array",items:{type:"string",enum:["Workflow","Flow","BusinessRule","Action","BusinessProcessFlow","Plugin","CustomAPI"]},description:"Filter by component type. Default: all types."}},required:["tableName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],V=/^[a-zA-Z0-9_.]+$/,Yt=h.object({actionName:h.string().min(1).regex(V,"actionName must contain only letters, digits, underscores, or dots"),parameters:h.record(h.unknown()).optional().default({})}),Jt=h.object({functionName:h.string().min(1).regex(V,"functionName must contain only letters, digits, underscores, or dots"),parameters:h.record(h.string()).optional().default({})}),Zt=h.object({entitySetName:b,id:h.string().uuid(),actionName:h.string().min(1).regex(V,"actionName must contain only letters, digits, underscores, or dots"),parameters:h.record(h.unknown()).optional().default({})}),ea=h.object({componentType:h.number().int().positive(),objectId:h.string().uuid()}),ta=["Workflow","Flow","BusinessRule","Action","BusinessProcessFlow","Plugin","CustomAPI"],aa=h.object({tableName:h.string().min(1),componentType:h.array(h.enum(ta)).optional()}),na=h.object({entitySetName:b,id:h.string().uuid(),functionName:h.string().min(1).regex(V,"functionName must contain only letters, digits, underscores, or dots"),parameters:h.record(h.string()).optional().default({})});async function De(a,r,i){switch(a){case"dataverse_execute_action":{let{actionName:e,parameters:t}=Yt.parse(r),n=await i.executeAction(e,t);return u(`Executed unbound action ${e}`,n,["Use dataverse_list_custom_actions to discover available actions"])}case"dataverse_execute_function":{let{functionName:e,parameters:t}=Jt.parse(r),n=await i.executeFunction(e,t);return u(`Executed function ${e}`,n,["Functions are read-only operations"])}case"dataverse_execute_bound_action":{let{entitySetName:e,id:t,actionName:n,parameters:o}=Zt.parse(r),s=await i.executeBoundAction(e,t,n,o);return u(`Executed bound action ${n} on ${e}(${t})`,s)}case"dataverse_list_dependencies":{let{tableName:e,componentType:t}=aa.parse(r),n=await i.listTableDependencies(e,t);return u(`${n.count} dependencies found for component`,n,["Use this information before deleting or modifying the component"])}case"dataverse_retrieve_dependencies_for_delete":{let{componentType:e,objectId:t}=ea.parse(r),n=await i.listDependencies(e,t),o=Array.isArray(n)?n:n.value??[n];return u(`${o.length} dependencies found`,n,["Review dependencies before deleting the component"])}case"dataverse_execute_bound_function":{let{entitySetName:e,id:t,functionName:n,parameters:o}=na.parse(r),s=await i.executeBoundFunction(e,t,n,o);return u(`Executed bound function ${n} on ${e}(${t})`,s)}default:throw new Error(`Unknown action tool: ${a}`)}}import{z as H}from"zod";var Oe=[{name:"dataverse_batch_execute",description:"Executes multiple Dataverse operations in a single HTTP $batch request to reduce network round-trips and improve throughput. Accepts up to 1000 individual GET, POST, PATCH, or DELETE requests. Use for bulk creates, updates, or deletes that need to be grouped for performance. Set useChangeset=true to wrap all mutating operations (POST/PATCH/DELETE) in an atomic changeset \u2014 a failure rolls back ALL changeset operations. Individual per-operation results (status, body) are returned as an array in the same order as the input requests. WHEN TO USE: Bulk creates, updates, or deletes (up to 1000 ops) needing a single HTTP round-trip. BEST PRACTICES: Use useChangeset=true for atomic all-or-nothing mutations; batch GETs for parallel reads. WORKFLOW: bulk_operations.",inputSchema:{type:"object",properties:{requests:{type:"array",description:"Array of batch requests to execute",items:{type:"object",properties:{method:{type:"string",enum:["GET","POST","PATCH","DELETE"],description:"HTTP method"},url:{type:"string",description:'Relative URL (e.g., "accounts(guid)" or "contacts")'},body:{type:"object",description:"Request body for POST/PATCH operations"}},required:["method","url"]}},useChangeset:{type:"boolean",description:"Wrap mutating operations (POST/PATCH/DELETE) in an atomic changeset. A failure rolls back ALL operations in the changeset. Defaults to false."}},required:["requests"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}}],ra=H.object({method:H.enum(["GET","POST","PATCH","DELETE"]),url:H.string().min(1).refine(a=>!/[\r\n]/.test(a),{message:"Batch URL must not contain CR or LF characters"}).refine(a=>!a.startsWith("http"),{message:"Batch URL must be a relative path, not an absolute URL"}).refine(a=>!/(\.\.[\/\\])|(^\.\.$)/.test(a),{message:"Batch URL must not contain path traversal sequences"}),body:H.record(H.unknown()).optional()}),oa=H.object({requests:H.array(ra).min(1).max(1e3),useChangeset:H.boolean().default(!1).describe("Wrap mutating operations (POST/PATCH/DELETE) in an atomic changeset. A failure rolls back ALL operations in the changeset.")});async function Ie(a,r,i,e){if(a==="dataverse_batch_execute"){let{requests:t,useChangeset:n}=oa.parse(r),o=t.map(c=>({method:c.method,url:c.url,body:c.body}));await e?.report(0,t.length);let s=await i.batchExecute(o,n);await e?.report(t.length,t.length);let l=s.filter(c=>c===null||!c.error).length;return u(`Batch executed: ${l}/${s.length} operations succeeded`,{results:s,count:s.length},["Use batch for bulk create/update operations to reduce HTTP round-trips"])}throw new Error(`Unknown batch tool: ${a}`)}import{z as K}from"zod";var Ce=[{name:"dataverse_change_detection",description:"Detects new, modified, and deleted records since a previous sync using Dataverse change tracking (delta queries). On first call, pass deltaToken=null to get an initial snapshot and receive a token. On subsequent calls, pass the returned token to retrieve only changes since last sync. Change tracking must be enabled on the table in Dataverse settings. Returns newAndModified records, deleted record IDs, and the nextDeltaToken for the next call. WHEN TO USE: Incremental sync \u2014 detecting records created, updated, or deleted since a previous snapshot. BEST PRACTICES: Store the deltaToken persistently; always specify $select to minimize payload. WORKFLOW: query_data.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:'OData entity set name (e.g., "accounts")'},deltaToken:{anyOf:[{type:"string"},{type:"null"}],description:"Delta token from a previous call, or null for the initial sync"},select:{type:"array",items:{type:"string"},description:"Columns to return (recommended to minimise payload)"}},required:["entitySetName","deltaToken"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],ia=K.object({entitySetName:b,deltaToken:K.string().nullable(),select:K.array(K.string()).optional()});async function Ue(a,r,i){if(a==="dataverse_change_detection"){let{entitySetName:e,deltaToken:t,select:n}=ia.parse(r),o;try{o=await i.getChangedRecords(e,t,n)}catch(l){let c=l instanceof Error?l.message:String(l);if(/change.?track|0x80072491/i.test(c))return $({type:"feature_disabled",feature:"Change Tracking",cannotProceedBecause:`Change tracking is not enabled on '${e}', so delta queries cannot be executed.`,adminPortal:"Power Apps Maker Portal",steps:["Open Power Apps maker portal (make.powerapps.com)",`Navigate to Tables \u2192 search for '${e}'`,"Open the table \u2192 click the Settings (gear) icon",'Enable "Track changes"',"Save the table, then publish customizations"],fixableViaToolName:"dataverse_update_entity"});throw l}let s=Array.isArray(o?.newAndModified)?o.newAndModified:[];return u(`${s.length} changed records detected`,o,["Store the returned deltaToken for subsequent incremental sync"])}throw new Error(`Unknown tracking tool: ${a}`)}import{z as T}from"zod";var sa=T.object({includeManaged:T.boolean().optional().default(!1),nameFilter:T.string().optional(),top:T.number().int().min(1).max(200).optional().default(50)}),la=T.object({solutionName:T.string().min(1).describe("Unique name of the solution (not the display name)"),componentType:T.number().int().optional().describe("Filter by Dataverse component type code (1=Entity, 29=Workflow, 97=WebResource, 90=PluginAssembly, etc.). Omit for all types."),top:T.number().int().min(1).max(5e3).default(200).optional()}),da=T.object({components:T.object({entities:T.array(T.string()).optional().describe("Entity logical names to publish"),webResources:T.array(T.string()).optional().describe("Web resource names to publish"),optionSets:T.array(T.string()).optional().describe("Global OptionSet names to publish")}).optional().describe("Specific components to publish. If omitted, ALL unpublished customizations are published.")}),He=[{name:"dataverse_list_solutions",description:"Lists Dataverse solutions in the environment. By default returns only unmanaged solutions. Set includeManaged=true to include managed (imported) solutions. Use nameFilter to search by unique name. WHEN TO USE: Discovering solutions in the environment for inspection or management. BEST PRACTICES: Use nameFilter for targeted lookup; check version and publisher for managed solutions. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{includeManaged:{type:"boolean",description:"Include managed (imported) solutions. Default: false"},nameFilter:{type:"string",description:"Filter solutions by unique name (contains match)"},top:{type:"number",description:"Maximum number of solutions to return (default 50, max 200)"}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_solution_components",description:"Lists all components in a named Dataverse solution (entities, attributes, workflows, web resources, plugins, etc.). Use the unique solution name (not display name). Optionally filter by component type code (1=Entity, 29=Workflow, 97=WebResource, 90=PluginAssembly). WHEN TO USE: Inspecting which entities, workflows, or web resources belong to a specific solution. BEST PRACTICES: Filter by componentType code to reduce noise; use unique name, not display name. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{solutionName:{type:"string",description:"Unique name of the solution"},componentType:{type:"number",description:"Filter by component type code"},top:{type:"number",description:"Max results (default 200, max 5000)"}},required:["solutionName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_publish_customizations",description:'Publishes unpublished Dataverse customizations. Without parameters, publishes ALL pending customizations (equivalent to clicking "Publish All" in Power Apps maker portal). Optionally specify entities, webResources, or optionSets to publish only those components. WARNING: Publishing all can take 30-120 seconds in large environments. WHEN TO USE: After making schema or UI changes that need to be visible to users. BEST PRACTICES: Publish specific entities when possible to reduce duration; full publish can take 30-120s. WORKFLOW: manage_solution.',inputSchema:{type:"object",properties:{components:{type:"object",description:"Specific components. Omit to publish all.",properties:{entities:{type:"array",items:{type:"string"}},webResources:{type:"array",items:{type:"string"}},optionSets:{type:"array",items:{type:"string"}}}}},required:[]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}];async function qe(a,r,i){switch(a){case"dataverse_list_solutions":{let e=sa.parse(r??{}),t=["isvisible eq true"];e.includeManaged||t.push("ismanaged eq false"),e.nameFilter&&t.push(`contains(uniquename,'${v(e.nameFilter)}')`);let o=((await i.query("solutions",{select:["solutionid","uniquename","friendlyname","version","ismanaged","installedon"],filter:t.join(" and "),expand:"publisherid($select=friendlyname)",orderby:"friendlyname asc",top:e.top})).value??[]).map(s=>({solutionId:s.solutionid,uniqueName:s.uniquename,friendlyName:s.friendlyname,version:s.version,isManaged:s.ismanaged,installedOn:s.installedon,publisher:s.publisherid?.friendlyname??null}));return u(`${o.length} solutions found`,{solutions:o,count:o.length},["Use dataverse_solution_components to inspect a specific solution"])}case"dataverse_solution_components":{let{solutionName:e,componentType:t,top:n=200}=la.parse(r),o=await i.getSolutionComponents(e,t,n);return u(`${o.count} components in solution '${e}'`,o,["Filter by componentType for specific component kinds"])}case"dataverse_publish_customizations":{let{components:e}=da.parse(r??{}),t=await i.publishCustomizations(e);return u("Customizations published successfully",t,["Changes are now visible to all users in the environment"])}default:throw new Error(`Unknown solution tool: ${a}`)}}import{z as B}from"zod";var ca=B.object({callerId:B.string().uuid().describe('Azure AD Object ID (GUID) of the Dataverse system user to impersonate. Requires the executing account to have the "Act on behalf of another user" privilege in Dataverse.'),toolName:B.string().min(1).describe('Name of the MCP tool to execute on behalf of the user (e.g., "dataverse_create", "dataverse_query")'),toolArgs:B.record(B.unknown()).describe("Arguments for the wrapped tool, as an object")}),ne=[{name:"dataverse_impersonate",description:'Executes another Dataverse tool on behalf of a different system user by injecting the MSCRMCallerId header. Requires the executing account to have the "Act on behalf of another user" privilege in Dataverse. The impersonation applies ONLY to the single tool call specified. Use for auditing workflows that must create or update records under a specific user identity. WHEN TO USE: Creating or updating records under a specific user identity for audit-trail purposes. BEST PRACTICES: Impersonation applies to the single wrapped tool call only; cannot impersonate System Administrators. WORKFLOW: update_record.',inputSchema:{type:"object",properties:{callerId:{type:"string",description:"GUID (Azure AD Object ID) of the Dataverse system user to impersonate"},toolName:{type:"string",description:'MCP tool to execute while impersonating (e.g., "dataverse_create")'},toolArgs:{type:"object",description:"Arguments for the wrapped tool"}},required:["callerId","toolName","toolArgs"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}}];async function Le(a,r,i,e){if(a!=="dataverse_impersonate")throw new Error(`Unknown impersonate tool: ${a}`);let{callerId:t,toolName:n,toolArgs:o}=ca.parse(r);try{if(((await i.query(`systemusers(${v(t)})/systemuserroles_association`,{select:["name"],filter:"name eq 'System Administrator'",top:1})).value?.length??0)>0)throw new Error("Security policy: impersonation of users with System Administrator role is prohibited to prevent privilege escalation.")}catch(c){throw c instanceof Error&&c.message.includes("Security policy")?c:new Error(`Security policy: cannot verify callerId roles \u2014 impersonation denied. Cause: ${String(c)}`)}let s=i.http,l=s.defaultHeaders.MSCRMCallerId;try{s.defaultHeaders.MSCRMCallerId=t;let c=await e(n,o,i);return u(`Impersonated as ${t}, executed ${n}`,{impersonatedAs:t,tool:n,result:JSON.parse(c.content[0].text)},["Impersonation applies to this single call only"])}finally{l===void 0?delete s.defaultHeaders.MSCRMCallerId:s.defaultHeaders.MSCRMCallerId=l}}import{z as N}from"zod";var ua={10:"Pre-validation",20:"Pre-operation",40:"Post-operation",45:"Post-operation (deprecated)"},pa={0:"Synchronous",1:"Asynchronous"},ma=N.object({workflowId:N.string().uuid(),activate:N.boolean()}),Pe=[{name:"dataverse_list_custom_actions",description:"Lists all custom actions (custom API / SDK messages) registered in the environment. Returns the message name, category, bound entity (if any), execute privilege, and whether it is customizable. Useful for discovering available automation entry points and agent-callable actions. WHEN TO USE: Discovering available custom API / SDK messages for automation. BEST PRACTICES: Use nameFilter to search; follow up with dataverse_execute_action to invoke. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{top:{type:"number",description:"Max records (default 100, max 500)"},nameFilter:{type:"string",description:"Filter by name (substring match)"}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_list_plugin_steps",description:"Lists plugin steps (SdkMessageProcessingStep registrations) in the environment. Shows plugin assembly, step name, message (Create/Update/Delete/\u2026), entity, stage (pre/post), mode (sync/async), and state (enabled/disabled). Essential for understanding what custom business logic fires on Dataverse operations. WHEN TO USE: Understanding what custom business logic fires on CRUD operations for a table. BEST PRACTICES: Filter by entityLogicalName; check stage and mode to understand execution order and timing. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{top:{type:"number",description:"Max records (default 100, max 500)"},activeOnly:{type:"boolean",description:"Return only enabled steps (default: true)"},entityLogicalName:{type:"string",description:"Filter by entity logical name (e.g. 'account')"}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_set_workflow_state",description:"Activates or deactivates a Dataverse workflow (classic workflow / real-time workflow / action). Set activate=true to activate (statecode 1, statuscode 2) or activate=false to deactivate (statecode 0, statuscode 1). Returns the new state. WHEN TO USE: Activating or deactivating a classic workflow, real-time workflow, or action. BEST PRACTICES: Verify workflow ID first; deactivation stops future triggers but does not cancel in-progress runs. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{workflowId:{type:"string",description:"The workflow GUID"},activate:{type:"boolean",description:"true = activate, false = deactivate (draft)"}},required:["workflowId","activate"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_list_connection_references",description:"Lists all Power Automate connection references defined in the environment, showing their display name, connector ID, connection ID, and active status. Use this before importing a solution containing flows to detect broken or unmapped connections that would cause silent import failures. WHEN TO USE: Pre-deployment solution validation, auditing connection health, identifying broken flow connections. BEST PRACTICES: Look for inactive (isActive=false) references \u2014 these indicate flows that will fail after deployment. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{connectorId:{type:"string",description:"Filter by connector ID substring (e.g. '/providers/Microsoft.PowerApps/apis/shared_sharepointonline')"},activeOnly:{type:"boolean",description:"Return only active connection references (default: false = return all)"},top:{type:"number",description:"Maximum records to return (default 100, max 500)"}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],fa=N.object({top:N.number().positive().max(500).optional().default(100),nameFilter:N.string().optional()}),ga=N.object({top:N.number().positive().max(500).optional().default(100),activeOnly:N.boolean().optional().default(!0),entityLogicalName:N.string().optional()}),ya=N.object({connectorId:N.string().optional(),activeOnly:N.boolean().optional().default(!1),top:N.number().int().positive().max(500).optional().default(100)});async function $e(a,r,i){switch(a){case"dataverse_list_custom_actions":{let{top:e,nameFilter:t}=fa.parse(r??{}),n=["isprivate eq false"];t&&n.push(`contains(name,'${v(t)}')`);let s=(await i.query("sdkmessages",{select:["sdkmessageid","name","categoryname","isprivate","isreadonly","isvalidforexecuteasync"],filter:n.join(" and "),top:e})).value.map(l=>({id:l.sdkmessageid,name:l.name,category:l.categoryname??"",isPrivate:l.isprivate,isReadOnly:l.isreadonly,asyncSupported:l.isvalidforexecuteasync}));return u(`${s.length} custom actions found`,{total:s.length,messages:s},["Use dataverse_execute_action to run an action"])}case"dataverse_list_plugin_steps":{let{top:e,activeOnly:t,entityLogicalName:n}=ga.parse(r??{}),o={select:["sdkmessageprocessingstepid","name","stage","mode","rank","statecode","filteringattributes","asyncautodelete"],expand:"sdkmessageid($select=name,categoryname),plugintypeid($select=name,assemblyname),sdkmessagefilterid($select=primaryobjecttypecode)",top:e};t&&(o.filter="statecode eq 0");let l=(await i.query("sdkmessageprocessingsteps",o)).value;if(n){let d=n.toLowerCase();l=l.filter(m=>m.sdkmessagefilterid?.primaryobjecttypecode?.toLowerCase()===d)}let c=l.map(d=>({id:d.sdkmessageprocessingstepid,name:d.name,message:d.sdkmessageid?.name??"",entity:d.sdkmessagefilterid?.primaryobjecttypecode??"",assembly:d.plugintypeid?.assemblyname??"",pluginType:d.plugintypeid?.name??"",stage:d.stage,stageName:ua[d.stage]??`Stage ${d.stage}`,mode:d.mode,modeName:pa[d.mode]??`Mode ${d.mode}`,rank:d.rank,isActive:d.statecode===0,filteringAttributes:d.filteringattributes??null,asyncAutoDelete:d.asyncautodelete}));return u(`${c.length} plugin steps found`,{total:c.length,steps:c},["Use dataverse_get_plugin_trace_logs for debugging plugin issues"])}case"dataverse_set_workflow_state":{let{workflowId:e,activate:t}=ma.parse(r);try{await i.updateRecord("workflows",e,{statecode:t?1:0,statuscode:t?2:1})}catch(n){let o=n instanceof Error?n.message:String(n);throw/0x80040203|does not refer to a valid workflow|404/i.test(o)?new Error(`Workflow '${e}' not found. Use dataverse_list_workflows to retrieve valid workflow GUIDs in this environment. Original error: ${o}`):n}return u(`Workflow state updated for ${e}`,{workflowId:e,newState:t?"Activated":"Draft",statecode:t?1:0,statuscode:t?2:1})}case"dataverse_list_connection_references":{let{connectorId:e,activeOnly:t,top:n}=ya.parse(r??{}),o=[];t&&o.push("statecode eq 0"),e&&o.push(`contains(connectorid,'${v(e)}')`);let s={select:["connectionreferenceid","connectionreferencelogicalname","connectionreferencedisplayname","connectorid","connectionid","statecode","statuscode"],orderby:"connectionreferencedisplayname asc",top:n};o.length&&(s.filter=o.join(" and "));let c=((await i.query("connectionreferences",s)).value??[]).map(p=>({id:p.connectionreferenceid??"",logicalName:p.connectionreferencelogicalname??"",displayName:p.connectionreferencedisplayname??"",connectorId:p.connectorid??"",connectionId:p.connectionid??null,isActive:p.statecode===0,statusCode:p.statuscode??null})),d=c.filter(p=>!p.isActive).length,m=d>0?`${c.length} connection references found (${d} inactive \u2014 check before deploying)`:`${c.length} connection references found`;return u(m,{connectionReferences:c,count:c.length,inactiveCount:d},["Inactive references (isActive=false) indicate broken connections that will cause flow failures","Use the Power Apps maker portal to reconnect or replace broken connection references before deploying"])}default:throw new Error(`Unknown customization tool: ${a}`)}}import{z as R}from"zod";var va={1e8:"String",100000001:"Number",100000002:"Boolean",100000003:"JSON",100000004:"DataSource"},ha={String:1e8,Integer:100000001,Boolean:100000002,JSON:100000003},We=[{name:"dataverse_get_environment_variable",description:"Retrieves an environment variable's definition and current value from Dataverse. Returns the schema name, display name, type, default value, and the current override value (if set). Useful for reading feature flags, configuration values, and integration settings stored in Dataverse environment variables. WHEN TO USE: Reading configuration values, feature flags, or integration settings stored as environment variables. BEST PRACTICES: Check both defaultValue and currentValue; the effective value is currentValue ?? defaultValue. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{schemaName:{type:"string",description:"The schema name of the environment variable (e.g. 'new_MyConfig')"}},required:["schemaName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_set_environment_variable",description:"Sets or updates an environment variable value in Dataverse. If a value record already exists for the variable, it is updated; otherwise a new value record is created. The schemaName must match an existing environment variable definition. WHEN TO USE: Updating configuration values or feature flags stored in Dataverse environment variables. BEST PRACTICES: Verify the variable exists first with dataverse_get_environment_variable; schemaName must match exactly. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{schemaName:{type:"string",description:"The schema name of the environment variable"},value:{type:"string",description:"The new value to set"}},required:["schemaName","value"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_create_environment_variable",description:"Creates a new Dataverse environment variable definition and sets its initial value. Use this when the variable does not yet exist \u2014 use dataverse_set_environment_variable to update an existing one. WHEN TO USE: Initial setup of configuration flags, feature toggles, or integration settings. BEST PRACTICES: Use a solution-prefixed schemaName (e.g. 'myprefix_MyConfig'); provide solutionUniqueName to register in a solution. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{schemaName:{type:"string",description:"Schema name of the new variable (e.g. 'new_MyConfig'). Case-sensitive."},displayName:{type:"string",description:"Human-readable display name"},type:{type:"string",enum:["String","Integer","Boolean","JSON"],description:"Variable type"},value:{type:"string",description:"Initial value to set"},description:{type:"string",description:"Optional description"},defaultValue:{type:"string",description:"Optional default value (fallback when no override value is set)"},confirm:{type:"boolean",description:"Must be true \u2014 confirms intentional creation of a new environment variable definition"}},required:["schemaName","displayName","type","value","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}}],ba=R.object({schemaName:R.string().min(1)}),_a=R.object({schemaName:R.string().min(1),value:R.string()}),wa=R.object({schemaName:R.string().min(1),displayName:R.string().min(1),type:R.enum(["String","Integer","Boolean","JSON"]),value:R.string(),description:R.string().optional(),defaultValue:R.string().optional(),confirm:R.literal(!0,{errorMap:()=>({message:"Set confirm: true to create a new environment variable definition"})})});async function Ta(a,r){let{schemaName:i}=ba.parse(a),t=(await r.query("environmentvariabledefinitions",{filter:`schemaname eq '${v(i)}'`,select:["environmentvariabledefinitionid","schemaname","displayname","description","type","defaultvalue","isrequired"],top:1})).value;if(t.length===0)throw new Error(`Environment variable '${i}' not found. Check the schema name (it is case-sensitive, e.g. 'new_MyConfig'). To browse existing variables: Power Apps maker portal \u2192 Solutions \u2192 your solution \u2192 Environment Variables. To create a new one: open a solution \u2192 New \u2192 More \u2192 Environment Variable.`);let n=t[0],o=n.environmentvariabledefinitionid,l=(await r.query("environmentvariablevalues",{filter:`_environmentvariabledefinitionid_value eq ${o}`,select:["environmentvariablevalueid","value"],top:1})).value,c=l.length>0?l[0]:null,d=n.type,m=c?c.value??null:null,p=n.defaultvalue??null,g={schemaName:n.schemaname,displayName:n.displayname??"",description:n.description??"",type:d,typeName:va[d]??"Unknown",defaultValue:p,currentValue:m,valueId:c?c.environmentvariablevalueid:null,isRequired:n.isrequired??!1,effectiveValue:m??p};return u(`Environment variable '${g.schemaName}': ${g.typeName} = ${g.effectiveValue??"(not set)"}`,g,["Use dataverse_set_environment_variable to update the value"])}async function Sa(a,r){let{schemaName:i,value:e}=_a.parse(a),n=(await r.query("environmentvariabledefinitions",{filter:`schemaname eq '${v(i)}'`,select:["environmentvariabledefinitionid","schemaname"],top:1})).value;if(n.length===0)throw new Error(`Environment variable definition '${i}' not found. This tool can only update the value of an existing variable. To create a new environment variable: Power Apps maker portal \u2192 Solutions \u2192 your solution \u2192 New \u2192 More \u2192 Environment variable. Then call this tool to set its value.`);let o=n[0].environmentvariabledefinitionid,l=(await r.query("environmentvariablevalues",{filter:`_environmentvariabledefinitionid_value eq ${o}`,select:["environmentvariablevalueid","value"],top:1})).value,c,d;l.length>0?(d=l[0].environmentvariablevalueid,await r.updateRecord("environmentvariablevalues",d,{value:e}),c="updated"):(d=await r.createRecord("environmentvariablevalues",{value:e,"EnvironmentVariableDefinitionId@odata.bind":`/environmentvariabledefinitions(${o})`}),c="created");let m={schemaName:i,operation:c,valueId:d,value:e};return u(`Environment variable '${i}' set to new value`,m,["Use dataverse_get_environment_variable to verify the update"])}async function Na(a,r){let{schemaName:i,displayName:e,type:t,value:n,description:o,defaultValue:s}=wa.parse(a),l=U({toolName:"dataverse_create_environment_variable"}).map(I=>`[${I.severity.toUpperCase()}] ${I.code}: ${I.message}`);if((await r.query("environmentvariabledefinitions",{filter:`schemaname eq '${v(i)}'`,select:["environmentvariabledefinitionid"],top:1})).value.length>0)throw new Error(`Environment variable '${i}' already exists. Use dataverse_set_environment_variable to update its value.`);let d=ha[t]??1e8,m={schemaname:i,displayname:e,type:d};o&&(m.description=o),s!==void 0&&(m.defaultvalue=s);let p=await r.createRecord("environmentvariabledefinitions",m),g=await r.createRecord("environmentvariablevalues",{value:n,"EnvironmentVariableDefinitionId@odata.bind":`/environmentvariabledefinitions(${p})`});return u(`Environment variable '${i}' created (type: ${t}, value: '${n}').`,{schemaName:i,displayName:e,type:t,typeCode:d,definitionId:p,valueId:g,value:n,...l.length>0&&{warnings:l}},["Use dataverse_get_environment_variable to verify","Use dataverse_set_environment_variable to update the value later"])}async function je(a,r,i){switch(a){case"dataverse_get_environment_variable":return Ta(r,i);case"dataverse_set_environment_variable":return Sa(r,i);case"dataverse_create_environment_variable":return Na(r,i);default:throw new Error(`Unknown environment tool: ${a}`)}}import{z as D}from"zod";var xa={0:"Execute",1:"Create",2:"Retrieve",3:"RetrieveMultiple",4:"GetParent",5:"Update",6:"Delete",7:"Assign"},Ra={0:"Waiting",10:"WaitingForResources",20:"InProgress",21:"Pausing",22:"Canceling",30:"Succeeded",31:"Failed",32:"Canceled"},Aa={0:"ReadyToRun",1:"Suspended",2:"Locked",3:"Completed"},Ea=["plugintracelogid","typename","messagename","primaryentity","depth","operationtype","exceptiondetails","messageblock","createdon","performanceexecutionduration","correlationid","requestid"],ka=["asyncoperationid","name","operationtype","statuscode","statecode","message","createdon","startedon","completedon"],Me=[{name:"dataverse_get_plugin_trace_logs",description:"Retrieves recent plugin and custom workflow activity trace logs from Dataverse. Shows execution details including plugin type name, triggering message, entity, execution duration, trace messages written by the developer, and exception details if the plugin failed. Requires the Plugin Trace Log feature to be enabled in Dataverse settings (Settings > Administration > System Settings > Customization tab > 'Enable logging to plugin trace log'). Essential for debugging plugin failures in production. WHEN TO USE: Debugging plugin failures or performance issues in production. BEST PRACTICES: Enable Plugin Trace Log in Dataverse settings first; filter by plugin name or entity to narrow results. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{top:{type:"number",description:"Max records to return (default 50, max 200)"},pluginTypeFilter:{type:"string",description:"Filter by plugin type name (substring match, e.g. 'AccountValidation')"},messageFilter:{type:"string",description:"Filter by message name (e.g. 'Create', 'Update')"},entityFilter:{type:"string",description:"Filter by entity logical name (e.g. 'account')"},exceptionsOnly:{type:"boolean",description:"Return only traces where an exception occurred (default false)"}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_get_workflow_trace_logs",description:"Retrieves background workflow (Power Automate classic / legacy workflow engine) execution records from Dataverse. These are the AsyncOperation records for workflow-type operations, useful for diagnosing failures in background workflows and real-time workflows running asynchronously. Note: For modern cloud flows (Power Automate), use the Power Automate portal instead. WHEN TO USE: Diagnosing failures in classic/legacy background workflows. BEST PRACTICES: Use failedOnly=true to focus on errors; for modern cloud flows use Power Automate portal. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{top:{type:"number",description:"Max records (default 50, max 200)"},failedOnly:{type:"boolean",description:"Return only failed workflows (statuscode eq 31, default false)"},entityFilter:{type:"string",description:"Filter by regarding entity type (e.g. 'account')"}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],Da=D.object({top:D.number().int().positive().max(200).optional().default(50),pluginTypeFilter:D.string().optional(),messageFilter:D.string().optional(),entityFilter:D.string().optional(),exceptionsOnly:D.boolean().optional().default(!1)}),Oa=D.object({top:D.number().int().positive().max(200).optional().default(50),failedOnly:D.boolean().optional().default(!1),entityFilter:D.string().optional()});function k(a){return typeof a=="string"?a:""}function Ia(a){return typeof a=="number"?a:null}async function Fe(a,r,i){switch(a){case"dataverse_get_plugin_trace_logs":{let e=Da.parse(r),t=[];e.pluginTypeFilter&&t.push(`contains(typename,'${v(e.pluginTypeFilter)}')`),e.messageFilter&&t.push(`messagename eq '${v(e.messageFilter)}'`),e.entityFilter&&t.push(`primaryentity eq '${v(e.entityFilter)}'`),e.exceptionsOnly&&t.push("exceptiondetails ne null");let n=t.length>0?t.join(" and "):void 0,l=((await i.query("plugintracelogs",{select:Ea,orderby:"createdon desc",top:e.top,...n!==void 0?{filter:n}:{}})).value??[]).map(d=>{let m=typeof d.operationtype=="number"?d.operationtype:0,p=d.exceptiondetails;return{id:k(d.plugintracelogid),typeName:k(d.typename),message:k(d.messagename),entity:k(d.primaryentity),depth:typeof d.depth=="number"?d.depth:0,operationType:m,operationTypeName:xa[m]??String(m),createdOn:k(d.createdon),durationMs:Ia(d.performanceexecutionduration),correlationId:k(d.correlationid),requestId:k(d.requestid),hasException:p!=null&&p!=="",exceptionDetails:typeof p=="string"?p:null,messageBlock:typeof d.messageblock=="string"?d.messageblock:null}}),c={total:l.length,logs:l};return u(`${l.length} plugin trace logs found`,c,["Filter by plugin name or correlation ID for specific traces"])}case"dataverse_get_workflow_trace_logs":{let e=Oa.parse(r),t=["operationtype eq 10"];e.failedOnly&&t.push("statuscode eq 31");let n=t.join(" and "),l=((await i.query("asyncoperations",{select:ka,filter:n,orderby:"createdon desc",top:e.top})).value??[]).map(d=>{let m=typeof d.statuscode=="number"?d.statuscode:0,p=typeof d.statecode=="number"?d.statecode:0,g=d.message;return{id:k(d.asyncoperationid),name:k(d.name),statusCode:m,statusName:Ra[m]??String(m),stateCode:p,stateName:Aa[p]??String(p),createdOn:k(d.createdon),startedOn:typeof d.startedon=="string"?d.startedon:null,completedOn:typeof d.completedon=="string"?d.completedon:null,errorMessage:typeof g=="string"?g:null}}),c={total:l.length,workflows:l};return u(`${l.length} workflow trace logs found`,c,["Filter by status or entity type for specific workflow traces"])}default:throw new Error(`Unknown trace tool: ${a}`)}}import{z as x}from"zod";var Be=[{name:"dataverse_search",description:"Full-text Relevance Search across all configured Dataverse tables. Returns ranked results with entity name, record ID, score, and matched fields. Requires Relevance Search to be enabled in Dataverse admin settings. Use when you need to find records without knowing which table they belong to. WHEN TO USE: Cross-table free-text search when you don't know which table contains the data. BEST PRACTICES: Narrow with entities[] and $filter; use searchType=full for Lucene operators (AND, OR, NOT, wildcards). WORKFLOW: search_data.",inputSchema:{type:"object",properties:{query:{type:"string",description:"Full-text search string (supports Lucene syntax with searchType=full)"},entities:{type:"array",items:{type:"string"},description:"Restrict to specific table logical names (omit to search all configured tables)"},top:{type:"number",description:"Max results (default 10, max 50)"},searchMode:{type:"string",enum:["any","all"],description:"Match any or all terms (default: any)"},searchType:{type:"string",enum:["simple","full"],description:"Search mode: simple (default) or full (enables Lucene syntax: AND, OR, NOT, wildcards, fuzzy)"},filter:{type:"string",description:'OData $filter to apply on search results (e.g., "statecode eq 0")'},facets:{type:"array",items:{type:"string"},description:'Fields to return faceted counts for (e.g., ["@search.entityname","statecode"])'},orderby:{type:"array",items:{type:"string"},description:'OData $orderby for result sorting (e.g., ["@search.score desc","name asc"])'},select:{type:"array",items:{type:"string"},description:"Fields to return in each result (default: all indexed fields)"}},required:["query"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],Ca=x.object({query:x.string().min(1),entities:x.array(x.string()).optional(),top:x.number().int().positive().max(50).optional().default(10),searchMode:x.enum(["any","all"]).optional().default("any"),searchType:x.enum(["simple","full"]).optional(),filter:x.string().optional(),facets:x.array(x.string()).optional(),orderby:x.array(x.string()).optional(),select:x.array(x.string()).optional()});async function Ge(a,r,i){switch(a){case"dataverse_search":{let e=Ca.parse(r),t={search:e.query,top:e.top,searchMode:e.searchMode,returntotalrecordcount:!0};e.entities?.length&&(t.entities=e.entities),e.searchType&&(t.searchType=e.searchType),e.filter&&(t.filter=e.filter),e.facets?.length&&(t.facets=e.facets),e.orderby?.length&&(t.orderby=e.orderby),e.select?.length&&(t.select=e.select);let n;try{n=await i.executeAction("../../search/v1.0/query",t)}catch(l){let c=l instanceof Error?l.message:String(l);if(c.includes("404")||c.includes("Not Found"))return $({type:"feature_disabled",feature:"Dataverse Search (Relevance Search)",cannotProceedBecause:"Relevance Search is not enabled for this Dataverse environment, so full-text cross-table search is unavailable.",adminPortal:"Power Platform Admin Center",steps:["Open Power Platform Admin Center (admin.powerplatform.microsoft.com)","Select your environment \u2192 Settings","Navigate to Product \u2192 Features",'Under "Search", toggle "Dataverse Search" to On',"Save \u2014 indexing may take a few minutes before search is available"]});throw l}let o=(n.value??[]).map(l=>({entityName:l.entityname??"",objectId:l.objectid??"",score:l.score??0,highlights:l.highlights??{},fields:l.attributes??{}})),s={totalRecordCount:n.totalrecordcount??0,results:o};return n.facets&&(s.facets=n.facets),u(`${o.length} search results for '${e.query}'`,s,["Use dataverse_get to retrieve full record details","Narrow results with entities[] filter"])}default:throw new Error(`Unknown search tool: ${a}`)}}import{z as q}from"zod";var re={1:"Create",2:"Update",3:"Delete",4:"Activate",5:"Deactivate",11:"Share",12:"Unshare",13:"Assign",104:"Access"},Ua=Object.fromEntries(Object.entries(re).map(([a,r])=>[r,Number(a)])),ze=[{name:"dataverse_get_audit_log",description:"Retrieves audit log entries from Dataverse. Returns operation details, user info, and parsed change data for each entry. At least one filter (recordId, entityLogicalName, userId, fromDate, or operations) is recommended to avoid large result sets. Audit must be enabled on the environment and table \u2014 returns a clear error if auditing is disabled (HTTP 403). WHEN TO USE: Tracking who changed what and when on Dataverse records. BEST PRACTICES: Always provide at least one filter; audit must be enabled on the table. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{recordId:{type:"string",description:"GUID of a specific record to retrieve audit entries for"},entityLogicalName:{type:"string",description:'Logical name of the entity to filter audit entries (e.g., "account", "contact")'},userId:{type:"string",description:"GUID of the user who made the changes"},fromDate:{type:"string",description:"ISO 8601 date string \u2014 only return audit entries created on or after this date"},top:{type:"number",description:"Maximum number of audit entries to return (default: 50, max: 500)"},operations:{type:"array",items:{type:"string"},description:'Filter by operation names: "Create", "Update", "Delete", "Activate", "Deactivate", "Share", "Unshare", "Assign", "Access"'}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],Ha=q.object({recordId:q.string().uuid().optional(),entityLogicalName:q.string().min(1).optional(),userId:q.string().uuid().optional(),fromDate:q.string().datetime({offset:!0}).optional(),top:q.number().positive().max(500).optional().default(50),operations:q.array(q.string().min(1)).optional()});function qa(a){if(!a)return{};try{return JSON.parse(a)}catch{return a}}function La(a){return{auditId:a.auditid,operation:a.operation,operationName:re[a.operation]??`Unknown(${a.operation})`,action:a.action,actionName:re[a.action]??`Unknown(${a.action})`,createdOn:a.createdon,userId:a._userid_value,userFullName:a.userid?.fullname??"",userDomainName:a.userid?.domainname??"",objectId:a._objectid_value,objectTypeCode:a.objecttypecode,changes:qa(a.changedata)}}async function Ve(a,r,i){switch(a){case"dataverse_get_audit_log":{let e=Ha.parse(r),t=[];if(e.recordId&&t.push(`_objectid_value eq ${e.recordId}`),e.entityLogicalName){let n=v(e.entityLogicalName);t.push(`objecttypecode eq '${n}'`)}if(e.userId&&t.push(`_userid_value eq ${e.userId}`),e.fromDate&&t.push(`createdon ge ${e.fromDate}`),e.operations?.length){let n=e.operations.map(o=>Ua[o]).filter(o=>o!==void 0);if(n.length>0){let o=n.map(s=>`action eq ${s}`).join(" or ");t.push(`(${o})`)}}try{let o=(await i.query("audits",{select:["auditid","action","operation","createdon","_objectid_value","objecttypecode","changedata","_userid_value"],...t.length>0?{filter:t.join(" and ")}:{},orderby:"createdon desc",top:e.top,expand:"userid($select=fullname,domainname)"})).value.map(La);return u(`${o.length} audit records for ${e.entityLogicalName??e.recordId??"query"}`,{entries:o,count:o.length},["Filter by operation type for specific changes"])}catch(n){let o=n instanceof Error?n.message:String(n);if(o.includes("403")||o.includes("Forbidden"))return{content:[{type:"text",text:JSON.stringify({isError:!0,error:"Audit log access denied (HTTP 403). Ensure auditing is enabled on the Dataverse environment and the target table, and that the authenticated user has sufficient privileges."})}]};throw n}}default:throw new Error(`Unknown audit tool: ${a}`)}}import{z as M}from"zod";var Ke=[{name:"dataverse_detect_duplicates",description:"Checks for potential duplicate records before creating. Uses Dataverse built-in duplicate detection rules. Pass the prospective record fields to check against existing records. WHEN TO USE: Before creating a new record to check if a duplicate already exists. BEST PRACTICES: Duplicate detection rules must be configured in Dataverse admin; pass key identifying fields only. WORKFLOW: create_record.",inputSchema:{type:"object",properties:{entityLogicalName:{type:"string",description:'Table to check, e.g., "account"'},record:{type:"object",description:"The prospective record fields to check for duplicates"},top:{type:"number",description:"Maximum number of duplicates to return (default 5, max 20)"}},required:["entityLogicalName","record"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],Pa=M.object({entityLogicalName:M.string().min(1).regex(/^[a-z_][a-z0-9_]*$/i,"Must be a valid Dataverse logical name"),record:M.record(M.string(),M.unknown()),top:M.number().int().positive().max(20).optional().default(5)});async function Xe(a,r,i){switch(a){case"dataverse_detect_duplicates":{let n=function(p){return String(p).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")};var e=n;let t=Pa.parse(r),o=Object.entries(t.record).filter(([,p])=>p!=null).map(([p,g])=>`<condition attribute="${n(p)}" operator="eq" value="${n(String(g))}" />`).join(`
|
|
3
|
+
`);if(!o)return u("No fields provided for duplicate detection",{hasDuplicates:!1,duplicateCount:0,duplicates:[]},["Provide at least one field value to check for duplicates"]);let s=t.entityLogicalName+"s",l=`<fetch top="${t.top}" distinct="true">
|
|
4
|
+
<entity name="${n(t.entityLogicalName)}">
|
|
5
|
+
<all-attributes />
|
|
6
|
+
<filter type="or">
|
|
7
|
+
${o}
|
|
8
|
+
</filter>
|
|
9
|
+
</entity>
|
|
10
|
+
</fetch>`,c=await i.executeFetchXml(s,l),m=(Array.isArray(c)?c:Array.isArray(c?.value)?c.value:[]).map(p=>{let g={};for(let[I,Dt]of Object.entries(p))I.startsWith("@")||(g[I]=Dt);return g});return u(`Duplicate detection: ${m.length>0?`${m.length} potential duplicate(s) found`:"no duplicates found"}`,{hasDuplicates:m.length>0,duplicateCount:m.length,duplicates:m,note:"Field exact-match candidates (OR across provided fields). Dataverse duplicate detection rules are not applied via REST API."},["Review candidate records before creating","For rule-based duplicate detection, configure rules in Dataverse admin"])}default:throw new Error(`Unknown quality tool: ${a}`)}}import{z as A}from"zod";var $a=A.object({recordId:A.string().uuid(),includeContent:A.boolean().optional().default(!1),top:A.number().int().positive().max(100).optional().default(20),mimeTypeFilter:A.string().optional()}),Wa=A.object({recordId:A.string().uuid(),entitySetName:b,notetext:A.string().optional(),subject:A.string().optional(),filename:A.string().optional(),mimetype:A.string().optional(),documentbody:A.string().optional()}).refine(a=>!!a.notetext||!!a.documentbody,{message:"At least one of notetext or documentbody is required"});function ja(a){return{opportunities:"opportunity",territories:"territory",categories:"category",queues:"queue",activities:"activitypointer"}[a]??a.replace(/s$/,"")}var Qe=[{name:"dataverse_get_annotations",description:"Retrieves notes and file attachments (annotations) linked to a Dataverse record. Returns note text, file metadata (name, size, MIME type), owner, and timestamps. Set includeContent=true to also retrieve base64 file content (warning: can be very large). WHEN TO USE: Retrieving notes or file attachments linked to a specific record. BEST PRACTICES: Avoid includeContent=true unless you need file data \u2014 base64 payloads can be very large. WORKFLOW: file_operations.",inputSchema:{type:"object",properties:{recordId:{type:"string",description:"The parent record's GUID"},includeContent:{type:"boolean",description:"If true, include documentbody (base64). WARNING: can be very large.",default:!1},top:{type:"number",description:"Maximum number of annotations to return (default 20, max 100)",default:20},mimeTypeFilter:{type:"string",description:'Filter by MIME type (e.g. "application/pdf")'}},required:["recordId"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_create_annotation",description:"Creates a note or file attachment (annotation) linked to a Dataverse record. Provide notetext for a text note, documentbody (base64) for a file attachment, or both. The parent record is identified by entitySetName and recordId. WHEN TO USE: Adding a text note or file attachment to an existing record. BEST PRACTICES: Provide filename and mimetype when attaching files; at least notetext or documentbody is required. WORKFLOW: file_operations.",inputSchema:{type:"object",properties:{recordId:{type:"string",description:"The parent record's GUID"},entitySetName:{type:"string",description:'The OData entity set name of the parent record (e.g., "accounts", "contacts")'},notetext:{type:"string",description:"Text content of the note"},subject:{type:"string",description:"Subject/title of the note"},filename:{type:"string",description:"File name (required when attaching a file)"},mimetype:{type:"string",description:'MIME type of the file (e.g., "application/pdf")'},documentbody:{type:"string",description:"Base64-encoded file content"}},required:["recordId","entitySetName"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}}];async function Ye(a,r,i){switch(a){case"dataverse_get_annotations":{let e=$a.parse(r),t=["annotationid","subject","notetext","filename","filesize","mimetype","isdocument","createdon","modifiedon","_ownerid_value"];e.includeContent&&t.push("documentbody");let n=[`_objectid_value eq ${e.recordId}`];e.mimeTypeFilter&&n.push(`mimetype eq '${v(e.mimeTypeFilter)}'`);let l=((await i.query("annotations",{select:t,filter:n.join(" and "),orderby:"createdon desc",top:e.top})).value??[]).map(c=>{let d={id:c.annotationid??"",subject:c.subject??null,noteText:c.notetext??null,isDocument:c.isdocument===!0,createdOn:c.createdon??"",modifiedOn:c.modifiedon??"",owner:c._ownerid_value??null};return c.isdocument===!0&&(d.fileName=c.filename??null,d.fileSize=c.filesize??null,d.mimeType=c.mimetype??null),e.includeContent&&c.documentbody&&(d.documentBody=c.documentbody),d});return u(`${l.length} annotations found for record ${e.recordId}`,{recordId:e.recordId,annotations:l,count:l.length},["Use dataverse_create_annotation to add a note or attachment"])}case"dataverse_create_annotation":{let e=Wa.parse(r),t=ja(e.entitySetName),n={[`objectid_${t}@odata.bind`]:`/${e.entitySetName}(${e.recordId})`,objecttypecode:t};e.notetext!==void 0&&(n.notetext=e.notetext),e.subject!==void 0&&(n.subject=e.subject),e.filename!==void 0&&(n.filename=e.filename),e.mimetype!==void 0&&(n.mimetype=e.mimetype),e.documentbody!==void 0&&(n.documentbody=e.documentbody,n.isdocument=!0);let o;try{o=await i.createRecord("annotations",n)}catch(s){let l=String(s);if(l.includes("0x80048d19")||l.includes("objectid_")&&l.includes("undeclared"))return $({type:"feature_disabled",feature:"Notes (HasNotes)",cannotProceedBecause:`The table '${t}' does not have Notes enabled (HasNotes=false), so annotations cannot be created.`,adminPortal:"Power Apps Maker Portal",steps:["Open Power Apps maker portal (make.powerapps.com)",`Navigate to Tables \u2192 search for '${t}'`,"Open the table \u2192 click Properties or Settings",'Enable "Notes (includes file attachments)"',"Save the table, then publish customizations"],fixableViaToolName:"dataverse_update_entity"});throw s}return u(`Created annotation ${o}`,{created:!0,annotationId:o,parentRecordId:e.recordId,entitySetName:e.entitySetName},["Use dataverse_get_annotations to list all notes for this record"])}default:throw new Error(`Unknown annotation tool: ${a}`)}}import{z as w}from"zod";var Ma=w.object({userId:w.string().uuid()}),Fa=w.object({search:w.string().optional(),businessUnitId:w.string().optional(),includeDisabled:w.boolean().optional().default(!1),includeApplicationUsers:w.boolean().optional().default(!1),top:w.number().int().positive().max(100).optional().default(20)}),Ba=w.object({nameContains:w.string().optional(),businessUnitId:w.string().optional(),top:w.number().int().positive().max(200).optional().default(50)}),Ga=w.object({userId:w.string().uuid(),roleId:w.string().uuid(),confirm:w.literal(!0,{errorMap:()=>({message:"confirm must be true to assign a role"})})}),za=w.object({userId:w.string().uuid(),roleId:w.string().uuid(),confirm:w.literal(!0,{errorMap:()=>({message:"confirm must be true to remove a role"})})}),Je=[{name:"dataverse_get_user_roles",description:"Returns all security roles assigned to a Dataverse system user. Provide the user GUID to retrieve full name, domain name (UPN), and the list of roles with role ID, name, and managed status. WHEN TO USE: Checking what security roles a user has for permission auditing or troubleshooting. BEST PRACTICES: Use the userId from dataverse_whoami or dataverse_list_users. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{userId:{type:"string",description:"The system user GUID"}},required:["userId"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_list_users",description:"Searches Dataverse system users by name or email. Returns user ID, full name, domain name (UPN), email, business unit, and disabled status. Excludes application users and disabled users by default. WHEN TO USE: Finding user GUIDs for impersonation, record assignment, or permission review. BEST PRACTICES: Search by name or email; set includeDisabled=true for former employees. WORKFLOW: explore_schema.",inputSchema:{type:"object",properties:{search:{type:"string",description:"Full-name or email contains-search"},businessUnitId:{type:"string",description:"Restrict to a business unit (GUID)"},includeDisabled:{type:"boolean",description:"Include disabled users (default false)"},includeApplicationUsers:{type:"boolean",description:"Include application/service users (default false)"},top:{type:"number",description:"Maximum number of results (default 20, max 100)"}}},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_list_roles",description:"Lists security roles in the Dataverse environment. Returns role ID, name, business unit, and managed status. Use this to discover role GUIDs before assigning roles to users or teams via dataverse_assign_role_to_user or dataverse_assign_role_to_team. WHEN TO USE: Looking up security role GUIDs before RBAC assignment. BEST PRACTICES: Filter by businessUnitId or nameContains to narrow results. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{nameContains:{type:"string",description:"Filter roles by name (case-insensitive substring match)"},businessUnitId:{type:"string",description:"Filter roles to a specific business unit (GUID)"},top:{type:"number",description:"Maximum number of roles to return (default 50, max 200)"}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_assign_role_to_user",description:"Assigns a security role to a Dataverse system user. Use dataverse_list_roles to find the role GUID and dataverse_list_users to find the user GUID. Requires System Administrator or System Customizer privileges. WARNING: This operation modifies user permissions. Set confirm=true to proceed. WHEN TO USE: Granting a security role to a user during onboarding or permission changes. BEST PRACTICES: Verify role and user GUIDs first; check existing roles with dataverse_get_user_roles. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{userId:{type:"string",description:"GUID of the system user"},roleId:{type:"string",description:"GUID of the security role to assign"},confirm:{type:"boolean",description:"Must be true to proceed with role assignment"}},required:["userId","roleId","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_remove_role_from_user",description:"Removes a security role from a Dataverse system user. Uses the same N:N disassociation pattern as dataverse_disassociate. WARNING: This modifies user permissions. Set confirm=true to proceed. WHEN TO USE: Revoking access by removing a security role from a user. BEST PRACTICES: Verify the role is assigned first with dataverse_get_user_roles. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{userId:{type:"string",description:"GUID of the system user"},roleId:{type:"string",description:"GUID of the security role to remove"},confirm:{type:"boolean",description:"Must be true to proceed with role removal"}},required:["userId","roleId","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}];async function Ze(a,r,i){switch(a){case"dataverse_get_user_roles":{let{userId:e}=Ma.parse(r),n=(await i.query("systemusers",{filter:`systemuserid eq ${e}`,select:["fullname","domainname"],expand:"systemuserroles_association($select=name,roleid,ismanaged)",top:1})).value??[];if(n.length===0)throw new Error(`User with ID '${e}' not found`);let o=n[0],l=(o.systemuserroles_association??[]).map(c=>({roleId:c.roleid??"",name:c.name??"",isManaged:c.ismanaged===!0}));return u(`${l.length} roles assigned to user`,{userId:e,fullname:o.fullname??"",domainname:o.domainname??"",roles:l,roleCount:l.length},["Use dataverse_impersonate to execute operations as this user"])}case"dataverse_list_users":{let e=Fa.parse(r),t=[];if(e.includeDisabled||t.push("isdisabled eq false"),e.includeApplicationUsers||t.push("applicationid eq null"),e.search){let l=v(e.search);t.push(`(contains(fullname,'${l}') or contains(internalemailaddress,'${l}'))`)}e.businessUnitId&&t.push(`_businessunitid_value eq ${e.businessUnitId}`);let s=((await i.query("systemusers",{select:["systemuserid","fullname","domainname","internalemailaddress","applicationid","isdisabled"],filter:t.join(" and "),expand:"businessunitid($select=name)",orderby:"fullname asc",top:e.top})).value??[]).map(l=>({id:l.systemuserid??"",fullName:l.fullname??"",domainName:l.domainname??"",email:l.internalemailaddress??"",businessUnit:l.businessunitid?.name??null,isDisabled:l.isdisabled===!0,isApplicationUser:l.applicationid!=null}));return u(`${s.length} users found`,{users:s,count:s.length},["Use dataverse_get_user_roles to inspect specific user permissions"])}case"dataverse_list_roles":{let{nameContains:e,businessUnitId:t,top:n}=Ba.parse(r),o=[];e&&o.push(`contains(name,'${v(e)}')`),t&&o.push(`_businessunitid_value eq ${t}`);let s=o.length?o.join(" and "):void 0,c=((await i.query("roles",{select:["roleid","name","description","ismanaged","_businessunitid_value"],...s!==void 0?{filter:s}:{},orderby:"name asc",top:n})).value??[]).map(d=>({id:d.roleid??"",name:d.name??"",businessUnitId:d._businessunitid_value??null,isManaged:d.ismanaged===!0,description:d.description??""}));return u(`${c.length} security roles found`,{roles:c,count:c.length},["Use dataverse_assign_role_to_user or dataverse_assign_role_to_team with the role ID"])}case"dataverse_assign_role_to_user":{let{userId:e,roleId:t}=Ga.parse(r);return(((await i.query("systemusers",{filter:`systemuserid eq ${e}`,select:["systemuserid"],expand:`systemuserroles_association($select=roleid;$filter=roleid eq ${t};$top=1)`,top:1})).value??[])[0]?.systemuserroles_association??[]).length>0?u("Role is already assigned to this user.",{userId:e,roleId:t,status:"already_assigned"},["Use dataverse_get_user_roles to see current role assignments"]):(await i.associate("systemusers",e,"systemuserroles_association","roles",t),u("Role assigned to user successfully.",{userId:e,roleId:t,status:"assigned"},["Use dataverse_get_user_roles to verify the new assignment"]))}case"dataverse_remove_role_from_user":{let{userId:e,roleId:t}=za.parse(r);return(((await i.query("systemusers",{filter:`systemuserid eq ${e}`,select:["systemuserid"],expand:`systemuserroles_association($select=roleid;$filter=roleid eq ${t};$top=1)`,top:1})).value??[])[0]?.systemuserroles_association??[]).length===0?u("This role is not currently assigned to the user.",{userId:e,roleId:t,status:"not_assigned"},["Use dataverse_get_user_roles to see current role assignments"]):(await i.disassociate("systemusers",e,"systemuserroles_association",t,"roles"),u("Role removed from user successfully.",{userId:e,roleId:t,status:"removed"},["Use dataverse_get_user_roles to verify the updated assignments"]))}default:throw new Error(`Unknown user tool: ${a}`)}}import{z as X}from"zod";var Va=X.object({entityLogicalName:X.string().min(1),includePersonal:X.boolean().optional().default(!1),top:X.number().int().positive().max(100).optional().default(20)}),et=[{name:"dataverse_list_views",description:"Lists saved views (system and optionally personal) for a Dataverse table. System views come from savedqueries; personal views come from userqueries. Returns view name, ID, default flag, query type, and description. WHEN TO USE: Discovering saved system or personal views for a table. BEST PRACTICES: Use the view's FetchXML with dataverse_execute_fetchxml to run it. WORKFLOW: explore_schema.",inputSchema:{type:"object",properties:{entityLogicalName:{type:"string",description:'Logical name of the entity to list views for (e.g., "account")'},includePersonal:{type:"boolean",description:"Include personal (user) views in addition to system views (default false)"},top:{type:"number",description:"Maximum number of results per category (default 20, max 100)"}},required:["entityLogicalName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}];async function tt(a,r,i){switch(a){case"dataverse_list_views":{let{entityLogicalName:e,includePersonal:t,top:n}=Va.parse(r),o=v(e),l=(await i.query("savedqueries",{filter:`returnedtypecode eq '${o}' and statecode eq 0`,select:["savedqueryid","name","isdefault","querytype","description"],orderby:"name asc",top:n})).value.map(m=>({id:m.savedqueryid,name:m.name,isDefault:m.isdefault,queryType:m.querytype,description:m.description??null,viewType:"system"})),c=[];t&&(c=(await i.query("userqueries",{filter:`returnedtypecode eq '${o}'`,select:["userqueryid","name","description"],orderby:"name asc",top:n})).value.map(p=>({id:p.userqueryid,name:p.name,description:p.description??null,viewType:"personal"})));let d=l.length+c.length;return u(`${d} views found for ${e}`,{entityLogicalName:e,systemViews:l,systemViewCount:l.length,personalViews:t?c:void 0,personalViewCount:t?c.length:void 0},["Use the view's fetchxml with dataverse_execute_fetchxml to run it"])}default:throw new Error(`Unknown view tool: ${a}`)}}import{z as oe}from"zod";var Ka=oe.object({top:oe.number().int().positive().max(200).optional().default(50),includeDisabled:oe.boolean().optional().default(!1)}),at=[{name:"dataverse_list_business_units",description:"Lists business units in the Dataverse environment. Returns name, ID, parent business unit ID, disabled status, and creation date. By default only active business units are returned. WHEN TO USE: Finding business unit IDs for user management, team setup, or security role scope. BEST PRACTICES: The root business unit is the parent of all others; filter disabled units by default. WORKFLOW: explore_schema.",inputSchema:{type:"object",properties:{top:{type:"number",description:"Maximum number of results (default 50, max 200)"},includeDisabled:{type:"boolean",description:"Include disabled business units (default false)"}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}];async function nt(a,r,i){switch(a){case"dataverse_list_business_units":{let{top:e,includeDisabled:t}=Ka.parse(r??{}),n=t?void 0:"isdisabled eq false",s=(await i.query("businessunits",{select:["businessunitid","name","parentbusinessunitid","isdisabled","createdon"],...n?{filter:n}:{},orderby:"name asc",top:e})).value.map(l=>({id:l.businessunitid,name:l.name,parentBusinessUnitId:l.parentbusinessunitid??null,isDisabled:l.isdisabled,createdOn:l.createdon}));return u(`${s.length} business units found`,{businessUnits:s,count:s.length},["Use business unit IDs for user management or security role assignment"])}default:throw new Error(`Unknown org tool: ${a}`)}}import{z as O}from"zod";var rt=/^[a-zA-Z0-9_]+$/,ot=/\.\.|[/\\]/,Xa=O.object({entitySetName:O.string().min(1).refine(a=>!ot.test(a),{message:"entitySetName must not contain path traversal characters"}),recordId:O.string().uuid(),columnName:O.string().min(1).refine(a=>rt.test(a),{message:"columnName must be alphanumeric/underscore only"}),fileContent:O.string().min(1).describe("Base64-encoded file content"),fileName:O.string().min(1)}),Qa=O.object({entitySetName:O.string().min(1).refine(a=>!ot.test(a),{message:"entitySetName must not contain path traversal characters"}),recordId:O.string().uuid(),columnName:O.string().min(1).refine(a=>rt.test(a),{message:"columnName must be alphanumeric/underscore only"})}),it=[{name:"dataverse_upload_file_column",description:"Uploads a file to a Dataverse file column. Provide the entity set name, record GUID, column name, base64-encoded file content, and file name. The file is stored in the specified file-type column on the record. WHEN TO USE: Uploading a file to a file-type column on a Dataverse record. BEST PRACTICES: Base64-encode the file content; verify the column is a file-type column via dataverse_get_table_metadata. WORKFLOW: file_operations.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:'OData entity set name (e.g., "accounts")'},recordId:{type:"string",description:"Record GUID"},columnName:{type:"string",description:"File column logical name"},fileContent:{type:"string",description:"Base64-encoded file content"},fileName:{type:"string",description:'File name including extension (e.g., "report.pdf")'}},required:["entitySetName","recordId","columnName","fileContent","fileName"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_download_file_column",description:"Downloads a file from a Dataverse file column. Returns the file content as a base64-encoded string along with the file name and size. WHEN TO USE: Downloading a file stored in a file-type column on a Dataverse record. BEST PRACTICES: The response is base64-encoded; decode before saving or processing. WORKFLOW: file_operations.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:'OData entity set name (e.g., "accounts")'},recordId:{type:"string",description:"Record GUID"},columnName:{type:"string",description:"File column logical name"}},required:["entitySetName","recordId","columnName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}];async function st(a,r,i){let e=i.http;switch(a){case"dataverse_upload_file_column":{let{entitySetName:t,recordId:n,columnName:o,fileContent:s,fileName:l}=Xa.parse(r),c=Buffer.from(s,"base64"),d=`${t}(${n})/${o}`;return await e.patch(d,c,{headers:{"Content-Type":"application/octet-stream","x-ms-file-name":l}}),u(`Uploaded file to ${o} on ${t}(${n})`,{success:!0,entitySetName:t,recordId:n,columnName:o,fileName:l,sizeBytes:c.length},["Use dataverse_download_file_column to retrieve the file"])}case"dataverse_download_file_column":{let{entitySetName:t,recordId:n,columnName:o}=Qa.parse(r),s=`${t}(${n})/${o}/$value`,l=await e.get(s,{responseType:"text"}),c=l.data,d=Buffer.from(c,"binary").toString("base64"),m=l.headers["x-ms-file-name"]??l.headers["content-disposition"]?.match(/filename="?([^";\n]+)"?/)?.[1]??"download",p=parseInt(l.headers["content-length"]??"0",10)||d.length;return u(`Downloaded file from ${o} on ${t}(${n})`,{entitySetName:t,recordId:n,columnName:o,fileName:m,sizeBytes:p,contentBase64:d})}default:throw new Error(`Unknown file tool: ${a}`)}}import{z as E}from"zod";var Ya={0:"Owner",1:"Access",2:"Office",3:"Security"},Ja=E.object({top:E.number().int().positive().max(200).optional().default(50),teamType:E.union([E.literal(0),E.literal(1),E.literal(2),E.literal(3)]).optional()}),Za=E.object({teamId:E.string().uuid(),roleId:E.string().uuid(),confirm:E.literal(!0,{errorMap:()=>({message:"confirm must be true to assign a role to a team"})})}),lt=[{name:"dataverse_list_teams",description:"Lists Dataverse teams in the environment. Useful for finding team owners for record assignment and sharing. teamtype: 0=Owner, 1=Access, 2=AAD Office Group, 3=AAD Security Group. WHEN TO USE: Finding team IDs for record assignment or sharing. BEST PRACTICES: Filter by teamType to distinguish owner, access, and AAD group teams. WORKFLOW: explore_schema.",inputSchema:{type:"object",properties:{top:{type:"number",description:"Maximum number of teams to return (1\u2013200, default 50)"},teamType:{type:"number",enum:[0,1,2,3],description:"Filter by team type: 0=Owner, 1=Access, 2=Office Group, 3=Security Group"}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_assign_role_to_team",description:"Assigns a security role to a Dataverse team. All team members inherit the role permissions. Use dataverse_list_roles to find the role GUID and dataverse_list_teams to find the team GUID. WARNING: This modifies team permissions for ALL members. Set confirm=true to proceed. WHEN TO USE: Providing role-based permissions to an entire team during configuration. BEST PRACTICES: Prefer team-based RBAC over individual user assignments for maintainability. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{teamId:{type:"string",description:"GUID of the team"},roleId:{type:"string",description:"GUID of the security role to assign"},confirm:{type:"boolean",description:"Must be true to proceed with role assignment"}},required:["teamId","roleId","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}}];async function dt(a,r,i){switch(a){case"dataverse_list_teams":{let{top:e,teamType:t}=Ja.parse(r),o={select:["teamid","name","teamtype","description","isdefault","createdon","_businessunitid_value"],top:e,orderby:"name asc"};t!==void 0&&(o.filter=`teamtype eq ${t}`);let l=(await i.query("teams",o)).value.map(c=>({...c,teamTypeName:Ya[c.teamtype]??"Unknown"}));return u(`${l.length} teams found`,{teams:l,count:l.length},["Use dataverse_assign with a team ID to assign records to a team"])}case"dataverse_assign_role_to_team":{let{teamId:e,roleId:t}=Za.parse(r);try{await i.associate("teams",e,"teamroles_association","roles",t)}catch(n){let o=n instanceof Error?n.message:String(n);if(o.toLowerCase().includes("duplicate")||o.includes("0x80040237"))return u("Role is already assigned to this team.",{teamId:e,roleId:t,status:"already_assigned"},["Use dataverse_list_teams to view current team configuration"]);throw n}return u("Role assigned to team successfully.",{teamId:e,roleId:t,status:"assigned"},["All members of this team now inherit the assigned role"])}default:throw new Error(`Unknown team tool: ${a}`)}}var G={dataverse_query:["query","read","odata"],dataverse_execute_fetchxml:["query","read","fetchxml","aggregate"],dataverse_create:["write","crud","create"],dataverse_update:["write","crud","update"],dataverse_delete:["write","crud","delete","destructive"],dataverse_get:["read","crud","get"],dataverse_search:["search","read","fulltext"],dataverse_list_tables:["metadata","schema","read"],dataverse_get_table_metadata:["metadata","schema","read"],dataverse_get_relationships:["metadata","schema","relations","read"],dataverse_list_views:["metadata","views","read"],dataverse_get_view_definition:["metadata","views","read"],dataverse_batch_execute:["write","bulk","batch"],dataverse_whoami:["auth","identity","read"],dataverse_list_solutions:["solutions","read","alm"],dataverse_get_solution_components:["solutions","read","alm"],dataverse_export_solution:["solutions","write","alm"],dataverse_import_solution:["solutions","write","alm"],dataverse_track_changes:["sync","delta","read"],dataverse_execute_action:["actions","write","custom"],dataverse_execute_bound_action:["actions","write","custom"],dataverse_publish_customizations:["customization","write"],dataverse_upsert:["write","crud","upsert"],dataverse_count:["query","read","count"],dataverse_retrieve_multiple_with_paging:["query","read","paging"],dataverse_audit_get_history:["audit","read"],dataverse_audit_get_detail:["audit","read"],dataverse_list_users:["admin","users","read"],dataverse_get_user_roles:["admin","users","security","read"],dataverse_get_user_teams:["admin","users","teams","read"],dataverse_list_teams:["admin","teams","read"],dataverse_get_team_members:["admin","teams","read"],dataverse_upload_file:["files","write","upload"],dataverse_download_file:["files","read","download"],dataverse_delete_file:["files","write","delete"],dataverse_associate:["relations","write"],dataverse_disassociate:["relations","write"],dataverse_impersonate:["security","admin"],dataverse_impersonate_clear:["security","admin"],dataverse_list_environments:["admin","environments","read"],dataverse_get_environment_details:["admin","environments","read"],dataverse_get_environment_settings:["admin","environments","read"],dataverse_get_trace_logs:["debug","trace","read"],dataverse_get_plugin_types:["debug","plugins","read"],dataverse_get_sdkmessageprocessingsteps:["debug","plugins","read"],dataverse_get_workflow_definitions:["debug","workflows","read"],dataverse_validate_entity:["quality","read","validation"],dataverse_list_duplicate_rules:["quality","read"],dataverse_list_notes:["notes","read"],dataverse_create_note:["notes","write"],dataverse_get_org_details:["admin","org","read"],dataverse_get_org_settings:["admin","org","read"],dataverse_get_global_option_set:["metadata","schema","read"],dataverse_publish_component:["customization","write"],dataverse_get_entity_xml:["customization","metadata","read"],dataverse_create_attribute:["metadata","schema","write","attribute"],dataverse_update_attribute:["metadata","schema","write","attribute"],dataverse_delete_attribute:["metadata","schema","write","attribute","destructive"]},ie={dataverse_query:"Query Dataverse tables using OData syntax",dataverse_execute_fetchxml:"Execute FetchXML queries with aggregation support",dataverse_create:"Create a new record in a Dataverse table",dataverse_update:"Update an existing Dataverse record",dataverse_delete:"Delete a Dataverse record",dataverse_get:"Retrieve a single record by ID",dataverse_search:"Full-text search across Dataverse tables",dataverse_list_tables:"List available Dataverse tables",dataverse_get_table_metadata:"Get column definitions for a table",dataverse_get_relationships:"Get relationships for a table",dataverse_list_views:"List saved views for a table",dataverse_get_view_definition:"Get the definition of a saved view",dataverse_batch_execute:"Execute multiple operations in a batch",dataverse_whoami:"Get current authenticated user context",dataverse_list_solutions:"List solutions in the environment",dataverse_get_solution_components:"Get components of a solution",dataverse_export_solution:"Export a solution as a ZIP file",dataverse_import_solution:"Import a solution from a ZIP file",dataverse_track_changes:"Track changes (delta sync) for a table",dataverse_execute_action:"Execute a custom unbound action",dataverse_execute_bound_action:"Execute a custom bound action",dataverse_publish_customizations:"Publish all customizations",dataverse_upsert:"Create or update a record by alternate key",dataverse_count:"Count records in a table",dataverse_retrieve_multiple_with_paging:"Query with automatic paging",dataverse_audit_get_history:"Get audit history for a record",dataverse_audit_get_detail:"Get audit detail for a specific change",dataverse_list_users:"List system users",dataverse_get_user_roles:"Get security roles for a user",dataverse_get_user_teams:"Get teams a user belongs to",dataverse_list_teams:"List teams in the environment",dataverse_get_team_members:"Get members of a team",dataverse_upload_file:"Upload a file to a record",dataverse_download_file:"Download a file from a record",dataverse_delete_file:"Delete a file from a record",dataverse_associate:"Associate two records via a relationship",dataverse_disassociate:"Remove association between two records",dataverse_impersonate:"Set impersonation for subsequent calls",dataverse_impersonate_clear:"Clear impersonation",dataverse_list_environments:"List available environments",dataverse_get_environment_details:"Get environment details",dataverse_get_environment_settings:"Get environment settings",dataverse_get_trace_logs:"Get plugin trace logs",dataverse_get_plugin_types:"Get registered plugin types",dataverse_get_sdkmessageprocessingsteps:"Get SDK message processing steps",dataverse_get_workflow_definitions:"Get workflow/flow definitions",dataverse_validate_entity:"Validate entity configuration quality",dataverse_list_duplicate_rules:"List duplicate detection rules",dataverse_list_notes:"List notes/annotations on a record",dataverse_create_note:"Create a note on a record",dataverse_get_org_details:"Get organization details",dataverse_get_org_settings:"Get organization settings",dataverse_get_global_option_set:"Get a global option set definition",dataverse_publish_component:"Publish a single component",dataverse_get_entity_xml:"Get entity customization XML",dataverse_create_attribute:"Create a column on a Dataverse table",dataverse_update_attribute:"Update mutable properties of a column",dataverse_delete_attribute:"Delete a column and all its data from a table"};function en(a){return a.toLowerCase().replace(/[^a-z0-9_]/g," ").split(/\s+/).filter(r=>r.length>1)}function tn(a,r){let i=G[a]??[],e=(ie[a]??"").toLowerCase(),t=a.toLowerCase(),n=0;for(let o of r)i.includes(o)&&(n+=3),t.includes(o)&&(n+=2),e.includes(o)&&(n+=1);return n}function an(a){let r=en(a);if(r.length===0)return["dataverse_whoami","dataverse_list_tables","dataverse_query","dataverse_search","dataverse_get_table_metadata"].map(t=>({name:t,description:ie[t]??"",tags:G[t]??[],score:1}));let i=[];for(let e of Object.keys(G)){let t=tn(e,r);t>0&&i.push({name:e,description:ie[e]??"",tags:G[e]??[],score:t})}return i.sort((e,t)=>t.score-e.score),i.slice(0,5)}function nn(){let a=new Map;for(let r of Object.values(G))for(let i of r)a.set(i,(a.get(i)??0)+1);return Array.from(a.entries()).map(([r,i])=>({tag:r,toolCount:i})).sort((r,i)=>i.toolCount-r.toolCount)}var ct=[{name:"dataverse_suggest_tools",description:"Suggests the most relevant Dataverse tools for a given intent. Provide a natural-language description of what you want to do and this tool returns the top 5 matching tools with descriptions and tags.",inputSchema:{type:"object",properties:{intent:{type:"string",description:"Natural language description of the desired action"}},required:["intent"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},{name:"dataverse_list_tool_tags",description:"Lists all available tool tags with the number of tools in each category. Use this to discover what kinds of operations are available.",inputSchema:{type:"object",properties:{},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}}];async function ut(a,r,i){if(a==="dataverse_suggest_tools"){let{intent:e}=r??{},t=an(e??""),n=t.length>0?[`Try: ${t[0].name}`]:["Use dataverse_list_tool_tags to explore categories"];return u(`${t.length} tools match intent "${e??""}"`,t,n)}if(a==="dataverse_list_tool_tags"){let e=nn();return C("tags",e,["Use dataverse_suggest_tools with an intent to find specific tools"])}throw new Error(`Unknown router tool: ${a}`)}var rn=[{uriTemplate:"dataverse://tables/{tableName}/schema",name:"Table Schema",description:"Returns the full schema (columns, types, requirements) for a Dataverse table",mimeType:"application/json"},{uriTemplate:"dataverse://tables/{tableName}/relationships",name:"Table Relationships",description:"Returns all 1:N and N:N relationships for a Dataverse table",mimeType:"application/json"}],on=[{uri:"dataverse://tables",name:"Available Tables",description:"Lists all custom tables in the connected Dataverse environment",mimeType:"application/json"},{uri:"dataverse://server/instructions",name:"Server Instructions",description:"Usage guidelines and best practices for interacting with this Dataverse MCP server",mimeType:"text/plain"}];function sn(a){let r="dataverse://";if(!a.startsWith(r))throw new Error(`Unknown resource URI: ${a}`);let i=a.slice(r.length);if(i==="tables")return{type:"tables"};if(i==="server/instructions")return{type:"instructions"};let e=/^tables\/([^/]+)\/schema$/.exec(i);if(e)return{type:"schema",tableName:e[1]};let t=/^tables\/([^/]+)\/relationships$/.exec(i);if(t)return{type:"relationships",tableName:t[1]};throw new Error(`Unknown resource URI: ${a}`)}function pt(){return on}function mt(){return rn}async function ft(a,r,i){let e=sn(a);switch(e.type){case"tables":{let t=await r.listTables(!0);return{uri:a,mimeType:"application/json",text:JSON.stringify(t,null,2)}}case"schema":{let t=await r.getTableMetadata(e.tableName);return{uri:a,mimeType:"application/json",text:JSON.stringify(t,null,2)}}case"relationships":{let t=await r.getRelationships(e.tableName);return{uri:a,mimeType:"application/json",text:JSON.stringify(t,null,2)}}case"instructions":return{uri:a,mimeType:"text/plain",text:i}}}var Q=new Map([["query_data",{name:"query_data",description:"Query data from Dataverse tables. Authenticate, discover tables, inspect schema, then query with OData or FetchXML.",tags:["query","data","fetchxml","odata","read"],steps:[{order:1,tool:"dataverse_whoami",description:"Verify authentication and get user context",required:!0},{order:2,tool:"dataverse_list_tables",description:"Discover available tables",required:!0,tips:"Use customOnly=true to focus on custom tables"},{order:3,tool:"dataverse_get_table_metadata",description:"Inspect column names, types, and required fields",required:!0,tips:"Note logical names for use in $select and $filter"},{order:4,tool:"dataverse_query",description:"Execute OData query or use dataverse_execute_fetchxml for complex joins",required:!0,tips:"Always use $select to limit columns; use $top to limit rows"}]}],["explore_schema",{name:"explore_schema",description:"Explore the Dataverse schema: tables, columns, relationships, and saved views.",tags:["schema","metadata","tables","relationships","views"],steps:[{order:1,tool:"dataverse_whoami",description:"Verify authentication",required:!0},{order:2,tool:"dataverse_list_tables",description:"List all available tables",required:!0},{order:3,tool:"dataverse_get_table_metadata",description:"Get detailed column metadata for a table",required:!0},{order:4,tool:"dataverse_get_relationships",description:"Discover one-to-many and many-to-many relationships",required:!1,tips:"Useful before building FetchXML joins"},{order:5,tool:"dataverse_list_views",description:"List saved views for the table",required:!1}]}],["create_record",{name:"create_record",description:"Create a new record in Dataverse. Inspect schema first to know required fields, then create and verify.",tags:["create","record","write","insert"],steps:[{order:1,tool:"dataverse_get_table_metadata",description:"Check required fields and column types before creating",required:!0,tips:"Pay attention to required fields and lookup columns"},{order:2,tool:"dataverse_create",description:"Create the new record",required:!0},{order:3,tool:"dataverse_query",description:"Verify the record was created with correct values",required:!1,tips:"Query by the returned ID to confirm field values"}]}],["update_record",{name:"update_record",description:"Update an existing record. Read current values, apply changes, then verify the update.",tags:["update","record","write","modify"],steps:[{order:1,tool:"dataverse_get_table_metadata",description:"Confirm column names and types",required:!0},{order:2,tool:"dataverse_get",description:"Read current record values and etag",required:!0,tips:"Capture @odata.etag for optimistic concurrency"},{order:3,tool:"dataverse_update",description:"Apply the update with etag for concurrency control",required:!0},{order:4,tool:"dataverse_get",description:"Verify the update was applied correctly",required:!1}]}],["delete_record",{name:"delete_record",description:"Delete a record from Dataverse. Read the record first to confirm identity, then delete.",tags:["delete","record","remove"],steps:[{order:1,tool:"dataverse_get",description:"Read the record to confirm it is the correct one",required:!0,tips:"Show record details to the user before deletion"},{order:2,tool:"dataverse_delete",description:"Delete the record (irreversible)",required:!0,tips:"Always confirm with the user before executing this step"}]}],["bulk_operations",{name:"bulk_operations",description:"Perform bulk create/update/delete using Dataverse batch API for efficiency.",tags:["bulk","batch","mass","multiple"],steps:[{order:1,tool:"dataverse_get_table_metadata",description:"Inspect schema to build correct request payloads",required:!0},{order:2,tool:"dataverse_batch_execute",description:"Execute batch of operations in a single request",required:!0,tips:"Group up to 1000 operations per batch; use changesets for transactional groups"}]}],["search_data",{name:"search_data",description:"Full-text search across Dataverse tables, then refine results with targeted queries.",tags:["search","find","fulltext","relevance"],steps:[{order:1,tool:"dataverse_search",description:"Execute full-text search across multiple tables",required:!0,tips:"Use entities filter to narrow which tables to search"},{order:2,tool:"dataverse_query",description:"Refine results with a targeted OData query on the relevant table",required:!1}]}],["manage_solution",{name:"manage_solution",description:"Inspect and manage Dataverse solutions and their components.",tags:["solution","customization","deployment","components"],steps:[{order:1,tool:"dataverse_list_solutions",description:"List all solutions in the environment",required:!0},{order:2,tool:"dataverse_solution_components",description:"List components within a specific solution",required:!0},{order:3,tool:"dataverse_publish_customizations",description:"Publish customizations after changes",required:!1,tips:"Required after modifying solution components"}]}],["inspect_audit",{name:"inspect_audit",description:"Review audit history for records or tables to track changes and user activity.",tags:["audit","history","changes","tracking"],steps:[{order:1,tool:"dataverse_get_audit_log",description:"Retrieve audit entries filtered by record, table, user, or date range",required:!0,tips:"Use at least one filter to avoid large result sets; audit must be enabled on the table"}]}],["file_operations",{name:"file_operations",description:"Upload or download files from Dataverse file/image columns.",tags:["file","upload","download","image","attachment"],steps:[{order:1,tool:"dataverse_get_table_metadata",description:"Identify file or image columns on the table",required:!0,tips:"Look for columns of type File or Image"},{order:2,tool:"dataverse_upload_file_column",description:"Upload a file to a file/image column",required:!1},{order:3,tool:"dataverse_download_file_column",description:"Download a file from a file/image column",required:!1,tips:"Returns base64-encoded content"}]}]]);var yt=[{name:"dataverse_list_guides",description:"Lists all available step-by-step guided workflows for common Dataverse tasks (query_data, create_record, manage_solution, etc.). Use this to discover recommended MCP patterns. NOTE: these are MCP-internal task guides, NOT Dataverse automation processes \u2014 use dataverse_list_workflows to list actual Dataverse flows/workflows.",inputSchema:{type:"object",properties:{},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0}},{name:"dataverse_get_guide",description:"Returns the full definition of an MCP task guide including ordered steps, tool names, tips, and required flags. Use this to get a step-by-step plan for a common Dataverse task. NOTE: these are MCP-internal guides \u2014 use dataverse_get_workflow to retrieve an actual Dataverse process.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Guide name (e.g. query_data, explore_schema, create_record, update_record, delete_record, bulk_operations, search_data, manage_solution, inspect_audit, file_operations)"}},required:["name"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0}},{name:"dataverse_list_workflows",description:"Lists automation processes stored in the Dataverse environment: Power Automate cloud flows, classic workflows, business rules, business process flows, and actions. Optionally filter by category or partial name.",inputSchema:{type:"object",properties:{category:{type:"number",description:"Filter by process category: 0=Classic Workflow, 1=Dialog, 2=Business Rule, 3=Action, 4=Business Process Flow, 6=Power Automate (Cloud Flow). Omit to list all categories."},nameContains:{type:"string",description:"Filter by partial name match (case-insensitive contains)."},top:{type:"number",description:"Maximum number of results to return (default 50)."}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0}},{name:"dataverse_get_workflow",description:"Returns full details of a Dataverse automation process (workflow, cloud flow, business rule, or action) by its GUID.",inputSchema:{type:"object",properties:{workflowId:{type:"string",description:"GUID of the workflow/process record"}},required:["workflowId"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0}}],gt={0:"Classic Workflow",1:"Dialog",2:"Business Rule",3:"Action",4:"Business Process Flow",6:"Power Automate (Cloud Flow)"};async function vt(a,r,i){if(a==="dataverse_list_guides"){let e=Array.from(Q.values()).map(t=>({name:t.name,description:t.description,tags:t.tags,stepCount:t.steps.length}));return C("guides",e,["Use dataverse_get_guide with a guide name to see detailed steps"])}if(a==="dataverse_get_guide"){let{name:e}=r,t=Q.get(e);if(!t){let n=Array.from(Q.keys()).join(", ");throw new Error(`Unknown guide: "${e}". Available guides: ${n}`)}return u(`Guide "${t.name}": ${t.description}`,t,t.steps.map(n=>`Step ${n.order}: ${n.tool} \u2014 ${n.description}`))}if(a==="dataverse_list_workflows"){let e=r,n=(await i.listDataverseWorkflows({...e.category!==void 0?{category:e.category}:{},...e.nameContains!==void 0?{nameContains:e.nameContains}:{},top:Math.min(e.top??50,200)})).map(o=>({workflowId:o.workflowid,name:o.name,category:gt[o.category]??o.category,state:o.statecode===1?"Active":"Inactive",modifiedOn:o.modifiedon}));return C(`Dataverse workflows (${n.length} found)`,n,["Use dataverse_get_workflow with a workflowId GUID for full details","Filter with category: 0=Classic, 2=Business Rule, 4=BPF, 6=Cloud Flow"])}if(a==="dataverse_get_workflow"){let{workflowId:e}=r,t=await i.getDataverseWorkflow(e),n=t.category,o={...t,categoryLabel:gt[n]??`Category ${n}`,stateLabel:t.statecode===1?"Active":"Inactive"};return u(`Workflow: ${t.name}`,o,[])}throw new Error(`Unknown workflow tool: ${a}`)}import{z as f}from"zod";var z=/^[a-z_][a-z0-9_]*$/,se=["String","Memo","Integer","Decimal","Money","DateTime","Boolean","Picklist"],ln={String:"Microsoft.Dynamics.CRM.StringAttributeMetadata",Memo:"Microsoft.Dynamics.CRM.MemoAttributeMetadata",Integer:"Microsoft.Dynamics.CRM.IntegerAttributeMetadata",Decimal:"Microsoft.Dynamics.CRM.DecimalAttributeMetadata",Money:"Microsoft.Dynamics.CRM.MoneyAttributeMetadata",DateTime:"Microsoft.Dynamics.CRM.DateTimeAttributeMetadata",Boolean:"Microsoft.Dynamics.CRM.BooleanAttributeMetadata",Picklist:"Microsoft.Dynamics.CRM.PicklistAttributeMetadata"},ht=f.object({entityLogicalName:f.string().min(1).regex(z,"Invalid Dataverse logical name"),schemaName:f.string().min(1).regex(/^[a-zA-Z_][a-zA-Z0-9_]*$/,"Invalid schema name"),attributeType:f.enum(se),displayName:f.string().min(1),description:f.string().optional(),requiredLevel:f.enum(["None","ApplicationRequired","Recommended"]).optional().default("None"),maxLength:f.number().int().positive().optional(),minValue:f.number().optional(),maxValue:f.number().optional(),precision:f.number().int().min(0).max(10).optional(),dateTimeFormat:f.enum(["DateOnly","DateAndTime"]).optional().default("DateAndTime"),defaultBooleanValue:f.boolean().optional().default(!1),picklistOptions:f.array(f.object({value:f.number().int(),label:f.string().min(1)})).optional(),languageCode:f.number().int().optional().default(1033),autoPublish:f.boolean().optional().default(!0),confirm:f.literal(!0,{errorMap:()=>({message:"Set confirm: true to create a column"})})}).refine(a=>a.attributeType!=="Picklist"||a.picklistOptions&&a.picklistOptions.length>0,{message:"picklistOptions is required for Picklist type and must have at least one option"}),bt=f.object({entityLogicalName:f.string().min(1).regex(z,"Invalid Dataverse logical name"),attributeLogicalName:f.string().min(1).regex(z,"Invalid Dataverse logical name"),displayName:f.string().min(1).optional(),description:f.string().optional(),requiredLevel:f.enum(["None","ApplicationRequired","Recommended"]).optional(),maxLength:f.number().int().positive().optional(),isSearchable:f.boolean().optional(),languageCode:f.number().int().optional().default(1033),autoPublish:f.boolean().optional().default(!0),confirm:f.literal(!0,{errorMap:()=>({message:"Set confirm: true to update column metadata"})})}).refine(a=>a.displayName!==void 0||a.description!==void 0||a.requiredLevel!==void 0||a.maxLength!==void 0||a.isSearchable!==void 0,{message:"At least one mutable property (displayName, description, requiredLevel, maxLength, isSearchable) must be provided"}),_t=f.object({entityLogicalName:f.string().min(1).regex(z,"Invalid Dataverse logical name"),attributeLogicalName:f.string().min(1).regex(z,"Invalid Dataverse logical name"),autoPublish:f.boolean().optional().default(!0),confirm:f.literal(!0,{errorMap:()=>({message:"Set confirm: true to delete a column \u2014 this is irreversible"})})});function L(a,r){return{"@odata.type":"Microsoft.Dynamics.CRM.Label",LocalizedLabels:[{Label:a,LanguageCode:r}],UserLocalizedLabel:{Label:a,LanguageCode:r}}}function wt(a){let r=a.languageCode,i={"@odata.type":ln[a.attributeType],SchemaName:a.schemaName,DisplayName:L(a.displayName,r),RequiredLevel:{Value:a.requiredLevel}};switch(a.description&&(i.Description=L(a.description,r)),a.attributeType){case"String":i.MaxLength=a.maxLength??100;break;case"Memo":i.MaxLength=a.maxLength??4e3;break;case"Integer":i.Format="None",a.minValue!==void 0&&(i.MinValue=a.minValue),a.maxValue!==void 0&&(i.MaxValue=a.maxValue);break;case"Decimal":a.precision!==void 0&&(i.Precision=a.precision),a.minValue!==void 0&&(i.MinValue=a.minValue),a.maxValue!==void 0&&(i.MaxValue=a.maxValue);break;case"Money":i.PrecisionSource=a.precision!==void 0?0:2,a.precision!==void 0&&(i.Precision=a.precision);break;case"DateTime":i.Format=a.dateTimeFormat;break;case"Boolean":i.DefaultValue=a.defaultBooleanValue,i.OptionSet={TrueOption:{Value:1,Label:L("Yes",r)},FalseOption:{Value:0,Label:L("No",r)}};break;case"Picklist":i.OptionSet={"@odata.type":"Microsoft.Dynamics.CRM.OptionSetMetadata",IsGlobal:!1,OptionSetType:"Picklist",Options:a.picklistOptions.map(e=>({Value:e.value,Label:L(e.label,r)}))};break}return i}var Tt=[{name:"dataverse_create_attribute",description:"Creates a new column (attribute) on an existing Dataverse table. Supports String, Memo, Integer, Decimal, Money, DateTime, Boolean, and Picklist types. IMPORTANT: Column type CANNOT be changed after creation \u2014 choose carefully. Requires System Customizer or System Administrator privileges. WHEN TO USE: Adding a new column to a table. BEST PRACTICES: Use dataverse_get_table_metadata first to check existing columns. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{entityLogicalName:{type:"string",description:"Logical name of the target table (e.g. 'account', 'new_mytable')"},schemaName:{type:"string",description:"Schema name for the new column (e.g. 'new_mycolumn'). Must include publisher prefix."},attributeType:{type:"string",enum:se,description:"Column data type. CANNOT be changed after creation. String=single-line text, Memo=multi-line, Integer=whole number, Decimal=decimal number, Money=currency, DateTime=date/time, Boolean=yes/no, Picklist=choice/option set."},displayName:{type:"string",description:"Display label for the column"},description:{type:"string",description:"Optional description of the column"},requiredLevel:{type:"string",enum:["None","ApplicationRequired","Recommended"],description:"Required level. Default: None"},maxLength:{type:"number",description:"Max length for String (default 100, max 4000) or Memo (default 4000, max 1048576) types"},minValue:{type:"number",description:"Minimum value for Integer or Decimal types"},maxValue:{type:"number",description:"Maximum value for Integer or Decimal types"},precision:{type:"number",description:"Decimal precision for Decimal (0-10) or Money types"},dateTimeFormat:{type:"string",enum:["DateOnly","DateAndTime"],description:"Format for DateTime type. CANNOT be changed after creation. Default: DateAndTime"},defaultBooleanValue:{type:"boolean",description:"Default value for Boolean type. Default: false"},picklistOptions:{type:"array",items:{type:"object",properties:{value:{type:"number",description:"Integer value for the option"},label:{type:"string",description:"Display label for the option"}},required:["value","label"]},description:"Options for Picklist type. Each option needs a value (integer) and label (text)."},languageCode:{type:"number",description:"Language code for labels. Default: 1033 (English)"},autoPublish:{type:"boolean",description:"Publish the customization after creation (default: true)."},confirm:{type:"boolean",description:"Must be true \u2014 confirms intentional schema modification"}},required:["entityLogicalName","schemaName","attributeType","displayName","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}},{name:"dataverse_update_attribute",description:"Updates mutable properties of an existing column (attribute): display name, description, required level, max length (increase only for String/Memo), and search indexing. CANNOT change: column type, logical name, DateTime format, or lookup targets \u2014 these are immutable after creation. Requires System Customizer or System Administrator privileges. WHEN TO USE: Changing a column's label, description, or required level. BEST PRACTICES: Use dataverse_get_table_metadata first to see current values. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{entityLogicalName:{type:"string",description:"Logical name of the table"},attributeLogicalName:{type:"string",description:"Logical name of the column to update"},displayName:{type:"string",description:"New display label for the column"},description:{type:"string",description:"New description for the column"},requiredLevel:{type:"string",enum:["None","ApplicationRequired","Recommended"],description:"New required level"},maxLength:{type:"number",description:"New max length for String/Memo columns. Can only INCREASE, never decrease."},isSearchable:{type:"boolean",description:"Enable or disable Dataverse Search indexing on this column"},languageCode:{type:"number",description:"Language code for labels. Default: 1033 (English)"},autoPublish:{type:"boolean",description:"Publish the customization after update (default: true)."},confirm:{type:"boolean",description:"Must be true \u2014 confirms intentional schema modification"}},required:["entityLogicalName","attributeLogicalName","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_delete_attribute",description:"Deletes a column (attribute) from a Dataverse table. WARNING: This permanently deletes the column AND all its data. Automatically checks dependencies (views, workflows, forms) before deletion \u2014 refuses if dependencies exist unless force=true. Managed solution columns cannot be deleted. WHEN TO USE: Removing an obsolete custom column. BEST PRACTICES: Use dataverse_get_table_metadata to verify the column exists; check dependencies first. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{entityLogicalName:{type:"string",description:"Logical name of the table"},attributeLogicalName:{type:"string",description:"Logical name of the column to delete"},autoPublish:{type:"boolean",description:"Publish the customization after deletion (default: true)."},confirm:{type:"boolean",description:"Must be true \u2014 confirms intentional deletion of column and ALL its data"}},required:["entityLogicalName","attributeLogicalName","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1,openWorldHint:!0}}];async function St(a,r,i){switch(a){case"dataverse_create_attribute":{let e=ht.parse(r),t=U({toolName:"dataverse_create_attribute",entitySetName:e.entityLogicalName}).map(l=>`[${l.severity.toUpperCase()}] ${l.code}: ${l.message}`),n=wt(e),o=await i.createAttribute(e.entityLogicalName,n),s="autoPublish=false (skipped)";return e.autoPublish&&(await i.publishCustomizations({entities:[e.entityLogicalName]}),s="published successfully"),u(`Column '${e.schemaName}' (${e.attributeType}) created on '${e.entityLogicalName}'. ${s}.`,{entityLogicalName:e.entityLogicalName,schemaName:e.schemaName,attributeType:e.attributeType,metadataId:o,published:e.autoPublish,...t.length>0&&{warnings:t}},["Use dataverse_get_table_metadata to verify the new column","Column type CANNOT be changed after creation \u2014 to change type, create a new column and migrate data"])}case"dataverse_update_attribute":{let e=bt.parse(r),t=U({toolName:"dataverse_update_attribute",entitySetName:e.entityLogicalName}).map(d=>`[${d.severity.toUpperCase()}] ${d.code}: ${d.message}`),n=e.languageCode,o={},s={};e.displayName!==void 0&&(o.DisplayName=L(e.displayName,n),s.DisplayName=e.displayName),e.description!==void 0&&(o.Description=L(e.description,n),s.Description=e.description),e.requiredLevel!==void 0&&(o.RequiredLevel={Value:e.requiredLevel},s.RequiredLevel=e.requiredLevel),e.maxLength!==void 0&&(o.MaxLength=e.maxLength,s.MaxLength=e.maxLength),e.isSearchable!==void 0&&(o.IsSearchable=e.isSearchable,s.IsSearchable=e.isSearchable),await i.updateAttribute(e.entityLogicalName,e.attributeLogicalName,o);let l=Object.entries(s).map(([d,m])=>`${d}=${String(m)}`).join(", "),c="autoPublish=false (skipped)";return e.autoPublish&&(await i.publishCustomizations({entities:[e.entityLogicalName]}),c="published successfully"),u(`Column '${e.attributeLogicalName}' on '${e.entityLogicalName}' updated: ${l}. ${c}.`,{entityLogicalName:e.entityLogicalName,attributeLogicalName:e.attributeLogicalName,changes:s,published:e.autoPublish,...t.length>0&&{warnings:t}},["Use dataverse_get_table_metadata to verify the changes","Column type, logical name, and DateTime format cannot be changed \u2014 these are immutable after creation"])}case"dataverse_delete_attribute":{let e=_t.parse(r),t=U({toolName:"dataverse_delete_attribute",entitySetName:e.entityLogicalName}).map(o=>`[${o.severity.toUpperCase()}] ${o.code}: ${o.message}`);try{await i.deleteAttribute(e.entityLogicalName,e.attributeLogicalName)}catch(o){let s=o instanceof Error?o.message:String(o);if(s.includes("0x80048405"))return u(`Cannot delete column '${e.attributeLogicalName}': it belongs to a managed solution and cannot be deleted.`,{error:"0x80048405",entityLogicalName:e.entityLogicalName,attributeLogicalName:e.attributeLogicalName},["Managed solution columns can only be removed by uninstalling the solution"]);if(s.includes("dependency")||s.includes("dependencies")||s.includes("DependencyList"))return u(`Cannot delete column '${e.attributeLogicalName}': it has dependencies (views, workflows, forms, or other components reference it).`,{error:"HAS_DEPENDENCIES",entityLogicalName:e.entityLogicalName,attributeLogicalName:e.attributeLogicalName,rawError:s},["Use dataverse_list_dependencies to identify what references this column","Remove all references before retrying deletion"]);throw o}let n="autoPublish=false (skipped)";return e.autoPublish&&(await i.publishCustomizations({entities:[e.entityLogicalName]}),n="published successfully"),u(`Column '${e.attributeLogicalName}' deleted from '${e.entityLogicalName}'. ${n}. All data in this column has been permanently removed.`,{entityLogicalName:e.entityLogicalName,attributeLogicalName:e.attributeLogicalName,deleted:!0,published:e.autoPublish,...t.length>0&&{warnings:t}},["This action is irreversible \u2014 the column and its data cannot be recovered","The column logical name is tombstoned and cannot be reused for ~90 days"])}default:throw new Error(`Unknown attribute tool: ${a}`)}}var At=pe([{tools:me,handler:fe},{tools:be,handler:_e},{tools:Se,handler:Ne},{tools:xe,handler:Re},{tools:Ae,handler:Ee},{tools:ke,handler:De},{tools:Oe,handler:Ie},{tools:Ce,handler:Ue},{tools:He,handler:qe},{tools:Pe,handler:$e},{tools:We,handler:je},{tools:Me,handler:Fe},{tools:Be,handler:Ge},{tools:ze,handler:Ve},{tools:Ke,handler:Xe},{tools:Qe,handler:Ye},{tools:Je,handler:Ze},{tools:et,handler:tt},{tools:at,handler:nt},{tools:it,handler:st},{tools:lt,handler:dt},{tools:yt,handler:vt},{tools:Tt,handler:St},{tools:ct,handler:ut}]),_n=new Set(ne.map(a=>a.name)),Et=[...At.getAllDefinitions(),...ne],wn=hn(bn(import.meta.url)),kt=JSON.parse(yn(vn(wn,"../package.json"),"utf-8")).version,Nt=`You are connected to a Microsoft Dataverse environment via the mcp-dataverse server.
|
|
11
|
+
|
|
12
|
+
## Recommended Workflow
|
|
13
|
+
1. Start with dataverse_whoami to verify authentication.
|
|
14
|
+
2. Use dataverse_list_tables to discover available tables (custom tables only by default).
|
|
15
|
+
3. Use dataverse_get_table_metadata to inspect column names, types, and required fields before querying or writing.
|
|
16
|
+
4. Query data with dataverse_query (OData) or dataverse_execute_fetchxml (complex joins/aggregations).
|
|
17
|
+
5. Use dataverse_create / dataverse_update / dataverse_delete for record operations.
|
|
18
|
+
|
|
19
|
+
## Best Practices
|
|
20
|
+
- Always specify $select to minimize payload size \u2014 never fetch all columns.
|
|
21
|
+
- Use $top to limit results (default 50, max 5000 per page).
|
|
22
|
+
- Prefer dataverse_query for simple reads; use dataverse_execute_fetchxml for aggregations, multi-table joins, or many-to-many traversal.
|
|
23
|
+
- Use dataverse_get_table_metadata before creating/updating records to confirm correct logical field names.
|
|
24
|
+
- For bulk operations (>5 records), use dataverse_batch_execute to reduce HTTP round-trips.
|
|
25
|
+
- Use dataverse_search for full-text search across multiple tables when you don't know which table contains the data.
|
|
26
|
+
- Check dataverse_get_relationships before building FetchXML joins or using dataverse_associate/dataverse_disassociate.
|
|
27
|
+
|
|
28
|
+
## Safety
|
|
29
|
+
- dataverse_delete is irreversible \u2014 always confirm with the user first.
|
|
30
|
+
- Use optimistic concurrency (etag parameter) on updates to prevent lost changes.
|
|
31
|
+
- Impersonation requires explicit privilege and is denied for System Administrator users.
|
|
32
|
+
|
|
33
|
+
## Performance
|
|
34
|
+
- Avoid retrieving more than 5000 records unless explicitly needed (use dataverse_retrieve_multiple_with_paging with maxTotal).
|
|
35
|
+
- Use server-side filtering ($filter / FetchXML conditions) instead of client-side filtering.
|
|
36
|
+
- Cache table metadata \u2014 it rarely changes during a session.
|
|
37
|
+
`;function xt(a,r,i,e){let t=At.getHandler(a);if(!t)throw new Error(`Unknown tool: ${a}`);return t(a,r,i,e)}function Rt(a){let r=new dn({name:"mcp-dataverse",version:kt},{capabilities:{tools:{},resources:{}},instructions:Nt});return r.setRequestHandler(pn,async()=>({tools:Et})),r.setRequestHandler(un,async i=>{let{name:e,arguments:t}=i.params,n=i.params._meta?.progressToken,o=new F(n!=null?r:void 0,n);try{return _n.has(e)?Le(e,t,a,xt):await xt(e,t,a,o)}catch(s){return{content:[{type:"text",text:`Error: ${s instanceof Error?s.message:String(s)}`}],isError:!0}}}),r.setRequestHandler(mn,async()=>({resources:pt()})),r.setRequestHandler(fn,async()=>({resourceTemplates:mt()})),r.setRequestHandler(gn,async i=>({contents:[await ft(i.params.uri,a,Nt)]})),r}async function Tn(){let a=le(),r=de(a),i=new ce(r,a.maxRetries,a.requestTimeoutMs),e=ue();if(e.transport==="http"){let{startHttpTransport:t}=await import("./http-server.js");process.stderr.write(`Starting HTTP transport on port ${e.port}...
|
|
38
|
+
`);try{await r.getToken(),process.stderr.write(`[mcp-dataverse] Authenticated \u2713
|
|
39
|
+
`)}catch(n){let o=n instanceof Error?n.message:String(n);process.stderr.write(`[mcp-dataverse] Authentication failed: ${o}
|
|
40
|
+
`)}await t(()=>Rt(i),e.port,kt,Et.length)}else{let t=Rt(i),n=new cn;await t.connect(n),process.stderr.write(`MCP Dataverse server started
|
|
41
|
+
`),r.getToken().then(()=>{process.stderr.write(`[mcp-dataverse] Authenticated \u2713
|
|
42
|
+
`)}).catch(o=>{let s=o instanceof Error?o.message:String(o);process.stderr.write(`[mcp-dataverse] Authentication failed: ${s}
|
|
43
|
+
`)})}}async function Sn(){if(process.argv.includes("install")){let{runInstall:a}=await import("./install.js");await a(),process.exit(0)}if(process.argv.includes("doctor")){let{runDoctor:a}=await import("./doctor.js");await a()}await Tn()}Sn().catch(a=>{process.stderr.write(`Fatal error: ${a instanceof Error?a.message:String(a)}
|
|
44
|
+
`),process.exit(1)});
|