Skip to content

Commit ce0510a

Browse files
Merge branch 'main' into brendan-kellam/add-idira-sso-SOU-1520
2 parents 891f696 + ddad4be commit ce0510a

10 files changed

Lines changed: 237 additions & 28 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111
- Added an optional `webUrl` field to the GitLab connection config, used to build links to repositories in the GitLab web UI when the API host differs from the browsable host. [#1457](https://github.com/sourcebot-dev/sourcebot/pull/1457)
1212
- [EE] Added Idira SSO support through OpenID Connect. [#1459](https://github.com/sourcebot-dev/sourcebot/pull/1459)
13+
- Added an optional `webUrl` field to the GitLab connection config, used to build links to repositories in the GitLab web UI when the API host differs from the browsable host. [#1458](https://github.com/sourcebot-dev/sourcebot/pull/1458)
1314

1415
### Fixed
1516
- Prevented focus rings in workspace connector dialogs from being clipped. [#1457](https://github.com/sourcebot-dev/sourcebot/pull/1457)

docs/docs/features/mcp-server.mdx

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ You can read more about the options in the [authorization](#authorization) secti
2727
If [anonymous access](https://docs.sourcebot.dev/docs/configuration/auth/access-settings#anonymous-access) is enabled on your Sourcebot instance, no OAuth token or API key is required. You can connect directly to the MCP endpoint without any authorization.
2828
</Note>
2929

30+
<Note>
31+
The examples below add the MCP server and connect it to Sourcebot's public deployment at [app.sourcebot.dev](https://app.sourcebot.dev). Replace the URL with your own Sourcebot deployment as needed.
32+
</Note>
33+
3034
<AccordionGroup>
3135
<Accordion title="Claude Code">
3236
[Claude Code MCP docs](https://code.claude.com/docs/en/mcp#connect-claude-code-to-tools-via-mcp)
@@ -38,18 +42,18 @@ You can read more about the options in the [authorization](#authorization) secti
3842
<LicenseKeyRequired feature="OAuth" />
3943

4044
```sh
41-
claude mcp add --transport http sourcebot https://sb.example.com/api/mcp
45+
claude mcp add --transport http sourcebot https://app.sourcebot.dev/api/mcp
4246
```
4347

44-
Replace `https://sb.example.com` with wherever your Sourcebot instance is hosted. Claude Code will automatically handle the OAuth 2.0 authorization flow the first time you connect.
48+
Replace `https://app.sourcebot.dev` with wherever your Sourcebot instance is hosted. Claude Code will automatically handle the OAuth 2.0 authorization flow the first time you connect.
4549
</Tab>
4650
<Tab title="API Key">
4751
```sh
48-
claude mcp add --transport http sourcebot https://sb.example.com/api/mcp \
52+
claude mcp add --transport http sourcebot https://app.sourcebot.dev/api/mcp \
4953
--header "Authorization: Bearer <key>"
5054
```
5155

52-
Replace `https://sb.example.com` with wherever your Sourcebot instance is hosted, and `<key>` with an API key generated in **Settings → API Keys**.
56+
Replace `https://app.sourcebot.dev` with wherever your Sourcebot instance is hosted, and `<key>` with an API key generated in **Settings → API Keys**.
5357
</Tab>
5458
</Tabs>
5559
</Accordion>
@@ -70,21 +74,21 @@ You can read more about the options in the [authorization](#authorization) secti
7074
"mcpServers": {
7175
"sourcebot": {
7276
"type": "http",
73-
"url": "https://sb.example.com/api/mcp"
77+
"url": "https://app.sourcebot.dev/api/mcp"
7478
}
7579
}
7680
}
7781
```
7882

79-
Replace `https://sb.example.com` with wherever your Sourcebot instance is hosted. Cursor will automatically handle the OAuth 2.0 authorization flow the first time you connect.
83+
Replace `https://app.sourcebot.dev` with wherever your Sourcebot instance is hosted. Cursor will automatically handle the OAuth 2.0 authorization flow the first time you connect.
8084
</Tab>
8185
<Tab title="API Key">
8286
```json
8387
{
8488
"mcpServers": {
8589
"sourcebot": {
8690
"type": "http",
87-
"url": "https://sb.example.com/api/mcp",
91+
"url": "https://app.sourcebot.dev/api/mcp",
8892
"headers": {
8993
"Authorization": "Bearer <key>"
9094
}
@@ -93,7 +97,7 @@ You can read more about the options in the [authorization](#authorization) secti
9397
}
9498
```
9599

96-
Replace `https://sb.example.com` with wherever your Sourcebot instance is hosted, and `<key>` with an API key generated in **Settings → API Keys**.
100+
Replace `https://app.sourcebot.dev` with wherever your Sourcebot instance is hosted, and `<key>` with an API key generated in **Settings → API Keys**.
97101
</Tab>
98102
</Tabs>
99103
</Accordion>
@@ -112,21 +116,21 @@ You can read more about the options in the [authorization](#authorization) secti
112116
"servers": {
113117
"sourcebot": {
114118
"type": "http",
115-
"url": "https://sb.example.com/api/mcp"
119+
"url": "https://app.sourcebot.dev/api/mcp"
116120
}
117121
}
118122
}
119123
```
120124

121-
Replace `https://sb.example.com` with wherever your Sourcebot instance is hosted. VS Code will automatically handle the OAuth 2.0 authorization flow the first time you connect.
125+
Replace `https://app.sourcebot.dev` with wherever your Sourcebot instance is hosted. VS Code will automatically handle the OAuth 2.0 authorization flow the first time you connect.
122126
</Tab>
123127
<Tab title="API Key">
124128
```json
125129
{
126130
"servers": {
127131
"sourcebot": {
128132
"type": "http",
129-
"url": "https://sb.example.com/api/mcp",
133+
"url": "https://app.sourcebot.dev/api/mcp",
130134
"headers": {
131135
"Authorization": "Bearer <key>"
132136
}
@@ -135,7 +139,7 @@ You can read more about the options in the [authorization](#authorization) secti
135139
}
136140
```
137141

138-
Replace `https://sb.example.com` with wherever your Sourcebot instance is hosted, and `<key>` with an API key generated in **Settings → API Keys**.
142+
Replace `https://app.sourcebot.dev` with wherever your Sourcebot instance is hosted, and `<key>` with an API key generated in **Settings → API Keys**.
139143
</Tab>
140144
</Tabs>
141145
</Accordion>
@@ -151,10 +155,10 @@ You can read more about the options in the [authorization](#authorization) secti
151155

152156
```toml
153157
[mcp_servers.sourcebot]
154-
url = "https://sb.example.com/api/mcp"
158+
url = "https://app.sourcebot.dev/api/mcp"
155159
```
156160

157-
Replace `https://sb.example.com` with wherever your Sourcebot instance is hosted. Then run the following to authenticate:
161+
Replace `https://app.sourcebot.dev` with wherever your Sourcebot instance is hosted. Then run the following to authenticate:
158162

159163
```sh
160164
codex mcp login sourcebot
@@ -163,11 +167,11 @@ You can read more about the options in the [authorization](#authorization) secti
163167
<Tab title="API Key">
164168
```toml
165169
[mcp_servers.sourcebot]
166-
url = "https://sb.example.com/api/mcp"
170+
url = "https://app.sourcebot.dev/api/mcp"
167171
bearer_token_env_var = "SOURCEBOT_API_KEY"
168172
```
169173

170-
Replace `https://sb.example.com` with wherever your Sourcebot instance is hosted, then set your API key as an environment variable:
174+
Replace `https://app.sourcebot.dev` with wherever your Sourcebot instance is hosted, then set your API key as an environment variable:
171175

172176
```sh
173177
export SOURCEBOT_API_KEY=<key>
@@ -193,13 +197,13 @@ You can read more about the options in the [authorization](#authorization) secti
193197
"mcp": {
194198
"sourcebot": {
195199
"type": "remote",
196-
"url": "https://sb.example.com/api/mcp"
200+
"url": "https://app.sourcebot.dev/api/mcp"
197201
}
198202
}
199203
}
200204
```
201205

202-
Replace `https://sb.example.com` with wherever your Sourcebot instance is hosted. OpenCode will automatically handle the OAuth 2.0 authorization flow the first time you connect. You can also manually trigger it with:
206+
Replace `https://app.sourcebot.dev` with wherever your Sourcebot instance is hosted. OpenCode will automatically handle the OAuth 2.0 authorization flow the first time you connect. You can also manually trigger it with:
203207

204208
```sh
205209
opencode mcp auth sourcebot
@@ -212,7 +216,7 @@ You can read more about the options in the [authorization](#authorization) secti
212216
"mcp": {
213217
"sourcebot": {
214218
"type": "remote",
215-
"url": "https://sb.example.com/api/mcp",
219+
"url": "https://app.sourcebot.dev/api/mcp",
216220
"oauth": false,
217221
"headers": {
218222
"Authorization": "Bearer <key>"
@@ -222,7 +226,7 @@ You can read more about the options in the [authorization](#authorization) secti
222226
}
223227
```
224228

225-
Replace `https://sb.example.com` with wherever your Sourcebot instance is hosted, and `<key>` with an API key generated in **Settings → API Keys**.
229+
Replace `https://app.sourcebot.dev` with wherever your Sourcebot instance is hosted, and `<key>` with an API key generated in **Settings → API Keys**.
226230
</Tab>
227231
</Tabs>
228232
</Accordion>
@@ -242,20 +246,20 @@ You can read more about the options in the [authorization](#authorization) secti
242246
{
243247
"mcpServers": {
244248
"sourcebot": {
245-
"serverUrl": "https://sb.example.com/api/mcp"
249+
"serverUrl": "https://app.sourcebot.dev/api/mcp"
246250
}
247251
}
248252
}
249253
```
250254

251-
Replace `https://sb.example.com` with wherever your Sourcebot instance is hosted. Windsurf will automatically handle the OAuth 2.0 authorization flow the first time you connect.
255+
Replace `https://app.sourcebot.dev` with wherever your Sourcebot instance is hosted. Windsurf will automatically handle the OAuth 2.0 authorization flow the first time you connect.
252256
</Tab>
253257
<Tab title="API Key">
254258
```json
255259
{
256260
"mcpServers": {
257261
"sourcebot": {
258-
"serverUrl": "https://sb.example.com/api/mcp",
262+
"serverUrl": "https://app.sourcebot.dev/api/mcp",
259263
"headers": {
260264
"Authorization": "Bearer <key>"
261265
}
@@ -264,7 +268,7 @@ You can read more about the options in the [authorization](#authorization) secti
264268
}
265269
```
266270

267-
Replace `https://sb.example.com` with wherever your Sourcebot instance is hosted, and `<key>` with an API key generated in **Settings → API Keys**.
271+
Replace `https://app.sourcebot.dev` with wherever your Sourcebot instance is hosted, and `<key>` with an API key generated in **Settings → API Keys**.
268272
</Tab>
269273
</Tabs>
270274
</Accordion>

packages/web/src/app/layout.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { PlanProvider } from "@/features/entitlements/planProvider";
2121
import { getEntitlements } from "@/lib/entitlements";
2222
import { IdentityProvidersProvider } from "@/features/auth/identityProvidersProvider";
2323
import { getIdentityProviderMetadata } from "@/lib/identityProviders";
24+
import { SentryUserProvider } from "./sentryUserProvider";
2425

2526
export const metadata: Metadata = {
2627
metadataBase: env.AUTH_URL ? new URL(env.AUTH_URL) : undefined,
@@ -74,6 +75,9 @@ export default async function RootLayout({
7475
<body>
7576
<Toaster />
7677
<SessionProvider>
78+
<SentryUserProvider
79+
isPiiEnabled={env.SOURCEBOT_TELEMETRY_PII_COLLECTION_ENABLED === 'true'}
80+
/>
7781
<PlanProvider entitlements={entitlements}>
7882
<IdentityProvidersProvider providers={identityProviders}>
7983
<PostHogProvider
@@ -106,4 +110,4 @@ export default async function RootLayout({
106110
</body>
107111
</html>
108112
);
109-
}
113+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { cleanup, render } from '@testing-library/react';
2+
import { afterEach, describe, expect, test, vi } from 'vitest';
3+
4+
const mocks = vi.hoisted(() => ({
5+
setSentryUser: vi.fn(),
6+
useSession: vi.fn(),
7+
}));
8+
9+
vi.mock('@/lib/sentryUser', () => ({
10+
setSentryUser: mocks.setSentryUser,
11+
}));
12+
13+
vi.mock('next-auth/react', () => ({
14+
useSession: mocks.useSession,
15+
}));
16+
17+
const { SentryUserProvider } = await import('./sentryUserProvider');
18+
19+
afterEach(() => {
20+
cleanup();
21+
vi.clearAllMocks();
22+
});
23+
24+
describe('SentryUserProvider', () => {
25+
test('waits for the session before changing the Sentry user', () => {
26+
mocks.useSession.mockReturnValue({ data: undefined, status: 'loading' });
27+
28+
render(<SentryUserProvider isPiiEnabled={false} />);
29+
30+
expect(mocks.setSentryUser).not.toHaveBeenCalled();
31+
});
32+
33+
test('sets and clears the Sentry user as authentication changes', () => {
34+
const user = {
35+
id: 'user-1',
36+
email: 'user@example.com',
37+
name: 'Example User',
38+
};
39+
mocks.useSession.mockReturnValue({
40+
data: { user },
41+
status: 'authenticated',
42+
});
43+
44+
const { rerender } = render(<SentryUserProvider isPiiEnabled={true} />);
45+
expect(mocks.setSentryUser).toHaveBeenLastCalledWith(user, true);
46+
47+
mocks.useSession.mockReturnValue({ data: null, status: 'unauthenticated' });
48+
rerender(<SentryUserProvider isPiiEnabled={true} />);
49+
50+
expect(mocks.setSentryUser).toHaveBeenLastCalledWith(null, true);
51+
});
52+
});
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
'use client';
2+
3+
import { setSentryUser } from '@/lib/sentryUser';
4+
import { useSession } from 'next-auth/react';
5+
import { useEffect } from 'react';
6+
7+
interface SentryUserProviderProps {
8+
isPiiEnabled: boolean;
9+
}
10+
11+
export function SentryUserProvider({ isPiiEnabled }: SentryUserProviderProps) {
12+
const { data: session, status } = useSession();
13+
14+
useEffect(() => {
15+
if (status === 'loading') {
16+
return;
17+
}
18+
19+
setSentryUser(session?.user ?? null, isPiiEnabled);
20+
}, [isPiiEnabled, session, status]);
21+
22+
return null;
23+
}

packages/web/src/auth.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { getAnonymousId } from '@/lib/anonymousId';
2222
import { captureEvent } from '@/lib/posthog';
2323
import { isEmailCodeLoginEnabled, isCredentialsLoginEnabled } from '@sourcebot/shared'
2424
import { onCreateUser } from './features/membership/onCreateUser';
25+
import { setSentryUser } from './lib/sentryUser';
2526

2627
export const runtime = 'nodejs';
2728

@@ -452,7 +453,12 @@ export const { handlers, signIn, signOut } = nextAuthResult;
452453
* without re-running the upstream resolver.
453454
*/
454455
export const auth = cache(async (): Promise<Session | null> => {
455-
return nextAuthResult.auth();
456+
const session = await nextAuthResult.auth();
457+
setSentryUser(
458+
session?.user ?? null,
459+
env.SOURCEBOT_TELEMETRY_PII_COLLECTION_ENABLED === 'true',
460+
);
461+
return session;
456462
});
457463

458464
/**
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { beforeEach, describe, expect, test, vi } from 'vitest';
2+
3+
const setUser = vi.fn();
4+
5+
vi.mock('@sentry/nextjs', () => ({
6+
setUser,
7+
}));
8+
9+
const { setSentryUser } = await import('./sentryUser');
10+
11+
describe('setSentryUser', () => {
12+
beforeEach(() => {
13+
setUser.mockClear();
14+
});
15+
16+
test('associates errors with the user id without PII by default', () => {
17+
setSentryUser({
18+
id: 'user-1',
19+
email: 'user@example.com',
20+
name: 'Example User',
21+
}, false);
22+
23+
expect(setUser).toHaveBeenCalledWith({ id: 'user-1' });
24+
});
25+
26+
test('includes user details when PII collection is enabled', () => {
27+
setSentryUser({
28+
id: 'user-1',
29+
email: 'user@example.com',
30+
name: 'Example User',
31+
}, true);
32+
33+
expect(setUser).toHaveBeenCalledWith({
34+
id: 'user-1',
35+
email: 'user@example.com',
36+
username: 'Example User',
37+
});
38+
});
39+
40+
test('clears the user for unauthenticated requests', () => {
41+
setSentryUser(null, true);
42+
43+
expect(setUser).toHaveBeenCalledWith(null);
44+
});
45+
});

0 commit comments

Comments
 (0)