How to Invoke SkillSpector Programmatically Using the Python API

You can invoke SkillSpector programmatically by importing the compiled LangGraph graph object from skillspector.graph and calling graph.invoke(state) with a dictionary matching the SkillspectorState schema.

SkillSpector is NVIDIA's open-source security scanner for AI skill bundles. While the tool provides a command-line interface, you can integrate it directly into Python workflows by leveraging its underlying LangGraph architecture and the programmatic API exposed in src/skillspector/graph.py.

Understanding the SkillSpector Python API Architecture

The SkillSpector Python API is built around a LangGraph workflow that processes security scans through a series of interconnected nodes. Understanding this architecture helps you construct valid inputs and interpret results correctly.

Core Components

The programmatic interface consists of three main components defined in the source code:

  1. Package Entry Point – src/skillspector/__init__.py exposes the compiled graph via graph and a create_graph factory function for advanced customization.

  2. State Schema – All data exchanged between nodes follows the SkillspectorState TypedDict defined in src/skillspector/state.py. This schema validates that required fields like skill_path and output_format are present while carrying intermediate data between analysis stages.

  3. Graph Construction – src/skillspector/graph.py builds a StateGraph connecting nodes for input handling, context building, static analyzers, LLM-based meta-analysis, and reporting. The graph.invoke() method in this file executes the entire pipeline.

Invocation Pattern

When you call graph.invoke(state, config=trace_cfg), the method accepts a state dictionary and an optional RunnableConfig for tracing. The graph runs synchronously and returns a plain Python dict containing final fields such as findings, report_body, sarif_report, and risk_score. This mirrors the behavior of src/skillspector/cli.py, which serves as a thin wrapper that prepares state and delegates to graph.invoke.

Step-by-Step Implementation Guide

Follow these steps to embed SkillSpector into your Python applications.

Import the Compiled Graph

Access the pre-configured graph instance directly from the package:

from skillspector.graph import graph

Alternatively, import create_graph from skillspector to build a custom graph instance with modified node configurations.

Build the Initial State

Construct a dictionary that satisfies the SkillspectorState schema. Only a few keys are required to initiate a scan:

  • skill_path: String path to the local skill directory or remote git URL
  • output_format: One of terminal, json, markdown, or sarif
  • use_llm: Boolean flag to enable or disable LLM-based semantic analysis

Optional keys include yara_rules_dir for custom rule paths and model_config for provider-specific settings.

Configure LLM Provider (Optional)

Set environment variables to control which model powers the analysis, as implemented in src/skillspector/providers/base.py:

import os

os.environ["SKILLSPECTOR_PROVIDER"] = "openai"
os.environ["SKILLSPECTOR_MODEL"] = "gpt-4o-mini"

The internal provider registry resolves these variables to instantiate the correct ChatModelProvider without requiring additional code.

Invoke and Read Results

Execute the scan and extract the results:

result = graph.invoke(state, config={"run_name": "security-scan"})
findings = result["findings"]  # List of Finding objects

report = result["report_body"]  # Human-readable report

sarif = result["sarif_report"]  # SARIF JSON output

The Finding objects are defined in src/skillspector/models.py and contain severity ratings, location data, and remediation suggestions.

Practical Code Examples

Minimal Scan of a Local Skill Directory

This example demonstrates the minimal code required to scan a local skill bundle and print results:

from pathlib import Path
from skillspector.graph import graph

# Build minimal required state

state = {
    "skill_path": str(Path("./my_skill").resolve()),
    "output_format": "terminal",
    "use_llm": True,
}

# Optional tracing configuration

trace_cfg = {
    "run_name": "my-skill-scan",
    "tags": ["skillspector", "example"],
    "metadata": {"skill_path": state["skill_path"]},
}

# Execute scan

result = graph.invoke(state, config=trace_cfg)

# Output results

print("=== Report ===")
print(result.get("report_body", ""))

print("\n=== Findings ===")
for f in result.get("findings", []):
    print(f"- [{f.severity}] {f.title}: {f.description}")

This implementation mirrors the CLI behavior in src/skillspector/cli.py while providing direct access to the result dictionary.

Programmatic Use with Custom LLM Provider

Configure the analysis backend programmatically using environment variables recognized by src/skillspector/providers/base.py:

import os
from skillspector.graph import graph

# Configure provider via environment (same mechanism as CLI)

os.environ["SKILLSPECTOR_PROVIDER"] = "openai"
os.environ["SKILLSPECTOR_MODEL"] = "gpt-4o-mini"

# State supports remote git URLs

state = {
    "skill_path": "https://github.com/example/malicious-skill",
    "output_format": "json",
    "use_llm": True,
}

result = graph.invoke(state)

# JSON report stored in report_body field

print(result["report_body"])

Static-Only Scan Without LLM Analysis

Disable LLM-based nodes for faster execution when only static pattern matching is required:

from skillspector.graph import graph

state = {
    "skill_path": "./some_skill",
    "output_format": "sarif",
    "use_llm": False,  # Disables meta_analyzer and semantic analyzers

}

result = graph.invoke(state)

# Access SARIF output directly

sarif = result["sarif_report"]
print(sarif)

Setting use_llm to False skips the meta_analyzer node and all LLM-dependent semantic analyzers, executing only YARA and pattern-based checks as handled in src/skillspector/cli.py.

Summary

  • Import the graph from skillspector.graph to access the compiled LangGraph workflow defined in src/skillspector/graph.py.
  • Construct state using the SkillspectorState schema from src/skillspector/state.py, providing at minimum skill_path, output_format, and use_llm.
  • Configure providers via SKILLSPECTOR_PROVIDER and SKILLSPECTOR_MODEL environment variables handled by src/skillspector/providers/base.py.
  • Call graph.invoke(state) to execute the scan and receive a dictionary containing findings, report_body, sarif_report, and risk_score.
  • Disable LLMs by setting use_llm: False in state to run static analysis only, matching the CLI --no-llm flag behavior.

Frequently Asked Questions

What is the minimum required state to invoke SkillSpector?

You must provide a dictionary with three keys: skill_path (string pointing to the skill directory or URL), output_format (string: terminal, json, markdown, or sarif), and use_llm (boolean). These fields are defined in src/skillspector/state.py as part of the SkillspectorState TypedDict. Additional optional keys like yara_rules_dir can customize the analysis behavior.

How do I configure a custom LLM provider when using the Python API?

Set the SKILLSPECTOR_PROVIDER and SKILLSPECTOR_MODEL environment variables before invoking the graph, as the provider registry in src/skillspector/providers/base.py resolves these at runtime. Alternatively, include a model_config entry in your state dictionary to override defaults programmatically without modifying environment variables.

Can I run SkillSpector without LLM analysis for faster execution?

Yes. Set "use_llm": False in your state dictionary before calling graph.invoke(). This configuration skips the meta_analyzer node and all LLM-based semantic analyzers, executing only static YARA and pattern checks. This programmatic approach corresponds to the --no-llm CLI flag implemented in src/skillspector/cli.py.

What does the graph.invoke() method return?

The method returns a plain Python dictionary containing the final state after graph execution. Key fields include findings (list of Finding objects from src/skillspector/models.py), report_body (human-readable text), sarif_report (SARIF JSON), and risk_score (numeric assessment). The structure matches the output schema defined in src/skillspector/state.py.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →