Skip to content

Commit 5448e7d

Browse files
authored
Add support for MCP scopes & tool permissions (#1266)
* Add scopes as part of the prisma schema * Add MCP OAuth scope discovery and selection * Refactor MCP scopes into configurable entries * Refactor MCP server tool data table * Add MCP server tool permissions * Cache MCP tool definitions in Redis * Auto-save MCP tool permission changes * Avoid expensive tool fetch after updating tool permissions. Just update react query cache instead * deduplicate some code for cleanliness * Squash prisma migrations. Update redis cache for mcp tools to be per user, 1h time out * update prisma migrations * Update mcpScope to be referred to as mcpOAuthScope across the codebase * Add documentation for scopes and permissions * Update docs to say Ask Sourcebot --------- Co-authored-by: Jack Minnetian <270441393+BlueBottleLatte@users.noreply.github.com>
1 parent e93d8d8 commit 5448e7d

50 files changed

Lines changed: 3268 additions & 213 deletions

Some content is hidden

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

docs/docs/features/ask/connectors.mdx

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ If Ask Sourcebot needs to use a tool that requires approval, it pauses and asks
3232
An owner must add a connector before organization members can use it.
3333

3434
To add a connector:
35-
1. **Settings → Workspace → Ask Agent**
35+
1. **Settings → Workspace → Ask Sourcebot**
3636
2. Click **Add connector**.
3737
3. Enter the MCP server URL and the name the organization will see for this server.
3838

@@ -50,9 +50,68 @@ then enter the OAuth client ID and client secret in Sourcebot.
5050
</Frame>
5151
</div>
5252

53+
## OAuth Scopes
54+
55+
Owners can configure which OAuth scopes users authorize when connecting to a connector.
56+
57+
Sourcebot checks the connector for discoverable scopes and shows them as options. You can also add custom scopes.
58+
59+
<div className="max-w-sm mx-auto">
60+
<Frame>
61+
<img
62+
src="/images/connectors_oauth_scopes.png"
63+
alt="OAuth scopes discovery"
64+
/>
65+
</Frame>
66+
</div>
67+
68+
Owners can change connector scopes at any time from **Settings → Workspace → Ask Sourcebot** and select **Edit OAuth scopes**.
69+
70+
<div className="max-w-xl mx-auto">
71+
<Frame>
72+
<img
73+
src="/images/connectors_edit_scopes.png"
74+
alt="OAuth scopes editing"
75+
/>
76+
</Frame>
77+
</div>
78+
79+
<Warning>
80+
Changing connector scopes requires all users to re-authenticate with that connector.
81+
</Warning>
82+
83+
## Tool Permissions
84+
85+
Owners can configure how Ask Sourcebot may use each tool exposed by a connector. Changes take effect immediately and do not require users to re-authenticate.
86+
87+
To edit tool permissions, open the connector menu from **Settings → Workspace → Ask Sourcebot** and select **Edit tool permissions**.
88+
89+
<div className="max-w-xl mx-auto">
90+
<Frame>
91+
<img
92+
src="/images/connectors_edit_tool_permissions.png"
93+
alt="Tools editing"
94+
/>
95+
</Frame>
96+
</div>
97+
98+
Each tool can be **Allowed**, require **Needs Approval**, or be **Blocked**. Allowed tools can run without pausing the chat, tools that need approval ask the user before running, and blocked tools are not available to Ask Sourcebot.
99+
100+
Sourcebot groups tools by the hints reported by the connector. Tools with a read-only hint are grouped separately and tools without that hint are treated as write/delete tools.
101+
102+
<div className="max-w-lg mx-auto">
103+
<Frame>
104+
<img
105+
src="/images/connectors_tool_permissions.png"
106+
alt="Tool permissions view"
107+
/>
108+
</Frame>
109+
</div>
110+
111+
53112
## Connecting
54113

55-
After an owner adds connectors for your organization, go to **Settings → Account → Ask Agent** to connect them.
114+
After an owner adds connectors for your organization, go to **Settings → Account → Ask Sourcebot** to connect them.
56115

57116
You can see all available connectors on this page. After you connect one, you can inspect the tools it provides and what each tool does.
58117

60.6 KB
Loading
54.9 KB
Loading
131 KB
Loading
256 KB
Loading

packages/backend/src/redis.ts

Lines changed: 2 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,3 @@
1-
import { env } from "@sourcebot/shared";
2-
import { Redis } from 'ioredis';
3-
import fs from "fs";
1+
import { createRedisClient } from "@sourcebot/shared";
42

5-
const buildTlsOptions = (): Record<string, unknown> => {
6-
if (env.REDIS_TLS_ENABLED !== "true" && !env.REDIS_URL.startsWith("rediss://")) {
7-
return {};
8-
}
9-
10-
return {
11-
tls: {
12-
ca: env.REDIS_TLS_CA_PATH
13-
? fs.readFileSync(env.REDIS_TLS_CA_PATH)
14-
: undefined,
15-
cert: env.REDIS_TLS_CERT_PATH
16-
? fs.readFileSync(env.REDIS_TLS_CERT_PATH)
17-
: undefined,
18-
key: env.REDIS_TLS_KEY_PATH
19-
? fs.readFileSync(env.REDIS_TLS_KEY_PATH)
20-
: undefined,
21-
...(env.REDIS_TLS_REJECT_UNAUTHORIZED
22-
? { rejectUnauthorized: env.REDIS_TLS_REJECT_UNAUTHORIZED === 'true' }
23-
: {}),
24-
...(env.REDIS_TLS_SERVERNAME
25-
? { servername: env.REDIS_TLS_SERVERNAME }
26-
: {}),
27-
...(env.REDIS_TLS_CHECK_SERVER_IDENTITY === "false"
28-
? { checkServerIdentity: () => undefined }
29-
: {}),
30-
...(env.REDIS_TLS_SECURE_PROTOCOL
31-
? { secureProtocol: env.REDIS_TLS_SECURE_PROTOCOL }
32-
: {}),
33-
...(env.REDIS_TLS_CIPHERS ? { ciphers: env.REDIS_TLS_CIPHERS } : {}),
34-
...(env.REDIS_TLS_HONOR_CIPHER_ORDER
35-
? {
36-
honorCipherOrder: env.REDIS_TLS_HONOR_CIPHER_ORDER === "true",
37-
}
38-
: {}),
39-
...(env.REDIS_TLS_KEY_PASSPHRASE
40-
? { passphrase: env.REDIS_TLS_KEY_PASSPHRASE }
41-
: {}),
42-
},
43-
};
44-
};
45-
46-
47-
export const redis = new Redis(env.REDIS_URL, {
48-
maxRetriesPerRequest: null,
49-
...buildTlsOptions(),
50-
});
3+
export const redis = createRedisClient();

packages/db/prisma/migrations/20260529214711_add_mcp_connectors_tables/migration.sql renamed to packages/db/prisma/migrations/20260603172622_add_mcp_connectors/migration.sql

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
-- CreateEnum
22
CREATE TYPE "McpServerClientInfoSource" AS ENUM ('DYNAMIC', 'STATIC');
33

4+
-- CreateEnum
5+
CREATE TYPE "McpServerToolPermission" AS ENUM ('ALLOWED', 'NEEDS_APPROVAL', 'DISABLED');
6+
47
-- CreateTable
58
CREATE TABLE "McpServer" (
69
"id" TEXT NOT NULL,
@@ -17,14 +20,26 @@ CREATE TABLE "McpServer" (
1720
);
1821

1922
-- CreateTable
20-
CREATE TABLE "McpServerToolCallCount" (
23+
CREATE TABLE "McpServerOAuthScope" (
24+
"mcpServerId" TEXT NOT NULL,
25+
"scope" TEXT NOT NULL,
26+
"enabled" BOOLEAN NOT NULL DEFAULT false,
27+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
28+
"updatedAt" TIMESTAMP(3) NOT NULL,
29+
30+
CONSTRAINT "McpServerOAuthScope_pkey" PRIMARY KEY ("mcpServerId","scope")
31+
);
32+
33+
-- CreateTable
34+
CREATE TABLE "McpServerTool" (
2135
"mcpServerId" TEXT NOT NULL,
2236
"toolName" TEXT NOT NULL,
23-
"count" INTEGER NOT NULL DEFAULT 0,
37+
"callCount" INTEGER NOT NULL DEFAULT 0,
38+
"permission" "McpServerToolPermission" NOT NULL DEFAULT 'NEEDS_APPROVAL',
2439
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
2540
"updatedAt" TIMESTAMP(3) NOT NULL,
2641

27-
CONSTRAINT "McpServerToolCallCount_pkey" PRIMARY KEY ("mcpServerId","toolName")
42+
CONSTRAINT "McpServerTool_pkey" PRIMARY KEY ("mcpServerId","toolName")
2843
);
2944

3045
-- CreateTable
@@ -57,7 +72,10 @@ CREATE INDEX "UserMcpServer_state_idx" ON "UserMcpServer"("state");
5772
ALTER TABLE "McpServer" ADD CONSTRAINT "McpServer_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "Org"("id") ON DELETE CASCADE ON UPDATE CASCADE;
5873

5974
-- AddForeignKey
60-
ALTER TABLE "McpServerToolCallCount" ADD CONSTRAINT "McpServerToolCallCount_mcpServerId_fkey" FOREIGN KEY ("mcpServerId") REFERENCES "McpServer"("id") ON DELETE CASCADE ON UPDATE CASCADE;
75+
ALTER TABLE "McpServerOAuthScope" ADD CONSTRAINT "McpServerOAuthScope_mcpServerId_fkey" FOREIGN KEY ("mcpServerId") REFERENCES "McpServer"("id") ON DELETE CASCADE ON UPDATE CASCADE;
76+
77+
-- AddForeignKey
78+
ALTER TABLE "McpServerTool" ADD CONSTRAINT "McpServerTool_mcpServerId_fkey" FOREIGN KEY ("mcpServerId") REFERENCES "McpServer"("id") ON DELETE CASCADE ON UPDATE CASCADE;
6179

6280
-- AddForeignKey
6381
ALTER TABLE "UserMcpServer" ADD CONSTRAINT "UserMcpServer_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;

packages/db/prisma/schema.prisma

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,12 @@ enum McpServerClientInfoSource {
347347
STATIC
348348
}
349349

350+
enum McpServerToolPermission {
351+
ALLOWED
352+
NEEDS_APPROVAL
353+
DISABLED
354+
}
355+
350356
model UserToOrg {
351357
joinedAt DateTime @default(now())
352358
@@ -684,7 +690,8 @@ model McpServer {
684690
orgId Int
685691
686692
userMcpServers UserMcpServer[]
687-
toolCallCounts McpServerToolCallCount[]
693+
tools McpServerTool[]
694+
oauthScopes McpServerOAuthScope[]
688695
689696
createdAt DateTime @default(now())
690697
updatedAt DateTime @updatedAt
@@ -693,12 +700,26 @@ model McpServer {
693700
@@unique([orgId, sanitizedName])
694701
}
695702

696-
/// Lifetime tool call counters for an MCP server.
697-
model McpServerToolCallCount {
703+
/// OAuth scope configuration for an MCP server.
704+
model McpServerOAuthScope {
705+
mcpServer McpServer @relation(fields: [mcpServerId], references: [id], onDelete: Cascade)
706+
mcpServerId String
707+
scope String
708+
enabled Boolean @default(false)
709+
710+
createdAt DateTime @default(now())
711+
updatedAt DateTime @updatedAt
712+
713+
@@id([mcpServerId, scope])
714+
}
715+
716+
/// Tool metadata for an MCP server.
717+
model McpServerTool {
698718
mcpServer McpServer @relation(fields: [mcpServerId], references: [id], onDelete: Cascade)
699719
mcpServerId String
700720
toolName String
701-
count Int @default(0)
721+
callCount Int @default(0)
722+
permission McpServerToolPermission @default(NEEDS_APPROVAL)
702723
703724
createdAt DateTime @default(now())
704725
updatedAt DateTime @updatedAt

packages/shared/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"@sourcebot/schemas": "workspace:*",
1919
"@t3-oss/env-core": "^0.13.10",
2020
"ajv": "^8.17.1",
21+
"ioredis": "^5.4.2",
2122
"micromatch": "^4.0.8",
2223
"strip-json-comments": "^5.0.1",
2324
"triple-beam": "^1.4.1",

packages/shared/src/index.server.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ export {
6565
export {
6666
getSMTPConnectionURL,
6767
} from "./smtp.js";
68+
export {
69+
createRedisClient,
70+
} from "./redis.js";
6871
export {
6972
SOURCEBOT_VERSION,
7073
} from "./version.js";
@@ -73,4 +76,4 @@ export {
7376
formatVersion,
7477
compareVersions,
7578
} from "./versionUtils.js";
76-
export type { Version } from "./versionUtils.js";
79+
export type { Version } from "./versionUtils.js";

0 commit comments

Comments
 (0)