# USCardForum MCP Server
A production-ready [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server for interacting with [USCardForum](https://www.uscardforum.com), a Discourse-based community focused on US credit cards, points, miles, and financial optimization.
## Features
- **22 Tools** organized into 4 logical groups:
- ๐ฐ **Discovery** (5) โ Find topics via hot/new/top/search/categories
- ๐ **Reading** (3) โ Access topic content with pagination
- ๐ค **Users** (9) โ Profile research, badges, activity, social
- ๐ **Auth** (5) โ Login, notifications, bookmarks, subscriptions
- **4 Prompts** for guided research workflows (Chinese)
- **3 Resources** for quick data access
- **Multiple Transports** โ stdio, SSE, Streamable HTTP
- **Strongly Typed** with Pydantic domain models
- **Rate Limiting** with exponential backoff
- **Cloudflare Bypass** via cloudscraper
- **Heroku Ready** deployment configuration
## Project Structure
```
uscardforum/
โโโ src/uscardforum/
โ โโโ __init__.py # Package exports
โ โโโ client.py # Main client (composes APIs)
โ โโโ server.py # FastMCP server (MCP layer)
โ โโโ server_core.py # Server configuration
โ โโโ models/ # Domain models (Pydantic)
โ โ โโโ topics.py # Topic, Post, TopicInfo, TopicSummary
โ โ โโโ users.py # UserSummary, UserAction, Badge, etc.
โ โ โโโ search.py # SearchResult, SearchPost, SearchTopic
โ โ โโโ categories.py # Category, CategoryMap
โ โ โโโ auth.py # Session, Notification, Bookmark, etc.
โ โโโ api/ # API modules (backend)
โ โ โโโ base.py # Base API with HTTP methods
โ โ โโโ topics.py # Topic operations
โ โ โโโ users.py # User profile operations
โ โ โโโ search.py # Search operations
โ โ โโโ auth.py # Authentication operations
โ โโโ server_tools/ # MCP tool definitions
โ โโโ utils/ # HTTP and Cloudflare utilities
โโโ tests/ # Integration tests
โโโ .github/workflows/ # CI/CD workflows
โ โโโ ci.yml # Tests, linting, type checking
โ โโโ deploy.yml # Multi-platform deployment
โโโ Dockerfile # Container build
โโโ docker-compose.yml # Local development
โโโ fly.toml # Fly.io configuration
โโโ railway.toml # Railway configuration
โโโ render.yaml # Render blueprint
โโโ koyeb.yaml # Koyeb configuration
โโโ digitalocean-app.yaml # DigitalOcean App Platform
โโโ cloudbuild.yaml # Google Cloud Build
โโโ heroku.yml # Heroku manifest
โโโ app.json # Heroku button config
โโโ Procfile # Heroku process
โโโ pyproject.toml # Python package config
```
## Installation
### Using UV (Recommended)
```bash
# Clone the repository
git clone https://github.com/uscardforum/mcp-server.git
cd uscardforum
# Install with UV
uv sync
# Run the server
uv run uscardforum
```
### Using pip
```bash
# Clone the repository
git clone https://github.com/uscardforum/mcp-server.git
cd uscardforum
# Create virtual environment
python -m venv .venv
source .venv/bin/activate # or .venv\Scripts\activate on Windows
# Install
pip install -e .
# Run
uscardforum
```
## Configuration
### Environment Variables
| Variable | Default | Description |
|----------|---------|-------------|
| `MCP_TRANSPORT` | `stdio` | Transport mode: `stdio`, `sse`, or `streamable-http` |
| `MCP_HOST` | `0.0.0.0` | HTTP server host (for `sse`/`streamable-http`) |
| `MCP_PORT` | `8000` | HTTP server port (for `sse`/`streamable-http`) |
| `NITAN_TOKEN` | *(none)* | Bearer token for MCP auth (`streamable-http` only) |
| `USCARDFORUM_URL` | `https://www.uscardforum.com` | Forum base URL |
| `USCARDFORUM_TIMEOUT` | `15.0` | Request timeout in seconds |
| `NITAN_USERNAME` | *(none)* | Auto-login username (optional) |
| `NITAN_PASSWORD` | *(none)* | Auto-login password (optional) |
### Transport Modes
The server supports three transport modes:
- **`stdio`** (default): Standard input/output, used by Cursor and Claude Desktop
- **`sse`**: Server-Sent Events over HTTP
- **`streamable-http`**: Streamable HTTP transport (recommended for web deployments)
#### Running with Streamable HTTP
```bash
# Start server with streamable HTTP transport
MCP_TRANSPORT=streamable-http MCP_PORT=8000 uv run uscardforum
# The MCP endpoint will be available at:
# http://localhost:8000/mcp
```
#### Streamable HTTP Authentication
When using `streamable-http` transport, you can require clients to authenticate with a bearer token by setting `NITAN_TOKEN`:
```bash
# Start server with authentication required
MCP_TRANSPORT=streamable-http NITAN_TOKEN=my-secret-token uv run uscardforum
# Clients must include Authorization header:
# Authorization: Bearer my-secret-token
```
This is useful for securing public deployments. The token is only enforced for `streamable-http` transport; `stdio` and `sse` modes do not use this authentication.
### Forum Auto-Login
If both `NITAN_USERNAME` and `NITAN_PASSWORD` are set, the server automatically logs into the forum on startup. This enables authenticated features (notifications, bookmarks, subscriptions) without manual login.
### Cursor IDE Integration
Add to `~/.cursor/mcp.json`:
```json
{
"mcpServers": {
"uscardforum": {
"command": "uv",
"args": ["--directory", "/path/to/uscardforum", "run", "uscardforum"],
"env": {
"NITAN_USERNAME": "your_forum_username",
"NITAN_PASSWORD": "your_forum_password"
}
}
}
}
```
### Claude Desktop Integration
Add to Claude Desktop's config file:
**macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
**Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
```json
{
"mcpServers": {
"uscardforum": {
"command": "uv",
"args": ["--directory", "/path/to/uscardforum", "run", "uscardforum"],
"env": {
"NITAN_USERNAME": "your_forum_username",
"NITAN_PASSWORD": "your_forum_password"
}
}
}
}
```
## Deployment
The USCardForum MCP Server supports multiple deployment platforms. Choose the one that best fits your needs.
### Quick Comparison
| Platform | Starting Price | Pros | Best For |
|----------|----------------|------|----------|
| **Heroku** | $7/mo | Easy, one-click deploy | Quick start |
| **Railway** | $5/mo | Simple, GitHub integration | Developers |
| **Render** | $7/mo | Auto-scaling, free tier | Production |
| **Fly.io** | $0-5/mo | Edge deployment, generous free tier | Global reach |
| **Google Cloud Run** | Pay-per-use | Auto-scaling to zero | Variable traffic |
| **DigitalOcean** | $5/mo | Predictable pricing | Self-managed |
| **Koyeb** | $5/mo | Fast deploys, global edge | Low latency |
| **Cloudflare** | Free | Global edge network | Edge deployment |
| **Docker** | Self-hosted | Full control | Privacy-conscious |
---
### Heroku
[](https://heroku.com/deploy?template=https://github.com/uscard-dev/uscardforum-mcp)
```bash
# Manual deployment
heroku login
heroku create your-app-name
# Set environment variables
heroku config:set NITAN_TOKEN=$(openssl rand -hex 32)
heroku config:set NITAN_USERNAME=your_username
heroku config:set NITAN_PASSWORD=your_password
# Deploy
git push heroku main
heroku ps:scale web=1
```
---
### Railway
[](https://railway.app/template/uscardforum-mcp)
```bash
# Install Railway CLI
npm i -g @railway/cli
# Login and deploy
railway login
railway init
railway up
# Set environment variables
railway variables set MCP_TRANSPORT=streamable-http
railway variables set NITAN_TOKEN=$(openssl rand -hex 32)
railway variables set NITAN_USERNAME=your_username
railway variables set NITAN_PASSWORD=your_password
# Open dashboard
railway open
```
---
### Render
[](https://render.com/deploy?repo=https://github.com/uscardforum/mcp-server)
1. Connect your GitHub repository to Render
2. Create a new **Web Service**
3. Select **Docker** as the runtime
4. Set environment variables in the dashboard:
- `MCP_TRANSPORT=streamable-http`
- `NITAN_TOKEN=your-secret-token`
- `NITAN_USERNAME=your-username` (optional)
- `NITAN_PASSWORD=your-password` (optional)
Or use the blueprint file:
```bash
# render.yaml is included in the repository
# Just connect your repo and Render will auto-detect it
```
---
### Fly.io
```bash
# Install Fly CLI
curl -L https://fly.io/install.sh | sh
# Login and launch
fly auth login
fly launch --name uscardforum-mcp
# Set secrets
fly secrets set NITAN_TOKEN=$(openssl rand -hex 32)
fly secrets set NITAN_USERNAME=your_username
fly secrets set NITAN_PASSWORD=your_password
# Deploy
fly deploy
# Check status
fly status
fly logs
```
---
### Google Cloud Run
[](https://shell.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/uscard-dev/uscardforum-mcp&cloudshell_tutorial=docs/cloudrun-tutorial.md)
After clicking, run in Cloud Shell:
```bash
# Deploy to Cloud Run
gcloud run deploy uscardforum-mcp \
--source . \
--region us-west1 \
--platform managed \
--allow-unauthenticated \
--port 8000 \
--memory 512Mi \
--set-env-vars "MCP_TRANSPORT=streamable-http,MCP_HOST=0.0.0.0,MCP_PORT=8000"
```
Or deploy via CLI:
```bash
# Enable required APIs
gcloud services enable run.googleapis.com cloudbuild.googleapis.com
# Deploy directly from source
gcloud run deploy uscardforum-mcp \
--source . \
--region us-west1 \
--platform managed \
--allow-unauthenticated \
--port 8000 \
--memory 512Mi \
--set-env-vars "MCP_TRANSPORT=streamable-http,MCP_HOST=0.0.0.0,MCP_PORT=8000"
# Set secrets (create them first in Secret Manager)
gcloud run services update uscardforum-mcp \
--set-secrets="NITAN_TOKEN=nitan-token:latest"
```
Or use Cloud Build with the included `cloudbuild.yaml`:
```bash
gcloud builds submit --config cloudbuild.yaml
```
---
### DigitalOcean App Platform
```bash
# Install doctl CLI
brew install doctl # or: snap install doctl
# Authenticate
doctl auth init
# Create app from spec
doctl apps create --spec digitalocean-app.yaml
# Or deploy via dashboard:
# 1. Go to https://cloud.digitalocean.com/apps
# 2. Create App โ GitHub โ Select repository
# 3. Configure environment variables
```
---
### Koyeb
```bash
# Install Koyeb CLI
curl -fsSL https://raw.githubusercontent.com/koyeb/koyeb-cli/master/install.sh | sh
# Login and deploy
koyeb login
koyeb app create uscardforum-mcp \
--docker-image ghcr.io/uscardforum/mcp-server:latest \
--ports 8000:http \
--env MCP_TRANSPORT=streamable-http \
--env MCP_PORT=8000
# Set secrets
koyeb secrets create nitan-token --value your-secret-token
koyeb app update uscardforum-mcp --env NITAN_TOKEN=@nitan-token
```
---
### Cloudflare Containers
[](https://deploy.workers.cloudflare.com/?url=https://github.com/uscard-dev/uscardforum-mcp)
---
### Docker (Self-Hosted)
```bash
# Pull from Docker Hub (recommended)
docker pull uscarddev/uscardforum-mcp:latest
# Run the container
docker run -d \
-p 8000:8000 \
-e MCP_TRANSPORT=streamable-http \
-e NITAN_TOKEN=your-secret-token \
--name uscardforum-mcp \
uscarddev/uscardforum-mcp:latest
# Or build locally
docker build -t uscardforum-mcp .
docker run -d \
-p 8000:8000 \
-e MCP_TRANSPORT=streamable-http \
-e NITAN_TOKEN=your-secret-token \
--name uscardforum-mcp \
uscardforum-mcp
# Or use Docker Compose
docker compose up -d
# View logs
docker compose logs -f
```
**Docker Hub**: [`uscarddev/uscardforum-mcp`](https://hub.docker.com/r/uscarddev/uscardforum-mcp)
Available tags:
- `latest` - Latest stable release
- `tagname` - Specific version tags
For production with HTTPS, use a reverse proxy like Traefik or nginx. See `docker-compose.yml` for Traefik example.
---
### Environment Variables Reference
| Variable | Default | Required | Description |
|----------|---------|----------|-------------|
| `MCP_TRANSPORT` | `stdio` | โ | Set to `streamable-http` for web deployment |
| `MCP_HOST` | `0.0.0.0` | | HTTP server bind address |
| `MCP_PORT` | `8000` | | HTTP server port (some platforms override this) |
| `NITAN_TOKEN` | | | Bearer token for MCP authentication |
| `USCARDFORUM_URL` | `https://www.uscardforum.com` | | Forum base URL |
| `USCARDFORUM_TIMEOUT` | `15.0` | | Request timeout in seconds |
| `NITAN_USERNAME` | | | Forum auto-login username |
| `NITAN_PASSWORD` | | | Forum auto-login password |
---
### Connecting to Your Deployed Server
After deployment, connect from Cursor or other MCP clients using the streamable HTTP URL:
```json
{
"mcpServers": {
"uscardforum": {
"url": "https://your-app.fly.dev/mcp",
"headers": {
"Authorization": "Bearer your-nitan-token"
}
}
}
}
```
Replace the URL with your deployment's URL:
- Heroku: `https://your-app.herokuapp.com/mcp`
- Railway: `https://your-app.up.railway.app/mcp`
- Render: `https://your-app.onrender.com/mcp`
- Fly.io: `https://your-app.fly.dev/mcp`
- Cloud Run: `https://your-app-xxxxx-uc.a.run.app/mcp`
- DigitalOcean: `https://your-app.ondigitalocean.app/mcp`
- Koyeb: `https://your-app.koyeb.app/mcp`
## Testing
Run integration tests against the live forum:
```bash
# Set test credentials
export NITAN_USERNAME="your_test_username"
export NITAN_PASSWORD="your_test_password"
# Run tests
uv run pytest tests/ -v
# Run with coverage
uv run pytest tests/ --cov=uscardforum --cov-report=term-missing
```
## Domain Models
All return types are strongly typed with Pydantic models:
### Topic Models
```python
from uscardforum import TopicSummary, TopicInfo, Post
# TopicSummary - for list views
topic: TopicSummary
topic.id # int: Topic ID
topic.title # str: Topic title
topic.posts_count # int: Number of posts
topic.views # int: View count
topic.like_count # int: Total likes
# TopicInfo - detailed metadata
info: TopicInfo
info.post_count # int: Total posts
info.highest_post_number # int: Last post number
# Post - individual post
post: Post
post.id # int: Post ID
post.post_number # int: Position in topic
post.username # str: Author
post.cooked # str: HTML content
post.like_count # int: Likes
```
### User Models
```python
from uscardforum import UserSummary, UserAction, Badge
# UserSummary - profile overview
summary: UserSummary
summary.username # str: Username
summary.stats # UserStats: Activity statistics
summary.badges # List[Badge]: Earned badges
# UserAction - activity entry
action: UserAction
action.topic_id # int: Related topic
action.excerpt # str: Content preview
```
### Search Models
```python
from uscardforum import SearchResult, SearchPost, SearchTopic
# SearchResult - search response
result: SearchResult
result.posts # List[SearchPost]: Matching posts
result.topics # List[SearchTopic]: Matching topics
result.users # List[SearchUser]: Matching users
```
### Auth Models
```python
from uscardforum import LoginResult, Session, Notification, Bookmark
# LoginResult - login response
login: LoginResult
login.success # bool: Whether succeeded
login.requires_2fa # bool: 2FA needed
# Session - current session
session: Session
session.is_authenticated # bool: Logged in
session.current_user # CurrentUser: User info
```
## API Modules
The backend is split into focused API modules:
| Module | Purpose |
|--------|---------|
| `TopicsAPI` | Topic lists, posts, pagination |
| `UsersAPI` | Profiles, activity, badges, social |
| `SearchAPI` | Full-text search |
| `CategoriesAPI` | Category mappings |
| `AuthAPI` | Login, notifications, bookmarks |
Each module inherits from `BaseAPI` which provides rate-limited HTTP methods.
## Available Tools (22 Tools)
### ๐ฐ Discovery โ Find Content to Read
| Tool | Return Type | Description |
|------|-------------|-------------|
| `get_hot_topics` | `List[TopicSummary]` | Currently trending topics by engagement |
| `get_new_topics` | `List[TopicSummary]` | Latest topics by creation time |
| `get_top_topics` | `List[TopicSummary]` | Top topics by period (daily/weekly/monthly/yearly) |
| `search_forum` | `SearchResult` | Full-text search with operators |
| `get_categories` | `CategoryMap` | Category ID to name mapping |
### ๐ Reading โ Access Topic Content
| Tool | Return Type | Description |
|------|-------------|-------------|
| `get_topic_info` | `TopicInfo` | Topic metadata (check post count first!) |
| `get_topic_posts` | `List[Post]` | Fetch ~20 posts starting at position |
| `get_all_topic_posts` | `List[Post]` | Fetch all posts with auto-pagination |
### ๐ค Users โ Profile & Activity Research
| Tool | Return Type | Description |
|------|-------------|-------------|
| `get_user_summary` | `UserSummary` | Profile overview and stats |
| `get_user_topics` | `List[Dict]` | Topics created by user |
| `get_user_replies` | `List[UserAction]` | User's reply history |
| `get_user_actions` | `List[UserAction]` | Full activity feed |
| `get_user_badges` | `UserBadges` | Badges earned by user |
| `get_user_following` | `FollowList` | Who the user follows |
| `get_user_followers` | `FollowList` | Who follows the user |
| `get_user_reactions` | `UserReactions` | Reactions given/received |
| `list_users_with_badge` | `Dict` | Find users with specific badge |
### ๐ Auth โ Authenticated Actions (requires login)
| Tool | Return Type | Description |
|------|-------------|-------------|
| `login` | `LoginResult` | Authenticate with forum credentials |
| `get_current_session` | `Session` | Check authentication status |
| `get_notifications` | `List[Notification]` | Fetch user notifications |
| `bookmark_post` | `Bookmark` | Bookmark a post for later |
| `subscribe_topic` | `SubscriptionResult` | Set topic notification level |
## Available Prompts (4 Prompts, ไธญๆ)
Guided workflows for common research tasks:
| Prompt | Args | Purpose |
|--------|------|---------|
| `research_topic` | `topic_query` | ็ ็ฉถ่ฎบๅ็นๅฎไธป้ข๏ผๆป็ป็คพๅบๅ
ฑ่ฏ |
| `analyze_user` | `username` | ๅๆ็จๆท่ตๆใ่ดก็ฎๅๅฏไฟกๅบฆ |
| `find_data_points` | `subject` | ๆฅๆพ็จๆทๆฅๅ็็ๅฎๆฐๆฎ็น |
| `compare_cards` | `card1`, `card2` | ๆฏ่พไธคๅผ ไฟก็จๅก็็คพๅบ่ฎจ่ฎบ |
## Available Resources (3 Resources)
Quick-access static data:
| URI | Description |
|-----|-------------|
| `forum://categories` | Category ID โ name mapping (JSON) |
| `forum://hot-topics` | Top 20 trending topics (JSON) |
| `forum://new-topics` | Top 20 latest topics (JSON) |
## Usage Examples
### Using the Client Directly
```python
from uscardforum import DiscourseClient
client = DiscourseClient()
# Browse hot topics
for topic in client.get_hot_topics():
print(f"{topic.title} ({topic.posts_count} posts, {topic.views} views)")
# Get topic info and posts
info = client.get_topic_info(12345)
print(f"Topic has {info.post_count} posts")
posts = client.get_topic_posts(12345)
for post in posts:
print(f"#{post.post_number} by {post.username}: {post.like_count} likes")
# Search
results = client.search("Chase Sapphire Reserve", order="latest")
for post in results.posts:
print(f"[Topic {post.topic_id}] {post.blurb}")
# User profile
summary = client.get_user_summary("creditexpert")
print(f"{summary.username}: {summary.stats.post_count} posts")
```
### Forum Authentication
```python
# Login to forum
result = client.login("username", "password")
if result.success:
print(f"Logged in as {result.username}")
elif result.requires_2fa:
result = client.login("username", "password", second_factor_token="123456")
# Get notifications
notifications = client.get_notifications(only_unread=True)
for n in notifications:
print(f"Notification {n.id}: {n.notification_type}")
# Bookmark a post
bookmark = client.bookmark_post(54321, name="Important info")
```
## Architecture
### Separation of Concerns
1. **Domain Models** (`models/`)
- Pydantic models for all return types
- Strong typing and validation
- Clear documentation
2. **API Modules** (`api/`)
- Focused functionality per domain
- Inherits from BaseAPI for HTTP
- Returns domain models
3. **Client** (`client.py`)
- Composes all API modules
- Unified interface
- Session management
4. **MCP Server** (`server.py`)
- FastMCP tool definitions
- Bearer token authentication
- Extensive docstrings (Chinese)
- Prompts and resources
### Security
- **MCP Authentication**: Bearer token via HTTP `Authorization` header (MCP transport-level)
- **Rate Limiting**: 4 requests per second with exponential backoff
- **Cloudflare Bypass**: Automatic handling via cloudscraper
## Development
```bash
# Install dev dependencies
uv sync --group dev
# Run tests
NITAN_USERNAME="user" NITAN_PASSWORD="pass" uv run pytest
# Lint
uv run ruff check src/
# Type check
uv run mypy src/
```
## License
MIT
## Contributing
Contributions welcome! Please:
1. Fork the repository
2. Create a feature branch
3. Submit a pull request
## Acknowledgments
- Built with [FastMCP](https://github.com/jlowin/fastmcp)
- Domain models with [Pydantic](https://docs.pydantic.dev/)
- Cloudflare bypass via [cloudscraper](https://github.com/VeNoMouS/cloudscraper)
- Discourse API documentation at [docs.discourse.org](https://docs.discourse.org/)