How to Connect MAREF to Your Agent via MCP — Step-by-Step

By MAREF Engineering

MCP tutorial Model Context Protocol integration agent governance

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:

Connect to an external MCP server
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:

Every tool call goes through 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:

Expose MAREF's registry as an MCP server
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:

mcp_stdio.py — MAREF stdio launcher
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()
Claude Code configuration — point it at the launcher
{
  "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:

Local governed demo
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.