MCP Server
README.md
# MCP server · TypeScript · LangChain
[](https://www.typescriptlang.org/)
[](https://nodejs.org/)
[](LICENSE)
Minimal **[Model Context Protocol](https://modelcontextprotocol.io/)** server in **TypeScript** with a **LangChain / OpenAI** assistant behind an MCP entrypoint (`assist`), plus thin tools for deterministic JSON (`getWeather`, `addNumbers`) and a TTS helper (`textToSpeech`). Exposes MCP over **stdio** (stdin/stdout JSON-RPC)—not Express/HTTP—for hosts like Cursor, Claude Desktop, or custom clients.
<!--
[](https://github.com/FedericaZuccarello/actions/workflows/ci.yml)
-->
## Why this repo
Demonstrates integrating **external LLM tooling** with a **narrow MCP surface**:
- MCP **stdio transport** + **Zod**-typed tool inputs
- **Server-side** routing: one NL entrypoint (`assist`) runs an internal agent; no extra “routing” tools on the wire
- **LangChain** `createAgent` with small domain tools (weather via Open-Meteo, math helpers)
- **TypeScript** strict mode, `tsc` build to `dist/`, demo scripts via `tsx`
## Quick start
```bash
git clone <your-fork-url>
cd mcp-server
nvm use # optional; reads .nvmrc (Node 20)
npm install
cp .env.example .env # set OPENAI_API_KEY for assist
npm run build
npm start # MCP on stdio (after pre-build)
```
Try the bundled demos (they build first, then spawn `dist/mcp/index.js`):
```bash
npm run chat -- "What is the weather in London? One short sentence."
npm run mcp:demo-client -- assist '{"message":"What is the weather in London? One short sentence."}'
```
## Architecture
```mermaid
flowchart LR
Host[MCP host] -->|stdio JSON-RPC| MCP[MCP server]
MCP --> assist["tool: assist"]
MCP --> getWeather["tool: getWeather"]
MCP --> addNumbers["tool: addNumbers"]
MCP --> textToSpeech["tool: textToSpeech (optional)"]
assist --> Agent[LangChain agent + OpenAI]
Agent --> W[Weather tool]
Agent --> M[Math tools]
getWeather --> API[Open-Meteo / geocoding]
```
### Design choices
Hosts should send **natural language through `assist`** so prompts, tool boundaries, and model config stay **on your machine**. Structured integrations can call **`getWeather`** or **`addNumbers`** directly when inputs are already known (no NL).
| MCP tool | Purpose |
|------------|---------|
| **`assist`** | Default. `{ message }` → runs the internal LangChain assistant (tools live in-process). |
| **`getWeather`** | Raw JSON for a known city (Open-Meteo, no API key). |
| **`addNumbers`** | Raw JSON sum of two numbers. |
| **`textToSpeech`** | Text → MP3 audio (MCP audio + metadata). |
`OPENAI_API_KEY` must be visible to the **MCP server process** for **`assist`** (e.g. `.env` loaded from `dotenv`, or env vars in your host’s MCP config—**never commit secrets**).
## Layout
| Path | Role |
|------|------|
| `src/mcp/index.ts` | MCP server bootstrap, stdio transport. |
| `src/mcp/tools/assist.ts` | NL → `runAgent(...)`. |
| `src/mcp/tools/weather.ts`, `math.ts` | Thin MCP tools. |
| `src/agents/` | Registry, `run.ts`, LangChain tools, `general` agent definition. |
| `src/services/` | HTTP/domain helpers (weather, math). |
| `scripts/llm-chat.ts` | Example host (LangChain + `MultiServerMCPClient`). |
| `scripts/mcp-stdio-client-demo.ts` | Raw MCP SDK client demo. |
`npm run build` emits **`dist/`** (listed in `.gitignore`).
## Commands
| Script | What it does (demo/testing) |
|--------|--------------|
| `npm run build` | Compile with `tsc` → **`dist/`**. |
| `npm test` | Same as **`build`** (CI smoke check). |
| `npm start` | Build (prestart), then **`node dist/mcp/index.js`** (starts the MCP server). |
| `npm run dev` | **`tsx watch src/mcp/index.ts`** — no prior build. |
| `npm run chat -- "…"` | Demo host running the **whole agent system** via MCP `assist` (recommended). |
| `npm run mcp:demo-client` | Demo/testing MCP client. Call tools individually (e.g. `... -- getWeather '{"city":"London"}'`). |
| `npm run speak -- "…"` | ElevenLabs TTS smoke test (needs `ELEVENLABS_API_KEY`). Uses **`ffplay`** when on PATH; otherwise opens a temp MP3 in your default player. |
### Examples: run everything vs tools only
Run the whole agent system (natural language → `assist` → internal LangChain agent → final text):
```bash
npm run chat -- "What is the weather in London? How is 34+7?"
```
Same flow, but calling `assist` explicitly with an object payload:
```bash
npm run mcp:demo-client -- assist '{"message":"What is the weather in London? How is 34+7?"}'
```
Testing individual MCP tools (not required for the full agent):
```bash
npm run mcp:demo-client -- getWeather '{"city":"London"}'
npm run mcp:demo-client -- addNumbers '{"a":34,"b":7}'
npm run mcp:demo-client -- textToSpeech '{"text":"Hello from the server"}'
```
### Cursor & Inspector
After **`npm run build`**, point the host at **`node /absolute/path/to/dist/mcp/index.js`**.
**[MCP Inspector](https://github.com/modelcontextprotocol/inspector):** `npx -y @modelcontextprotocol/inspector node dist/mcp/index.js` from the repo root. Pass **`assist`** secrets with `-e OPENAI_API_KEY=$OPENAI_API_KEY` where needed.
## FFmpeg and ffplay (optional)
The **`npm run speak`** script can play MP3 in the terminal using **`ffplay`**, which ships with **[FFmpeg](https://ffmpeg.org/)**. If `ffplay` is not on your **PATH**, the script still works by saving a short MP3 under your temp folder and opening it with the OS default app; install FFmpeg when you want in-terminal playback without that fallback.
### Windows
1. **winget** (built into Windows 10/11):
```powershell
winget install --id Gyan.FFmpeg --source winget --accept-package-agreements --accept-source-agreements
```
2. **Close and reopen** your terminal (or Cursor’s integrated terminal) so the updated **PATH** includes the FFmpeg `bin` directory.
3. Confirm `ffplay` is visible:
```powershell
ffplay -version
```
If `ffplay` is still not found, add the folder that contains **`ffplay.exe`** manually: **Settings → System → About → Advanced system settings → Environment Variables → Path → Edit → New** (typical install paths look like `C:\ffmpeg\bin` or under `%LOCALAPPDATA%\Microsoft\WinGet\Packages\…`).
**Manual download:** [gyan.dev FFmpeg builds](https://www.gyan.dev/ffmpeg/builds/) (full or essentials build), unzip, then add the `bin` folder to **PATH** as above.
### macOS
```bash
brew install ffmpeg
ffplay -version
```
### Linux (Debian / Ubuntu example)
```bash
sudo apt update && sudo apt install -y ffmpeg
ffplay -version
```
Other distributions: install the **`ffmpeg`** package from your package manager; `ffplay` is included in the same package.
## Contributing
Issues and PRs welcome. Extend internal agents via `src/agents/` and **`assist.ts`** routing if you split personas.
### Checklist before you promote the repo
- Set **`author`** and (optionally) **`repository`** / **`homepage`** / **`bugs`** in [`package.json`](package.json) so npm and GitHub link correctly.
- Replace `OWNER/REPO` in the commented CI badge when the remote exists, then uncomment the line.
- Add a **GitHub profile README** or link this repo from your CV/LinkedIn—recruiters land here first from the link.
- Set the repo **About** description and topics (e.g. `mcp`, `langchain`, `typescript`, `ai-agents`).
## License
**ISC** — see [`LICENSE`](LICENSE).
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues