An async Python MCP (Model Context Protocol) server that provides access to Google Custom Search API using service account authentication.
- google_search: Perform Google Custom Search queries with configurable result count
- Service Account Auth: Uses Google Service Account authentication (required since API keys are deprecated)
- Dual Authentication Support: File path for local dev, base64 for CI/CD
- Live Integration Tests: Real tests against Google Custom Search API
- FastMCP Integration: Uses the latest FastMCP Python SDK
- Docker Support: Alpine Linux-based Docker image for minimal footprint
- UV Compatibility: Modern Python packaging with UV
- Google Service Account: Create a service account in Google Cloud Console
- Custom Search Engine ID: Create a Custom Search Engine at Programmable Search Engine
- Enable Custom Search API: Enable the API for your Google Cloud project
# Clone the repository
git clone <your-repo-url>
cd google-custom-search-mcp
# Install with UV
uv sync
# Set environment variables
export GOOGLE_SERVICE_ACCOUNT_FILE=/path/to/your/service-account-key.json
export GOOGLE_SEARCH_ENGINE_ID=your-search-engine-id
# Run the server
uv run google-custom-search-mcp# Encode your service account JSON file to base64
base64 -i your-service-account-key.json -w 0 > service-account-base64.txt
# Use base64 encoded credential
export GOOGLE_SERVICE_ACCOUNT_BASE64=$(cat service-account-base64.txt)
export GOOGLE_SEARCH_ENGINE_ID=your-search-engine-id
# Run the server
uv run google-custom-search-mcp# Using file path (local development)
claude mcp add google-custom-search \
-s user \
uv run google-custom-search-mcp \
-e GOOGLE_SERVICE_ACCOUNT_FILE=/path/to/service-account-key.json \
-e GOOGLE_SEARCH_ENGINE_ID=your-search-engine-id
# Using base64 (CI/CD)
claude mcp add google-custom-search \
-s user \
uv run google-custom-search-mcp \
-e GOOGLE_SERVICE_ACCOUNT_BASE64=your-base64-encoded-json \
-e GOOGLE_SEARCH_ENGINE_ID=your-search-engine-idAdd to your Claude Desktop configuration:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"google-custom-search": {
"command": "uv",
"args": ["run", "google-custom-search-mcp"],
"env": {
"GOOGLE_SERVICE_ACCOUNT_FILE": "/path/to/your/service-account-key.json",
"GOOGLE_SEARCH_ENGINE_ID": "your-search-engine-id"
}
}
}
}The server supports different transport modes:
For use with Claude Desktop or command-line interaction:
python server.py # Uses STDIO transport by defaultFor deployment in containers or as a web service:
python server.py --transport http --host 0.0.0.0 --port 3000--transport: Transport mode (stdioorhttp, default:stdio)--host: HTTP host to bind to (default:0.0.0.0)--port: HTTP port to listen on (default:3000)
# Run in STDIO mode (for Claude Desktop)
python server.py
# Run as HTTP server on default port
python server.py --transport http
# Run on custom port
python server.py --transport http --port 8080
# Run on specific interface
python server.py --transport http --host 127.0.0.1 --port 5000- Go to Google Cloud Console
- Create a new project or select an existing one
- Enable the Custom Search API
- Navigate to "IAM & Admin" > "Service Accounts"
- Click "Create Service Account"
- Fill in name and description
- Grant appropriate roles (Custom Search API needs CSE role)
- Click "Create Key" and download JSON key file
- Go to Programmable Search Engine
- Click "Add" to create a new search engine
- Configure your search preferences
- Get the Search Engine ID from the control panel
Local Development (File Path):
export GOOGLE_SERVICE_ACCOUNT_FILE=/path/to/your/service-account-key.json
export GOOGLE_SEARCH_ENGINE_ID=your_actual_search_engine_id_hereCI/CD (Base64 Encoded):
# Encode service account JSON to base64
base64 -i your-service-account-key.json -w 0 > service-account-base64.txt
export GOOGLE_SERVICE_ACCOUNT_BASE64=$(cat service-account-base64.txt)
export GOOGLE_SEARCH_ENGINE_ID=your_actual_search_engine_id_heregoogle_search({
query: "Your search query",
num_results?: 10 # Optional, 1-100 (default: 10)
})Response Format:
{
"results": [
{
"title": "Result Title",
"link": "https://example.com",
"snippet": "Result description...",
"display_link": "example.com"
}
],
"total_results": 100,
"search_time": 0.5
}The Docker image automatically runs in HTTP mode on port 3000.
docker build -t google-custom-search-mcp .The container runs in HTTP mode by default on port 3000:
# Using file mount
docker run -d \
-p 3000:3000 \
-v /path/to/service-account-key.json:/app/service-account-key.json \
-e GOOGLE_SERVICE_ACCOUNT_FILE=/app/service-account-key.json \
-e GOOGLE_SEARCH_ENGINE_ID=your-engine-id \
google-custom-search-mcp
# Using base64 environment variable (recommended for production)
docker run -d \
-p 3000:3000 \
-e GOOGLE_SERVICE_ACCOUNT_BASE64=your-base64-encoded-json \
-e GOOGLE_SEARCH_ENGINE_ID=your-engine-id \
google-custom-search-mcpThe server will be available at http://localhost:3000 for MCP clients.
The Helm chart allows you to deploy the MCP server to Kubernetes easily.
# Clone the repository
git clone https://github.com/fbettag/google-custom-search-mcp.git
cd google-custom-search-mcp
# Install using Helm
helm install google-custom-search-mcp ./charts/google-custom-search-mcp \
--set google.searchEngineId="your-search-engine-id" \
--set serviceAccount.base64.value="$(base64 -w 0 < your-service-account.json)" \
--set serviceAccount.base64.enabled=truehelm upgrade google-custom-search-mcp ./charts/google-custom-search-mcp \
--set google.searchEngineId="your-search-engine-id" \
--set serviceAccount.base64.value="$(base64 -w 0 < your-service-account.json)" \
--set serviceAccount.base64.enabled=truehelm uninstall google-custom-search-mcpCreate a values-prod.yaml file:
# values-prod.yaml
replicaCount: 2
google:
searchEngineId: "your-search-engine-id"
serviceAccount:
base64:
enabled: true
value: "your-base64-encoded-service-account-json"
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"Install with values file:
helm install google-custom-search-mcp . -f values-prod.yamlhelm upgrade google-custom-search-mcp . \
--set google.searchEngineId="your-search-engine-id" \
--set serviceAccount.base64.value="$(base64 -i your-service-account.json -w 0)"helm uninstall google-custom-search-mcpThe Helm chart supports various configuration options:
| Parameter | Description | Default |
|---|---|---|
replicaCount |
Number of replicas | 1 |
image.repository |
Docker image repository | your-username/google-custom-search-mcp |
image.tag |
Docker image tag | latest |
google.searchEngineId |
Google Custom Search Engine ID | "" |
serviceAccount.base64.enabled |
Use base64 encoded service account | true |
serviceAccount.base64.value |
Base64 encoded service account JSON | "" |
serviceAccount.existingFile.enabled |
Use file mount for service account | false |
serviceAccount.existingFile.path |
Path to service account file | "" |
service.type |
Kubernetes service type | ClusterIP |
service.port |
Service port | 3000 |
resources.requests |
Resource requests | memory: 64Mi, cpu: 50m |
resources.limits |
Resource limits | memory: 128Mi, cpu: 100m |
Add these secrets to your GitHub repository:
GOOGLE_SERVICE_ACCOUNT_BASE64: Base64 encoded service account JSON fileGOOGLE_SEARCH_ENGINE_ID: Your Custom Search Engine ID
# Encode service account JSON to base64 (no line wraps)
base64 -i your-service-account-key.json -w 0
# Copy the output and add as GitHub secret GOOGLE_SERVICE_ACCOUNT_BASE64# Clone and setup
uv sync --dev
# Run live integration tests (requires valid credentials)
uv run pytest tests/ -v
# Run with coverage
uv run pytest tests/ --cov=server --cov-report=html
# Linting
uv run ruff check .
uv run ruff format .
# Type checking
uv run mypy server.pyTests perform live integration testing with the actual Google Custom Search API. You must set:
export GOOGLE_SERVICE_ACCOUNT_BASE64=your-base64-encoded-json
export GOOGLE_SEARCH_ENGINE_ID=your-search-engine-id| Variable | Description | Required |
|---|---|---|
GOOGLE_SERVICE_ACCOUNT_FILE |
Path to service account JSON file | Either this or base64 |
GOOGLE_SERVICE_ACCOUNT_BASE64 |
Base64 encoded service account JSON | Either this or file path |
GOOGLE_SEARCH_ENGINE_ID |
Custom Search Engine ID | Yes |
- Google Custom Search API provides 100 search queries per day for free
- Additional queries require billing setup
- Service accounts have the same quota limits as user accounts
- Refer to Google Custom Search Pricing
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Commit changes:
git commit -m 'Add amazing feature' - Push to branch:
git push origin feature/amazing-feature - Open a Pull Request
MIT License - see LICENSE file for details.
For issues and questions:
- Check the Google Custom Search documentation
- Review existing GitHub issues
- Create a new issue with detailed information