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

Packages

Contributors

Languages