Skip to main content

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 ToolMessage with the code and the reason, such as SealKeeper 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 no name, and outside a LangGraph ToolNode its tool_call_id is 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)

Source distribution for langchain-sealkeeper 0.1.1
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)

Table of built distributions (wheels) for langchain-sealkeeper 0.1.1
File Interpreter ABI Platform Actions
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 log

Release 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 log

Security 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.

Select from 2 files

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 log

A 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.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page