How to Connect MAREF to Your Agent via MCP — Step-by-Step
By MAREF Engineering
Model Context Protocol (MCP) has become the universal interface for agent-to-tool communication. Claude Code, Cursor, and Windsurf all speak it. The question isn't whether your agent will use MCP — it's whether anyone is watching the tools it calls. MAREF speaks MCP on both sides of that conversation, so governance is a layer, not a bolt-on.
This tutorial is written against the real MAREF API (maref.integration.mcp_client and maref.integration.mcp_bridge). Everything below runs on a stock pip install maref.
Two roles, one protocol
MAREF works in two directions over MCP, and understanding which one you need is the whole setup:
- MAREF as MCP client — MAREF reaches out to external MCP servers (file, shell, browser, email, or a third-party tool server), lists their tools, and runs every invocation through its security gate before the call goes through.
- MAREF as MCP server — Claude Code / Cursor / Windsurf connect to MAREF as a tool server. Every tool the agent would call becomes a governed tool in MAREF's registry.
Most teams start with the first and graduate to the second. Both are covered below.
1. MAREF as an MCP client — governing external tools
The entry point is MCPClient. You register an external server with an MCPServerConfig, and MAREF manages the connection lifecycle — initialize, capability negotiation, reconnects — for you:
from maref.integration.mcp_client import MCPClient, MCPServerConfig
client = MCPClient()
config = MCPServerConfig(
command=["npx", "-y", "@some/tool-server"],
transport_type="stdio", # or "sse" with url=
server_name="my-tool-server",
env={"TOOL_API_KEY": "..."},
)
conn = client.register_server(config) # returns an MCPConnection
tools = client.list_tools(conn) # list[MCPToolDef]
Now the interesting part. A raw MCPClient.call_tool skips governance. The safe path is MCPBridge, which wraps every call in the security gate:
from maref.integration.mcp_bridge import MCPBridge
bridge = MCPBridge(client) # optional: pass your own MCPSecurityGate
# watch governance events
bridge.on("maref.mcp.invoke", lambda e: print("governed:", e.data))
bridge.discover_tools(conn) # security-check each tool once
result = bridge.invoke_tool(
conn,
tool_name="create_file",
args={"path": "/tmp/demo.txt", "content": "hello"},
)
# if the security gate returns DENY, invoke_tool returns
# {"error": "Tool blocked by security gate", "tool": ...} — the
# external server is never even contacted.
That one line — bridge.invoke_tool — is the difference between "an agent that can call any tool" and "an agent that can call tools its policy allows." Every invocation emits a maref.mcp.invoke event you can route to your audit log, SIEM, or dashboards.
2. MAREF as an MCP server — governing Claude Code / Cursor
If your agent host already speaks MCP, expose MAREF's own tool registry as an MCP server. The MCPServerAdapter bridges MAREF's ToolRegistry to the MCP wire protocol — list_tools and handle_tool_call are the two methods the protocol needs:
from maref.mcp.router import MCPServerAdapter
from maref.tools import ToolRegistry
registry = ToolRegistry() # your governed tools live here
adapter = MCPServerAdapter(registry)
# MCP JSON-RPC requests come in, governed responses go out
adapter.handle_tool_call("send_email", {"to": "[email protected]"})
In practice you usually mount this behind the full MCPServer implementation (maref.integration.mcp_server), which gives you resources, prompts, and sampling callbacks on top of tools. MAREF doesn't ship a built-in maref mcp serve CLI command — the stdio entrypoint is a ~15-line launcher wired straight to the real MCPServer API:
import json, sys
from maref.integration.mcp_transport import JSONRPCRequest
from maref.integration.mcp_server import MCPServer
server = MCPServer(name="maref-mcp-server", security_gate=gate) # gate: your security gate
# ... server.register_tool(...) register your governed tools ...
for line in sys.stdin: # newline-delimited JSON-RPC 2.0
msg = json.loads(line)
req = JSONRPCRequest(method=msg["method"], params=msg.get("params"), id=msg.get("id", 0))
resp = server.handle_request(req)
sys.stdout.write(json.dumps({"jsonrpc": resp.jsonrpc, "result": resp.result, "error": resp.error, "id": resp.id}, ensure_ascii=False) + "
")
sys.stdout.flush() {
"mcpServers": {
"maref": {
"command": "python3",
"args": ["/path/to/mcp_stdio.py"]
}
}
} From that point on, when Claude Code or Cursor calls any tool, the call passes through MAREF's governance state machine — policy decision tree, safety gates, and audit trail — before it touches the world.
3. What governance actually blocks
Governance isn't a suggestion. It's a decision, and it's made four ways in the policy tree — Rule → Mode → SafetyGate → User:
- A hard rule (never touch
/etc) blocks instantly, no model consultation. - The current mode (read-only, triage, full) narrows what's permitted.
- The safety gate catches risky operations — high blast radius, untrusted targets, anomalous patterns.
- Human escalation fires for the genuinely dangerous cases, with a named approver and an audit line.
And because every decision is signed per-agent (Ed25519) and written to the audit log, "which agent did this?" is never a debate.
Try it now
The fastest way to see the loop working is the local demo — it boots a governed toy agent and a live dashboard so you can watch BLOCK/ALLOW decisions stream in:
pip install maref
maref demo --port 8080
# open http://localhost:8080 — the dashboard shows the 8-layer
# defense pipeline, trust scores, and the audit log, all live. 🛡️ Sources: MAREF source — src/maref/integration/mcp_client.py (MCPClient, MCPServerConfig, register_server), src/maref/integration/mcp_bridge.py (MCPBridge.discover_tools / invoke_tool), src/maref/integration/mcp_server.py (MCPServer), src/maref/mcp/router.py (MCPServerAdapter). See all integration options.