Skip to main content

Convert OpenAPI specifications into native, production-grade LangChain tools.

Project description

langchain-openapi

langchain-openapi logo

Convert OpenAPI v3.0 & v3.1 and Swagger 2.0 specifications into native, production-grade LangChain tools.

CI Status PyPI Version Documentation Python 3.11+ Code Style: Ruff Checked with MyPy License: MIT


langchain_openapi is a modern Python library designed to seamlessly convert OpenAPI v3.0, v3.1, and Swagger 2.0 specifications into native, type-safe LangChain tools for AI agents and LLM applications.

No python code generation is requiredโ€”tools are generated dynamically at runtime with strict Pydantic input schemas, prompt optimization options, and production-ready middleware.


Installation & Importing

Install the PyPI package:

pip install langchain-openapi-tools
# or using uv
uv add langchain-openapi-tools

Import in Python:

from langchain_openapi_tools import OpenAPIToolkit, OpenAPIToolkitConfig

Migration Note

# Old (deprecated, but still works for backward compatibility):
from langchain_openapi import OpenAPIToolkit

# New (recommended):
from langchain_openapi_tools import OpenAPIToolkit

Features

  • โšก Zero-Code Tool Generation: Runtime conversion of OpenAPI specs (YAML/JSON) into LangChain StructuredTools.
  • ๐Ÿงญ Typed, Generic & Hybrid Toolkits: Choose per-operation typed tools, a constant set of generic GET/POST/... tools with on-demand endpoint discovery, or a hybrid mix โ€” all sharing the same execution stack.
  • ๐Ÿš€ Swagger 2.0 & OpenAPI 3.x: Native normalization of legacy Swagger 2.0 and modern OpenAPI 3.0/3.1 specs.
  • ๐Ÿ—œ๏ธ Prompt Optimization & Filtering: Description modes (full, compact, minimal), description compression, overrides, callbacks, and operation/tag filtering to drastically reduce context window usage.
  • ๐Ÿ”’ Pluggable Authentication: Built-in support for Bearer Tokens, API Key Headers, Query Parameters, Basic Auth, and custom request providers.
  • ๐Ÿ›ก๏ธ Production Middleware: Composable middleware architecture for Retries (exponential backoff), Rate Limiting (token-bucket), Caching (TTL), Pagination aggregation, and Sanitized Logging.
  • ๐ŸŽฏ Type-Safe Validation: Dynamically generated Pydantic input schemas ensure LLM arguments adhere strictly to specification types before network transport.
  • ๐ŸŒ Async Engine: Non-blocking asynchronous network transport powered by httpx.AsyncClient.

Supported Specifications

  • โœ… Swagger 2.0 (Automatically normalized to OpenAPI 3.0 via the built-in adapter layer)
  • โœ… OpenAPI 3.0.x (JSON and YAML)
  • โœ… OpenAPI 3.1.x (JSON and YAML, including union type arrays, oneOf / anyOf / allOf, and const)

Note: Swagger 2.0 is commonly referred to as OpenAPI 2.0.

Compatibility Matrix

Feature Swagger 2.0 OpenAPI 3.0 OpenAPI 3.1
Path / query / header / cookie params โœ… โœ… โœ…
$ref resolution (internal) โœ… โœ… โœ…
Base URL from host + basePath โœ… โ€” โ€”
Base URL from servers[] โ€” โœ… โœ…
Fallback base URL from spec source URL โœ… โœ… โœ…
application/json request bodies โœ… โœ… โœ…
application/x-www-form-urlencoded โœ… โœ… โœ…
multipart/form-data (incl. files) โœ… โœ… โœ…
text/* and application/xml โœ… โœ… โœ…
Vendor +json media types โœ… โœ… โœ…
oneOf / anyOf (as Python Union) โ€” โœ… โœ…
allOf merge into single model โ€” โœ… โœ…
nullable: true (3.0) โ€” โœ… โ€”
type: ["string", "null"] (3.1) โ€” โ€” โœ…
const โ€” โ€” โœ…
readOnly / writeOnly / deprecated โœ… โœ… โœ…
Security: Bearer / API key / Basic โœ… โœ… โœ…
Async execution (httpx.AsyncClient) โœ… โœ… โœ…

Not yet supported: discriminator-driven union routing, patternProperties, $dynamicRef / $dynamicAnchor, callbacks, links, webhooks, and XML request-body serialization from Python dicts.


Quick Start

from langchain_openapi_tools import OpenAPIToolkit

# Load spec from remote URL or local file
toolkit = OpenAPIToolkit.from_url("https://api.crossref.org/swagger-docs")

# Extract generated tools
tools = toolkit.get_tools()

print(f"Generated {len(tools)} tools:")
for tool in tools[:3]:
    print(f"- {tool.name}: {tool.description}")

Agent Integration

import asyncio
from langchain_openapi_tools import OpenAPIToolkit


async def main():
    toolkit = OpenAPIToolkit.from_url("https://api.crossref.org/swagger-docs")
    search_tool = toolkit.get_tool("get_works")

    if search_tool:
        result = await search_tool.ainvoke({"query": "LangGraph"})
        print("Search Result:", result)


if __name__ == "__main__":
    asyncio.run(main())

Execution Strategies: Typed, Generic & Hybrid

Large APIs can generate hundreds or thousands of typed tools, which blows up prompts and slows agents. langchain_openapi_tools ships three execution strategies that all share the same loader / parser / provider / middleware / executor stack โ€” only the LangChain interface changes.

Toolkit Best For Tool Count
Typed Small & medium APIs, maximum correctness One per operation
Generic Large APIs, low context usage Constant (9 tools)
Hybrid Enterprise APIs mixing hot paths and long tails Typed for selected ops + generic for the rest

Typed Toolkit (default)

from langchain_openapi_tools import OpenAPIToolkit

toolkit = OpenAPIToolkit.from_url(url)
tools = toolkit.get_tools()          # one StructuredTool per operation

Generic Toolkit

from langchain_openapi_tools import GenericOpenAPIToolkit

toolkit = GenericOpenAPIToolkit.from_url(url)
tools = toolkit.get_tools()          # constant set of tools

The generic toolkit always exposes:

  • HTTP tools โ€” GET, POST, PUT, PATCH, DELETE
  • Discovery tools โ€” search_operations, describe_operation, list_operations, list_tags

Intended reasoning flow:

user โ†’ search_operations("books")
     โ†’ describe_operation("get_books")
     โ†’ GET(endpoint="/books", params={...})
     โ†’ response

Nothing is duplicated: authentication providers, retry / rate-limit / cache / logging / pagination middleware, and the async HTTP executor are all reused.

from langchain_openapi_tools import (
    BearerAuthProvider,
    CacheMiddleware,
    GenericOpenAPIToolkit,
    InMemoryCacheBackend,
    RetryMiddleware,
)

toolkit = GenericOpenAPIToolkit.from_url(
    url,
    provider=BearerAuthProvider(token),
    middleware=[
        RetryMiddleware(retries=3),
        CacheMiddleware(backend=InMemoryCacheBackend(), ttl=60),
    ],
    timeout=30,
)

Hybrid Toolkit

Mix strategies within the same toolkit โ€” typed tools for the operations you care about, generic HTTP tools for everything else.

toolkit = GenericOpenAPIToolkit.from_url(url)

tools = toolkit.get_tools(
    mode="hybrid",
    typed_tags=["Books", "Users"],   # only these tags become typed tools
)

The classic OpenAPIToolkit also accepts mode="generic" / mode="hybrid" and transparently delegates, so existing setups can opt in without switching classes:

toolkit = OpenAPIToolkit.from_url(url)
tools = toolkit.get_tools(mode="hybrid", typed_tags=["Books"])

Prompt Optimization & Tool Customization

Large OpenAPI specifications can generate extensive tool descriptions that exceed model context windows. langchain_openapi_tools provides full control over description generation and context footprint.

Configuration Object (OpenAPIToolkitConfig)

from langchain_openapi_tools import OpenAPIToolkit, OpenAPIToolkitConfig

config = OpenAPIToolkitConfig(
    description_mode="compact",
    compress_descriptions=True,
    include_tags=["Works"],
    tool_description_overrides={
        "get_works": "Search scholarly papers registered with Crossref."
    },
)

toolkit = OpenAPIToolkit.from_url(
    "https://api.crossref.org/swagger-docs", config=config
)

Description Modes (description_mode)

  • full (default): Complete summary, description, HTTP method, path, and schema parameter details.
  • compact: Summary and short parameter list without response schemas or redundant examples.
  • minimal: A single-sentence summary ideal for large specifications with dozens of tools.
toolkit = OpenAPIToolkit.from_url(url, description_mode="minimal")

Description Compression (compress_descriptions=True)

Removes duplicate text, redundant whitespace, and empty sections without altering tool semantics:

toolkit = OpenAPIToolkit.from_url(url, compress_descriptions=True)

Custom Overrides & Callbacks

Override descriptions for specific tools:

toolkit = OpenAPIToolkit.from_url(
    url, tool_description_overrides={"get_works": "Search scholarly papers."}
)

Or pass a custom builder callback:

def my_builder(operation):
    return f"Execute {operation.name} on path {operation.path}."


toolkit = OpenAPIToolkit.from_url(url, description_builder=my_builder)

Operation Filtering

Filter tools before they are created to reduce context window overhead:

# Filter by Tags
toolkit = OpenAPIToolkit.from_url(url, include_tags=["Works"], exclude_tags=["Admin"])

# Filter by Operations
toolkit = OpenAPIToolkit.from_url(
    url, include_operations=["get_works"], exclude_operations=["delete_work"]
)

Architecture Pipeline

          Swagger 2.0 / OpenAPI 3.x
                     โ”‚
                     โ–ผ
            Swagger Normalizer
                     โ”‚
                     โ–ผ
          Normalized OpenAPI Model
                     โ”‚
                     โ–ผ
              Existing Parser
                     โ”‚
                     โ–ผ
           Internal Operation Models
                     โ”‚
                     โ–ผ
            Schema Converter
                     โ”‚
                     โ–ผ
              HTTP Executor
                     โ”‚
                     โ–ผ
          LangChain StructuredTools

Example:

from langchain_openapi_tools import OpenAPIToolkit
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model

toolkit = OpenAPIToolkit.from_url(
    "https://api.crossref.org/swagger-docs"
)

model = init_chat_model("openrouter:qwen/qwen3-32b:free")

agent = create_agent(
    model=model,
    tools=toolkit.get_tools(tags=["Works"]),
)

response = agent.invoke({
    "messages": "Find papers about Kashmir"
})

Why langchain-openapi Instead of the Alternatives?

Wiring an LLM up to a real API usually looks like one of these two extremes. Neither works well in production.

โŒ Option A โ€” Hand-rolling one function per endpoint

The "just write it yourself" path:

@tool
def list_pets(status: str) -> list[dict]:
    r = httpx.get(
        f"{BASE}/pets",
        params={"status": status},
        headers={"Authorization": f"Bearer {TOKEN}"},
    )
    r.raise_for_status()
    return r.json()

# ...repeat 20, 200, or 2,000 times.

What you actually end up building yourself:

  • One tool per operation, hand-copied from the docs.
  • A Pydantic schema per tool, kept in sync with the spec โ€” by hand.
  • Bearer / API-key / Basic / OAuth2 / cookie plumbing on every call.
  • Retry with exponential backoff, Retry-After handling, jitter.
  • Token-bucket rate limiting so you do not get banned.
  • Response caching with TTLs and cache-key hashing.
  • Pagination aggregation across next, Link, cursor, offset styles.
  • Log sanitization that strips secrets before they hit disk.
  • A test suite that mocks every route with respx.
  • A migration every time the vendor bumps the spec.

That is a library, not a feature. langchain-openapi is exactly that library, already written, already tested against Petstore, Crossref, GitHub, Stripe, and Kubernetes.

โŒ Option B โ€” Dropping the OpenAPI URL into the prompt

The "let the model figure it out" path:

System: Here is the API doc: https://api.example.com/openapi.json
User:   Please list all my pets.

Why this fails in practice:

  • Context explosion. Real specs are 200 KB โ€“ 5 MB of JSON. That is a cold-start prompt tax on every turn.
  • Hallucinated endpoints. Models happily invent /users/{id}/pets when the real path is /pet/findByStatus.
  • No auth. The LLM cannot inject a Bearer token into a request it is only describing. You still have to build an executor.
  • No validation. Nothing stops the model from sending status="MAYBE" when the enum is [available, pending, sold] โ€” the server just 400s and the agent loops.
  • No middleware. No retries, no rate limiting, no caching, no sanitized logging. The first 429 kills the run.
  • No reproducibility. A doc URL that changes silently changes agent behaviour with zero code diff.

โœ… langchain-openapi: the middle path

langchain-openapi treats the OpenAPI spec as the source of truth and generates a strict, production-grade tool surface at runtime.

Concern Hand-rolled LLM-reads-spec langchain-openapi
Tool generation Manual None Automatic from spec
Pydantic argument validation You write it โŒ Generated per operation
Path/query/header/cookie routing You write it โŒ Handled by RequestBuilder
Bearer / API-Key / Basic / OAuth2 You write it โŒ Pluggable RequestProviders
Retry with backoff You write it โŒ RetryMiddleware
Rate limiting You write it โŒ RateLimitMiddleware
Response caching You write it โŒ CacheMiddleware
Pagination aggregation You write it โŒ PaginationMiddleware
Log sanitization You write it โŒ Built-in
Prompt / context footprint Grows linearly with endpoints Grows with the whole spec Typed / Generic / Hybrid modes
Endpoint discovery for large APIs N/A Full spec in prompt search_operations / describe_operation
Swagger 2.0 ยท OpenAPI 3.0 ยท 3.1 You port it Model guesses Normalized by the adapter layer
Spec changes Manual diff Silent drift Reload โ†’ new tools
Testability You write mocks โŒ Deterministic, respx-friendly

When to reach for it

  • You have an API with more than three endpoints โ€” the boilerplate savings already pay for themselves.
  • You need production-grade networking (auth, retries, rate limits, caching, logging) without hand-rolling middleware.
  • You want the agent's tool schema to always match the spec โ€” no drift, no hallucinated parameters.
  • You are targeting a large surface (GitHub, Kubernetes, Stripe) and want a constant prompt footprint via the Generic or Hybrid toolkit.
  • You want the same integration to work across Swagger 2.0, OpenAPI 3.0, and OpenAPI 3.1 without a rewrite.

TL;DR

Do not paste the OpenAPI URL into the prompt. Do not write 200 @tool functions by hand. Point langchain-openapi at the spec and get type-safe, authenticated, resilient LangChain tools โ€” with your choice of one-tool-per-operation, a constant generic surface, or a hybrid of both.

Project details


Download files

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

Source Distribution

langchain_openapi_tools-2.0.1.tar.gz (1.0 MB view details)

Uploaded Source

Built Distribution

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

langchain_openapi_tools-2.0.1-py3-none-any.whl (54.3 kB view details)

Uploaded Python 3

File details

Details for the file langchain_openapi_tools-2.0.1.tar.gz.

File metadata

  • Download URL: langchain_openapi_tools-2.0.1.tar.gz
  • Upload date:
  • Size: 1.0 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for langchain_openapi_tools-2.0.1.tar.gz
Algorithm Hash digest
SHA256 ed5fab93f30ac1e0640a03a1d67a4054bc94a7a9afef8f046597dab7d550ca00
MD5 bc0e8362233bde8085c4668581cd4dca
BLAKE2b-256 2656760af1ba6dadc662662a78e2be8d7c33e9378b736a705f6b7053287d74f2

See more details on using hashes here.

Provenance

The following attestation bundles were made for langchain_openapi_tools-2.0.1.tar.gz:

Publisher: publish.yml on abhaywani114/langchain-openapi

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

File details

Details for the file langchain_openapi_tools-2.0.1-py3-none-any.whl.

File metadata

File hashes

Hashes for langchain_openapi_tools-2.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 5353257872b70066b54d45c4b774fd600d1e2ee3935fc94c1d1a5b9e46fdb299
MD5 598f4214a5d8adb42280c1aa13298311
BLAKE2b-256 720441c72945276ed473af7c541223216808f7e33406ba0890fafb2f59eccd61

See more details on using hashes here.

Provenance

The following attestation bundles were made for langchain_openapi_tools-2.0.1-py3-none-any.whl:

Publisher: publish.yml on abhaywani114/langchain-openapi

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

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page