@north7/entraaware 0.0.5 → 0.0.6

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 (4) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +75 -23
  3. package/build/index.js +117 -6
  4. package/package.json +11 -5
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 North7 (EntraAware)
4
+ Portions copyright (c) 2025 Merill Fernando
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
package/README.md CHANGED
@@ -1,27 +1,29 @@
1
1
  # EntraAware MCP Server
2
2
 
3
- A simple, lightweight Model Context Protocol (MCP) server for querying Microsoft Entra (Azure AD) data.
3
+ A lightweight Model Context Protocol (MCP) server for querying Microsoft Entra (Azure AD) and Azure Resource Management data.
4
4
 
5
5
  ## What is EntraAware?
6
6
 
7
- EntraAware is an MCP Server allows AI assistants to directly access your Microsoft Entra (Azure AD) tenant data through the Microsoft Graph API. With EntraAware, you can ask natural language questions about your Entra environment.
7
+ EntraAware is an MCP Server that allows AI assistants to directly access your Microsoft Entra (Azure AD) tenant data through the Microsoft Graph API and Azure Resource Management API. With EntraAware, you can ask natural language questions or make structured API calls to your Microsoft cloud environments.
8
+
9
+ This project is inspired by and builds upon the [Lokka-Microsoft](https://github.com/lokkamcp/microsoft) MCP server (MIT license).
8
10
 
9
11
  ## Setup
10
12
 
11
13
  ### Prerequisites
12
14
 
13
15
  - Microsoft Entra (Azure AD) tenant
14
- - Application registration with appropriate Graph API permissions
16
+ - Application registration with appropriate Graph API permissions and Azure Resource Management permissions
15
17
  - Node.js 18 or higher
16
18
 
17
19
  ### Installation
18
20
 
19
21
  ```bash
20
22
  # Install globally
21
- npm install -g @uniquk/entraaware
23
+ npm install -g @north7/entraaware
22
24
 
23
25
  # Or use with npx (no installation needed)
24
- npx @uniquk/entraaware
26
+ npx @north7/entraaware
25
27
  ```
26
28
 
27
29
  ### Configuration
@@ -56,37 +58,87 @@ Replace the environment variables with your own:
56
58
 
57
59
  ## Usage
58
60
 
59
- Once configured, you can use EntraAware through VS Code by typing:
61
+ Once configured, you can use EntraAware through a compatible MCP client (like VS Code with the MCP extension).
60
62
 
61
- ```
62
- ask EntraAware>
63
- ```
63
+ ### Available Tools
64
64
 
65
- The EntraAware MCP tool provides a single function that automatically detects the right Graph API endpoint based on keywords in your question:
65
+ EntraAware provides three MCP tools:
66
66
 
67
- ```json
67
+ #### 1. askEntra
68
+
69
+ Direct access to Microsoft Graph API for accurate Entra (Azure AD) data.
70
+
71
+ ```javascript
72
+ // Example usage
68
73
  {
69
- "question": "Show me all conditional access policies"
74
+ "path": "/users",
75
+ "method": "get",
76
+ "select": "displayName,userPrincipalName,id",
77
+ "top": 10
78
+ }
79
+
80
+ // Advanced filtering
81
+ {
82
+ "path": "/users",
83
+ "method": "get",
84
+ "filter": "startsWith(displayName,'J')",
85
+ "consistencyLevel": "eventual"
70
86
  }
71
87
  ```
72
88
 
73
- ### Example Queries
89
+ #### 2. askAzure
90
+
91
+ Direct access to Azure Resource Management API for managing Azure resources.
92
+
93
+ ```javascript
94
+ // List subscriptions
95
+ {
96
+ "path": "/subscriptions",
97
+ "method": "get"
98
+ }
74
99
 
100
+ // List all resource groups in a subscription
101
+ {
102
+ "path": "/resourceGroups",
103
+ "method": "get",
104
+ "subscriptionId": "your-subscription-id",
105
+ "apiVersion": "2021-04-01"
106
+ }
107
+
108
+ // Use predefined operations
109
+ {
110
+ "operation": "listResources",
111
+ "subscriptionId": "your-subscription-id"
112
+ }
75
113
  ```
76
- // Get organization details
77
- Show me details about my organization
78
114
 
79
- // Get conditional access policies
80
- List all conditional access policies
115
+ #### 3. Lokka-Microsoft (Compatibility Layer)
116
+
117
+ A compatibility layer for the Lokka-Microsoft MCP server to ensure backward compatibility.
81
118
 
82
- // Get information about a specific user
83
- Find user john.doe@example.com
119
+ ```javascript
120
+ // Query Graph API
121
+ {
122
+ "apiType": "graph",
123
+ "path": "/users",
124
+ "method": "get"
125
+ }
84
126
 
85
- // Get all groups
86
- Show me all groups
127
+ // Query Azure API
128
+ {
129
+ "apiType": "azure",
130
+ "path": "/subscriptions",
131
+ "method": "get",
132
+ "apiVersion": "2022-12-01"
133
+ }
87
134
  ```
88
135
 
89
- ## References
136
+ ## License
137
+
138
+ MIT License - See LICENSE file for details.
139
+
140
+ ## Acknowledgements
90
141
 
142
+ - This project is inspired by and builds upon the [Lokka-Microsoft](https://github.com/merill/lokka) MCP server.
91
143
  - [Microsoft Graph API Documentation](https://learn.microsoft.com/en-us/graph/api/overview)
92
- - [EntraAware npm package](https://www.npmjs.com/package/@uniquk/entraaware)
144
+ - [Azure Resource Management API Documentation](https://learn.microsoft.com/en-us/rest/api/azure/)
package/build/index.js CHANGED
@@ -1,4 +1,10 @@
1
1
  #!/usr/bin/env node
2
+ /**
3
+ * EntraAware MCP Server
4
+ *
5
+ * Portions of this code are adapted from the Lokka-Microsoft MCP Server (MIT License)
6
+ * Original repository: https://github.com/merill/lokka
7
+ */
2
8
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
9
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
10
  import { z } from "zod";
@@ -11,13 +17,14 @@ let azureCredential = null;
11
17
  // Create server instance
12
18
  const server = new McpServer({
13
19
  name: "EntraAware",
14
- version: "0.0.5",
20
+ version: "0.0.6",
15
21
  capabilities: {
16
22
  resources: {},
17
23
  tools: {},
18
24
  },
19
25
  });
20
26
  // SHARED UTILITIES
27
+ // The following credential handling utilities are adapted from Lokka-Microsoft
21
28
  function getCredentials() {
22
29
  const tenantId = process.env.TENANT_ID;
23
30
  const clientId = process.env.CLIENT_ID;
@@ -27,6 +34,52 @@ function getCredentials() {
27
34
  }
28
35
  return { tenantId, clientId, clientSecret };
29
36
  }
37
+ // Helper to fetch the latest API version for a given resource provider and resource type
38
+ async function getLatestApiVersion(providerNamespace, resourceType) {
39
+ try {
40
+ // Get Azure credential
41
+ const credential = getAzureCredential();
42
+ // Get access token
43
+ const tokenResponse = await credential.getToken("https://management.azure.com/.default");
44
+ if (!tokenResponse?.token)
45
+ throw new Error("Failed to acquire Azure access token");
46
+ // Prepare request options
47
+ const headers = {
48
+ 'Authorization': `Bearer ${tokenResponse.token}`,
49
+ 'Content-Type': 'application/json'
50
+ };
51
+ // Make request to list provider details
52
+ const url = `https://management.azure.com/providers/${providerNamespace}?api-version=2021-04-01`;
53
+ const response = await fetch(url, { method: 'GET', headers });
54
+ if (!response.ok) {
55
+ throw new Error(`Failed to fetch API versions: ${response.status}`);
56
+ }
57
+ const providerData = await response.json();
58
+ // If resourceType is specified, find that specific type
59
+ if (resourceType) {
60
+ const resourceTypeInfo = providerData.resourceTypes.find((rt) => rt.resourceType.toLowerCase() === resourceType.toLowerCase());
61
+ if (resourceTypeInfo && resourceTypeInfo.apiVersions && resourceTypeInfo.apiVersions.length > 0) {
62
+ // Return the first (most recent) API version
63
+ return resourceTypeInfo.apiVersions[0];
64
+ }
65
+ }
66
+ else {
67
+ // Otherwise, just return a stable API version for the provider
68
+ // Providers typically have their most recent versions first
69
+ if (providerData.apiVersions && providerData.apiVersions.length > 0) {
70
+ return providerData.apiVersions[0];
71
+ }
72
+ }
73
+ // If we get here, we couldn't find a good API version
74
+ throw new Error(`Could not find API version for ${providerNamespace}${resourceType ? '/' + resourceType : ''}`);
75
+ }
76
+ catch (error) {
77
+ console.error(`Error fetching API versions: ${error instanceof Error ? error.message : String(error)}`);
78
+ // Return a default fallback version - you might want to customize this per provider
79
+ return '2021-04-01';
80
+ }
81
+ }
82
+ // This Azure credential handling approach is similar to Lokka-Microsoft
30
83
  function getAzureCredential() {
31
84
  if (!azureCredential) {
32
85
  try {
@@ -50,6 +103,7 @@ function getAzureCredential() {
50
103
  }
51
104
  return azureCredential;
52
105
  }
106
+ // Response formatting utilities inspired by Lokka-Microsoft
53
107
  function formatApiResponse(apiType, method, path, result) {
54
108
  return {
55
109
  content: [
@@ -98,6 +152,7 @@ function processODataParams({ queryParams = {}, select, filter, expand, orderBy,
98
152
  return processedParams;
99
153
  }
100
154
  // MICROSOFT GRAPH API TOOL
155
+ // This tool implementation is inspired by and adapted from Lokka-Microsoft's Graph API handling
101
156
  server.tool("askEntra", "Direct access to Microsoft Graph API for accurate Entra (Azure AD) data", {
102
157
  path: z.string().describe("The Graph API URL path (e.g. '/users/{id}/memberOf', '/directoryRoles')"),
103
158
  method: z.enum(["get", "post", "put", "patch", "delete"]).default("get").describe("HTTP method to use"),
@@ -197,6 +252,7 @@ server.tool("askEntra", "Direct access to Microsoft Graph API for accurate Entra
197
252
  }
198
253
  });
199
254
  // AZURE RESOURCE MANAGEMENT API TOOL
255
+ // This tool implementation is inspired by and adapted from Lokka-Microsoft's Azure API handling
200
256
  server.tool("askAzure", "Direct access to Azure Resource Management API for managing Azure resources", {
201
257
  path: z.string().describe("The Azure API path (e.g. '/subscriptions', '/resourceGroups/{name}')"),
202
258
  method: z.enum(["get", "post", "put", "patch", "delete"]).default("get").describe("HTTP method to use"),
@@ -222,18 +278,28 @@ server.tool("askAzure", "Direct access to Azure Resource Management API for mana
222
278
  const defaultApiVersions = {
223
279
  'resources': '2021-04-01',
224
280
  'resourceGroups': '2021-04-01',
225
- 'subscriptions': '2021-01-01',
281
+ 'subscriptions': '2022-12-01',
226
282
  'providers': '2021-04-01',
227
283
  'deployments': '2021-04-01',
228
284
  'Microsoft.Compute/virtualMachines': '2023-03-01',
229
285
  'Microsoft.Storage/storageAccounts': '2023-01-01',
230
286
  'Microsoft.Network/virtualNetworks': '2023-04-01',
231
- 'Microsoft.KeyVault/vaults': '2023-02-01'
287
+ 'Microsoft.KeyVault/vaults': '2023-02-01',
288
+ 'Microsoft.Billing/billingAccounts': '2024-04-01',
289
+ 'Microsoft.CostManagement/query': '2023-03-01'
232
290
  };
233
291
  // Set default API version for common paths if not provided
234
292
  if (!apiVersion && !queryParams['api-version']) {
235
293
  if (path === '/subscriptions') {
236
- apiVersion = '2022-12-01'; // Default API version for listing subscriptions
294
+ apiVersion = defaultApiVersions['subscriptions']; // Default API version for listing subscriptions
295
+ }
296
+ }
297
+ let extractedProviderNamespace = null;
298
+ // Try to extract provider namespace from the path if not explicitly provided
299
+ if (!providerNamespace && path.includes('/providers/')) {
300
+ const match = path.match(/\/providers\/([^\/]+)/);
301
+ if (match && match[1]) {
302
+ extractedProviderNamespace = match[1];
237
303
  }
238
304
  }
239
305
  // Handle predefined operations
@@ -283,7 +349,21 @@ server.tool("askAzure", "Direct access to Azure Resource Management API for mana
283
349
  path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/${providerNamespace}/${resourceType}/${resourceName}`;
284
350
  method = 'put';
285
351
  const providerResourceKey = `${providerNamespace}/${resourceType}`;
286
- apiVersion = apiVersion || defaultApiVersions[providerResourceKey] || '2021-04-01';
352
+ // Try to get the API version for this resource type
353
+ if (!apiVersion) {
354
+ // First check our default versions
355
+ apiVersion = defaultApiVersions[providerResourceKey];
356
+ // If not found in defaults, try to fetch it dynamically
357
+ if (!apiVersion && providerNamespace) {
358
+ try {
359
+ apiVersion = await getLatestApiVersion(providerNamespace, resourceType);
360
+ }
361
+ catch (error) {
362
+ console.error(`Error fetching API version: ${error instanceof Error ? error.message : String(error)}`);
363
+ apiVersion = '2021-04-01'; // Fall back to a safe default
364
+ }
365
+ }
366
+ }
287
367
  break;
288
368
  case 'deployTemplate':
289
369
  if (!resourceGroupName)
@@ -303,10 +383,41 @@ server.tool("askAzure", "Direct access to Azure Resource Management API for mana
303
383
  path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/${providerNamespace}/${resourceType}/${resourceName}`;
304
384
  method = 'delete';
305
385
  const deleteResourceKey = `${providerNamespace}/${resourceType}`;
306
- apiVersion = apiVersion || defaultApiVersions[deleteResourceKey] || '2021-04-01';
386
+ // Try to get the API version for this resource type
387
+ if (!apiVersion) {
388
+ // First check our default versions
389
+ apiVersion = defaultApiVersions[deleteResourceKey];
390
+ // If not found in defaults, try to fetch it dynamically
391
+ if (!apiVersion && providerNamespace) {
392
+ try {
393
+ apiVersion = await getLatestApiVersion(providerNamespace, resourceType);
394
+ }
395
+ catch (error) {
396
+ console.error(`Error fetching API version: ${error instanceof Error ? error.message : String(error)}`);
397
+ apiVersion = '2021-04-01'; // Fall back to a safe default
398
+ }
399
+ }
400
+ }
307
401
  break;
308
402
  }
309
403
  }
404
+ else if (extractedProviderNamespace && !apiVersion && !queryParams['api-version']) {
405
+ // For custom operations, try to get the API version based on the path
406
+ try {
407
+ // Try to extract resource type from path if possible
408
+ let extractedResourceType;
409
+ const resourceTypeMatch = path.match(/\/providers\/[^\/]+\/([^\/]+)\/?/);
410
+ if (resourceTypeMatch && resourceTypeMatch[1]) {
411
+ extractedResourceType = resourceTypeMatch[1];
412
+ }
413
+ // Fetch the latest API version
414
+ apiVersion = await getLatestApiVersion(extractedProviderNamespace, extractedResourceType);
415
+ }
416
+ catch (error) {
417
+ console.error(`Error fetching API version from path: ${error instanceof Error ? error.message : String(error)}`);
418
+ // Don't set a default here, let the error handling below catch it
419
+ }
420
+ }
310
421
  // Ensure API version is provided
311
422
  if (!apiVersion && !queryParams['api-version']) {
312
423
  throw new Error("Azure Resource Management API requires an 'apiVersion' parameter");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north7/entraaware",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "type": "module",
5
5
  "main": "build/index.js",
6
6
  "bin": {
@@ -13,10 +13,16 @@
13
13
  "publishConfig": {
14
14
  "access": "public"
15
15
  },
16
- "keywords": ["mcp-server", "entra", "azure-ad", "mcp", "microsoft-graph"],
17
- "author": "",
18
- "license": "ISC",
19
- "description": "MCP server for querying Entra and Azure",
16
+ "keywords": ["mcp-server", "entra", "azure-ad", "mcp", "microsoft-graph", "azure-api"],
17
+ "author": "North7",
18
+ "contributors": [
19
+ {
20
+ "name": "Merill Fernando",
21
+ "url": "https://github.com/merill/lokka"
22
+ }
23
+ ],
24
+ "license": "MIT",
25
+ "description": "MCP server for querying Microsoft Entra (Azure AD) and Azure Resource Management APIs",
20
26
  "files": [
21
27
  "build"
22
28
  ],