toolseal
Secure-by-default scaffolding and a cross-framework tool registry for agentic systems.
Setting up an agent means reconciling a provider SDK, a framework's tool-binding idiom, and MCP server configuration. That reconciliation is usually done by copying a quickstart — and it is where the system's permanent security posture gets written, in the first ten minutes, by someone optimising for "does it run".
toolseal makes the secure arrangement the default one, and makes the insecure arrangement visible when it already exists.
$ toolseal audit .
+--------------------------------------------------+
| score 32/100 BLOCKING: 1 critical check failed |
| 1 critical, 6 high, 2 medium, 1 low |
+--------------------------------------------------+
CRITICAL A1 Credential literal in project file
.env:1 - OpenAI-style key found in .env
fix Move the value into the OS keychain and revoke the exposed
credential; it must be treated as compromised.
HIGH C1 Unpinned dependency
langchain is declared as >=0.1.0
fix Pin langchain to an exact version.
HIGH E2 Agent inherits the full host environment
Cloud CLI profiles, SSH agent sockets and every exported secret
are visible to the agent and to any tool it calls
fix Launch with an explicit, minimal environment.
Install
pip install toolseal # or: uv tool install toolseal
toolseal doctor # verify the install
Requires Python 3.11+. Three runtime dependencies (typer, rich, keyring) —
a tool that counts other people's dependencies should be able to justify each of
its own.
Quickstart
Create a hardened project:
toolseal init myagent --framework crewai --provider anthropic
cd myagent
toolseal audit . # 100/100 — the scaffold passes its own checks
Or point it at a project you already have — toolseal did not need to create it:
toolseal audit /path/to/existing-project
toolseal audit . --json # machine-readable
toolseal audit . --sarif # SARIF 2.1.0, for GitHub code scanning
Start under a regulatory regime instead of the baseline policy:
toolseal init myagent --profile hipaa # or gdpr, dora
toolseal policy apply gdpr # or adopt one later
What it does
Scaffold — secure defaults, not a blank page
toolseal init wires a provider and framework together with least-privilege
configuration, credentials resolved from the OS keychain instead of a file on
disk, a redacting log filter, a pinned dependency set, and a generated SBOM.
toolseal add framework and add mcp extend a project that already exists.
Everything written is recorded, and toolseal revert removes exactly what was
added — including restoring files that existed beforehand, and refusing to
clobber edits you made since.
add mcp resolves a server's package name against npm and PyPI before writing
it anywhere, and refuses a name that resolves nowhere:
$ toolseal add mcp @invented/definitely-not-real
error: '@invented/definitely-not-real' resolves in no registry checked. A name
that does not exist today is one an attacker can register tomorrow. Re-run with
--skip-verify if you are certain. (exit 2: usage)
Audit — 28 checks, mapped to published standards
toolseal audit scores any project against a misconfiguration taxonomy of 28
checks in seven families:
| Family | Concern |
|---|---|
| A | Credential exposure |
| B | Capability overprovisioning |
| C | Supply-chain integrity |
| D | Transport and endpoint |
| E | Execution containment |
| F | Accountability |
| G | Translation integrity |
Every check documents itself, including which external obligations it serves:
$ toolseal policy explain B3
+- B3 - Filesystem capability with unbounded or home-directory root -+
| |
| How to fix it |
| Confine filesystem access to the workspace. |
| |
| Obligations this serves |
| owasp-llm-top10:LLM06 Excessive Agency |
| owasp-agentic-threats:T3 Privilege Compromise |
| owasp-agentic-top10:ASI03 Identity & Privilege Abuse |
+--------------------------------------------------- severity: high -+
A check that could not be evaluated is reported as not evaluated — data unavailable, not a pass. The distinction is deliberate and load-bearing: a
missing answer is never quietly scored as a good one.
Registry — normalised, provenance-checked tool descriptors
toolseal registry indexes open-source tools and MCP servers into a Unified
Tool Descriptor carrying capability schema, security annotations, and
provenance. Every entry ships with its own assessment — the security review is
the entry, not an afterthought bolted on:
$ toolseal registry search context7
+--------------------------------------------------------------------------+
| | score | name | package@version | registry | tools |
| -+-------+----------------------+---------------------+----------+------ |
| | 90 | io.github.upstash... | @upstash/context... | npm | - |
+--------------------------------------------------------------------------+
- tools not enumerated (would require running the server)
A curated set ships inside the package, so search and show work immediately
after install — before registry sync has ever run. Curation criteria are
fixed and published, applied by a
script rather than a person, and deliberately blind to the audit score: a
registry that selects entries because they scored well and then reports that its
entries score well has measured nothing.
Nothing in the index is ever executed. Enumerating a server's tools means
running it, so entries record tools_enumerated: false rather than implying an
empty tool set. A gap you can see beats a number you cannot trust.
Translate — compensating guards for what a framework cannot express
This is the part that does not exist elsewhere. A tool declares security properties at its source; each target framework can represent some subset of them. The difference is translation loss, and toolseal computes it instead of letting you discover it in production.
Measurement (probe P0) found the loss is adapter-dependent, not inherent:
langchain-mcp-adapters preserves MCP annotation hints, crewai-tools drops all
of them and rewrites every description. Because the loss is a choice rather than
a law, it can be repaired.
$ toolseal add tool mcp/example/db@1.0.0 --framework crewai
Lowered delete_records into crewai (compensated)
+ tools/delete_records.py
+ compensation.json
guards emitted:
- require_approval: G1: the source declared destructiveHint, which this
framework cannot carry. The consequence is restored as an approval step.
- preserve_description: G5: this framework rewrites tool descriptions, so the
author's text is preserved verbatim as SOURCE_DESCRIPTION.
The guards are behaviour, not annotation. Restoring destructiveHint into a
framework with no field for it means wrapping the call in an approval step —
re-establishing the consequence of the hint, since the hint itself has nowhere
to live:
# G1: the source declared destructiveHint, which this framework cannot carry.
# The consequence is restored as an approval step.
@require_approval("declared destructive by its author")
@tool
def delete_records(**kwargs: object) -> object:
"""Permanently delete rows matching a filter. This cannot be undone."""
return DISPATCH("delete_records", kwargs)
Whatever happened is written to a compensation manifest, which toolseal audit
reads back through family G — so a property that was dropped with no guard in
its place becomes a finding rather than a silence.
Policy, regimes, and sealing
$ toolseal policy list
standard | coverage | checkable
----------------------+----------+----------
iso-42001 | 33%* | 1/3
nist-ai-rmf | 80%* | 4/5
owasp-agentic-threats | 100% | 6/6
owasp-agentic-top10 | 100% | 5/5
owasp-llm-top10 | 100% | 5/5
* curated subset of the standard, not a full enumeration -
the percentage measures our selection, not the standard's reach.
Regulatory regimes — GDPR, HIPAA, DORA — pin severities on top of the
baseline. They are never scored, and a report run under one never ends in a
verdict, only in not_assessed. A configuration auditor cannot certify
compliance, and this one does not pretend to: it produces evidence toward an
assessment, never the assessment itself.
Two more commands close the loop:
toolseal policy relax <check>— a justified, expiring deviation, written intotoolseal.tomlwith its reason and expiry date.toolseal policy enforce/verify— seal a resolved policy, then later prove nothing has drifted from what was sealed.
Command reference
| Command | Purpose |
|---|---|
init |
Create a new agent project with secure defaults |
audit |
Score a project against the misconfiguration taxonomy |
add framework |
Write a framework's configuration into an existing project |
add mcp |
Add an MCP server, refusing a name that resolves nowhere |
add tool |
Lower a registry entry into the project, compensating what is lost |
revert |
Undo what toolseal wrote into this project |
policy list / explain / show |
Inspect checks, standards, and regimes |
policy apply / relax / enforce / verify |
Adopt, deviate from, and seal a policy |
registry sync / search / show |
Crawl, query, and inspect the tool index |
doctor |
Report environment information for diagnosing a problem |
Supported targets
| Axis | Supported |
|---|---|
| Providers | Anthropic, OpenAI, Gemini, Ollama |
| Frameworks | LangGraph, CrewAI, Claude Code |
| Regulatory regimes | GDPR, HIPAA, DORA |
| Standards mapped | OWASP LLM Top 10, OWASP Agentic Threats (T1–T15), OWASP Agentic Top 10 (ASI01–ASI10), NIST AI RMF, ISO/IEC 42001 |
| Output formats | Human-readable, --json, SARIF 2.1.0 |
Out of scope, deliberately: runtime proxying, sandboxing, malicious-code detection, and trust scoring. Each is covered by existing work, and toolseal is a configuration tool — it reasons about what a project declares, not about what its code does at runtime.
Using it in CI
Exit codes are a stable contract: 0 clean, 1 findings, 2 usage error,
3 internal error.
- run: pip install toolseal
- run: toolseal audit . --min-severity high
Or upload SARIF to GitHub code scanning:
- run: toolseal audit . --sarif > toolseal.sarif
continue-on-error: true
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: toolseal.sarif
Contributing
Contributions are welcome. See CONTRIBUTING.md for the full guide.
git clone https://github.com/5Tarun3/ToolSeal
cd ToolSeal
uv sync
uv run pre-commit install
CI runs exactly four commands — run them locally first:
uv run ruff check .
uv run ruff format --check .
uv run mypy
uv run pytest
Two project-specific rules worth knowing before your first PR:
toolseal audit .must report 100/100 on this repository. A security tool that fails its own checks is not making an argument.--jsonand SARIF output are inviolable machine contracts, as are the exit codes above. A research harness parses this output.
Documentation
reference/taxonomy.md— the normative misconfiguration taxonomy: every check, its severity, and its mapping to published standards.research/— probes, measurement harnesses, and the evidence behind the project's claims, including the registry curation criteria and the evaluation protocol.CHANGELOG.md— release history.
Security
To report a vulnerability in toolseal, see SECURITY.md.
For how this project handles vulnerabilities it finds in other people's quickstarts, templates, and MCP servers during its own research, see DISCLOSURE.md.
License
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 toolseal-0.1.1.tar.gz.
File metadata
- Download URL: toolseal-0.1.1.tar.gz
- Upload date:
- Size: 253.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3b5cbf717930e33b6da9479e166ec0f1d5e43a8d9c1f5d0d068ef02587df8dc8
|
|
| MD5 |
e1eafbce4ac45718ab1113357a4e6148
|
|
| BLAKE2b-256 |
ae947d31e01031fba08046fee8db0af89c9f2edba6aba060ad02723cfe6e7f17
|
Provenance
The following attestation bundles were made for toolseal-0.1.1.tar.gz:
Publisher:
release.yml on 5Tarun3/ToolSeal
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
toolseal-0.1.1.tar.gz -
Subject digest:
3b5cbf717930e33b6da9479e166ec0f1d5e43a8d9c1f5d0d068ef02587df8dc8 - Sigstore transparency entry: 2626799665
- Sigstore integration time:
-
Permalink:
5Tarun3/ToolSeal@42b8dbbacf9e5697cb09beada68b99b1ff9ecf8e -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/5Tarun3
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@42b8dbbacf9e5697cb09beada68b99b1ff9ecf8e -
Trigger Event:
push
-
Statement type:
File details
Details for the file toolseal-0.1.1-py3-none-any.whl.
File metadata
- Download URL: toolseal-0.1.1-py3-none-any.whl
- Upload date:
- Size: 301.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e453866a01ee9eb9ad5f8d4d82416798b91e315c0b750a95daf39607b029d63c
|
|
| MD5 |
9b299cd3e51c59b510a5cbc25c8393b3
|
|
| BLAKE2b-256 |
25b3b5f4948a99e9281d23863c140b2cbf5a833da5c9e341d57e599e528797f8
|
Provenance
The following attestation bundles were made for toolseal-0.1.1-py3-none-any.whl:
Publisher:
release.yml on 5Tarun3/ToolSeal
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
toolseal-0.1.1-py3-none-any.whl -
Subject digest:
e453866a01ee9eb9ad5f8d4d82416798b91e315c0b750a95daf39607b029d63c - Sigstore transparency entry: 2626799765
- Sigstore integration time:
-
Permalink:
5Tarun3/ToolSeal@42b8dbbacf9e5697cb09beada68b99b1ff9ecf8e -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/5Tarun3
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@42b8dbbacf9e5697cb09beada68b99b1ff9ecf8e -
Trigger Event:
push
-
Statement type: