Skip to content

Commit c70753d

Browse files
committed
Refactor chat_completions to codebase_consultant with simplified API
- Renamed chat_completions to codebase_consultant for clarity - Simplified API: now accepts 'question' parameter instead of messages array - Internal transformation: question → [{"role": "user", "content": question}] - Cleaner contract: agents just pass a string question, not nested objects - Updated all imports, tests, and documentation - Tests passing: simplified validation for empty questions - Maintains backward compatibility with data source format The new name better reflects the capability (consulting an AI expert about code) and the simplified API is more intuitive for agents to use.
1 parent f9bbb9b commit c70753d

File tree

7 files changed

+75
-105
lines changed

7 files changed

+75
-105
lines changed

CLAUDE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ This is a Model Context Protocol (MCP) server that provides AI clients with acce
4848
### Core Components
4949

5050
- **`codealive_mcp_server.py`**: Main server implementation using FastMCP framework
51-
- **Three main tools**: `chat_completions`, `codebase_search`, `get_data_sources`
51+
- **Three main tools**: `codebase_consultant`, `codebase_search`, `get_data_sources`
5252
- **CodeAliveContext**: Manages HTTP client and API credentials
5353
- **Async lifespan management**: Handles client setup/teardown
5454

@@ -63,7 +63,7 @@ This is a Model Context Protocol (MCP) server that provides AI clients with acce
6363
### Data Flow
6464

6565
1. AI client connects to MCP server via stdio/SSE transport
66-
2. Client calls tools (`get_data_sources``codebase_search``chat_completions`)
66+
2. Client calls tools (`get_data_sources``codebase_search``codebase_consultant`)
6767
3. MCP server translates tool calls to CodeAlive API requests
6868
4. CodeAlive API returns semantic search results or chat completions
6969
5. Server formats and returns results to AI client

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,15 @@ Once connected, you'll have access to these powerful tools:
2323

2424
1. **`get_data_sources`** - List your indexed repositories and workspaces
2525
2. **`codebase_search`** - Semantic code search across your indexed codebase (main/master branch)
26-
3. **`chat_completions`** - AI chat with full project context
26+
3. **`codebase_consultant`** - AI consultant with full project expertise
2727

2828
## 🎯 Usage Examples
2929

3030
After setup, try these commands with your AI assistant:
3131

3232
- *"Show me all available repositories"* → Uses `get_data_sources`
3333
- *"Find authentication code in the user service"* → Uses `codebase_search`
34-
- *"Explain how the payment flow works in this codebase"* → Uses `chat_completions`
34+
- *"Explain how the payment flow works in this codebase"* → Uses `codebase_consultant`
3535

3636
## Table of Contents
3737

src/codealive_mcp_server.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
# Import core components
2727
from core import codealive_lifespan, setup_debug_logging
28-
from tools import chat_completions, get_data_sources, codebase_search
28+
from tools import codebase_consultant, get_data_sources, codebase_search
2929

3030
# Initialize FastMCP server with lifespan and enhanced system instructions
3131
mcp = FastMCP(
@@ -88,7 +88,7 @@ async def health_check(request: Request) -> JSONResponse:
8888

8989

9090
# Register tools
91-
mcp.tool()(chat_completions)
91+
mcp.tool()(codebase_consultant)
9292
mcp.tool()(get_data_sources)
9393
mcp.tool()(codebase_search)
9494

src/tests/test_chat_tool.py

Lines changed: 28 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
"""Test suite for chat completions tool."""
1+
"""Test suite for codebase consultant tool."""
22

33
import pytest
44
from unittest.mock import AsyncMock, MagicMock, patch
55
import json
66
from fastmcp import Context
7-
from tools.chat import chat_completions
7+
from tools.chat import codebase_consultant
88

99

1010
@pytest.mark.asyncio
1111
@patch('tools.chat.get_api_key_from_context')
12-
async def test_chat_with_simple_ids(mock_get_api_key):
13-
"""Test chat completions with simple string IDs."""
12+
async def test_consultant_with_simple_ids(mock_get_api_key):
13+
"""Test codebase consultant with simple string IDs."""
1414
mock_get_api_key.return_value = "test_key"
1515

1616
ctx = MagicMock(spec=Context)
@@ -40,9 +40,9 @@ async def mock_aiter_lines():
4040
ctx.request_context.lifespan_context = mock_codealive_context
4141

4242
# Test with simple string IDs
43-
result = await chat_completions(
43+
result = await codebase_consultant(
4444
ctx=ctx,
45-
messages=[{"role": "user", "content": "Test question"}],
45+
question="Test question",
4646
data_sources=["repo123", "repo456"]
4747
)
4848

@@ -61,8 +61,8 @@ async def mock_aiter_lines():
6161

6262
@pytest.mark.asyncio
6363
@patch('tools.chat.get_api_key_from_context')
64-
async def test_chat_with_dict_ids(mock_get_api_key):
65-
"""Test chat completions with dictionary format IDs."""
64+
async def test_consultant_preserves_string_ids(mock_get_api_key):
65+
"""Test codebase consultant preserves string IDs."""
6666
mock_get_api_key.return_value = "test_key"
6767

6868
ctx = MagicMock(spec=Context)
@@ -88,14 +88,11 @@ async def mock_aiter_lines():
8888

8989
ctx.request_context.lifespan_context = mock_codealive_context
9090

91-
# Test with dict format (backward compatibility)
92-
result = await chat_completions(
91+
# Test with string IDs
92+
result = await codebase_consultant(
9393
ctx=ctx,
94-
messages=[{"role": "user", "content": "Test"}],
95-
data_sources=[
96-
{"type": "repository", "id": "repo123"},
97-
{"id": "repo456"}
98-
]
94+
question="Test",
95+
data_sources=["repo123", "repo456"]
9996
)
10097

10198
call_args = mock_client.post.call_args
@@ -112,8 +109,8 @@ async def mock_aiter_lines():
112109

113110
@pytest.mark.asyncio
114111
@patch('tools.chat.get_api_key_from_context')
115-
async def test_chat_with_conversation_id(mock_get_api_key):
116-
"""Test chat completions with existing conversation ID."""
112+
async def test_consultant_with_conversation_id(mock_get_api_key):
113+
"""Test codebase consultant with existing conversation ID."""
117114
mock_get_api_key.return_value = "test_key"
118115

119116
ctx = MagicMock(spec=Context)
@@ -137,9 +134,9 @@ async def mock_aiter_lines():
137134

138135
ctx.request_context.lifespan_context = mock_codealive_context
139136

140-
result = await chat_completions(
137+
result = await codebase_consultant(
141138
ctx=ctx,
142-
messages=[{"role": "user", "content": "Follow up"}],
139+
question="Follow up",
143140
conversation_id="conv_123"
144141
)
145142

@@ -156,51 +153,29 @@ async def mock_aiter_lines():
156153

157154
@pytest.mark.asyncio
158155
@patch('tools.chat.get_api_key_from_context')
159-
async def test_chat_empty_messages_validation(mock_get_api_key):
160-
"""Test validation of empty messages."""
156+
async def test_consultant_empty_question_validation(mock_get_api_key):
157+
"""Test validation of empty question."""
161158
mock_get_api_key.return_value = "test_key"
162159

163160
ctx = MagicMock(spec=Context)
164161
ctx.request_context.lifespan_context = MagicMock()
165162

166-
# Test with no messages
167-
result = await chat_completions(ctx=ctx, messages=None)
168-
assert "Error: No messages provided" in result
163+
# Test with empty question
164+
result = await codebase_consultant(ctx=ctx, question="")
165+
assert "Error: No question provided" in result
169166

170-
# Test with empty list
171-
result = await chat_completions(ctx=ctx, messages=[])
172-
assert "Error: No messages provided" in result
167+
# Test with whitespace only
168+
result = await codebase_consultant(ctx=ctx, question=" ")
169+
assert "Error: No question provided" in result
173170

174171

175-
@pytest.mark.asyncio
176-
@patch('tools.chat.get_api_key_from_context')
177-
async def test_chat_invalid_message_format(mock_get_api_key):
178-
"""Test validation of message format."""
179-
mock_get_api_key.return_value = "test_key"
180-
181-
ctx = MagicMock(spec=Context)
182-
ctx.request_context.lifespan_context = MagicMock()
183-
184-
# Test with missing role
185-
result = await chat_completions(
186-
ctx=ctx,
187-
messages=[{"content": "Test"}]
188-
)
189-
assert "Error: Each message must have 'role' and 'content'" in result
190-
191-
# Test with missing content
192-
result = await chat_completions(
193-
ctx=ctx,
194-
messages=[{"role": "user"}]
195-
)
196-
assert "Error: Each message must have 'role' and 'content'" in result
197172

198173

199174
@pytest.mark.asyncio
200175
@patch('tools.chat.get_api_key_from_context')
201176
@patch('tools.chat.handle_api_error')
202-
async def test_chat_error_handling(mock_handle_error, mock_get_api_key):
203-
"""Test error handling in chat completions."""
177+
async def test_consultant_error_handling(mock_handle_error, mock_get_api_key):
178+
"""Test error handling in codebase consultant."""
204179
mock_get_api_key.return_value = "test_key"
205180
mock_handle_error.return_value = "Error: Authentication failed"
206181

@@ -216,9 +191,9 @@ async def test_chat_error_handling(mock_handle_error, mock_get_api_key):
216191

217192
ctx.request_context.lifespan_context = mock_codealive_context
218193

219-
result = await chat_completions(
194+
result = await codebase_consultant(
220195
ctx=ctx,
221-
messages=[{"role": "user", "content": "Test"}],
196+
question="Test",
222197
data_sources=["repo123"]
223198
)
224199

src/tools/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Tool implementations for CodeAlive MCP server."""
22

3-
from .chat import chat_completions
3+
from .chat import codebase_consultant
44
from .datasources import get_data_sources
55
from .search import codebase_search
66

7-
__all__ = ['chat_completions', 'get_data_sources', 'codebase_search']
7+
__all__ = ['codebase_consultant', 'get_data_sources', 'codebase_search']

src/tools/chat.py

Lines changed: 37 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -11,77 +11,72 @@
1111
from utils import handle_api_error, format_data_source_ids
1212

1313

14-
async def chat_completions(
14+
async def codebase_consultant(
1515
ctx: Context,
16-
messages: Optional[List[Dict[str, str]]] = None,
17-
data_sources: Optional[List] = None, # Accept both strings and dicts for compatibility
16+
question: str,
17+
data_sources: Optional[List[str]] = None,
1818
conversation_id: Optional[str] = None
1919
) -> str:
2020
"""
21-
Streams chat completions from the CodeAlive API for code-aware conversations with knowledge of your codebase.
21+
Consult with an AI expert about your codebase for insights, explanations, and architectural guidance.
22+
23+
This consultant understands your entire codebase and can help with:
24+
- Architecture and design decisions
25+
- Implementation strategies
26+
- Code explanations and walkthroughs
27+
- Best practices and optimization advice
28+
- Debugging and problem-solving
2229
2330
Args:
24-
messages: List of message objects with "role" and "content" fields
25-
Example: [
26-
{"role": "system", "content": "Analyze the authentication flow"},
27-
{"role": "user", "content": "How does the login process work?"}
28-
]
31+
question: What you want to know about the codebase
32+
Example: "How does the authentication system work?"
2933
30-
data_sources: List of data source IDs (repository or workspace IDs).
34+
data_sources: Repository or workspace IDs to analyze
3135
Example: ["67f664fd4c2a00698a52bb6f", "5e8f9a2c1d3b7e4a6c9d0f8e"]
32-
Just pass the IDs directly as strings.
33-
conversation_id: Optional ID to continue a previous conversation
34-
Example: "conv_6789f123a456b789c123d456"
3536
37+
conversation_id: Continue a previous consultation session
38+
Example: "conv_6789f123a456b789c123d456"
3639
3740
Returns:
38-
The generated completion text with code understanding from specified repositories/workspaces.
39-
The response will incorporate knowledge from the specified code repositories.
41+
Expert analysis and explanation addressing your question.
4042
4143
Examples:
42-
1. Start a new conversation with simple ID format (recommended):
43-
chat_completions(
44-
messages=[{"role": "user", "content": "Explain the authentication flow in this code"}],
44+
1. Ask about architecture:
45+
codebase_consultant(
46+
question="What's the best way to add caching to our API?",
4547
data_sources=["67f664fd4c2a00698a52bb6f"]
4648
)
4749
48-
2. Start a new conversation using multiple data sources:
49-
chat_completions(
50-
messages=[{"role": "user", "content": "How do the microservices communicate with each other?"}],
51-
data_sources=["67f664fd4c2a00698a52bb6f", "5e8f9a2c1d3b7e4a6c9d0f8e"]
50+
2. Understand implementation:
51+
codebase_consultant(
52+
question="How do the microservices communicate?",
53+
data_sources=["workspace_123", "repo_456"]
5254
)
5355
54-
3. Continue an existing conversation:
55-
chat_completions(
56-
messages=[{"role": "user", "content": "How is password hashing implemented?"}],
56+
3. Continue a consultation:
57+
codebase_consultant(
58+
question="What about error handling in that flow?",
5759
conversation_id="conv_6789f123a456b789c123d456"
5860
)
5961
60-
6162
Note:
6263
- Either conversation_id OR data_sources is typically provided
63-
- When creating a new conversation, data_sources is optional if the API key has exactly one assigned data source
64-
- When continuing a conversation, conversation_id is required
65-
- The conversation maintains context across multiple messages
66-
- Messages should be in chronological order with the newest message last
67-
- Choose between workspace-level access (for broader context) or repository-level access
68-
(for targeted analysis) based on your query needs
69-
- If a user is working in a local git repository that matches one of the indexed repositories
70-
in CodeAlive (by URL), you can leverage this integration for enhanced code understanding
64+
- When creating a new conversation, data_sources is optional if your API key has exactly one assigned data source
65+
- When continuing a conversation, conversation_id is required to maintain context
66+
- The consultant maintains full conversation history for follow-up questions
67+
- Choose workspace IDs for broad architectural questions or repository IDs for specific implementation details
7168
"""
7269
context: CodeAliveContext = ctx.request_context.lifespan_context
7370

74-
if not messages or len(messages) == 0:
75-
return "Error: No messages provided. Please include at least one message with 'role' and 'content' fields."
71+
if not question or not question.strip():
72+
return "Error: No question provided. Please provide a question to ask the consultant."
7673

7774
# Validate that either conversation_id or data_sources is provided
7875
if not conversation_id and (not data_sources or len(data_sources) == 0):
7976
await ctx.info("No data sources provided. If the API key has exactly one assigned data source, that will be used as default.")
8077

81-
# Validate that each message has the required fields
82-
for msg in messages:
83-
if not msg.get("role") or not msg.get("content"):
84-
return "Error: Each message must have 'role' and 'content' fields. Valid roles are 'system', 'user', and 'assistant'."
78+
# Transform simple question into message format internally
79+
messages = [{"role": "user", "content": question}]
8580

8681
# Prepare the request payload
8782
request_data = {
@@ -99,8 +94,8 @@ async def chat_completions(
9994
api_key = get_api_key_from_context(ctx)
10095

10196
# Log the attempt
102-
await ctx.info(f"Requesting chat completion with {len(messages)} messages" +
103-
(f" in conversation {conversation_id}" if conversation_id else " in a new conversation"))
97+
await ctx.info(f"Consulting about: '{question[:100]}...'" if len(question) > 100 else f"Consulting about: '{question}'" +
98+
(f" (continuing conversation {conversation_id})" if conversation_id else ""))
10499

105100
headers = {"Authorization": f"Bearer {api_key}"}
106101

src/tools/datasources.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ async def get_data_sources(ctx: Context, alive_only: bool = True) -> str:
4848
For workspaces, the repositoryIds can be used to identify and work with
4949
individual repositories that make up the workspace.
5050
51-
Use the returned data source IDs with the codebase_search and chat_completions functions.
51+
Use the returned data source IDs with the codebase_search and codebase_consultant functions.
5252
"""
5353
context: CodeAliveContext = ctx.request_context.lifespan_context
5454

@@ -84,7 +84,7 @@ async def get_data_sources(ctx: Context, alive_only: bool = True) -> str:
8484
result = f"Available data sources:\n{formatted_data}"
8585

8686
# Add usage hint
87-
result += "\n\nYou can use these data source IDs with the codebase_search and chat_completions functions."
87+
result += "\n\nYou can use these data source IDs with the codebase_search and codebase_consultant functions."
8888

8989
return result
9090

0 commit comments

Comments
 (0)