query_repository
Query code repositories using natural language to get detailed answers with code references. Understand codebases by asking questions about repositories from GitHub or GitLab.
Instructions
Query repositories using natural language to get detailed answers with code references
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | Natural language query about the codebase | |
| repositories | No | List of repositories to query | |
| session_id | No | Session ID for conversation continuity (auto-generated if not provided) | |
| stream | No | Enable streaming response | |
| genius | No | Use enhanced query capabilities | |
| timeout | No | Request timeout in milliseconds | |
| previous_messages | No | Previous conversation messages for context |
Implementation Reference
- src/server.ts:456-544 (handler)Main MCP tool handler for 'query_repository'. Processes arguments, handles session management, streaming/non-streaming logic, error checking, and delegates to GreptileClient.queryRepositories.private async handleQueryRepository( args: unknown ): Promise<{ content: Array<{ type: string; text: string }> }> { if (!this.greptileClient) { return { content: [ { type: 'text', text: createErrorResponse( 'Cannot query repository: Missing environment variables. Use greptile_env_check for setup guidance.', 'Configuration Error', undefined ), }, ], }; } const { query, repositories = [], session_id, stream = false, genius = true, timeout, previous_messages = [], } = args as any; // Generate session ID if not provided const sessionId = session_id || generateSessionId(); // Prepare messages array const messages = [...previous_messages, { role: 'user' as const, content: query }]; if (stream) { // Handle streaming response const streamResults: string[] = []; const streamingResponse = await this.greptileClient.queryRepositories( messages, repositories, sessionId, true, genius, timeout ); for await (const chunk of streamingResponse as AsyncIterable<any>) { if (chunk.type === 'text' && chunk.content) { streamResults.push(chunk.content); } } return { content: [ { type: 'text', text: JSON.stringify( { message: streamResults.join(''), session_id: sessionId, streamed: true, }, null, 2 ), }, ], }; } else { // Handle regular response const result = await this.greptileClient.queryRepositories( messages, repositories, sessionId, false, genius, timeout ); return { content: [ { type: 'text', text: JSON.stringify({ ...result, session_id: sessionId }, null, 2), }, ], }; } }
- src/server.ts:136-194 (registration)MCP tool registration including name, description, and input schema definition in the ListTools handler.name: 'query_repository', description: 'Query repositories using natural language to get detailed answers with code references', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Natural language query about the codebase', }, repositories: { type: 'array', items: { type: 'object', properties: { remote: { type: 'string', enum: ['github', 'gitlab'] }, repository: { type: 'string' }, branch: { type: 'string' }, }, required: ['remote', 'repository', 'branch'], }, description: 'List of repositories to query', }, session_id: { type: 'string', description: 'Session ID for conversation continuity (auto-generated if not provided)', }, stream: { type: 'boolean', description: 'Enable streaming response', default: false, }, genius: { type: 'boolean', description: 'Use enhanced query capabilities', default: true, }, timeout: { type: 'number', description: 'Request timeout in milliseconds', default: 60000, }, previous_messages: { type: 'array', items: { type: 'object', properties: { role: { type: 'string', enum: ['user', 'assistant'] }, content: { type: 'string' }, }, required: ['role', 'content'], }, description: 'Previous conversation messages for context', }, }, required: ['query'], }, },
- src/types/index.ts:78-86 (schema)TypeScript interface defining the input shape for query_repository tool, matching the runtime schema.export interface QueryRepositoryInput { query: string; repositories?: Repository[]; session_id?: string; stream?: boolean; genius?: boolean; timeout?: number; previous_messages?: QueryMessage[]; }
- src/clients/greptile.ts:73-101 (helper)Core GreptileClient method implementing non-streaming query logic by making POST request to Greptile API /query endpoint.async queryRepositories( messages: QueryMessage[], repositories: Repository[], sessionId?: string, stream: boolean = false, genius: boolean = true, timeout?: number ): Promise<QueryResponse | AsyncIterable<StreamingChunk>> { if (stream) { return this.streamQueryRepositories(messages, repositories, sessionId, genius, timeout); } const url = `${this.baseUrl}/query`; const payload: Record<string, unknown> = { messages, stream: false, genius, }; if (repositories.length > 0) { payload.repositories = repositories; } if (sessionId) { payload.sessionId = sessionId; } const response = await this.makeRequest('POST', url, payload, timeout); return response as QueryResponse; }
- src/clients/greptile.ts:106-187 (helper)GreptileClient streaming query implementation using Server-Sent Events (SSE), parsing chunks and yielding standardized StreamingChunk objects.async *streamQueryRepositories( messages: QueryMessage[], repositories: Repository[], sessionId?: string, genius: boolean = true, timeout?: number ): AsyncIterable<StreamingChunk> { const url = `${this.baseUrl}/query`; const payload: Record<string, unknown> = { messages, stream: true, genius, }; if (repositories.length > 0) { payload.repositories = repositories; } if (sessionId) { payload.sessionId = sessionId; } const streamHeaders = { ...this.headers, Accept: 'text/event-stream', 'Cache-Control': 'no-cache', }; const controller = new AbortController(); const timeoutId = timeout ? setTimeout(() => controller.abort(), timeout) : null; try { const response = await fetch(url, { method: 'POST', headers: streamHeaders, body: JSON.stringify(payload), signal: controller.signal, }); if (!response.ok) { throw this.createError(`HTTP ${response.status}: ${response.statusText}`, response.status); } if (!response.body) { throw this.createError('No response body for streaming request'); } const reader = response.body.getReader(); const decoder = new TextDecoder(); let buffer = ''; try { while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); const lines = buffer.split('\n'); buffer = lines.pop() || ''; for (const line of lines) { if (line.trim() && line.startsWith('data: ')) { const data = line.slice(6); const chunk = safeJsonParse(data, null); if (chunk) { const processedChunk = this.processStreamChunk(chunk); if (processedChunk) { yield processedChunk; } } } } } } finally { reader.releaseLock(); } } finally { if (timeoutId) { clearTimeout(timeoutId); } } }