@north7/entraaware 0.0.4 → 0.0.5
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 +35 -46
- package/package.json +1 -1
package/build/index.js
CHANGED
@@ -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.5",
|
15
15
|
capabilities: {
|
16
16
|
resources: {},
|
17
17
|
tools: {},
|
@@ -80,6 +80,23 @@ function formatErrorResponse(err, apiType) {
|
|
80
80
|
],
|
81
81
|
};
|
82
82
|
}
|
83
|
+
// Process OData parameters for Graph API
|
84
|
+
function processODataParams({ queryParams = {}, select, filter, expand, orderBy, top, count }) {
|
85
|
+
const processedParams = { ...queryParams };
|
86
|
+
if (select)
|
87
|
+
processedParams['$select'] = select;
|
88
|
+
if (filter)
|
89
|
+
processedParams['$filter'] = filter;
|
90
|
+
if (expand)
|
91
|
+
processedParams['$expand'] = expand;
|
92
|
+
if (orderBy)
|
93
|
+
processedParams['$orderby'] = orderBy;
|
94
|
+
if (top !== undefined)
|
95
|
+
processedParams['$top'] = top.toString();
|
96
|
+
if (count)
|
97
|
+
processedParams['$count'] = 'true';
|
98
|
+
return processedParams;
|
99
|
+
}
|
83
100
|
// MICROSOFT GRAPH API TOOL
|
84
101
|
server.tool("askEntra", "Direct access to Microsoft Graph API for accurate Entra (Azure AD) data", {
|
85
102
|
path: z.string().describe("The Graph API URL path (e.g. '/users/{id}/memberOf', '/directoryRoles')"),
|
@@ -97,51 +114,38 @@ server.tool("askEntra", "Direct access to Microsoft Graph API for accurate Entra
|
|
97
114
|
top: z.number().optional().describe("Shorthand for $top query parameter"),
|
98
115
|
count: z.boolean().optional().describe("Shorthand for $count=true to include count of items"),
|
99
116
|
}, async ({ path, method, queryParams = {}, body, apiVersion, fetchAllPages, consistencyLevel, select, filter, expand, orderBy, top, count }) => {
|
100
|
-
console.error(`[askEntra] Processing request: ${method} ${path}`);
|
101
117
|
try {
|
102
118
|
// Process shorthand query parameters
|
103
|
-
const processedParams = {
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
processedParams['$top'] = top.toString();
|
114
|
-
if (count)
|
115
|
-
processedParams['$count'] = 'true';
|
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
|
119
|
+
const processedParams = processODataParams({
|
120
|
+
queryParams,
|
121
|
+
select,
|
122
|
+
filter,
|
123
|
+
expand,
|
124
|
+
orderBy,
|
125
|
+
top,
|
126
|
+
count
|
127
|
+
});
|
128
|
+
// Initialize client on demand
|
121
129
|
if (!graphClient) {
|
130
|
+
const credential = getAzureCredential();
|
122
131
|
const authProvider = new TokenCredentialAuthenticationProvider(credential, {
|
123
132
|
scopes: ["https://graph.microsoft.com/.default"],
|
124
133
|
});
|
125
134
|
graphClient = Client.initWithMiddleware({ authProvider });
|
126
|
-
console.error(`[askEntra] Graph client initialized`);
|
127
135
|
}
|
128
136
|
// Build request with API path and version
|
129
|
-
console.error(`[askEntra] Creating request for ${path} with version ${apiVersion}`);
|
130
137
|
let request = graphClient.api(path).version(apiVersion);
|
131
138
|
// Add query parameters
|
132
139
|
if (Object.keys(processedParams).length > 0) {
|
133
|
-
console.error(`[askEntra] Adding query parameters: ${JSON.stringify(processedParams)}`);
|
134
140
|
request = request.query(processedParams);
|
135
141
|
}
|
136
142
|
// Add consistency level header if provided
|
137
143
|
if (consistencyLevel) {
|
138
|
-
console.error(`[askEntra] Adding consistency level: ${consistencyLevel}`);
|
139
144
|
request = request.header('ConsistencyLevel', consistencyLevel);
|
140
145
|
}
|
141
146
|
// Handle pagination for GET requests
|
142
147
|
let result;
|
143
148
|
if (method === 'get' && fetchAllPages) {
|
144
|
-
console.error(`[askEntra] Executing GET with pagination`);
|
145
149
|
const firstPage = await request.get();
|
146
150
|
// If no pagination needed, return first page
|
147
151
|
if (!firstPage["@odata.nextLink"]) {
|
@@ -152,7 +156,6 @@ server.tool("askEntra", "Direct access to Microsoft Graph API for accurate Entra
|
|
152
156
|
const allItems = [...(firstPage.value || [])];
|
153
157
|
let nextLink = firstPage["@odata.nextLink"];
|
154
158
|
while (nextLink) {
|
155
|
-
console.error(`[askEntra] Fetching next page: ${nextLink}`);
|
156
159
|
const nextPage = await graphClient.api(nextLink).get();
|
157
160
|
if (nextPage.value)
|
158
161
|
allItems.push(...nextPage.value);
|
@@ -168,7 +171,6 @@ server.tool("askEntra", "Direct access to Microsoft Graph API for accurate Entra
|
|
168
171
|
}
|
169
172
|
else {
|
170
173
|
// Execute appropriate method
|
171
|
-
console.error(`[askEntra] Executing ${method} request`);
|
172
174
|
switch (method) {
|
173
175
|
case 'get':
|
174
176
|
result = await request.get();
|
@@ -188,14 +190,9 @@ server.tool("askEntra", "Direct access to Microsoft Graph API for accurate Entra
|
|
188
190
|
break;
|
189
191
|
}
|
190
192
|
}
|
191
|
-
console.error(`[askEntra] Successfully executed request`);
|
192
193
|
return formatApiResponse('Entra', method, path, result);
|
193
194
|
}
|
194
195
|
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
|
-
}
|
199
196
|
return formatErrorResponse(err, 'Entra');
|
200
197
|
}
|
201
198
|
});
|
@@ -220,7 +217,6 @@ server.tool("askAzure", "Direct access to Azure Resource Management API for mana
|
|
220
217
|
resourceGroupName: z.string().optional().describe("Resource group name for resource operations"),
|
221
218
|
resourceName: z.string().optional().describe("Resource name for resource operations"),
|
222
219
|
}, async ({ path, method, apiVersion, subscriptionId, body, queryParams = {}, fetchAllPages, operation = "custom", providerNamespace, resourceType, resourceGroupName, resourceName }) => {
|
223
|
-
console.error(`[askAzure] Processing request: ${operation} - ${method} ${path}`);
|
224
220
|
try {
|
225
221
|
// Default API versions for common resource types
|
226
222
|
const defaultApiVersions = {
|
@@ -234,6 +230,12 @@ server.tool("askAzure", "Direct access to Azure Resource Management API for mana
|
|
234
230
|
'Microsoft.Network/virtualNetworks': '2023-04-01',
|
235
231
|
'Microsoft.KeyVault/vaults': '2023-02-01'
|
236
232
|
};
|
233
|
+
// Set default API version for common paths if not provided
|
234
|
+
if (!apiVersion && !queryParams['api-version']) {
|
235
|
+
if (path === '/subscriptions') {
|
236
|
+
apiVersion = '2022-12-01'; // Default API version for listing subscriptions
|
237
|
+
}
|
238
|
+
}
|
237
239
|
// Handle predefined operations
|
238
240
|
if (operation !== "custom") {
|
239
241
|
const requiredSubscriptionId = !['listResourceProviders', 'getResourceProvider', 'registerResourceProvider'].includes(operation);
|
@@ -310,7 +312,6 @@ server.tool("askAzure", "Direct access to Azure Resource Management API for mana
|
|
310
312
|
throw new Error("Azure Resource Management API requires an 'apiVersion' parameter");
|
311
313
|
}
|
312
314
|
// Get Azure credential
|
313
|
-
console.error(`[askAzure] Getting Azure credential`);
|
314
315
|
const credential = getAzureCredential();
|
315
316
|
// Construct the base URL and path
|
316
317
|
const baseUrl = "https://management.azure.com";
|
@@ -322,12 +323,10 @@ server.tool("askAzure", "Direct access to Azure Resource Management API for mana
|
|
322
323
|
const params = new URLSearchParams(queryParams);
|
323
324
|
if (apiVersion)
|
324
325
|
params.set('api-version', apiVersion);
|
325
|
-
console.error(`[askAzure] Requesting token for Azure Resource Management API`);
|
326
326
|
// Get access token
|
327
327
|
const tokenResponse = await credential.getToken("https://management.azure.com/.default");
|
328
328
|
if (!tokenResponse?.token)
|
329
329
|
throw new Error("Failed to acquire Azure access token");
|
330
|
-
console.error(`[askAzure] Successfully acquired token`);
|
331
330
|
// Prepare request options
|
332
331
|
const headers = {
|
333
332
|
'Authorization': `Bearer ${tokenResponse.token}`,
|
@@ -342,12 +341,10 @@ server.tool("askAzure", "Direct access to Azure Resource Management API for mana
|
|
342
341
|
}
|
343
342
|
// Construct URL
|
344
343
|
const url = `${baseUrl}${fullPath}?${params.toString()}`;
|
345
|
-
console.error(`[askAzure] Making request to ${url}`);
|
346
344
|
// Execute request with pagination if needed
|
347
345
|
let result;
|
348
346
|
if (method === 'get' && fetchAllPages) {
|
349
347
|
// Fetch first page
|
350
|
-
console.error(`[askAzure] Executing GET with pagination`);
|
351
348
|
const response = await fetch(url, options);
|
352
349
|
if (!response.ok) {
|
353
350
|
const errorText = await response.text();
|
@@ -363,7 +360,6 @@ server.tool("askAzure", "Direct access to Azure Resource Management API for mana
|
|
363
360
|
const allItems = [...(firstPage.value || [])];
|
364
361
|
let nextLink = firstPage.nextLink;
|
365
362
|
while (nextLink) {
|
366
|
-
console.error(`[askAzure] Fetching next page: ${nextLink}`);
|
367
363
|
const pageResponse = await fetch(nextLink, options);
|
368
364
|
if (!pageResponse.ok)
|
369
365
|
throw new Error(`Azure API pagination error: ${pageResponse.status}`);
|
@@ -380,11 +376,9 @@ server.tool("askAzure", "Direct access to Azure Resource Management API for mana
|
|
380
376
|
}
|
381
377
|
else {
|
382
378
|
// Single page request
|
383
|
-
console.error(`[askAzure] Executing ${method} request`);
|
384
379
|
const response = await fetch(url, options);
|
385
380
|
if (!response.ok) {
|
386
381
|
const errorText = await response.text();
|
387
|
-
console.error(`[askAzure] Request failed with status ${response.status}: ${errorText}`);
|
388
382
|
let errorDetail;
|
389
383
|
try {
|
390
384
|
errorDetail = JSON.parse(errorText);
|
@@ -400,14 +394,9 @@ server.tool("askAzure", "Direct access to Azure Resource Management API for mana
|
|
400
394
|
const text = await response.text();
|
401
395
|
result = text ? JSON.parse(text) : { status: "Success" };
|
402
396
|
}
|
403
|
-
console.error(`[askAzure] Successfully executed request`);
|
404
397
|
return formatApiResponse('Azure', method, path, result);
|
405
398
|
}
|
406
399
|
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
|
-
}
|
411
400
|
return formatErrorResponse(err, 'Azure');
|
412
401
|
}
|
413
402
|
});
|