Watchlight MCP Runtime PEP
A Policy Enforcement Point for the Model Context Protocol. It sits in front of an upstream MCP server and makes one guarantee:
No governed MCP action reaches the target system until Watchlight has made a deterministic runtime authorization decision — and only an explicit PERMIT forwards it.
Every tools/call is authorized in-process by the real Watchlight engine
before it can reach the upstream server. A denied call is blocked before it
executes — not detected after. Zero infrastructure: no database, no network
hop, no sidecar.
Install
pip install watchlight-mcp
Run
import watchlight_mcp
watchlight_mcp.serve(
listen_addr="127.0.0.1:9700",
upstream_url="http://localhost:3000/mcp", # the MCP server you are governing
upstream_server="github", # a stable name for it (used in policy + audit)
policy_files=["policies/allow-read.json"], # Cedar policies, wl-apdp shape
audit_path="audit/mcp-pep.jsonl", # value-free JSONL audit (omit → logs)
)
Point your MCP client at http://127.0.0.1:9700/mcp instead of the server
directly, and have it send Watchlight-Agent-Id: <agent> on every request.
Governed tools/calls are authorized (one without that header is refused);
everything else is forwarded transparently.
What it enforces
- MCP
2026-07-28Streamable HTTP: the JSON-RPC body is authoritative, and theMcp-Method/Mcp-Name/MCP-Protocol-Versionrouting headers are validated against it — a spoofed header cannot route a governed call around enforcement. - Fail-closed: a deny, a runtime error, a malformed body, a header/body mismatch, or an unsupported protocol version all block the action. The upstream is contacted only on an explicit permit.
- Value-free audit and logs: the audit record has no field for tool
arguments, tokens, or credentials — only the decision and safe correlation ids.
The process log is value-free at every level too: tool name, decision and ids,
never an argument value. A resource URI is recorded as its scheme and host
plus a SHA-256 prefix of the path (host-less URIs such as
data:ormailto:as the scheme plus a hash).
Identity
The PEP reads client-asserted Watchlight-* headers to build the authorization
principal and execution context — the Developer-Edition trust model. The
principal comes from exactly one header:
Watchlight-Agent-Id(default) →Agent::"<id>". A request that also (or instead) carriesWatchlight-Principal-Idis refused.Watchlight-Principal-Id, only when you opt in withserve(..., principal_header="Watchlight-Principal-Id")→ a Cedar entity ref such asUser::"alice". A request carryingWatchlight-Agent-Idis refused.
Watchlight-Execution-Id, Watchlight-Parent-Execution-Id,
Watchlight-Session-Id and Watchlight-Task-Id are read as execution context.
A request that repeats any of these headers, or sends an empty or malformed one,
is refused with JSON-RPC error -32002 (HTTP 400) and never forwarded. So is an
identity value that contains ,, is a malformed Type::"id", or names the
reserved unattributed principal.
Identity is mandatory. A governed call (tools/call, resources/read,
resources/subscribe, prompts/get) without an identity header is refused
with -32002; initialize, ping, tools/list and other pass-through methods
need none. To decide such calls as the reserved Agent::"unattributed"
principal instead (the 0.4.3 behaviour), pass allow_unattributed=True to
serve / serve_background. serve_stdio refuses to start without
agent_id or principal_id unless allow_unattributed=True.
Anyone who can reach the listener can assert these headers. Bind to loopback, or put a gateway in front that strips or overwrites all of them.
Responses
| Outcome | JSON-RPC error | HTTP status |
|---|---|---|
| Permit | none (the server's response is relayed) | the server's status |
| Denied by policy, or a fail-closed engine error | -32001 not authorized |
200 |
| Identity header refused, or no identity on a governed call | -32002 |
400 |
Mcp-Method / Mcp-Name disagrees with the body |
-32020 |
400 |
Unsupported MCP-Protocol-Version |
-32022 |
400 |
| Malformed or ambiguous body, missing tool name | -32602 |
400 |
| Upstream server unreachable | -32603 |
502 |
© Watchlight AI. The embedded runtime is proprietary; see LICENSE.
Metadata
Release files for watchlight-mcp 0.4.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| watchlight_mcp-0.4.4-cp39-abi3-manylinux_2_28_aarch64.whl | CPython 3.9 | abi3 | Linux glibc 2.28+ ARM64 | Details |
| watchlight_mcp-0.4.4-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | CPython 3.9 | abi3 | Linux glibc 2.17+ x86-64 | Details |
| watchlight_mcp-0.4.4-cp39-abi3-macosx_11_0_arm64.whl | CPython 3.9 | abi3 | macOS 11.0+ ARM64 | Details |
| watchlight_mcp-0.4.4-cp39-abi3-macosx_10_12_x86_64.whl | CPython 3.9 | abi3 | macOS 10.12+ x86-64 | Details |
Total release size: 22.8 MB
Release files / watchlight_mcp-0.4.4-cp39-abi3-manylinux_2_28_aarch64.whl
| Download URL | watchlight_mcp-0.4.4-cp39-abi3-manylinux_2_28_aarch64.whl |
|---|---|
| Size | 5.6 MB |
| Tags | CPython 3.9 Linux glibc 2.28+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
20c9931cbc7ceb5085e0217932d0e9bdc2f00e86d6be1b675e1d661b293cf91b
|
|
BLAKE2b-256 checksum How to use checksums |
bad51fc75764d8829c7803b0f42318ea7da1ae74d535407bfac7133a0f58ad2a
|
| 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 Oct 2, 2026.
Transparency logRelease files / watchlight_mcp-0.4.4-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | watchlight_mcp-0.4.4-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 6.0 MB |
| Tags | CPython 3.9 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
b4ec3abcd7690cb640c276a22c2385216147d6709d1df1988b3aa589fcb646ab
|
|
BLAKE2b-256 checksum How to use checksums |
d0a83fd6c61b69f02b8799b91d4e2c624ead165d50b74d32aa61106ff63280f6
|
| 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 Oct 2, 2026.
Transparency logRelease files / watchlight_mcp-0.4.4-cp39-abi3-macosx_11_0_arm64.whl
| Download URL | watchlight_mcp-0.4.4-cp39-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 5.5 MB |
| Tags | CPython 3.9 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
002914992ebe6277b854703353dfd19a7f43287f4e499da18c2287e725d9d1b5
|
|
BLAKE2b-256 checksum How to use checksums |
334ca0b7aeba01d5601335ab26d2c07c059898ba1d28914ac49cdefcb3d5e62e
|
| 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 Oct 2, 2026.
Transparency logRelease files / watchlight_mcp-0.4.4-cp39-abi3-macosx_10_12_x86_64.whl
| Download URL | watchlight_mcp-0.4.4-cp39-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 5.8 MB |
| Tags | CPython 3.9 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
f23e7b90cd27b73f7ed65cb6fc8dae227634788b63bc2982c4ba55cfd029f8c2
|
|
BLAKE2b-256 checksum How to use checksums |
b99b5b28effd7ecd21bb2ffef414d660c0c7e4fe275b1ce4315eadcba929d1c4
|
| 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 Oct 2, 2026.
Transparency log