mcp-consultant-tools 0.4.6 → 2.0.0

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.
Files changed (3) hide show
  1. package/README.md +98 -138
  2. package/build/index.js +5 -150
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -37,6 +37,41 @@ npx mcp-consultant-tools
37
37
 
38
38
  This is an MCP server designed to work with MCP-compatible clients like Claude Desktop, Cursor, or Claude Code (VS Code extension).
39
39
 
40
+ ### Quick Start: VS Code (Claude Code) Configuration
41
+
42
+ For VS Code with Claude Code extension, create a `.vscode/mcp.json` file in your repository:
43
+
44
+ ```json
45
+ {
46
+ "servers": {
47
+ "mcp-consultant-tools": {
48
+ "command": "npx",
49
+ "args": ["-y", "mcp-consultant-tools"],
50
+ "env": {
51
+ "POWERPLATFORM_URL": "https://yourenvironment.crm.dynamics.com",
52
+ "POWERPLATFORM_CLIENT_ID": "your-azure-app-client-id",
53
+ "POWERPLATFORM_CLIENT_SECRET": "your-azure-app-client-secret",
54
+ "POWERPLATFORM_TENANT_ID": "your-azure-tenant-id",
55
+ "AZUREDEVOPS_ORGANIZATION": "your-organization-name",
56
+ "AZUREDEVOPS_PAT": "your-personal-access-token",
57
+ "AZUREDEVOPS_PROJECTS": "Project1,Project2",
58
+ "AZUREDEVOPS_API_VERSION": "7.1",
59
+ "AZUREDEVOPS_ENABLE_WORK_ITEM_WRITE": "true",
60
+ "AZUREDEVOPS_ENABLE_WORK_ITEM_DELETE": "false",
61
+ "AZUREDEVOPS_ENABLE_WIKI_WRITE": "false"
62
+ }
63
+ }
64
+ }
65
+ }
66
+ ```
67
+
68
+ **After configuration:**
69
+ 1. Save the `.vscode/mcp.json` file
70
+ 2. Reload VS Code window
71
+ 3. The MCP server will be available in Claude Code
72
+
73
+ **Note:** You can omit PowerPlatform or Azure DevOps credentials if you only need one integration (see Environment Variables Reference below).
74
+
40
75
  ### Quick Start: Claude Desktop Configuration
41
76
 
42
77
  Add this to your Claude Desktop config file at `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
@@ -97,7 +132,12 @@ For local development and testing, you can run the server directly from your clo
97
132
 
98
133
  ### Environment Variables Reference
99
134
 
100
- **PowerPlatform/Dataverse (Required):**
135
+ **Note:** Both PowerPlatform and Azure DevOps integrations are optional. You can configure only the services you need:
136
+ - PowerPlatform only: Set `POWERPLATFORM_*` variables
137
+ - Azure DevOps only: Set `AZUREDEVOPS_*` variables
138
+ - Both: Set all variables
139
+
140
+ **PowerPlatform/Dataverse (Optional):**
101
141
  - `POWERPLATFORM_URL`: Your PowerPlatform environment URL
102
142
  - `POWERPLATFORM_CLIENT_ID`: Azure AD app registration client ID
103
143
  - `POWERPLATFORM_CLIENT_SECRET`: Azure AD app registration client secret
@@ -131,7 +171,6 @@ Once configured, the MCP server will expose tools for retrieving PowerPlatform e
131
171
  - `get-global-option-set`: Get a global option set definition
132
172
  - `get-record`: Get a specific record by entity name and ID
133
173
  - `query-records`: Query records using an OData filter expression
134
- - `use-powerplatform-prompt`: Use pre-defined prompt templates for PowerPlatform entities
135
174
 
136
175
  #### Plugin Registration & Validation Tools
137
176
  - `get-plugin-assemblies`: List all plugin assemblies in the environment
@@ -141,151 +180,29 @@ Once configured, the MCP server will expose tools for retrieving PowerPlatform e
141
180
 
142
181
  ## MCP Prompts
143
182
 
144
- The server includes a prompts feature that provides formatted, context-rich information about PowerPlatform entities.
183
+ The server includes MCP prompts that provide formatted, context-rich information.
145
184
 
146
- ### Available Prompt Types
185
+ ### Available Prompts
147
186
 
148
187
  #### Entity Prompts
149
- The `use-powerplatform-prompt` tool supports the following prompt types:
150
-
151
- 1. **ENTITY_OVERVIEW**: Comprehensive overview of an entity
152
- 2. **ATTRIBUTE_DETAILS**: Detailed information about a specific entity attribute
153
- 3. **QUERY_TEMPLATE**: OData query template for an entity with example filters
154
- 4. **RELATIONSHIP_MAP**: Visual map of entity relationships
188
+ 1. **entity-overview**: Comprehensive overview of a PowerPlatform entity
189
+ 2. **attribute-details**: Detailed information about a specific entity attribute
190
+ 3. **query-template**: OData query template for an entity with example filters
191
+ 4. **relationship-map**: Visual map of entity relationships
155
192
 
156
193
  #### Plugin Prompts
157
194
  5. **plugin-deployment-report**: Generate a comprehensive deployment report for a plugin assembly with validation warnings
158
195
  6. **entity-plugin-pipeline-report**: Generate a visual execution pipeline showing all plugins for an entity in order
159
196
 
160
- ### Examples
161
-
162
- #### Entity Overview Prompt
163
-
164
- ```javascript
165
- // Example client code
166
- await mcpClient.invoke("use-powerplatform-prompt", {
167
- promptType: "ENTITY_OVERVIEW",
168
- entityName: "account"
169
- });
170
- ```
171
-
172
- **Output:**
173
- ```
174
- ## Power Platform Entity: account
175
-
176
- This is an overview of the 'account' entity in Microsoft Power Platform/Dataverse:
197
+ #### Workflow & Flow Prompts
198
+ 7. **flows-report**: Comprehensive report of all Power Automate flows
199
+ 8. **workflows-report**: Comprehensive report of all classic Dynamics workflows
177
200
 
178
- ### Entity Details
179
- - Display Name: Account
180
- - Schema Name: Account
181
- - Description: Business that represents a customer or potential customer
182
- - Primary Key: accountid
183
- - Primary Name: name
184
-
185
- ### Key Attributes
186
- - name: String (Account Name)
187
- - emailaddress1: String (Email)
188
- - telephone1: String (Main Phone)
189
- - address1_city: String (City)
190
- - statecode: Status (Status)
191
- - accountnumber: String (Account Number)
192
- - industrycode: OptionSetValue (Industry)
193
- - revenue: Money (Annual Revenue)
194
- - ownerid: Owner (Owner)
195
- - createdon: DateTime (Created On)
196
-
197
- ### Relationships
198
- - One-to-Many Relationships: 42
199
- - Many-to-Many Relationships: 8
200
-
201
- You can query this entity using OData filters against the plural name.
202
- ```
203
-
204
- #### Attribute Details Prompt
205
-
206
- ```javascript
207
- await mcpClient.invoke("use-powerplatform-prompt", {
208
- promptType: "ATTRIBUTE_DETAILS",
209
- entityName: "account",
210
- attributeName: "revenue"
211
- });
212
- ```
213
-
214
- **Output:**
215
- ```
216
- ## Attribute: revenue
217
-
218
- Details for the 'revenue' attribute of the 'account' entity:
219
-
220
- - Display Name: Annual Revenue
221
- - Description: Annual revenue for the account
222
- - Type: Money
223
- - Format: Currency
224
- - Is Required: No
225
- - Is Searchable: true
226
-
227
- ### Usage Notes
228
- - Data Type: Money
229
- - Required: No
230
- - Max Length: N/A
231
- ```
232
-
233
- #### Query Template Prompt
234
-
235
- ```javascript
236
- await mcpClient.invoke("use-powerplatform-prompt", {
237
- promptType: "QUERY_TEMPLATE",
238
- entityName: "account"
239
- });
240
- ```
241
-
242
- **Output:**
243
- ```
244
- ## OData Query Template for accounts
245
-
246
- Use this template to build queries against the accounts entity:
247
- accounts?$select=name,emailaddress1,telephone1, address1_city,statecode&$filter=name eq 'Example'&$orderby=name asc&$top=50
248
- ```
249
-
250
- ### Common Filter Examples
251
- - Equals: `name eq 'Contoso'`
252
- - Contains: `contains(name, 'Contoso')`
253
- - Greater than date: `createdon gt 2023-01-01T00:00:00Z`
254
- - Multiple conditions: `name eq 'Contoso' and statecode eq 0`
255
- ```
256
-
257
- #### Relationship Map Prompt
258
-
259
- ```javascript
260
- await mcpClient.invoke("use-powerplatform-prompt", {
261
- promptType: "RELATIONSHIP_MAP",
262
- entityName: "contact"
263
- });
264
- ```
265
-
266
- **Output:**
267
- ```
268
- ## Relationship Map for contact
269
-
270
- This shows all relationships for the 'contact' entity:
271
-
272
- ### One-to-Many Relationships (contact as Primary)
273
- - contact_activity_parties: contact (1) → activityparty (N)
274
- - contact_connections1: contact (1) → connection (N)
275
- - contact_connections2: contact (1) → connection (N)
276
- - contact_customer_contacts: contact (1) → contact (N)
277
- - contact_master_contact: contact (1) → contact (N)
278
-
279
- ### One-to-Many Relationships (contact as Related)
280
- - account_primary_contact: account (1) → contact (N)
281
- - customer_contacts: customer (1) → contact (N)
282
- - system_user_contacts: systemuser (1) → contact (N)
283
-
284
- ### Many-to-Many Relationships
285
- - contactleads_association: contact (N) ↔ lead (N)
286
- - contactopportunities_association: contact (N) ↔ opportunity (N)
287
- - contactquotes_association: contact (N) ↔ quote (N)
288
- ```
201
+ #### Azure DevOps Prompts
202
+ 9. **wiki-search-results**: Search Azure DevOps wiki pages with formatted results
203
+ 10. **wiki-page-content**: Get a formatted wiki page with navigation context
204
+ 11. **work-item-summary**: Get a comprehensive summary of a work item with comments
205
+ 12. **work-items-query-report**: Execute a WIQL query and get formatted results
289
206
 
290
207
  ## Prompt Examples
291
208
 
@@ -963,6 +880,49 @@ AZUREDEVOPS_ENABLE_WIKI_WRITE=true
963
880
  # PAT with: vso.wiki (read/write), vso.work_write (read/write), vso.search (read)
964
881
  ```
965
882
 
883
+ ## Development & Release Strategy
884
+
885
+ This project follows a structured branching strategy for development and releases:
886
+
887
+ ### Branch Strategy
888
+
889
+ - **`feature/*` branches**: For active development of new features
890
+ - Create feature branches off `main`
891
+ - Develop and test changes locally
892
+ - Do NOT publish to npm from feature branches
893
+
894
+ - **`release/*` branches**: For testing versions before release
895
+ - Used to test and validate changes before merging to main
896
+ - Do NOT publish to npm from release branches
897
+ - Example: `release/1.0`, `release/2.0`
898
+
899
+ - **`main` branch**: Production-ready code
900
+ - **ONLY** publish to npm when main branch is updated
901
+ - Merge feature/release branches to main when ready
902
+ - Publishing workflow:
903
+ 1. Merge changes to `main`
904
+ 2. Update version: `npm version patch|minor|major`
905
+ 3. Publish: `npm publish`
906
+ 4. Push to GitHub: `git push && git push --tags`
907
+
908
+ ### Local Development Setup
909
+
910
+ For local testing during development (feature/release branches):
911
+
912
+ ```json
913
+ {
914
+ "mcpServers": {
915
+ "mcp-consultant-tools-dev": {
916
+ "command": "node",
917
+ "args": ["/absolute/path/to/mcp-consultant-tools/build/index.js"],
918
+ "env": { ... }
919
+ }
920
+ }
921
+ }
922
+ ```
923
+
924
+ This allows you to test changes locally without publishing to npm.
925
+
966
926
  ## License
967
927
 
968
928
  MIT
package/build/index.js CHANGED
@@ -1183,155 +1183,6 @@ server.tool("query-records", "Query records using an OData filter expression", {
1183
1183
  };
1184
1184
  }
1185
1185
  });
1186
- // PowerPlatform MCP Prompts
1187
- server.tool("use-powerplatform-prompt", "Use a predefined prompt template for PowerPlatform entities", {
1188
- promptType: z.enum([
1189
- "ENTITY_OVERVIEW",
1190
- "ATTRIBUTE_DETAILS",
1191
- "QUERY_TEMPLATE",
1192
- "RELATIONSHIP_MAP"
1193
- ]).describe("The type of prompt template to use"),
1194
- entityName: z.string().describe("The logical name of the entity"),
1195
- attributeName: z.string().optional().describe("The logical name of the attribute (required for ATTRIBUTE_DETAILS prompt)"),
1196
- }, async ({ promptType, entityName, attributeName }) => {
1197
- try {
1198
- // Get or initialize PowerPlatformService
1199
- const service = getPowerPlatformService();
1200
- let promptContent = "";
1201
- let replacements = {};
1202
- switch (promptType) {
1203
- case "ENTITY_OVERVIEW": {
1204
- // Get entity metadata and key attributes
1205
- const [metadata, attributes] = await Promise.all([
1206
- service.getEntityMetadata(entityName),
1207
- service.getEntityAttributes(entityName)
1208
- ]);
1209
- // Format entity details
1210
- const entityDetails = `- Display Name: ${metadata.DisplayName?.UserLocalizedLabel?.Label || entityName}\n` +
1211
- `- Schema Name: ${metadata.SchemaName}\n` +
1212
- `- Description: ${metadata.Description?.UserLocalizedLabel?.Label || 'No description'}\n` +
1213
- `- Primary Key: ${metadata.PrimaryIdAttribute}\n` +
1214
- `- Primary Name: ${metadata.PrimaryNameAttribute}`;
1215
- // Get key attributes
1216
- const keyAttributes = attributes.value
1217
- //.slice(0, 10) // Limit to first 10 important attributes
1218
- .map((attr) => {
1219
- const attrType = attr["@odata.type"] || attr.odata?.type || "Unknown type";
1220
- return `- ${attr.LogicalName}: ${attrType}`;
1221
- })
1222
- .join('\n');
1223
- // Get relationships summary
1224
- const relationships = await service.getEntityRelationships(entityName);
1225
- const oneToManyCount = relationships.oneToMany.value.length;
1226
- const manyToManyCount = relationships.manyToMany.value.length;
1227
- const relationshipsSummary = `- One-to-Many Relationships: ${oneToManyCount}\n` +
1228
- `- Many-to-Many Relationships: ${manyToManyCount}`;
1229
- promptContent = powerPlatformPrompts.ENTITY_OVERVIEW(entityName);
1230
- replacements = {
1231
- '{{entity_details}}': entityDetails,
1232
- '{{key_attributes}}': keyAttributes,
1233
- '{{relationships}}': relationshipsSummary
1234
- };
1235
- break;
1236
- }
1237
- case "ATTRIBUTE_DETAILS": {
1238
- if (!attributeName) {
1239
- throw new Error("attributeName is required for ATTRIBUTE_DETAILS prompt");
1240
- }
1241
- // Get attribute details
1242
- const attribute = await service.getEntityAttribute(entityName, attributeName);
1243
- // Format attribute details
1244
- const attrDetails = `- Display Name: ${attribute.DisplayName?.UserLocalizedLabel?.Label || attributeName}\n` +
1245
- `- Description: ${attribute.Description?.UserLocalizedLabel?.Label || 'No description'}\n` +
1246
- `- Type: ${attribute.AttributeType}\n` +
1247
- `- Format: ${attribute.Format || 'N/A'}\n` +
1248
- `- Is Required: ${attribute.RequiredLevel?.Value || 'No'}\n` +
1249
- `- Is Searchable: ${attribute.IsValidForAdvancedFind || false}`;
1250
- promptContent = powerPlatformPrompts.ATTRIBUTE_DETAILS(entityName, attributeName);
1251
- replacements = {
1252
- '{{attribute_details}}': attrDetails,
1253
- '{{data_type}}': attribute.AttributeType,
1254
- '{{required}}': attribute.RequiredLevel?.Value || 'No',
1255
- '{{max_length}}': attribute.MaxLength || 'N/A'
1256
- };
1257
- break;
1258
- }
1259
- case "QUERY_TEMPLATE": {
1260
- // Get entity metadata to determine plural name
1261
- const metadata = await service.getEntityMetadata(entityName);
1262
- const entityNamePlural = metadata.EntitySetName;
1263
- // Get a few important fields for the select example
1264
- const attributes = await service.getEntityAttributes(entityName);
1265
- const selectFields = attributes.value
1266
- .slice(0, 5) // Just take first 5 for example
1267
- .map((attr) => attr.LogicalName)
1268
- .join(',');
1269
- promptContent = powerPlatformPrompts.QUERY_TEMPLATE(entityNamePlural);
1270
- replacements = {
1271
- '{{selected_fields}}': selectFields,
1272
- '{{filter_conditions}}': `${metadata.PrimaryNameAttribute} eq 'Example'`,
1273
- '{{order_by}}': `${metadata.PrimaryNameAttribute} asc`,
1274
- '{{max_records}}': '50'
1275
- };
1276
- break;
1277
- }
1278
- case "RELATIONSHIP_MAP": {
1279
- // Get relationships
1280
- const relationships = await service.getEntityRelationships(entityName);
1281
- // Format one-to-many relationships where this entity is primary
1282
- const oneToManyPrimary = relationships.oneToMany.value
1283
- .filter((rel) => rel.ReferencingEntity !== entityName)
1284
- //.slice(0, 10) // Limit to 10 for readability
1285
- .map((rel) => `- ${rel.SchemaName}: ${entityName} (1) → ${rel.ReferencingEntity} (N)`)
1286
- .join('\n');
1287
- // Format one-to-many relationships where this entity is related
1288
- const oneToManyRelated = relationships.oneToMany.value
1289
- .filter((rel) => rel.ReferencingEntity === entityName)
1290
- //.slice(0, 10) // Limit to 10 for readability
1291
- .map((rel) => `- ${rel.SchemaName}: ${rel.ReferencedEntity} (1) → ${entityName} (N)`)
1292
- .join('\n');
1293
- // Format many-to-many relationships
1294
- const manyToMany = relationships.manyToMany.value
1295
- //.slice(0, 10) // Limit to 10 for readability
1296
- .map((rel) => {
1297
- const otherEntity = rel.Entity1LogicalName === entityName ? rel.Entity2LogicalName : rel.Entity1LogicalName;
1298
- return `- ${rel.SchemaName}: ${entityName} (N) ↔ ${otherEntity} (N)`;
1299
- })
1300
- .join('\n');
1301
- promptContent = powerPlatformPrompts.RELATIONSHIP_MAP(entityName);
1302
- replacements = {
1303
- '{{one_to_many_primary}}': oneToManyPrimary || 'None found',
1304
- '{{one_to_many_related}}': oneToManyRelated || 'None found',
1305
- '{{many_to_many}}': manyToMany || 'None found'
1306
- };
1307
- break;
1308
- }
1309
- }
1310
- // Replace all placeholders in the template
1311
- for (const [placeholder, value] of Object.entries(replacements)) {
1312
- promptContent = promptContent.replace(placeholder, value);
1313
- }
1314
- return {
1315
- content: [
1316
- {
1317
- type: "text",
1318
- text: promptContent,
1319
- },
1320
- ],
1321
- };
1322
- }
1323
- catch (error) {
1324
- console.error("Error using PowerPlatform prompt:", error);
1325
- return {
1326
- content: [
1327
- {
1328
- type: "text",
1329
- text: `Failed to use PowerPlatform prompt: ${error.message}`,
1330
- },
1331
- ],
1332
- };
1333
- }
1334
- });
1335
1186
  // Plugin Assemblies List Tool
1336
1187
  server.tool("get-plugin-assemblies", "Get a list of all plugin assemblies in the environment", {
1337
1188
  includeManaged: z.boolean().optional().describe("Include managed assemblies (default: false)"),
@@ -1911,7 +1762,11 @@ server.tool("add-work-item-comment", "Add a comment to a work item in Azure DevO
1911
1762
  server.tool("update-work-item", "Update a work item in Azure DevOps using JSON Patch operations (requires AZUREDEVOPS_ENABLE_WORK_ITEM_WRITE=true)", {
1912
1763
  project: z.string().describe("The project name"),
1913
1764
  workItemId: z.number().describe("The work item ID"),
1914
- patchOperations: z.array(z.any()).describe("Array of JSON Patch operations (e.g., [{\"op\": \"add\", \"path\": \"/fields/System.State\", \"value\": \"Resolved\"}])"),
1765
+ patchOperations: z.array(z.object({
1766
+ op: z.string().describe("The operation type (e.g., 'add', 'replace', 'remove')"),
1767
+ path: z.string().describe("The field path (e.g., '/fields/System.State')"),
1768
+ value: z.any().optional().describe("The value to set (not required for 'remove' operation)")
1769
+ })).describe("Array of JSON Patch operations"),
1915
1770
  }, async ({ project, workItemId, patchOperations }) => {
1916
1771
  try {
1917
1772
  const service = getAzureDevOpsService();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-consultant-tools",
3
- "version": "0.4.6",
3
+ "version": "2.0.0",
4
4
  "description": "Dynamics CRM Consultant MCP Server",
5
5
  "main": "build/index.js",
6
6
  "bin": {