Skip to main content

fastmcp-pvl-core

The opinionated shared implementation for the pvliesdonk/*-mcp server family. fastmcp-pvl-core owns the shape of cross-cutting concerns — auth, middleware, logging, config, and server-factory builders — and exposes narrow hooks to downstream servers for domain-specific behaviour. Downstream conforms to the shape; pvl-core does not adapt to downstream preferences. See Design principles for the rationale and the classification test that follows from it.

Ecosystem

Design principles

fastmcp-pvl-core is not a buffet of helpers downstream picks from à la carte. It is the load-bearing layer that fixes the shape of cross-cutting concerns across the server family so the family stays coherent as it grows. Five principles follow from that role; a sixth keeps the exit clean for forks that leave the family.

Shape decisions live in pvl-core

Tool names, parameter shapes, route structures, capability declarations, error envelopes, environment-variable contracts — pvl-core picks one shape and downstream conforms. If two downstream servers would each prefer a different shape, the resolution is for pvl-core to pick one and migrate the others to it, not for pvl-core to grow an override kwarg.

Hooks expose domain-specific behaviour only

A hook like "where in my storage model do these bytes go?" is appropriate — pvl-core cannot know the answer for a particular downstream. A hook like "what should this tool be called?" or "what HTTP status code should an oversize body return?" is not — those are shape decisions pvl-core owns, and downstream accepts them.

The test for any proposed kwarg on a register_* helper, Build* factory, or middleware constructor: would pvl-core be wrong to make this decision itself? If pvl-core could pick a sensible value and downstream has no domain-specific basis to disagree, pvl-core picks it — no kwarg. If pvl-core literally cannot answer because the answer is about the downstream's domain, the kwarg exists and is not optional unless the entire feature is opt-in. There is no third bucket of "pvl-core has a default but downstream can override."

Operator-side configuration (TTL ceilings, max body sizes, listening ports, debug flags) is a separate axis — environment variables, not kwargs. The kwarg surface is purely domain hooks.

If a proposed kwarg mixes the two — a legitimate hook bundled with an override of shape — split it: keep the hook, drop the override. PRs that grow override kwargs disguised as hooks are rejected.

Spec docs are protocol extensions, not design docs

Files under docs/specs/ describe the wire format and behaviour requirements between independently developed servers — what bytes move between systems and under what rules. Implementation choices that pvl-core happens to make (lazy materialisation strategies, route mechanics, framework-specific helpers, downstream tool naming and registration mechanics) do not belong in a spec doc; they belong in pvl-core's own implementor docs and code comments. Real spec gaps are resolved through a proper spec evolution — a new release with the version field bumped — not through inline amendments to a published version.

Pre-existing downstream conflicts resolve by migration

If a downstream server has already shipped a different shape (a differently named tool, a divergent parameter, a custom error envelope), the resolution is for the downstream to migrate. pvl-core does not grow a compatibility shim to spare downstream the migration cost, even when the migration is large. If the migration cannot land immediately, file a tracked downstream issue and ship the breaking change in pvl-core anyway — the umbrella tracker coordinates the cutover and the fastmcp-server-template scaffold updates carry the new shape forward to fresh consumers.

This applies to shape divergence (the things owned by pvl-core). Domain-specific divergence between downstreams is expected and does not require any migration — downstreams are supposed to differ in domain logic.

Downstream reuses pvl-core; it does not reimplement the protocol

Downstream servers reuse pvl-core's implementation of the shared cross-cutting protocols — auth, logging, and the rest. They do not reimplement a wire protocol independently. The specs under docs/specs/ are the wire authority; pvl-core is their single shared implementation. No implementation is "the reference" — not pvl-core's either; the spec is.

If pvl-core's implementation is wrong, or diverges from a spec, the fix is to correct pvl-core centrally — one change, every downstream follows — or to evolve the spec. A downstream that believes pvl-core is wrong files the issue against pvl-core; it does not fork the behaviour and reimplement it locally.

Keep pvl-core cleanly foldable

A fork is not a downstream. The MIT licence lets anyone vendor pvl-core into their own tree — to take over a single server when the family is no longer maintained, or to run their own opinionated variant. That exit ramp is kept cheap on purpose: the seams that make pvl-core foldable (relative intra-package imports, no runtime lookups of its own package name, identity passed in rather than hard-coded, a narrow public surface) are the same seams that keep it a clean load-bearing layer. Foldability is a modularity property, not a coherence compromise — and never an excuse to flatten pvl-core's own abstractions "in case someone forks"; collapsing those is fork-side work.

Planning to fork and cut the dependency? See docs/forking.md for the fold-in recipe and what a single-server fork can safely collapse.

API stability

This package is stable at 2.x and follows semantic versioning: breaking changes bump the major version, new features bump the minor, bugfixes bump the patch. "Public API" means symbols re-exported from the top-level fastmcp_pvl_core package (see __all__), which intentionally covers both the runtime surface (auth, middleware, factory builders, env/config helpers) and the CLI parser helpers consumed by downstream server.py entrypoints. Modules prefixed with _ are internal and may change without a major-version bump.

Install

uv add fastmcp-pvl-core
# If you use RemoteAuthProvider mode:
uv add "fastmcp-pvl-core[remote-auth]"
# For attaching a remote Python debugger inside a container image:
uv add "fastmcp-pvl-core[debug]"

Usage

See src/fastmcp_pvl_core/ for the full surface. Typical usage:

from fastmcp import FastMCP
from fastmcp_pvl_core import (
    InstructionRole, ServerConfig, apply_tool_visibility, build_auth,
    finalize_instructions, instructions_for, wire_middleware_stack,
)

config = ServerConfig.from_env("MY_APP")
mcp = FastMCP(name="my-app", auth=build_auth(config))
wire_middleware_stack(mcp)

instructions = instructions_for(mcp)
instructions.identity("my-app", "A widget service.")
instructions.add(
    "This instance is READ-ONLY.",
    role=InstructionRole.INSTANCE,
)
instructions.documentation("https://example.com/my-app/llms.txt")
# ... register tools; core register_* helpers add their own workflow snippets ...
apply_tool_visibility(mcp, config)
finalize_instructions(mcp, config, env_prefix="MY_APP")

Instructions (model-facing guidance)

Instructions carry what no single tool description can carry: identity, a documentation pointer, cross-tool workflows, and enforced instance facts.

Role Owner Meaning Tool dependencies
IDENTITY pvl-core shape, template values Deployed server and product identity forbidden
ROUTING operator Data or domain this deployment serves forbidden
INSTANCE domain/core Enforced configuration facts and limits allowed when the fact depends on named tools
POLICY operator Deployment-specific behavioral policy forbidden
CAPABILITIES domain/core Classes of work the server performs allowed
WORKFLOWS domain/core How multiple tools compose allowed
DOCUMENTATION template/core Where complete documentation lives forbidden

Add domain snippets with instructions_for(mcp).add(text, role=..., requires_tools=...). General contributors use only INSTANCE, CAPABILITIES, and WORKFLOWS; pvl-core reserves identity, operator routing/policy, and documentation so their shape and ownership stay consistent. A snippet requiring a tool FastMCP hides through global provider, mount, namespace, or ordered visibility transforms is dropped at finalize_instructions. Per-session transforms and per-subject authorization remain outside this static instruction string; guidance for an auth-gated tool must read naturally when that tool is unavailable to the current caller. Finalization runs during synchronous server construction, before entering an event loop, so FastMCP can evaluate its asynchronous global listing path safely.

The rendered survival order is deployment identity, operator routing, enforced instance facts, operator policy, capabilities, workflows, then documentation. The operator environment contract is:

  • {PREFIX}_SERVER_NAME is passed by the server factory to identity(server_name, product_description) and identifies the deployment.
  • {PREFIX}_INSTANCE_DESCRIPTION concisely describes which material or responsibility distinguishes this instance for routing.
  • {PREFIX}_INSTRUCTIONS_EXTRA supplies deployment-specific behavioral policy.
  • {PREFIX}_INSTRUCTIONS is a deprecated full replacement. When set, it ignores both additive operator variables and logs a warning naming them.

pvl-core measures instructions in UTF-16 code units, matching JavaScript String.length. Generated guidance targets at most 1,536 units, reserving 512 units for normal operator routing and policy within Claude Code's known 2,048 unit boundary. Exceeding either threshold logs a role-level warning; pvl-core does not truncate instructions or fail startup. Use utf16_code_units, GENERATED_INSTRUCTIONS_TARGET_UTF16, and CLAUDE_CODE_INSTRUCTIONS_LIMIT_UTF16 to enforce the same profile in tests.

Tool visibility (operator allow-/denylist)

Every exposed tool costs context in the connecting MCP client, so operators can trim what an instance exposes with two env vars, each a comma-separated list of explicit tool names:

  • {PREFIX}_TOOLS_ALLOW — the instance exposes only these tools.
  • {PREFIX}_TOOLS_DENY — these tools are hidden.

Hidden tools disappear from tools/list and are rejected on tools/call. Setting both variables is a startup ConfigurationError (an allowlist already expresses every exclusion). Individual names matching no registered tool are inert, so one operator config survives releases that add or remove tools — but an allowlist that leaves zero tools exposed (fully mistyped or fully stale) logs a startup WARNING, since that would otherwise present as a silent total tool outage. Resources, resource templates, and prompts are unaffected.

Servers wire it in with one call, after any visibility adjustments of their own so the operator's lists win:

from fastmcp_pvl_core import apply_tool_visibility

apply_tool_visibility(mcp, config)   # config: ServerConfig.from_env("MY_APP")

Logging

configure_logging_from_env resolves the log level from the -v CLI flag (forces DEBUG), then FASTMCP_LOG_LEVEL, then defaults to INFO.

At INFO and above, two noisy third-party loggers are demoted to WARNING so they do not flood the operator log stream:

  • uvicorn.access — the INFO: <ip> - "POST /mcp ..." HTTP access log.
  • mcp.server.lowlevel.server — the MCP SDK's Processing request of type ... line.

Both reappear at DEBUG (-v or FASTMCP_LOG_LEVEL=DEBUG). uvicorn.error is never demoted — it carries genuine bind / startup failures.

One logger is capped in the other direction. docket.worker — pydocket's background-task worker, which every consumer inherits through the fastmcp[tasks] base dependency — logs a record per poll iteration at its 250 ms default check interval, roughly 2500 lines/minute on a queue that never receives a job. At DEBUG it is pinned to INFO, so its startup and lifecycle records still appear while the idle poll trace does not; at every other level it is untouched. An operator debugging the task queue itself restores the full stream after the call:

configure_logging_from_env(verbose=True)
logging.getLogger("docket.worker").setLevel(logging.DEBUG)

wire_middleware_stack installs a single conforming request-logging middleware. Every line it emits starts with a bare snake_case event name, followed by key=value pairs, with request timing carried inline:

tool_call_started   tool=read method=tools/call source=client
tool_call_completed tool=read duration_ms=68.57
tool_call_failed    tool=read duration_ms=109.84 error_type=ValueError error="Section '1.3' not found"

Non-tool messages use a generic request_* / notification_* vocabulary keyed by method=. Set FASTMCP_ENABLE_RICH_LOGGING=false to emit one JSON object per record instead of key=value text — for log aggregators such as the ELK stack or Splunk.

Background task backend

SEP-1686 task support (fastmcp[tasks] / Docket) is a pvl-core base dependency — nearly every family server carries long-running tools, and fastmcp raises ImportError at registration time for any task=True tool when pydocket is missing, so the ~10 MB is deliberately always present. Servers that register task-enabled tools call configure_task_backend once before mcp.run(...):

from fastmcp_pvl_core import configure_task_backend

configure_task_backend("MY_APP", config)

Backend selection then follows pvl-core's unified surface: an explicit MY_APP_TASKS_URL (memory:// or redis://) wins; otherwise a redis:// MY_APP_KV_STORE_URL is reused for the task queue too, so one variable configures every stateful subsystem and tasks; otherwise fastmcp's memory:// default applies (in-process, lost on restart — fine for development, not for a multi-process deployment). The Docket queue name is derived from the env prefix so family servers sharing one Redis do not share a queue. The helper degrades to a no-op in the degenerate case of a stripped fork or an incompatible pydocket pin.

The remaining Docket worker tunables are native fastmcp variables, deliberately not wrapped: FASTMCP_DOCKET_CONCURRENCY, FASTMCP_DOCKET_WORKER_NAME, FASTMCP_DOCKET_REDELIVERY_TIMEOUT, FASTMCP_DOCKET_RECONNECTION_DELAY, FASTMCP_DOCKET_MINIMUM_CHECK_INTERVAL. FASTMCP_DOCKET_URL / FASTMCP_DOCKET_NAME also keep working as native escape hatches when the pvl-core surface leaves them untouched.

Long-running tools (dual mode)

A tool that may outlive the client's request timeout registers once and gets both behaviours: protocol-native SEP-1686 task execution when the request is task-augmented, and foreground execution with soft-deadline promotion to a pollable background job otherwise:

from fastmcp_pvl_core import (
    JobsConfig, build_jobs, register_job_tools, register_long_running_tool,
)

jobs_config = JobsConfig.from_env("MY_APP")   # MY_APP_JOBS_* knobs
jobs = build_jobs(config, jobs_config)

@register_long_running_tool(mcp, jobs, tags={"reports"})
async def build_report(paths: list[str]) -> dict:
    ...  # domain work; may take minutes

register_job_tools(mcp, jobs)  # the one generic get_job_result tool

A call that beats MY_APP_JOBS_SOFT_DEADLINE_S returns its result inline; a slower one immediately returns a job handle ({"status": "working", "job_id": ..., "poll_with": "get_job_result", ...}) and finishes in the background — results are retrievable via get_job_result until MY_APP_JOBS_RESULT_TTL_S expires, scoped to the calling subject.

A server whose long-running tool the wrapper cannot express (its own promotion decision, a handle minted from a route) composes on the same mechanics without the wrapper — from fastmcp_pvl_core.jobs import build_jobs and use jobs.run_with_deadline(...) / jobs.start(...) inside its own tool. For intentional, runtime deferrals such as an upstream rate limit, jobs.defer(...) adds the client-visible reason and first-poll interval. The handles resolve through the same generic polling tool. Do not reach into fastmcp_pvl_core._jobs internals; the jobs namespace is the supported seam.

The downstream contract — payload shapes, inline-failure semantics, scoping/retention limits, and the path-2 rules — lives in the docstrings of register_long_running_tool, register_job_tools, Jobs, and build_jobs (they are the authority a coding agent reads first); docs/jobs.md is the same contract as a narrative implementation guide.

Per-user subject mapping (bearer auth)

Bearer auth has two modes:

  • Single tokenMY_APP_BEARER_TOKEN=<token> accepts one shared token. Authenticated callers all share the same subject (default "bearer-anon"; override with MY_APP_BEARER_DEFAULT_SUBJECT=<value>).

  • Mapped tokensMY_APP_BEARER_TOKENS_FILE=/path/to/tokens.toml loads a token→subject map at startup. Each token resolves to a distinct subject string for downstream attribution (audit logs, ACLs, request metadata).

# tokens.toml
[tokens]
"ghp_alice_xxxxxxxx" = "user:alice@example.com"
"sk_ci_yyyyyyyy"     = "service:ci-bot"

If both MY_APP_BEARER_TOKEN and MY_APP_BEARER_TOKENS_FILE are set, the file wins and a WARNING is logged. Subject strings are opaque to the library; the <kind>:<id> convention (user:, service:, token:) is documentation only.

If MY_APP_BEARER_TOKENS_FILE is set but the file is missing, unparseable, or schema-invalid, the loader raises fastmcp_pvl_core.ConfigurationError at startup — the server fails fast rather than silently denying every request. The exception type is part of the public API; downstream code can import and except it as a stable contract.

MY_APP_BEARER_DEFAULT_SUBJECT only applies when bearer auth runs in single-token mode (either standalone or as the bearer side of multi mode alongside OIDC). It is ignored when MY_APP_BEARER_TOKENS_FILE is set, including in multi mode — mapped mode uses the per-token subjects from the TOML file.

OIDC scopes — requested vs. required

Two different questions, two different settings:

  • What a client should ask the IdP for — advertised in the server's protected-resource metadata (RFC 9728). pvl-core advertises openid offline_access by default. offline_access is what makes the IdP issue a refresh token; without it a session ends at access-token expiry and needs a human to complete a browser flow again.

  • What a token must carry to be acceptedMY_APP_OIDC_REQUIRED_SCOPES=<space- or comma-separated>. This is a hard requirement checked on every request, so keep it minimal; a scope listed here is always advertised too, or clients would never request it and every token would fail the check.

Override the advertised set with MY_APP_OIDC_ADVERTISED_SCOPES=<space- or comma-separated> when the deployment needs something else — for example a registered client that is not permitted offline_access, or extra claim scopes (groups, email) that clients should request but that tokens are not required to carry. MY_APP_OIDC_REQUIRED_SCOPES is still added on top.

pvl-core's own default is filtered against the IdP's published scopes_supported (some providers reject an authorization request outright with invalid_scope rather than ignoring an unknown scope); a scope dropped that way is logged at WARNING. An operator-set MY_APP_OIDC_ADVERTISED_SCOPES is used verbatim — a client-level restriction is not visible in discovery, so the operator's list wins.

Identifying the caller — get_subject

Tools, middleware, and resource handlers can call fastmcp_pvl_core.get_subject() to retrieve the subject of the current request without knowing which auth mode is active:

from fastmcp_pvl_core import get_subject

@mcp.tool
def whoami() -> str:
    subject = get_subject()
    return subject or "anonymous"

Resolution order:

  1. Token present: prefer claims["sub"] (OIDC's standard subject claim); fall back to client_id if sub is absent. The auth builders normalise client_id per mode:
    • bearer-singlebearer_default_subject (default "bearer-anon").
    • bearer-mapped → the per-token subject from the TOML map.
    • OIDC modes (oidc-proxy, remote) → typically claims["sub"] wins (a real OIDC token always carries sub); the client_id fallback is defensive.
    • multi → bearer-validated requests follow the bearer path, OIDC-validated requests follow the OIDC path.
  2. No token, auth_mode == "none": returns the literal "local".
  3. No token, auth required: returns None — caller decides whether to fall back or error.

Authorization (opt-in) — native auth checks

pvl-core builds on FastMCP's native authorization (AuthCheck + AuthMiddleware). It ships factories for the two checks the framework has no built-in for — subject→scope (the only per-token authz available in bearer modes) and claim→scope (group/role authz for OIDC modes) — plus an OR-combinator for multi mode. Scope- and tag-based patterns use FastMCP's own require_scopes / restrict_tag.

Components opt in with meta={"required_scope": "<scope>"}; the checks read it. Components without it are unrestricted.

import os
from pathlib import Path
from fastmcp import FastMCP
from fastmcp.server.middleware import AuthMiddleware
from fastmcp_pvl_core import (
    make_acl_check, make_claims_check, any_check, load_acl, parse_claim_grants,
)

# OIDC mode — claim-based (identity: name IdP groups to match scopes)
mcp = FastMCP(..., middleware=[AuthMiddleware(auth=make_claims_check("groups"))])

# bearer mode — static subject ACL
mcp = FastMCP(..., middleware=[AuthMiddleware(auth=make_acl_check(load_acl(Path("/etc/my-app/acl.toml"))))])

# multi mode — OR of both
raw = os.environ.get("MY_APP_AUTHZ_GRANTS")
grants = parse_claim_grants(raw) if raw else None
mcp = FastMCP(..., middleware=[AuthMiddleware(auth=any_check(
    make_acl_check(load_acl(Path("/etc/my-app/acl.toml"))),
    make_claims_check(os.environ.get("MY_APP_AUTHZ_CLAIM", "groups"), grants),
))])

@mcp.tool(meta={"required_scope": "write"})
async def edit_document(...): ...

ACL TOML schema (load_acl) and inline-JSON grants (parse_claim_grants):

[subjects]
"user:alice@example.com" = ["read", "write"]
"user:admin@example.com" = ["*"]          # wildcard scope
{"app-writers": ["read", "write"], "app-admins": ["*"]}

Key properties:

  • Claim vs scope. Claim-based authz reads OIDC claims (groups, roles) — the user's IdP-issued permissions — not OAuth scopes (which describe the client/token grant). Bearer tokens carry no usable claims, so use make_acl_check there.
  • Opt-in per component via meta["required_scope"]; absent ⇒ unrestricted.
  • * is the only special scope ("any required scope passes").
  • Loaders fail fast with ConfigurationError; never silent denial.
  • Loaded once at startup. Restart to pick up changes.
  • stdio transport bypasses checks entirely — FastMCP's AuthMiddleware short-circuits for stdio (no OAuth concept there), so every component is reachable.
  • On HTTP, install these checks only alongside an AuthProvider. AuthMiddleware still runs without one, but every request then carries no token, so a component with meta["required_scope"] is denied outright (unannotated ones stay open). Authorization is meaningful only when authentication is configured.

Remote debugging in containers

Containerised consumers can opt into a remote Python debugger by calling maybe_start_debugpy(env_prefix) early in their CLI entrypoint, passing the same per-app prefix the server uses for the rest of its config:

from fastmcp_pvl_core import configure_logging_from_env, maybe_start_debugpy

def main() -> None:
    configure_logging_from_env()
    maybe_start_debugpy("MY_APP")  # no-op unless MY_APP_DEBUG_PORT is set
    ...

Environment contract ({PREFIX} matches the argument):

  • {PREFIX}_DEBUG_PORT — TCP port to listen on. Unset, blank, or any value that parses to 0 is a silent no-op. Non-numeric or out-of-1..65535 values log a WARNING and the helper returns without raising.
  • {PREFIX}_DEBUG_WAIT — when truthy (1/true/yes/on, case-insensitive), block startup until the IDE attaches. Default is non-blocking.
  • If debugpy.listen() itself fails (port in use, permission denied, debugpy-internal error), the helper logs a WARNING and continues — a debug-port problem must never crash the server.

Install the optional debug extra on images that need the listener:

uv add "fastmcp-pvl-core[debug]"   # quote brackets in zsh
# or, equivalently:
uv add debugpy

The helper logs a WARNING and continues if debugpy is unavailable, so it is safe to ship in default scaffolds.

⚠️ Security: the listener binds 0.0.0.0 and debugpy's DAP protocol is unauthenticated — any peer that can reach the port has arbitrary code execution as the server process. Only enable {PREFIX}_DEBUG_PORT in environments where the port is reachable solely from a trusted developer workstation, e.g. kubectl port-forward, docker run -p 127.0.0.1:5678:5678 (loopback bind), or an SSH tunnel. Never publish the debug port on a public network.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fastmcp_pvl_core-6.0.0.tar.gz (568.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fastmcp_pvl_core-6.0.0-py3-none-any.whl (134.5 kB view details)

Uploaded Python 3

File details

Details for the file fastmcp_pvl_core-6.0.0.tar.gz.

File metadata

  • Download URL: fastmcp_pvl_core-6.0.0.tar.gz
  • Upload date:
  • Size: 568.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for fastmcp_pvl_core-6.0.0.tar.gz
Algorithm Hash digest
SHA256 360330f380ce3874d7dc9a48a9fcfcfed6229c9b3462b19db1156cc41084d875
MD5 28adf0c71874893bdf6ceea957586e63
BLAKE2b-256 38f558c0d4b929c9b7644701abe4775b3e4180ef97366875b20b48d83fdd5c79

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastmcp_pvl_core-6.0.0.tar.gz:

Publisher: release.yml on pvliesdonk/fastmcp-pvl-core

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fastmcp_pvl_core-6.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for fastmcp_pvl_core-6.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a271514591a6e1cbec4567b6503982b56171c94e61975eb1fbe28de6023b2285
MD5 6258c146e67ae01cf5ef7ebafa916c7a
BLAKE2b-256 df41d8d8fb7372fc9c9a35e2f6d2819e781f04eb4f3888d9fbe4b3ddc22ecf02

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastmcp_pvl_core-6.0.0-py3-none-any.whl:

Publisher: release.yml on pvliesdonk/fastmcp-pvl-core

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

7.0.0

2 files

This release

6.0.0 This release

2 files

5.1.0

2 files

5.0.0

2 files

4.11.3

2 files

4.11.2

2 files

4.11.1

2 files

4.11.0

2 files

4.10.1

2 files

4.9.0

2 files

4.8.0

2 files

4.7.0

2 files

4.6.1

2 files

4.6.0

2 files

4.5.0

2 files

4.4.0

2 files

4.3.0

2 files

4.2.0

2 files

4.1.0

2 files

4.0.1

2 files

4.0.0

2 files

3.2.0

2 files

3.1.0

2 files

3.0.1rc3

2 files

3.0.1rc2

2 files

3.0.1rc1

2 files

3.0.0

2 files

3.0.0rc1

2 files

2.1.0

2 files

2.0.0

2 files

1.2.0

2 files

1.1.0

2 files

1.0.0

2 files

0.1.0

2 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