Skip to main content

pyfsr

PyPI version Python versions License: MIT Tests Documentation Status codecov Ruff

Documentation · Installation · Quick start · CLI · AI / agents

pyfsr is a batteries-included Python client for the FortiSOAR REST API. It gives you a typed query/CRUD layer over any module, picklist resolution, connector execution, playbook-run history, safe deletes -- and a ready-made AI/agent surface (tool-schema registry + an optional MCP server) so an agent can drive FortiSOAR with no glue code.

There's also a pyfsr CLI for the things you reach for outside a script: poking at an appliance's health and services, and authoring playbooks in YAML and pushing them to a live instance.

Python 3.10+ · Pydantic v2 · MIT.

Installation

pip install pyfsr
# with the optional generic MCP server:
pip install 'pyfsr[mcp]'

Quick start

from pyfsr import FortiSOAR, Query

# API-key auth, or ("username", "password")
client = FortiSOAR("soar.example.com", "your-api-key")

# Generic, typed CRUD for ANY module via client.records(module)
incidents = client.records("incidents")

inc = incidents.get("0d2c...")          # by uuid, "module:uuid", or full IRI
inc["name"], inc.uuid                    # records are dict- AND attribute-accessible

# Structured queries with a fluent builder -> a HydraPage you can iterate
page = incidents.query(
    Query().eq("status.itemValue", "Open").like("name", "phish").limit(50)
)
for inc in incidents.iterate(Query().eq("status.itemValue", "Open")):
    ...                                  # lazily walks every page

# Create / update / delete (soft by default; hard= for permanent)
new = incidents.create({"name": "Suspicious login", "severity": "High"},
                       resolve_picklists=True)   # friendly values -> IRIs
incidents.update(new.uuid, {"status": "Closed"}, resolve_picklists=True)
incidents.delete(new.uuid)               # delete(..., hard=True) to purge

Configure from the environment

from pyfsr import EnvConfig

# reads FSR_BASE_URL (+ FSR_API_KEY or FSR_USERNAME/FSR_PASSWORD),
# FSR_PORT, FSR_VERIFY_SSL, FSR_TIMEOUT
client = EnvConfig.from_env().client()

Features

  • Generic record access -- client.records(module) for CRUD on any module; no hand-built /api/3/... URLs or Hydra unwrapping.
  • Query DSL -- Query().eq(...).in_(...).group(...).sort(...).limit(...), compiled to the FortiSOAR query-body shape (pagination handled for you).
  • Typed models -- Alert/Incident/Task/Comment come back as Pydantic v2 models that are also dict-compatible; unknown modules fall back to a lenient BaseRecord, so custom fields/modules never break.
  • Picklists -- client.picklists resolves friendly values ("High") to IRIs and discovers which picklist a (module, field) binds to.
  • Connectors -- client.connectors lists configured connectors, runs healthchecks, and executes operations.
  • Playbooks -- client.playbooks merges live + historical run history and resumes manual-input steps.
  • Safe deletes -- soft-delete/restore + guarded single-row hard delete.
  • Schema discovery -- client.list_modules() / client.describe_module().
  • Resilient transport -- configurable timeout=, automatic retry with backoff on idempotent requests (429/5xx), and secrets masked in verbose logs.
  • Bundled OpenAPI spec -- pyfsr.spec.load_spec() for offline reference and drift(client) to compare the spec against a live appliance.

AI / agent-friendly

pyfsr ships a transport-neutral tool registry for the core operations, with token-efficient results and structured (never-raised) errors -- feed it to Anthropic tool-use, OpenAI function calling, your own agent loop, or the bundled MCP server.

from pyfsr.agent.tools import to_anthropic_tools, to_openai_tools, dispatch

tools = to_anthropic_tools()             # or to_openai_tools(), or tool_schemas()

# ... your model picks a tool ...
result = dispatch(client, "search_records",
                  {"module": "alerts", "summary": True, "limit": 10})
# result is JSON-safe and trimmed; failures come back as {"error": {...}}

Reads accept summary=True or fields=[...] to keep payloads small:

client.records("alerts").query(Query().limit(20), summary=True)

Generic MCP server

Point any MCP-capable agent at any FortiSOAR with one command:

pip install 'pyfsr[mcp]'
FSR_BASE_URL=soar.example.com FSR_API_KEY=... python -m pyfsr.agent.mcp

It exposes the same registry (record CRUD, schema discovery, picklists, connectors, playbook runs) as MCP tools -- generic and dependency-light, distinct from any domain-specific FortiSOAR MCP.

Command-line tools

Installing pyfsr puts a pyfsr command on your path with seven groups.

pyfsr instances -- inspect the named-instance registry (~/.pyfsr/instances.toml), which is what every --instance flag resolves against:

pyfsr instances list                 # every alias: base URL, auth kind, SSH profile
pyfsr instances show 206             # one instance's resolved settings (no secrets)
pyfsr instances check                # connect to each box; exit 1 if any fails

check probes the version endpoint and then an authenticated read, so a registered-but-broken alias shows up as unreachable or auth-failed before a script fails on it.

pyfsr appliance -- operational verbs against a FortiSOAR box (most run over SSH/sudo and stay dependency-light on the far end):

pyfsr appliance info                 # host, version, content DB, device UUID
pyfsr appliance host                 # mem / swap / load / per-service RSS / disk
pyfsr appliance service restart cyops-postman --yes
pyfsr appliance db                   # Postgres verbs, multi-DB aware
pyfsr appliance es                   # Elasticsearch health + shard state
pyfsr appliance license              # licensing / identity, drift check
pyfsr appliance content-hub sync     # pull the Content Hub catalog + artifacts

Other appliance subgroups: mq (RabbitMQ), ha, certs, logs, and diagnose (runs fsr_diagnose.sh). --help on any of them lists the verbs.

pyfsr playbook -- author playbooks as YAML and deploy them:

pyfsr playbook steps                 # list every step type you can write
pyfsr playbook step-help TYPE        # keys + a compiling example for one type
pyfsr playbook examples              # foundational playbook library (--intent/--stage/--manifest)
pyfsr playbook show SLUG             # print one library playbook's metadata + YAML
pyfsr playbook validate flow.yaml    # compile + report diagnostics (offline)
pyfsr playbook compile flow.yaml     # emit the FSR import envelope (offline)
pyfsr playbook lint flow.yaml        # live preflight: connector steps missing config
pyfsr playbook deploy flow.yaml      # compile and create it on the appliance

pyfsr records -- query and manage FortiSOAR records over the API:

pyfsr records alerts [--status Open] [--severity High]
pyfsr records incidents '<field=value or Query DSL JSON>'
pyfsr records delete <module> <uuid...> [--yes]

pyfsr repo -- discover and download from Fortinet's content repo (no appliance needed).

pyfsr widget -- upload and publish widgets on a live appliance.

pyfsr mcp -- call FortiSOAR's own native MCP tool gateway (list-tools / call), distinct from the generic pyfsr.agent.mcp server.

Development

uv sync
uv run pytest -q                 # unit tests (live tests deselected by default)
uvx ruff check src tests

Live integration tests run with pytest -m integration against the instance named by FSR_INSTANCE (an alias from ~/.pyfsr/instances.toml), the FSR_* environment variables, or examples/config.toml, in that order. The AI tests (test_ai_integration.py) make no LLM calls, so they spend no AI tokens.

License

MIT -- see LICENSE.

Metadata

Release files for pyfsr 0.21.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pyfsr 0.21.0
File Size Uploaded
pyfsr-0.21.0.tar.gz 1.6 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for pyfsr 0.21.0
File Interpreter ABI Platform
pyfsr-0.21.0-py3-none-any.whl Python 3 none any Details

Total release size: 2.6 MB

Release files / pyfsr-0.21.0.tar.gz

Download URL pyfsr-0.21.0.tar.gz
Size 1.6 MB
Tags Source
SHA-256 checksum
How to use checksums
34b36a560c24a328a4f89eb21595e97e19e6c7ae874463e69fb00bc54bb3ca3a
BLAKE2b-256 checksum
How to use checksums
399ad5b0534560ce972e8e40dce1b0428291c9ac7f8f6d94e835bf849325986c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 28, 2026.

Transparency log

Release files / pyfsr-0.21.0-py3-none-any.whl

Download URL pyfsr-0.21.0-py3-none-any.whl
Size 984.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
235fcaba98849939b4c48951dbab05324dce3a76050afd5b53a836795d484231
BLAKE2b-256 checksum
How to use checksums
8ef44f3666398bb537b9090c1af0357c12af2ec254b2136bd3ca597ab960a785
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 28, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.21.0 This release

2 release files

0.20.0

2 release files

0.19.2

2 release files

0.19.1

2 release files

0.19.0

2 release files

0.18.6

2 release files

0.18.5

2 release files

0.18.4

2 release files

0.18.3

2 release files

0.18.1

2 release files

0.16.1

2 release files

0.16.0

2 release files

0.15.1

2 release files

0.15.0

2 release files

0.14.0

2 release files

0.13.0

2 release files

0.12.0

2 release files

0.11.1

2 release files

0.11.0

2 release files

0.10.1

2 release files

0.10.0

2 release files

0.9.0

2 release files

0.8.9

2 release files

0.8.8

2 release files

0.8.7

2 release files

0.8.6

2 release files

0.8.5

2 release files

0.8.4

2 release files

0.8.3

2 release files

0.8.2

2 release files

0.8.1

2 release files

0.8.0

2 release files

0.7.9

2 release files

0.7.8

2 release files

0.7.7

2 release files

0.7.6

2 release files

0.7.5

2 release files

0.7.4

2 release files

0.7.3

2 release files

0.6.7

2 release files

0.6.6

2 release files

0.6.5

2 release files

0.6.4

2 release files

0.6.3

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.4

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.5

2 release files

0.1.4

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page