Skip to content

Commit 190f12f

Browse files
authored
feat(copilot): copilot mcp + server side copilot execution (#3173)
* v0 * v1 * Basic ss tes * Ss tests * Stuff * Add mcp * mcp v1 * Improvement * Fix * BROKEN * Checkpoint * Streaming * Fix abort * Things are broken * Streaming seems to work but copilot is dumb * Fix edge issue * LUAAAA * Fix stream buffer * Fix lint * Checkpoint * Initial temp state, in the middle of a refactor * Initial test shows diff store still working * Tool refactor * First cleanup pass complete - untested * Continued cleanup * Refactor * Refactor complete - no testing yet * Fix - cursor makes me sad * Fix mcp * Clean up mcp * Updated mcp * Add respond to subagents * Fix definitions * Add tools * Add tools * Add copilot mcp tracking * Fix lint * Fix mcp * Fix * Updates * Clean up mcp * Fix copilot mcp tool names to be sim prefixed * Add opus 4.6 * Fix discovery tool * Fix * Remove logs * Fix go side tool rendering * Update docs * Fix hydration * Fix tool call resolution * Fix * Fix lint * Fix superagent and autoallow integrations * Fix always allow * Update block * Remove plan docs * Fix hardcoded ff * Fix dropped provider * Fix lint * Fix tests * Fix dead messages array * Fix discovery * Fix run workflow * Fix run block * Fix run from block in copilot * Fix lint * Fix skip and mtb * Fix typing * Fix tool call * Bump api version * Fix bun lock * Nuke bad files
1 parent e5d3049 commit 190f12f

File tree

199 files changed

+29091
-16677
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

199 files changed

+29091
-16677
lines changed

apps/docs/content/docs/en/copilot/index.mdx

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ Switch between modes using the mode selector at the bottom of the input area.
5656
Select your preferred AI model using the model selector at the bottom right of the input area.
5757

5858
**Available Models:**
59-
- Claude 4.5 Opus, Sonnet (default), Haiku
59+
- Claude 4.6 Opus (default), 4.5 Opus, Sonnet, Haiku
6060
- GPT 5.2 Codex, Pro
6161
- Gemini 3 Pro
6262

@@ -190,3 +190,99 @@ Copilot usage is billed per token from the underlying LLM. If you reach your usa
190190
<Callout type="info">
191191
See the [Cost Calculation page](/execution/costs) for billing details.
192192
</Callout>
193+
## Copilot MCP
194+
195+
You can use Copilot as an MCP server in your favorite editor or AI client. This lets you build, test, deploy, and manage Sim workflows directly from tools like Cursor, Claude Code, Claude Desktop, and VS Code.
196+
197+
### Generating a Copilot API Key
198+
199+
To connect to the Copilot MCP server, you need a **Copilot API key**:
200+
201+
1. Go to [sim.ai](https://sim.ai) and sign in
202+
2. Navigate to **Settings****Copilot**
203+
3. Click **Generate API Key**
204+
4. Copy the key — it is only shown once
205+
206+
The key will look like `sk-sim-copilot-...`. You will use this in the configuration below.
207+
208+
### Cursor
209+
210+
Add the following to your `.cursor/mcp.json` (project-level) or global Cursor MCP settings:
211+
212+
```json
213+
{
214+
"mcpServers": {
215+
"sim-copilot": {
216+
"url": "https://www.sim.ai/api/mcp/copilot",
217+
"headers": {
218+
"X-API-Key": "YOUR_COPILOT_API_KEY"
219+
}
220+
}
221+
}
222+
}
223+
```
224+
225+
Replace `YOUR_COPILOT_API_KEY` with the key you generated above.
226+
227+
### Claude Code
228+
229+
Run the following command to add the Copilot MCP server:
230+
231+
```bash
232+
claude mcp add sim-copilot \
233+
--transport http \
234+
https://www.sim.ai/api/mcp/copilot \
235+
--header "X-API-Key: YOUR_COPILOT_API_KEY"
236+
```
237+
238+
Replace `YOUR_COPILOT_API_KEY` with your key.
239+
240+
### Claude Desktop
241+
242+
Claude Desktop requires [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) to connect to HTTP-based MCP servers. Add the following to your Claude Desktop config file (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
243+
244+
```json
245+
{
246+
"mcpServers": {
247+
"sim-copilot": {
248+
"command": "npx",
249+
"args": [
250+
"-y",
251+
"mcp-remote",
252+
"https://www.sim.ai/api/mcp/copilot",
253+
"--header",
254+
"X-API-Key: YOUR_COPILOT_API_KEY"
255+
]
256+
}
257+
}
258+
}
259+
```
260+
261+
Replace `YOUR_COPILOT_API_KEY` with your key.
262+
263+
### VS Code
264+
265+
Add the following to your VS Code `settings.json` or workspace `.vscode/settings.json`:
266+
267+
```json
268+
{
269+
"mcp": {
270+
"servers": {
271+
"sim-copilot": {
272+
"type": "http",
273+
"url": "https://www.sim.ai/api/mcp/copilot",
274+
"headers": {
275+
"X-API-Key": "YOUR_COPILOT_API_KEY"
276+
}
277+
}
278+
}
279+
}
280+
}
281+
```
282+
283+
Replace `YOUR_COPILOT_API_KEY` with your key.
284+
285+
<Callout type="info">
286+
For self-hosted deployments, replace `https://www.sim.ai` with your self-hosted Sim URL.
287+
</Callout>
288+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import type { NextRequest, NextResponse } from 'next/server'
2+
import { createMcpAuthorizationServerMetadataResponse } from '@/lib/mcp/oauth-discovery'
3+
4+
export async function GET(request: NextRequest): Promise<NextResponse> {
5+
return createMcpAuthorizationServerMetadataResponse(request)
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import type { NextRequest, NextResponse } from 'next/server'
2+
import { createMcpAuthorizationServerMetadataResponse } from '@/lib/mcp/oauth-discovery'
3+
4+
export async function GET(request: NextRequest): Promise<NextResponse> {
5+
return createMcpAuthorizationServerMetadataResponse(request)
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import type { NextRequest, NextResponse } from 'next/server'
2+
import { createMcpAuthorizationServerMetadataResponse } from '@/lib/mcp/oauth-discovery'
3+
4+
export async function GET(request: NextRequest): Promise<NextResponse> {
5+
return createMcpAuthorizationServerMetadataResponse(request)
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import type { NextRequest, NextResponse } from 'next/server'
2+
import { createMcpProtectedResourceMetadataResponse } from '@/lib/mcp/oauth-discovery'
3+
4+
export async function GET(request: NextRequest): Promise<NextResponse> {
5+
return createMcpProtectedResourceMetadataResponse(request)
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import type { NextRequest, NextResponse } from 'next/server'
2+
import { createMcpProtectedResourceMetadataResponse } from '@/lib/mcp/oauth-discovery'
3+
4+
export async function GET(request: NextRequest): Promise<NextResponse> {
5+
return createMcpProtectedResourceMetadataResponse(request)
6+
}

apps/sim/app/api/billing/update-cost/route.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const UpdateCostSchema = z.object({
1818
model: z.string().min(1, 'Model is required'),
1919
inputTokens: z.number().min(0).default(0),
2020
outputTokens: z.number().min(0).default(0),
21+
source: z.enum(['copilot', 'mcp_copilot']).default('copilot'),
2122
})
2223

2324
/**
@@ -75,12 +76,14 @@ export async function POST(req: NextRequest) {
7576
)
7677
}
7778

78-
const { userId, cost, model, inputTokens, outputTokens } = validation.data
79+
const { userId, cost, model, inputTokens, outputTokens, source } = validation.data
80+
const isMcp = source === 'mcp_copilot'
7981

8082
logger.info(`[${requestId}] Processing cost update`, {
8183
userId,
8284
cost,
8385
model,
86+
source,
8487
})
8588

8689
// Check if user stats record exists (same as ExecutionLogger)
@@ -96,7 +99,7 @@ export async function POST(req: NextRequest) {
9699
return NextResponse.json({ error: 'User stats record not found' }, { status: 500 })
97100
}
98101

99-
const updateFields = {
102+
const updateFields: Record<string, unknown> = {
100103
totalCost: sql`total_cost + ${cost}`,
101104
currentPeriodCost: sql`current_period_cost + ${cost}`,
102105
totalCopilotCost: sql`total_copilot_cost + ${cost}`,
@@ -105,17 +108,24 @@ export async function POST(req: NextRequest) {
105108
lastActive: new Date(),
106109
}
107110

111+
// Also increment MCP-specific counters when source is mcp_copilot
112+
if (isMcp) {
113+
updateFields.totalMcpCopilotCost = sql`total_mcp_copilot_cost + ${cost}`
114+
updateFields.currentPeriodMcpCopilotCost = sql`current_period_mcp_copilot_cost + ${cost}`
115+
}
116+
108117
await db.update(userStats).set(updateFields).where(eq(userStats.userId, userId))
109118

110119
logger.info(`[${requestId}] Updated user stats record`, {
111120
userId,
112121
addedCost: cost,
122+
source,
113123
})
114124

115125
// Log usage for complete audit trail
116126
await logModelUsage({
117127
userId,
118-
source: 'copilot',
128+
source: isMcp ? 'mcp_copilot' : 'copilot',
119129
model,
120130
inputTokens,
121131
outputTokens,

apps/sim/app/api/copilot/api-keys/generate/route.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { type NextRequest, NextResponse } from 'next/server'
22
import { z } from 'zod'
33
import { getSession } from '@/lib/auth'
4-
import { SIM_AGENT_API_URL_DEFAULT } from '@/lib/copilot/constants'
4+
import { SIM_AGENT_API_URL } from '@/lib/copilot/constants'
55
import { env } from '@/lib/core/config/env'
66

77
const GenerateApiKeySchema = z.object({
@@ -17,9 +17,6 @@ export async function POST(req: NextRequest) {
1717

1818
const userId = session.user.id
1919

20-
// Move environment variable access inside the function
21-
const SIM_AGENT_API_URL = env.SIM_AGENT_API_URL || SIM_AGENT_API_URL_DEFAULT
22-
2320
const body = await req.json().catch(() => ({}))
2421
const validationResult = GenerateApiKeySchema.safeParse(body)
2522

apps/sim/app/api/copilot/api-keys/route.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ describe('Copilot API Keys API Route', () => {
1919

2020
vi.doMock('@/lib/copilot/constants', () => ({
2121
SIM_AGENT_API_URL_DEFAULT: 'https://agent.sim.example.com',
22+
SIM_AGENT_API_URL: 'https://agent.sim.example.com',
2223
}))
2324

2425
vi.doMock('@/lib/core/config/env', async () => {

apps/sim/app/api/copilot/api-keys/route.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { type NextRequest, NextResponse } from 'next/server'
22
import { getSession } from '@/lib/auth'
3-
import { SIM_AGENT_API_URL_DEFAULT } from '@/lib/copilot/constants'
3+
import { SIM_AGENT_API_URL } from '@/lib/copilot/constants'
44
import { env } from '@/lib/core/config/env'
55

66
export async function GET(request: NextRequest) {
@@ -12,8 +12,6 @@ export async function GET(request: NextRequest) {
1212

1313
const userId = session.user.id
1414

15-
const SIM_AGENT_API_URL = env.SIM_AGENT_API_URL || SIM_AGENT_API_URL_DEFAULT
16-
1715
const res = await fetch(`${SIM_AGENT_API_URL}/api/validate-key/get-api-keys`, {
1816
method: 'POST',
1917
headers: {
@@ -68,8 +66,6 @@ export async function DELETE(request: NextRequest) {
6866
return NextResponse.json({ error: 'id is required' }, { status: 400 })
6967
}
7068

71-
const SIM_AGENT_API_URL = env.SIM_AGENT_API_URL || SIM_AGENT_API_URL_DEFAULT
72-
7369
const res = await fetch(`${SIM_AGENT_API_URL}/api/validate-key/delete`, {
7470
method: 'POST',
7571
headers: {

0 commit comments

Comments
 (0)