Update repository URLs and add production installation instructions
- Update all GitHub URLs to git.supported.systems/MCP/mcrentcast - Add production installation using uvx --from git+https://... - Distinguish between development and production installation methods - Fix pyproject.toml project URLs
This commit is contained in:
parent
c91588ee6e
commit
fedfc7a6cf
501
README.md
501
README.md
@ -2,35 +2,504 @@
|
|||||||
|
|
||||||
A Model Context Protocol (MCP) server that provides intelligent access to the Rentcast API with advanced caching, rate limiting, and cost management features.
|
A Model Context Protocol (MCP) server that provides intelligent access to the Rentcast API with advanced caching, rate limiting, and cost management features.
|
||||||
|
|
||||||
## Features
|
## 🌟 Features
|
||||||
|
|
||||||
- 🏠 Complete Rentcast API integration
|
- **🏠 Complete Rentcast API Integration**: Access all Rentcast endpoints for property data, valuations, listings, and market statistics
|
||||||
- 💾 Intelligent caching with hit/miss tracking
|
- **💾 Intelligent Caching**: Automatic response caching with hit/miss tracking and configurable TTL
|
||||||
- 🛡️ Rate limiting with exponential backoff
|
- **🛡️ Rate Limiting**: Configurable daily/monthly/per-minute limits with exponential backoff
|
||||||
- 💰 Cost management and API usage tracking
|
- **💰 Cost Management**: Track API usage, estimate costs, and get confirmations before expensive operations
|
||||||
- ✨ MCP elicitation for user confirmations
|
- **🧪 Mock API for Testing**: Full mock implementation for development without consuming credits
|
||||||
- 🐳 Docker Compose development environment
|
- **✨ MCP Integration**: Seamless integration with Claude and other MCP-compatible clients
|
||||||
- 🧪 Comprehensive test suite with beautiful reports
|
- **🐳 Docker Ready**: Complete Docker Compose setup for easy deployment
|
||||||
|
- **📊 Usage Analytics**: Track API usage patterns, cache performance, and costs
|
||||||
|
|
||||||
## Quick Start
|
## 📋 Table of Contents
|
||||||
|
|
||||||
|
- [Quick Start](#quick-start)
|
||||||
|
- [Installation](#installation)
|
||||||
|
- [Configuration](#configuration)
|
||||||
|
- [Usage](#usage)
|
||||||
|
- [MCP Tools](#mcp-tools)
|
||||||
|
- [Mock API](#mock-api)
|
||||||
|
- [Development](#development)
|
||||||
|
- [Testing](#testing)
|
||||||
|
- [API Documentation](#api-documentation)
|
||||||
|
- [Troubleshooting](#troubleshooting)
|
||||||
|
|
||||||
|
## 🚀 Quick Start
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- Python 3.13+
|
||||||
|
- [uv](https://github.com/astral-sh/uv) package manager
|
||||||
|
- Rentcast API key (or use mock mode for testing)
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
1. Copy environment configuration:
|
|
||||||
```bash
|
```bash
|
||||||
|
# Clone the repository
|
||||||
|
git clone https://git.supported.systems/MCP/mcrentcast.git
|
||||||
|
cd mcrentcast
|
||||||
|
|
||||||
|
# Run the installation script
|
||||||
|
./install.sh
|
||||||
|
|
||||||
|
# IMPORTANT: Set your API key in the .env file
|
||||||
|
nano .env # Set RENTCAST_API_KEY=your_actual_api_key
|
||||||
|
|
||||||
|
# For development (from cloned repo)
|
||||||
|
claude mcp add mcrentcast -- uvx --from . mcrentcast
|
||||||
|
|
||||||
|
# For production (install from git)
|
||||||
|
claude mcp add mcrentcast -- uvx --from git+https://git.supported.systems/MCP/mcrentcast.git mcrentcast
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📦 Installation
|
||||||
|
|
||||||
|
### Method 1: Automated Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This script will:
|
||||||
|
- Install Python dependencies with uv
|
||||||
|
- Create necessary directories
|
||||||
|
- Initialize the database
|
||||||
|
- Set up configuration files
|
||||||
|
|
||||||
|
### Method 2: Manual Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install dependencies
|
||||||
|
uv sync
|
||||||
|
|
||||||
|
# Create data directory
|
||||||
|
mkdir -p data
|
||||||
|
|
||||||
|
# Initialize database
|
||||||
|
uv run python -c "from mcrentcast.database import db_manager; db_manager.create_tables()"
|
||||||
|
|
||||||
|
# Copy environment configuration
|
||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
|
|
||||||
|
# Edit .env with your API key
|
||||||
|
nano .env
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Set your Rentcast API key in `.env`:
|
### Using the Installed Scripts
|
||||||
|
|
||||||
|
After installation, you can use the provided command-line scripts:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# Run the MCP server (for Claude integration)
|
||||||
|
uv run mcrentcast
|
||||||
|
|
||||||
|
# Run the mock API server (for testing)
|
||||||
|
uv run mcrentcast-mock-api
|
||||||
|
```
|
||||||
|
|
||||||
|
### Method 3: Docker
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start with Docker Compose
|
||||||
|
make dev # Development mode
|
||||||
|
make prod # Production mode
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⚙️ Configuration
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
Create a `.env` file with your configuration:
|
||||||
|
|
||||||
|
```env
|
||||||
|
# Rentcast API Configuration
|
||||||
RENTCAST_API_KEY=your_api_key_here
|
RENTCAST_API_KEY=your_api_key_here
|
||||||
|
RENTCAST_BASE_URL=https://api.rentcast.io/v1
|
||||||
|
|
||||||
|
# Mock API Settings (for testing)
|
||||||
|
USE_MOCK_API=false # Set to true for testing without credits
|
||||||
|
MOCK_API_URL=http://localhost:8001/v1
|
||||||
|
|
||||||
|
# Rate Limiting
|
||||||
|
DAILY_API_LIMIT=100
|
||||||
|
MONTHLY_API_LIMIT=1000
|
||||||
|
REQUESTS_PER_MINUTE=3
|
||||||
|
|
||||||
|
# Cache Settings
|
||||||
|
CACHE_TTL_HOURS=24
|
||||||
|
MAX_CACHE_SIZE_MB=100
|
||||||
|
|
||||||
|
# Database
|
||||||
|
DATABASE_URL=sqlite:///./data/mcrentcast.db
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Start the development environment:
|
### Claude Desktop Configuration
|
||||||
|
|
||||||
|
#### Option 1: Using `claude mcp add` with uvx (Recommended)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make dev
|
# Navigate to the project directory
|
||||||
|
cd /path/to/mcrentcast
|
||||||
|
|
||||||
|
# Make sure your API key is set in .env file
|
||||||
|
# Edit .env and set RENTCAST_API_KEY=your_actual_key
|
||||||
|
|
||||||
|
# For production (install directly from git)
|
||||||
|
claude mcp add mcrentcast -- uvx --from git+https://git.supported.systems/MCP/mcrentcast.git mcrentcast
|
||||||
|
|
||||||
|
# For development (from cloned repository)
|
||||||
|
claude mcp add mcrentcast -- uvx --from . mcrentcast
|
||||||
|
|
||||||
|
# Alternative: use the mcp.json configuration (development only)
|
||||||
|
claude mcp add mcrentcast .
|
||||||
|
|
||||||
|
# For testing with mock API (no real API key needed)
|
||||||
|
claude mcp add mcrentcast-test -- uvx --from git+https://git.supported.systems/MCP/mcrentcast.git mcrentcast \
|
||||||
|
-e USE_MOCK_API=true \
|
||||||
|
-e RENTCAST_API_KEY=test_key_basic
|
||||||
|
|
||||||
|
# To use different scopes
|
||||||
|
claude mcp add --scope user mcrentcast -- uvx --from . mcrentcast # Available to all projects
|
||||||
|
claude mcp add --scope local mcrentcast . # Only for current project (default)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Development
|
#### Option 2: Manual Configuration
|
||||||
|
|
||||||
This project uses uv for Python dependency management and Docker Compose for the development environment.
|
Add to your Claude MCP configuration file (usually `~/.config/claude/mcp.json` or similar):
|
||||||
|
|
||||||
See `docs/` directory for detailed documentation.
|
```json
|
||||||
|
{
|
||||||
|
"servers": {
|
||||||
|
"mcrentcast": {
|
||||||
|
"command": "uv",
|
||||||
|
"args": ["run", "python", "-m", "mcrentcast.server"],
|
||||||
|
"cwd": "/path/to/mcrentcast",
|
||||||
|
"env": {
|
||||||
|
"PYTHONPATH": "/path/to/mcrentcast/src:${PYTHONPATH}",
|
||||||
|
"RENTCAST_API_KEY": "your_api_key_here",
|
||||||
|
"USE_MOCK_API": "false"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: The server uses the `mcp.json` file in the project root for configuration when using `claude mcp add`.
|
||||||
|
|
||||||
|
## 🔧 Usage
|
||||||
|
|
||||||
|
### With Claude
|
||||||
|
|
||||||
|
Once added to Claude, you can use natural language:
|
||||||
|
|
||||||
|
```
|
||||||
|
User: Search for properties in Austin, Texas
|
||||||
|
|
||||||
|
Claude: I'll search for properties in Austin, Texas.
|
||||||
|
[Uses search_properties tool]
|
||||||
|
Found 10 properties in Austin, TX...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Direct Python Usage
|
||||||
|
|
||||||
|
```python
|
||||||
|
from mcrentcast.rentcast_client import RentcastClient
|
||||||
|
|
||||||
|
async def example():
|
||||||
|
client = RentcastClient(api_key="your_key")
|
||||||
|
|
||||||
|
# Search properties
|
||||||
|
properties, cached, age = await client.get_property_records(
|
||||||
|
city="Austin",
|
||||||
|
state="TX",
|
||||||
|
limit=10
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get value estimate
|
||||||
|
estimate, cached, age = await client.get_value_estimate(
|
||||||
|
address="123 Main St, Austin, TX"
|
||||||
|
)
|
||||||
|
|
||||||
|
await client.close()
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🛠️ MCP Tools
|
||||||
|
|
||||||
|
The server provides 13 MCP tools:
|
||||||
|
|
||||||
|
### Property Data Tools
|
||||||
|
|
||||||
|
| Tool | Description | Parameters |
|
||||||
|
|------|-------------|------------|
|
||||||
|
| `search_properties` | Search for property records | `city`, `state`, `zipCode`, `limit`, `offset` |
|
||||||
|
| `get_property` | Get specific property details | `property_id` |
|
||||||
|
| `get_value_estimate` | Get property value estimate | `address` |
|
||||||
|
| `get_rent_estimate` | Get rental price estimate | `address`, `bedrooms`, `bathrooms`, `squareFootage` |
|
||||||
|
|
||||||
|
### Listing Tools
|
||||||
|
|
||||||
|
| Tool | Description | Parameters |
|
||||||
|
|------|-------------|------------|
|
||||||
|
| `search_sale_listings` | Find properties for sale | `city`, `state`, `zipCode`, `limit` |
|
||||||
|
| `search_rental_listings` | Find rental properties | `city`, `state`, `zipCode`, `limit` |
|
||||||
|
| `get_market_statistics` | Get market analysis data | `city`, `state`, `zipCode` |
|
||||||
|
|
||||||
|
### Management Tools
|
||||||
|
|
||||||
|
| Tool | Description | Parameters |
|
||||||
|
|------|-------------|------------|
|
||||||
|
| `set_api_key` | Configure Rentcast API key | `api_key` |
|
||||||
|
| `expire_cache` | Expire cache entries | `cache_key`, `all` |
|
||||||
|
| `get_cache_stats` | View cache performance | - |
|
||||||
|
| `get_usage_stats` | Track API usage and costs | `days` |
|
||||||
|
| `set_api_limits` | Configure rate limits | `daily_limit`, `monthly_limit`, `requests_per_minute` |
|
||||||
|
| `get_api_limits` | View current limits | - |
|
||||||
|
|
||||||
|
## 🧪 Mock API
|
||||||
|
|
||||||
|
The project includes a complete mock of the Rentcast API for testing without consuming API credits:
|
||||||
|
|
||||||
|
### Starting Mock API
|
||||||
|
|
||||||
|
#### Using the Script Command
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start mock API server (runs on port 8001)
|
||||||
|
uv run mcrentcast-mock-api
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Using Make Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start mock API only
|
||||||
|
make mock-api
|
||||||
|
|
||||||
|
# Start full stack with mock API
|
||||||
|
make test-mock
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test API Keys
|
||||||
|
|
||||||
|
The mock API includes predefined test keys with different rate limits:
|
||||||
|
|
||||||
|
| Key | Daily Limit | Use Case |
|
||||||
|
|-----|-------------|----------|
|
||||||
|
| `test_key_free_tier` | 50 | Testing free tier |
|
||||||
|
| `test_key_basic` | 100 | Standard testing |
|
||||||
|
| `test_key_pro` | 1,000 | High-volume testing |
|
||||||
|
| `test_key_enterprise` | 10,000 | Unlimited testing |
|
||||||
|
| `test_key_rate_limited` | 1 | Rate limit testing |
|
||||||
|
|
||||||
|
### Using Mock Mode
|
||||||
|
|
||||||
|
To use the mock API instead of the real Rentcast API, set in `.env`:
|
||||||
|
|
||||||
|
```env
|
||||||
|
USE_MOCK_API=true
|
||||||
|
RENTCAST_API_KEY=test_key_basic
|
||||||
|
MOCK_API_URL=http://localhost:8001/v1
|
||||||
|
```
|
||||||
|
|
||||||
|
Then run the MCP server normally:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv run mcrentcast
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔬 Development
|
||||||
|
|
||||||
|
### Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
mcrentcast/
|
||||||
|
├── src/mcrentcast/ # Core MCP server code
|
||||||
|
│ ├── server.py # FastMCP server implementation
|
||||||
|
│ ├── rentcast_client.py # Rentcast API client
|
||||||
|
│ ├── database.py # Database management
|
||||||
|
│ ├── models.py # Data models
|
||||||
|
│ ├── config.py # Configuration
|
||||||
|
│ └── mock_api.py # Mock API server
|
||||||
|
├── tests/ # Test suite
|
||||||
|
├── docs/ # Documentation
|
||||||
|
├── scripts/ # Utility scripts
|
||||||
|
├── docker-compose.yml # Docker configuration
|
||||||
|
├── Makefile # Build automation
|
||||||
|
└── pyproject.toml # Python dependencies
|
||||||
|
```
|
||||||
|
|
||||||
|
### Running Locally
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install development dependencies
|
||||||
|
uv sync --dev
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
uv run pytest
|
||||||
|
|
||||||
|
# Run with mock API
|
||||||
|
USE_MOCK_API=true uv run python -m mcrentcast.server
|
||||||
|
|
||||||
|
# Run linting
|
||||||
|
uv run ruff check src/
|
||||||
|
|
||||||
|
# Format code
|
||||||
|
uv run black src/ tests/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker Development
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build images
|
||||||
|
make build
|
||||||
|
|
||||||
|
# Start development environment
|
||||||
|
make dev
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
make logs
|
||||||
|
|
||||||
|
# Run tests in container
|
||||||
|
make test
|
||||||
|
|
||||||
|
# Access shell
|
||||||
|
make shell
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🧪 Testing
|
||||||
|
|
||||||
|
### Unit Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv run pytest tests/ -v
|
||||||
|
```
|
||||||
|
|
||||||
|
### Integration Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start mock API
|
||||||
|
make mock-api
|
||||||
|
|
||||||
|
# Run integration tests
|
||||||
|
uv run pytest tests/test_integration.py -v
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Coverage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv run pytest --cov=src --cov-report=html
|
||||||
|
# View report at htmlcov/index.html
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📚 API Documentation
|
||||||
|
|
||||||
|
### Rentcast API Endpoints
|
||||||
|
|
||||||
|
The server integrates with all major Rentcast endpoints:
|
||||||
|
|
||||||
|
- **Property Records**: Search and retrieve property information
|
||||||
|
- **Value Estimates**: Get property valuations
|
||||||
|
- **Rent Estimates**: Calculate rental prices
|
||||||
|
- **Sale Listings**: Find properties for sale
|
||||||
|
- **Rental Listings**: Find rental properties
|
||||||
|
- **Market Statistics**: Access market trends and analytics
|
||||||
|
|
||||||
|
### Response Caching
|
||||||
|
|
||||||
|
All API responses are automatically cached:
|
||||||
|
- Default TTL: 24 hours (configurable)
|
||||||
|
- Cache hit/miss tracking
|
||||||
|
- Manual cache expiration available
|
||||||
|
- Cache size management
|
||||||
|
|
||||||
|
### Rate Limiting
|
||||||
|
|
||||||
|
Configurable rate limits:
|
||||||
|
- Daily request limits
|
||||||
|
- Monthly request limits
|
||||||
|
- Per-minute rate limiting
|
||||||
|
- Exponential backoff on failures
|
||||||
|
|
||||||
|
## 🔍 Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
#### Server won't start
|
||||||
|
```bash
|
||||||
|
# Check Python version
|
||||||
|
python --version # Should be 3.13+
|
||||||
|
|
||||||
|
# Reinstall dependencies
|
||||||
|
uv sync --reinstall
|
||||||
|
|
||||||
|
# Check database
|
||||||
|
rm -f data/mcrentcast.db
|
||||||
|
uv run python -c "from mcrentcast.database import db_manager; db_manager.create_tables()"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### API Key Issues
|
||||||
|
```bash
|
||||||
|
# Test with mock API
|
||||||
|
USE_MOCK_API=true uv run python scripts/test_mock_api.py
|
||||||
|
|
||||||
|
# Verify API key
|
||||||
|
curl -H "X-Api-Key: your_key" https://api.rentcast.io/v1/properties
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Cache Issues
|
||||||
|
```bash
|
||||||
|
# Clear cache
|
||||||
|
rm -f data/mcrentcast.db
|
||||||
|
|
||||||
|
# Expire specific cache
|
||||||
|
uv run python -c "
|
||||||
|
from mcrentcast.database import db_manager
|
||||||
|
import asyncio
|
||||||
|
asyncio.run(db_manager.expire_cache_entry('cache_key_here'))
|
||||||
|
"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debug Mode
|
||||||
|
|
||||||
|
Enable debug logging:
|
||||||
|
```env
|
||||||
|
DEBUG=true
|
||||||
|
LOG_LEVEL=DEBUG
|
||||||
|
```
|
||||||
|
|
||||||
|
### Getting Help
|
||||||
|
|
||||||
|
- Check `docs/` directory for detailed guides
|
||||||
|
- Review `docs/mock-api.md` for testing documentation
|
||||||
|
- Check `docs/claude-setup.md` for MCP integration help
|
||||||
|
|
||||||
|
## 📄 License
|
||||||
|
|
||||||
|
MIT License - See LICENSE file for details
|
||||||
|
|
||||||
|
## 🤝 Contributing
|
||||||
|
|
||||||
|
Contributions are welcome! Please:
|
||||||
|
1. Fork the repository
|
||||||
|
2. Create a feature branch
|
||||||
|
3. Add tests for new features
|
||||||
|
4. Ensure all tests pass
|
||||||
|
5. Submit a pull request
|
||||||
|
|
||||||
|
## 🙏 Acknowledgments
|
||||||
|
|
||||||
|
- Built with [FastMCP](https://github.com/jlowin/fastmcp) for MCP support
|
||||||
|
- Uses [Rentcast API](https://developers.rentcast.io/) for property data
|
||||||
|
- Powered by [uv](https://github.com/astral-sh/uv) for Python management
|
||||||
|
|
||||||
|
## 📞 Support
|
||||||
|
|
||||||
|
- Documentation: See `/docs` directory
|
||||||
|
- Issues: Create an issue on GitHub
|
||||||
|
- Mock API Guide: See `docs/mock-api.md`
|
||||||
|
- Claude Setup: See `docs/claude-setup.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Note**: This is an unofficial integration with the Rentcast API. Please ensure you comply with Rentcast's terms of service and API usage guidelines.
|
673
docs/api-reference.md
Normal file
673
docs/api-reference.md
Normal file
@ -0,0 +1,673 @@
|
|||||||
|
# API Reference - mcrentcast MCP Server
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The mcrentcast MCP server provides 13 tools for interacting with the Rentcast API. All tools support intelligent caching, rate limiting, and cost management.
|
||||||
|
|
||||||
|
## Authentication
|
||||||
|
|
||||||
|
### set_api_key
|
||||||
|
|
||||||
|
Configure or update the Rentcast API key for the session.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `api_key` (string, required): Your Rentcast API key
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tool": "set_api_key",
|
||||||
|
"parameters": {
|
||||||
|
"api_key": "your_rentcast_api_key_here"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "API key updated successfully"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Property Data Tools
|
||||||
|
|
||||||
|
### search_properties
|
||||||
|
|
||||||
|
Search for property records by location.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `address` (string, optional): Property address
|
||||||
|
- `city` (string, optional): City name
|
||||||
|
- `state` (string, optional): State code (e.g., "TX", "CA")
|
||||||
|
- `zipCode` (string, optional): ZIP code
|
||||||
|
- `limit` (integer, optional): Maximum results (1-500, default: 10)
|
||||||
|
- `offset` (integer, optional): Results offset for pagination (default: 0)
|
||||||
|
- `force_refresh` (boolean, optional): Force cache refresh (default: false)
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tool": "search_properties",
|
||||||
|
"parameters": {
|
||||||
|
"city": "Austin",
|
||||||
|
"state": "TX",
|
||||||
|
"limit": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "prop_000001",
|
||||||
|
"address": "123 Main St",
|
||||||
|
"city": "Austin",
|
||||||
|
"state": "TX",
|
||||||
|
"zipCode": "78701",
|
||||||
|
"propertyType": "Single Family",
|
||||||
|
"bedrooms": 3,
|
||||||
|
"bathrooms": 2.0,
|
||||||
|
"squareFootage": 1800,
|
||||||
|
"yearBuilt": 2010,
|
||||||
|
"lastSalePrice": 350000
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"count": 5,
|
||||||
|
"cached": false,
|
||||||
|
"cache_age_hours": null,
|
||||||
|
"message": "Found 5 properties (fresh data)"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### get_property
|
||||||
|
|
||||||
|
Get detailed information for a specific property by ID.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `property_id` (string, required): Property ID from Rentcast
|
||||||
|
- `force_refresh` (boolean, optional): Force cache refresh (default: false)
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tool": "get_property",
|
||||||
|
"parameters": {
|
||||||
|
"property_id": "prop_000123"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"property": {
|
||||||
|
"id": "prop_000123",
|
||||||
|
"address": "456 Oak Ave",
|
||||||
|
"city": "Dallas",
|
||||||
|
"state": "TX",
|
||||||
|
"owner": {
|
||||||
|
"name": "John Doe",
|
||||||
|
"mailingAddress": "789 Business Park"
|
||||||
|
},
|
||||||
|
"taxAssessments": [
|
||||||
|
{
|
||||||
|
"year": 2023,
|
||||||
|
"total": 450000
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"features": {
|
||||||
|
"cooling": "Central Air",
|
||||||
|
"heating": "Forced Air",
|
||||||
|
"pool": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cached": true,
|
||||||
|
"cache_age_hours": 2.5,
|
||||||
|
"message": "Property found (from cache, age: 2.5 hours)"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Valuation Tools
|
||||||
|
|
||||||
|
### get_value_estimate
|
||||||
|
|
||||||
|
Get property value estimate for an address.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `address` (string, required): Full property address
|
||||||
|
- `force_refresh` (boolean, optional): Force cache refresh (default: false)
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tool": "get_value_estimate",
|
||||||
|
"parameters": {
|
||||||
|
"address": "123 Main St, Austin, TX 78701"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"estimate": {
|
||||||
|
"address": "123 Main St, Austin, TX 78701",
|
||||||
|
"price": 425000,
|
||||||
|
"priceRangeLow": 382500,
|
||||||
|
"priceRangeHigh": 467500,
|
||||||
|
"confidence": "High",
|
||||||
|
"lastSaleDate": "2020-05-15",
|
||||||
|
"lastSalePrice": 380000,
|
||||||
|
"comparables": [
|
||||||
|
{
|
||||||
|
"address": "125 Main St",
|
||||||
|
"price": 430000,
|
||||||
|
"distance": 0.1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"cached": false,
|
||||||
|
"cache_age_hours": null,
|
||||||
|
"message": "Value estimate: $425,000 (fresh data)"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### get_rent_estimate
|
||||||
|
|
||||||
|
Get rent estimate for a property.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `address` (string, required): Full property address
|
||||||
|
- `propertyType` (string, optional): Property type (e.g., "Single Family", "Condo")
|
||||||
|
- `bedrooms` (integer, optional): Number of bedrooms
|
||||||
|
- `bathrooms` (float, optional): Number of bathrooms
|
||||||
|
- `squareFootage` (integer, optional): Square footage
|
||||||
|
- `force_refresh` (boolean, optional): Force cache refresh (default: false)
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tool": "get_rent_estimate",
|
||||||
|
"parameters": {
|
||||||
|
"address": "456 Oak Ave, Dallas, TX",
|
||||||
|
"bedrooms": 3,
|
||||||
|
"bathrooms": 2.0,
|
||||||
|
"squareFootage": 1500
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"estimate": {
|
||||||
|
"address": "456 Oak Ave, Dallas, TX",
|
||||||
|
"rent": 2500,
|
||||||
|
"rentRangeLow": 2250,
|
||||||
|
"rentRangeHigh": 2750,
|
||||||
|
"confidence": "Medium",
|
||||||
|
"comparables": [
|
||||||
|
{
|
||||||
|
"address": "458 Oak Ave",
|
||||||
|
"rent": 2450,
|
||||||
|
"distance": 0.05
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"cached": true,
|
||||||
|
"cache_age_hours": 1.2,
|
||||||
|
"message": "Rent estimate: $2,500/month (from cache, age: 1.2 hours)"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Listing Tools
|
||||||
|
|
||||||
|
### search_sale_listings
|
||||||
|
|
||||||
|
Search for properties for sale.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `address` (string, optional): Property address
|
||||||
|
- `city` (string, optional): City name
|
||||||
|
- `state` (string, optional): State code
|
||||||
|
- `zipCode` (string, optional): ZIP code
|
||||||
|
- `limit` (integer, optional): Maximum results (1-500, default: 10)
|
||||||
|
- `offset` (integer, optional): Results offset for pagination
|
||||||
|
- `force_refresh` (boolean, optional): Force cache refresh (default: false)
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tool": "search_sale_listings",
|
||||||
|
"parameters": {
|
||||||
|
"city": "Houston",
|
||||||
|
"state": "TX",
|
||||||
|
"limit": 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"listings": [
|
||||||
|
{
|
||||||
|
"id": "sale_000001",
|
||||||
|
"address": "789 Park Blvd",
|
||||||
|
"city": "Houston",
|
||||||
|
"state": "TX",
|
||||||
|
"price": 550000,
|
||||||
|
"bedrooms": 4,
|
||||||
|
"bathrooms": 3.0,
|
||||||
|
"squareFootage": 2400,
|
||||||
|
"listingDate": "2024-01-15",
|
||||||
|
"daysOnMarket": 45,
|
||||||
|
"photos": ["url1", "url2"],
|
||||||
|
"description": "Beautiful modern home"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"count": 10,
|
||||||
|
"cached": false,
|
||||||
|
"cache_age_hours": null,
|
||||||
|
"message": "Found 10 sale listings (fresh data)"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### search_rental_listings
|
||||||
|
|
||||||
|
Search for rental properties.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `address` (string, optional): Property address
|
||||||
|
- `city` (string, optional): City name
|
||||||
|
- `state` (string, optional): State code
|
||||||
|
- `zipCode` (string, optional): ZIP code
|
||||||
|
- `limit` (integer, optional): Maximum results (1-500, default: 10)
|
||||||
|
- `offset` (integer, optional): Results offset for pagination
|
||||||
|
- `force_refresh` (boolean, optional): Force cache refresh (default: false)
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tool": "search_rental_listings",
|
||||||
|
"parameters": {
|
||||||
|
"city": "San Antonio",
|
||||||
|
"state": "TX",
|
||||||
|
"limit": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"listings": [
|
||||||
|
{
|
||||||
|
"id": "rental_000001",
|
||||||
|
"address": "321 Rental Rd",
|
||||||
|
"city": "San Antonio",
|
||||||
|
"state": "TX",
|
||||||
|
"rent": 1800,
|
||||||
|
"bedrooms": 2,
|
||||||
|
"bathrooms": 2.0,
|
||||||
|
"squareFootage": 1100,
|
||||||
|
"availableDate": "2024-02-01",
|
||||||
|
"pets": "Cats OK",
|
||||||
|
"photos": ["url1", "url2"],
|
||||||
|
"description": "Cozy apartment"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"count": 5,
|
||||||
|
"cached": true,
|
||||||
|
"cache_age_hours": 3.5,
|
||||||
|
"message": "Found 5 rental listings (from cache, age: 3.5 hours)"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### get_market_statistics
|
||||||
|
|
||||||
|
Get market statistics for a location.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `city` (string, optional): City name
|
||||||
|
- `state` (string, optional): State code
|
||||||
|
- `zipCode` (string, optional): ZIP code
|
||||||
|
- `force_refresh` (boolean, optional): Force cache refresh (default: false)
|
||||||
|
|
||||||
|
**Note:** At least one location parameter is required.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tool": "get_market_statistics",
|
||||||
|
"parameters": {
|
||||||
|
"city": "Phoenix",
|
||||||
|
"state": "AZ"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"statistics": {
|
||||||
|
"city": "Phoenix",
|
||||||
|
"state": "AZ",
|
||||||
|
"medianSalePrice": 485000,
|
||||||
|
"medianRent": 2200,
|
||||||
|
"averageDaysOnMarket": 32,
|
||||||
|
"inventoryCount": 1250,
|
||||||
|
"pricePerSquareFoot": 285.50,
|
||||||
|
"rentPerSquareFoot": 1.85,
|
||||||
|
"appreciation": 6.5
|
||||||
|
},
|
||||||
|
"cached": false,
|
||||||
|
"cache_age_hours": null,
|
||||||
|
"message": "Market statistics retrieved (fresh data)"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cache Management Tools
|
||||||
|
|
||||||
|
### expire_cache
|
||||||
|
|
||||||
|
Expire cache entries to force fresh API calls.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `cache_key` (string, optional): Specific cache key to expire
|
||||||
|
- `endpoint` (string, optional): Expire all cache for endpoint
|
||||||
|
- `all` (boolean, optional): Expire all cache entries (default: false)
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tool": "expire_cache",
|
||||||
|
"parameters": {
|
||||||
|
"all": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "Expired 42 cache entries"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### get_cache_stats
|
||||||
|
|
||||||
|
Get cache statistics including hit/miss rates and storage usage.
|
||||||
|
|
||||||
|
**Parameters:** None
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tool": "get_cache_stats",
|
||||||
|
"parameters": {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"stats": {
|
||||||
|
"total_entries": 156,
|
||||||
|
"total_hits": 1247,
|
||||||
|
"total_misses": 312,
|
||||||
|
"cache_size_mb": 8.4,
|
||||||
|
"oldest_entry": "2024-01-01T10:00:00Z",
|
||||||
|
"newest_entry": "2024-01-15T14:30:00Z",
|
||||||
|
"hit_rate": 80.0
|
||||||
|
},
|
||||||
|
"message": "Cache hit rate: 80.0%"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage Analytics Tools
|
||||||
|
|
||||||
|
### get_usage_stats
|
||||||
|
|
||||||
|
Get API usage statistics including costs and endpoint breakdown.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `days` (integer, optional): Number of days to include (default: 30)
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tool": "get_usage_stats",
|
||||||
|
"parameters": {
|
||||||
|
"days": 7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"stats": {
|
||||||
|
"total_requests": 234,
|
||||||
|
"cache_hits": 187,
|
||||||
|
"cache_misses": 47,
|
||||||
|
"hit_rate": 79.9,
|
||||||
|
"total_cost": 4.70,
|
||||||
|
"by_endpoint": {
|
||||||
|
"property-records": 89,
|
||||||
|
"value-estimate": 45,
|
||||||
|
"rent-estimate-long-term": 38,
|
||||||
|
"market-statistics": 62
|
||||||
|
},
|
||||||
|
"days": 7
|
||||||
|
},
|
||||||
|
"message": "Usage stats for last 7 days"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration Tools
|
||||||
|
|
||||||
|
### set_api_limits
|
||||||
|
|
||||||
|
Update API rate limits and usage quotas.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `daily_limit` (integer, optional): Daily API request limit
|
||||||
|
- `monthly_limit` (integer, optional): Monthly API request limit
|
||||||
|
- `requests_per_minute` (integer, optional): Requests per minute limit
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tool": "set_api_limits",
|
||||||
|
"parameters": {
|
||||||
|
"daily_limit": 200,
|
||||||
|
"monthly_limit": 2000,
|
||||||
|
"requests_per_minute": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"limits": {
|
||||||
|
"daily_limit": 200,
|
||||||
|
"monthly_limit": 2000,
|
||||||
|
"requests_per_minute": 5
|
||||||
|
},
|
||||||
|
"message": "API limits updated"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### get_api_limits
|
||||||
|
|
||||||
|
Get current API rate limits and usage quotas.
|
||||||
|
|
||||||
|
**Parameters:** None
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tool": "get_api_limits",
|
||||||
|
"parameters": {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"limits": {
|
||||||
|
"daily_limit": 100,
|
||||||
|
"monthly_limit": 1000,
|
||||||
|
"requests_per_minute": 3,
|
||||||
|
"current_daily_usage": 42,
|
||||||
|
"current_monthly_usage": 567,
|
||||||
|
"current_minute_usage": 1
|
||||||
|
},
|
||||||
|
"message": "Daily: 42/100, Monthly: 567/1000"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Responses
|
||||||
|
|
||||||
|
All tools return consistent error responses:
|
||||||
|
|
||||||
|
### API Key Not Configured
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"error": "API key not configured",
|
||||||
|
"message": "Please set your Rentcast API key first using set_api_key tool"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rate Limit Exceeded
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"error": "Rate limit exceeded",
|
||||||
|
"message": "Daily rate limit exceeded (100 requests). Try again tomorrow.",
|
||||||
|
"retry_after": "Please wait before making more requests"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Confirmation Required
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"confirmation_required": true,
|
||||||
|
"message": "API call requires confirmation (estimated cost: $0.15)",
|
||||||
|
"retry_with": "Please confirm to proceed with the API request"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Invalid Parameters
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"error": "Invalid parameters",
|
||||||
|
"message": "At least one location parameter required"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Internal Error
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"error": "Internal error",
|
||||||
|
"message": "An unexpected error occurred: [details]"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Caching Behavior
|
||||||
|
|
||||||
|
### Cache Keys
|
||||||
|
|
||||||
|
Cache keys are generated based on:
|
||||||
|
- Endpoint name
|
||||||
|
- Request parameters (sorted)
|
||||||
|
- MD5 hash of the combination
|
||||||
|
|
||||||
|
### Cache Expiration
|
||||||
|
|
||||||
|
- Default TTL: 24 hours (configurable via `CACHE_TTL_HOURS`)
|
||||||
|
- Manual expiration available via `expire_cache` tool
|
||||||
|
- Automatic cleanup of expired entries
|
||||||
|
|
||||||
|
### Cache Indicators
|
||||||
|
|
||||||
|
All data-fetching tools return cache information:
|
||||||
|
- `cached`: Boolean indicating if response was from cache
|
||||||
|
- `cache_age_hours`: Age of cached data in hours (null if fresh)
|
||||||
|
- Message includes cache status
|
||||||
|
|
||||||
|
## Rate Limiting
|
||||||
|
|
||||||
|
### Limits
|
||||||
|
|
||||||
|
Configurable via environment variables or `set_api_limits` tool:
|
||||||
|
- Daily limit (default: 100)
|
||||||
|
- Monthly limit (default: 1000)
|
||||||
|
- Per-minute limit (default: 3)
|
||||||
|
|
||||||
|
### Enforcement
|
||||||
|
|
||||||
|
- Checked before each API call
|
||||||
|
- Returns 429-style error when exceeded
|
||||||
|
- Counters reset automatically
|
||||||
|
|
||||||
|
### Exponential Backoff
|
||||||
|
|
||||||
|
Failed requests automatically retry with exponential backoff:
|
||||||
|
- Base: 2.0 seconds
|
||||||
|
- Maximum: 300 seconds (5 minutes)
|
||||||
|
- Maximum attempts: 3
|
||||||
|
|
||||||
|
## Cost Management
|
||||||
|
|
||||||
|
### Cost Estimation
|
||||||
|
|
||||||
|
Approximate costs per endpoint:
|
||||||
|
- Property records: $0.10
|
||||||
|
- Property record (by ID): $0.05
|
||||||
|
- Value estimate: $0.15
|
||||||
|
- Rent estimate: $0.15
|
||||||
|
- Sale listings: $0.08
|
||||||
|
- Rental listings: $0.08
|
||||||
|
- Market statistics: $0.20
|
||||||
|
|
||||||
|
### Confirmation System
|
||||||
|
|
||||||
|
For non-cached requests:
|
||||||
|
1. Cost is estimated
|
||||||
|
2. User confirmation requested (if supported)
|
||||||
|
3. Request proceeds only after confirmation
|
||||||
|
4. Confirmations cached for 15 minutes
|
||||||
|
|
||||||
|
### Cost Tracking
|
||||||
|
|
||||||
|
- All API calls logged with cost estimates
|
||||||
|
- View total costs via `get_usage_stats`
|
||||||
|
- Cache hits have zero cost
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Use Caching**: Let the cache work for you - avoid `force_refresh` unless necessary
|
||||||
|
2. **Batch Requests**: Group related searches to maximize cache efficiency
|
||||||
|
3. **Monitor Usage**: Regularly check `get_usage_stats` and `get_api_limits`
|
||||||
|
4. **Test with Mock**: Use mock API (`USE_MOCK_API=true`) for development
|
||||||
|
5. **Set Appropriate Limits**: Configure rate limits based on your API plan
|
||||||
|
6. **Handle Errors**: Implement proper error handling for rate limits and confirmations
|
||||||
|
7. **Optimize Searches**: Use specific parameters to improve cache hit rates
|
@ -43,12 +43,13 @@ dev = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
Homepage = "https://github.com/your-username/mcrentcast"
|
Homepage = "https://git.supported.systems/MCP/mcrentcast"
|
||||||
Repository = "https://github.com/your-username/mcrentcast.git"
|
Repository = "https://git.supported.systems/MCP/mcrentcast.git"
|
||||||
Documentation = "https://github.com/your-username/mcrentcast#readme"
|
Documentation = "https://git.supported.systems/MCP/mcrentcast#readme"
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
mcrentcast = "mcrentcast.server:main"
|
mcrentcast = "mcrentcast.server:main"
|
||||||
|
mcrentcast-mock-api = "mcrentcast.mock_api:run_mock_server"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["hatchling"]
|
requires = ["hatchling"]
|
||||||
|
79
scripts/test_mcp_server.py
Normal file
79
scripts/test_mcp_server.py
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
"""Test the MCP server functionality."""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Add src to path
|
||||||
|
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
||||||
|
|
||||||
|
from mcrentcast.server import app
|
||||||
|
|
||||||
|
|
||||||
|
async def test_tools():
|
||||||
|
"""Test MCP server tools."""
|
||||||
|
print("Testing mcrentcast MCP Server")
|
||||||
|
print("=" * 50)
|
||||||
|
|
||||||
|
# List available tools
|
||||||
|
tools = []
|
||||||
|
for name, func in app._tools.items():
|
||||||
|
tools.append(name)
|
||||||
|
print(f" - {name}")
|
||||||
|
|
||||||
|
print(f"\nTotal tools: {len(tools)}")
|
||||||
|
|
||||||
|
# Test set_api_key
|
||||||
|
print("\n1. Testing set_api_key...")
|
||||||
|
result = await app._tools["set_api_key"](api_key="test_key_basic")
|
||||||
|
print(f" Result: {result}")
|
||||||
|
|
||||||
|
# Test get_api_limits
|
||||||
|
print("\n2. Testing get_api_limits...")
|
||||||
|
result = await app._tools["get_api_limits"]()
|
||||||
|
print(f" Result: {json.dumps(result, indent=2)}")
|
||||||
|
|
||||||
|
# Test search_properties (with cache miss)
|
||||||
|
print("\n3. Testing search_properties...")
|
||||||
|
result = await app._tools["search_properties"](
|
||||||
|
city="Austin",
|
||||||
|
state="TX",
|
||||||
|
limit=2
|
||||||
|
)
|
||||||
|
print(f" Found {result.get('count', 0)} properties")
|
||||||
|
print(f" Cached: {result.get('cached', False)}")
|
||||||
|
|
||||||
|
# Test again (should hit cache)
|
||||||
|
print("\n4. Testing search_properties (cache hit)...")
|
||||||
|
result = await app._tools["search_properties"](
|
||||||
|
city="Austin",
|
||||||
|
state="TX",
|
||||||
|
limit=2
|
||||||
|
)
|
||||||
|
print(f" Found {result.get('count', 0)} properties")
|
||||||
|
print(f" Cached: {result.get('cached', False)}")
|
||||||
|
print(f" Cache age: {result.get('cache_age_hours', 'N/A')} hours")
|
||||||
|
|
||||||
|
# Test get_cache_stats
|
||||||
|
print("\n5. Testing get_cache_stats...")
|
||||||
|
result = await app._tools["get_cache_stats"]()
|
||||||
|
print(f" Result: {json.dumps(result, indent=2)}")
|
||||||
|
|
||||||
|
# Test get_usage_stats
|
||||||
|
print("\n6. Testing get_usage_stats...")
|
||||||
|
result = await app._tools["get_usage_stats"](days=7)
|
||||||
|
print(f" Result: {json.dumps(result, indent=2)}")
|
||||||
|
|
||||||
|
print("\n" + "=" * 50)
|
||||||
|
print("All tests completed successfully!")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# Set environment for testing
|
||||||
|
os.environ["USE_MOCK_API"] = "true"
|
||||||
|
os.environ["RENTCAST_API_KEY"] = "test_key_basic"
|
||||||
|
|
||||||
|
asyncio.run(test_tools())
|
@ -4,13 +4,20 @@ import os
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from pydantic import Field
|
from pydantic import Field, ConfigDict
|
||||||
from pydantic_settings import BaseSettings
|
from pydantic_settings import BaseSettings
|
||||||
|
|
||||||
|
|
||||||
class Settings(BaseSettings):
|
class Settings(BaseSettings):
|
||||||
"""Application settings."""
|
"""Application settings."""
|
||||||
|
|
||||||
|
model_config = ConfigDict(
|
||||||
|
env_file=".env",
|
||||||
|
env_file_encoding="utf-8",
|
||||||
|
case_sensitive=False,
|
||||||
|
extra="ignore" # Allow extra fields from .env file
|
||||||
|
)
|
||||||
|
|
||||||
# Environment
|
# Environment
|
||||||
mode: str = Field(default="development", description="Application mode")
|
mode: str = Field(default="development", description="Application mode")
|
||||||
debug: bool = Field(default=False, description="Debug mode")
|
debug: bool = Field(default=False, description="Debug mode")
|
||||||
@ -47,12 +54,6 @@ class Settings(BaseSettings):
|
|||||||
exponential_backoff_base: float = Field(default=2.0, description="Exponential backoff base")
|
exponential_backoff_base: float = Field(default=2.0, description="Exponential backoff base")
|
||||||
exponential_backoff_max_delay: int = Field(default=300, description="Max delay for exponential backoff in seconds")
|
exponential_backoff_max_delay: int = Field(default=300, description="Max delay for exponential backoff in seconds")
|
||||||
|
|
||||||
class Config:
|
|
||||||
env_file = ".env"
|
|
||||||
env_file_encoding = "utf-8"
|
|
||||||
case_sensitive = False
|
|
||||||
extra = "ignore" # Allow extra fields from .env file
|
|
||||||
|
|
||||||
def __init__(self, **data):
|
def __init__(self, **data):
|
||||||
super().__init__(**data)
|
super().__init__(**data)
|
||||||
self._ensure_directories()
|
self._ensure_directories()
|
||||||
|
@ -21,8 +21,7 @@ from sqlalchemy import (
|
|||||||
create_engine,
|
create_engine,
|
||||||
func,
|
func,
|
||||||
)
|
)
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
from sqlalchemy.orm import declarative_base, sessionmaker, Session
|
||||||
from sqlalchemy.orm import sessionmaker, Session
|
|
||||||
|
|
||||||
from .config import settings
|
from .config import settings
|
||||||
from .models import (
|
from .models import (
|
||||||
|
@ -481,6 +481,17 @@ async def get_test_keys():
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
def run_mock_server():
|
||||||
|
"""Run the mock Rentcast API server."""
|
||||||
import uvicorn
|
import uvicorn
|
||||||
uvicorn.run(mock_app, host="0.0.0.0", port=8001)
|
uvicorn.run(
|
||||||
|
"mcrentcast.mock_api:mock_app",
|
||||||
|
host="0.0.0.0",
|
||||||
|
port=8001,
|
||||||
|
reload=True,
|
||||||
|
log_level="info"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
run_mock_server()
|
@ -752,65 +752,31 @@ async def get_api_limits() -> Dict[str, Any]:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# Health check endpoint
|
# Initialize on module load
|
||||||
@app.get("/health")
|
logger.info("Starting mcrentcast MCP server", mode=settings.mode)
|
||||||
async def health_check():
|
|
||||||
"""Health check endpoint."""
|
|
||||||
return {
|
|
||||||
"status": "healthy",
|
|
||||||
"api_key_configured": settings.validate_api_key(),
|
|
||||||
"mode": settings.mode,
|
|
||||||
"timestamp": datetime.utcnow().isoformat()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
# Create database tables
|
||||||
|
db_manager.create_tables()
|
||||||
|
|
||||||
# Startup and shutdown events
|
# Initialize Rentcast client if API key is configured
|
||||||
@app.on_startup
|
if settings.validate_api_key():
|
||||||
async def startup():
|
|
||||||
"""Initialize database and client on startup."""
|
|
||||||
logger.info("Starting mcrentcast MCP server", mode=settings.mode)
|
|
||||||
|
|
||||||
# Create database tables
|
|
||||||
db_manager.create_tables()
|
|
||||||
|
|
||||||
# Initialize Rentcast client if API key is configured
|
|
||||||
if settings.validate_api_key():
|
|
||||||
global rentcast_client
|
|
||||||
rentcast_client = RentcastClient()
|
rentcast_client = RentcastClient()
|
||||||
logger.info("Rentcast client initialized")
|
logger.info("Rentcast client initialized")
|
||||||
else:
|
else:
|
||||||
logger.warning("No API key configured - set using set_api_key tool")
|
logger.warning("No API key configured - set using set_api_key tool")
|
||||||
|
|
||||||
# Clean expired cache entries
|
|
||||||
count = await db_manager.clean_expired_cache()
|
|
||||||
logger.info(f"Cleaned {count} expired cache entries")
|
|
||||||
|
|
||||||
|
|
||||||
@app.on_shutdown
|
|
||||||
async def shutdown():
|
|
||||||
"""Cleanup on shutdown."""
|
|
||||||
logger.info("Shutting down mcrentcast MCP server")
|
|
||||||
|
|
||||||
# Close Rentcast client
|
|
||||||
if rentcast_client:
|
|
||||||
await rentcast_client.close()
|
|
||||||
|
|
||||||
logger.info("Shutdown complete")
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Main entry point for the MCP server."""
|
"""Main entry point for the MCP server."""
|
||||||
import uvicorn
|
# FastMCP handles the stdio communication automatically
|
||||||
|
# when the module is imported
|
||||||
# Run the server
|
pass
|
||||||
uvicorn.run(
|
|
||||||
app,
|
|
||||||
host="0.0.0.0",
|
|
||||||
port=settings.mcp_server_port,
|
|
||||||
log_level="info" if settings.is_development else "warning",
|
|
||||||
reload=settings.is_development
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
# Keep the process running for MCP communication
|
||||||
|
import asyncio
|
||||||
|
try:
|
||||||
|
asyncio.get_event_loop().run_forever()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass
|
Loading…
x
Reference in New Issue
Block a user