feat(mcp/client): add MCP client CLI for querying the code graph via MCP server#402
feat(mcp/client): add MCP client CLI for querying the code graph via MCP server#402
Conversation
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a new command-line interface (CLI) client for the MCP (Microservice Codebase Processor) server. This client enables programmatic interaction with the code graph by launching the MCP server as a subprocess and executing the Highlights
Changelog
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces a new CLI client for the MCP server, which is a useful addition for scripting and CI. However, a critical security vulnerability has been identified: the client passes untrusted natural language input directly to a powerful LLM agent with access to sensitive tools like shell command execution and file modification. This creates a high risk of prompt injection, especially when input is derived from untrusted sources in scripting and CI environments. It is crucial to implement confirmation prompts for sensitive operations and restrict tool access in non-interactive environments, aligning with the project's emphasis on fail-safe security for LLM agent interactions. Additionally, the implementation includes docstrings, which are disallowed by project coding standards, and the subprocess invocation could be made more robust by using sys.executable instead of a hardcoded python command.
| question: str = typer.Option( | ||
| ..., "--ask-agent", "-a", help="Question to ask about the codebase" | ||
| ), |
There was a problem hiding this comment.
The main function in codebase_rag/mcp/client.py accepts a natural language question via a CLI option and passes it directly to the ask_agent tool on the MCP server. According to the project's architecture (seen in codebase_rag/main.py), the agent has access to highly sensitive tools, including shell_command_tool, file_writer_tool, and file_editor_tool.
The PR description explicitly states that this client is "Useful for scripting and CI pipelines." In such environments, the question input may be derived from untrusted sources, such as Pull Request titles, commit messages, or issue descriptions. An attacker could use prompt injection techniques to manipulate the LLM agent into executing arbitrary shell commands or making unauthorized modifications to the codebase.
Furthermore, unlike the primary CLI implementation in codebase_rag/main.py, this MCP client lacks any confirmation prompts or safety checks (e.g., the confirm_edits_globally logic) before executing potentially destructive operations.
References
- For security-sensitive features like shell command execution by an LLM agent, prioritize fail-safe behavior. Prefer false positives (blocking a safe command) over false negatives (allowing a dangerous one), especially if the fix for the false positive adds complexity.
|
|
||
| # Call the ask_agent tool | ||
|
|
||
| result = await session.call_tool("ask_agent", {"question": question}) |
There was a problem hiding this comment.
This line passes the untrusted question directly to the ask_agent tool. If the agent is configured with sensitive tools (like shell access), this represents a significant security risk through prompt injection, especially in non-interactive environments like CI/CD.
References
- For security-sensitive features like shell command execution by an LLM agent, prioritize fail-safe behavior. Prefer false positives (blocking a safe command) over false negatives (allowing a dangerous one), especially if the fix for the false positive adds complexity.
| """MCP client for querying the code graph via the MCP server. | ||
|
|
||
|
|
||
|
|
||
| This module provides a simple CLI client that connects to the MCP server | ||
|
|
||
| and executes the ask_agent tool with a provided question. | ||
|
|
||
| """ |
| """Query the MCP server with a question. | ||
|
|
||
|
|
||
|
|
||
| Args: | ||
|
|
||
| question: The question to ask about the codebase | ||
|
|
||
|
|
||
|
|
||
| Returns: | ||
|
|
||
| Dictionary with the response from the server | ||
|
|
||
| """ |
There was a problem hiding this comment.
|
|
||
| with open(os.devnull, "w") as devnull: | ||
| server_params = StdioServerParameters( | ||
| command="python", |
There was a problem hiding this comment.
Hardcoding command="python" can lead to issues if the user's environment has multiple Python versions or is not on the system's PATH. It's more robust to use sys.executable to ensure the MCP server subprocess runs with the same Python interpreter as the client script.
| command="python", | |
| command=sys.executable, |
| """Query the code graph via MCP server. | ||
|
|
||
|
|
||
|
|
||
| Example: | ||
|
|
||
| python -m codebase_rag.mcp.client --ask-agent "What functions call UserService.create_user?" | ||
|
|
||
| """ |
There was a problem hiding this comment.
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Greptile SummaryThis PR adds a new MCP client CLI module ( Key Changes:
Issues Found:
Confidence Score: 2/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant User
participant CLI as MCP Client CLI
participant Server as MCP Server<br/>(subprocess)
participant Tool as ask_agent Tool
User->>CLI: --ask-agent "question"
CLI->>Server: spawn subprocess<br/>(python -m codebase_rag.main mcp-server)
CLI->>Server: stdio_client connection
CLI->>Server: session.initialize()
Server-->>CLI: initialized
CLI->>Server: call_tool("ask_agent", {"question": "..."})
Server->>Tool: execute ask_agent
Tool-->>Server: result
Server-->>CLI: TextContent response
CLI->>CLI: parse JSON response
CLI->>User: print output to stdout
Last reviewed commit: 453f399 |
| app = typer.Typer() | ||
|
|
||
|
|
||
| async def query_mcp_server(question: str) -> dict[str, Any]: |
There was a problem hiding this comment.
dict[str, Any] violates strict typing requirements. Use TypedDict instead:
| async def query_mcp_server(question: str) -> dict[str, Any]: | |
| async def query_mcp_server(question: str) -> MCPResponse: |
Then define:
class MCPResponse(TypedDict):
output: strContext Used: Rule from dashboard - ## Technical Requirements
Agentic Framework
- PydanticAI Only: This project uses PydanticAI... (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: codebase_rag/mcp/client.py
Line: 24
Comment:
`dict[str, Any]` violates strict typing requirements. Use TypedDict instead:
```suggestion
async def query_mcp_server(question: str) -> MCPResponse:
```
Then define:
```python
class MCPResponse(TypedDict):
output: str
```
**Context Used:** Rule from `dashboard` - ## Technical Requirements
### Agentic Framework
- **PydanticAI Only**: This project uses PydanticAI... ([source](https://app.greptile.com/review/custom-context?memory=d4240b05-b763-467a-a6bf-94f73e8b6859))
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| import json | ||
| import os | ||
| import sys | ||
| from typing import Any |
There was a problem hiding this comment.
Any type is explicitly prohibited. Remove this import and use TypedDict for dict shapes.
Context Used: Rule from dashboard - ## Technical Requirements
Agentic Framework
- PydanticAI Only: This project uses PydanticAI... (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: codebase_rag/mcp/client.py
Line: 15
Comment:
`Any` type is explicitly prohibited. Remove this import and use TypedDict for dict shapes.
**Context Used:** Rule from `dashboard` - ## Technical Requirements
### Agentic Framework
- **PydanticAI Only**: This project uses PydanticAI... ([source](https://app.greptile.com/review/custom-context?memory=d4240b05-b763-467a-a6bf-94f73e8b6859))
How can I resolve this? If you propose a fix, please make it concise.| """Query the MCP server with a question. | ||
|
|
||
|
|
||
|
|
||
| Args: | ||
|
|
||
| question: The question to ask about the codebase | ||
|
|
||
|
|
||
|
|
||
| Returns: | ||
|
|
||
| Dictionary with the response from the server | ||
|
|
There was a problem hiding this comment.
Docstrings are not allowed per the Comment Policy. Code should be self-documenting. Only comments with (H) prefix are allowed.
Context Used: Rule from dashboard - ## Technical Requirements
Agentic Framework
- PydanticAI Only: This project uses PydanticAI... (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: codebase_rag/mcp/client.py
Line: 25-38
Comment:
Docstrings are not allowed per the Comment Policy. Code should be self-documenting. Only comments with `(H)` prefix are allowed.
**Context Used:** Rule from `dashboard` - ## Technical Requirements
### Agentic Framework
- **PydanticAI Only**: This project uses PydanticAI... ([source](https://app.greptile.com/review/custom-context?memory=d4240b05-b763-467a-a6bf-94f73e8b6859))
How can I resolve this? If you propose a fix, please make it concise.| """Query the code graph via MCP server. | ||
|
|
||
|
|
||
|
|
||
| Example: | ||
|
|
||
| python -m codebase_rag.mcp.client --ask-agent "What functions call UserService.create_user?" | ||
|
|
There was a problem hiding this comment.
Docstrings are not allowed per the Comment Policy. Code should be self-documenting.
Context Used: Rule from dashboard - ## Technical Requirements
Agentic Framework
- PydanticAI Only: This project uses PydanticAI... (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: codebase_rag/mcp/client.py
Line: 88-95
Comment:
Docstrings are not allowed per the Comment Policy. Code should be self-documenting.
**Context Used:** Rule from `dashboard` - ## Technical Requirements
### Agentic Framework
- **PydanticAI Only**: This project uses PydanticAI... ([source](https://app.greptile.com/review/custom-context?memory=d4240b05-b763-467a-a6bf-94f73e8b6859))
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| # Start the MCP server as a subprocess with stderr redirected to /dev/null | ||
|
|
||
| # This suppresses all server logs while keeping stdout/stdin for MCP communication | ||
|
|
||
| with open(os.devnull, "w") as devnull: | ||
| server_params = StdioServerParameters( | ||
| command="python", | ||
| args=["-m", "codebase_rag.main", "mcp-server"], | ||
| ) | ||
|
|
||
| async with stdio_client(server=server_params, errlog=devnull) as (read, write): | ||
| async with ClientSession(read, write) as session: | ||
| # Initialize the session | ||
|
|
||
| await session.initialize() | ||
|
|
||
| # Call the ask_agent tool | ||
|
|
||
| result = await session.call_tool("ask_agent", {"question": question}) | ||
|
|
||
| # Extract the response text | ||
|
|
||
| if result.content: | ||
| response_text = result.content[0].text | ||
|
|
||
| # Parse JSON response | ||
|
|
||
| try: | ||
| parsed = json.loads(response_text) | ||
|
|
||
| if isinstance(parsed, dict): | ||
| return parsed | ||
|
|
||
| return {"output": str(parsed)} | ||
|
|
||
| except json.JSONDecodeError: | ||
| return {"output": response_text} | ||
|
|
||
| return {"output": "No response from server"} | ||
|
|
||
|
|
||
| @app.command() | ||
| def main( | ||
| question: str = typer.Option( | ||
| ..., "--ask-agent", "-a", help="Question to ask about the codebase" | ||
| ), | ||
| ) -> None: | ||
| """Query the code graph via MCP server. | ||
|
|
||
|
|
||
|
|
||
| Example: | ||
|
|
||
| python -m codebase_rag.mcp.client --ask-agent "What functions call UserService.create_user?" | ||
|
|
||
| """ | ||
|
|
||
| try: | ||
| # Run the async query | ||
|
|
||
| result = asyncio.run(query_mcp_server(question)) | ||
|
|
||
| # Print only the output (clean for scripting) | ||
|
|
||
| if isinstance(result, dict) and "output" in result: | ||
| print(result["output"]) | ||
|
|
||
| else: | ||
| print(json.dumps(result)) | ||
|
|
||
| except Exception as e: | ||
| # Print error to stderr and exit with error code | ||
|
|
There was a problem hiding this comment.
Inline comments without (H) prefix violate the Comment Policy. Remove all comments or prefix them with (H) if they provide essential human context that cannot be expressed in code.
Context Used: Rule from dashboard - ## Technical Requirements
Agentic Framework
- PydanticAI Only: This project uses PydanticAI... (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: codebase_rag/mcp/client.py
Line: 41-113
Comment:
Inline comments without `(H)` prefix violate the Comment Policy. Remove all comments or prefix them with `(H)` if they provide essential human context that cannot be expressed in code.
**Context Used:** Rule from `dashboard` - ## Technical Requirements
### Agentic Framework
- **PydanticAI Only**: This project uses PydanticAI... ([source](https://app.greptile.com/review/custom-context?memory=d4240b05-b763-467a-a6bf-94f73e8b6859))
How can I resolve this? If you propose a fix, please make it concise.| command="python", | ||
| args=["-m", "codebase_rag.main", "mcp-server"], | ||
| ) | ||
|
|
||
| async with stdio_client(server=server_params, errlog=devnull) as (read, write): | ||
| async with ClientSession(read, write) as session: | ||
| # Initialize the session | ||
|
|
||
| await session.initialize() | ||
|
|
||
| # Call the ask_agent tool | ||
|
|
||
| result = await session.call_tool("ask_agent", {"question": question}) |
There was a problem hiding this comment.
Hardcoded strings should be moved to a constants file. Create codebase_rag/mcp/constants.py for strings like "python", "-m", "codebase_rag.main", "mcp-server", "ask_agent", "question".
Context Used: Rule from dashboard - ## Technical Requirements
Agentic Framework
- PydanticAI Only: This project uses PydanticAI... (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: codebase_rag/mcp/client.py
Line: 47-59
Comment:
Hardcoded strings should be moved to a constants file. Create `codebase_rag/mcp/constants.py` for strings like `"python"`, `"-m"`, `"codebase_rag.main"`, `"mcp-server"`, `"ask_agent"`, `"question"`.
**Context Used:** Rule from `dashboard` - ## Technical Requirements
### Agentic Framework
- **PydanticAI Only**: This project uses PydanticAI... ([source](https://app.greptile.com/review/custom-context?memory=d4240b05-b763-467a-a6bf-94f73e8b6859))
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| else: | ||
| print(json.dumps(result)) | ||
|
|
||
| except Exception as e: |
There was a problem hiding this comment.
Catching bare Exception is too broad. Catch specific exceptions like ConnectionError, TimeoutError, or create a custom exception type.
Context Used: Rule from dashboard - ## Technical Requirements
Agentic Framework
- PydanticAI Only: This project uses PydanticAI... (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: codebase_rag/mcp/client.py
Line: 111
Comment:
Catching bare `Exception` is too broad. Catch specific exceptions like `ConnectionError`, `TimeoutError`, or create a custom exception type.
**Context Used:** Rule from `dashboard` - ## Technical Requirements
### Agentic Framework
- **PydanticAI Only**: This project uses PydanticAI... ([source](https://app.greptile.com/review/custom-context?memory=d4240b05-b763-467a-a6bf-94f73e8b6859))
How can I resolve this? If you propose a fix, please make it concise.| if result.content: | ||
| response_text = result.content[0].text |
There was a problem hiding this comment.
No type narrowing before accessing result.content[0]. This will raise IndexError if content is empty. Add length check:
| if result.content: | |
| response_text = result.content[0].text | |
| if result.content and len(result.content) > 0: | |
| response_text = result.content[0].text |
Prompt To Fix With AI
This is a comment left during a code review.
Path: codebase_rag/mcp/client.py
Line: 63-64
Comment:
No type narrowing before accessing `result.content[0]`. This will raise `IndexError` if content is empty. Add length check:
```suggestion
if result.content and len(result.content) > 0:
response_text = result.content[0].text
```
How can I resolve this? If you propose a fix, please make it concise.| except Exception as e: | ||
| # Print error to stderr and exit with error code | ||
|
|
||
| print(f"Error: {str(e)}", file=sys.stderr) |
There was a problem hiding this comment.
Use loguru for error logging instead of print() to stderr:
| print(f"Error: {str(e)}", file=sys.stderr) | |
| logger.error(f"Error: {str(e)}") | |
| sys.exit(1) |
Context Used: Rule from dashboard - ## Technical Requirements
Agentic Framework
- PydanticAI Only: This project uses PydanticAI... (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: codebase_rag/mcp/client.py
Line: 114
Comment:
Use `loguru` for error logging instead of `print()` to stderr:
```suggestion
logger.error(f"Error: {str(e)}")
sys.exit(1)
```
**Context Used:** Rule from `dashboard` - ## Technical Requirements
### Agentic Framework
- **PydanticAI Only**: This project uses PydanticAI... ([source](https://app.greptile.com/review/custom-context?memory=d4240b05-b763-467a-a6bf-94f73e8b6859))
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
|




New module codebase_rag/mcp/client.py that spawns the MCP server as a subprocess and calls the ask_agent tool, printing only clean output to stdout. Useful for scripting and CI pipelines.
Dependency graph
Merge in this order: