Skip to main content
Archived

This project has been archived by its maintainers, and is no longer receiving any updates.

skillscan-trace

CI CodeQL PyPI License Python

Behavioral execution engine for MCP-based AI agent skills.

skillscan-trace runs a skill against a real language model inside an instrumented, isolated environment and records everything the model does: every file it reads, every network request it makes, every environment variable it accesses, every binary it probes. The output is a structured, machine-readable trace report.

It is the dynamic analysis counterpart to skillscan, which performs static analysis. Together they form two legs of the skillscan family:

skillscan family
├── skillscan        — static analysis (pattern matching, ML classifier)
├── skillscan-trace  — behavioral execution engine  ← this repo
└── skillscan-lint   — schema and format validation (planned)

What it does

A skill is a Markdown file that becomes a system prompt for an AI agent. Most skills are benign. Some are malicious — they instruct the agent to exfiltrate credentials, probe the filesystem, call attacker-controlled servers, or hijack the agent's behavior through prompt injection embedded in tool output.

Static analysis catches the obvious cases. Behavioral analysis catches the rest: conditional payloads that only activate in certain environments, obfuscated instructions that decode at runtime, and prompt injection delivered through external content the skill fetches.

skillscan-trace works by:

  1. Loading the skill's SKILL.md as the system prompt for a local language model
  2. Sending a realistic user prompt that exercises the skill's stated functionality
  3. Driving the model's tool-use loop through an instrumented MCP server that intercepts every tool call
  4. Checking each call against a canary taxonomy (credential files, wallet paths, ENV vars, binary probing, network destinations)
  5. Emitting a structured trace report (JSON + SARIF) with every observed behavior and any findings

The model runs locally via Ollama — no API key required, no cloud dependency. API providers (OpenAI, OpenRouter) are supported for users who prefer them or want access to more capable models.


Status

v0.2.0 — verdict banner + full CI matrix. All phases implemented and 199/199 tests pass across Python 3.11, 3.12, and 3.13. The tool is installable and usable today.


Quick start

# Install from source
git clone https://github.com/kurtpayne/skillscan-trace
cd skillscan-trace
pip install -e .

# Run a trace with OpenAI
export OPENAI_API_KEY=sk-...
skillscan-trace run ./path/to/skill/

# Run with OpenRouter (200+ models via one key)
export OPENROUTER_API_KEY=sk-or-...
skillscan-trace run ./skill/ --provider openrouter --model mistralai/mistral-7b-instruct

# Run with a local Ollama model (no API key required)
# Model must support tool calling — llama3.1:8b and llama3.2:3b are verified
skillscan-trace run ./skill/ --provider ollama --model llama3.1:8b

# Dry run — validate config and skill loading without calling the LLM
skillscan-trace run ./skill/ --dry-run

# Run with explicit base URL (Azure, Mistral, etc.)
skillscan-trace run ./skill/ --base-url https://api.mistral.ai/v1 --api-key $MISTRAL_KEY

# Output formats
skillscan-trace run ./skill/ --format sarif   # SARIF 2.1.0 for CI
skillscan-trace run ./skill/ --format json    # native trace format
skillscan-trace run ./skill/ --format text    # human-readable summary

# Verify connectivity
skillscan-trace check
skillscan-trace check --provider openrouter
skillscan-trace check --provider ollama

Skill format support

skillscan-trace handles all skill formats found in the wild:

Format Description Example
Single SKILL.md Standard Claude Code / MCP skill ./my-skill/SKILL.md
Single .md file Flat file, no directory ./my-skill.md
Directory with SKILL.md Standard with supporting files ./my-skill/
Frontmatter + body YAML frontmatter + Markdown body name:, allowed-tools:
Plain Markdown No frontmatter Any .md file
Multi-file skill Directory with multiple .md files Loaded in alphabetical order

Docker

A single image supports both run (single trace) and serve (HTTP API) modes. The same image powers the hosted service at trace.skillscan.sh and enterprise self-hosting.

Pull the image:

docker pull kurtpayne/skillscan-trace:latest

Run mode (single trace)

Mount your skill and pass an API key via env var. The container exits after the trace completes.

docker run --rm \
  -v "$(pwd)":/data \
  -e OPENAI_API_KEY=sk-... \
  kurtpayne/skillscan-trace run /data/SKILL.md

Use OpenRouter instead:

docker run --rm \
  -v "$(pwd)":/data \
  -e OPENROUTER_API_KEY=sk-or-... \
  kurtpayne/skillscan-trace run /data/SKILL.md --provider openrouter

Fully local with Ollama on the host:

docker run --rm --network host \
  -v "$(pwd)":/data \
  kurtpayne/skillscan-trace run /data/SKILL.md --provider ollama

Serve mode (HTTP API)

Starts the FastAPI server on port 8080. Callers provide their own API key per request (BYOK) — keys are never logged or stored.

docker run -d -p 8080:8080 --name sst \
  kurtpayne/skillscan-trace serve

curl http://localhost:8080/v1/health

For persistent caching across restarts, mount volumes for /trace-cache and /trace-output. A full docker-compose.yml is provided in the repository root.


Output: Trace Report

Every trace produces a JSON trace report and optionally a SARIF report.

{
  "schema_version": "1.0.0",
  "trace_id": "trc_20260320_abc123",
  "skill": {
    "path": "./my-skill/SKILL.md",
    "name": "git-helper",
    "sha256": "a1b2c3..."
  },
  "model": {
    "provider": "ollama",
    "model": "llama3.1:8b",
    "version": "..."
  },
  "prompt": "Help me commit my changes",
  "duration_ms": 4823,
  "tool_calls": [...],
  "findings": [...],
  "summary": {
    "total_tool_calls": 7,
    "finding_count": 1,
    "severity_max": "HIGH",
    "clean": false
  }
}

Relationship to skillscan-security

skillscan-trace is a sibling project to skillscan-security. The two projects share:

  • Finding schema: The same finding IDs (EXF-001, MAL-001, IOC-001, etc.) and severity levels
  • Canary taxonomy: The same list of high-value target paths and ENV var names
  • Domain allowlist: trace/domains/verified.yml from skillscan-security is the source of truth; skillscan-trace consumes it
  • Corpus feedback loop: Traces that produce findings can be reviewed and added to the skillscan-security corpus as sandbox_verified/ examples, improving the ML classifier's recall on behavioral patterns that static analysis misses

Privacy & Security

Your API keys are never stored, logged, or transmitted by SkillScan — they are passed directly to the LLM provider you chose over HTTPS and held in memory only for the duration of the run.

  • No telemetry, no analytics, no phone-home — skillscan-trace makes zero network requests beyond the LLM API calls you explicitly authorize
  • Local-first — the canary server runs in-process on your machine; nothing leaves your network except LLM API calls
  • Hosted service (trace.skillscan.sh) — follows the BYOK model; your API key is forwarded to the LLM provider and never stored or cached; only the trace report is cached
  • No user identity — no accounts, no login, no tracking
  • Ollama — fully local, zero external network requests, no API key required

See PRIVACY.md for the full data flow diagram and detailed explanation.


Contributing

See CONTRIBUTING.md.


License

Apache-2.0

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

skillscan_trace-1.0.0.tar.gz (136.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

skillscan_trace-1.0.0-py3-none-any.whl (135.7 kB view details)

Uploaded Python 3

File details

Details for the file skillscan_trace-1.0.0.tar.gz.

File metadata

  • Download URL: skillscan_trace-1.0.0.tar.gz
  • Upload date:
  • Size: 136.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for skillscan_trace-1.0.0.tar.gz
Algorithm Hash digest
SHA256 c28d768deb6a53402534fd3430e22e56451d77493972cd3663c7e3f89608125f
MD5 52d6d5bc7c8ddb217ccf01a718d6d69b
BLAKE2b-256 33758bcde2c0585c7a0c46857045a9ddc8eac88244cd5277aa37909f790eae3d

See more details on using hashes here.

Provenance

The following attestation bundles were made for skillscan_trace-1.0.0.tar.gz:

Publisher: release-pypi.yml on kurtpayne/skillscan-trace

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file skillscan_trace-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: skillscan_trace-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 135.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for skillscan_trace-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b19909835311891d8e00f725f43190a8ee9ea048d95725cbf9af68573f40980d
MD5 1f0d5fcd9d04bd83ea6c8fc65af6a94d
BLAKE2b-256 745d2d99760231b1f2834e0a0c9e07d97a42733dd13dd84b3682c0d0b44bc96b

See more details on using hashes here.

Provenance

The following attestation bundles were made for skillscan_trace-1.0.0-py3-none-any.whl:

Publisher: release-pypi.yml on kurtpayne/skillscan-trace

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 files

0.1.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page