Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
622d0ca
Merge pull request #3172 from simstudioai/fix/notifs
waleedlatif1 Feb 9, 2026
b3dbb44
improvement(jsm): destructured outputs for jsm, jira, and added 1pass…
waleedlatif1 Feb 10, 2026
e5d3049
fix(slack): resolve file metadata via files.info when event payload i…
waleedlatif1 Feb 10, 2026
190f12f
feat(copilot): copilot mcp + server side copilot execution (#3173)
Sg312 Feb 10, 2026
8b4b3af
fix(mcp): harden notification system against race conditions (#3168)
waleedlatif1 Feb 10, 2026
e321f88
improvement(preview): added trigger mode context for deploy preview (…
waleedlatif1 Feb 10, 2026
73540e3
feat(logs): add skill icon to trace spans (#3181)
emir-karabeg Feb 10, 2026
be3cdcf
Merge pull request #3179 from simstudioai/improvement/file-download-t…
icecrasher321 Feb 10, 2026
20b230d
improvement(schema): centralize derivation of block schemas (#3175)
icecrasher321 Feb 11, 2026
c5dd90e
feat(copilot): enterprise configuration (#3184)
Sg312 Feb 11, 2026
f8e9614
improvement(helm): support copilot-only deployments (#3185)
waleedlatif1 Feb 11, 2026
6d16f21
improvement(mcp): improved mcp sse events notifs, update jira to hand…
waleedlatif1 Feb 11, 2026
78fef22
fix(execution): scope execution state per workflow to prevent cross-w…
waleedlatif1 Feb 11, 2026
f5dc180
fix(memory): upgrade bun from 1.3.3 to 1.3.9 (#3186)
waleedlatif1 Feb 11, 2026
c471627
fix(posthog): replace proxy rewrite with route handler for reliable b…
waleedlatif1 Feb 11, 2026
8a24b56
improvement(terminal): increase workflow logs limit from 1k to 5k per…
waleedlatif1 Feb 11, 2026
af01dce
fix(terminal): subflow logs rendering (#3189)
icecrasher321 Feb 11, 2026
13a9111
fix(logs): surface handled errors as info in logs (#3190)
waleedlatif1 Feb 11, 2026
3d5bd00
fix(triggers): add copilot as a trigger type (#3191)
waleedlatif1 Feb 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
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
  • Loading branch information
Sg312 authored Feb 10, 2026
commit 190f12fd77bfff50f5ea7ccd6eb9ca1711d5abff
98 changes: 97 additions & 1 deletion apps/docs/content/docs/en/copilot/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Switch between modes using the mode selector at the bottom of the input area.
Select your preferred AI model using the model selector at the bottom right of the input area.

**Available Models:**
- Claude 4.5 Opus, Sonnet (default), Haiku
- Claude 4.6 Opus (default), 4.5 Opus, Sonnet, Haiku
- GPT 5.2 Codex, Pro
- Gemini 3 Pro

Expand Down Expand Up @@ -190,3 +190,99 @@ Copilot usage is billed per token from the underlying LLM. If you reach your usa
<Callout type="info">
See the [Cost Calculation page](/execution/costs) for billing details.
</Callout>
## Copilot MCP

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.

### Generating a Copilot API Key

To connect to the Copilot MCP server, you need a **Copilot API key**:

1. Go to [sim.ai](https://sim.ai) and sign in
2. Navigate to **Settings** → **Copilot**
3. Click **Generate API Key**
4. Copy the key — it is only shown once

The key will look like `sk-sim-copilot-...`. You will use this in the configuration below.

### Cursor

Add the following to your `.cursor/mcp.json` (project-level) or global Cursor MCP settings:

```json
{
"mcpServers": {
"sim-copilot": {
"url": "https://www.sim.ai/api/mcp/copilot",
"headers": {
"X-API-Key": "YOUR_COPILOT_API_KEY"
}
}
}
}
```

Replace `YOUR_COPILOT_API_KEY` with the key you generated above.

### Claude Code

Run the following command to add the Copilot MCP server:

```bash
claude mcp add sim-copilot \
--transport http \
https://www.sim.ai/api/mcp/copilot \
--header "X-API-Key: YOUR_COPILOT_API_KEY"
```

Replace `YOUR_COPILOT_API_KEY` with your key.

### Claude Desktop

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):

```json
{
"mcpServers": {
"sim-copilot": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://www.sim.ai/api/mcp/copilot",
"--header",
"X-API-Key: YOUR_COPILOT_API_KEY"
]
}
}
}
```

Replace `YOUR_COPILOT_API_KEY` with your key.

### VS Code

Add the following to your VS Code `settings.json` or workspace `.vscode/settings.json`:

```json
{
"mcp": {
"servers": {
"sim-copilot": {
"type": "http",
"url": "https://www.sim.ai/api/mcp/copilot",
"headers": {
"X-API-Key": "YOUR_COPILOT_API_KEY"
}
}
}
}
}
```

Replace `YOUR_COPILOT_API_KEY` with your key.

<Callout type="info">
For self-hosted deployments, replace `https://www.sim.ai` with your self-hosted Sim URL.
</Callout>

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { NextRequest, NextResponse } from 'next/server'
import { createMcpAuthorizationServerMetadataResponse } from '@/lib/mcp/oauth-discovery'

export async function GET(request: NextRequest): Promise<NextResponse> {
return createMcpAuthorizationServerMetadataResponse(request)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { NextRequest, NextResponse } from 'next/server'
import { createMcpAuthorizationServerMetadataResponse } from '@/lib/mcp/oauth-discovery'

export async function GET(request: NextRequest): Promise<NextResponse> {
return createMcpAuthorizationServerMetadataResponse(request)
}
6 changes: 6 additions & 0 deletions apps/sim/app/.well-known/oauth-authorization-server/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { NextRequest, NextResponse } from 'next/server'
import { createMcpAuthorizationServerMetadataResponse } from '@/lib/mcp/oauth-discovery'

export async function GET(request: NextRequest): Promise<NextResponse> {
return createMcpAuthorizationServerMetadataResponse(request)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { NextRequest, NextResponse } from 'next/server'
import { createMcpProtectedResourceMetadataResponse } from '@/lib/mcp/oauth-discovery'

export async function GET(request: NextRequest): Promise<NextResponse> {
return createMcpProtectedResourceMetadataResponse(request)
}
6 changes: 6 additions & 0 deletions apps/sim/app/.well-known/oauth-protected-resource/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { NextRequest, NextResponse } from 'next/server'
import { createMcpProtectedResourceMetadataResponse } from '@/lib/mcp/oauth-discovery'

export async function GET(request: NextRequest): Promise<NextResponse> {
return createMcpProtectedResourceMetadataResponse(request)
}
16 changes: 13 additions & 3 deletions apps/sim/app/api/billing/update-cost/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const UpdateCostSchema = z.object({
model: z.string().min(1, 'Model is required'),
inputTokens: z.number().min(0).default(0),
outputTokens: z.number().min(0).default(0),
source: z.enum(['copilot', 'mcp_copilot']).default('copilot'),
})

/**
Expand Down Expand Up @@ -75,12 +76,14 @@ export async function POST(req: NextRequest) {
)
}

const { userId, cost, model, inputTokens, outputTokens } = validation.data
const { userId, cost, model, inputTokens, outputTokens, source } = validation.data
const isMcp = source === 'mcp_copilot'

logger.info(`[${requestId}] Processing cost update`, {
userId,
cost,
model,
source,
})

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

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

// Also increment MCP-specific counters when source is mcp_copilot
if (isMcp) {
updateFields.totalMcpCopilotCost = sql`total_mcp_copilot_cost + ${cost}`
updateFields.currentPeriodMcpCopilotCost = sql`current_period_mcp_copilot_cost + ${cost}`
}

await db.update(userStats).set(updateFields).where(eq(userStats.userId, userId))

logger.info(`[${requestId}] Updated user stats record`, {
userId,
addedCost: cost,
source,
})

// Log usage for complete audit trail
await logModelUsage({
userId,
source: 'copilot',
source: isMcp ? 'mcp_copilot' : 'copilot',
model,
inputTokens,
outputTokens,
Expand Down
5 changes: 1 addition & 4 deletions apps/sim/app/api/copilot/api-keys/generate/route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { getSession } from '@/lib/auth'
import { SIM_AGENT_API_URL_DEFAULT } from '@/lib/copilot/constants'
import { SIM_AGENT_API_URL } from '@/lib/copilot/constants'
import { env } from '@/lib/core/config/env'

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

const userId = session.user.id

// Move environment variable access inside the function
const SIM_AGENT_API_URL = env.SIM_AGENT_API_URL || SIM_AGENT_API_URL_DEFAULT

const body = await req.json().catch(() => ({}))
const validationResult = GenerateApiKeySchema.safeParse(body)

Expand Down
1 change: 1 addition & 0 deletions apps/sim/app/api/copilot/api-keys/route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ describe('Copilot API Keys API Route', () => {

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

vi.doMock('@/lib/core/config/env', async () => {
Expand Down
6 changes: 1 addition & 5 deletions apps/sim/app/api/copilot/api-keys/route.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { SIM_AGENT_API_URL_DEFAULT } from '@/lib/copilot/constants'
import { SIM_AGENT_API_URL } from '@/lib/copilot/constants'
import { env } from '@/lib/core/config/env'

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

const userId = session.user.id

const SIM_AGENT_API_URL = env.SIM_AGENT_API_URL || SIM_AGENT_API_URL_DEFAULT

const res = await fetch(`${SIM_AGENT_API_URL}/api/validate-key/get-api-keys`, {
method: 'POST',
headers: {
Expand Down Expand Up @@ -68,8 +66,6 @@ export async function DELETE(request: NextRequest) {
return NextResponse.json({ error: 'id is required' }, { status: 400 })
}

const SIM_AGENT_API_URL = env.SIM_AGENT_API_URL || SIM_AGENT_API_URL_DEFAULT

const res = await fetch(`${SIM_AGENT_API_URL}/api/validate-key/delete`, {
method: 'POST',
headers: {
Expand Down
Loading