| title | Cursor + fak: governed local-model integration |
|---|---|
| description | Wire fak as a tool-governance layer for Cursor AI agents via MCP or an OpenAI-compatible proxy, adding capability-floor enforcement and quarantine. |
This guide shows how to use fak as a tool-governance layer for Cursor AI agents, adding capability-floor enforcement and quarantine protection to Cursor's coding workflows.
fak is the Fused Agent Kernel: a single Go binary that sits between an AI agent and its tools, enforcing a capability floor (which tools may be called) and quarantine (whether tool results can enter the agent's context). Cursor is an AI-native IDE that can integrate with external tools through two primary mechanisms:
- MCP (Model Context Protocol) - Native protocol for tool and data source integration
- OpenAI-compatible HTTP proxy - Cursor can be configured to use custom OpenAI-compatible endpoints
This guide covers both integration approaches.
# From the repo (the Go module is the repo root)
git clone https://github.com/anthony-chaudhary/fak && cd fak
go build -o fak ./cmd/fak
# Or via the installer
curl -fsSL https://raw.githubusercontent.com/anthony-chaudhary/fak/main/install.sh | shVerify installation:
./fak versionDownload from cursor.com and install following the official setup guide.
Cursor can connect to fak in two modes:
- Proxy mode:
fakforwards to an external model (OpenAI, Anthropic, Ollama, vLLM, etc.) - In-kernel mode:
fakserves its own fused SmolLM2-135M or Qwen model
For development, proxy mode is recommended as it gives you full model capabilities while still enforcing tool governance.
Cursor has native MCP support, making this the cleanest integration path. fak exposes its syscall boundary as an MCP server with two primary tools:
| Tool | Purpose |
|---|---|
fak_adjudicate |
Get a verdict for a tool call without executing it (client-side execution) |
fak_syscall |
Full path: adjudicate + execute through fak's kernel (self-contained) |
fak_admit |
Check whether a tool result should be admitted into context (quarantine gate) |
fak_changes |
Subscribe to cross-agent coherence events (what other agents changed) |
fak_revoke |
Trigger fleet-wide refutation of a poisoned witness |
# Start in stdio MCP mode (for Cursor)
./fak serve --stdio \
--base-url http://localhost:11434/v1 \ # Your upstream model
--model qwen2.5:1.5b \
--policy examples/customer-support-readonly-policy.jsonOr use an upstream provider:
./fak serve --stdio \
--provider openai \
--base-url https://api.openai.com/v1 \
--model gpt-4o \
--api-key-env OPENAI_API_KEY \
--policy examples/dev-agent-policy.json- Open Cursor Settings (Cmd/Ctrl + ,)
- Navigate to MCP Servers
- Add a new MCP server configuration:
Point command at the fak binary you built (an absolute path, or fak if it's on
your PATH) and --policy at a shipped example to start (then adapt):
{
"mcpServers": {
"fak": {
"command": "./fak",
"args": [
"serve",
"--stdio",
"--base-url", "http://localhost:11434/v1",
"--model", "qwen2.5:1.5b",
"--policy", "examples/customer-support-readonly-policy.json"
],
"env": {
"OPENAI_API_KEY": "your-key-here"
}
}
}
}Once configured, Cursor can invoke fak tools:
@fak please adjudicate a call to "delete_account" with args {"user_id":"123"}
Or via the Cursor chat interface:
Use fak_syscall to execute "read_customer_record" for user "alice@example.com"
fak_adjudicate request:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "fak_adjudicate",
"arguments": {
"tool": "delete_account",
"args": "{\"user_id\":\"123\"}"
}
}
}Response (DENY):
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"verdict": "DENY",
"reason": "POLICY_BLOCK",
"by": "adjudicator"
}
}Cursor can use custom OpenAI-compatible endpoints. fak provides an OpenAI-compatible /v1/chat/completions endpoint that:
- Receives the model's proposed tool calls
- Adjudicates each call through the capability floor
- Executes allowed calls (or returns them for client-side execution)
- Quarantines suspicious results
- Returns the filtered results to the model
# Start the OpenAI-compatible proxy
./fak serve \
--addr 127.0.0.1:8080 \
--base-url http://localhost:11434/v1 \
--model qwen2.5:1.5b \
--policy examples/customer-support-readonly-policy.json \
--vdso=trueVerify health:
curl http://127.0.0.1:8080/healthz
curl http://127.0.0.1:8080/metrics # Prometheus metricsCursor supports proxy configuration via environment variables or settings:
Via Environment Variables (Recommended):
# Set before launching Cursor
export OPENAI_API_BASE=http://127.0.0.1:8080/v1
export OPENAI_API_KEY=optional-key-if-fak-requiresOr via Cursor Settings:
- Open Cursor Settings
- Navigate to API Keys or Models
- Configure a custom endpoint:
- Base URL:
http://127.0.0.1:8080/v1 - Model:
qwen2.5:1.5b(or your upstream model)
- Base URL:
With the proxy configured, Cursor's tool calls flow through fak:
Cursor → fak /v1/chat/completions → adjudication → upstream model
↓
capability floor
↓
allowed/denied/transformed
↓
Cursor (with filtered results)
A capability floor defines which tools Cursor may call. Start from the built-in default:
# Dump the default policy as a starting point
./fak policy --dump > cursor-policy.json{
"version": "fak-policy/v1",
"posture": "fail_closed",
"allow": [
"read_file",
"search_files",
"list_directory",
"get_definition",
"git_diff",
"git_log"
],
"allow_prefix": [
"read_",
"get_",
"search_",
"list_",
"git_",
"lint_",
"format_"
],
"deny": {
"write_file": "POLICY_BLOCK",
"delete_file": "POLICY_BLOCK",
"run_command": "POLICY_BLOCK",
"execute_code": "POLICY_BLOCK",
"git_push": "POLICY_BLOCK",
"git_commit": "POLICY_BLOCK",
"install_package": "SUPPLY_CHAIN"
},
"self_modify_globs": [
".git/",
".cursor/",
"cursor-policy.json",
".env",
"id_rsa"
],
"redact_fields": [
"password",
"secret",
"api_key",
"token"
],
"safe_sinks": [
"list_directory"
],
"sources": {
"read_file": "trusted_local",
"search_files": "trusted_local",
"git_diff": "trusted_local"
},
"arg_rules": [
{
"tool": "read_file",
"arg": "path",
"deny_regex": ".*\\.env$",
"reason": "SECRET_EXFIL"
},
{
"tool": "read_file",
"arg": "max_bytes",
"max_bytes": 100000,
"reason": "OVERSIZE"
}
]
}Validate before using:
./fak policy --check cursor-policy.jsonConfigure fak to allow read operations but require explicit approval for writes:
{
"allow": ["read_file", "list_directory"],
"deny": {
"write_file": "REQUIRE_APPROVAL",
"delete_file": "POLICY_BLOCK"
}
}In Cursor:
@fak read file src/main.ts
@fak please call write_file on src/main.ts with my refactor (I'll approve separately)
Allow Git reads but block destructive Git operations:
{
"allow_prefix": ["git_diff", "git_log", "git_show", "git_blame"],
"deny": {
"git_push": "POLICY_BLOCK",
"git_reset": "POLICY_BLOCK",
"git_clean": "POLICY_BLOCK"
},
"self_modify_globs": [".git/"]
}Protect against poisoned responses from external APIs:
# Enable quarantine on the gateway
./fak serve --addr 127.0.0.1:8080 \
--base-url https://api.openai.com/v1 \
--policy policy.json \
--vdso=true # Enables content-addressed cache and quarantineIf an external tool returns suspicious content (e.g., injection attempts), fak automatically quarantines it, preventing it from entering Cursor's context.
curl http://127.0.0.1:8080/healthzcurl http://127.0.0.1:8080/metricsKey metrics:
fak_gateway_time_to_ready_seconds- Startup timefak_vdso_hit_rate- Cache hit ratefak_gateway_operations_total{verdict="DENY"}- Denied calls (by reason label)fak_kernel_quarantines_total- Quarantined results
# Get changes since sequence 0
curl http://127.0.0.1:8080/v1/fak/changes?since=0curl -X POST http://127.0.0.1:8080/v1/fak/revoke \
-H 'Content-Type: application/json' \
-d '{"witness":"git-commit-abc123"}'-
Verify
fakis running:./fak serve --stdio --policy policy.json --base-url http://localhost:11434/v1 --model qwen2.5:1.5b
-
Check Cursor's MCP configuration path:
- The path to
fakmust be absolute - Policy file paths in args must also be absolute
- The path to
-
Check Cursor's MCP logs for connection errors
-
Verify your policy file is valid:
./fak policy --check your-policy.json
-
Check the policy's posture:
posture: "fail_closed"(default) denies everything not explicitly allowed- Ensure your tools are in
allowor match anallow_prefix
-
Test a specific call:
./fak preflight --tool read_file --args '{"path":"test.txt"}' --policy your-policy.json
This is expected behavior. Quarantined results are intentionally excluded from the agent's context. Check the metrics to see what's being quarantined:
curl -s http://127.0.0.1:8080/metrics | grep quarantineFor multiple Cursor instances or multi-agent setups, fak provides:
- Cross-agent coherence - All instances see what others changed via the
/v1/fak/changesfeed - Shared vDSO cache - Deduplicated tool results across all agents
- Scoped invalidation - Namespace or resource-level cache invalidation
Example invalidation configuration:
./fak serve --addr 127.0.0.1:8080 \
--invalidation namespace \
--policy policy.json- fak documentation: README.md
- Policy schema: POLICY.md
- Cursor MCP docs: cursor.com/docs/mcp
- MCP protocol: modelcontextprotocol.io
- Example policies: fak/examples/
Apache-2.0 (matches the Microsoft Agent Governance Toolkit dependency).