Skip to main content
Glama
GeBakaev

mcp-investment-data

by GeBakaev
README.md
# mcp-investment-data

A small **Model Context Protocol (MCP)** server that exposes investment-data tools to any MCP host (Claude Desktop, the MCP Inspector, or a custom client). It's the warm-start for an AI-native data layer.

## What it exposes

Four tools over a **~10,000-company synthetic firmographic dataset** (generated by `data.py` — deterministic, no external data):

- `search_companies(query, hq, limit)` — match companies by name, sector, or HQ; `hq` accepts a region keyword (`MENA`, `Gulf`/`GCC`, `Europe`) or a country
- `get_company(name)` — one company's full record
- `get_signals(name)` — a company's financial-intent signals (intent score, hiring velocity, web-traffic trend, news mentions)
- `list_recent_funding(sector, since, hq, limit)` — recent funding rounds, newest first, filterable by sector, date, and region/country

No JSON Schema, no request parsing, no validation code — the type hints _are_ the schema. That's the point of MCP: business logic in, protocol handled for you.

The dataset is generated, not real: `data.py` produces 10k companies with firmographics (sector, HQ, headcount, funding) and mock "financial-intent" signals — the shape of the investment-data problem, without shipping anyone's real data.

## Run it

Requires Python 3.10+.

```bash
pip install -r requirements.txt
```

**Verify it (no extra tooling) —** a tiny MCP client that spawns the server, does the handshake, and calls a tool:

```bash
python test_client.py
```

Expected (abridged):

```
Connected. Tools: ['search_companies', 'get_company', 'get_signals', 'list_recent_funding']

search_companies('Open Banking', limit=3): ...
get_signals('<company>'): {"intent_score": ..., "hiring_velocity_90d": ..., ...}
list_recent_funding('Crypto Exchange', since='2026-01-01'): ...
```

**Use it in Claude Desktop —** add to `~/Library/Application Support/Claude/claude_desktop_config.json`, then restart Claude Desktop and ask it to *"use investment-data to search companies for UAE."*

```json
{
  "mcpServers": {
    "investment-data": {
      "command": "/absolute/path/to/python",
      "args": ["/absolute/path/to/server.py"]
    }
  }
}
```

> The `args` **array** is the reliable way to pass paths — unlike a single command string (e.g. the MCP Inspector's box), it never splits on spaces, so a project path containing a space works as-is.

## The agent (thesis-agent)

`agent.py` is a small **agent that consumes this server** — give it a fund thesis in plain English and it returns a ranked shortlist of candidate companies with reasons:

```bash
export OPENAI_API_KEY=sk-...
python agent.py "early-stage MENA open-banking companies that raised in the last year"
```

It's an **MCP client + an LLM tool-use loop**. The trick: the server's tools _are_ the model's tools — their JSON schemas are passed straight through to the LLM, so adding a tool to `server.py` gives the agent a new ability with no extra glue. The model decides which tools to call (search by sector, check recent funding, pull signals), the agent runs each call against the server, and the model ranks and explains.

Backend is OpenAI (`OPENAI_MODEL`, default `gpt-4o-mini`) — so, unlike the server, the agent needs an `OPENAI_API_KEY`. The server itself stays LLM-free.

## Design note (why v1.x, not v2)

The MCP Python SDK's **v2 is a pre-release** (alpha/beta) with breaking changes between builds — the SDK's own README says not to use it in production. This repo pins **stable v1.x** (`mcp[cli]>=1.27,<2`) so it keeps working. Deliberate dependency hygiene, not laziness.

## Roadmap

- [x] **v0 — hello world:** two tools over a 5-company in-memory dataset.
- [x] **v1 — real dataset:** ~10k generated firmographic rows + `get_signals` and `list_recent_funding`. _(this)_
- [x] **v2 — agent:** `agent.py` consumes this server — "given a fund's thesis, return the top candidate companies and why." _(this)_
- [ ] **v3 — write-up:** essay "What MCP means for investment-data infrastructure" + a buyer-facing README.