Skip to main content
Glama
deSigntheClutch

mcp-stock-notion-ai

README.md
# mcp-stock-notion-ai

An [MCP](https://modelcontextprotocol.io) server that fetches stock market data,
stores it in **PostgreSQL** as the system of record, and pushes selected results
to **Notion**. Uses the **Streamable HTTP** transport and ships as a container
for **AWS ECS Fargate**.

```
Finnhub  →  Postgres (durable history, queryable)  →  Notion (a view you push to)
```

## Tools

| Tool | Description |
| --- | --- |
| `get_stock_quote(symbol)` | Current price, change, %, day high/low/open, prev close (live, not stored) |
| `get_company_profile(symbol)` | Company name, exchange, industry, market cap |
| `record_quote(symbol)` | Fetch the quote and **store it in Postgres** (the system of record) |
| `push_to_notion(symbol)` | Push the latest stored quote for a symbol **from Postgres to Notion** |
| `get_quote_history(symbol, limit)` | List recently recorded quotes from Postgres, newest first |

The MCP endpoint is served at `POST /mcp`. A `GET /health` endpoint is provided
for load-balancer health checks.

## Prerequisites

1. **Finnhub API key** — free at <https://finnhub.io>.
2. **Notion integration** — create one at
   <https://www.notion.so/my-integrations>, copy the token.
3. **Notion database** with these properties (exact names/types):

   | Property | Type |
   | --- | --- |
   | `Symbol` | Title |
   | `Price` | Number |
   | `Change` | Number |
   | `Percent Change` | Number |
   | `As Of` | Date |

   Then **share the database with your integration** (••• → Connections) and
   copy its database id from the URL.

## Configure

```bash
cp .env.example .env
# then fill in FINNHUB_API_KEY, NOTION_TOKEN, NOTION_DATABASE_ID
```

## Run locally

```bash
docker compose up -d      # start local Postgres (port 5432)
pip install -e ".[dev]"
mcp-stock-notion          # serves http://0.0.0.0:8000/mcp
```

The `quotes` table is created automatically on first use — no migration step
needed for local dev.

Test the health endpoint:

```bash
curl localhost:8000/health   # {"status":"ok"}
```

Point an MCP client (e.g. Claude Desktop / Claude Code) at the Streamable HTTP
URL `http://localhost:8000/mcp`.

## Run the tests

```bash
pytest
```

## Docker

```bash
docker build -t mcp-stock-notion .
docker run --rm -p 8000:8000 --env-file .env mcp-stock-notion
```

## Deploy to AWS ECS Fargate (outline)

1. **Push image to ECR**
   ```bash
   aws ecr create-repository --repository-name mcp-stock-notion
   docker tag mcp-stock-notion:latest <acct>.dkr.ecr.<region>.amazonaws.com/mcp-stock-notion:latest
   docker push <acct>.dkr.ecr.<region>.amazonaws.com/mcp-stock-notion:latest
   ```
2. **Store secrets** in AWS Secrets Manager (or SSM Parameter Store):
   `FINNHUB_API_KEY`, `NOTION_TOKEN`, `NOTION_DATABASE_ID`. Reference them from
   the task definition's `secrets` block so they're never baked into the image.
3. **Task definition**: container port `8000`, Fargate, inject the secrets above.
4. **Service**: behind an Application Load Balancer; target group health check
   path `/health` (HTTP 200). Since MCP Streamable HTTP can hold sessions open,
   enable ALB stickiness and set generous idle timeouts.
5. **TLS**: terminate HTTPS at the ALB (ACM cert) so remote clients connect to
   `https://<your-domain>/mcp`.

## Layout

```
src/mcp_stock_notion/
  server.py   # FastMCP app, tool definitions, /health route
  config.py   # env-based settings (pydantic-settings)
  stock.py    # Finnhub provider (swappable)
  notion.py   # async Notion client
```