From 07c190856c213c01cb20c1e7adf21cb05298fb14 Mon Sep 17 00:00:00 2001 From: Jack Minnetian <270441393+BlueBottleLatte@users.noreply.github.com> Date: Sat, 28 Mar 2026 13:29:29 -0700 Subject: [PATCH 01/40] Add ask MCP server integration --- .../migration.sql | 41 +++ .../migration.sql | 2 + .../migrations/20260326230727_/migration.sql | 24 ++ .../migration.sql | 2 + packages/db/prisma/schema.prisma | 74 +++++ packages/shared/src/env.server.ts | 1 + packages/web/package.json | 3 +- .../[owner]/[repo]/components/landingPage.tsx | 2 +- .../chat/[id]/components/chatThreadPanel.tsx | 9 +- .../chat/components/landingPageChatBox.tsx | 7 +- .../web/src/app/(app)/settings/layout.tsx | 14 +- .../settings/mcpServers/mcpServersPage.tsx | 285 ++++++++++++++++++ .../app/(app)/settings/mcpServers/page.tsx | 14 + packages/web/src/app/api/(client)/client.ts | 33 +- .../web/src/app/api/(server)/chat/route.ts | 5 +- .../api/(server)/ee/askmcp/callback/route.ts | 122 ++++++++ .../api/(server)/ee/askmcp/connect/route.ts | 77 +++++ .../api/(server)/ee/askmcp/connect/types.ts | 4 + .../api/(server)/ee/askmcp/servers/route.ts | 91 ++++++ packages/web/src/ee/features/mcp/actions.ts | 136 +++++++++ .../mcp/components/connectMcpButton.tsx | 58 ++++ .../ee/features/mcp/components/mcpFavicon.tsx | 24 ++ .../ee/features/mcp/mcpClientFactory.test.ts | 132 ++++++++ .../src/ee/features/mcp/mcpClientFactory.ts | 105 +++++++ .../ee/features/mcp/mcpToolRegistry.test.ts | 185 ++++++++++++ .../src/ee/features/mcp/mcpToolRegistry.ts | 99 ++++++ .../src/ee/features/mcp/mcpToolSets.test.ts | 284 +++++++++++++++++ .../web/src/ee/features/mcp/mcpToolSets.ts | 149 +++++++++ .../web/src/ee/features/mcp/utils.test.ts | 36 +++ packages/web/src/ee/features/mcp/utils.ts | 11 + packages/web/src/features/chat/agent.ts | 260 +++++++++++++--- .../components/chatBox/chatBoxPlusButton.tsx | 151 ++++++++++ .../components/chatBox/chatBoxToolbar.tsx | 16 + .../components/chatBox/plusButtonInfoCard.tsx | 15 + .../chat/components/chatThread/chatThread.tsx | 106 ++++++- .../chatThread/chatThreadListItem.tsx | 21 +- .../components/chatThread/detailsCard.tsx | 20 +- .../chatThread/mcpFailedServersBanner.tsx | 43 +++ .../chatThread/toolApprovalBanner.tsx | 101 +++++++ .../chatThread/tools/jsonHighlighter.tsx | 151 ++++++++++ .../chatThread/tools/mcpToolComponent.tsx | 173 +++++++++++ .../chatThread/tools/toolOutputGuard.tsx | 13 +- .../tools/toolSearchToolComponent.tsx | 53 ++++ packages/web/src/features/chat/constants.ts | 1 + .../features/chat/mcpServerIconContext.tsx | 10 + .../src/features/chat/toolApprovalContext.tsx | 9 + packages/web/src/features/chat/types.test.ts | 72 +++++ packages/web/src/features/chat/types.ts | 15 +- .../features/chat/useCreateNewChatThread.ts | 21 +- packages/web/src/features/chat/utils.test.ts | 32 +- packages/web/src/features/chat/utils.ts | 3 +- .../features/mcp/prismaOAuthClientProvider.ts | 168 +++++++++++ packages/web/src/lib/errorCodes.ts | 2 + yarn.lock | 49 ++- 54 files changed, 3451 insertions(+), 83 deletions(-) create mode 100644 packages/db/prisma/migrations/20260324182442_support_mcp_clients/migration.sql create mode 100644 packages/db/prisma/migrations/20260325184501_add_mcp_server_credential_state_index/migration.sql create mode 100644 packages/db/prisma/migrations/20260326230727_/migration.sql create mode 100644 packages/db/prisma/migrations/20260327233318_add_tokens_expires_at/migration.sql create mode 100644 packages/web/src/app/(app)/settings/mcpServers/mcpServersPage.tsx create mode 100644 packages/web/src/app/(app)/settings/mcpServers/page.tsx create mode 100644 packages/web/src/app/api/(server)/ee/askmcp/callback/route.ts create mode 100644 packages/web/src/app/api/(server)/ee/askmcp/connect/route.ts create mode 100644 packages/web/src/app/api/(server)/ee/askmcp/connect/types.ts create mode 100644 packages/web/src/app/api/(server)/ee/askmcp/servers/route.ts create mode 100644 packages/web/src/ee/features/mcp/actions.ts create mode 100644 packages/web/src/ee/features/mcp/components/connectMcpButton.tsx create mode 100644 packages/web/src/ee/features/mcp/components/mcpFavicon.tsx create mode 100644 packages/web/src/ee/features/mcp/mcpClientFactory.test.ts create mode 100644 packages/web/src/ee/features/mcp/mcpClientFactory.ts create mode 100644 packages/web/src/ee/features/mcp/mcpToolRegistry.test.ts create mode 100644 packages/web/src/ee/features/mcp/mcpToolRegistry.ts create mode 100644 packages/web/src/ee/features/mcp/mcpToolSets.test.ts create mode 100644 packages/web/src/ee/features/mcp/mcpToolSets.ts create mode 100644 packages/web/src/ee/features/mcp/utils.test.ts create mode 100644 packages/web/src/ee/features/mcp/utils.ts create mode 100644 packages/web/src/features/chat/components/chatBox/chatBoxPlusButton.tsx create mode 100644 packages/web/src/features/chat/components/chatBox/plusButtonInfoCard.tsx create mode 100644 packages/web/src/features/chat/components/chatThread/mcpFailedServersBanner.tsx create mode 100644 packages/web/src/features/chat/components/chatThread/toolApprovalBanner.tsx create mode 100644 packages/web/src/features/chat/components/chatThread/tools/jsonHighlighter.tsx create mode 100644 packages/web/src/features/chat/components/chatThread/tools/mcpToolComponent.tsx create mode 100644 packages/web/src/features/chat/components/chatThread/tools/toolSearchToolComponent.tsx create mode 100644 packages/web/src/features/chat/mcpServerIconContext.tsx create mode 100644 packages/web/src/features/chat/toolApprovalContext.tsx create mode 100644 packages/web/src/features/chat/types.test.ts create mode 100644 packages/web/src/features/mcp/prismaOAuthClientProvider.ts diff --git a/packages/db/prisma/migrations/20260324182442_support_mcp_clients/migration.sql b/packages/db/prisma/migrations/20260324182442_support_mcp_clients/migration.sql new file mode 100644 index 000000000..3d3d9966f --- /dev/null +++ b/packages/db/prisma/migrations/20260324182442_support_mcp_clients/migration.sql @@ -0,0 +1,41 @@ +-- CreateTable +CREATE TABLE "McpServer" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "serverUrl" TEXT NOT NULL, + "clientInfo" TEXT, + "orgId" INTEGER NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "McpServer_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "McpServerCredential" ( + "id" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "serverId" TEXT NOT NULL, + "tokens" TEXT, + "codeVerifier" TEXT, + "state" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "McpServerCredential_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "McpServer_serverUrl_orgId_key" ON "McpServer"("serverUrl", "orgId"); + +-- CreateIndex +CREATE UNIQUE INDEX "McpServerCredential_userId_serverId_key" ON "McpServerCredential"("userId", "serverId"); + +-- AddForeignKey +ALTER TABLE "McpServer" ADD CONSTRAINT "McpServer_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "Org"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "McpServerCredential" ADD CONSTRAINT "McpServerCredential_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "McpServerCredential" ADD CONSTRAINT "McpServerCredential_serverId_fkey" FOREIGN KEY ("serverId") REFERENCES "McpServer"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/db/prisma/migrations/20260325184501_add_mcp_server_credential_state_index/migration.sql b/packages/db/prisma/migrations/20260325184501_add_mcp_server_credential_state_index/migration.sql new file mode 100644 index 000000000..d14625836 --- /dev/null +++ b/packages/db/prisma/migrations/20260325184501_add_mcp_server_credential_state_index/migration.sql @@ -0,0 +1,2 @@ +-- CreateIndex +CREATE INDEX "McpServerCredential_state_idx" ON "McpServerCredential"("state"); diff --git a/packages/db/prisma/migrations/20260326230727_/migration.sql b/packages/db/prisma/migrations/20260326230727_/migration.sql new file mode 100644 index 000000000..b17ca3d7e --- /dev/null +++ b/packages/db/prisma/migrations/20260326230727_/migration.sql @@ -0,0 +1,24 @@ +/* + Warnings: + + - You are about to drop the column `name` on the `McpServer` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "McpServer" DROP COLUMN "name"; + +-- CreateTable +CREATE TABLE "UserMcpServer" ( + "userId" TEXT NOT NULL, + "serverId" TEXT NOT NULL, + "name" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "UserMcpServer_pkey" PRIMARY KEY ("userId","serverId") +); + +-- AddForeignKey +ALTER TABLE "UserMcpServer" ADD CONSTRAINT "UserMcpServer_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "UserMcpServer" ADD CONSTRAINT "UserMcpServer_serverId_fkey" FOREIGN KEY ("serverId") REFERENCES "McpServer"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/db/prisma/migrations/20260327233318_add_tokens_expires_at/migration.sql b/packages/db/prisma/migrations/20260327233318_add_tokens_expires_at/migration.sql new file mode 100644 index 000000000..26f316ab1 --- /dev/null +++ b/packages/db/prisma/migrations/20260327233318_add_tokens_expires_at/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "McpServerCredential" ADD COLUMN "tokensExpiresAt" TIMESTAMP(3); diff --git a/packages/db/prisma/schema.prisma b/packages/db/prisma/schema.prisma index 0318f941d..42756b9fe 100644 --- a/packages/db/prisma/schema.prisma +++ b/packages/db/prisma/schema.prisma @@ -294,6 +294,8 @@ model Org { chats Chat[] repoVisits RepoVisit[] + mcpServers McpServer[] + license License? } @@ -408,6 +410,9 @@ model User { /// claim baked into the JWT cookie at mint time. sessionVersion Int @default(0) + mcpServerCredentials McpServerCredential[] + userMcpServers UserMcpServer[] + createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@ -642,3 +647,72 @@ model ChangelogEntry { @@index([publishedAt]) } + +/// An external MCP server endpoint, unique per org. +/// Stores the dynamic client registration (client_id/client_secret) once per org. +model McpServer { + id String @id @default(cuid()) + serverUrl String /// MCP server endpoint (e.g., "https://mcp.linear.app/mcp") + + /// Dynamic client registration result (RFC 7591). + /// Encrypted JSON of OAuthClientInformation: { client_id, client_secret, client_id_issued_at, client_secret_expires_at } + /// Null until first user in the org triggers registration. + clientInfo String? + + org Org @relation(fields: [orgId], references: [id], onDelete: Cascade) + orgId Int + + credentials McpServerCredential[] + userMcpServers UserMcpServer[] + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@unique([serverUrl, orgId]) +} + +/// Junction table: a user's personal reference to an MCP server with their chosen display name. +model UserMcpServer { + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + userId String + + server McpServer @relation(fields: [serverId], references: [id], onDelete: Cascade) + serverId String + + name String /// User-chosen display name (e.g., "Linear") + + createdAt DateTime @default(now()) + + @@id([userId, serverId]) +} + +/// Per-user OAuth credentials for an external MCP server. +/// Stores tokens (long-lived) and ephemeral auth-flow state separately. +model McpServerCredential { + id String @id @default(cuid()) + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + userId String + + server McpServer @relation(fields: [serverId], references: [id], onDelete: Cascade) + serverId String + + /// OAuth tokens (access_token, refresh_token, etc.) — encrypted JSON of OAuthTokens. + tokens String? + + /// Absolute expiry time of the access token, computed at issuance from expires_in. + /// Null when no tokens are stored or the provider did not include expires_in. + tokensExpiresAt DateTime? + + /// PKCE code verifier — ephemeral, only used between redirect and callback. + codeVerifier String? + + /// OAuth state parameter — ephemeral, for CSRF protection during auth flow. + state String? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@unique([userId, serverId]) + @@index([state]) +} diff --git a/packages/shared/src/env.server.ts b/packages/shared/src/env.server.ts index d9ee5cae3..4be0d4c32 100644 --- a/packages/shared/src/env.server.ts +++ b/packages/shared/src/env.server.ts @@ -282,6 +282,7 @@ const options = { */ SOURCEBOT_CHAT_MODEL_TEMPERATURE: numberSchema.optional(), SOURCEBOT_CHAT_MAX_STEP_COUNT: numberSchema.default(100), + SOURCEBOT_MCP_TOOL_CALL_TIMEOUT_MS: numberSchema.default(60000), DEBUG_WRITE_CHAT_MESSAGES_TO_FILE: booleanSchema.default('false'), DEBUG_ENABLE_REACT_SCAN: booleanSchema.default('false'), diff --git a/packages/web/package.json b/packages/web/package.json index dfed8625f..cd06162c7 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -20,6 +20,7 @@ "@ai-sdk/deepseek": "^2.0.29", "@ai-sdk/google": "^3.0.64", "@ai-sdk/google-vertex": "^4.0.111", + "@ai-sdk/mcp": "^2.0.0-beta.11", "@ai-sdk/mistral": "^3.0.30", "@ai-sdk/openai": "^3.0.53", "@ai-sdk/openai-compatible": "^2.0.41", @@ -196,7 +197,7 @@ "use-stick-to-bottom": "^1.1.3", "usehooks-ts": "^3.1.0", "vscode-icons-js": "^11.6.1", - "zod": "^3.25.74", + "zod": "^3.25.76", "zod-to-json-schema": "^3.24.5" }, "devDependencies": { diff --git a/packages/web/src/app/(app)/askgh/[owner]/[repo]/components/landingPage.tsx b/packages/web/src/app/(app)/askgh/[owner]/[repo]/components/landingPage.tsx index 5a8a92abc..fe50bc5ca 100644 --- a/packages/web/src/app/(app)/askgh/[owner]/[repo]/components/landingPage.tsx +++ b/packages/web/src/app/(app)/askgh/[owner]/[repo]/components/landingPage.tsx @@ -68,7 +68,7 @@ export const LandingPage = ({
+ Connect external MCP servers to use with Ask Sourcebot. +
+No MCP servers yet
++ Click "Add MCP Server" above to connect an external MCP server. +
+Error: No answer response was provided
)} diff --git a/packages/web/src/features/chat/components/chatThread/detailsCard.tsx b/packages/web/src/features/chat/components/chatThread/detailsCard.tsx index 0e2365ea6..5997df6e7 100644 --- a/packages/web/src/features/chat/components/chatThread/detailsCard.tsx +++ b/packages/web/src/features/chat/components/chatThread/detailsCard.tsx @@ -25,6 +25,8 @@ import { ListReposToolComponent } from './tools/listReposToolComponent'; import { ListTreeToolComponent } from './tools/listTreeToolComponent'; import { ReadFileToolComponent } from './tools/readFileToolComponent'; import { ToolOutputGuard } from './tools/toolOutputGuard'; +import { McpToolComponent } from './tools/mcpToolComponent'; +import { ToolSearchToolComponent } from './tools/toolSearchToolComponent'; interface DetailsCardProps { @@ -48,7 +50,10 @@ const DetailsCardComponent = ({ }: DetailsCardProps) => { const captureEvent = useCaptureEvent(); - const toolCallCount = useMemo(() => thinkingSteps.flat().filter(part => part.type.startsWith('tool-')).length, [thinkingSteps]); + const toolCallCount = useMemo(() => thinkingSteps.flat().filter(part => + part.type.startsWith('tool-') || + (part.type === 'dynamic-tool' && part.toolName.startsWith('mcp_')) + ).length, [thinkingSteps]); const handleExpandedChanged = useCallback((next: boolean) => { captureEvent('wa_chat_details_card_toggled', { chatId, isExpanded: next }); @@ -308,8 +313,19 @@ export const StepPartRenderer = ({ part }: { part: SBChatMessagePart }) => { {(output) =>
+ {tokens.map((token, i) => {
+ const cls = TOKEN_CLASSES[token.type];
+ if (!cls) {
+ return token.value;
+ }
+ return (
+
+ {token.value}
+
+ );
+ })}
+
+ );
+};
diff --git a/packages/web/src/features/chat/components/chatThread/tools/mcpToolComponent.tsx b/packages/web/src/features/chat/components/chatThread/tools/mcpToolComponent.tsx
new file mode 100644
index 000000000..3e679a21b
--- /dev/null
+++ b/packages/web/src/features/chat/components/chatThread/tools/mcpToolComponent.tsx
@@ -0,0 +1,173 @@
+'use client';
+
+import { CopyIconButton } from "@/app/(app)/components/copyIconButton";
+import { McpFavicon } from "@/ee/features/mcp/components/mcpFavicon";
+import { useMcpServerIconMap } from "@/features/chat/mcpServerIconContext";
+import { cn } from "@/lib/utils";
+import { DynamicToolUIPart } from "ai";
+import { CheckCircle, ChevronDown, XCircle } from "lucide-react";
+import { useCallback, useMemo, useState } from "react";
+import { JsonHighlighter, unescapeJsonStrings } from "./jsonHighlighter";
+
+export function parseMcpToolName(toolName: string): { serverName: string; toolName: string } | null {
+ if (!toolName.startsWith('mcp_')) {
+ return null;
+ }
+ const withoutPrefix = toolName.slice(4);
+ const doubleUnderscoreIdx = withoutPrefix.indexOf('__');
+ if (doubleUnderscoreIdx === -1) {
+ return null;
+ }
+ return {
+ serverName: withoutPrefix.slice(0, doubleUnderscoreIdx),
+ toolName: withoutPrefix.slice(doubleUnderscoreIdx + 2),
+ };
+}
+
+export const McpToolComponent = ({ part }: { part: DynamicToolUIPart }) => {
+ const needsApproval = part.state === 'approval-requested';
+ const [isExpanded, setIsExpanded] = useState(needsApproval);
+ const onToggle = useCallback(() => setIsExpanded(v => !v), []);
+
+ const iconMap = useMcpServerIconMap();
+ const parsed = parseMcpToolName(part.toolName);
+ const displayName = parsed
+ ? `${parsed.serverName}: ${parsed.toolName}`
+ : part.toolName;
+ const faviconUrl = parsed ? iconMap[parsed.serverName] : undefined;
+
+ const hasInput = part.state !== 'input-streaming';
+
+ const requestText = useMemo(
+ () => hasInput ? JSON.stringify(part.input, null, 2) : '',
+ [hasInput, part.input]
+ );
+ const responseText = useMemo(() => {
+ if (part.state === 'output-available') {
+ try {
+ return JSON.stringify(unescapeJsonStrings(part.output), null, 2);
+ } catch {
+ return String(part.output);
+ }
+ }
+ if (part.state === 'output-error') {
+ return part.errorText ?? '';
+ }
+ return undefined;
+ }, [part.state, part.output, part.errorText]);
+
+ const onCopyRequest = useCallback(() => {
+ navigator.clipboard.writeText(requestText);
+ return true;
+ }, [requestText]);
+
+ const onCopyResponse = useCallback(() => {
+ if (!responseText) {
+ return false;
+ }
+ navigator.clipboard.writeText(responseText);
+ return true;
+ }, [responseText]);
+
+ const renderStatus = () => {
+ if (part.state === 'output-error') {
+ return (
+
+
- {requestText}
-
+
- {responseText}
-
+ - Connect external MCP servers to use with Ask Sourcebot. + {canManageMcpServers + ? "Approve external MCP servers for your workspace." + : "Connect to workspace-approved MCP servers to use them with Ask Sourcebot."}
No MCP servers yet
- Click "Add MCP Server" above to connect an external MCP server. + {canManageMcpServers + ? "Add an MCP server above to make it available to workspace members." + : "No MCP servers have been approved for this workspace yet."}
@@ -218,35 +225,37 @@ export function McpServersPage({ callbackStatus, callbackServer, callbackMessage+ Configure the MCP servers that workspace members can connect to. +
+Saved MCP connections
++ Current workspace members with saved MCP server credentials. +
++ {totalSavedConnectionCount} {pluralize(totalSavedConnectionCount, "connection")} +
+ )} +Allowed MCP servers
++ Sourcebot Ask can use only workspace-approved MCP servers. +
+Only approved servers
+No MCP servers configured yet
++ Add a workspace-approved MCP server so members can connect it to Ask Sourcebot. +
+{server.name || server.serverUrl}
+{server.serverUrl}
++ {server.savedConnectionCount} {pluralize(server.savedConnectionCount, "saved connection")} +
++ Use Sourcebot API keys for MCP access on this deployment. +
++ {canManageMcpServers ? "No MCP servers configured yet" : "No MCP servers available"} +
++ {canManageMcpServers + ? "Go to Workspace MCP Configuration to add servers before connecting them to Ask Sourcebot." + : "No MCP servers have been approved for this workspace yet. Contact your workspace admin."} +
+ {canManageMcpServers && ( +- {canManageMcpServers - ? "Approve external MCP servers for your workspace." - : "Connect to workspace-approved MCP servers to use them with Ask Sourcebot."} -
-+ Connect to workspace-approved MCP servers to use them with Ask Sourcebot. +
No MCP servers yet
-- {canManageMcpServers - ? "Add an MCP server above to make it available to workspace members." - : "No MCP servers have been approved for this workspace yet."} -
-OAuth MCP is unavailable
++ You can remove existing approved servers and stored credentials, but cannot add new MCP servers. +
+Allowed MCP servers
- Sourcebot Ask can use only workspace-approved MCP servers. + {isOAuthUnavailable + ? "Existing workspace-approved MCP servers are available for cleanup." + : "Sourcebot Ask can use only workspace-approved MCP servers."}
Only approved servers
@@ -141,47 +159,57 @@ export function McpConfigurationPage() {No MCP servers configured yet
- Add a workspace-approved MCP server so members can connect it to Ask Sourcebot. + {isOAuthUnavailable + ? "OAuth MCP is unavailable on this Sourcebot instance." + : "Add a workspace-approved MCP server so members can connect it to Ask Sourcebot."}
{pendingClientCredentialsServer.name}
+{pendingClientCredentialsServer.serverUrl}
+{server.name}
+{getDisplayServerUrl(server.serverUrl)}
+{server.name}
diff --git a/packages/web/src/app/api/(server)/ee/askmcp/configuration/route.ts b/packages/web/src/app/api/(server)/ee/askmcp/configuration/route.ts index 9a3901108..303418a82 100644 --- a/packages/web/src/app/api/(server)/ee/askmcp/configuration/route.ts +++ b/packages/web/src/app/api/(server)/ee/askmcp/configuration/route.ts @@ -51,7 +51,7 @@ export const GET = apiHandler(async (_request: NextRequest) => { const savedConnectionCount = countByServerId.get(server.id) ?? 0; return { ...server, - faviconUrl: getMcpFaviconUrl(server.serverUrl), + faviconUrl: getMcpFaviconUrl(server.serverUrl, server.name), savedConnectionCount, }; }); diff --git a/packages/web/src/app/api/(server)/ee/askmcp/servers/route.ts b/packages/web/src/app/api/(server)/ee/askmcp/servers/route.ts index aaaa005cd..8fe277379 100644 --- a/packages/web/src/app/api/(server)/ee/askmcp/servers/route.ts +++ b/packages/web/src/app/api/(server)/ee/askmcp/servers/route.ts @@ -53,7 +53,7 @@ export const GET = apiHandler(async (_request: NextRequest) => { return orgServers.map((server): McpServerWithStatus => { const userServer = userServerByServerId.get(server.id); - const faviconUrl = getMcpFaviconUrl(server.serverUrl); + const faviconUrl = getMcpFaviconUrl(server.serverUrl, server.name); let isConnected = false; let isAuthExpired = false; diff --git a/packages/web/src/ee/features/mcp/mcpToolSets.ts b/packages/web/src/ee/features/mcp/mcpToolSets.ts index 3772d307a..febae502c 100644 --- a/packages/web/src/ee/features/mcp/mcpToolSets.ts +++ b/packages/web/src/ee/features/mcp/mcpToolSets.ts @@ -5,6 +5,7 @@ import Ajv from 'ajv'; import { jsonSchema, ToolExecutionOptions } from 'ai'; import type { JSONSchema7, JSONSchema7Definition } from 'json-schema'; import { getExternalMcpErrorLogFields } from './externalMcpError'; +import { getMcpFaviconUrl } from './utils'; const logger = createLogger('mcp-tool-sets'); const ajv = new Ajv({ allErrors: true, strict: false }); @@ -124,8 +125,10 @@ export async function getMcpTools(clients: McpToolSet[]): PromiseAllowed MCP servers
-- {isOAuthUnavailable - ? "Existing workspace-approved MCP servers are available for cleanup." - : "Sourcebot Ask can use only workspace-approved MCP servers."} -
-Only approved servers
-+ Connect to workspace-approved MCP servers to use them with Ask Sourcebot. +
++ Connected +
++ {connectedServers.length} {pluralize(connectedServers.length, "server")} +
++ {searchQuery.trim() + ? "No connected servers match your search." + : "No servers connected yet."} +
++ {server.name || server.serverUrl} +
++ {displayUrl(server.serverUrl)} +
++ Suggested +
++ workspace-approved +
++ {searchQuery.trim() + ? "No suggested servers match your search." + : "All servers are connected."} +
++ {server.name || server.serverUrl} +
++ {displayUrl(server.serverUrl)} +
+- {canManageMcpServers ? "No MCP servers configured yet" : "No MCP servers available"} + {canManageConnectors ? "No connectors configured yet" : "No connectors available"}
- {canManageMcpServers - ? "Go to Workspace MCP Configuration to add servers before connecting them to Ask Sourcebot." - : "No MCP servers have been approved for this workspace yet. Contact your workspace admin."} + {canManageConnectors + ? "Open Workspace Ask Agent to approve connectors for your workspace." + : "No connectors have been approved for this workspace yet. Contact your workspace admin."}
- {canManageMcpServers && ( + {canManageConnectors && (- Connect to workspace-approved MCP servers to use them with Ask Sourcebot. + Manage your personal Ask Agent setup.
+ Manage workspace-approved connectors for use with Ask Agent. +
+- Connect to workspace-approved MCP servers to use them with Ask Sourcebot. + Manage your personal Ask Agent setup.
+ Manage workspace-approved connectors for use with Ask Agent. +
- {connectedServers.length} {pluralize(connectedServers.length, "server")} + {connectedServers.length} {pluralize(connectedServers.length, "connector")}
{searchQuery.trim() - ? "No connected servers match your search." - : "No servers connected yet."} + ? "No connected connectors match your search." + : "No connectors connected yet."}
{searchQuery.trim() - ? "No suggested servers match your search." - : "All servers are connected."} + ? "No suggested connectors match your search." + : "All connectors are connected."}
- Configure the MCP servers that workspace members can connect to. -
-OAuth MCP is unavailable
-- You can remove existing approved servers and stored credentials, but cannot add new MCP servers. -
-Saved MCP connections
-- Current workspace members with saved MCP server credentials. -
-- {totalSavedConnectionCount} {pluralize(totalSavedConnectionCount, "connection")} -
- )} -{pendingClientCredentialsServer.name}
-{pendingClientCredentialsServer.serverUrl}
-No MCP servers configured yet
-- {isOAuthUnavailable - ? "OAuth MCP is unavailable on this Sourcebot instance." - : "Add a workspace-approved MCP server so members can connect it to Ask Sourcebot."} -
-{server.name || server.serverUrl}
-{server.serverUrl}
-- {server.savedConnectionCount} {pluralize(server.savedConnectionCount, "saved connection")} -
-+ Customize Ask Agent for your workspace. +
+Connector OAuth is unavailable
++ You can remove existing approved connectors and stored credentials, but cannot add new connectors. +
++ Connectors are MCP servers that let Ask Agent use approved external tools. +
+Saved connector connections
++ Current workspace members with saved connector credentials. +
++ {totalSavedConnectionCount} {pluralize(totalSavedConnectionCount, "connection")} +
+ )} +{pendingClientCredentialsServer.name}
+{pendingClientCredentialsServer.serverUrl}
+No connectors configured yet
++ {isOAuthUnavailable + ? "Connector OAuth is unavailable on this Sourcebot instance." + : "Add a workspace-approved connector so members can use it with Ask Agent."} +
+{server.name || server.serverUrl}
+{server.serverUrl}
++ {server.savedConnectionCount} {pluralize(server.savedConnectionCount, "saved connection")} +
+- Use Sourcebot API keys for MCP access on this deployment. + Use Sourcebot API keys for agent access on this deployment.
- {server.name || server.serverUrl} -
-- {displayUrl(server.serverUrl)} -
+- {server.name || server.serverUrl} -
-- {displayUrl(server.serverUrl)} -
-- Customize Ask Agent for your workspace. +
+ Configure what external tools Ask Agent can use across this workspace.
Connector OAuth is unavailable
-- You can remove existing approved connectors and stored credentials, but cannot add new connectors. -
-Connector OAuth is unavailable
++ You can remove existing approved connectors and stored credentials, but cannot add new connectors. +
+- Connectors are MCP servers that let Ask Agent use approved external tools. +
+ Connectors are MCP servers that let Ask Agent use approved external tools alongside your indexed code.
Saved connector connections
-- Current workspace members with saved connector credentials. -
-Allowed Connectors
{isLoading ? ( -- {totalSavedConnectionCount} {pluralize(totalSavedConnectionCount, "connection")} -
+{servers.length}
)} -approved for workspace
+ + +Saved Connections
+ {isLoading ? ( +{totalSavedConnectionCount}
+ )} ++ {totalSavedConnectionCount === 1 ? "member has" : "members have"} credentials saved +
+Active in Last 7d
+—
+tool calls
+{isOAuthUnavailable ? "Remove existing connector approvals and their stored credentials." : "Approve connector URLs that workspace members can connect to."} -
{pendingClientCredentialsServer.name}
-{pendingClientCredentialsServer.serverUrl}
-No connectors configured yet
-- {isOAuthUnavailable - ? "Connector OAuth is unavailable on this Sourcebot instance." - : "Add a workspace-approved connector so members can use it with Ask Agent."} -
-No connectors configured yet
++ {isOAuthUnavailable + ? "Connector OAuth is unavailable on this Sourcebot instance." + : "Add a workspace-approved connector so members can use it with Ask Agent."} +
+{server.name || server.serverUrl}
-{server.serverUrl}
-- {server.savedConnectionCount} {pluralize(server.savedConnectionCount, "saved connection")} -
-{pendingClientCredentialsServer.name}
+{pendingClientCredentialsServer.serverUrl}
++ {name || serverUrl} +
++ {getDisplayServerUrl(serverUrl)} +
+ {children} +Connector OAuth is unavailable
++ {canManageConnectors + ? "Open Workspace Ask Agent to remove existing connector approvals and stored credentials." + : "Connector setup is unavailable on this Sourcebot instance."} +
+ {canManageConnectors && ( ++ Manage your personal Ask Agent setup. +
++ Manage workspace-approved connectors for use with Ask Agent. +
+No tools exposed by this connector.
+ ) : ( +{tool.description}
+ )} +{tool.description}
+ )} +No tools exposed by this connector.
) : ( -{tool.description}
- )} -Error: No answer response was provided
)}No thinking steps
@@ -336,4 +345,4 @@ export const StepPartRenderer = ({ part }: { part: SBChatMessagePart }) => { part satisfies never; return null; } -} \ No newline at end of file +} diff --git a/packages/web/src/features/chat/components/chatThread/signInPromptBanner.tsx b/packages/web/src/features/chat/components/chatThread/signInPromptBanner.tsx index 1d1ba36fb..ec4d690e1 100644 --- a/packages/web/src/features/chat/components/chatThread/signInPromptBanner.tsx +++ b/packages/web/src/features/chat/components/chatThread/signInPromptBanner.tsx @@ -14,7 +14,7 @@ interface SignInPromptBannerProps { isAuthenticated: boolean; isOwner: boolean; hasMessages: boolean; - isStreaming: boolean; + isTurnInProgress: boolean; } export const SignInPromptBanner = ({ @@ -22,7 +22,7 @@ export const SignInPromptBanner = ({ isAuthenticated, isOwner, hasMessages, - isStreaming, + isTurnInProgress, }: SignInPromptBannerProps) => { const pathname = usePathname(); const [isDismissed, setIsDismissed] = useState(true); // Start as true to avoid flash @@ -39,7 +39,7 @@ export const SignInPromptBanner = ({ !isAuthenticated && isOwner && hasMessages && - !isStreaming; + !isTurnInProgress; // Show the banner after first response completes and track display useEffect(() => { diff --git a/packages/web/src/features/chat/components/chatThread/toolApprovalBanner.tsx b/packages/web/src/features/chat/components/chatThread/toolApprovalBanner.tsx index 0724c93b7..044795e60 100644 --- a/packages/web/src/features/chat/components/chatThread/toolApprovalBanner.tsx +++ b/packages/web/src/features/chat/components/chatThread/toolApprovalBanner.tsx @@ -4,15 +4,20 @@ import { Button } from "@/components/ui/button"; import { McpFavicon } from "@/ee/features/mcp/components/mcpFavicon"; import { useMcpServerIconMap } from "@/features/chat/mcpServerIconContext"; import { useToolApproval } from "@/features/chat/toolApprovalContext"; +import { SBChatToolPart } from "@/features/chat/utils"; import { cn } from "@/lib/utils"; -import { DynamicToolUIPart } from "ai"; +import { getToolName } from "ai"; import { ChevronRight } from "lucide-react"; import { useCallback, useState } from "react"; import { parseMcpToolName } from "./tools/mcpToolComponent"; import { JsonHighlighter } from "./tools/jsonHighlighter"; +export type ApprovalRequestedToolPart = SBChatToolPart & { + state: 'approval-requested'; +}; + interface ToolApprovalBannerProps { - parts: DynamicToolUIPart[]; + parts: ApprovalRequestedToolPart[]; } export const ToolApprovalBanner = ({ parts }: ToolApprovalBannerProps) => { @@ -42,18 +47,18 @@ const ToolApprovalItem = ({ addToolApprovalResponse, iconMap, }: { - part: DynamicToolUIPart; + part: ApprovalRequestedToolPart; addToolApprovalResponse: ReturnTypeTop Connectors
+ {isLoading ? ( +—
+no tool calls yet
+ > + ) : ( + <> ++ {formatCount(grandTotalToolCalls)} total tool calls +
+ > + )} + + + ); +} + export function WorkspaceAskAgentPage({ callbackStatus, callbackServer, callbackMessage }: WorkspaceAskAgentPageProps) { const { toast } = useToast(); const queryClient = useQueryClient(); @@ -215,6 +293,8 @@ export function WorkspaceAskAgentPage({ callbackStatus, callbackServer, callback const servers = data?.servers ?? []; const totalSavedConnectionCount = data?.totalSavedConnectionCount ?? 0; + const topConnectors = data?.topConnectors ?? []; + const grandTotalToolCalls = data?.grandTotalToolCalls ?? 0; const canCreateConnectors = data?.isOAuthAvailable === true; const isOAuthUnavailable = data?.isOAuthAvailable === false; const connectedServerCount = useMemo( @@ -418,7 +498,7 @@ export function WorkspaceAskAgentPage({ callbackStatus, callbackServer, callbackAllowed Connectors
@@ -443,13 +523,11 @@ export function WorkspaceAskAgentPage({ callbackStatus, callbackServer, callbackActive in Last 7d
-—
-tool calls
-No tools exposed by this connector.
- ) : ( -No tools exposed by this connector.
+ ) : ( +Lifetime tool usage
+ {toolUsage.totalCalls === 0 || toolUsage.tools.length === 0 ? ( +No tool calls yet.
+ ) : ( + <> ++ {tool.toolName} +
+ )} ++ {formatCount(toolUsage.totalCalls)} total tool calls across{' '} + {toolEntry + ? `${formatCount(toolUsage.usedToolCount)} of ${formatCount(toolEntry.tools.length)} ${pluralize(toolEntry.tools.length, 'tool')}` + : `${formatCount(toolUsage.usedToolCount)} used ${pluralize(toolUsage.usedToolCount, 'tool')}`} +
+ > + )} +Top Connectors
- {isLoading ? ( -—
-no tool calls yet
- > - ) : ( - <> -- {formatCount(grandTotalToolCalls)} total tool calls -
- > - )} - - - ); -} - export function WorkspaceAskAgentPage({ callbackStatus, callbackServer, callbackMessage }: WorkspaceAskAgentPageProps) { const { toast } = useToast(); const queryClient = useQueryClient(); @@ -299,9 +222,6 @@ export function WorkspaceAskAgentPage({ callbackStatus, callbackServer, callback }, [serversWithStatus]); const servers = data?.servers ?? []; - const totalSavedConnectionCount = data?.totalSavedConnectionCount ?? 0; - const topConnectors = data?.topConnectors ?? []; - const grandTotalToolCalls = data?.grandTotalToolCalls ?? 0; const canCreateConnectors = data?.isOAuthAvailable === true; const isOAuthUnavailable = data?.isOAuthAvailable === false; const connectedServerCount = useMemo( @@ -504,39 +424,6 @@ export function WorkspaceAskAgentPage({ callbackStatus, callbackServer, callbackAllowed Connectors
- {isLoading ? ( -{servers.length}
- )} -approved for workspace
-Saved Connections
- {isLoading ? ( -{totalSavedConnectionCount}
- )} -- {totalSavedConnectionCount === 1 ? "member has" : "members have"} credentials saved -
-Enables/disables authentication with basic credentials. Username and passwords are stored encrypted at rest within the postgres database. Checkout the [auth docs](/docs/configuration/auth/overview) for more info
| -| `AUTH_EMAIL_CODE_LOGIN_ENABLED` | `false` |Enables/disables authentication with a login code that's sent to a users email. `SMTP_CONNECTION_URL` and `EMAIL_FROM_ADDRESS` must also be set. Checkout the [auth docs](/docs/configuration/auth/overview) for more info
| +| `AUTH_CREDENTIALS_LOGIN_ENABLED` | `true` |Enables/disables authentication with basic credentials. Username and passwords are stored encrypted at rest within the postgres database. Checkout the [auth docs](/docs/configuration/auth/authentication) for more info
| +| `AUTH_EMAIL_CODE_LOGIN_ENABLED` | `false` |Enables/disables authentication with a login code that's sent to a users email. `SMTP_CONNECTION_URL` and `EMAIL_FROM_ADDRESS` must also be set. Checkout the [auth docs](/docs/configuration/auth/authentication) for more info
| | `AUTH_SECRET` | Automatically generated at startup if no value is provided. Generated using `openssl rand -base64 33` |Used to validate login session cookies
| | `AUTH_SESSION_MAX_AGE_SECONDS` | `2592000` (30 days) |Relative time from now in seconds when to expire the session.
| | `AUTH_SESSION_UPDATE_AGE_SECONDS` | `86400` (1 day) |How often the session should be updated in seconds. If set to `0`, session is updated every time.
| @@ -43,7 +43,7 @@ The following environment variables allow you to configure your Sourcebot deploy | `SOURCEBOT_LOG_LEVEL` | `info` |The Sourcebot logging level. Valid values are `debug`, `info`, `warn`, `error`, in order of severity.
| | `SOURCEBOT_STRUCTURED_LOGGING_ENABLED` | `false` |Enables/disable structured JSON logging. See [this doc](/docs/configuration/structured-logging) for more info.
| | `SOURCEBOT_STRUCTURED_LOGGING_FILE` | - |Optional file to log to if structured logging is enabled
| -| `SOURCEBOT_TELEMETRY_DISABLED` | `false` |Enables/disables telemetry collection in Sourcebot. See [this doc](/docs/overview#telemetry) for more info.
| +| `SOURCEBOT_TELEMETRY_DISABLED` | `false` |Enables/disables telemetry collection in Sourcebot. See [this doc](/docs/misc/telemetry) for more info.
| | `DEFAULT_MAX_MATCH_COUNT` | `10000` |The default maximum number of search results to return when using search in the web app.
| | `ALWAYS_INDEX_FILE_PATTERNS` | - |A comma separated list of glob patterns matching file paths that should always be indexed, regardless of size or number of trigrams.
| | `NODE_USE_ENV_PROXY` | `0` |Enables Node.js to automatically use `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` environment variables for network requests. Set to `1` to enable or `0` to disable. See [this doc](https://nodejs.org/en/learn/http/enterprise-network-configuration) for more info.
| diff --git a/docs/docs/configuration/idp.mdx b/docs/docs/configuration/idp.mdx index 6126277fd..2496fc06e 100644 --- a/docs/docs/configuration/idp.mdx +++ b/docs/docs/configuration/idp.mdx @@ -5,7 +5,7 @@ sidebarTitle: External identity providers import LicenseKeyRequired from '/snippets/license-key-required.mdx' -
- - `"Contents" repository permissions (read)` (only needed if using the app to [authenticate a connection](/docs/connections/github#github-app))
+ - `"Contents" repository permissions (read)` (only needed if using the app to [authenticate a connection](/docs/connections/github#authenticating-with-github))
diff --git a/docs/docs/connections/ado-cloud.mdx b/docs/docs/connections/ado-cloud.mdx
index b1d12d5e3..6808817b3 100644
--- a/docs/docs/connections/ado-cloud.mdx
+++ b/docs/docs/connections/ado-cloud.mdx
@@ -6,7 +6,7 @@ icon: https://www.svgrepo.com/show/448307/azure-devops.svg
import AzureDevopsSchema from '/snippets/schemas/v3/azuredevops.schema.mdx'
-If you're not familiar with Sourcebot [connections](/docs/connections/overview), please read that overview first.
+If you're not familiar with Sourcebot [connections](/docs/connections/indexing-your-code), please read that overview first.
## Examples
diff --git a/docs/docs/connections/ado-server.mdx b/docs/docs/connections/ado-server.mdx
index 09a592e7f..61d243e54 100644
--- a/docs/docs/connections/ado-server.mdx
+++ b/docs/docs/connections/ado-server.mdx
@@ -6,7 +6,7 @@ icon: https://www.svgrepo.com/show/448307/azure-devops.svg
import AzureDevopsSchema from '/snippets/schemas/v3/azuredevops.schema.mdx'
-If you're not familiar with Sourcebot [connections](/docs/connections/overview), please read that overview first.
+If you're not familiar with Sourcebot [connections](/docs/connections/indexing-your-code), please read that overview first.
## Examples
diff --git a/docs/docs/connections/bitbucket-cloud.mdx b/docs/docs/connections/bitbucket-cloud.mdx
index dfa620565..755b4f85f 100644
--- a/docs/docs/connections/bitbucket-cloud.mdx
+++ b/docs/docs/connections/bitbucket-cloud.mdx
@@ -10,7 +10,7 @@ import BitbucketSchema from '/snippets/schemas/v3/bitbucket.schema.mdx'
Looking for docs on Bitbucket Data Center? See [this doc](/docs/connections/bitbucket-data-center).
-If you're not familiar with Sourcebot [connections](/docs/connections/overview), please read that overview first.
+If you're not familiar with Sourcebot [connections](/docs/connections/indexing-your-code), please read that overview first.
## Examples
diff --git a/docs/docs/connections/bitbucket-data-center.mdx b/docs/docs/connections/bitbucket-data-center.mdx
index be536e9e9..c14e81bcc 100644
--- a/docs/docs/connections/bitbucket-data-center.mdx
+++ b/docs/docs/connections/bitbucket-data-center.mdx
@@ -10,7 +10,7 @@ import BitbucketSchema from '/snippets/schemas/v3/bitbucket.schema.mdx'
Looking for docs on Bitbucket Cloud? See [this doc](/docs/connections/bitbucket-cloud).
-If you're not familiar with Sourcebot [connections](/docs/connections/overview), please read that overview first.
+If you're not familiar with Sourcebot [connections](/docs/connections/indexing-your-code), please read that overview first.
## Examples
diff --git a/docs/docs/connections/generic-git-host.mdx b/docs/docs/connections/generic-git-host.mdx
index 4ebf363b6..585bf0eb3 100644
--- a/docs/docs/connections/generic-git-host.mdx
+++ b/docs/docs/connections/generic-git-host.mdx
@@ -5,13 +5,13 @@ icon: git-alt
import GenericGitHost from '/snippets/schemas/v3/genericGitHost.schema.mdx'
-Sourcebot can sync code from any Git host (by clone url). This is helpful when you want to search code that not in a [supported code host](/docs/connections/overview#platform-connection-guides).
+Sourcebot can sync code from any Git host (by clone url). This is helpful when you want to search code that not in a [supported code host](/docs/connections/indexing-your-code#platform-connection-guides).
-If you're not familiar with Sourcebot [connections](/docs/connections/overview), please read that overview first.
+If you're not familiar with Sourcebot [connections](/docs/connections/indexing-your-code), please read that overview first.
## Getting Started
-To connect to a Git host, create a new [connection](/docs/connections/overview) with type `git` and specify the clone url in the `url` property. For example:
+To connect to a Git host, create a new [connection](/docs/connections/indexing-your-code) with type `git` and specify the clone url in the `url` property. For example:
```json
{
diff --git a/docs/docs/connections/gerrit.mdx b/docs/docs/connections/gerrit.mdx
index ae90104ee..0db4dace3 100644
--- a/docs/docs/connections/gerrit.mdx
+++ b/docs/docs/connections/gerrit.mdx
@@ -10,7 +10,7 @@ import GerritSchema from '/snippets/schemas/v3/gerrit.schema.mdx'
Sourcebot can sync code from self-hosted gerrit instances.
-If you're not familiar with Sourcebot [connections](/docs/connections/overview), please read that overview first.
+If you're not familiar with Sourcebot [connections](/docs/connections/indexing-your-code), please read that overview first.
## Connecting to a Gerrit instance
diff --git a/docs/docs/connections/gitea.mdx b/docs/docs/connections/gitea.mdx
index 1c589c413..b21e35d51 100644
--- a/docs/docs/connections/gitea.mdx
+++ b/docs/docs/connections/gitea.mdx
@@ -8,7 +8,7 @@ import GiteaSchema from '/snippets/schemas/v3/gitea.schema.mdx'
Sourcebot can sync code from Gitea Cloud, and self-hosted.
-If you're not familiar with Sourcebot [connections](/docs/connections/overview), please read that overview first.
+If you're not familiar with Sourcebot [connections](/docs/connections/indexing-your-code), please read that overview first.
## Examples
diff --git a/docs/docs/connections/github.mdx b/docs/docs/connections/github.mdx
index 5f03fef96..fd5fbbb42 100644
--- a/docs/docs/connections/github.mdx
+++ b/docs/docs/connections/github.mdx
@@ -9,7 +9,7 @@ import LicenseKeyRequired from '/snippets/license-key-required.mdx'
Sourcebot can sync code from GitHub.com, GitHub Enterprise Server, and GitHub Enterprise Cloud.
-If you're not familiar with Sourcebot [connections](/docs/connections/overview), please read that overview first.
+If you're not familiar with Sourcebot [connections](/docs/connections/indexing-your-code), please read that overview first.
## Examples
@@ -161,7 +161,7 @@ In order to index private repositories, you'll need to authenticate with GitHub.
[GitHub docs](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#fine-grained-personal-access-tokens)
diff --git a/docs/docs/features/mcp-server.mdx b/docs/docs/features/mcp-server.mdx
index ff0d2be26..1cb24b0d9 100644
--- a/docs/docs/features/mcp-server.mdx
+++ b/docs/docs/features/mcp-server.mdx
@@ -16,7 +16,7 @@ The Sourcebot MCP Server connects AI tools to your [Sourcebot deployment](/docs/
Sourcebot MCP uses a [Streamable HTTP](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#streamable-http) transport hosted at the `/api/mcp` route. Two authorization mechanisms are supported:
-- **OAuth (preferred)**: MCP clients that support OAuth 2.0 will automatically handle the authorization flow and issue a short lived access token. No API key or manual token management required. Only available with an active [Enterprise license](/docs/license-key).
+- **OAuth (preferred)**: MCP clients that support OAuth 2.0 will automatically handle the authorization flow and issue a short lived access token. No API key or manual token management required. Only available with an active [Enterprise license](/docs/activating-a-subscription).
- **API key**: Any MCP client can authorize using a Sourcebot API key passed as a `Authorization: Bearer
- You can now ask Sourcebot any question about your codebase. Checkout the docs for more information. + You can now ask Sourcebot any question about your codebase. Checkout the docs for more information.
Hit a bug? Open up an issue. diff --git a/packages/web/src/app/(app)/components/banners/servicePingFailedBanner.tsx b/packages/web/src/app/(app)/components/banners/servicePingFailedBanner.tsx index f1848ac93..7771c056b 100644 --- a/packages/web/src/app/(app)/components/banners/servicePingFailedBanner.tsx +++ b/packages/web/src/app/(app)/components/banners/servicePingFailedBanner.tsx @@ -13,8 +13,7 @@ interface ServicePingFailedBannerProps extends BannerProps { lastSyncAt: string | null; } -// @nocheckin: link to the service ping docs here when ready. -const SERVICE_PING_DOCS_LINK = "https://docs.sourcebot.dev/docs"; +const SERVICE_PING_DOCS_LINK = "https://docs.sourcebot.dev/docs/misc/service-ping"; export function ServicePingFailedBanner({ id, diff --git a/packages/web/src/app/(app)/components/searchModeSelector.tsx b/packages/web/src/app/(app)/components/searchModeSelector.tsx index 4b2ef3e95..610f6b5dc 100644 --- a/packages/web/src/app/(app)/components/searchModeSelector.tsx +++ b/packages/web/src/app/(app)/components/searchModeSelector.tsx @@ -13,9 +13,9 @@ import { useHotkeys } from "react-hotkeys-hook"; export type SearchMode = "precise" | "agentic"; -const PRECISE_SEARCH_DOCS_URL = "https://docs.sourcebot.dev/docs/features/search/overview"; +const PRECISE_SEARCH_DOCS_URL = "https://docs.sourcebot.dev/docs/features/search/code-search"; // @tood: point this to the actual docs page -const AGENTIC_SEARCH_DOCS_URL = "https://docs.sourcebot.dev/docs/features/ask/overview"; +const AGENTIC_SEARCH_DOCS_URL = "https://docs.sourcebot.dev/docs/features/ask/ask-sourcebot"; export interface SearchModeSelectorProps { searchMode: SearchMode; diff --git a/packages/web/src/app/(app)/settings/connections/page.tsx b/packages/web/src/app/(app)/settings/connections/page.tsx index 2b046cabe..fdce7b08f 100644 --- a/packages/web/src/app/(app)/settings/connections/page.tsx +++ b/packages/web/src/app/(app)/settings/connections/page.tsx @@ -6,7 +6,7 @@ import Link from "next/link"; import { ConnectionsTable } from "./components/connectionsTable"; import { ConnectionSyncJobStatus } from "@prisma/client"; -const DOCS_URL = "https://docs.sourcebot.dev/docs/connections/overview"; +const DOCS_URL = "https://docs.sourcebot.dev/docs/connections/indexing-your-code"; export default async function ConnectionsPage() { const _connections = await getConnectionsWithLatestJob(); diff --git a/packages/web/src/app/components/authMethodSelector.tsx b/packages/web/src/app/components/authMethodSelector.tsx index f309e7e04..0e42fb478 100644 --- a/packages/web/src/app/components/authMethodSelector.tsx +++ b/packages/web/src/app/components/authMethodSelector.tsx @@ -51,7 +51,7 @@ export const AuthMethodSelector = ({ return (
No authentication methods available. Please contact your administrator to configure authentication.
- Learn more + Learn more+ + Auto-renewing on {formatDate(renewalAt)} + {" "} + + with {seats} {seats === 1 ? 'seat' : 'seats'}. + +
+ {!noticeDeadlineHasPassed && ( +
+ You have until {formatDate(noticeDeadline)} to{" "}
+
+ Track your organization's seat usage and any pending overage charges for the current subscription term, ending on {formatDate(termEndsAt)}.{" "} + + Learn more + +
++ {currentUsers} / {committedSeats} +
+ {billableOverageSeats > 0 ? ( ++ Seats in use:{" "} + the number of users in your organization right now. +
++ Seats in subscription:{" "} + the seats you've paid for so far in your current subscription term. +
++ Term complete, awaiting renewal. +
++ Quarter {currentQuarterNumber} of {totalQuartersInTerm} · term ends {formatDate(termEndsAt)} +
+ )} +{peakSeats}
++ {billableOverageSeats} +
+{label}
+Connectors are MCP servers that let Ask Agent use approved external tools alongside your indexed code.
diff --git a/packages/web/src/features/chat/components/chatThread/chatThreadListItem.tsx b/packages/web/src/features/chat/components/chatThread/chatThreadListItem.tsx index 4f39532b4..d05508081 100644 --- a/packages/web/src/features/chat/components/chatThread/chatThreadListItem.tsx +++ b/packages/web/src/features/chat/components/chatThread/chatThreadListItem.tsx @@ -426,7 +426,7 @@ const ChatThreadListItemComponent = forwardRef