Skip to main content

toolfuncs

toolfuncs turns typed Python functions into tools that have native Python, Cyclopts command-line, and local FastMCP interfaces.

A toolfunc exposes typed functions through one module-level App. It can be an executable Python script or an explicitly opted-in installed package console script. Its lowercase kebab-case command name determines the Python facade name by replacing hyphens with underscores. PATH controls discovery and lookup in both cases; ordinary installed commands do not become toolfuncs automatically.

Package a tool

Use ordinary project metadata with matching console-script and toolfuncs entry points:

[project]
name = "agent-apps"
version = "0.1.0"
dependencies = ["toolfuncs>=2,<3"]

[project.scripts]
agent-apps = "agent_apps.tools:app"

[project.entry-points.toolfuncs]
agent-apps = "agent_apps.tools:app"

Define the interface once in agent_apps/tools.py:

"""Manage agent application conversations."""

import toolfuncs.sdk as toolsdk

app = toolsdk.App()


@app.command
def describe(conversation: str) -> dict[str, str]:
    """Describe one conversation.

    Parameters
    ----------
    conversation:
        Conversation identifier to inspect.
    """
    return {"conversation": conversation}

Install with uv-launch install agent-apps "agent-apps @ file:///absolute/path/to/project" for managed launches. Use uv-launch by default; it prepares and updates the command environment automatically. The installer generates the console script; no separate handwritten executable, self-dependent PEP 723 script, custom backend, or registration command is needed. agent-apps describe example calls the same decorated function exposed as toolfuncs.agent_apps.describe("example"). Direct Python lookup returns the real agent_apps.tools module and native objects/exceptions.

Discovery reads installed entry points, unique RECORD ownership, a strong launcher checksum, and the module docstring. It never imports a target, starts an interpreter, prepares dependencies, or uses the network. uv-launch records locate foreign command environments and capture one immutable generation. Packages installed in the caller's own interpreter use its metadata search paths. Generated launcher bodies are opaque: Python shebangs, long-path shell wrappers, and future installer templates follow the same ownership and checksum checks. Unmanaged foreign environments must be migrated with uv-launch; their interpreter locations are not inferred from executable text. Installed Python source is required; editable import-hook layouts are not inferred. The first docstring line is the static description.

These checks detect inconsistent installations, not malicious package metadata. The package installer is trusted to generate the command declared by matching console_scripts and toolfuncs entry points. Discovery does not prove executable semantics or authenticate a publisher.

Python consumers can call toolfuncs.sdk.discover(path=...) for the same static Catalog used by listing and agent hooks. Omit path to use PATH; a supplied path has the same first-executable precedence. Read catalog.tools and catalog.invalid_tools without executing command help.

Direct Python loading installs the owning distribution into the caller's interpreter if needed, using its recorded exact Git origin, other direct origin, or published name/version. It then imports the actual module. This preserves native Python semantics rather than opening an RPC proxy into the tool environment. Use a suitable interpreter when tools have incompatible dependency requirements. toolfuncs doctor /path/to/agent-apps validates the installed module's registered commands and generated help. import_path retains its existing local-script contract and does not import package console wrappers.

Write a toolfunc

Create an extensionless file named weather-report:

#!/usr/bin/env toolfuncs
# /// script
# requires-python = ">=3.11"
# dependencies = ["toolfuncs>=2,<3", "weather-client"]
# [tool.toolfuncs]
# description = "Read current weather conditions."
# cli_name = "weather-report"
# python_name = "weather_report"
# ///

"""Read current weather conditions."""

import toolfuncs.sdk as toolsdk
import weather_client
from pydantic import JsonValue

app = toolsdk.App()


@app.command
def current(city: str) -> dict[str, JsonValue]:
    """Read current conditions for one city.

    Parameters
    ----------
    city:
        City whose current conditions should be read.
    """

    return weather_client.current(city)

Make it executable:

chmod +x weather-report

No registration operation is required. Move it to an existing PATH directory only when it should be globally discoverable:

mv weather-report ~/.local/bin/

Call it

A known source can be run explicitly without adding it to PATH:

$ toolfuncs ./weather-report current London
{"city": "London", "temperature": 18}

When the source is on PATH, its filename is also the CLI:

$ weather-report current London
{"city": "London", "temperature": 18}

A known source is importable by path:

import toolfuncs as tools

weather_report = tools.import_path("./weather-report")
conditions = weather_report.current("London")

When the source is on PATH, the same file is also dynamically importable by name in a Python process with that PATH:

import toolfuncs as tools

conditions = tools.weather_report.current("London")

import_path() also works for an ordinary local Python script whose path is already known:

import toolfuncs as tools

module = tools.import_path("scripts/prepare_data.py")

It accepts one ordinary .py file or extensionless script and derives the module name from its filename. An extensionless lowercase kebab-case filename is mapped to its snake-case Python name. It does not require a shebang, executable bit, [tool.toolfuncs] metadata, or App, and it does not import packages, projects, distributions, or URLs. When an optional PEP 723 block exists, the default installer installs all declared requirements together into the running Python environment before import. A caller can replace that behavior with one callable that accepts list[str] and returns None:

module = tools.import_path(
    "scripts/prepare_data.py",
    dependency_installer=my_installer,
)

Python dependency preparation uses uv-launch: it reuses satisfying installed distributions and adds missing packages to the current interpreter. Conflicts fail before installation instead of replacing existing packages. Repeated imports retain their actual module objects. Use a fresh environment for a new session that needs different package versions.

Direct Python calls return ordinary Python objects and raise the original exceptions. Dynamic tool attributes and toolfuncs.import_path() prepare declared requirements in the running Python environment before import. CLI calls use Cyclopts to parse annotated values. Interactive terminals receive Rich help and errors, automatic record tables, and readable JSON for other result shapes. Pipes and other non-terminal destinations receive plain help and errors plus compact JSON. Both result renderers consume the same Pydantic-serialized value.

Set TOOLFUNCS_OUTPUT=terminal or TOOLFUNCS_OUTPUT=machine before invoking a tool to force either presentation. The default auto mode decides independently for stdout and stderr, so redirecting a result preserves machine-readable stdout without changing an attached terminal's error presentation. Cyclopts help_formatter and error_formatter settings remain configurable. Custom result_action settings are rejected: return a value for success, or raise an exception for failure. Help and version are framework control operations, so their internal return values bypass the command result action; a registered command that returns None remains an ordinary result and renders as null in both modes. Automatic detection uses the actual stream TTY, independently of color settings. Agent launchers should set TOOLFUNCS_OUTPUT=machine in the agent child environment, including for commands using a pseudo-terminal; a hook subprocess cannot set its parent environment. Tables retain all fields and values; data that does not fit uses complete readable JSON. Per-tool __rich__ methods and custom table views are not used.

Function commands inherit help and error presentation from their parent app. All registered commands share the same input validation and result contract.

Use MCP

Register one server to expose the effective PATH catalog. Scripts and opted-in package executables remain separate tool families:

{
  "mcpServers": {
    "toolfuncs": {
      "command": "/absolute/path/to/toolfuncs",
      "args": ["mcp"]
    }
  }
}

toolfuncs mcp prepares and captures PATH, then starts one persistent child per family in its selected dependency environment. Each distinct uv-launch owner is refreshed once. Children inherit the launch directory and environment. After preparation, family startup is bounded to 120 seconds with at most eight families starting concurrently. Configure the client's startup timeout to allow cold installation plus that startup window. Failed families appear in catalog with an error; healthy families remain callable. toolfuncs doctor loads all families and exits nonzero if any are unavailable. It does not invoke their operations. Use toolfuncs doctor SOURCE for the full source documentation and help checks.

Session launchers can prepare providers before starting the native client:

from pathlib import Path
from toolfuncs.sdk import prepare_session

overrides = prepare_session(Path("/path/to/session/runtime"))
# Add these overrides to the environment of the client and all its children.

The returned UV_LAUNCH_SESSION, TOOLFUNCS_CATALOG, and TOOLFUNCS_STATUS_FILE values select the same managed generations and catalog for hooks, CLI, Python imports, and MCP. Keep the directory for the lifetime of those processes. Preparation does not change the caller's environment. environment= supplies the launcher's child environment without changing the parent; path= overrides its PATH. offline=True explicitly reuses cached generations. A prepared session does not update providers on each call. Starting another session prepares a new selection. Local script bodies remain live; unmanaged installed packages keep their installed identity. CLI and MCP use each captured family runtime. Python imports keep compatible packages already installed in the kernel; captured versions constrain new additions, and captured source revisions replace floating references. Resolution uses the kernel Python, so inactive conditional dependencies are not added. The owning packaged tool always retains its exact selected identity.

The MCP server publishes family status to the selected status file. Fast synchronous prompt and post-tool hooks report each new family failure once to both the agent and the native client's human message channel. They only read local state. Missing or pending reports are not errors, and failures do not block conversation. Whole-server startup failures remain visible through the client's native MCP status display.

The server's operation catalog remains fixed until restart. A crashed child becomes unavailable and is not restarted automatically. Interrupted operations are never replayed, since their side effects may already have occurred. Ordinary function exceptions remain tool errors and leave the family available. Shutdown closes the children. Installing tools, changing PATH, or updating source/dependencies requires restarting the server.

toolfuncs mcp SOURCE remains available to serve one source or opted-in package command. Use --prepared only when the selected interpreter already contains every selected source's dependencies. Toolfuncs does not edit client configuration.

Progressive discovery

The hook advertises families. Inspect one group at a time, then one selected operation:

toolfuncs catalog
toolfuncs commands weather-report
toolfuncs commands weather-report --group '["records"]'
toolfuncs inspect-operation weather-report '["records", "show"]'

The generic MCP server exposes these same three discovery functions as catalog, commands, and inspect_operation. Group views contain only immediate child names and short descriptions. Selected operation views contain the original input/output schemas, documentation, canonical CLI argv, MCP name, and a Python attribute when it is verifiably exported by the source module. Do not reconstruct Python access from an MCP name: registered aliases and nested CLI groups need not match Python attributes.

Python uses the same bound functions:

from toolfuncs.sdk import ToolCatalog

async with ToolCatalog() as catalog:
    families = catalog.catalog()  # static discovery, no child startup
    await catalog.start(["weather-report"])
    children = catalog.commands("weather-report")
    detail = catalog.inspect_operation("weather-report", ["records", "show"])

In Codex Code Mode, use toolfuncs discovery to select an operation, resolve its runtime name from ALL_TOOLS, and invoke tools[name](arguments). Retain deferred MCP exposure; native tool search is unnecessary. In Claude Code, keep the server's tools deferred ("alwaysLoad": false on supported versions), load the selected tool with ToolSearch query select:mcp__toolfuncs__NAME, then call it normally. Claude still exposes operation names initially. These context policies belong to the client; generic MCP clients may expose all schemas. For the agent hook's client-specific guidance, set TOOLFUNCS_MCP_SERVER=toolfuncs in the host environment when that server is registered.

For programmatic use, server = app.to_mcp() returns a FastMCP server. Sync functions use FastMCP's thread dispatch and async functions are awaited. FastMCP owns request handling and cancellation. MCP calls do not share live state with an IPython kernel. The function implementation is shared.

A nested CLI command such as records show becomes records__show; the PATH server prefixes the family, for example weather_report__records__show. Hyphens become underscores. Aliases remain CLI aliases. Collisions and names exceeding 64 characters fail explicitly. Every successful MCP result has structured content {"result": value}, including scalar and null results, plus JSON text content. Exceptions become MCP tool errors.

Use stderr for logs and diagnostics. Stdout belongs to the CLI result or MCP protocol; an operation that prints to stdout can corrupt that stream. Durable background tasks, push notifications, remote hosting, resources and prompts are outside the current interface.

Portable functions

Use ordinary def or async def with explicit input and return annotations.

Every operation must have Pydantic validation and serialization schemas. Pydantic owns the supported annotations, constraints, validators, serializers, aliases, exclusions, computed fields and construction hooks. Toolfuncs does not maintain an annotation allowlist. This includes abstract sequences and mappings, Any, untyped collections and custom types with appropriate Pydantic support. A schema does not guarantee that every possible returned object will serialize. Actual results must validate, serialize without warnings, produce finite JSON and match the serialization schema. Transport output uses Pydantic serialization aliases consistently with that schema.

Pydantic defaults and author configuration control input conversion and extra model fields. For example, a normal int field can accept a numeric string; a model with ConfigDict(strict=True, extra="forbid") rejects that conversion and unknown fields. Unknown function arguments always fail. A custom validator runs once per supplied input in both CLI and MCP; result validation is a separate step. Defaults follow Pydantic, including explicit validate_default and default factories. Native Python calls remain unchanged.

Parameters can be positional-or-keyword or keyword-only. Annotations must be resolvable. Positional-only and variadic signatures and generator operations remain outside the single-request interface. Cyclopts metadata can customize names, help and presentation. CLI-only converters, validators, environment inputs, config providers, meta apps, hidden injection, repeated-token parsing and custom result policies remain errors because they change only one transport. Express shared conversion and validation with Pydantic or the function itself.

Cyclopts owns command discovery, tokenization, option spelling and help. String-shaped types use ordinary shell tokens; other inputs use JSON syntax with bare text as a string fallback. Collections and records still occupy one token, for example --labels '["a","b"]'. For unions, null selects null and '"null"' selects the string. Boolean options retain --enabled and --no-enabled. Both transports then validate the decoded JSON arguments through one Pydantic adapter. Native Python callers supply native values directly.

See the 2.0 migration guide and the full contract.

Source contract

A toolfunc source must:

  1. be one executable file;
  2. have an extensionless lowercase ASCII kebab-case filename;
  3. start with one of the two exact toolfuncs shebangs described below;
  4. contain exactly one PEP 723 script block with dependencies, a one-line description, and required cli_name and python_name documentation that exactly matches the filename-derived identities;
  5. define a module-level app = toolsdk.App() after import toolfuncs.sdk as toolsdk;
  6. give the module and every registered operation a docstring, annotate every operation parameter and return value, and provide effective Cyclopts help for every visible CLI argument;
  7. register each CLI-callable function explicitly with @app.command.

Declare toolfuncs>=2,<3 in PEP 723 dependencies to make the required major version explicit. Managed source installation requires uv-launch 0.2.9 or later to honor that declaration.

The description, either [tool.toolfuncs].description or a package module's first docstring line, is the line agents read in toolfuncs list and session-start catalogs when they choose a tool. Write one sentence that says what the tool does for its caller. Keep implementation details, compatibility notes and contract caveats out of it; put them in later docstring text or in command docstrings.

Descriptions are static by default. A script whose available domain surface changes at runtime may opt into a last-known catalog description with dynamic = true:

[tool.toolfuncs]
description = "Use connected services."
dynamic = true
cli_name = "connected-services"
python_name = "connected_services"

After refreshing its own state, the tool publishes one current line without changing its source:

toolsdk.publish_description(
    "connected-services",
    "Use connected services: Gmail, Google Drive, and Slack.",
)

Toolfuncs stores the line at ${XDG_STATE_HOME:-~/.local/state}/toolfuncs/descriptions/connected-services. Missing or invalid state uses the required static description. The CLI and Python identities always remain static.

Git requirements may use full commits, branches, tags, abbreviated hashes, or the default branch. uv-launch pins each resolution to exact commits and checks floating references in the foreground before selecting a session generation. The ordinary #!/usr/bin/env toolfuncs shebang is sufficient. The older --allow-floating-vcs shebang is accepted but no longer changes policy. Adjacent uv lock --script files are not consumed; uv-launch owns the cached resolutions.

Only functions registered on app become CLI commands. Imported functions and __all__ do not define the command surface. Function command and option spelling follows Cyclopts' Python-to-kebab-case projection.

No if __name__ == "__main__": block is required. The operating system passes the source path to the shebang interpreter, and toolfuncs imports the source under its declared Python name before invoking its module-level app. Consequently, weather-report ... is the supported CLI while python weather-report ... merely defines the module and exits, or fails if its dependencies are not already installed.

Packaged tools opt in with an additional matching entry point:

[project.entry-points."toolfuncs.dynamic_description"]
my-tool = "my_package.tools:app"

Keep the matching project.scripts and project.entry-points.toolfuncs declarations. Publish descriptions through the same toolsdk.publish_description API. Discovery reads the current user's published line on each call and falls back to the module docstring when no valid line exists; it does not import the tool or access its service.

Package-backed implementations

A toolfunc remains one script even when most of its implementation lives in a package:

#!/usr/bin/env toolfuncs
# /// script
# requires-python = ">=3.11"
# dependencies = ["my-large-package"]
# [tool.toolfuncs]
# description = "Run the package's agent operation."
# cli_name = "package-operation"
# python_name = "package_operation"
# ///

from my_large_package.agent_tool import app, perform_operation

The imported app and functions are the original Python objects. Toolfuncs does not inspect the package's project layout, metadata, or import structure.

Discovery and precedence

Toolfuncs reads the current process's PATH from left to right. It considers only executable files and symlinks, reads their first line, and parses PEP 723 metadata only when either exact toolfuncs shebang matches.

The first executable filename claims a command name even when it is not a toolfunc. Therefore an ordinary executable earlier on PATH hides a same-named toolfunc later on PATH, matching what the shell actually executes. Repeated directories are scanned once, inaccessible directories are skipped, and discovered records are sorted by CLI name.

toolfuncs list returns one JSON object with tools and invalid_tools. Valid records contain cli, python, path, and the effective description; python is the complete dynamic facade name, such as toolfuncs.weather_report. For dynamic = true, discovery reads the optional one-line state file and otherwise uses the static description. An opted-in but invalid tool is reported with its path and validation error without hiding unrelated valid tools; the command also prints a concise warning to stderr and exits successfully. Listing never imports a tool, prepares its dependencies, executes its source, or accesses the network.

Validate a tool while authoring

Run the doctor against one explicit source before installing or committing it:

toolfuncs doctor ./weather-report

The doctor validates the static source contract, prepares the declared dependencies, loads the source, and inspects the actual Cyclopts command graph. It requires a module docstring, at least one registered operation, a docstring and complete annotations for every operation, effective help text for every visible parsed CLI argument, and successful root and command help generation. Aliases are inspected once through their underlying command. Doctor also checks the shared interface and reports independent parameter, nested-field and return-schema errors. It does not invoke operations or predict their results. Normal Pydantic schema hooks can run during inspection; this is not a sandbox for author code.

The result is always one strict-JSON tagged union. A valid source exits zero:

{"status": "success", "path": "/path/to/weather-report", "commands": ["current"]}

Expected authoring failures return status: "error", list every independently detectable operation error, and exit nonzero without using an exception as the result:

{"status": "error", "path": "/path/to/weather-report", "errors": ["current: CLI argument '--city' must have help text"]}

Loading is intentional: the registered command graph and Cyclopts' effective parameter help can be constructed dynamically and should not be approximated with a second source parser. The CLI doctor prepares an isolated uv-launch runtime; calling the Python doctor directly uses the current interpreter.

Install the runtime

uvx --from "toolfuncs>=2,<3" toolfuncs setup

Setup bootstraps uv-launch through a transient uv tool run invocation, prepares managed toolfuncs and toolfuncs-agent-hook commands, and writes small forwarding wrappers at ~/.local/bin/toolfuncs and ~/.agents/hooks/toolfuncs/hook. Both managed commands use at least the running toolfuncs version and receive updates within that major version. These wrappers no longer invoke uvx on each call. The destination must already be on PATH; setup is idempotent, refuses to overwrite unmanaged commands, and does not modify shell profiles.

Both shebang invocation and toolfuncs SOURCE delegate requirements to uv-launch before executing the source in an immutable environment. Outside a prepared session, launch waits for a foreground freshness check. Failed checks fail the launch; cached reuse requires UV_LAUNCH_OFFLINE=1 or UV_OFFLINE=1. Within a prepared session, launches reuse its selected generation. Source edits run immediately; changed requirements or interpreter constraints need a matching environment. toolfuncs doctor SOURCE uses the same isolated preparation.

The managed hook uses the toolfuncs[hooks] environment and is registered at user scope for Codex and Claude Code through typed-agent-hooks. Its wrapper directly executes the immutable hook executable selected during setup, so timed hooks cannot start an update or wait for a session ledger lock. Running setup again refreshes that runtime and reconciles registrations. A session launcher can instead provide its own freshly prepared runtime and shared selection.

Setup does not discover, copy, register, link, synchronize, or remove tools. There is no dedicated tools directory, registry, manifest, generated shim, project scope, user scope, or sync command.

Management commands

toolfuncs SOURCE [ARGS...]
toolfuncs doctor [--prepared] [SOURCE]
toolfuncs mcp [--prepared] [SOURCE]
toolfuncs list
toolfuncs catalog
toolfuncs commands FAMILY [--group JSON_PATH]
toolfuncs inspect-operation FAMILY JSON_PATH
toolfuncs setup [--bin-dir DIRECTORY]

toolfuncs SOURCE [ARGS...] runs a known source directly. This is the explicit-source interface, not a name dispatcher: toolfuncs weather-report resolves weather-report as a source path rather than searching PATH for that tool name. A toolfunc on PATH can instead be invoked directly by its own filename.

Agent integration

The discovery hook allows 30 seconds for reading a cold PATH. Large system executable directories can take longer than 10 seconds on first access, even though discovery only reads static metadata. Re-run toolfuncs setup after upgrading to refresh an existing hook registration.

The installed hook runs at initial session start and at the session-start event emitted after compaction. It discovers the effective tools directly from that harness process's PATH, then adds a compact catalog such as:

Available toolfuncs:
- `weather_report`: Read current weather conditions.
  CLI: `weather-report --help`
  Python: `import toolfuncs as tools; help(tools.weather_report)`

The hook reads executable headers and PEP 723 metadata, plus the optional one-line state file for tools declaring dynamic = true. It advertises valid tools and lists invalid tool paths and errors separately; one invalid tool does not suppress the others. It does not import tools, resolve their dependencies, execute them, or access the network. The hook itself is harness infrastructure, not a toolfunc, and therefore does not carry the toolfuncs shebang or appear in the catalog.

Development

Development and CI resolve dependencies from PyPI. The runtime requires uv-launch 0.2.9 or later for managed source installations that select toolfuncs 2.0.

uv sync
uv run pytest
uv run ruff format --check .
uv run ruff check .
uv run basedpyright

The exact guarantees and non-goals are recorded in the contract.

Releasing

The GitHub Release is the release control point. Set [project].version, merge and push that commit, then publish a GitHub Release whose tag is v<version>. The release workflow verifies that the tag and package version match, builds the wheel and source distribution, and publishes them to PyPI through Trusted Publishing.

Prepared source installations

For a toolfunc delivered together with adjacent files, use uv-launch 0.2.9 or later and uv-launch install-source NAME REPOSITORY SOURCE. The launcher resolves the PEP 723 dependencies and validates the tool before selecting a generation. PATH discovery and the Python facade recognize the managed source without executing its shell wrapper.

toolfuncs run-prepared SOURCE ARGS... executes in the caller's already selected environment. toolfuncs doctor --prepared SOURCE validates that same boundary. These commands are for environment managers and integration tests; ordinary local development can continue to use toolfuncs SOURCE ARGS..., which prepares declared dependencies when necessary.

Metadata

Release files for toolfuncs 2.1.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 toolfuncs 2.1.0
File Size Uploaded Provenance
toolfuncs-2.1.0.tar.gz 200.6 kB With provenance Details

Built distribution (wheel)

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

Total release size: 255.8 kB

Release files / toolfuncs-2.1.0.tar.gz

Download URL toolfuncs-2.1.0.tar.gz
Size 200.6 kB
Tags Source
SHA-256 checksum
How to use checksums
fe0674e80b1326dceaa937dd6a0a411851c3dc8572eac7297f8f7233a9e45c77
BLAKE2b-256 checksum
How to use checksums
20e3c12f55481a886735b98cf73b61a1b21a6b7b84fe19e0b8c32d21878c542d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 20, 2026.

Transparency log

Release files / toolfuncs-2.1.0-py3-none-any.whl

Download URL toolfuncs-2.1.0-py3-none-any.whl
Size 55.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
18722abd23750c88fa7b452305be7eea0274181ae161f616423784dfc282bc25
BLAKE2b-256 checksum
How to use checksums
195e9d5c355db44aea2be61230019604b22b88a3e1675eb1881f2a59b6b79e4d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 20, 2026.

Transparency log

Security for toolfuncs 2.1.0

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for toolfuncs-2.1.0.tar.gz

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 Oct 20, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

2.1.0 This release

2 release files Full provenance

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