carnet-mcp
The Carnet door, as a Python client. Three lines from an agent framework to a tool list that is scoped to the token, called under a credential the agent never holds, and audited per call.
pip install "carnet-mcp[langchain]" # or [crewai], [autogen], [openai-agents]
from carnet_mcp.langchain import tools
tools = tools("https://carnet.example.com/api/mcp", token) # the URL on the connect card
agent = create_agent(model, tools)
The same for the others: carnet_mcp.crewai.tools(url, token) returns CrewAI tools;
await carnet_mcp.openai_agents.tools(url, token) returns FunctionTools for the OpenAI
Agents SDK; await carnet_mcp.autogen.tools(url, token) returns AutoGen tools. Each
framework is an extra and is imported only by its own module. The bare install pulls
httpx and nothing else.
token is a Carnet access token — a service token granted one agent is the right shape
for a process: the agent's scope is the process's reach. Nothing else is configured here
and nothing is written to disk.
What you get, and why it is shaped this way
- The door's sentence, relayed. A call the token is not allowed comes back to the
model on the framework's tool-error channel, in the door's own words, with the audit
row's id appended — denied: 'OTHER' is outside this agent's 'read' scope. Allowed:
ACME (carnet call door-…) — and never as an exception that ends the agent's turn. The
model can say so, which is the outcome least privilege exists to produce. A door that
did not answer at all raises
DoorUnavailable, because that is not something a model adapts to. - One host. This package makes HTTP requests to exactly one URL: the one you constructed it with. No telemetry, no version check, no anonymous anything. The test suite reads the source to assert there is no other address in it.
- Fetched once, enforced always. The tool list is read when the tools are built and cached for the life of the object, because that is what every framework's API does. A grant revoked while an agent is up may leave the tool in its list; the door refuses the call, uncached, as it always did, and the refusal is on the audit trail. Visibility goes stale; enforcement does not.
- No retries. A retried
tools/callis a second brokered call with a second audit row and possibly a second side effect.httpx's defaults and nothing more.
Under the adapters
carnet_mcp.Door and carnet_mcp.AsyncDoor are the protocol itself, for a framework
not listed here or for code that is not a framework at all:
from carnet_mcp import Door
with Door(url, token) as door:
for tool in door.tools(): # name, description, input_schema
...
result = door.call("jira_search_issues", {"project": "ACME"})
result.text # what the tool said
result.is_error # a brokered denial or a failed tool
result.sentence # the door's reason, with the call id
result.call_id # `door-<hex>`, the row on the door-calls page
Both take timeout= or your own httpx client (a proxy, a private CA, a pool).
DoorRefused is raised for a token the door does not accept and for a tool the token
is not granted; DoorUnavailable when the door did not answer — including a wrong
address (a 404, or a redirect, which this client does not follow) with a sentence saying
to check the /mcp URL. A pasted token is trimmed, and a whole Bearer … header pasted
as the token loses its prefix rather than being sent twice. An AsyncDoor that owns its
client survives a sync agent's asyncio.run per call; one given a borrowed client says
so if it is moved to another loop.
Compatibility
This client speaks to any Carnet door from 0.11.0 onward. It is versioned separately from the server on purpose — a client must talk to a range of self-hosted doors — and a door speaking a protocol revision this client does not know produces one warning at the handshake, not a refusal.
Verified, and not yet
All four adapters have been run against a real door: the tool list, a call in scope
under the broker's credential, and a call outside scope returned to the framework as the
door's sentence with the call id. LangChain further has an end-to-end script
(backend/scripts/e2e_client_langchain.py) that drives its tools through LangChain's own
invoke and ainvoke, reads the denial as a ToolMessage with an error status, and
revokes the grant mid-session to watch the next call refused by name. What has not been
run here is an agent with a real model completing a task through any of the four; the
script has a flag for that half and says so.
Apache-2.0. Issues, the source and the reasoning behind every decision here are in the Carnet repository; the reasoning lives beside the code it explains, so the module docstrings are the design record.
Metadata
Release files for carnet-mcp 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| carnet_mcp-0.1.0.tar.gz | 30.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| carnet_mcp-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 56.4 kB
Release files / carnet_mcp-0.1.0.tar.gz
| Download URL | carnet_mcp-0.1.0.tar.gz |
|---|---|
| Size | 30.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0a19c70aa03d0b5bd0ab0117cc9a5bee43ad6b22ddc93e9617360a5181bea905
|
|
BLAKE2b-256 checksum How to use checksums |
8832b47a1c8cbd34e6d1d926e7143b8f0c9daa206a423d053420a6163c86ff32
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Sep 20, 2026.
Transparency logRelease files / carnet_mcp-0.1.0-py3-none-any.whl
| Download URL | carnet_mcp-0.1.0-py3-none-any.whl |
|---|---|
| Size | 25.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b88f10a6ba98c32ccb293739fd71ba59a9d31621cd3caec6cfc5391856279b16
|
|
BLAKE2b-256 checksum How to use checksums |
71f9bee034f716e6634baed43cec0a2368878e2edb387e382a81513421145315
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Sep 20, 2026.
Transparency log