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... | API | Start here |
|---|---|---|
| Signed LangChain tool results | jacs_signing_middleware, signed_tool | LangChain / LangGraph section below |
Signed LangGraph ToolNode outputs | jacs_wrap_tool_call, with_jacs_signing | LangChain / LangGraph section below |
| Signed FastAPI responses and verified inbound requests | JacsMiddleware, jacs_route | FastAPI section below |
| Signed Anthropic tool return values | jacs.adapters.anthropic.signed_tool | Anthropic 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=Falseorverify_requests=Falseto narrow the behaviora2a=Trueto also expose A2A discovery routes from the same FastAPI app
Legacy compatibility options are explicit and dangerous:
allow_unsigned_output=Truepermits raw output after signing failsallow_unverified_passthrough=Truepermits unverifiable input to continueallow_plain_signature_fallback=Truepermits 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