Skip to main content
Glama
README.md
# Stream MCP Server

An [MCP](https://modelcontextprotocol.io/) server for the **Stream** (streampay.sa) payment platform, built with [FastMCP](https://github.com/jlowin/fastmcp).

Exposes **27 tools** across six resource domains — payment links, customers, products, coupons, invoices, and payments — plus a read-only OpenAPI documentation resource.

---

## Quick Start

### 1. Install

```bash
# Clone & install in editable mode
git clone <repo-url> stream-mcp-server && cd stream-mcp-server
pip install -e ".[dev]"
```

### 2. Configure

```bash
cp .env.example .env
# Edit .env and set your Stream API key:
#   STREAM_API_KEY=sk_live_…
```

| Variable | Default | Description |
|---|---|---|
| `STREAM_API_KEY` | *(required for stdio mode)* | Your Stream API key |
| `STREAM_BASE_URL` | `https://stream-app-service.streampay.sa` | API base URL |
| `STREAM_OPENAPI_URL` | `https://stream-app-service.streampay.sa/openapi.json` | OpenAPI spec URL (independent of `STREAM_BASE_URL`, e.g. staging) |
| `STREAM_TIMEOUT` | `30` | Request timeout (seconds) |
| `STREAM_MAX_RETRIES` | `2` | Retry count for 429 / 5xx |
| `HOST` | `0.0.0.0` | Remote server bind host (`stream-mcp-remote`) |
| `PORT` | `8000` | Remote server bind port (`stream-mcp-remote`) |

### 3. Run

#### Local stdio mode (recommended)

```bash
stream-mcp
```

#### Remote HTTP mode (URL clients)

```bash
# No STREAM_API_KEY needed on the server process in remote mode
HOST=0.0.0.0 PORT=8000 stream-mcp-remote
```

Endpoint:

```text
http://localhost:8000/mcp
```

---

## MCP Client Configuration (Claude Desktop / Cursor / VS Code)

Use one of these two patterns in your MCP config file (`claude_desktop_config.json` or `mcp.json`).

### Option A: stdio

```json
{
  "mcpServers": {
    "stream": {
      "command": "stream-mcp",
      "env": {
        "STREAM_API_KEY": "sk_live_your_key_here"
      }
    }
  }
}
```

### Option B: remote URL (`stream-mcp-remote`)

```json
{
  "mcpServers": {
    "stream": {
      "url": "http://localhost:8000/mcp",
      "headers": {
        "Authorization": "Bearer sk_live_your_key_here"
      }
    }
  }
}
```

---

## Available Tools

### Payment Links
| Tool | Description |
|---|---|
| `create_payment_link` | Create a new checkout / payment link |
| `list_payment_links` | Paginated list with optional status filter |
| `get_payment_link` | Get a single payment link by ID |
| `deactivate_payment_link` | Deactivate / archive a payment link |

### Customers
| Tool | Description |
|---|---|
| `create_customer` | Create a customer with name, email, phone, metadata |
| `list_customers` | Paginated list of customers |
| `get_customer` | Get a single customer by ID |
| `update_customer` | Update customer fields |
| `delete_customer` | Soft-delete a customer |

### Products
| Tool | Description |
|---|---|
| `create_product` | Create a one-time or recurring product |
| `list_products` | List products with optional type filter |
| `get_product` | Get a single product by ID |
| `update_product` | Update product name, description, or price |
| `archive_product` | Archive a product |

### Coupons
| Tool | Description |
|---|---|
| `create_coupon` | Create a fixed or percentage discount coupon |
| `list_coupons` | List coupons with optional status filter |
| `get_coupon` | Get a single coupon by ID |
| `deactivate_coupon` | Deactivate a coupon |

### Invoices
| Tool | Description |
|---|---|
| `create_invoice` | Create a ZATCA-compliant invoice |
| `list_invoices` | List invoices with filters |
| `get_invoice` | Get a single invoice by ID |
| `send_invoice` | (Re)send an invoice via email / SMS |
| `void_invoice` | Void / cancel an unpaid invoice |

### Payments
| Tool | Description |
|---|---|
| `list_payments` | List payments with filters |
| `get_payment` | Get payment details |
| `refund_payment` | Issue a full or partial refund |

### Resources
| Resource URI | Description |
|---|---|
| `stream://docs/openapi` | Full Stream OpenAPI spec from the API host (cached, auto-refreshed) |

---

## Remote Deployment (Hosted URL)

You can deploy the MCP server as a **hosted URL** so users connect to it remotely.

Each user passes their own Stream API key as a Bearer token.

### 1. Run locally (remote mode)

```bash
# No STREAM_API_KEY needed — each user provides their own
stream-mcp-remote
# → Listening on http://0.0.0.0:8000

# Custom host/port
HOST=0.0.0.0 PORT=3000 stream-mcp-remote
```

### 2. Deploy with Docker

```bash
docker build -t stream-mcp .
docker run --rm -p 8000:8000 -e HOST=0.0.0.0 stream-mcp
```

If you use `--env-file` from a **local** `.env` that sets `HOST=127.0.0.1`, the process listens only on loopback **inside** the container, and the host will see `ECONNREFUSED` on `localhost:8000`. Either omit `HOST` in that file for Docker, or pass `-e HOST=0.0.0.0` **after** `--env-file` so it overrides.

### 3. How users connect (remote)

Users add this to their MCP client config:

**Claude Desktop / VS Code:**
```json
{
  "mcpServers": {
    "stream": {
      "url": "https://your-domain.com/mcp",
      "headers": {
        "Authorization": "Bearer sk_live_YOUR_STREAM_API_KEY"
      }
    }
  }
}
```

> Each user passes their **own** Stream API key as the Bearer token.
> The server never stores keys — they are used only for the duration of the session.

---

## Project Structure

```
src/stream_mcp/
├── server.py          # FastMCP app entry-point (local + remote modes)
├── config.py          # Settings from env vars
├── client.py          # Async HTTP client (auth, retries, errors)
├── auth.py            # Bearer token middleware (remote mode)
├── helpers.py         # get_client() — resolves per-request StreamClient
├── models/            # Pydantic v2 request/response models
│   ├── payment_links.py
│   ├── customers.py
│   ├── products.py
│   ├── coupons.py
│   ├── invoices.py
│   └── payments.py
└── tools/             # FastMCP tool definitions
    ├── __init__.py    # Registers all tools
    ├── payment_links.py
    ├── customers.py
    ├── products.py
    ├── coupons.py
    ├── invoices.py
    ├── payments.py
    └── docs.py        # OpenAPI resource
```

**Adding a new resource domain** = add one file in `models/`, one in `tools/`, and one import line in `tools/__init__.py`.

---

## Error Handling

All tools catch `StreamAPIError` and return a structured dict instead of raising:

```json
{
  "error": true,
  "code": 422,
  "message": "Validation failed: …"
}
```

This ensures the LLM agent always receives a usable response.

---

## Development

```bash
# Install with dev dependencies
pip install -e ".[dev]"

# Run tests
pytest tests/ -v
```

---

## License

MIT

TDQS

A3.5/5.0

Scored across 29 tools

Disambiguation4/5

Most tools have distinct purposes targeting specific resources (e.g., customers, invoices, payments) with clear actions. However, some overlap exists: 'archive_product' and 'deactivate_payment_link' both perform soft-deletes, and 'send_invoice' could be confused with 'create_invoice' with notify_consumer=True. The descriptions help clarify, but there is minor ambiguity.

Naming Consistency5/5

Tool names follow a highly consistent verb_noun pattern throughout (e.g., create_customer, get_invoice, list_payments, update_product). All tools use snake_case with clear, descriptive verbs, making the set predictable and easy to navigate.

Tool Count3/5

With 29 tools, the count feels heavy for an e-commerce/payment processing server. While the domain is broad, many tools are variations (e.g., multiple list/get/update operations) that could potentially be consolidated. It's borderline but manageable, leaning toward slightly excessive.

Completeness5/5

The tool set provides comprehensive CRUD and lifecycle coverage for core resources like customers, products, invoices, payments, and coupons. It includes creation, retrieval, listing, updating, archiving/deactivation, and specific actions like refunding or voiding, with no obvious gaps for the stated domain.

Maintenance

ActivitySlowing
ResponsivenessSyncing