Skip to main content
Glama
hmumixaM

USCardForum MCP Server

by hmumixaM
README.md
# 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

[![Deploy](https://www.herokucdn.com/deploy/button.svg)](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

[![Deploy on Railway](https://railway.app/button.svg)](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

[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](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

[![Open in Cloud Shell](https://gstatic.com/cloudssh/images/open-btn.svg)](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

[![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](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/)

TDQS

A4/5.0

Scored across 22 tools

Disambiguation4/5

Most tools have distinct purposes, but some overlap exists, such as get_user_replies and get_user_actions, which could cause confusion. However, descriptions clarify their differences, and tools like get_topic_posts and get_all_topic_posts are well-differentiated for pagination vs. bulk fetching.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern, such as get_topic_posts, bookmark_post, and search_forum. The naming is uniform and predictable, making it easy for agents to understand the action and target resource.

Tool Count4/5

With 22 tools, the count is slightly high but reasonable for a forum server covering categories, topics, posts, users, search, and authentication. It provides comprehensive functionality without being overwhelming, though some tools like get_user_actions and get_user_replies could potentially be consolidated.

Completeness5/5

The tool set offers complete coverage for a forum domain, including CRUD-like operations (e.g., login, bookmark, subscribe), extensive read capabilities (e.g., get topics, posts, users, categories), and search functionality. There are no obvious gaps, and agents can perform typical forum interactions without dead ends.