How to Use SkillSpector with LangGraph: Integration Guide and API Reference
SkillSpector exposes a compiled LangGraph StateGraph that executes security scans through a series of nodes, allowing you to invoke it directly, run it from the CLI, or embed it as a subgraph in larger LangGraph workflows.
SkillSpector is an open-source security scanner from NVIDIA built on top of LangGraph, a Python framework for composing stateful graph workflows. The repository implements a complete analysis pipeline as a LangGraph StateGraph, making it trivial to integrate into existing agentic systems or run standalone. Understanding how SkillSpector leverages LangGraph's node-based architecture enables you to customize the security scanning workflow or compose it into complex multi-agent systems.
Understanding the SkillSpector LangGraph Architecture
The core workflow lives in src/skillspector/graph.py, where the create_graph() function constructs a StateGraph using the SkillspectorState Pydantic model defined in src/skillspector/state.py. The graph follows a linear progression with branching analyzer nodes:
START → resolve_input → build_context → [analyzers] → meta_analyzer → report → END
Each node performs a distinct function in the security scan pipeline.
State Management with SkillspectorState
The workflow state is strictly typed via the SkillspectorState Pydantic model in src/skillspector/state.py. This state object tracks:
input_path/skill_path: The original skill location provided by the usertemp_dir_for_cleanup: Temporary directory requiring post-execution cleanupcomponent_metadata: Extracted metadata gathered by thebuild_contextnodefindings: Detector outputs from each analyzer noderisk_score: Aggregated numeric risk rating computed by themeta_analyzersarif_report/report_body: Final rendered artifacts from thereportnode
Node Structure and Execution Flow
The graph executes six distinct node types defined in src/skillspector/nodes/:
resolve_input: Normalizes user input (directories, zip files, URLs, or git repos) into a temporary pathbuild_context: Walks the resolved path and gathers component metadata (executables, manifests) into the state- Analyzer nodes: Static YARA rules, AST checks, and LLM-driven semantic analyzers registered in
src/skillspector/nodes/analyzers.py meta_analyzer: Aggregates all findings, computes the risk score, and optionally enriches results with LLM-generated observationsreport: Renders output in SARIF, JSON, Markdown, or terminal formats based on theoutput_formatparameter
The wiring in create_graph() programmatically adds edges from build_context to each analyzer in ANALYZER_NODE_IDS, then from each analyzer to meta_analyzer. This design allows adding new analyzers by simply registering their node IDs.
Running SkillSpector from the Command Line
The CLI entry point in src/skillspector/cli.py provides the most common interface. The CLI parses arguments, invokes the compiled graph, and handles cleanup of temporary directories.
skillspector scan /path/to/skill \
--output_format json \
--use_llm
Supported formats include json, markdown, terminal, and sarif. The --use_llm flag enables LLM-based semantic analysis in the relevant analyzer nodes.
Programmatic Usage with LangGraph
For integration into Python applications, SkillSpector exports the compiled graph as a module-level variable graph from src/skillspector/graph.py.
Direct Graph Invocation
Import the graph and state definition, then invoke with a payload dictionary:
from skillspector import graph
from skillspector.state import SkillspectorState
payload = {
"input_path": "https://github.com/example/my_skill/archive/refs/heads/main.zip",
"output_format": "markdown",
"use_llm": True,
}
result = graph.invoke(payload)
print(result["report_body"])
print("Risk score:", result["risk_score"])
The graph.invoke() method returns a dictionary matching the SkillspectorState schema, containing the final report and risk assessment.
Embedding as a Subgraph
You can embed SkillSpector as a node within larger LangGraph workflows by treating the compiled graph as a subgraph:
from langgraph.graph import START, END, StateGraph
from skillspector import graph as skill_graph
from skillspector.state import SkillspectorState
def create_security_pipeline():
workflow = StateGraph(SkillspectorState)
workflow.add_node("skillscan", skill_graph)
workflow.add_edge(START, "skillscan")
workflow.add_edge("skillscan", END)
return workflow.compile()
pipeline = create_security_pipeline()
result = pipeline.invoke({
"input_path": "./my_skill",
"output_format": "json"
})
This composition pattern allows you to integrate security scanning into multi-agent systems, CI/CD pipelines, or complex analysis workflows while maintaining LangGraph's state management guarantees.
Summary
- SkillSpector implements a LangGraph StateGraph in
src/skillspector/graph.pywith a strictly typedSkillspectorStateschema - The workflow progresses through
resolve_input,build_context, analyzers,meta_analyzer, andreportnodes - Register new analyzers by adding node IDs to
ANALYZER_NODE_IDSinsrc/skillspector/nodes/analyzers.py - Use
graph.invoke(payload)for direct Python execution or embed the graph as a node in larger workflows usingworkflow.add_node("skillscan", skill_graph) - Always cleanup
temp_dir_for_cleanupafter execution when calling programmatically (the CLI handles this automatically)
Frequently Asked Questions
How do I add a custom analyzer to the SkillSpector LangGraph?
Create a new node function that accepts the current state and returns updated state with findings, then register it in ANALYZER_NODE_IDS and ANALYZER_NODES within src/skillspector/nodes/analyzers.py. The create_graph() function automatically wires edges from build_context to your new analyzer and from your analyzer to meta_analyzer.
Can I run SkillSpector without the LLM components?
Yes. Omit the use_llm key or set it to False in your invocation payload. When disabled, the graph skips LLM-driven semantic analysis nodes while still executing static YARA and AST-based analyzers, reducing execution time and API costs.
What output formats does the SkillSpector graph support?
The report node in src/skillspector/nodes/report.py supports four output formats: sarif (Static Analysis Results Interchange Format), json, markdown, and terminal. Specify your desired format in the output_format field of the input payload.
How do I handle temporary directory cleanup when using SkillSpector programmatically?
The SkillspectorState includes a temp_dir_for_cleanup field containing the path to temporary directories created during resolve_input. When invoking graph.invoke() directly (not via CLI), you must delete this directory after execution. The CLI in src/skillspector/cli.py performs this cleanup automatically.
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 →