Python implementation of the FortiSOAR REST API
Project description
pyfsr
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.picklistsresolves friendly values ("High") to IRIs and discovers which picklist a(module, field)binds to. - Connectors —
client.connectorslists configured connectors, runs healthchecks, and executes operations. - Playbooks —
client.playbooksmerges 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 anddrift(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.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.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 two groups.
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
Other groups: 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 validate flow.yaml # compile + report diagnostics (offline)
pyfsr playbook compile flow.yaml # emit the FSR import envelope (offline)
pyfsr playbook deploy flow.yaml # compile and create it on the appliance
pyfsr playbook check-fresh # compare the cached catalog vs a live SOAR
Development
uv sync --extra dev
uv run pytest -q # unit tests (live tests deselected by default)
uvx ruff check src tests
Live integration tests run with pytest -m integration and need an
examples/config.toml pointing at a FortiSOAR instance.
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyfsr-0.7.7.tar.gz.
File metadata
- Download URL: pyfsr-0.7.7.tar.gz
- Upload date:
- Size: 829.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
81bee7caad059ecea9e2dc7e10be0ad4fa5063c8f1ec5881fb90d6ccb16cbee8
|
|
| MD5 |
f4d31da80956827a9653063f5028f240
|
|
| BLAKE2b-256 |
32d2e5930e9555f91193d0fee458ea760fd482a2cf11ce0cfadc6c45ccd801bd
|
Provenance
The following attestation bundles were made for pyfsr-0.7.7.tar.gz:
Publisher:
publish.yml on ftnt-dspille/pyfsr
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyfsr-0.7.7.tar.gz -
Subject digest:
81bee7caad059ecea9e2dc7e10be0ad4fa5063c8f1ec5881fb90d6ccb16cbee8 - Sigstore transparency entry: 2009747277
- Sigstore integration time:
-
Permalink:
ftnt-dspille/pyfsr@ffe7cfb0e2acb8c993cdc335f5dbfe236b57e65e -
Branch / Tag:
refs/tags/v0.7.7 - Owner: https://github.com/ftnt-dspille
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ffe7cfb0e2acb8c993cdc335f5dbfe236b57e65e -
Trigger Event:
release
-
Statement type:
File details
Details for the file pyfsr-0.7.7-py3-none-any.whl.
File metadata
- Download URL: pyfsr-0.7.7-py3-none-any.whl
- Upload date:
- Size: 610.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7e9401e90506e82cddb68e46a1025816e2da04fb11055c36759712019d6fd33d
|
|
| MD5 |
d994fd98316385d3dd28d1c04b6cef6b
|
|
| BLAKE2b-256 |
6ec06867d5d2fc8b29de41055ffa18b29ca2f7b98d9cae7937f6348c48ffe38d
|
Provenance
The following attestation bundles were made for pyfsr-0.7.7-py3-none-any.whl:
Publisher:
publish.yml on ftnt-dspille/pyfsr
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyfsr-0.7.7-py3-none-any.whl -
Subject digest:
7e9401e90506e82cddb68e46a1025816e2da04fb11055c36759712019d6fd33d - Sigstore transparency entry: 2009747358
- Sigstore integration time:
-
Permalink:
ftnt-dspille/pyfsr@ffe7cfb0e2acb8c993cdc335f5dbfe236b57e65e -
Branch / Tag:
refs/tags/v0.7.7 - Owner: https://github.com/ftnt-dspille
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ffe7cfb0e2acb8c993cdc335f5dbfe236b57e65e -
Trigger Event:
release
-
Statement type: