A terminal-based agentic AI system built on the Model Context Protocol (MCP) — the same architecture used by Claude Desktop, Cursor, and other production AI tooling. Nexus connects a free Groq LLM to a custom MCP document server, enabling multi-step reasoning, tool execution, and document management entirely from the command line.
┌─────────────────────────────────────────┐
│ CLI (main.py) │
│ prompt-toolkit interface │
└────────────────┬────────────────────────┘
│
┌────────────────▼────────────────────────┐
│ CliChat / Chat │
│ orchestrates messages & tool loops │
└──────┬─────────────────────┬───────────┘
│ │
┌──────▼──────┐ ┌────────▼────────┐
│ core/ │ │ MCPClient │
│ claude.py │ │ (mcp_client.py)│
│ Groq LLM │ │ stdio transport│
└─────────────┘ └────────┬────────┘
│ subprocess
┌────────▼────────┐
│ mcp_server.py │
│ Tools │
│ Resources │
│ Prompts │
└─────────────────┘
The MCP server runs as a child process of the main application, communicating over stdio — exactly how MCP works in production. The client and server share no memory; all communication happens over the MCP protocol.
The system runs a full agentic loop: the model receives tool schemas, decides which tools to call, the application executes them via the MCP client, results are fed back, and the loop continues until the model has enough information to respond — all transparently within a single user query.
Inject document contents directly into context before the model is called:
> What are the key findings in @deposition.md?
No tool call needed — the content is embedded in the prompt automatically.
Trigger server-side prompt templates with tab autocomplete:
> /summarize report.pdf
> /format plan.md
Prompts are defined, tested, and maintained on the server — clients consume them without needing to know the prompt internals.
| Type | Name | Description |
|---|---|---|
| Tool | read_doc_contents |
Read a document by ID |
| Tool | edit_document |
Find-and-replace within a document |
| Resource | docs://documents |
List all document IDs |
| Resource | docs://documents/{doc_id} |
Fetch a specific document |
| Prompt | format |
Rewrite a document in Markdown |
| Prompt | summarize |
Produce a concise document summary |
User input
│
▼
Build message list ──► Send to Groq
│
stop_reason == "tool_use"?
YES │ NO │
▼ ▼
Execute tools Return final
via MCPClient text response
│
▼
Append tool results
to message list
│
└──► Send to Groq again
| Component | Library |
|---|---|
| LLM API | Groq — free tier, Llama 3.3 70B |
| MCP framework | mcp[cli] Python SDK |
| Terminal UI | prompt-toolkit |
| Env management | python-dotenv |
- Python 3.10+
- A free Groq API key
Create a .env file in the project root:
GROQ_API_KEY="" # Your Groq API key
GROQ_MODEL="llama-3.3-70b-versatile"
USE_UV=0 # Set to 1 if using uvuv is a fast Python package installer and resolver.
pip install uv
uv venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
uv pip install -e .python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install groq "mcp[cli]>=1.8.0" prompt-toolkit python-dotenv# With uv
uv run main.py
# Without uv
python main.py> What is the boiling point of water?
> Tell me about @deposition.md
> Compare @report.pdf and @outlook.pdf
> /summarize deposition.md
> /format plan.md
Press Tab after / to autocomplete available commands.
Install uv first (pip install uv), then:
mcp dev mcp_server.pyOpens a browser-based inspector at http://127.0.0.1:6274 — test all tools, resources, and prompts without running the full application.
├── main.py # Entry point — wires everything together
├── mcp_server.py # MCP server: tools, resources, prompts
├── mcp_client.py # MCP client wrapper (stdio transport)
├── core/
│ ├── claude.py # Groq LLM adapter (Anthropic-compatible interface)
│ ├── chat.py # Base chat loop with agentic tool-use
│ ├── cli_chat.py # CLI-specific chat: @mentions, /commands
│ ├── cli.py # Terminal UI (prompt-toolkit)
│ └── tools.py # Tool schema conversion & execution
├── .env # API keys (not committed)
└── pyproject.toml
Edit the docs dictionary in mcp_server.py:
docs = {
"your-file.md": "Document content goes here.",
...
}All MCP server features are defined in mcp_server.py using decorators:
@mcp.tool(name="my_tool", description="...")
def my_tool(param: str = Field(description="...")):
...
@mcp.resource("docs://my-resource")
def my_resource() -> str:
...
@mcp.prompt(name="my_prompt", description="...")
def my_prompt(doc_id: str = Field(...)) -> list[base.Message]:
...API adapter layer — Originally designed for the Anthropic SDK. core/claude.py contains a full message-format adapter that converts Anthropic-style conversation history and tool schemas to the OpenAI-compatible format Groq expects. No other file required changes — the abstraction boundary held cleanly.
stdio transport — The MCP server communicates over standard input/output, not HTTP. This is the MCP stdio transport — the same mechanism used by Claude Desktop and other MCP hosts in production.
In-memory document store — Documents are held in memory and do not persist between sessions. Persistent storage via SQLite or the filesystem is the natural next step.