Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Framework Adapters

Use adapters when the model already runs inside your Python app and you want provenance at the framework boundary, not a separate MCP server.

Choose The Adapter

If you need...APIStart here
Signed LangChain tool resultsjacs_signing_middleware, signed_toolLangChain / LangGraph section below
Signed LangGraph ToolNode outputsjacs_wrap_tool_call, with_jacs_signingLangChain / LangGraph section below
Signed FastAPI responses and verified inbound requestsJacsMiddleware, jacs_routeFastAPI section below
Signed Anthropic tool return valuesjacs.adapters.anthropic.signed_toolAnthropic section below

Install only the extra you need:

pip install jacs[langchain]
pip install jacs[fastapi]
pip install jacs[anthropic]

Optional: jacs[langgraph] (LangGraph ToolNode), jacs[ws] (WebSockets). See pyproject.toml for the full list.

LangChain / LangGraph

This is the smallest JACS path if your model already lives in LangChain.

LangChain middleware

from langchain.agents import create_agent
from jacs.client import JacsClient
from jacs.adapters.langchain import jacs_signing_middleware

client = JacsClient.quickstart(name="langchain-agent", domain="langchain.local")

agent = create_agent(
    model="openai:gpt-4o",
    tools=[search_tool, calc_tool],
    middleware=[jacs_signing_middleware(client=client)],
)

LangGraph ToolNode

from jacs.adapters.langchain import with_jacs_signing

tool_node = with_jacs_signing([search_tool, calc_tool], client=client)

Wrap one tool instead of the whole graph

from jacs.adapters.langchain import signed_tool

signed_search = signed_tool(search_tool, client=client)

The executable example to start from in this repo is jacspy/examples/langchain/signing_callback.py.

FastAPI / Starlette

Use this when the trust boundary is an API route instead of an MCP transport.

from fastapi import FastAPI
from jacs.client import JacsClient
from jacs.adapters.fastapi import JacsMiddleware

client = JacsClient.quickstart(name="api-agent", domain="api.local")
app = FastAPI()
app.add_middleware(JacsMiddleware, client=client)

Useful options:

  • Signing and verification failures fail closed by default
  • sign_responses=False or verify_requests=False to narrow the behavior
  • a2a=True to also expose A2A discovery routes from the same FastAPI app

Legacy compatibility options are explicit and dangerous:

  • allow_unsigned_output=True permits raw output after signing fails
  • allow_unverified_passthrough=True permits unverifiable input to continue
  • allow_plain_signature_fallback=True permits an attestation request to downgrade to a plain signature, dropping attestation claims

strict=True always disables all three fallbacks. Do not use these options at a trust boundary unless the caller separately marks the data as unsigned or unverified.

For auth-style endpoints, replay protection is available:

app.add_middleware(
    JacsMiddleware,
    client=client,
    strict=True,
    auth_replay_protection=True,
    auth_max_age_seconds=30,
    auth_clock_skew_seconds=5,
)

To sign only one route:

from jacs.adapters.fastapi import jacs_route

@app.get("/signed")
@jacs_route(client=client)
async def signed_endpoint():
    return {"ok": True}

Anthropic / Claude SDK

Use the Anthropic adapter when you want signed return values from normal Python tool functions:

from jacs.adapters.anthropic import signed_tool

@signed_tool(client=client)
def get_weather(location: str) -> str:
    return f"Weather in {location}: sunny"

When To Use MCP Instead

Choose Python MCP Integration instead of adapters when:

  • the model is outside your process and talks over MCP
  • you want an MCP tool suite for JACS operations
  • you need the same server to be usable by external MCP clients