Enterprise governance middleware for MCP servers: cost attribution, auth, observability, quotas, audit, and policy — composable around the official MCP Python SDK.
Project description
mcp-harness
Enterprise governance middleware for MCP servers. Cost attribution, auth, observability, quotas, audit, and policy — composable around the official MCP Python SDK.
Why this exists
Most public MCP servers are toy examples. The gap between "works on my laptop" and "I can ship this to 500 engineers under SOC 2, GDPR, and a Finance team that wants per-business-unit cost allocation" is enormous — and almost none of that gap is in the protocol. It's in the boring middleware around it.
mcp-harness is that middleware. It doesn't fork the protocol, doesn't reinvent the SDK, and
doesn't try to be an agent framework. It's a set of composable decorators and middleware that wrap
the official SDK so the resulting server can be safely run inside a company.
The headline use case: your CFO asks who's spending the AI budget, and you can answer by end of week one.
Install
pip install mcp-harness # core pipeline (no MCP SDK required)
pip install 'mcp-harness[server]' # + the official MCP SDK, to actually serve tools
pip install 'mcp-harness[all]' # + OpenTelemetry, Prometheus, tiktoken, JWT
The core middleware pipeline has no hard dependency on mcp, so you can unit-test all your
governance behaviour without a transport. Install the server extra to run a live server.
Hello, governed world
The same MCP server you'd write anyway, with the governance layer declared once at the top:
from mcp_harness import Harness
from mcp_harness.auth import APIKeyAuth
from mcp_harness.governance import CostTracking, Quotas, AuditLog
from mcp_harness.observability import OTELTracing, StructuredLogging
from mcp_harness.policy import AllowList
harness = Harness(
name="customer-data-mcp",
auth=APIKeyAuth(keys={"sk-finance": {"id": "svc-reports", "team": "finance"}}),
middleware=[
OTELTracing(service_name="customer-data-mcp"),
StructuredLogging(),
AllowList.from_yaml("policies/tool-access.yaml"),
Quotas(per_principal_per_minute=60),
CostTracking(
cost_center_resolver=lambda p: p.team,
sink="jsonl://costs.jsonl",
),
AuditLog(sink="jsonl://audit.jsonl"),
],
)
@harness.tool()
async def search_customer(customer_id: str) -> dict:
"""Find a customer record by ID."""
return {"id": customer_id, "name": "ACME Corp"}
if __name__ == "__main__":
harness.run() # stdio by default; transport="streamable-http" for HTTP
Then attribute spend:
$ mcp-harness daily-rollup costs.jsonl
cost_center calls in_tok out_tok cost_usd
----------------------------------------------------------
finance 42 5\,210 9\,830 0.1284
platform 18 1\,940 3\,110 0.0451
----------------------------------------------------------
TOTAL 60 7\,150 12\,940 0.1735
How it works
A tiny onion middleware pipeline, decoupled from the SDK. @harness.tool() registers your
function; calls flow through each layer and into your tool. The same path runs whether the call
arrives from a live MCP client or from the in-process test client.
client ─▶ FastMCP ─▶ wrapper ─┐
├─▶ auth ─▶ policy ─▶ quotas ─▶ tracing ─▶ cost ─▶ audit ─▶ your tool
test/direct ─▶ dispatch ──────┘
Each layer is independently useful, independently testable, and opt-in. Adopt just CostTracking,
or stack the whole thing.
Modules
| Module | What you get |
|---|---|
mcp_harness.auth |
APIKeyAuth (with rotation), AnonymousAuth, ChainedAuth, experimental AzureADAuth (Entra ID JWT) |
mcp_harness.governance |
CostTracking (tokens → $ → cost center), Quotas (rate / team cap / concurrency), AuditLog (async, shape-only) |
mcp_harness.observability |
StructuredLogging (JSON + correlation ids), OTELTracing, Metrics (Prometheus or in-memory) |
mcp_harness.policy |
AllowList / DenyList (YAML, argument constraints), PIIRedactor |
mcp_harness.resilience |
CircuitBreaker, Retry decorators for individual tools |
mcp_harness.testing |
HarnessTestClient, MockPrincipal, pytest fixtures |
mcp-harness CLI |
daily-rollup per-cost-center spend reports |
Optional integrations degrade gracefully: OTELTracing is a no-op (warned once) without the
otel extra; Metrics falls back to an in-memory backend without prometheus-client; cost token
counting uses a heuristic without tiktoken.
Testing your server
Governance is covered by ordinary unit tests — no transport, no mocks of the SDK:
from mcp_harness.testing import HarnessTestClient, MockPrincipal
from myserver import harness
async def test_finance_can_search(harness_client): # fixture auto-registered
client = harness_client(harness, principal=MockPrincipal("svc-a", team="finance"))
assert await client.call("search_customer", {"customer_id": "c-1"})
Examples
examples/quickstart_server.py— smallest governed server.examples/local_governed_server.py— full stack, zero cloud deps. Runpython examples/local_governed_server.py --demoto watch the governance layers reject calls in real time.examples/azure_governed_server.py— the design's Azure-centric reference server.
Scope
In scope: auth, observability, cost attribution, quotas, audit, policy, resilience, testing.
Out of scope (by design): agent orchestration, a UI/dashboard, a tool registry, and anything that breaks MCP wire-compatibility. A library that does a few things well beats one that does fifteen poorly.
Compatibility & status
- Python 3.10+. Wraps the official
mcpSDK (FastMCP) without modifying the wire protocol. - Beta (v0.1). The
Harness, auth,CostTracking,Quotas, observability,AllowList, andAuditLogAPIs are real and tested. Cloud sinks (Azure Monitor, Event Hubs, Kinesis, Kafka), Redis-backed quotas,SchemaGuard, andAzureADAuthhardening are extension points — base interfaces ship, full wiring is on the roadmap. See CHANGELOG.md.
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mcp_harness-0.1.0.tar.gz.
File metadata
- Download URL: mcp_harness-0.1.0.tar.gz
- Upload date:
- Size: 49.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
088476362ef6103781df06dfd01c2432d415542f70aa06e95a82512fd6a648c9
|
|
| MD5 |
fd2c0f23bc6e3902d3ec58e4d24916f9
|
|
| BLAKE2b-256 |
e29dfed096942df44ee81938186646ab008f701b530619f1172c1ca23856e3e5
|
Provenance
The following attestation bundles were made for mcp_harness-0.1.0.tar.gz:
Publisher:
release.yml on nagenshukla/mcp-harness
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcp_harness-0.1.0.tar.gz -
Subject digest:
088476362ef6103781df06dfd01c2432d415542f70aa06e95a82512fd6a648c9 - Sigstore transparency entry: 1986062888
- Sigstore integration time:
-
Permalink:
nagenshukla/mcp-harness@1e62d627b6873fc211167ccf0b5a13c18c8d56aa -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/nagenshukla
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1e62d627b6873fc211167ccf0b5a13c18c8d56aa -
Trigger Event:
push
-
Statement type:
File details
Details for the file mcp_harness-0.1.0-py3-none-any.whl.
File metadata
- Download URL: mcp_harness-0.1.0-py3-none-any.whl
- Upload date:
- Size: 54.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d4ba9dd964e1d67370c261c78f58a1c4dc506d2d25a89475f21a4da20bba2ce7
|
|
| MD5 |
1e4aa2d9741c691a8be9d0fe15320ce6
|
|
| BLAKE2b-256 |
4e2f627a15d3910d9520a55599e8b4fc1159010f0abcde4f3fa6c8ff3ddc1df3
|
Provenance
The following attestation bundles were made for mcp_harness-0.1.0-py3-none-any.whl:
Publisher:
release.yml on nagenshukla/mcp-harness
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcp_harness-0.1.0-py3-none-any.whl -
Subject digest:
d4ba9dd964e1d67370c261c78f58a1c4dc506d2d25a89475f21a4da20bba2ce7 - Sigstore transparency entry: 1986063000
- Sigstore integration time:
-
Permalink:
nagenshukla/mcp-harness@1e62d627b6873fc211167ccf0b5a13c18c8d56aa -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/nagenshukla
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1e62d627b6873fc211167ccf0b5a13c18c8d56aa -
Trigger Event:
push
-
Statement type: