Skip to main content

FuncWire

PyPI Python License CI

Portable contracts for Python callables.

FuncWire is a zero-dependency contract runtime for typed Python callables. It converts ordinary functions, bound methods, and callable objects into deterministic, machine-readable contracts with JSON Schema, strict recursive validation, annotation-directed codecs, exact Python signature binding, synchronous and asynchronous invocation, return contracts, and provider adapters.

Designed as infrastructure rather than framework glue, FuncWire gives RPC systems, workflow engines, plugin platforms, remote workers, interface generators, MCP servers, and LLM tool-calling stacks one stable intermediate representation for Python callable boundaries.

Engineering guarantees

  • Provider-neutral core: no OpenAI, Anthropic, Gemini, MCP, or orchestration assumptions in the IR.
  • Exact signature semantics: positional-only, keyword-only, defaults, *args, and **kwargs.
  • Strict by default: external strings are not silently coerced into numbers or booleans.
  • Structured codecs: dataclasses, TypedDicts, enums, UUIDs, paths, dates, times, and decimals.
  • Deterministic contracts: modern JSON Schema, recursive $defs/$ref, and versioned serialization.
  • Small dependency surface: no runtime dependencies and no provider SDK imports.
  • Operational clarity: structured errors, sync/async separation, security guidance, and typed APIs.

FuncWire is an alpha. The v1 serialized contract is versioned, but the Python API may evolve before 1.0.

Install

pip install funcwire

Python 3.10 or newer is required.

First contract

from typing import Literal
from funcwire import spec


def weather(city: str, units: Literal["c", "f"] = "c") -> dict[str, float]:
    """Get current weather.

    Args:
        city: City to look up.
        units: Temperature unit.
    """
    return {"temperature": 21.0}


tool = spec(weather)

print(tool.name)
print(tool.input_schema)
values = tool.validate({"city": "Toronto"})
result = tool.call({"city": "Toronto", "units": "c"})
openai_tool = tool.export("openai")
mcp_tool = tool.export("mcp")

validate() is strict: "21" is not accepted for int or float. Decoding only happens when the annotation justifies it; for example, a UUID string becomes uuid.UUID, and an object matching a dataclass becomes that dataclass.

Annotated constraints

from typing import Annotated
from funcwire import Description, Max, Min, spec


def set_speed(
    speed: Annotated[float, Description("Motor speed"), Min(0), Max(1)],
) -> None: ...


speed = spec(set_speed)
speed.validate({"speed": 0.5})

Description, Min, Max, MinLength, MaxLength, Pattern, and Examples map directly to JSON Schema concepts and are enforced locally where applicable.

Signatures and invocation

FuncWire supports positional-only parameters, positional-or-keyword parameters, keyword-only parameters, defaults, *args, and **kwargs. External calls are always objects. The keys for variadic parameters contain an array and object respectively:

def invoke(a: int, /, *items: str, enabled: bool, **labels: float):
    return a, items, enabled, labels


tool = spec(invoke)
tool.call(
    {
        "a": 1,
        "items": ["x", "y"],
        "enabled": True,
        "labels": {"score": 0.8},
    }
)

Calling .call() on an async function raises InvocationError. Use await tool.acall(data). acall() can also invoke a synchronous callable without using a thread or executor.

Supported annotations

  • str, int, float, bool, None, and Any
  • list, tuple, set, frozenset, and string-keyed dict
  • Sequence, Iterable, Mapping, Optional, Union, Literal, and Annotated
  • modern | unions, resolvable aliases, bounded/constrained TypeVar
  • dataclasses, nested/recursive dataclasses, TypedDict, and Enum
  • datetime, date, time, Path, UUID, and Decimal

Unresolvable forward references, non-string mapping keys, unsupported classes, non-scalar enum or literal values, and unknown Annotated metadata fail explicitly. Arbitrary classes are not guessed.

Custom types

from funcwire import register_type

register_type(
    MyType,
    schema={"type": "string", "format": "my-type"},
    validator=lambda value: isinstance(value, str),
    decoder=MyType.parse,
    encoder=str,
)

Registration is atomic and concurrent reads use a lock-protected registry. A newly built contract captures schema metadata, while validation and codecs consult the current registry. Register types during application startup for stable behavior. unregister_type(MyType) is intended primarily for tests and plugin lifecycle management.

Provider adapters

tool.export("openai"), tool.export("anthropic"), tool.export("gemini"), and tool.export("mcp") return plain dictionaries. No provider SDK is imported or required. These are metadata transformations; the universal FuncSpec remains provider-neutral.

Results, serialization, and compatibility

Use validate_result() for external result data and encode_result() for Python results. to_dict() and to_json() serialize only the contract description, tagged with funcwire_version: 1; executable callables are deliberately not serialized. diff(old, new) reports conservative compatibility changes to parameters and returns.

Validation is not a sandbox

tool.call(data) executes the wrapped Python callable normally with the process's permissions. Validation establishes data shape and type; it does not make untrusted code safe, authorize an operation, limit resource use, or isolate side effects. Provider exports provide no security boundary.

Limitations

  • Callable reconstruction from serialized contracts is intentionally unsupported.
  • Generator functions, overloaded dispatch semantics, protocols, arbitrary user classes, and provider-specific strict-schema subsets do not receive special handling yet.
  • Decimal uses a JSON string to avoid precision loss.
  • Abstract sequences decode to list; abstract mappings decode to dict.
  • Compatibility analysis is conservative; type changes are classified as potentially breaking rather than attempting formal schema subsumption.

See the architecture, competitive analysis, and release audit.

Development

python -m venv .venv
.venv/bin/pip install -e '.[dev]'
.venv/bin/pytest --cov=funcwire --cov-branch
.venv/bin/ruff check .
.venv/bin/mypy src
.venv/bin/python -m build

Contributions are welcome under the Apache License 2.0. See CONTRIBUTING.md.

Metadata

Release files for funcwire 0.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 funcwire 0.1.0
File Size Uploaded
funcwire-0.1.0.tar.gz 31.7 kB Details

Built distribution (wheel)

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

Total release size: 56.0 kB

Release files / funcwire-0.1.0.tar.gz

Download URL funcwire-0.1.0.tar.gz
Size 31.7 kB
Tags Source
SHA-256 checksum
How to use checksums
a813a703670f6f70d1c1c06aa54116259df6bb308f8534d591fb1a5431bb1436
BLAKE2b-256 checksum
How to use checksums
d5a45f8f3a9883293d358684d6f27fff801b1cd34fbf8ca4978ebd90be5a3226
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.4

Release files / funcwire-0.1.0-py3-none-any.whl

Download URL funcwire-0.1.0-py3-none-any.whl
Size 24.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a5599a077b668e4e897c277309ce0d6b99e7fd4b0a46bf25c590c775241e7633
BLAKE2b-256 checksum
How to use checksums
6672afb71ea3747cf1641e1e94238c9f3e4fb5f5dd37301f5365e6a3ba0a11a7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.4

Release history Release notifications | RSS feed

This release

0.1.0 This release

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