MCP¶
jdlib.integrations.mcp exposes the same chain to a model-facing client: tools and resources instead
of routes, with the same authentication, the same tenant rule, the same policy decision and the same
audit vocabulary.
The parts¶
| Module | What it does |
|---|---|
server |
builds the FastMCP server from the composed collaborators |
boundary |
the request boundary: credential, context, tenant, the audit events a call produces |
invocation |
binds a call to the context and runs the tool body behind the PEP |
tools |
the tool registry and the declared input models |
graph_tools |
the graph tools, over the closed query vocabulary |
resources |
resource URIs and their resolution |
errors |
the refusals, in the library's vocabulary |
What it refuses¶
- A tool call without an allowed decision. The PEP runs first; the tool body is not entered, and the refusal is an MCP result rather than an exception the client cannot read.
- Arguments that do not match the declared schema. The schema a tool advertises is the schema the library enforces — the enterprise example asserts the two are the same, because an advertised schema that is not enforced is a promise the surface does not keep.
- An unregistered resource URI. A refusal, not a crash.
- A free-text graph search.
graph_searchis deliberately absent: the graph surface exposes the closed vocabulary, and a free-text search is a statement that vocabulary cannot express. The absence is recorded as a decision, not an omission.
What a denial leaves behind¶
A refused tool call writes three audit events — the call, the decision and the outcome — where the HTTP path writes one. The difference is real and the enterprise example asserts it, so an incident can reconstruct an MCP denial from the audit plane alone.
Where it is tested¶
tests/unit/test_mcp_*.py (the registry, the boundary, invocation, resources, reliability,
observability, the adversarial cases) and examples/enterprise/tests/ for the composed surface
against a live tenant. The feature guide is the how-to.