@north7/entraaware 0.0.4 → 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 +150 -50
  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.3",
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: [
@@ -80,7 +134,25 @@ function formatErrorResponse(err, apiType) {
80
134
  ],
81
135
  };
82
136
  }
137
+ // Process OData parameters for Graph API
138
+ function processODataParams({ queryParams = {}, select, filter, expand, orderBy, top, count }) {
139
+ const processedParams = { ...queryParams };
140
+ if (select)
141
+ processedParams['$select'] = select;
142
+ if (filter)
143
+ processedParams['$filter'] = filter;
144
+ if (expand)
145
+ processedParams['$expand'] = expand;
146
+ if (orderBy)
147
+ processedParams['$orderby'] = orderBy;
148
+ if (top !== undefined)
149
+ processedParams['$top'] = top.toString();
150
+ if (count)
151
+ processedParams['$count'] = 'true';
152
+ return processedParams;
153
+ }
83
154
  // MICROSOFT GRAPH API TOOL
155
+ // This tool implementation is inspired by and adapted from Lokka-Microsoft's Graph API handling
84
156
  server.tool("askEntra", "Direct access to Microsoft Graph API for accurate Entra (Azure AD) data", {
85
157
  path: z.string().describe("The Graph API URL path (e.g. '/users/{id}/memberOf', '/directoryRoles')"),
86
158
  method: z.enum(["get", "post", "put", "patch", "delete"]).default("get").describe("HTTP method to use"),
@@ -97,51 +169,38 @@ server.tool("askEntra", "Direct access to Microsoft Graph API for accurate Entra
97
169
  top: z.number().optional().describe("Shorthand for $top query parameter"),
98
170
  count: z.boolean().optional().describe("Shorthand for $count=true to include count of items"),
99
171
  }, async ({ path, method, queryParams = {}, body, apiVersion, fetchAllPages, consistencyLevel, select, filter, expand, orderBy, top, count }) => {
100
- console.error(`[askEntra] Processing request: ${method} ${path}`);
101
172
  try {
102
173
  // Process shorthand query parameters
103
- const processedParams = { ...queryParams };
104
- if (select)
105
- processedParams['$select'] = select;
106
- if (filter)
107
- processedParams['$filter'] = filter;
108
- if (expand)
109
- processedParams['$expand'] = expand;
110
- if (orderBy)
111
- processedParams['$orderby'] = orderBy;
112
- if (top !== undefined)
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
174
+ const processedParams = processODataParams({
175
+ queryParams,
176
+ select,
177
+ filter,
178
+ expand,
179
+ orderBy,
180
+ top,
181
+ count
182
+ });
183
+ // Initialize client on demand
121
184
  if (!graphClient) {
185
+ const credential = getAzureCredential();
122
186
  const authProvider = new TokenCredentialAuthenticationProvider(credential, {
123
187
  scopes: ["https://graph.microsoft.com/.default"],
124
188
  });
125
189
  graphClient = Client.initWithMiddleware({ authProvider });
126
- console.error(`[askEntra] Graph client initialized`);
127
190
  }
128
191
  // Build request with API path and version
129
- console.error(`[askEntra] Creating request for ${path} with version ${apiVersion}`);
130
192
  let request = graphClient.api(path).version(apiVersion);
131
193
  // Add query parameters
132
194
  if (Object.keys(processedParams).length > 0) {
133
- console.error(`[askEntra] Adding query parameters: ${JSON.stringify(processedParams)}`);
134
195
  request = request.query(processedParams);
135
196
  }
136
197
  // Add consistency level header if provided
137
198
  if (consistencyLevel) {
138
- console.error(`[askEntra] Adding consistency level: ${consistencyLevel}`);
139
199
  request = request.header('ConsistencyLevel', consistencyLevel);
140
200
  }
141
201
  // Handle pagination for GET requests
142
202
  let result;
143
203
  if (method === 'get' && fetchAllPages) {
144
- console.error(`[askEntra] Executing GET with pagination`);
145
204
  const firstPage = await request.get();
146
205
  // If no pagination needed, return first page
147
206
  if (!firstPage["@odata.nextLink"]) {
@@ -152,7 +211,6 @@ server.tool("askEntra", "Direct access to Microsoft Graph API for accurate Entra
152
211
  const allItems = [...(firstPage.value || [])];
153
212
  let nextLink = firstPage["@odata.nextLink"];
154
213
  while (nextLink) {
155
- console.error(`[askEntra] Fetching next page: ${nextLink}`);
156
214
  const nextPage = await graphClient.api(nextLink).get();
157
215
  if (nextPage.value)
158
216
  allItems.push(...nextPage.value);
@@ -168,7 +226,6 @@ server.tool("askEntra", "Direct access to Microsoft Graph API for accurate Entra
168
226
  }
169
227
  else {
170
228
  // Execute appropriate method
171
- console.error(`[askEntra] Executing ${method} request`);
172
229
  switch (method) {
173
230
  case 'get':
174
231
  result = await request.get();
@@ -188,18 +245,14 @@ server.tool("askEntra", "Direct access to Microsoft Graph API for accurate Entra
188
245
  break;
189
246
  }
190
247
  }
191
- console.error(`[askEntra] Successfully executed request`);
192
248
  return formatApiResponse('Entra', method, path, result);
193
249
  }
194
250
  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
251
  return formatErrorResponse(err, 'Entra');
200
252
  }
201
253
  });
202
254
  // AZURE RESOURCE MANAGEMENT API TOOL
255
+ // This tool implementation is inspired by and adapted from Lokka-Microsoft's Azure API handling
203
256
  server.tool("askAzure", "Direct access to Azure Resource Management API for managing Azure resources", {
204
257
  path: z.string().describe("The Azure API path (e.g. '/subscriptions', '/resourceGroups/{name}')"),
205
258
  method: z.enum(["get", "post", "put", "patch", "delete"]).default("get").describe("HTTP method to use"),
@@ -220,20 +273,35 @@ server.tool("askAzure", "Direct access to Azure Resource Management API for mana
220
273
  resourceGroupName: z.string().optional().describe("Resource group name for resource operations"),
221
274
  resourceName: z.string().optional().describe("Resource name for resource operations"),
222
275
  }, async ({ path, method, apiVersion, subscriptionId, body, queryParams = {}, fetchAllPages, operation = "custom", providerNamespace, resourceType, resourceGroupName, resourceName }) => {
223
- console.error(`[askAzure] Processing request: ${operation} - ${method} ${path}`);
224
276
  try {
225
277
  // Default API versions for common resource types
226
278
  const defaultApiVersions = {
227
279
  'resources': '2021-04-01',
228
280
  'resourceGroups': '2021-04-01',
229
- 'subscriptions': '2021-01-01',
281
+ 'subscriptions': '2022-12-01',
230
282
  'providers': '2021-04-01',
231
283
  'deployments': '2021-04-01',
232
284
  'Microsoft.Compute/virtualMachines': '2023-03-01',
233
285
  'Microsoft.Storage/storageAccounts': '2023-01-01',
234
286
  'Microsoft.Network/virtualNetworks': '2023-04-01',
235
- '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'
236
290
  };
291
+ // Set default API version for common paths if not provided
292
+ if (!apiVersion && !queryParams['api-version']) {
293
+ if (path === '/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];
303
+ }
304
+ }
237
305
  // Handle predefined operations
238
306
  if (operation !== "custom") {
239
307
  const requiredSubscriptionId = !['listResourceProviders', 'getResourceProvider', 'registerResourceProvider'].includes(operation);
@@ -281,7 +349,21 @@ server.tool("askAzure", "Direct access to Azure Resource Management API for mana
281
349
  path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/${providerNamespace}/${resourceType}/${resourceName}`;
282
350
  method = 'put';
283
351
  const providerResourceKey = `${providerNamespace}/${resourceType}`;
284
- 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
+ }
285
367
  break;
286
368
  case 'deployTemplate':
287
369
  if (!resourceGroupName)
@@ -301,16 +383,46 @@ server.tool("askAzure", "Direct access to Azure Resource Management API for mana
301
383
  path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/${providerNamespace}/${resourceType}/${resourceName}`;
302
384
  method = 'delete';
303
385
  const deleteResourceKey = `${providerNamespace}/${resourceType}`;
304
- 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
+ }
305
401
  break;
306
402
  }
307
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
+ }
308
421
  // Ensure API version is provided
309
422
  if (!apiVersion && !queryParams['api-version']) {
310
423
  throw new Error("Azure Resource Management API requires an 'apiVersion' parameter");
311
424
  }
312
425
  // Get Azure credential
313
- console.error(`[askAzure] Getting Azure credential`);
314
426
  const credential = getAzureCredential();
315
427
  // Construct the base URL and path
316
428
  const baseUrl = "https://management.azure.com";
@@ -322,12 +434,10 @@ server.tool("askAzure", "Direct access to Azure Resource Management API for mana
322
434
  const params = new URLSearchParams(queryParams);
323
435
  if (apiVersion)
324
436
  params.set('api-version', apiVersion);
325
- console.error(`[askAzure] Requesting token for Azure Resource Management API`);
326
437
  // Get access token
327
438
  const tokenResponse = await credential.getToken("https://management.azure.com/.default");
328
439
  if (!tokenResponse?.token)
329
440
  throw new Error("Failed to acquire Azure access token");
330
- console.error(`[askAzure] Successfully acquired token`);
331
441
  // Prepare request options
332
442
  const headers = {
333
443
  'Authorization': `Bearer ${tokenResponse.token}`,
@@ -342,12 +452,10 @@ server.tool("askAzure", "Direct access to Azure Resource Management API for mana
342
452
  }
343
453
  // Construct URL
344
454
  const url = `${baseUrl}${fullPath}?${params.toString()}`;
345
- console.error(`[askAzure] Making request to ${url}`);
346
455
  // Execute request with pagination if needed
347
456
  let result;
348
457
  if (method === 'get' && fetchAllPages) {
349
458
  // Fetch first page
350
- console.error(`[askAzure] Executing GET with pagination`);
351
459
  const response = await fetch(url, options);
352
460
  if (!response.ok) {
353
461
  const errorText = await response.text();
@@ -363,7 +471,6 @@ server.tool("askAzure", "Direct access to Azure Resource Management API for mana
363
471
  const allItems = [...(firstPage.value || [])];
364
472
  let nextLink = firstPage.nextLink;
365
473
  while (nextLink) {
366
- console.error(`[askAzure] Fetching next page: ${nextLink}`);
367
474
  const pageResponse = await fetch(nextLink, options);
368
475
  if (!pageResponse.ok)
369
476
  throw new Error(`Azure API pagination error: ${pageResponse.status}`);
@@ -380,11 +487,9 @@ server.tool("askAzure", "Direct access to Azure Resource Management API for mana
380
487
  }
381
488
  else {
382
489
  // Single page request
383
- console.error(`[askAzure] Executing ${method} request`);
384
490
  const response = await fetch(url, options);
385
491
  if (!response.ok) {
386
492
  const errorText = await response.text();
387
- console.error(`[askAzure] Request failed with status ${response.status}: ${errorText}`);
388
493
  let errorDetail;
389
494
  try {
390
495
  errorDetail = JSON.parse(errorText);
@@ -400,14 +505,9 @@ server.tool("askAzure", "Direct access to Azure Resource Management API for mana
400
505
  const text = await response.text();
401
506
  result = text ? JSON.parse(text) : { status: "Success" };
402
507
  }
403
- console.error(`[askAzure] Successfully executed request`);
404
508
  return formatApiResponse('Azure', method, path, result);
405
509
  }
406
510
  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
511
  return formatErrorResponse(err, 'Azure');
412
512
  }
413
513
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north7/entraaware",
3
- "version": "0.0.4",
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
  ],