Skip to main content
Cursor is an AI code editor built on VS Code. There are two separate things you can do with the TrueFoundry AI Gateway, and you can use either one or both:

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.
The Override Base URL approach has compatibility issues with non-Databricks providers that will cause requests to fail. The integration described below uses the Databricks provider which handles Cursor’s request format correctly.

Prerequisites

Before integrating Cursor with TrueFoundry, ensure you have:
  1. TrueFoundry Account: Create a TrueFoundry account and follow the instructions in our Gateway Quick Start Guide
  2. Databricks Model Account: Create a Databricks model provider account in TrueFoundry with the base URL set to:
  3. Models Configured: Add the models you want to use to the Databricks model account on the TrueFoundry platform
  4. Cursor Installed: Install Cursor on your machine

Integrate TrueFoundry with Cursor

1

Open Cursor Settings

Open Cursor and navigate to Cursor SettingsModels.
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

Follow these guidelines to avoid common integration issues:
  • 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, or Gemini in 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 input instead of messages, sends flat tool definitions, and includes parameters like store, previous_response_id, and truncation that 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/completions endpoint.
  • 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:
  1. 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.
  2. 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

  1. Cursor is about to do something (say, read a file). It sends the details of that action, as JSON, to a command you configure.
  2. That command is a small helper script. It forwards the JSON to the gateway at POST /hooks/cursor.
  3. The gateway runs the guardrails you picked for that action and replies with JSON.
  4. 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.
Not every action supports both. Here are the eight actions the gateway supports: In short:
  • Blocking works on all eight.
  • Rewriting works only on preToolUse (tool input) and postToolUse (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 the x-tfy-hook-guardrails header. Its value is a plain JSON list of guardrail names, written as group/guardrail-name:
The helper script below builds this header from the arguments you give it, so each action in hooks.json can use its own guardrails.
This header is only for the hook endpoint. It is not the x-tfy-guardrails header used for model requests, and the two are not interchangeable. Sending x-tfy-guardrails here selects nothing, and your guardrails silently will not run.
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

  1. A guardrail group with at least one guardrail in it — see Create a guardrail. Note its full name, e.g. security/email-regex.
  2. A TrueFoundry API key with access to that guardrail group.
  3. Cursor installed.
Use a personal or service-account API key. If you use the gateway’s own internal TFY_API_KEY, the request is treated as an internal service call and rejected with x-tfy-assume-user header is required for internal service calls. This is an easy trap if you copy the key out of a gateway deployment’s environment.

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.
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.
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 ~/.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.
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 security/prompt-pii security/secrets runs both on that action only.
failClosed: true matters. By default, if the script crashes, times out, or prints something Cursor can’t read, Cursor lets the action through as though the hook had approved it. Setting failClosed: true flips that around, so a broken hook stops the action instead of quietly waving it through.
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 {"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.
Add 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.
The command path depends on where the config lives, because each location runs the command from a different folder. User hooks run from ~/.cursor/, so the command is ./hooks/tfy-guardrails.sh. Project hooks run from the repository root, so the same script committed at <repo>/.cursor/hooks/tfy-guardrails.sh must be written as .cursor/hooks/tfy-guardrails.sh. Getting this wrong is the most common reason a Cursor hook silently does nothing.
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.
For enforcement you can rely on, use the team or enterprise location. Both outrank project and user config, so a developer cannot turn the guardrails off by editing a file on their own machine.

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) or updated_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.
An “allowed” reply looks the same as no guardrail having run. Unlike the Claude Code endpoint, this reply has no field telling you what was checked — a request that named no guardrails at all comes back with the same permission: "allow" as one that was checked and approved. Always confirm your setup with input you know should be caught, rather than trusting a pass. The helper script covers the most common cause by refusing to run when no guardrails are listed.

Limitations to know about

  • postToolUse cannot 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, or beforeReadFile.
  • postToolUse can only clean up MCP tool output. Cursor applies updated_mcp_tool_output to 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 only additional_context added. Don’t rely on postToolUse to redact built-in tool output — check the input at the matching “before” action, or use beforeReadFile for file contents.
  • Only “stop” is reliably enforced. Cursor honours permission: "deny". An allow does 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. beforeTabFileRead has no message field, so the user sees no explanation. Check the Hooks output channel, or set TFY_HOOK_LOG in the config file, when debugging.
  • File-read hooks slow down every read. beforeReadFile and beforeTabFileRead fire 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. preToolUse and postToolUse don’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 name cursor (with Cursor’s MCP: prefix stripped from the tool name). beforeShellExecution reports no tool name at all and shows up as server cursor, tool Shell. If you write guardrail rules that depend on which MCP server is involved, use beforeMCPExecution, which reports the real server URL or command.
  • Hooks can’t check the model’s answers. Cursor’s afterAgentResponse hook 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-guardrails header; 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: true and 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.