I love Cursor IDE. It is arguably the most advanced AI code editor on the market today.
But I hate closed ecosystems. And I absolutely refuse to pay for two AI subscriptions when one should do the job.
I already have a GitHub Copilot license. I wanted Cursor’s UI (the Composer, the terminal execution, the MCP tools) to be powered by the Copilot backend I already pay for.
But Cursor is a walled garden. If you try to use their "Agent" features with your own API keys, it actively blocks you. It hardcodes its internal router to force models like claude-3.5-sonnet through their proprietary billing backend.
So, I decided to build a local Man-in-the-Middle (MITM) proxy. I wanted to trick the smartest IDE in the world into thinking it was talking to its own servers, while secretly tunneling everything to GitHub.
Here is the deep technical breakdown of how I reverse-engineered the handshake, bypassed the router, and mutated the Abstract Syntax Trees (AST) in real-time to make it happen.
🏗️ The Architecture of a Heist
To pull this off, I needed two local services running in tandem:
- Port 4141 (The Auth Layer): An open-source connector that handles the OAuth handshake with GitHub and exposes an OpenAI-compatible endpoint.
- Port 4142 (The Intelligence Proxy): A Bun-powered proxy router that intercepts Cursor's outbound HTTP traffic, manipulates the payloads, and forwards them to Port 4141.
The flow looks like this:
Cursor UI ➡️ Proxy Router (4142) ➡️ Copilot Bridge (4141) ➡️ GitHub Servers
Building a proxy is easy. Bypassing Cursor's internal logic was the hard part. Here are the two massive roadblocks I had to solve.
🕳️ Hack 1: Bypassing the Router (The cus- Loophole)
Cursor is smart. If you go into Settings and define a custom model called claude-3.5-sonnet pointing to localhost:4142, Cursor ignores your URL. It sees the word "claude", assumes it belongs to them, and routes the network request to api2.cursor.sh.
You cannot overwrite their internal routing table.
The Solution: I needed a loophole. Cursor only hijacks known model names. If it doesn't recognize the model, it gracefully falls back to your custom API endpoint.
So, I prepended a custom tag: cus-.
In Cursor, I configured the model as cus-claude-3.5-sonnet. Cursor looks at this, says "I have no idea what this is, must be a local LLM," and allows the payload to escape the IDE and hit my proxy on localhost:4142.
Inside my Bun proxy, I intercept the request body and silently strip the prefix before forwarding it to GitHub:
// proxy-router.ts
let json = await req.json();
const PREFIX = "cus-";
// The Heist: Strip the prefix so Copilot gets the real model name
if (json.model && json.model.startsWith(PREFIX)) {
const targetModel = json.model.slice(PREFIX.length);
console.log(`🔄 Rewriting model: ${json.model} -> ${targetModel}`);
json.model = targetModel;
}
Handshake bypassed. ✅
🧬 Hack 2: The AST Schema Mutation (The Final Boss)
Routing the request was only 10% of the battle. Getting the Agent to actually write code was a nightmare.
When you use Cursor's Composer or Agent features, the IDE sends massive arrays of Tool Calling instructions (File Editors, Linter commands, Terminal executors).
Because Cursor partners heavily with Anthropic, their frontend is hardcoded to send Anthropic-flavored tool schemas (using the input_schema key).
But GitHub Copilot's API expects strict OpenAI-flavored function calls (using the parameters key wrapped inside a function object).
If you just forward Cursor's request to Copilot, Copilot’s backend immediately throws a 400 Bad Request: Invalid Schema.
Worse, Cursor injects illegal JSON schema properties deep inside the tool definitions—things like additionalProperties: false, title, and $schema. Copilot’s strict parser rejects all of these.
The Solution: Real-Time AST Cleansing
I couldn't just use JSON.stringify.replace(). The payload is too complex.
I had to write a recursive AST cleaner inside the proxy. This function intercepts the HTTP stream in milliseconds, walks the entire JSON tree, and mutates it on the fly:
// --- HELPER: Recursively clean illegal properties from the AST ---
const cleanSchema = (schema: any) => {
if (!schema || typeof schema !== 'object') return schema;
// Copilot's OpenAI parser hates these. Nuke them.
if (schema.additionalProperties !== undefined) delete schema.additionalProperties;
if (schema.$schema !== undefined) delete schema.$schema;
if (schema.title !== undefined) delete schema.title;
// Recurse into nested object properties
if (schema.properties) {
for (const key in schema.properties) {
cleanSchema(schema.properties[key]);
}
}
// Recurse into arrays
if (schema.items) cleanSchema(schema.items);
return schema;
};
Once we have a weapon to clean the AST, we intercept Cursor's tools array, dynamically translate the Anthropic shapes into strict OpenAI formats, and run the cleaner over every single tool:
// --- TRANSFORM TOOLS (Anthropic -> OpenAI) ---
if (json.tools && Array.isArray(json.tools)) {
json.tools = json.tools.map((tool: any) => {
// Cursor uses input_schema. OpenAI wants parameters.
let parameters = tool.input_schema || tool.parameters || {};
// Mutate the AST to remove illegal tokens
parameters = cleanSchema(parameters);
// Reconstruct the tool in strict OpenAI format
return {
type: "function",
function: {
name: tool.name,
description: tool.desc || tool.description,
parameters: parameters
}
};
});
}
🤯 The Result: Absolute Freedom
When I spun up the proxy and asked Cursor's Agent to build a React component... it worked.
- Cursor’s frontend thinks it’s talking to its proprietary Anthropic backend.
- GitHub Copilot thinks it’s serving a standard VS Code extension via OpenAI spec.
- I sit in the middle, seamlessly translating their protocols in real-time.
Suddenly, Cursor's "Agent Mode", Terminal Execution, and MCP Tools started working perfectly... powered entirely by my existing Copilot license. Zero duplicate subscriptions. Total freedom.
I open-sourced the entire proxy router so you can run it yourself.
👉 Study the Source Code on GitHub (jacksonkasi1/copilot-for-cursor)
📚 Deeper Exploration
If you are fascinated by this kind of reverse engineering and API mutation, here are the foundational concepts you should explore next:
-
Man-in-the-Middle (MITM) Proxies: To figure out what Cursor was sending in the first place, I had to intercept the traffic. Tools like
mitmproxyorBurp Suiteare essential for inspecting encrypted HTTPS traffic and dealing with SSL Certificate Pinning. Read more on reverse engineering private APIs with MITM proxies. - LLM Tool Calling Schemas: The AI industry is heavily fragmented. Understanding the exact schema differences between Anthropic's Tool Use (input_schema) and OpenAI's Function Calling (parameters) is critical if you want to build cross-platform AI tools.
-
AST (Abstract Syntax Tree) Traversal: The recursive
cleanSchemafunction is a basic form of AST traversal. If you want to write compilers, linters, or advanced proxies, mastering Depth-First Search (DFS) on JSON trees is a mandatory skill.
Have you ever had to build a Man-in-the-Middle proxy to bypass a vendor lock-in? Let's talk about reverse-engineering in the comments.
Top comments (0)