Skip to main content
Glama

Hostaway MCP Server

research.md22.8 kB
# Research: Hostaway MCP Server Integration **Feature Branch**: `001-we-need-to` **Created**: 2025-10-12 **Status**: Complete ## Executive Summary This research document evaluates technical approaches for building a FastAPI-based MCP server that integrates with the Hostaway property management API. The focus areas include OAuth 2.0 authentication, rate limiting, async HTTP client configuration, FastAPI-MCP integration patterns, and Hostaway API endpoint mapping. --- ## 1. OAuth 2.0 Implementation Patterns ### Decision: Token-Based Authentication with Automatic Refresh **Approach**: - Use OAuth 2.0 Client Credentials Grant flow - Implement token storage in memory with thread-safe access - Automatic token refresh before expiration (24-month validity) - FastAPI dependency injection for token management **Rationale**: 1. **Hostaway Requirements**: Hostaway uses OAuth 2.0 Client Credentials Grant as the standard authentication method 2. **Long Token Lifetime**: 24-month token validity reduces refresh frequency concerns 3. **Thread Safety**: FastAPI's async nature requires thread-safe token management for concurrent requests 4. **Zero Downtime**: Proactive token refresh ensures uninterrupted service **Alternatives Considered**: | Alternative | Why Rejected | |------------|--------------| | API Key Only | Hostaway requires OAuth 2.0; not supported | | User-Based OAuth | Not applicable for server-to-server integration | | Token per Request | Inefficient; wastes API calls and adds latency | | External Token Store (Redis) | Over-engineering for single-tenant deployment | **Implementation Notes**: ```python # Authentication endpoint POST https://api.hostaway.com/v1/accessTokens Content-Type: application/x-www-form-urlencoded grant_type=client_credentials client_id={HOSTAWAY_ACCOUNT_ID} client_secret={HOSTAWAY_SECRET_KEY} scope=general ``` **Token Management Strategy**: 1. **Storage**: In-memory singleton with asyncio.Lock for thread safety 2. **Refresh Logic**: Check expiration before each request; refresh if <7 days remaining 3. **FastAPI Integration**: Use `Depends()` to inject authenticated client into route handlers 4. **Error Handling**: Fail fast on invalid credentials; auto-retry on transient token errors **Key Libraries**: - `httpx.AsyncClient` for OAuth requests - `asyncio.Lock` for thread-safe token access - `pydantic-settings` for secure credential loading from environment **Security Considerations**: - Store credentials in environment variables only (never in code/logs) - Transmit tokens over HTTPS exclusively - Implement token rotation before expiration to avoid service disruption - Log authentication events (success/failure) for audit trail --- ## 2. Rate Limiting Strategies ### Decision: Token Bucket Algorithm with Semaphore-Based Concurrency Control **Approach**: - Implement Token Bucket algorithm for per-second rate limiting - Use `asyncio.Semaphore` for concurrent request limiting - Client-side rate limiting to prevent API lockout - Separate limits for IP (15 req/10s) and account (20 req/10s) **Rationale**: 1. **Hostaway Limits**: Dual rate limits (15 per 10s per IP, 20 per 10s per account) require sophisticated client-side enforcement 2. **Token Bucket Advantages**: Smooths burst traffic, allows slight overages, natural fit for async Python 3. **Semaphore for Concurrency**: Prevents overwhelming the API with simultaneous requests from multiple AI sessions 4. **Proactive Prevention**: Client-side limiting is more reliable than handling 429 responses **Alternatives Considered**: | Alternative | Why Rejected | |------------|--------------| | Fixed Window | Allows bursts at window boundaries; less smooth traffic | | Sliding Window | More complex to implement; token bucket sufficient for our needs | | Reactive (429 Handling Only) | Risks API lockout; doesn't prevent issues | | Server-Side Only | No control; relies on Hostaway's enforcement | **Implementation Notes**: ```python # Using aiolimiter for token bucket from aiolimiter import AsyncLimiter # Rate limiters ip_limiter = AsyncLimiter(max_rate=15, time_period=10) # 15 req per 10s account_limiter = AsyncLimiter(max_rate=20, time_period=10) # 20 req per 10s # Concurrency limiter concurrency_semaphore = asyncio.Semaphore(10) # Max 10 concurrent requests async def make_request(client, endpoint): async with concurrency_semaphore: async with ip_limiter: async with account_limiter: return await client.get(endpoint) ``` **Rate Limiting Configuration**: - **IP Rate Limit**: 15 requests per 10 seconds (conservative; actual limit 15/10s) - **Account Rate Limit**: 20 requests per 10 seconds (conservative; actual limit 20/10s) - **Concurrency Limit**: 10 simultaneous requests (prevents connection pool exhaustion) - **Burst Allowance**: Token bucket naturally allows small bursts when tokens available **Key Libraries**: - `aiolimiter` - Token bucket implementation with async support - `asyncio.Semaphore` - Built-in concurrency limiting - `asyncio.Lock` - Thread-safe rate limiter access **Monitoring & Adjustment**: - Log rate limit near-misses (>90% capacity) - Track 429 responses despite client-side limiting - Adjust limits based on production traffic patterns - Consider dynamic limits based on time of day --- ## 3. FastAPI-MCP Integration Patterns ### Decision: Direct Mounting with Purpose-Built Tools **Approach**: - Mount MCP server directly to FastAPI app at `/mcp` endpoint - Use FastAPI's dependency injection for authentication - Create high-level, purpose-built MCP tools (not 1:1 API endpoint mapping) - ASGI transport for direct communication (no HTTP overhead) **Rationale**: 1. **Best Practice 2025**: FastAPI-MCP is designed as a native extension, not a converter 2. **Authentication Integration**: Leverage existing FastAPI `Depends()` for secure tool access 3. **Performance**: ASGI transport eliminates HTTP call overhead between MCP and FastAPI 4. **Maintainability**: Purpose-built tools are clearer for AI agents than raw API endpoints **Alternatives Considered**: | Alternative | Why Rejected | |------------|--------------| | Separate MCP Deployment | Adds deployment complexity; requires HTTP calls between services | | Auto-Generate All Endpoints | Overloads toolset; confuses AI with too many low-level options | | Custom MCP Protocol | Reinventing the wheel; FastAPI-MCP handles this well | | REST-Only (No MCP) | Doesn't meet requirement for AI-callable tools | **Implementation Notes**: ```python # FastAPI app with MCP integration from fastapi import FastAPI, Depends from fastapi_mcp import FastMCP app = FastAPI() mcp = FastMCP() # High-level tool example @mcp.tool() async def search_available_properties( check_in: str, check_out: str, guests: int = 2, client: HostawayClient = Depends(get_authenticated_client) ) -> list[PropertySummary]: """Search for available properties in date range. This tool combines listing retrieval, availability checking, and guest capacity filtering into one AI-friendly operation. """ listings = await client.get_listings() available = await client.check_availability(listings, check_in, check_out) return [p for p in available if p.capacity >= guests] # Mount MCP to FastAPI app.mount("/mcp", mcp.app) ``` **Tool Design Principles**: 1. **High-Level Operations**: Combine multiple API calls into single tools (e.g., "search available properties" vs separate listing/availability calls) 2. **Clear Descriptions**: Every tool has detailed docstrings explaining purpose, parameters, and return values 3. **Selective Exposure**: Only expose endpoints useful and safe for AI agents 4. **Input Validation**: Use Pydantic models for all tool parameters 5. **Error Context**: Return actionable error messages when operations fail **Authentication Flow**: ```python # Dependency injection for auth async def get_authenticated_client() -> HostawayClient: token = await token_manager.get_valid_token() return HostawayClient(token=token) # Applied to tools automatically @mcp.tool() async def get_booking( booking_id: int, client: HostawayClient = Depends(get_authenticated_client) ) -> BookingDetails: return await client.get_booking(booking_id) ``` **Key Libraries**: - `fastapi-mcp` (v0.3.0+) - MCP integration for FastAPI - `fastapi` (v0.115+) - Web framework - `pydantic` (v2.0+) - Data validation and serialization **Best Practices Applied**: 1. **Lifespan Management**: Use nested async context managers for proper initialization order 2. **Schema Validation**: MCP Inspector automatically validates tool schemas 3. **Security**: Authenticate all tools via FastAPI dependencies 4. **Logging**: Enable detailed logging for debugging tool invocations 5. **Testing**: Test tool schemas with MCP Inspector before deployment --- ## 4. Hostaway API Endpoints ### Decision: Comprehensive Endpoint Coverage with Priority-Based Implementation **Approach**: - Map all critical Hostaway endpoints to MCP tools - Implement Priority 1 (P1) endpoints first: authentication, listings, bookings - Use Pydantic models for request/response validation - Design tools around user scenarios, not raw API structure **Hostaway API Overview**: | Category | Endpoints | Response Schema | Priority | |----------|-----------|-----------------|----------| | **Authentication** | `POST /v1/accessTokens` | `{access_token, expires_in}` | P1 | | **Listings** | `GET /listings`<br>`GET /listings/{id}`<br>`POST /listings`<br>`PUT /listings/{id}` | Property details, amenities, pricing, capacity | P1 | | **Bookings/Reservations** | `GET /reservations`<br>`GET /reservations/{id}`<br>`POST /reservations`<br>`PUT /reservations/{id}` | Guest info, dates, payment status, property link | P1 | | **Calendar** | `GET /calendar`<br>`POST /calendar/block`<br>`DELETE /calendar/block` | Available/blocked dates, existing reservations | P2 | | **Guest Communication** | `POST /messages`<br>`GET /conversations` | Message content, delivery channel, history | P2 | | **Financial** | `GET /financialReports`<br>`GET /reservations/{id}/payments` | Revenue, expenses, payment methods | P3 | **Key Listing Object Fields**: ```python class Listing(BaseModel): id: int name: str address: str description: str capacity: int bedrooms: int bathrooms: float amenities: list[str] pricing: PricingInfo availability: AvailabilityInfo cancellation_policy: str images: list[str] channel_ids: dict[str, str] # Airbnb, VRBO, Booking.com ``` **Key Reservation Object Fields**: ```python class Reservation(BaseModel): id: int listing_id: int guest_id: int check_in: date check_out: date guests: int status: str # confirmed, cancelled, pending, etc. total_price: float payment_status: str booking_source: str guest_name: str guest_email: str guest_phone: str ``` **API Features**: - **Partial Updates**: Listing endpoint supports partial updates (pass only changed fields) - **Multi-Channel**: Listings can be synced to Airbnb, VRBO, Booking.com - **Webhooks**: Available for reservation changes and new messages - **Pagination**: Large result sets paginated (default 100 items per page) **Rate Limits (Reiterated)**: - 15 requests per 10 seconds per IP - 20 requests per 10 seconds per account - Access tokens valid for 24 months **Implementation Priority**: **Phase 1 (P1) - Core Operations**: - Authentication (token exchange) - List all properties - Get property details - Search bookings (date range, status filters) - Get booking details **Phase 2 (P2) - Enhanced Features**: - Check calendar availability - Block/unblock dates - Send guest messages - View conversation history **Phase 3 (P3) - Analytics & Reporting**: - Financial reports - Revenue aggregation - Payment tracking **Webhook Considerations**: - Webhooks available for push notifications (reservation updates, new messages) - Consider implementing webhook receiver for real-time updates - Reduces polling frequency for calendar/booking changes - Out of scope for initial implementation; add in future iteration --- ## 5. Async HTTP Client Best Practices ### Decision: httpx.AsyncClient with Connection Pooling and Retry Logic **Approach**: - Use `httpx.AsyncClient` as singleton with connection pooling - Configure connection limits based on rate limits - Implement exponential backoff with jitter for retry logic - Set appropriate timeouts for external API calls **Rationale**: 1. **Connection Pooling**: Reusing connections reduces latency and overhead 2. **Async Native**: httpx designed for asyncio; integrates seamlessly with FastAPI 3. **Retry Logic**: Transient network errors are common; automatic retries improve reliability 4. **Resource Management**: Proper limits prevent resource exhaustion under load **Alternatives Considered**: | Alternative | Why Rejected | |------------|--------------| | requests (sync) | Blocking; incompatible with FastAPI async handlers | | aiohttp | Less mature; httpx has better type hints and simpler API | | urllib3 | Too low-level; requires manual async handling | | Multiple Client Instances | Defeats connection pooling; wastes resources | **Implementation Notes**: ```python import httpx from httpx import Limits, Timeout # Connection pool configuration limits = Limits( max_keepalive_connections=20, # Reuse up to 20 connections max_connections=50, # Total connection limit keepalive_expiry=30.0 # Close idle connections after 30s ) # Timeout configuration timeout = Timeout( connect=5.0, # 5s to establish connection read=30.0, # 30s to read response write=10.0, # 10s to send request pool=5.0 # 5s to acquire connection from pool ) # Singleton async client client = httpx.AsyncClient( base_url="https://api.hostaway.com/v1", limits=limits, timeout=timeout, http2=True, # Enable HTTP/2 for multiplexing follow_redirects=True ) ``` **Retry Logic with Exponential Backoff**: ```python from tenacity import ( retry, stop_after_attempt, wait_exponential, retry_if_exception_type ) @retry( stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10), retry=retry_if_exception_type((httpx.TimeoutException, httpx.NetworkError)), reraise=True ) async def make_api_request(endpoint: str, **kwargs): async with rate_limiters: response = await client.get(endpoint, **kwargs) response.raise_for_status() return response.json() ``` **Retry Strategy Details**: - **Attempts**: Retry up to 3 times (total 4 attempts including initial) - **Backoff**: Exponential with base 2 seconds (2s, 4s, 8s) - **Max Delay**: Cap at 10 seconds to avoid excessive waits - **Jitter**: Add randomness to prevent thundering herd (built into tenacity) - **Retry Conditions**: Only retry on network/timeout errors, not on 4xx client errors - **Final Failure**: Re-raise exception if all retries exhausted **Timeout Strategy**: - **Connect**: 5 seconds (reasonable for API) - **Read**: 30 seconds (some endpoints may be slow) - **Write**: 10 seconds (requests are small) - **Pool**: 5 seconds (fail fast if no connections available) **Connection Pool Sizing**: - **Max Connections (50)**: Based on concurrent semaphore limit (10) with safety margin - **Keep-Alive (20)**: Matches expected concurrent usage; reduces connection overhead - **Keep-Alive Expiry (30s)**: Balances resource usage with connection freshness **Error Handling**: ```python # Distinguish error types try: response = await make_api_request("/listings") except httpx.HTTPStatusError as e: if e.response.status_code == 429: # Rate limit exceeded (shouldn't happen with client-side limiting) log.error("Rate limit exceeded despite client-side limiting") raise RateLimitError("Too many requests") elif e.response.status_code == 401: # Authentication failure await token_manager.invalidate_token() raise AuthenticationError("Invalid or expired token") else: raise APIError(f"HTTP {e.response.status_code}: {e.response.text}") except httpx.TimeoutException: # All retries exhausted raise APIError("Request timed out after retries") except httpx.NetworkError: # Network connectivity issues raise APIError("Network error; check connectivity") ``` **Key Libraries**: - `httpx` (v0.27+) - Async HTTP client with HTTP/2 support - `tenacity` (v8.0+) - Retry logic with exponential backoff - `aiolimiter` - Rate limiting (from section 2) **Performance Considerations**: - **Connection Reuse**: Keep-alive connections reduce latency by ~50ms per request - **HTTP/2 Multiplexing**: Multiple requests over single connection - **Connection Pool Warmup**: Pre-establish connections on startup (optional) - **Monitoring**: Track connection pool metrics (active, idle, wait time) **Resource Cleanup**: ```python # Proper shutdown @app.on_event("shutdown") async def shutdown_event(): await client.aclose() # Close all connections gracefully ``` --- ## Implementation Recommendations ### Priority 1: Foundation (Week 1) 1. **Authentication System** - OAuth 2.0 token exchange - Token storage with thread-safe access - Automatic refresh logic - FastAPI dependency for authenticated client 2. **HTTP Client Setup** - Configure httpx.AsyncClient singleton - Implement connection pooling - Add retry logic with exponential backoff - Set appropriate timeouts 3. **Rate Limiting** - Token bucket implementation with aiolimiter - Semaphore-based concurrency control - Monitoring for rate limit violations ### Priority 2: Core Tools (Week 2) 4. **Property Management Tools** - `list_listings` - Retrieve all properties - `get_listing` - Get property details - `check_availability` - Calendar availability 5. **Booking Management Tools** - `search_bookings` - Filter by date/status/property - `get_booking` - Retrieve booking details - `get_booking_guest` - Guest information ### Priority 3: Communication & Analytics (Week 3) 6. **Guest Communication** - `send_guest_message` - Email/SMS/platform messaging - `get_conversation_history` - Message thread 7. **Financial & Calendar** - `get_financial_report` - Revenue/expenses - `block_dates` - Calendar management ### Testing Strategy - **Unit Tests**: Mock HTTP responses for fast testing - **Integration Tests**: Use Hostaway sandbox environment - **Load Tests**: Verify rate limiting under concurrent load - **Security Tests**: Validate credential handling and token refresh ### Monitoring & Observability - **Metrics to Track**: - Token refresh frequency and failures - Rate limit capacity usage (percentage) - API latency percentiles (p50, p95, p99) - Retry rates by error type - Connection pool utilization - **Logging**: - All authentication events - Rate limit near-misses - Tool invocations with parameters (sanitized) - API errors with full context ### Deployment Considerations - **Environment Variables**: - `HOSTAWAY_ACCOUNT_ID` - OAuth client ID - `HOSTAWAY_SECRET_KEY` - OAuth client secret - `RATE_LIMIT_IP` - IP rate limit (default 15/10s) - `RATE_LIMIT_ACCOUNT` - Account rate limit (default 20/10s) - `LOG_LEVEL` - Logging verbosity (default INFO) - **Infrastructure**: - Deploy behind HTTPS load balancer - Use secrets manager for credentials (AWS Secrets Manager, HashiCorp Vault) - Enable health check endpoint (`/health`) - Configure graceful shutdown for connection cleanup - **Scaling**: - Horizontal scaling limited by account rate limit (20 req/10s) - Consider multiple Hostaway accounts for higher throughput - Use distributed rate limiting (Redis) for multi-instance deployments --- ## Risk Assessment | Risk | Impact | Mitigation | |------|--------|------------| | **Hostaway API Downtime** | High | Implement circuit breaker; return cached data; clear error messages | | **Rate Limit Exhaustion** | Medium | Client-side enforcement; monitoring alerts; request queuing | | **Token Expiration During Operation** | Low | Proactive refresh; automatic retry with new token | | **Connection Pool Exhaustion** | Medium | Semaphore limits; connection pool sizing; monitoring | | **Slow API Responses** | Medium | Appropriate timeouts; async operations; status feedback to AI | | **Credential Leakage** | High | Environment variables only; no logging; secrets rotation | --- ## Open Questions 1. **Webhook Implementation**: Should we implement webhook receiver for real-time updates, or rely on polling? - *Recommendation*: Start with polling; add webhooks in iteration 2 if needed 2. **Multi-Tenant Support**: Should the server support multiple Hostaway accounts? - *Recommendation*: Single account per deployment for MVP; multi-tenant in future if demand exists 3. **Caching Strategy**: Should we cache property/booking data to reduce API calls? - *Recommendation*: Start without caching; add TTL-based caching if rate limits become constraint 4. **Error Recovery**: How should the system handle partial failures (e.g., 5 of 10 properties retrieved)? - *Recommendation*: Return partial results with error context; AI can decide whether to retry 5. **Token Storage**: Should tokens be persisted to disk for server restarts? - *Recommendation*: In-memory only for MVP; tokens are long-lived (24 months), re-auth on restart is acceptable --- ## References 1. **Hostaway API Documentation**: https://api.hostaway.com/documentation 2. **FastAPI-MCP GitHub**: https://github.com/tadata-org/fastapi_mcp 3. **MCP Specification**: https://modelcontextprotocol.io/ 4. **httpx Documentation**: https://www.python-httpx.org/ 5. **OAuth 2.0 RFC 6749**: https://datatracker.ietf.org/doc/html/rfc6749 6. **aiolimiter**: https://github.com/mjpieters/aiolimiter 7. **tenacity**: https://tenacity.readthedocs.io/ --- ## Conclusion The recommended architecture combines: - **FastAPI-MCP** for native MCP integration with ASGI transport - **OAuth 2.0** with automatic token refresh and dependency injection - **Token Bucket + Semaphore** for comprehensive rate limiting - **httpx.AsyncClient** with connection pooling and retry logic - **Purpose-Built Tools** that map to user scenarios, not raw API endpoints This approach balances performance, reliability, maintainability, and security while adhering to FastAPI-MCP best practices and Hostaway API requirements. **Next Steps**: 1. Proceed to `/speckit.plan` for detailed implementation planning 2. Create data models for Hostaway API responses 3. Define API contracts for MCP tools 4. Generate implementation tasks with `/speckit.tasks`

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/darrentmorgan/hostaway-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server