Skip to main content
Glama
gpetruzella

OpenAlex MCP Server

by gpetruzella
README.md
# OpenAlex MCP Server

A production-ready Model Context Protocol (MCP) server that provides academic research tools using the [OpenAlex API](https://docs.openalex.org/). Built with FastAPI and designed for deployment on Google Cloud Run.

## Features

### šŸ”¬ Academic Research Tools

- **search_works** - Search for papers, articles, and academic publications
- **search_authors** - Find researchers and their profiles with h-index, citations
- **get_work_details** - Get detailed metadata for specific papers
- **search_concepts** - Explore research topics and their relationships
- **search_institutions** - Find universities and research organizations
- **get_citations** - Analyze citation networks (citing/cited works)
- **advanced_filter** - Complex multi-criteria searches with OpenAlex filter syntax

### šŸš€ MCP Protocol Features

- **Streamable-HTTP transport** - Modern HTTP/SSE-based communication
- **JSON-RPC 2.0** - Standard message protocol
- **Server-Sent Events** - Real-time streaming for large responses
- **Cloud-ready** - Optimized for Google Cloud Run deployment

### šŸ“Š OpenAlex Polite Access

- Automatic rate limiting (10 requests/second)
- Mailto parameter for polite pool access
- Proper User-Agent headers
- Exponential backoff for retries

## Quick Start

### Local Development

1. **Clone and setup**:
```bash
cd openalex-mcp-server
cp .env.example .env
# Edit .env and set your MAILTO_EMAIL
```

2. **Install dependencies**:
```bash
pip install -r requirements.txt
```

3. **Run the server**:
```bash
python server.py
# Or with uvicorn:
uvicorn server:app --reload --port 8080
```

4. **Test the endpoint**:
```bash
curl http://localhost:8080/health
```

### Docker Local Testing

```bash
docker build -t openalex-mcp-server .
docker run -p 8080:8080 -e MAILTO_EMAIL=your-email@williamscollege.edu openalex-mcp-server
```

## Google Cloud Run Deployment

### Prerequisites

- [Google Cloud SDK](https://cloud.google.com/sdk/docs/install) installed
- GCP project with Cloud Run API enabled
- Docker installed locally

### Deployment Steps

1. **Authenticate with Google Cloud**:
```bash
gcloud auth login
gcloud config set project YOUR_PROJECT_ID
```

2. **Build and push container**:
```bash
# Configure Docker for Google Container Registry
gcloud auth configure-docker

# Build the image
docker build -t gcr.io/YOUR_PROJECT_ID/openalex-mcp-server .

# Push to Container Registry
docker push gcr.io/YOUR_PROJECT_ID/openalex-mcp-server
```

3. **Deploy to Cloud Run**:
```bash
gcloud run deploy openalex-mcp-server \
  --image gcr.io/YOUR_PROJECT_ID/openalex-mcp-server \
  --platform managed \
  --region us-central1 \
  --allow-unauthenticated \
  --set-env-vars MAILTO_EMAIL=your-email@williamscollege.edu \
  --memory 512Mi \
  --cpu 1 \
  --max-instances 10
```

4. **Get your service URL**:
```bash
gcloud run services describe openalex-mcp-server --platform managed --region us-central1 --format 'value(status.url)'
```

### Alternative: One-Command Deployment

```bash
gcloud run deploy openalex-mcp-server \
  --source . \
  --region us-central1 \
  --allow-unauthenticated \
  --set-env-vars MAILTO_EMAIL=your-email@williamscollege.edu
```

## MCP Client Configuration

### Claude Desktop

Add to your `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "openalex": {
      "transport": "streamable-http",
      "url": "https://YOUR-SERVICE-URL.run.app/mcp",
      "description": "OpenAlex academic research API"
    }
  }
}
```

**Config file locations**:
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
- **Linux**: `~/.config/Claude/claude_desktop_config.json`

### Other MCP Clients

The server implements standard MCP over HTTP/SSE, so it works with any compatible client:

```python
import httpx
import json

# Initialize request
init_message = {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
        "protocolVersion": "2025-03-26",
        "clientInfo": {
            "name": "my-client",
            "version": "1.0.0"
        }
    }
}

response = httpx.post(
    "https://YOUR-SERVICE-URL.run.app/mcp",
    json=init_message,
    headers={"Content-Type": "application/json"}
)

print(response.json())
```

## Usage Examples

### Search for Papers

```json
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "search_works",
    "arguments": {
      "query": "machine learning climate change",
      "publication_year": "2020-2024",
      "open_access": true,
      "limit": 10
    }
  }
}
```

### Find Researchers

```json
{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "search_authors",
    "arguments": {
      "name": "Andrew Ng",
      "institution": "Stanford",
      "limit": 5
    }
  }
}
```

### Get Citation Network

```json
{
  "jsonrpc": "2.0",
  "id": 4,
  "method": "tools/call",
  "params": {
    "name": "get_citations",
    "arguments": {
      "work_id": "W2741809807",
      "direction": "citing",
      "limit": 50
    }
  }
}
```

### Advanced Filtering

```json
{
  "jsonrpc": "2.0",
  "id": 5,
  "method": "tools/call",
  "params": {
    "name": "advanced_filter",
    "arguments": {
      "entity_type": "works",
      "filters": {
        "publication_year": ">2020",
        "cited_by_count": ">100",
        "is_oa": true
      },
      "search": "artificial intelligence",
      "sort": "cited_by_count:desc",
      "limit": 25
    }
  }
}
```

## API Endpoints

### POST /mcp
Main MCP endpoint for JSON-RPC messages. Supports both JSON and SSE responses.

**Headers**:
- `Content-Type: application/json` (required)
- `Accept: application/json` (JSON response) or `Accept: text/event-stream` (SSE)

### GET /mcp
Optional SSE endpoint for server-initiated messages (keepalive, notifications).

**Headers**:
- `Accept: text/event-stream` (required)

### GET /health
Health check endpoint for monitoring.

**Response**: `{"status": "healthy", "service": "openalex-mcp-server", "version": "1.0.0"}`

### GET /
Service information and endpoint documentation.

## Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `PORT` | `8080` | Server port (Cloud Run sets automatically) |
| `MAILTO_EMAIL` | `researcher@williamscollege.edu` | **Required** for polite pool access |
| `OPENALEX_BASE_URL` | `https://api.openalex.org` | OpenAlex API endpoint |
| `MAX_REQUESTS_PER_SECOND` | `10` | Rate limit for API calls |
| `LOG_LEVEL` | `INFO` | Logging verbosity |
| `ALLOWED_ORIGINS` | `None` | CORS allowed origins (comma-separated) |

## Architecture

```
openalex-mcp-server/
ā”œā”€ā”€ server.py              # FastAPI app with HTTP/SSE transport
ā”œā”€ā”€ mcp_handler.py         # MCP protocol & JSON-RPC handling
ā”œā”€ā”€ config.py              # Environment configuration
ā”œā”€ā”€ tools/
│   ā”œā”€ā”€ __init__.py
│   ā”œā”€ā”€ search.py          # OpenAlex API tool implementations
│   ā”œā”€ā”€ filters.py         # Filter utilities
│   └── utils.py           # Formatting helpers
ā”œā”€ā”€ Dockerfile             # Cloud Run deployment
ā”œā”€ā”€ requirements.txt       # Python dependencies
ā”œā”€ā”€ .env.example           # Environment template
ā”œā”€ā”€ claude_desktop_config.json  # Client config example
└── README.md
```

## Development

### Adding New Tools

1. Implement the tool function in `tools/search.py`:
```python
async def my_new_tool(param1: str, param2: int = 10) -> str:
    """Tool description."""
    # Implementation
    return json.dumps(result)
```

2. Add to `MCPHandler` in `mcp_handler.py`:
```python
self.tools = {
    # ... existing tools
    "my_new_tool": my_new_tool,
}
```

3. Add schema in `_get_tool_schema()`:
```python
schemas = {
    # ... existing schemas
    "my_new_tool": {
        "description": "Tool description",
        "inputSchema": {
            "type": "object",
            "properties": {
                "param1": {"type": "string", "description": "..."},
                "param2": {"type": "integer", "default": 10}
            },
            "required": ["param1"]
        }
    }
}
```

### Running Tests

```bash
# Install test dependencies
pip install pytest pytest-asyncio httpx

# Run tests
pytest
```

### Monitoring Cloud Run

```bash
# View logs
gcloud run services logs read openalex-mcp-server --region us-central1

# Check service status
gcloud run services describe openalex-mcp-server --region us-central1
```

## Security Considerations

### Current Configuration (Development)
- No authentication required
- CORS allows all origins
- Suitable for testing and internal use

### Production Hardening
1. **Enable authentication**:
```bash
gcloud run deploy openalex-mcp-server \
  --no-allow-unauthenticated
```

2. **Set allowed origins** in `.env`:
```bash
ALLOWED_ORIGINS=https://yourdomain.com,https://anotherdomain.com
```

3. **Use Cloud Run IAM** for access control
4. **Enable Cloud Armor** for DDoS protection
5. **Set up VPC** for private networking

## Rate Limiting & Polite Access

The server implements OpenAlex polite pool best practices:

- āœ… **10 requests/second** rate limit (vs 6 req/s for non-polite)
- āœ… **Mailto parameter** in all requests
- āœ… **User-Agent header** with contact info
- āœ… **Exponential backoff** for errors
- āœ… **Response caching** (where appropriate)

**Always set `MAILTO_EMAIL`** to get better rate limits!

## Troubleshooting

### Issue: "Origin not allowed"
**Solution**: Set `ALLOWED_ORIGINS` environment variable or update CORS middleware in `server.py`

### Issue: Rate limiting errors
**Solution**: Verify `MAILTO_EMAIL` is set correctly for polite pool access

### Issue: Cloud Run timeout
**Solution**: Increase timeout in deployment:
```bash
gcloud run deploy openalex-mcp-server --timeout 300
```

### Issue: Memory errors
**Solution**: Increase memory allocation:
```bash
gcloud run deploy openalex-mcp-server --memory 1Gi
```

## Resources

- [OpenAlex API Documentation](https://docs.openalex.org/)
- [Model Context Protocol Specification](https://modelcontextprotocol.io/)
- [Google Cloud Run Documentation](https://cloud.google.com/run/docs)
- [FastAPI Documentation](https://fastapi.tiangolo.com/)

## Contributing

Contributions welcome! Please:

1. Fork the repository
2. Create a feature branch
3. Add tests for new functionality
4. Submit a pull request

## License

MIT License - See LICENSE file for details

## Support

For issues and questions:
- OpenAlex API: support@openalex.org
- Williams College: Contact your research computing support

---

**Built for Williams College undergraduate researchers** šŸŽ“

Happy researching! šŸ”¬