Skip to main content
Glama
nitinthewiz
by nitinthewiz
README.md
# freshrss-mcp-server

An MCP (Model Context Protocol) server implementation to interact with your FreshRSS server, using the Google Reader API.

## Features

- **Full FreshRSS Integration**: Access all major Google Reader API features
- **Simple Authentication**: Direct FreshRSS API authentication
- **Subscription Management**: Subscribe, unsubscribe, import/export OPML
- **Content Reading**: Fetch reading lists, starred items, specific feeds, and categories
- **Item Operations**: Mark as read/unread, star/unstar, add/remove labels
- **Category Management**: List, rename, and delete categories
- **Persistent Storage**: SQLite database for auth tokens and sync state at `~/.freshrss/`
- **Session Management**: Login/logout with complete data clearing
- **Claude Desktop Ready**: Works seamlessly with Claude Desktop as a local MCP server

## Installation

```bash
# Clone the repository
git clone https://github.com/nitinthewiz/freshrss-mcp-server.git
cd freshrss-mcp-server

# Install in editable mode with all dependencies
pip install -e .

# For development (includes testing and linting tools)
pip install -e ".[dev]"
```

## Configuration

### Claude Desktop

Add to your Claude Desktop config at `~/Library/Application Support/Claude/claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "freshrss": {
      "command": "python3",
      "args": ["-m", "src.main"],
      "cwd": "/path/to/freshrss-mcp-server",
      "env": {
        "PYTHONPATH": "/path/to/freshrss-mcp-server"
      }
    }
  }
}
```

### Environment Variables (Optional)

All configuration is optional with sensible defaults:

```bash
# Server Transport ("stdio" or "streamable-http")
MCP_TRANSPORT=stdio
MCP_HOST=127.0.0.1    # For HTTP transport
MCP_PORT=8000         # For HTTP transport

# Database (default: ~/.freshrss/freshrss.db)
DATABASE_PATH=/path/to/custom/freshrss.db

# Logging
DEBUG=false
LOG_LEVEL=INFO

# HTTP Client Settings
HTTP_TIMEOUT=30
HTTP_MAX_RETRIES=3
HTTP_RETRY_DELAY=1.0
HTTP_BACKOFF_FACTOR=2.0

# Cache Settings
CACHE_SUBSCRIPTION_TTL=300
CACHE_UNREAD_COUNT_TTL=30
```

## Usage

### Running the Server

**stdio Mode (Default - for Claude Desktop)**

```bash
# Using Python directly
python -m src.main

# Using FastMCP CLI
fastmcp run src/main.py:mcp
```

**Streamable HTTP Mode (for HTTP-based MCP clients)**

This mode supports the MCP specification 2025-03-26 Streamable HTTP transport, enabling HTTP-based MCP clients to connect.

```bash
# Using environment variables
MCP_TRANSPORT=streamable-http python -m src.main

# With custom host/port
MCP_TRANSPORT=streamable-http MCP_HOST=0.0.0.0 MCP_PORT=8080 python -m src.main

# Using FastMCP CLI
fastmcp run src/main.py:mcp --transport streamable-http --port 8000
```

The server will be available at `http://127.0.0.1:8000/mcp` (or your configured host:port).

**Development Mode (with MCP Inspector)**

```bash
# Opens web UI for testing tools interactively
fastmcp dev src/main.py:mcp
```

### Available MCP Tools

#### Authentication

- `login_to_freshrss(server_url, username, password)` - Login and store credentials
  - Supports flexible URL formats (base URL, with /i/, or /api/greader.php)
- `logout_from_freshrss()` - Logout and clear all data
- `get_freshrss_user_info()` - Get current user info
- `check_freshrss_session()` - Validate session

#### Subscriptions

- `list_freshrss_subscriptions()` - List all feeds
- `subscribe_to_feed(feed_url, title?, category?)` - Subscribe to a feed
- `unsubscribe_from_feed(feed_id)` - Unsubscribe from a feed
- `quick_subscribe_to_feed(feed_url)` - Quick subscribe with auto-discovery
- `export_freshrss_subscriptions()` - Export OPML
- `import_freshrss_subscriptions(opml_content)` - Import OPML
- `edit_feed_subscription(feed_id, ...)` - Edit feed properties

#### Content

- `get_reading_list(count?, since_timestamp?, continuation?)` - Get unread articles
- `get_starred_articles(count?, ...)` - Get starred items
- `get_feed_articles(feed_id, count?, ...)` - Get specific feed articles
- `get_category_articles(category_name, count?, ...)` - Get category articles

#### Item Operations

- `mark_articles_as_read(item_ids)` - Mark as read
- `mark_articles_as_unread(item_ids)` - Mark as unread
- `star_articles(item_ids)` - Star articles
- `unstar_articles(item_ids)` - Unstar articles
- `add_label_to_articles(item_ids, label)` - Add label
- `remove_label_from_articles(item_ids, label)` - Remove label
- `mark_all_as_read_in_stream(stream_id, timestamp?)` - Mark all as read

#### Categories

- `list_categories()` - List all categories
- `get_unread_counts()` - Get unread counts for all feeds
- `rename_category(old_name, new_name)` - Rename category
- `delete_category(category_name)` - Delete category

## Development

### Running Tests

```bash
# Run all tests
pytest

# Run with coverage
pytest --cov=src --cov-report=html

# Run specific test suite
pytest tests/unit/
pytest tests/integration/
```

### Code Quality

```bash
# Format code
black src/

# Lint
ruff check src/

# Type checking
mypy src/
```

## Architecture

The server uses a layered architecture:

- **MCP Tools Layer**: FastMCP tool definitions (26 tools)
- **Service Layer**: Manager classes for each API domain (Auth, Subscription, Stream, Item, Tag)
- **HTTP Client Layer**: GReader API client with retry logic and exponential backoff
- **Storage Layer**: SQLite for auth tokens and sync state

## Security

- HTTPS-only connections to FreshRSS servers
- Auth tokens persisted in SQLite (consider SQLCipher for encryption)
- No plaintext password storage
- Input validation on all user inputs
- Parameterized SQL queries

## License

MIT License - see [LICENSE](LICENSE) file for details.

## Contributing

Contributions welcome! Please ensure:

- Tests pass (`pytest`)
- Code is formatted (`black src/`)
- No linting errors (`ruff check src/`)
- Type checking passes (`mypy src/`)

## Support

For issues, questions, or feature requests, please open an issue on GitHub.