langchain-sealkeeper
Check an AI agent or MCP server on SealKeeper from LangChain, before your agent calls it.
SealKeeper issues the SEAL, a signed credential. This package checks it on your machine with the sealkeeper library, and every decision is the library's Gate.
A SEAL comes in two kinds. An Agent SEAL is for an AI agent, such as a remote A2A agent. A Service SEAL is for a service, such as an MCP server.
Install
pip install langchain-sealkeeper
Python 3.10 or later. It depends on sealkeeper, langchain-core and langchain. The MCP example also needs langchain-mcp-adapters.
In each example, model is any LangChain chat model that calls tools.
To gate an MCP server or an A2A agent by its URL, copy the URL from its page on SealKeeper, under Use and verify. Only that exact URL finds it. A trailing slash or a query string finds nothing, and the gate blocks the call as not_found.
Look up
SealKeeperLookupTool lets the model look a target up.
from langchain.agents import create_agent
from langchain_sealkeeper import SealKeeperLookupTool
agent = create_agent(model, tools=[SealKeeperLookupTool()])
question = "Check aibenchmarkindex.com/ai-benchmark-index on SealKeeper."
result = agent.invoke({"messages": [{"role": "user", "content": question}]})
The tool is sealkeeper_lookup, with one argument, target. A target is a handle such as aibenchmarkindex.com/ai-benchmark-index, a SealKeeper page URL, an agent id, a did:key, an A2A card URL or an MCP server URL.
The model reads one line, such as aibenchmarkindex.com/ai-benchmark-index: verified agent SEAL, level bronze, Trust Score 200. or alice/helper: no verified SEAL (not_found).
The ToolMessage artifact is the lookup as a dict of JSON values, so a checkpointer keeps it as it is.
lookup = next(m for m in result["messages"] if m.type == "tool").artifact
if lookup["verified"]:
seal = lookup["seal"]
print(seal["kind"], seal["level"], seal["trust"])
else:
print(lookup["code"])
verified says whether the SEAL verified, and seal holds it when it did. Without one, code says why, as in the sealkeeper library's Lookup. A broken SEAL reads verified false and no level, kind or Trust Score.
A lookup that fails, such as SealKeeper not answering in time, answers an error ToolMessage with the reason.
Gate an MCP server
SealGateInterceptor checks an MCP server's SEAL before each call to one of its tools. The example is stoop-mcp-server, which holds a bronze Service SEAL.
import asyncio
from langchain.agents import create_agent
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_sealkeeper import SealGateInterceptor
connections = {
"stoop": {
"transport": "streamable_http",
"url": "https://stoop-mcp-server.stoop.workers.dev/mcp",
},
}
async def main():
gate = SealGateInterceptor(connections, ["stoop"])
client = MultiServerMCPClient(connections, tool_interceptors=[gate])
tools = await client.get_tools()
agent = create_agent(model, tools=tools)
result = await agent.ainvoke(
{"messages": [{"role": "user", "content": "Find a plumber in Miami."}]}
)
print(result["messages"][-1].content)
asyncio.run(main())
Pass it the same connections as the client. It reads each server's url from them at every call, as a service. Load the tools with client.get_tools(), or with load_mcp_tools(session, server_name=...) and the connection's name, since a tool loaded without its server's name is not gated.
- A pass calls the tool.
- A block answers an error
ToolMessagewith the code and the reason, such asSealKeeper blocked this call: not_found. SealKeeper knows no agent or service by that target. The tool is not called and the agent's run goes on. The message has noname, and outside a LangGraphToolNodeitstool_call_idis empty. - A server you do not name passes untouched.
- A stdio server has no URL to check, so naming one raises
ValueError.
Gate a remote agent
SealGateMiddleware checks a target's SEAL before each call to a tool that calls it. The example is AI Benchmark Index, an A2A agent that holds a bronze Agent SEAL. The tool sends an A2A message/send to the endpoint its card names in url.
import uuid
import httpx
from langchain.agents import create_agent
from langchain.tools import tool
from langchain_sealkeeper import SealGateMiddleware, SealTarget
CARD_URL = "https://aibenchmarkindex.com/.well-known/agent-card.json"
ENDPOINT = "https://aibenchmarkindex.com/a2a"
@tool
def ask_benchmark_index(question: str) -> str:
"""Ask AI Benchmark Index, a remote A2A agent, to find AI benchmarks."""
message = {
"kind": "message",
"messageId": str(uuid.uuid4()),
"role": "user",
"parts": [{"kind": "text", "text": question}],
}
request = {
"jsonrpc": "2.0",
"id": 1,
"method": "message/send",
"params": {"message": message},
}
answer = httpx.post(ENDPOINT, json=request, timeout=30)
return answer.text
gate = SealGateMiddleware({"ask_benchmark_index": SealTarget("agent", CARD_URL)})
agent = create_agent(model, tools=[ask_benchmark_index], middleware=[gate])
question = "List benchmarks in the Coding category."
result = agent.invoke({"messages": [{"role": "user", "content": question}]})
Each pinned tool name maps to a SealTarget(kind, ref). kind is "agent" or "service", and ref is any target the lookup takes. Pin it to the card URL, so the SEAL must name the card you read the endpoint from.
A block answers an error ToolMessage with the code and the reason, the tool is not called and the run goes on. A tool you do not pin passes untouched. It works with invoke and ainvoke.
The rules
Both gates decide with a sealkeeper.Gate and its default rules. A pass needs a current, valid SEAL of the kind at bronze or above, and for a card or MCP server URL the SEAL must name that URL.
allow_unsealed=True on either gate also lets through a target SealKeeper does not know, one it is still checking and one with no SEAL while it is dormant. A withheld or broken SEAL, a delisted target and SealKeeper unavailable still block.
gate = SealGateInterceptor(connections, ["stoop"], allow_unsealed=True)
The block codes are in the sealkeeper README. Each decision writes one INFO line to the sealkeeper logger, never the SEAL, a full URL or the tool's arguments.
The guide at sealkeeper.run/docs/langchain walks through each example.
License
MIT
Metadata
Release files for langchain-sealkeeper 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| langchain_sealkeeper-0.1.1.tar.gz | 13.9 kB | With provenance | Details for langchain_sealkeeper-0.1.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| langchain_sealkeeper-0.1.1-py3-none-any.whl | Python 3 | none | any | Details for langchain_sealkeeper-0.1.1-py3-none-any.whl |
Total release size: 23.8 kB
Release files / langchain_sealkeeper-0.1.1.tar.gz
| Download URL | langchain_sealkeeper-0.1.1.tar.gz |
|---|---|
| Size | 13.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4158a5dd1b8c5223a3dd14e484ed70afb6e94e9b96797dcb216e1fea4e6e9b58
|
|
BLAKE2b-256 checksum How to use checksums |
486b0771e155f4c4bd0f358ef340bebcd210ed99f988902d9679eaad0c35b1d9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.3 {"installer":{"name":"uv","version":"0.13.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.
Transparency logRelease files / langchain_sealkeeper-0.1.1-py3-none-any.whl
| Download URL | langchain_sealkeeper-0.1.1-py3-none-any.whl |
|---|---|
| Size | 9.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5c14ecd1337e40aaa84d443a6ef38442dfdbfe656a0eee2ca74c02128485592f
|
|
BLAKE2b-256 checksum How to use checksums |
66b3739c6cbdcb36615e568643929cf39d90a4f809a5d7e71d38774726bf1310
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.3 {"installer":{"name":"uv","version":"0.13.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.
Transparency logSecurity for langchain-sealkeeper 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for langchain_sealkeeper-0.1.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.