Supported library reference. Rust, Node.js, Python and Go remain supported in a separate workspace. This chapter can contain historical version or installation text; use the current package guide. Extended CLI examples use
jacs-compatin 0.15. See the current MCP guide for actual runtime profiles and the release inventory for published versions.
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