@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.
- package/LICENSE +22 -0
- package/README.md +75 -23
- package/build/index.js +117 -6
- 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
|
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
|
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 @
|
23
|
+
npm install -g @north7/entraaware
|
22
24
|
|
23
25
|
# Or use with npx (no installation needed)
|
24
|
-
npx @
|
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
|
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
|
-
|
65
|
+
EntraAware provides three MCP tools:
|
66
66
|
|
67
|
-
|
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
|
-
"
|
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
|
-
|
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
|
-
|
80
|
-
|
115
|
+
#### 3. Lokka-Microsoft (Compatibility Layer)
|
116
|
+
|
117
|
+
A compatibility layer for the Lokka-Microsoft MCP server to ensure backward compatibility.
|
81
118
|
|
82
|
-
|
83
|
-
|
119
|
+
```javascript
|
120
|
+
// Query Graph API
|
121
|
+
{
|
122
|
+
"apiType": "graph",
|
123
|
+
"path": "/users",
|
124
|
+
"method": "get"
|
125
|
+
}
|
84
126
|
|
85
|
-
//
|
86
|
-
|
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
|
-
##
|
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
|
-
- [
|
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.
|
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': '
|
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 = '
|
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
|
-
|
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
|
-
|
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.
|
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
|
-
"
|
19
|
-
|
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
|
],
|