-
Notifications
You must be signed in to change notification settings - Fork 256
Expand file tree
/
Copy pathserver.ts
More file actions
140 lines (123 loc) · 6.02 KB
/
server.ts
File metadata and controls
140 lines (123 loc) · 6.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import {
languageModelInfoSchema,
} from '@/features/chat/types';
import { askCodebase } from '@/features/mcp/askCodebase';
import { captureEvent } from '@/lib/posthog';
import { isServiceError } from '@/lib/utils';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { ChatVisibility } from '@sourcebot/db';
import { SOURCEBOT_VERSION } from '@sourcebot/shared';
import _dedent from 'dedent';
import { z } from 'zod';
import { getConfiguredLanguageModelsInfo } from "../chat/utils.server";
import {
findSymbolDefinitionsDefinition,
findSymbolReferencesDefinition,
listCommitsDefinition,
listReposDefinition,
listTreeDefinition,
readFileDefinition,
registerMcpTool,
grepDefinition,
ToolContext,
globDefinition,
} from '../tools';
const dedent = _dedent.withOptions({ alignValues: true });
export async function createMcpServer(options?: { userId?: string }): Promise<McpServer> {
const server = new McpServer({
name: 'sourcebot-mcp-server',
version: SOURCEBOT_VERSION,
});
const configuredLanguageModels = await getConfiguredLanguageModelsInfo();
const hasLanguageModels = configuredLanguageModels.length > 0;
const toolContext: ToolContext = {
source: 'sourcebot-mcp-server',
userId: options?.userId,
}
registerMcpTool(server, grepDefinition, toolContext);
registerMcpTool(server, globDefinition, toolContext);
registerMcpTool(server, listCommitsDefinition, toolContext);
registerMcpTool(server, listReposDefinition, toolContext);
registerMcpTool(server, readFileDefinition, toolContext);
registerMcpTool(server, listTreeDefinition, toolContext);
registerMcpTool(server, findSymbolDefinitionsDefinition, toolContext);
registerMcpTool(server, findSymbolReferencesDefinition, toolContext);
server.registerTool(
"list_language_models",
{
description: dedent`Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling ask_codebase.`,
annotations: {
readOnlyHint: true,
idempotentHint: true,
}
},
async () => {
const models = await getConfiguredLanguageModelsInfo();
captureEvent('tool_used', {
toolName: 'list_language_models',
source: 'sourcebot-mcp-server',
success: true,
}, { distinctId: options?.userId });
return { content: [{ type: "text", text: JSON.stringify(models) }] };
}
);
if (hasLanguageModels) {
server.registerTool(
"ask_codebase",
{
description: dedent`
DO NOT USE THIS TOOL UNLESS EXPLICITLY ASKED TO. THE PROMPT MUST SPECIFICALLY ASK TO USE THE ask_codebase TOOL.
Ask a natural language question about the codebase. This tool uses an AI agent to autonomously search code, read files, and find symbol references/definitions to answer your question.
This is a blocking operation that may take 60+ seconds to research the codebase, so only invoke it if the user has explicitly asked you to by specifying the ask_codebase tool call in the prompt.
The agent will:
- Analyze your question and determine what context it needs
- Search the codebase using multiple strategies (code search, symbol lookup, file reading)
- Synthesize findings into a comprehensive answer with code references
Returns a detailed answer in markdown format with code references, plus a link to view the full research session (including all tool calls and reasoning) in the Sourcebot web UI.
When using this in shared environments (e.g., Slack), you can set the visibility parameter to 'PUBLIC' to ensure everyone can access the chat link.
`,
inputSchema: z.object({
query: z.string().describe("The query to ask about the codebase."),
repos: z.array(z.string()).optional().describe("The repositories accessible to the agent. If not provided, all repositories are accessible."),
languageModel: languageModelInfoSchema.optional().describe("The language model to use. If not provided, defaults to the first model in the config."),
visibility: z.enum(['PRIVATE', 'PUBLIC']).optional().describe("The visibility of the chat session. Defaults to PRIVATE for authenticated users."),
}),
annotations: {
readOnlyHint: true,
}
},
async (request) => {
const result = await askCodebase({
query: request.query,
repos: request.repos,
languageModel: request.languageModel,
visibility: request.visibility as ChatVisibility | undefined,
source: 'mcp',
});
if (isServiceError(result)) {
captureEvent('tool_used', {
toolName: 'ask_codebase',
source: 'sourcebot-mcp-server',
success: false,
}, { distinctId: options?.userId });
return {
content: [{ type: "text", text: `Failed to ask codebase: ${result.message}` }],
};
}
captureEvent('tool_used', {
toolName: 'ask_codebase',
source: 'sourcebot-mcp-server',
success: true,
}, { distinctId: options?.userId });
const formattedResponse = dedent`
${result.answer}
---
**View full research session:** ${result.chatUrl}
**Model used:** ${result.languageModel.model}
`;
return { content: [{ type: "text", text: formattedResponse }] };
}
);
}
return server;
}