Skip to main content
Glama

Binalyze AIR MCP Server

Official
by binalyze
MIT License
66
7
  • Linux
  • Apple
assets.ts17 kB
// src/tools/assets.ts import { z } from 'zod'; import { api, Asset, AssetDetail, AssetTask } from '../api/assets/assets'; // Schema for list assets arguments export const ListAssetsArgsSchema = z.object({ organizationIds: z.union([ z.string(), z.array(z.string()) ]).optional().describe('Organization IDs to filter assets by. Defaults to "0" or specific IDs like "123" or ["123", "456"]'), }); // Schema for get asset by id arguments export const GetAssetByIdArgsSchema = z.object({ id: z.string().describe('The ID of the asset to retrieve'), }); // Schema for get asset tasks by id arguments export const GetAssetTasksByIdArgsSchema = z.object({ id: z.string().describe('The ID of the asset to retrieve tasks for'), }); // Schema for uninstall assets arguments, mimicking the AssetFilter structure export const UninstallAssetsArgsSchema = z.object({ filter: z.object({ searchTerm: z.string().optional(), name: z.string().optional(), ipAddress: z.string().optional(), groupId: z.string().optional(), groupFullPath: z.string().optional(), managedStatus: z.array(z.string()).optional(), isolationStatus: z.array(z.string()).optional(), platform: z.array(z.string()).optional(), issue: z.string().optional(), onlineStatus: z.array(z.string()).optional(), tagId: z.string().optional(), version: z.string().optional(), policy: z.string().optional(), includedEndpointIds: z.array(z.string()).optional().describe('Required: At least one endpoint ID must be included for the uninstall operation.'), excludedEndpointIds: z.array(z.string()).optional(), organizationIds: z.array(z.union([z.number(), z.string()])).optional().default([0]), }).describe('Filter criteria to select assets for uninstallation without purge. `includedEndpointIds` is the primary way to target specific assets.') }); // Schema for purge and uninstall assets arguments // It reuses the structure but enforces includedEndpointIds must not be empty. export const PurgeAndUninstallAssetsArgsSchema = z.object({ filter: z.object({ searchTerm: z.string().optional(), name: z.string().optional(), ipAddress: z.string().optional(), groupId: z.string().optional(), groupFullPath: z.string().optional(), managedStatus: z.array(z.string()).optional(), isolationStatus: z.array(z.string()).optional(), platform: z.array(z.string()).optional(), issue: z.string().optional(), onlineStatus: z.array(z.string()).optional(), tagId: z.string().optional(), version: z.string().optional(), policy: z.string().optional(), // Enforce that includedEndpointIds is provided and not empty includedEndpointIds: z.array(z.string()).min(1, { message: "Required: At least one endpoint ID must be included for the purge and uninstall operation." }), excludedEndpointIds: z.array(z.string()).optional(), organizationIds: z.array(z.union([z.number(), z.string()])).optional().default([0]), }).describe('Filter criteria to select assets for purge and uninstallation. `includedEndpointIds` is REQUIRED.') }); // Schema for adding tags to assets arguments export const AddTagsToAssetsArgsSchema = z.object({ filter: z.object({ searchTerm: z.string().optional(), name: z.string().optional(), ipAddress: z.string().optional(), groupId: z.string().optional(), groupFullPath: z.string().optional(), managedStatus: z.array(z.string()).optional(), isolationStatus: z.array(z.string()).optional(), platform: z.array(z.string()).optional(), issue: z.string().optional(), onlineStatus: z.array(z.string()).optional(), tagId: z.string().optional(), // Note: This filter might target assets that *already* have a specific tag. version: z.string().optional(), policy: z.string().optional(), includedEndpointIds: z.array(z.string()).min(1, { message: "Required: At least one endpoint ID must be included to add tags." }), excludedEndpointIds: z.array(z.string()).optional(), organizationIds: z.array(z.union([z.number(), z.string()])).optional().default([0]), }).describe('Filter criteria to select assets for adding tags. `includedEndpointIds` is REQUIRED.'), tags: z.array(z.string()).min(1, { message: "Required: At least one tag must be provided." }).describe('Array of tags to add to the selected assets.'), }); // Schema for removing tags from assets arguments export const RemoveTagsFromAssetsArgsSchema = z.object({ filter: z.object({ searchTerm: z.string().optional(), name: z.string().optional(), ipAddress: z.string().optional(), groupId: z.string().optional(), groupFullPath: z.string().optional(), managedStatus: z.array(z.string()).optional(), isolationStatus: z.array(z.string()).optional(), platform: z.array(z.string()).optional(), issue: z.string().optional(), onlineStatus: z.array(z.string()).optional(), tagId: z.string().optional(), // Filter assets by existing tag ID before removal version: z.string().optional(), policy: z.string().optional(), includedEndpointIds: z.array(z.string()).min(1, { message: "Required: At least one endpoint ID must be included to remove tags." }), excludedEndpointIds: z.array(z.string()).optional(), organizationIds: z.array(z.union([z.number(), z.string()])).optional().default([0]), }).describe('Filter criteria to select assets for removing tags. `includedEndpointIds` is REQUIRED.'), tags: z.array(z.string()).min(1, { message: "Required: At least one tag must be provided to remove." }).describe('Array of tags to remove from the selected assets.'), }); // Format asset for display function formatAsset(asset: Asset): string { return ` Asset: ${asset.name} (${asset._id}) OS: ${asset.os} Platform: ${asset.platform} CPU: ${asset.systemResources.cpu.model} (Usage: ${asset.systemResources.cpu.usage}%) RAM: ${formatBytes(asset.systemResources.ram.freeSpace)} free of ${formatBytes(asset.systemResources.ram.totalSize)} Status: ${asset.onlineStatus} Issues: ${asset.issues.length > 0 ? asset.issues.join(', ') : 'None'} `; } // Format detailed asset information function formatAssetDetail(asset: AssetDetail): string { return ` Asset: ${asset.name} (${asset._id}) OS: ${asset.os} Platform: ${asset.platform} IP Address: ${asset.ipAddress} Group: ${asset.groupFullPath} (${asset.groupId}) Type: ${asset.isServer ? 'Server' : 'Workstation'} Management: ${asset.isManaged ? 'Managed' : 'Unmanaged'} Last Seen: ${new Date(asset.lastSeen).toLocaleString()} Version: ${asset.version} (${asset.versionNo}) Registered: ${new Date(asset.registeredAt).toLocaleString()} Created: ${new Date(asset.createdAt).toLocaleString()} Updated: ${new Date(asset.updatedAt).toLocaleString()} Organization ID: ${asset.organizationId} Online Status: ${asset.onlineStatus} Isolation Status: ${asset.isolationStatus} Tags: ${asset.tags.length > 0 ? asset.tags.join(', ') : 'None'} Issues: ${asset.issues.length > 0 ? asset.issues.join(', ') : 'None'} Waiting For Version Update Fix: ${asset.waitingForVersionUpdateFix ? 'Yes' : 'No'} Policies: ${asset.policies.length > 0 ? asset.policies.length : 'None'} `; } // Format asset task information function formatAssetTask(task: AssetTask): string { return ` Task: ${task.name} (${task._id}) Task ID: ${task.taskId} Type: ${task.type} Endpoint: ${task.endpointName} (${task.endpointId}) Organization ID: ${task.organizationId} Status: ${task.status} Progress: ${task.progress}% Duration: ${task.duration !== null ? `${task.duration} seconds` : 'N/A'} Case IDs: ${task.caseIds ? task.caseIds.join(', ') : 'None'} Created: ${new Date(task.createdAt).toLocaleString()} Updated: ${new Date(task.updatedAt).toLocaleString()} `; } // Helper function to format bytes to human-readable format function formatBytes(bytes: number): string { const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; if (bytes === 0) return '0 Bytes'; const i = Math.floor(Math.log(bytes) / Math.log(1024)); return parseFloat((bytes / Math.pow(1024, i)).toFixed(2)) + ' ' + sizes[i]; } export const assetTools = { // List all assets async listAssets(args: z.infer<typeof ListAssetsArgsSchema>) { try { const orgIds = args.organizationIds === undefined || args.organizationIds === "" ? "0" : args.organizationIds; const response = await api.getAssets(orgIds); if (!response.success) { return { content: [ { type: 'text', text: `Error fetching assets: ${response.errors.join(', ')}` } ] }; } const assetList = response.result.entities.map((asset: Asset) => `${asset._id}: ${asset.name} (${asset.platform} - ${asset.os})` ).join('\n'); return { content: [ { type: 'text', text: `Found ${response.result.totalEntityCount} assets:\n${assetList}` } ] }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; return { content: [ { type: 'text', text: `Failed to fetch assets: ${errorMessage}` } ] }; } }, // Get asset by ID async getAssetById(args: z.infer<typeof GetAssetByIdArgsSchema>) { try { const response = await api.getAssetById(args.id); if (!response.success) { return { content: [ { type: 'text', text: `Error fetching asset: ${response.errors.join(', ')}` } ] }; } return { content: [ { type: 'text', text: `Asset details:\n${formatAssetDetail(response.result)}` } ] }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; return { content: [ { type: 'text', text: `Failed to fetch asset: ${errorMessage}` } ] }; } }, // Get asset tasks by ID async getAssetTasksById(args: z.infer<typeof GetAssetTasksByIdArgsSchema>) { try { const response = await api.getAssetTasksById(args.id); if (!response.success) { return { content: [ { type: 'text', text: `Error fetching asset tasks: ${response.errors.join(', ')}` } ] }; } if (response.result.entities.length === 0) { return { content: [ { type: 'text', text: `No tasks found for asset with ID ${args.id}` } ] }; } const tasksOverview = response.result.entities.map(task => `${task._id}: ${task.name} (Type: ${task.type}, Status: ${task.status}, Progress: ${task.progress}%)` ).join('\n'); return { content: [ { type: 'text', text: `Found ${response.result.totalEntityCount} tasks for asset with ID ${args.id}:\n${tasksOverview}` } ] }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; return { content: [ { type: 'text', text: `Failed to fetch asset tasks: ${errorMessage}` } ] }; } }, // Uninstall assets by filter without purge async uninstallAssets(args: z.infer<typeof UninstallAssetsArgsSchema>) { try { // Basic validation: Ensure at least one includedEndpointId is provided, even if schema makes it optional. // The API call itself might implicitly require it based on behavior, despite the optional schema field. if (!args.filter.includedEndpointIds || args.filter.includedEndpointIds.length === 0) { // Simplified check: if the array is missing or empty, return an error. return { content: [ { type: 'text', text: 'Error: You must provide at least one endpoint ID in `filter.includedEndpointIds` to uninstall.' } ] }; } const response = await api.uninstallAssetsByFilter(args.filter); if (response.success) { // No need to check args.filter.includedEndpointIds again, validation above ensures it exists and is non-empty const targetedIds = args.filter.includedEndpointIds.join(', '); return { content: [ { type: 'text', text: `Successfully initiated uninstall task for assets matching the filter (targeted IDs: ${targetedIds}).` } ] }; } else { return { content: [ { type: 'text', text: `Error uninstalling assets: ${response.errors.join(', ')} (Status Code: ${response.statusCode})` } ] }; } } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error during uninstall operation'; return { content: [ { type: 'text', text: `Failed to uninstall assets: ${errorMessage}` } ] }; } }, // Purge and Uninstall assets by filter async purgeAndUninstallAssets(args: z.infer<typeof PurgeAndUninstallAssetsArgsSchema>) { try { // Zod schema already enforces includedEndpointIds.min(1) const response = await api.purgeAndUninstallAssetsByFilter(args.filter); if (response.success) { return { content: [ { type: 'text', text: `Successfully initiated purge and uninstall task for assets matching the filter (targeted IDs: ${args.filter.includedEndpointIds.join(', ')}).` } ] }; } else { return { content: [ { type: 'text', text: `Error purging and uninstalling assets: ${response.errors.join(', ')} (Status Code: ${response.statusCode})` } ] }; } } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error during purge and uninstall operation'; return { content: [ { type: 'text', text: `Failed to purge and uninstall assets: ${errorMessage}` } ] }; } }, // New tool function to add tags to assets async addTagsToAssets(args: z.infer<typeof AddTagsToAssetsArgsSchema>) { try { // Schema validation already ensures includedEndpointIds and tags are present and non-empty. const response = await api.addTagsToAssetsByFilter(args.filter, args.tags); if (response.success) { return { content: [ { type: 'text', text: `Successfully added tags [${args.tags.join(', ')}] to assets matching the filter (targeted IDs: ${args.filter.includedEndpointIds.join(', ')}).` } ] }; } else { return { content: [ { type: 'text', text: `Error adding tags to assets: ${response.errors.join(', ')} (Status Code: ${response.statusCode})` } ] }; } } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error during add tags operation'; return { content: [ { type: 'text', text: `Failed to add tags to assets: ${errorMessage}` } ] }; } }, // New tool function to remove tags from assets async removeTagsFromAssets(args: z.infer<typeof RemoveTagsFromAssetsArgsSchema>) { try { // Schema validation already ensures includedEndpointIds and tags are present and non-empty. const response = await api.removeTagsFromAssetsByFilter(args.filter, args.tags); if (response.success) { return { content: [ { type: 'text', text: `Successfully removed tags [${args.tags.join(', ')}] from assets matching the filter (targeted IDs: ${args.filter.includedEndpointIds.join(', ')}).` } ] }; } else { return { content: [ { type: 'text', text: `Error removing tags from assets: ${response.errors.join(', ')} (Status Code: ${response.statusCode})` } ] }; } } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error during remove tags operation'; return { content: [ { type: 'text', text: `Failed to remove tags from assets: ${errorMessage}` } ] }; } } };

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/binalyze/air-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server