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:
-
Package Entry Point –
src/skillspector/__init__.pyexposes the compiled graph viagraphand acreate_graphfactory function for advanced customization. -
State Schema – All data exchanged between nodes follows the
SkillspectorStateTypedDict defined insrc/skillspector/state.py. This schema validates that required fields likeskill_pathandoutput_formatare present while carrying intermediate data between analysis stages. -
Graph Construction –
src/skillspector/graph.pybuilds aStateGraphconnecting nodes for input handling, context building, static analyzers, LLM-based meta-analysis, and reporting. Thegraph.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 URLoutput_format: One ofterminal,json,markdown, orsarifuse_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.graphto access the compiled LangGraph workflow defined insrc/skillspector/graph.py. - Construct state using the
SkillspectorStateschema fromsrc/skillspector/state.py, providing at minimumskill_path,output_format, anduse_llm. - Configure providers via
SKILLSPECTOR_PROVIDERandSKILLSPECTOR_MODELenvironment variables handled bysrc/skillspector/providers/base.py. - Call
graph.invoke(state)to execute the scan and receive a dictionary containingfindings,report_body,sarif_report, andrisk_score. - Disable LLMs by setting
use_llm: Falsein state to run static analysis only, matching the CLI--no-llmflag 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →