Route model requests
Send Cursor’s model traffic through the gateway using the Override Base URL setting with the Databricks model provider.
Guardrail what Cursor does
Block or rewrite risky prompts, file reads, shell commands and tool calls using Cursor hooks. This works no matter how Cursor reaches the model.
BYOK Works, Override Base URL Only Works with Databricks
Cursor offers two ways to connect to models:- BYOK (Bring Your Own Key) — You enter your API key directly in Cursor settings without changing the base URL. This works as expected.
- Override Base URL — You change the OpenAI base URL to point to a custom gateway or proxy. This only works with the Databricks model provider as described below.
What Works
Databricks Provider with Override Base URL — Create a Databricks model provider account with the
/cursor/v1 base URL and add your TrueFoundry API key in Cursor settings.What Breaks
Other Providers with Override Base URL — OpenAI, Anthropic, and Gemini providers send incompatible request formats to
/chat/completions, and no amount of proxying can reliably fix it.Prerequisites
Before integrating Cursor with TrueFoundry, ensure you have:- TrueFoundry Account: Create a TrueFoundry account and follow the instructions in our Gateway Quick Start Guide
- Databricks Model Account: Create a Databricks model provider account in TrueFoundry with the base URL set to:
- Models Configured: Add the models you want to use to the Databricks model account on the TrueFoundry platform
- Cursor Installed: Install Cursor on your machine
Integrate TrueFoundry with Cursor
1
Open Cursor Settings
Open Cursor and navigate to Cursor Settings → Models.
2
Add API Key and Base URL
Scroll to the API Keys section at the bottom and configure:
- OpenAI API Key: Enter your TrueFoundry API key (Personal Access Token)
- OpenAI Base URL: Enter
{GATEWAY_BASE_URL}/cursor/v1
3
Add Custom Models
Add the custom models you configured on the TrueFoundry platform. Use the exact Model ID from your TrueFoundry model configuration as the model name in Cursor.
Important Precautions
- Model name must match Model ID — When adding custom models in Cursor, use the exact same name as the Model ID configured on the TrueFoundry platform.
- Avoid reserved words in model account name — Do not use words like
GPT,Claude, orGeminiin your Databricks model provider account name. Cursor uses these keywords internally to determine request formatting, which can cause unexpected behavior. - Cursor-only integration — This integration works exclusively within the Cursor IDE. It will not work on the TrueFoundry Playground or any other interface.
What Goes Wrong with Other Providers
When you override the base URL, Cursor still sends requests to the/chat/completions endpoint. But the request body format it sends does not match the Chat Completions spec. The format changes depending on the model you select:
- OpenAI models — Cursor sends requests in the Responses API format. It uses
inputinstead ofmessages, sends flat tool definitions, and includes parameters likestore,previous_response_id, andtruncationthat don’t belong in Chat Completions. - Anthropic models — Cursor sends requests in the native Anthropic format with Responses API structure on top, while still targeting the
/chat/completionsendpoint. - Gemini models — Similar behavior. Cursor sends Gemini specific formatting that does not follow the Chat Completions spec.
In all cases, the endpoint is
/chat/completions but the payload is something else entirely. This is the root cause of failures with non-Databricks providers.Why a Proxy Does Not Fix This
You might think you can set up a proxy to intercept and transform these requests. This does not work reliably for two reasons:- The request format is unpredictable. Cursor mixes Responses API fields, native model formats, and custom tool types (
"type": "custom"). Transforming all of this into valid Chat Completions format requires heavy and fragile patching. - Cursor expects strict Chat Completions responses. Even if you fix the request, Cursor will reject any response that does not exactly match the Chat Completions response format. So you need to transform both directions perfectly.
No Support for Thinking Tokens
The Chat Completions API does not support thinking or reasoning tokens. Even if you get requests flowing through a proxy, the model’s reasoning process cannot be shown in Cursor’s UI.This matters because thinking tokens give visibility into how the model arrives at its answer. Without them, the experience feels like a black box.
The underlying issue with non-Databricks providers is tracked by the Cursor team. See the Cursor Community Forum thread for technical details and discussion.
Guardrails using Cursor hooks
Cursor does most of its work outside the model API — reading files, running shell commands, calling MCP tools, starting subagents. TrueFoundry can check all of that using Cursor hooks. Cursor tells the gateway what it is about to do, the gateway runs your guardrails, and the gateway’s answer either lets the action happen, stops it, or hands back a cleaned-up version.Hooks have nothing to do with how Cursor reaches the model, so they work even if you are not using the base-URL setup described above. Use either or both.
How it works
- Cursor is about to do something (say, read a file). It sends the details of that action, as JSON, to a command you configure.
- That command is a small helper script. It forwards the JSON to the gateway at
POST /hooks/cursor. - The gateway runs the guardrails you picked for that action and replies with JSON.
- The helper script prints the reply back to Cursor, and Cursor does what it says: allow, stop, or use the rewritten content.
Cursor cannot call a URL on its own — unlike Claude Code, it has no HTTP hook type. Hooks are commands that read from standard input and write to standard output, which is why the helper script is needed. The gateway already replies in exactly the format Cursor expects, so the script just passes the reply straight through without changing it.
What a guardrail can do at each action
A guardrail can do one of two things:- Block — stop the prompt, file read, command or tool call before it happens.
- Rewrite — edit the content in place (for example, redact an email address out of a tool’s input) and let the action continue with the cleaned-up version.
In short:
- Blocking works on all eight.
- Rewriting works only on
preToolUse(tool input) andpostToolUse(MCP tool results). Everything else is allow-or-stop. - If a guardrail that rewrites content is used on an action that can’t carry a rewrite, the gateway stops the action instead of letting the original through. The content the guardrail wanted to clean up is never sent. So a redaction guardrail on those actions effectively behaves as a blocker.
Hooks cannot check the model’s answers. Cursor’s
afterAgentResponse hook is notification-only — it has no fields for changing anything — so the assistant’s reply can’t be stopped or edited here. To check model responses, use output guardrails on the model request path instead (see Guardrails).Choosing which guardrails run
You pick the guardrails for each request with thex-tfy-hook-guardrails header. Its value is a plain JSON list of guardrail names, written as group/guardrail-name:
hooks.json can use its own guardrails.
You don’t have to say where a guardrail should run — the Cursor action decides that for you:
A guardrail set up for one of these roles won’t work on an action that needs a different one, and the gateway blocks the action when that happens. Match the table above when choosing names.
Two behaviours worth knowing:
- No header, or an empty one — no guardrails are selected and the action goes through unchecked. The helper script below refuses to run in this case, because the gateway’s reply would look exactly like a clean pass.
- A header that isn’t a valid JSON list of names — the action is blocked.
What you need
- A guardrail group with at least one guardrail in it — see Create a guardrail. Note its full name, e.g.
security/email-regex. - A TrueFoundry API key with access to that guardrail group.
- Cursor installed.
Set up
Three files, all in your home directory, so the guardrails apply to every project you open in Cursor. To limit them to a single repository instead, see Where to put the config — only the paths change.1
Store your credentials
Cursor is launched from the desktop, so it does not see the environment variables you set in your terminal. The hook can’t read an exported variable. Put your key in a small config file that the helper script reads instead.Keeping the key in its own file, rather than inside the script, means the hook files stay safe to commit — to a dotfiles repository for the global setup below, or to a project repository if you scope the hooks to one repo.
- macOS
- Linux
- Windows
TFY_GATEWAY_URL is your gateway host root — the same host you use for model requests but without the /api/llm suffix, because the hook endpoint sits at the root (for example https://your-gateway-host/hooks/cursor).2
Add the helper script
This script forwards each action to the gateway and prints the gateway’s reply back to Cursor.
- macOS
- Linux
- Windows
Save as
~/.cursor/hooks/tfy-guardrails.sh, then run chmod +x ~/.cursor/hooks/tfy-guardrails.sh to make it executable.3
Register the hooks
Save this as Register only the actions you need — each one means another call to the gateway every time it happens. To use different guardrails per action, change the arguments: adding
~/.cursor/hooks.json (%USERPROFILE%\.cursor\hooks.json on Windows). Replace the security/guardrail* placeholders with your own guardrail names — they can differ per action, or all be the same.- macOS
- Linux
- Windows
security/prompt-pii security/secrets runs both on that action only.4
Verify
Test the helper script on its own before involving Cursor. It reads its own credentials, so this works from any terminal:A stopped read prints
- macOS
- Linux
- Windows
{"permission":"deny","user_message":"..."} with exit=0. Run it again with clean content and you should get {"permission":"allow"}. Do both — a setup that is broken and stopping everything looks identical to one that is correctly catching a violation.Then open any project in Cursor normally — from the Dock, Start menu, recent projects, however you like. Cursor watches hooks.json and reloads it automatically; restart Cursor if the hooks don’t appear. The Hooks tab under Customize lists what’s registered, and the Hooks output channel shows error messages and exit codes — that’s where tfy-guardrails: ... messages appear.beforeMCPExecution if you need rules that apply to a specific MCP server — it is the only “before” action that tells you which server a tool belongs to. Otherwise it covers the same ground as preToolUse, so registering both means two gateway calls for every MCP tool call.
Where to put the config
Cursor reads hooks from four places. The setup above uses the user location, which covers every project you open.
When two of these disagree, Cursor takes the answer from the highest one, in the order Enterprise → Team → Project → User. But that only settles disagreements — hooks from every location still run. A
hooks.json in your home directory does not replace one in a repository. If you open a project that ships its own hooks, both fire, which means two calls to the gateway and two guardrail checks for every action. Register a given action in one place only, unless you actually want both.
Hooks in your home directory do not run in Cursor cloud agents — those machines have no access to it. Cloud agents load project, team, and enterprise hooks only, so commit a project-level config if you need cloud agents guarded.
beforeTabFileRead and beforeMCPExecution don’t run in cloud agents at all, and hooks are inactive during a cloud agent’s first read-only turns.What the gateway sends back
The gateway replies with only the fields Cursor understands, and which fields Cursor acts on depends on the action:- Stopped — the reply carries that action’s stop fields, with the guardrail’s message as the reason (or
Request blocked by guardrail.if the guardrail didn’t supply one). - Rewritten — the cleaned-up content comes back in
updated_input(preToolUse) orupdated_mcp_tool_output(postToolUse). On every other action, a rewrite turns into a stop. - Something went wrong — if the gateway hits an internal error, or can’t find a guardrail you named, it sends back the same stop fields. An action it couldn’t check is stopped rather than allowed through.
- A stop is always sent as HTTP 200, because Cursor reads the decision out of the reply body, not the status code. The helper script treats anything other than a 2xx as a failure and stops the action.
Limitations to know about
postToolUsecannot prevent a tool from running. It fires after the tool has run, so its effects — a file written, a command executed, an API called — have already happened. Stopping there only replaces the result the model sees. To actually prevent a dangerous action, use the matching “before” action:preToolUse,beforeShellExecution, orbeforeReadFile.postToolUsecan only clean up MCP tool output. Cursor appliesupdated_mcp_tool_outputto MCP tools only. For built-in tools (Shell,Read,Write, and so on) the replacement is ignored and the original output still reaches the model, with onlyadditional_contextadded. Don’t rely onpostToolUseto redact built-in tool output — check the input at the matching “before” action, or usebeforeReadFilefor file contents.- Only “stop” is reliably enforced. Cursor honours
permission: "deny". Anallowdoes not skip Cursor’s own approval prompts, so a pass means “this guardrail has no objection”, not “run this without asking”. - A stopped Tab read is silent.
beforeTabFileReadhas no message field, so the user sees no explanation. Check the Hooks output channel, or setTFY_HOOK_LOGin the config file, when debugging. - File-read hooks slow down every read.
beforeReadFileandbeforeTabFileReadfire on every file entering context, including Tab’s background reads, and each one costs a call to the gateway. Register them deliberately. - Built-in tools all report the same server name.
preToolUseandpostToolUsedon’t say which MCP server a tool came from, so every tool — built-in or MCP — reaches the guardrail engine under the stand-in server namecursor(with Cursor’sMCP:prefix stripped from the tool name).beforeShellExecutionreports no tool name at all and shows up as servercursor, toolShell. If you write guardrail rules that depend on which MCP server is involved, usebeforeMCPExecution, which reports the real server URL or command. - Hooks can’t check the model’s answers. Cursor’s
afterAgentResponsehook is notification-only, so model output can’t be stopped or edited here. Use output guardrails on the model request path instead (Guardrails). - Guardrails are chosen by the header, not by your tenant’s rules. The endpoint runs exactly the guardrails named in the
x-tfy-hook-guardrailsheader; it does not look at your tenant’s Guardrail Config rules. Each action’s hook config carries its own list. - Hooks aren’t a replacement for gateway-side controls. With
failClosed: trueand the helper script’s exit code 2, a gateway error or an unreachable gateway stops the action. But hooks run on the developer’s own machine and can be edited there unless you distribute them as team or enterprise config, so they shouldn’t be your only control for hard security requirements.