@north7/entraaware 0.0.2 → 0.0.4
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/build/index.js +52 -6
- package/package.json +1 -1
package/build/index.js
CHANGED
@@ -3,7 +3,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
4
4
|
import { z } from "zod";
|
5
5
|
import { Client } from "@microsoft/microsoft-graph-client";
|
6
|
-
import { ClientSecretCredential } from "@azure/identity";
|
6
|
+
import { ClientSecretCredential, DefaultAzureCredential } from "@azure/identity";
|
7
7
|
import { TokenCredentialAuthenticationProvider } from "@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials/index.js";
|
8
8
|
// Initialize clients
|
9
9
|
let graphClient = null;
|
@@ -11,7 +11,7 @@ let azureCredential = null;
|
|
11
11
|
// Create server instance
|
12
12
|
const server = new McpServer({
|
13
13
|
name: "EntraAware",
|
14
|
-
version: "0.0.
|
14
|
+
version: "0.0.3",
|
15
15
|
capabilities: {
|
16
16
|
resources: {},
|
17
17
|
tools: {},
|
@@ -29,8 +29,24 @@ function getCredentials() {
|
|
29
29
|
}
|
30
30
|
function getAzureCredential() {
|
31
31
|
if (!azureCredential) {
|
32
|
-
|
33
|
-
|
32
|
+
try {
|
33
|
+
// Try DefaultAzureCredential which includes CLI credentials
|
34
|
+
console.error("Attempting to use DefaultAzureCredential (will try Azure CLI if environment variables not set)");
|
35
|
+
azureCredential = new DefaultAzureCredential();
|
36
|
+
}
|
37
|
+
catch (error) {
|
38
|
+
// Fall back to ClientSecretCredential
|
39
|
+
console.error(`DefaultAzureCredential failed: ${error instanceof Error ? error.message : String(error)}`);
|
40
|
+
console.error("Falling back to ClientSecretCredential");
|
41
|
+
try {
|
42
|
+
const { tenantId, clientId, clientSecret } = getCredentials();
|
43
|
+
azureCredential = new ClientSecretCredential(tenantId, clientId, clientSecret);
|
44
|
+
}
|
45
|
+
catch (secretError) {
|
46
|
+
console.error(`ClientSecretCredential failed: ${secretError instanceof Error ? secretError.message : String(secretError)}`);
|
47
|
+
throw new Error("Failed to initialize any Azure credential. Please ensure you are logged in with 'az login' or have set environment variables.");
|
48
|
+
}
|
49
|
+
}
|
34
50
|
}
|
35
51
|
return azureCredential;
|
36
52
|
}
|
@@ -81,6 +97,7 @@ server.tool("askEntra", "Direct access to Microsoft Graph API for accurate Entra
|
|
81
97
|
top: z.number().optional().describe("Shorthand for $top query parameter"),
|
82
98
|
count: z.boolean().optional().describe("Shorthand for $count=true to include count of items"),
|
83
99
|
}, async ({ path, method, queryParams = {}, body, apiVersion, fetchAllPages, consistencyLevel, select, filter, expand, orderBy, top, count }) => {
|
100
|
+
console.error(`[askEntra] Processing request: ${method} ${path}`);
|
84
101
|
try {
|
85
102
|
// Process shorthand query parameters
|
86
103
|
const processedParams = { ...queryParams };
|
@@ -96,27 +113,35 @@ server.tool("askEntra", "Direct access to Microsoft Graph API for accurate Entra
|
|
96
113
|
processedParams['$top'] = top.toString();
|
97
114
|
if (count)
|
98
115
|
processedParams['$count'] = 'true';
|
99
|
-
|
116
|
+
console.error(`[askEntra] Getting Azure credential`);
|
117
|
+
// Initialize or get Azure credential
|
118
|
+
const credential = getAzureCredential();
|
119
|
+
console.error(`[askEntra] Initializing Graph client`);
|
120
|
+
// Initialize Graph client if not already done
|
100
121
|
if (!graphClient) {
|
101
|
-
const credential = getAzureCredential();
|
102
122
|
const authProvider = new TokenCredentialAuthenticationProvider(credential, {
|
103
123
|
scopes: ["https://graph.microsoft.com/.default"],
|
104
124
|
});
|
105
125
|
graphClient = Client.initWithMiddleware({ authProvider });
|
126
|
+
console.error(`[askEntra] Graph client initialized`);
|
106
127
|
}
|
107
128
|
// Build request with API path and version
|
129
|
+
console.error(`[askEntra] Creating request for ${path} with version ${apiVersion}`);
|
108
130
|
let request = graphClient.api(path).version(apiVersion);
|
109
131
|
// Add query parameters
|
110
132
|
if (Object.keys(processedParams).length > 0) {
|
133
|
+
console.error(`[askEntra] Adding query parameters: ${JSON.stringify(processedParams)}`);
|
111
134
|
request = request.query(processedParams);
|
112
135
|
}
|
113
136
|
// Add consistency level header if provided
|
114
137
|
if (consistencyLevel) {
|
138
|
+
console.error(`[askEntra] Adding consistency level: ${consistencyLevel}`);
|
115
139
|
request = request.header('ConsistencyLevel', consistencyLevel);
|
116
140
|
}
|
117
141
|
// Handle pagination for GET requests
|
118
142
|
let result;
|
119
143
|
if (method === 'get' && fetchAllPages) {
|
144
|
+
console.error(`[askEntra] Executing GET with pagination`);
|
120
145
|
const firstPage = await request.get();
|
121
146
|
// If no pagination needed, return first page
|
122
147
|
if (!firstPage["@odata.nextLink"]) {
|
@@ -127,6 +152,7 @@ server.tool("askEntra", "Direct access to Microsoft Graph API for accurate Entra
|
|
127
152
|
const allItems = [...(firstPage.value || [])];
|
128
153
|
let nextLink = firstPage["@odata.nextLink"];
|
129
154
|
while (nextLink) {
|
155
|
+
console.error(`[askEntra] Fetching next page: ${nextLink}`);
|
130
156
|
const nextPage = await graphClient.api(nextLink).get();
|
131
157
|
if (nextPage.value)
|
132
158
|
allItems.push(...nextPage.value);
|
@@ -142,6 +168,7 @@ server.tool("askEntra", "Direct access to Microsoft Graph API for accurate Entra
|
|
142
168
|
}
|
143
169
|
else {
|
144
170
|
// Execute appropriate method
|
171
|
+
console.error(`[askEntra] Executing ${method} request`);
|
145
172
|
switch (method) {
|
146
173
|
case 'get':
|
147
174
|
result = await request.get();
|
@@ -161,9 +188,14 @@ server.tool("askEntra", "Direct access to Microsoft Graph API for accurate Entra
|
|
161
188
|
break;
|
162
189
|
}
|
163
190
|
}
|
191
|
+
console.error(`[askEntra] Successfully executed request`);
|
164
192
|
return formatApiResponse('Entra', method, path, result);
|
165
193
|
}
|
166
194
|
catch (err) {
|
195
|
+
console.error(`[askEntra] ERROR: ${err instanceof Error ? err.message : String(err)}`);
|
196
|
+
if (err instanceof Error && err.stack) {
|
197
|
+
console.error(`[askEntra] Stack trace: ${err.stack}`);
|
198
|
+
}
|
167
199
|
return formatErrorResponse(err, 'Entra');
|
168
200
|
}
|
169
201
|
});
|
@@ -188,6 +220,7 @@ server.tool("askAzure", "Direct access to Azure Resource Management API for mana
|
|
188
220
|
resourceGroupName: z.string().optional().describe("Resource group name for resource operations"),
|
189
221
|
resourceName: z.string().optional().describe("Resource name for resource operations"),
|
190
222
|
}, async ({ path, method, apiVersion, subscriptionId, body, queryParams = {}, fetchAllPages, operation = "custom", providerNamespace, resourceType, resourceGroupName, resourceName }) => {
|
223
|
+
console.error(`[askAzure] Processing request: ${operation} - ${method} ${path}`);
|
191
224
|
try {
|
192
225
|
// Default API versions for common resource types
|
193
226
|
const defaultApiVersions = {
|
@@ -277,6 +310,7 @@ server.tool("askAzure", "Direct access to Azure Resource Management API for mana
|
|
277
310
|
throw new Error("Azure Resource Management API requires an 'apiVersion' parameter");
|
278
311
|
}
|
279
312
|
// Get Azure credential
|
313
|
+
console.error(`[askAzure] Getting Azure credential`);
|
280
314
|
const credential = getAzureCredential();
|
281
315
|
// Construct the base URL and path
|
282
316
|
const baseUrl = "https://management.azure.com";
|
@@ -288,10 +322,12 @@ server.tool("askAzure", "Direct access to Azure Resource Management API for mana
|
|
288
322
|
const params = new URLSearchParams(queryParams);
|
289
323
|
if (apiVersion)
|
290
324
|
params.set('api-version', apiVersion);
|
325
|
+
console.error(`[askAzure] Requesting token for Azure Resource Management API`);
|
291
326
|
// Get access token
|
292
327
|
const tokenResponse = await credential.getToken("https://management.azure.com/.default");
|
293
328
|
if (!tokenResponse?.token)
|
294
329
|
throw new Error("Failed to acquire Azure access token");
|
330
|
+
console.error(`[askAzure] Successfully acquired token`);
|
295
331
|
// Prepare request options
|
296
332
|
const headers = {
|
297
333
|
'Authorization': `Bearer ${tokenResponse.token}`,
|
@@ -306,10 +342,12 @@ server.tool("askAzure", "Direct access to Azure Resource Management API for mana
|
|
306
342
|
}
|
307
343
|
// Construct URL
|
308
344
|
const url = `${baseUrl}${fullPath}?${params.toString()}`;
|
345
|
+
console.error(`[askAzure] Making request to ${url}`);
|
309
346
|
// Execute request with pagination if needed
|
310
347
|
let result;
|
311
348
|
if (method === 'get' && fetchAllPages) {
|
312
349
|
// Fetch first page
|
350
|
+
console.error(`[askAzure] Executing GET with pagination`);
|
313
351
|
const response = await fetch(url, options);
|
314
352
|
if (!response.ok) {
|
315
353
|
const errorText = await response.text();
|
@@ -325,6 +363,7 @@ server.tool("askAzure", "Direct access to Azure Resource Management API for mana
|
|
325
363
|
const allItems = [...(firstPage.value || [])];
|
326
364
|
let nextLink = firstPage.nextLink;
|
327
365
|
while (nextLink) {
|
366
|
+
console.error(`[askAzure] Fetching next page: ${nextLink}`);
|
328
367
|
const pageResponse = await fetch(nextLink, options);
|
329
368
|
if (!pageResponse.ok)
|
330
369
|
throw new Error(`Azure API pagination error: ${pageResponse.status}`);
|
@@ -341,9 +380,11 @@ server.tool("askAzure", "Direct access to Azure Resource Management API for mana
|
|
341
380
|
}
|
342
381
|
else {
|
343
382
|
// Single page request
|
383
|
+
console.error(`[askAzure] Executing ${method} request`);
|
344
384
|
const response = await fetch(url, options);
|
345
385
|
if (!response.ok) {
|
346
386
|
const errorText = await response.text();
|
387
|
+
console.error(`[askAzure] Request failed with status ${response.status}: ${errorText}`);
|
347
388
|
let errorDetail;
|
348
389
|
try {
|
349
390
|
errorDetail = JSON.parse(errorText);
|
@@ -359,9 +400,14 @@ server.tool("askAzure", "Direct access to Azure Resource Management API for mana
|
|
359
400
|
const text = await response.text();
|
360
401
|
result = text ? JSON.parse(text) : { status: "Success" };
|
361
402
|
}
|
403
|
+
console.error(`[askAzure] Successfully executed request`);
|
362
404
|
return formatApiResponse('Azure', method, path, result);
|
363
405
|
}
|
364
406
|
catch (err) {
|
407
|
+
console.error(`[askAzure] ERROR: ${err instanceof Error ? err.message : String(err)}`);
|
408
|
+
if (err instanceof Error && err.stack) {
|
409
|
+
console.error(`[askAzure] Stack trace: ${err.stack}`);
|
410
|
+
}
|
365
411
|
return formatErrorResponse(err, 'Azure');
|
366
412
|
}
|
367
413
|
});
|