Skip to main content
Glama
README.md
# MCP Chat

A React/Next.js web app that gives you a browser chat UI for an LLM running
on the server (via [Ollama](https://ollama.com)), with the model able to call
tools exposed by a Node MCP server.

Under the hood:

1. The MCP server ([src/server.js](src/server.js)) reads its available tools from `tools.js`.
2. It sends those tool definitions to a small local model via Ollama.
3. It executes any tool calls the model requests.
4. It feeds the tool results back to the model and returns the final reply.
5. The web app never does any of this itself — it just calls the MCP server.

## Architecture

```mermaid
flowchart LR
    subgraph Clients[" "]
        direction TB
        Browser["Browser<br/>(page.js)"]
        OtherClient["Other MCP clients<br/>(Claude Desktop, etc.)"]
    end

    Browser -- "fetch, same-origin" --> Route["Next.js<br/>/api/chat route<br/>(thin proxy)"]
    OtherClient -- "MCP protocol" --> Server
    Route -- "POST :3939/chat" --> Server

    subgraph Server["MCP server process (src/server.js)"]
        direction TB
        MCP["POST /mcp<br/>(Streamable HTTP,<br/>real MCP protocol)"]
        Chat["POST /chat<br/>(plain REST)"]
        Tools["tools.js +<br/>ollamaLoop.js"]
        Chat --> Tools
    end

    Tools -- "plain HTTP, not MCP<br/>POST :11434/api/chat" --> Ollama["Ollama (LLM)"]
```

**The web app never talks to Ollama.** The Next.js `/api/chat` route ([web/src/app/api/chat/route.js](web/src/app/api/chat/route.js)) is a thin same-origin proxy — its only job is forwarding the browser's request to the MCP server's `/chat` endpoint and returning the response. It holds no Ollama client code and no tool-calling logic.

All of that lives inside `src/server.js`, the MCP server process, which exposes **two** HTTP endpoints:

- `POST /mcp` — the actual MCP protocol (Streamable HTTP). Any real MCP client (Claude Desktop, another agent, etc.) can connect here to list/call the tools directly, with no Ollama involved at all.
- `POST /chat` — a plain REST endpoint, used only by our own web app. It reads tool definitions straight from `tools.js` (in-process, no MCP round-trip needed since it's the same process), then runs the Ollama tool-calling loop ([src/ollamaLoop.js](src/ollamaLoop.js)): send messages + tool defs to Ollama's `/api/chat`, execute any requested tool calls locally, feed results back, repeat until Ollama replies with plain text.

So Ollama is purely an implementation detail of the MCP server — nothing outside `src/server.js` (and certainly not the browser or the Next.js route) ever calls it directly.

## Available tools

Defined in [src/tools.js](src/tools.js), exposed to the model via the MCP server:

- `get_current_time` — current server date/time.
- `calculate` — evaluates a basic arithmetic expression.
- `list_directory` — lists files/folders relative to the server's working directory.
- `read_readme` — reads this README (or `web/README.md`), so the model can explain how the app works, its architecture, or how to run it.

## Setup

```bash
npm install
cd web && npm install && cd ..

# install Ollama (macOS): https://ollama.com/download
brew install ollama
ollama serve &                # starts the Ollama daemon on :11434
ollama pull llama3.2:3b       # small model with tool-calling support
```

Other small tool-capable models that work too: `qwen2.5:3b`, `mistral-nemo`.
Override with `OLLAMA_MODEL=qwen2.5:3b`.

## Run the MCP server

```bash
npm run server
```

Starts the MCP server on `http://localhost:3939` — `/mcp` (Streamable HTTP MCP
protocol) and `/chat` (plain REST, runs the Ollama loop) both live here.

## Run the web chat

```bash
cd web && npm run dev
```

Open `http://localhost:3000`. The page posts to its own `/api/chat` route,
which just proxies to the MCP server's `/chat` endpoint.
Requires `npm run server` (above) and Ollama to be running.

## Files

- [src/tools.js](src/tools.js) — tool definitions/handlers, registered on the MCP server.
- [src/server.js](src/server.js) — the MCP server: `/mcp` (Streamable HTTP MCP protocol) and `/chat` (REST, runs the Ollama loop).
- [src/ollamaLoop.js](src/ollamaLoop.js) — talks to Ollama's `/api/chat`, runs the tool-calling loop. Used only by `src/server.js`.
- [web/src/app/page.js](web/src/app/page.js) — top-level page, renders `ChatWindow`.
- [web/src/app/api/chat/route.js](web/src/app/api/chat/route.js) — thin same-origin proxy to the MCP server's `/chat`.
- [web/src/hooks/useChat.js](web/src/hooks/useChat.js) — conversation state + `/api/chat` calls, used by `ChatWindow`.
- [web/src/components/ChatWindow.jsx](web/src/components/ChatWindow.jsx) — assembles the chat UI from `MessageList` + `ChatInput`.
- [web/src/components/MessageList.jsx](web/src/components/MessageList.jsx) / [ChatMessage.jsx](web/src/components/ChatMessage.jsx) / [WelcomeMessage.jsx](web/src/components/WelcomeMessage.jsx) — message history rendering.
- [web/src/components/ChatInput.jsx](web/src/components/ChatInput.jsx) — the textarea + Send/Cancel form.

## Testing

```bash
npm test              # MCP server + tools (Node's built-in test runner)
cd web && npm test     # React components + useChat hook (Vitest + Testing Library)
```

- [src/tools.test.js](src/tools.test.js) / [src/server.test.js](src/server.test.js) — tool handlers, and an integration test that spins up the MCP server on an ephemeral port and talks to it over real Streamable HTTP.
- `web/src/**/*.test.jsx` / `*.test.js` — component and hook tests, colocated with the code they test.

## Environment variables

- `OLLAMA_HOST` (default `http://localhost:11434`) — used by `src/server.js`.
- `OLLAMA_MODEL` (default `llama3.2:3b`) — used by `src/server.js`.
- `PORT` (default `3939`) — MCP server port.
- `MCP_SERVER_CHAT_URL` (default `http://localhost:3939/chat`) — used by `web/`'s `/api/chat` proxy route.

Maintenance

ActivitySlowing
ResponsivenessNo issues