Skip to main content
Glama
shravanthivr

AI Project Explorer

by shravanthivr
README.md
# AI Project Explorer

A Python Model Context Protocol server that exposes GitHub repository exploration tools through local STDIO and remote Streamable HTTP transports.

The server ships two transports:

| Transport | Entry point | Use case |
|-----------|-------------|----------|
| **STDIO** | `server.py` | Local development, MCP Inspector, `client.py`, `llm_client.py` |
| **Streamable HTTP** | `http_server.py` | Portfolio backend, remote deployment |

---

## Architecture

```
Local learning path:
client.py or llm_client.py
        ↓ STDIO
server.py
        ↓
GitHub public REST API

Portfolio production path:
Portfolio Express backend
        ↓ Streamable HTTP + bearer token
http_server.py
        ↓
Shared MCP tools (app/mcp_server.py)
        ↓
GitHub public REST API
```

```mermaid
flowchart LR
    L[LinkedIn Featured link] --> P[Portfolio Ask AI page]
    P --> B[Portfolio Express backend and LLM host]
    B -->|Streamable HTTP and bearer token| M[Remote Python MCP server]
    M --> T1[list_repositories]
    M --> T2[get_repository_readme]
    T1 --> G[GitHub public REST API]
    T2 --> G

    C[Local client.py or llm_client.py] -->|STDIO| S[Local MCP server]
    S --> T1
    S --> T2
```

---

## Project structure

```
ai-project-explorer/
  server.py           — STDIO entry point (local dev, MCP Inspector)
  http_server.py      — Streamable HTTP entry point (production)
  remote_client.py    — Smoke-test client for the HTTP server
  client.py           — Manual STDIO client
  llm_client.py       — LLM-powered STDIO client
  app/
    __init__.py
    config.py         — Environment variable configuration and validation
    github_client.py  — GitHub public REST API calls
    mcp_server.py     — Shared MCPServer instance + tool definitions
  tests/
    test_github_client.py
    test_mcp_tools.py
    test_http_server.py
  .env.example
  Dockerfile
  .dockerignore
  requirements.txt
```

---

## MCP tools

Both transports expose the same two tools:

### `list_repositories(username, limit=10)`
Lists recently-updated public GitHub repositories for a user.

### `get_repository_readme(username, repository)`
Fetches the raw README content for a repository.

Tool names and JSON schemas are identical between STDIO and HTTP transports.

---

## Stateless HTTP operation

The HTTP server uses `stateless_http=True` when mounting the MCP transport. Both tools are simple request/response operations with no shared state between calls, so a session manager is unnecessary. Stateless mode is simpler to deploy and scale horizontally.

---

## Setup

```bash
# Clone
git clone https://github.com/shravanthivr/ai-project-explorer.git
cd ai-project-explorer

# Create virtual environment
python -m venv .venv
source .venv/bin/activate   # Windows: .venv\Scripts\activate

# Install dependencies
pip install -r requirements.txt

# Copy environment template
cp .env.example .env
# Edit .env and fill in optional values
```

For local development, leave `ALLOWED_GITHUB_USERNAME` unset to allow lookups
for any public GitHub username. For a public portfolio deployment, set it to
the single account your deployment should serve:

```bash
ALLOWED_GITHUB_USERNAME=your-github-username
```

---

## Commands

### Run the STDIO server (local dev)
```bash
python server.py
```

### Run the manual STDIO client
```bash
python client.py
```

### Run the LLM-powered STDIO client
```bash
python llm_client.py
```

### Run the MCP Inspector
```bash
npm run inspector
```

### Run the Streamable HTTP server
```bash
uvicorn http_server:app --host 0.0.0.0 --port 8000
# or
python http_server.py
```

### Check the health endpoint
```bash
curl http://localhost:8000/healthz
# → {"status":"ok","service":"ai-project-explorer-mcp"}
```

### Run the remote smoke-test client
```bash
# Set the server URL (and optional token)
export MCP_SERVER_URL=http://localhost:8000
export MCP_SERVER_AUTH_TOKEN=your-token   # if auth is enabled
export REMOTE_CLIENT_GITHUB_USER=your-github-username

# Discover tools only
python remote_client.py

# Discover tools and call list_repositories
python remote_client.py --list-repos

# Or pass the username explicitly
python remote_client.py --list-repos --username your-github-username
```

### Run tests
```bash
pytest tests/ -v
```

### Build and run the Docker image
```bash
docker build -t ai-project-explorer .
docker run --rm -p 8000:8000 \
  -e PORT=8000 \
  -e ALLOWED_GITHUB_USERNAME=your-github-username \
  -e MCP_SERVER_AUTH_TOKEN=your-token \
  ai-project-explorer
```

---

## Environment variables

| Variable | Default | Required | Description |
|----------|---------|----------|-------------|
| `HOST` | `0.0.0.0` | No | Bind address for HTTP server |
| `PORT` | `8000` | No | Bind port for HTTP server |
| `ALLOWED_GITHUB_USERNAME` | *(unset)* | Production | Restricts tools to this username only when configured |
| `GITHUB_API_BASE_URL` | `https://api.github.com` | No | GitHub API base URL |
| `GITHUB_REQUEST_TIMEOUT_SECONDS` | `10` | No | GitHub request timeout |
| `MAX_REPOSITORIES` | `30` | No | Maximum repositories returned |
| `MAX_README_CHARACTERS` | `30000` | No | README truncation limit |
| `GITHUB_TOKEN` | *(unset)* | No | GitHub token (raises rate limit to 5 000/hr) |
| `MCP_SERVER_AUTH_TOKEN` | *(unset)* | Production | Bearer token for `/mcp` auth |

---

## Security model

- **Browser → MCP**: The browser never calls this server directly. Only the portfolio Express backend does.
- **Browser → GitHub**: The browser never calls GitHub. All GitHub requests happen server-side.
- **MCP token**: The bearer token is held only by the portfolio backend. It is never exposed to the browser or frontend code.
- **OpenAI credentials**: Remain in the portfolio backend. This server has no knowledge of them.
- **Username restriction**: Source code is reusable by default. Leave `ALLOWED_GITHUB_USERNAME` unset for local development or unrestricted self-hosted use. Set it in a public deployment to restrict the MCP tools to one GitHub account; calls for any other username are rejected before reaching GitHub.
- **Public data only**: Only public GitHub repositories and READMEs are accessible. No authentication to GitHub is needed for reading public data; the optional `GITHUB_TOKEN` only raises the unauthenticated rate limit.

---

## MCP versus direct REST

This project intentionally uses MCP for several reasons:

**MCP adds value because:**
- The portfolio LLM can discover tools dynamically via `list_tools()`.
- Tool schemas are standardised — the model receives typed parameter definitions.
- Tool execution is decoupled from model orchestration (the Express backend decides how to call tools; the Python server just executes them).
- The same tools are reused by local STDIO clients and the deployed portfolio backend.
- More tools can be added later (e.g. `search_code`, `list_issues`) without redesigning the frontend API contract.

**A direct REST endpoint would be simpler when:**
- There is only one fixed operation.
- No model chooses or sequences tools.
- Tool discovery and interoperability are unnecessary.
- The service is only used by one tightly-coupled client.

This project intentionally uses MCP as a learning and portfolio demonstration, while recognising that a direct REST endpoint would require fewer components for this small two-tool use case.

---

## Why I built this

I wanted to understand MCP by building a real tool — learning how clients discover tools, how servers expose capabilities, how AI assistants invoke external functions, and the difference between local (STDIO) and remote (Streamable HTTP) transports.