Skip to content

adwibha/mcp-cli-agent

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

mcp-cli-agent

Origin: This repository is based on the starter project provided as part of the Anthropic MCP Course on Skilljar. The original project was a skeleton implementation intended as a course exercise. I downloaded it, completed the TODOs, extended the architecture to support multiple LLM providers (Google Gemini and Perplexity in addition to Anthropic), and used it as a personal learning sandbox for exploring the Model Context Protocol.


Overview

MCP Chat is a command-line interface application that enables interactive conversation with AI language models through the Model Context Protocol (MCP). The application supports document retrieval via @mentions, slash-command-driven prompts, tab completion, and extensible tool integrations — all backed by a pluggable LLM provider layer.


Supported LLM Providers

The provider is selected via a single environment variable. All three providers share the same code interface, so switching between them requires no code changes.

Provider Recommended Model Free Tier API Key
Anthropic claude-haiku-4-5 No — paid credits required console.anthropic.com
Google Gemini gemini-2.0-flash Yes — via AI Studio aistudio.google.com
Perplexity sonar Yes — $5 trial credit perplexity.ai/api

Prerequisites

  • Python 3.10 or higher
  • An API key for at least one of the supported providers above

Setup

Step 1: Configure environment variables

Copy .env.example to .env and fill in your values:

cp .env.example .env

Open .env and set LLM_PROVIDER to your chosen provider, then provide the corresponding model name and API key:

# Choose your provider: anthropic | gemini | perplexity
LLM_PROVIDER="perplexity"

# Anthropic
CLAUDE_MODEL="claude-haiku-4-5"
ANTHROPIC_API_KEY=""

# Google Gemini
GEMINI_MODEL="gemini-2.0-flash"
GEMINI_API_KEY=""

# Perplexity
PERPLEXITY_MODEL="sonar"
PERPLEXITY_API_KEY=""

Only the key and model for the selected provider need to be filled in.

Switching providers

Update the single LLM_PROVIDER line in .env:

LLM_PROVIDER="anthropic"   # Use Anthropic Claude
LLM_PROVIDER="gemini"      # Use Google Gemini
LLM_PROVIDER="perplexity"  # Use Perplexity

Step 2: Install dependencies

Option A — Using uv (recommended)

uv is a fast Python package manager.

# Install uv if not already installed
pip install uv

# Create virtual environment and install dependencies
uv venv
source .venv/bin/activate  # Windows: .venv\Scripts\activate
uv pip install -e .

# Run
uv run main.py

Option B — Using standard pip

python -m venv .venv
source .venv/bin/activate  # Windows: .venv\Scripts\activate
pip install -e .

python main.py

Usage

Basic conversation

Type any message and press Enter:

> What is the boiling point of water?

Document references

Prefix a document ID with @ to include its content in your query:

> Summarize @deposition.md
> Compare @report.pdf and @outlook.pdf

Document IDs auto-complete when you type @.

Slash commands

Use / to invoke an MCP prompt. Available commands auto-complete on Tab:

> /summarize deposition.md
> /rewrite_as_markdown report.pdf

Keyboard shortcuts

Key Action
Tab Open completion menu
/ Open command completion
@ Open document completion
Ctrl+C Exit

Project Structure

.
├── main.py               # Entry point — provider selection and app bootstrap
├── mcp_server.py         # MCP server exposing docs as tools, resources, and prompts
├── mcp_client.py         # MCP client wrapper (async, stdio transport)
├── core/
│   ├── chat.py           # Base agentic loop (tool-use handling)
│   ├── cli_chat.py       # CLI-aware chat: document retrieval and command handling
│   ├── cli.py            # Interactive REPL (prompt_toolkit, completions, key bindings)
│   ├── tools.py          # MCP tool aggregation and dispatch
│   ├── claude.py         # Anthropic provider
│   ├── gemini.py         # Google Gemini provider
│   └── perplexity.py     # Perplexity provider
├── .env.example          # Environment variable template
└── pyproject.toml        # Project metadata and dependencies

Key Learnings

Model Context Protocol (MCP)

Building MCP server and client implementations revealed the protocol's power as a bridge between applications and LLMs. Key insights:

  • Resource abstraction — exposing documents as MCP resources enables structured LLM access without embedding data in prompts
  • Tool composition — organizing LLM-invocable functions as MCP tools scales better than hardcoded function calls
  • Protocol simplicity — the stdio transport and JSON-RPC foundation make MCP straightforward to reason about

Provider Abstraction Patterns

Implementing three distinct LLM providers (Anthropic, Gemini, Perplexity) exposed both commonalities and critical differences:

  • Unified interfaces — abstracting chat, message formatting, and response parsing into provider-agnostic methods reduces coupling
  • API divergence — each provider has different tool schemas, error handling, and streaming semantics; a thin adapter layer handles these elegantly
  • Cost vs. capability tradeoffs — free-tier availability (Gemini, Perplexity) vs. enterprise features (Anthropic) shapes real-world deployment decisions

Async Python & Context Management

This project relies heavily on Python's async ecosystem for concurrent I/O:

  • AsyncExitStack — managing multiple async context managers (MCP clients) in a single cleanup operation prevents resource leaks
  • Async generators & streaming — handling streamed responses from LLM APIs without blocking the CLI requires careful async/await patterns
  • Event loop lifecycle — Windows requires explicit event loop policy configuration; macOS/Linux default behavior can hide platform-specific issues

CLI Design with prompt-toolkit

Building an interactive REPL with tab completion and keybindings taught practical UX patterns:

  • Completion strategies — lazy-loading completions (documents, commands) keeps the CLI responsive even with many resources
  • Prompt state — maintaining context across turns (MCP connections, chat history) while remaining reactive to user input
  • Keyboard UX — well-chosen shortcuts (Tab for completions, Ctrl+C for exit) significantly improve usability without documentation

Development

Adding new documents

Edit the docs dictionary in mcp_server.py:

docs = {
    "my_document.md": "Document content goes here.",
    ...
}

Adding a new LLM provider

  1. Create core/<provider>.py implementing four methods:
    • chat(messages, system, tools, ...) — send a request, return a response with .stop_reason
    • add_user_message(messages, message) — append a user turn
    • add_assistant_message(messages, message) — append an assistant turn
    • text_from_message(message) — extract text from a response object
  2. Import and wire it into build_llm_service() in main.py
  3. Add the provider SDK to pyproject.toml

Extending MCP capabilities

The MCP server (mcp_server.py) can be extended with additional:

  • Tools — functions the LLM can invoke (decorated with @mcp.tool())
  • Resources — data the client can read (decorated with @mcp.resource(uri))
  • Prompts — reusable prompt templates (decorated with @mcp.prompt())

Additional MCP servers can be connected by passing their scripts as CLI arguments:

uv run main.py path/to/another_server.py

About

A command-line AI chat application built on the Model Context Protocol (MCP), with pluggable support for Anthropic, Google Gemini, and Perplexity.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages