Skip to main content
Glama

inkog_generate_mlbom

Generate a Machine Learning Bill of Materials (MLBOM) for AI agents to document models, tools, data sources, and dependencies for supply chain compliance.

Instructions

Generate a Machine Learning Bill of Materials (MLBOM) for AI agents. Lists all models, tools, data sources, frameworks, and dependencies. Supports CycloneDX and SPDX formats. Use this when documenting AI agent dependencies for supply chain compliance.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
pathYesPath to agent codebase to analyze
formatNoOutput format: cyclonedx (recommended), spdx, or jsoncyclonedx
include_vulnerabilitiesNoInclude known vulnerabilities for detected components

Implementation Reference

  • The mlbomHandler function handles the logic for generating the MLBOM by first scanning the project files and then calling the Inkog API to generate the BOM report.
    async function mlbomHandler(rawArgs: Record<string, unknown>): Promise<ToolResult> {
      // Validate arguments
      const parseResult = MlbomArgsSchema.safeParse(rawArgs);
      if (!parseResult.success) {
        return {
          content: [
            {
              type: 'text',
              text: `Invalid arguments: ${parseResult.error.message}`,
            },
          ],
          isError: true,
        };
      }
    
      const args: MlbomArgs = parseResult.data;
    
      try {
        // Read files from path
        const readResult = readDirectory(args.path);
    
        if (readResult.files.length === 0) {
          return {
            content: [
              {
                type: 'text',
                text: `No files found in: ${args.path}`,
              },
            ],
            isError: true,
          };
        }
    
        // Get relative paths
        const files = getRelativePaths(readResult.files, args.path);
    
        // Call Inkog API - first scan, then generate MLBOM
        const client = getClient();
    
        // Step 1: Run a scan to get a scan_id
        const scanResponse = await client.scan(files, { policy: 'balanced' });
        if (!scanResponse.success || !scanResponse.scan_id) {
          return {
            content: [
              {
                type: 'text',
                text: 'Scan failed: Unable to analyze files',
              },
            ],
            isError: true,
          };
        }
    
        // Step 2: Use scan_id to generate MLBOM
        const response: MlbomResponse = await client.generateMlbom([], {
          format: args.format,
          includeVulnerabilities: args.include_vulnerabilities,
          scanId: scanResponse.scan_id,
        });
    
        // Build header
        let output = '╔══════════════════════════════════════════════════════╗\n';
        output += '║           📋 ML Bill of Materials (MLBOM)             ║\n';
        output += '╚══════════════════════════════════════════════════════╝\n\n';
    
        output += `📦 Format: ${response.format.toUpperCase()}\n`;
        output += `📅 Generated: ${response.generated_at}\n`;
    
        if (response.report_id) {
          output += `🔗 Report ID: ${response.report_id}\n`;
        }
    
        // Summary statistics
        output += '\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n';
        output += '📊 COMPONENT SUMMARY\n\n';
        output += formatSummaryScore(response.summary) + '\n\n';
    
        if (response.summary.total_components > 0) {
          if (response.summary.models > 0) {
            output += `   🧠 Models: ${response.summary.models}\n`;
          }
          if (response.summary.frameworks > 0) {
            output += `   📦 Frameworks: ${response.summary.frameworks}\n`;
          }
          if (response.summary.tools > 0) {
            output += `   🔧 Tools: ${response.summary.tools}\n`;
          }
          if (response.summary.data_sources > 0) {
            output += `   💾 Data Sources: ${response.summary.data_sources}\n`;
          }
          if (response.summary.dependencies > 0) {
            output += `   📚 Dependencies: ${response.summary.dependencies}\n`;
          }
        }
    
        // Warning if applicable
        if (response.warning) {
          output += '\n⚠️  WARNING: ' + response.warning + '\n';
        }
    
        // Completeness metrics if available
        if (response.completeness) {
          output += '\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n';
          output += formatCompleteness(response.completeness);
        }
    
        // Handle different output formats
        if (args.format !== 'json' && response.bom) {
          // CycloneDX or SPDX format - show the structured BOM
          output += '\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n';
          output += `${args.format.toUpperCase()} Output:\n\n`;
          output += '```json\n';
          output += typeof response.bom === 'string'
            ? response.bom
            : JSON.stringify(response.bom, null, 2);
          output += '\n```\n';
        } else if (response.components && response.components.length > 0) {
          // JSON format with detailed component list
          const groups = groupComponentsByType(response.components);
          const typeOrder: ComponentType[] = ['model', 'tool', 'data-source', 'framework', 'dependency'];
    
          for (const type of typeOrder) {
            const components = groups[type];
            if (components && components.length > 0) {
              output += '\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n';
    
              switch (type) {
                case 'model':
                  output += '🧠 MODELS\n\n';
                  break;
                case 'tool':
                  output += '🔧 TOOLS\n\n';
                  break;
                case 'data-source':
                  output += '💾 DATA SOURCES\n\n';
                  break;
                case 'framework':
                  output += '📦 FRAMEWORKS\n\n';
                  break;
                case 'dependency':
                  output += '📚 DEPENDENCIES\n\n';
                  break;
              }
    
              // Show detailed info for models/tools, compact for dependencies
              const showDetailed = type === 'model' || type === 'tool' || type === 'data-source';
              for (const component of components) {
                output += formatComponent(component, showDetailed) + '\n';
              }
            }
          }
        }
    
        // Footer
        output += '\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n';
        output += '💡 TIP: Use format="cyclonedx" for SBOM compliance tooling\n';
        output += 'MLBOM generation powered by Inkog AI Security Platform\n';
        output += 'Learn more: https://inkog.io/mlbom\n';
    
        return {
          content: [
            {
              type: 'text',
              text: output,
            },
          ],
        };
      } catch (error) {
        if (error instanceof InkogAuthError) {
          return {
            content: [
              {
                type: 'text',
                text: '🔐 API Key Required\n\nGet your free key at https://app.inkog.io',
              },
            ],
            isError: true,
          };
        }
    
        if (error instanceof InkogRateLimitError) {
          return {
            content: [
              {
                type: 'text',
                text: `⏱️ Rate Limited\n\nToo many requests. Please retry after ${error.retryAfter} seconds.`,
              },
            ],
            isError: true,
          };
        }
    
        if (error instanceof InkogNetworkError) {
          return {
            content: [
              {
                type: 'text',
                text: `Network error: ${error.message}`,
              },
            ],
            isError: true,
          };
        }
    
        if (error instanceof InkogApiError) {
          return {
            content: [
              {
                type: 'text',
                text: `API error: ${error.message}${error.details ? `\n\nDetails: ${JSON.stringify(error.details)}` : ''}`,
              },
            ],
            isError: true,
          };
        }
    
        const message = error instanceof Error ? error.message : 'Unknown error occurred';
        return {
          content: [
            {
              type: 'text',
              text: `Error: ${message}`,
            },
          ],
          isError: true,
        };
      }
    }
  • MlbomArgsSchema defines the input arguments required for the inkog_generate_mlbom tool.
    const MlbomArgsSchema = z.object({
      path: z.string().describe('Path to agent codebase to analyze'),
      format: z
        .enum(['cyclonedx', 'spdx', 'json'])
        .optional()
        .default('cyclonedx')
        .describe('Output format: cyclonedx (recommended), spdx, or json'),
      include_vulnerabilities: z
        .boolean()
        .optional()
        .default(true)
        .describe('Include known vulnerabilities for detected components'),
    });
  • The registration of the inkog_generate_mlbom tool, mapping it to its handler.
    export const mlbomTool: ToolDefinition = {
      tool: {
        name: 'inkog_generate_mlbom',
        description:
          'Generate a Machine Learning Bill of Materials (MLBOM) for AI agents. Lists all models, tools, data sources, frameworks, and dependencies. Supports CycloneDX and SPDX formats. Use this when documenting AI agent dependencies for supply chain compliance.',
        inputSchema: {
          type: 'object',
          properties: {
            path: {
              type: 'string',
              description: 'Path to agent codebase to analyze',
            },
            format: {
              type: 'string',
              enum: ['cyclonedx', 'spdx', 'json'],
              default: 'cyclonedx',
              description: 'Output format: cyclonedx (recommended), spdx, or json',
            },
            include_vulnerabilities: {
              type: 'boolean',
              default: true,
              description: 'Include known vulnerabilities for detected components',
            },
          },
          required: ['path'],
        },
      },
      handler: mlbomHandler,
    };
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It mentions the tool 'Supports CycloneDX and SPDX formats' and implies analysis of a codebase, but doesn't disclose behavioral traits like whether it's read-only vs. destructive, authentication needs, rate limits, error handling, or output structure. The description adds some context but lacks comprehensive behavioral disclosure.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is efficiently structured in two sentences: the first states the purpose and scope, the second provides usage guidelines. Every sentence earns its place with no wasted words, and it's front-loaded with the core functionality.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's moderate complexity (analyzing codebases for dependencies), no annotations, and no output schema, the description is adequate but incomplete. It covers purpose and usage well but lacks details on behavioral traits, output format examples, error conditions, or prerequisites. It's minimally viable but has clear gaps for a tool with no structured output documentation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all three parameters thoroughly. The description adds minimal value beyond the schema by mentioning 'Supports CycloneDX and SPDX formats' (which aligns with the 'format' parameter) and 'for AI agents' (implied in the 'path' parameter context). Baseline 3 is appropriate when the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the specific action ('Generate a Machine Learning Bill of Materials'), the resource ('for AI agents'), and the scope ('Lists all models, tools, data sources, frameworks, and dependencies'). It distinguishes from siblings by focusing on MLBOM generation rather than auditing, scanning, or compliance reporting.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly states when to use this tool: 'Use this when documenting AI agent dependencies for supply chain compliance.' This provides clear context and distinguishes it from sibling tools that might perform audits, scans, or explain findings without generating a structured bill of materials.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

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/inkog-io/inkog'

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