Skip to main content
Glama
server.py29.7 kB
"""Trade Me MCP Server - Main server implementation.""" import asyncio import logging import os from typing import Any from dotenv import load_dotenv from mcp.server import Server from mcp.server.stdio import stdio_server from mcp.types import Tool, TextContent from .client import TradeMeClient from .auth import TradeMeAuth from .tools import catalogue, search, listings, bidding # Load environment variables load_dotenv() # Configure logging logging.basicConfig( level=os.getenv("LOG_LEVEL", "INFO"), format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", ) logger = logging.getLogger(__name__) # Initialize MCP server app = Server("trademe-mcp-server") # Initialize Trade Me client trademe_client: TradeMeClient | None = None def get_client() -> TradeMeClient: """Get or create Trade Me client instance.""" global trademe_client if trademe_client is None: trademe_client = TradeMeClient() return trademe_client @app.list_tools() async def list_tools() -> list[Tool]: """List all available MCP tools.""" return [ Tool( name="get_categories", description="Retrieve the full Trade Me category tree with all categories and subcategories", inputSchema={ "type": "object", "properties": { "depth": { "type": "integer", "description": "Optional depth limit for subcategories (1-4)", "minimum": 1, "maximum": 4, }, "with_counts": { "type": "boolean", "description": "Whether to include listing counts", }, }, }, ), Tool( name="get_category_details", description="Get detailed information about a specific category including subcategories and attributes", inputSchema={ "type": "object", "properties": { "category_number": { "type": "string", "description": "The Trade Me category number (e.g., '0004-0369-6076-')", }, "depth": { "type": "integer", "description": "Optional depth limit for subcategories", "minimum": 1, "maximum": 4, }, }, "required": ["category_number"], }, ), Tool( name="get_localities", description="Retrieve all Trade Me localities (regions, districts, suburbs) in New Zealand", inputSchema={ "type": "object", "properties": {}, }, ), Tool( name="get_locality_details", description="Get detailed information about a specific locality", inputSchema={ "type": "object", "properties": { "locality_id": { "type": "integer", "description": "The locality ID number", }, }, "required": ["locality_id"], }, ), Tool( name="get_districts", description="Retrieve all Trade Me districts in New Zealand", inputSchema={ "type": "object", "properties": {}, }, ), Tool( name="get_attributes", description="Get available searchable and required attributes for a specific category", inputSchema={ "type": "object", "properties": { "category_number": { "type": "string", "description": "The Trade Me category number", }, }, "required": ["category_number"], }, ), Tool( name="get_legal_notice", description="Get the legal notice text for a category (some categories require sellers to display legal notices)", inputSchema={ "type": "object", "properties": { "category_number": { "type": "string", "description": "The Trade Me category number", }, }, "required": ["category_number"], }, ), # Search tools Tool( name="search_general", description="Search the Trade Me marketplace across all categories with various filtering options", inputSchema={ "type": "object", "properties": { "search_string": {"type": "string", "description": "Search keywords"}, "category": {"type": "string", "description": "Category number to search within"}, "price_min": {"type": "number", "description": "Minimum price filter"}, "price_max": {"type": "number", "description": "Maximum price filter"}, "condition": {"type": "string", "description": "Item condition (New, Used, Refurbished)"}, "buy_now_only": {"type": "boolean", "description": "Only show Buy Now listings"}, "region": {"type": "integer", "description": "Region ID for location filtering"}, "district": {"type": "integer", "description": "District ID for location filtering"}, "suburb": {"type": "integer", "description": "Suburb ID for location filtering"}, "page": {"type": "integer", "description": "Page number for pagination (default: 1)"}, "rows": {"type": "integer", "description": "Results per page (default: 25, max: 500)"}, "sort_order": {"type": "string", "description": "Sort order (Default, ExpiryAsc, ExpiryDesc, PriceAsc, PriceDesc, Featured)"}, }, }, ), Tool( name="search_property_residential", description="Search for residential properties for sale (houses, apartments, land)", inputSchema={ "type": "object", "properties": { "search_string": {"type": "string", "description": "Search keywords"}, "region": {"type": "integer", "description": "Region ID"}, "district": {"type": "integer", "description": "District ID"}, "suburb": {"type": "integer", "description": "Suburb ID"}, "price_min": {"type": "number", "description": "Minimum price"}, "price_max": {"type": "number", "description": "Maximum price"}, "bedrooms_min": {"type": "integer", "description": "Minimum bedrooms"}, "bedrooms_max": {"type": "integer", "description": "Maximum bedrooms"}, "bathrooms_min": {"type": "integer", "description": "Minimum bathrooms"}, "property_type": {"type": "string", "description": "Property type (House, Apartment, Townhouse, Unit, Land)"}, "adjacent_suburbs": {"type": "boolean", "description": "Include adjacent suburbs"}, "page": {"type": "integer", "description": "Page number"}, "rows": {"type": "integer", "description": "Results per page (max 500)"}, "sort_order": {"type": "string", "description": "Sort order"}, }, }, ), Tool( name="search_property_rental", description="Search for rental properties (houses and apartments to rent)", inputSchema={ "type": "object", "properties": { "search_string": {"type": "string", "description": "Search keywords"}, "region": {"type": "integer", "description": "Region ID"}, "district": {"type": "integer", "description": "District ID"}, "suburb": {"type": "integer", "description": "Suburb ID"}, "price_min": {"type": "number", "description": "Minimum weekly rent"}, "price_max": {"type": "number", "description": "Maximum weekly rent"}, "bedrooms_min": {"type": "integer", "description": "Minimum bedrooms"}, "bedrooms_max": {"type": "integer", "description": "Maximum bedrooms"}, "property_type": {"type": "string", "description": "Property type"}, "pets_ok": {"type": "boolean", "description": "Allow pets"}, "smokers_ok": {"type": "boolean", "description": "Allow smokers"}, "date_available": {"type": "string", "description": "Date available from (YYYY-MM-DD)"}, "adjacent_suburbs": {"type": "boolean", "description": "Include adjacent suburbs"}, "page": {"type": "integer", "description": "Page number"}, "rows": {"type": "integer", "description": "Results per page (max 500)"}, }, }, ), Tool( name="search_property_commercial", description="Search for commercial real estate (offices, retail, industrial)", inputSchema={ "type": "object", "properties": { "search_string": {"type": "string", "description": "Search keywords"}, "region": {"type": "integer", "description": "Region ID"}, "district": {"type": "integer", "description": "District ID"}, "suburb": {"type": "integer", "description": "Suburb ID"}, "price_min": {"type": "number", "description": "Minimum price"}, "price_max": {"type": "number", "description": "Maximum price"}, "property_type": {"type": "string", "description": "Commercial property type"}, "area_min": {"type": "number", "description": "Minimum floor area (sqm)"}, "area_max": {"type": "number", "description": "Maximum floor area (sqm)"}, "page": {"type": "integer", "description": "Page number"}, "rows": {"type": "integer", "description": "Results per page (max 500)"}, }, }, ), Tool( name="search_motors", description="Search for motor vehicles (cars, motorcycles, boats)", inputSchema={ "type": "object", "properties": { "search_string": {"type": "string", "description": "Search keywords (make/model)"}, "category": {"type": "string", "description": "Motors category"}, "price_min": {"type": "number", "description": "Minimum price"}, "price_max": {"type": "number", "description": "Maximum price"}, "year_min": {"type": "integer", "description": "Minimum year"}, "year_max": {"type": "integer", "description": "Maximum year"}, "odometer_min": {"type": "integer", "description": "Minimum odometer (km)"}, "odometer_max": {"type": "integer", "description": "Maximum odometer (km)"}, "make": {"type": "string", "description": "Vehicle make"}, "model": {"type": "string", "description": "Vehicle model"}, "body_style": {"type": "string", "description": "Body style"}, "transmission": {"type": "string", "description": "Transmission (Automatic, Manual)"}, "fuel_type": {"type": "string", "description": "Fuel type (Petrol, Diesel, Electric, Hybrid)"}, "engine_size_min": {"type": "integer", "description": "Minimum engine size (cc)"}, "engine_size_max": {"type": "integer", "description": "Maximum engine size (cc)"}, "region": {"type": "integer", "description": "Region ID"}, "page": {"type": "integer", "description": "Page number"}, "rows": {"type": "integer", "description": "Results per page (max 500)"}, }, }, ), Tool( name="search_jobs", description="Search for job listings and employment opportunities", inputSchema={ "type": "object", "properties": { "search_string": {"type": "string", "description": "Search keywords (job title, company)"}, "category": {"type": "string", "description": "Jobs category number"}, "region": {"type": "integer", "description": "Region ID"}, "district": {"type": "integer", "description": "District ID"}, "type": {"type": "string", "description": "Job type (FullTime, PartTime, Contract, Casual, Temporary)"}, "pay_min": {"type": "number", "description": "Minimum salary/pay"}, "pay_max": {"type": "number", "description": "Maximum salary/pay"}, "pay_type": {"type": "string", "description": "Pay period (Annual, Hourly, Daily)"}, "work_type": {"type": "string", "description": "Work arrangement (Office, Remote, Hybrid)"}, "page": {"type": "integer", "description": "Page number"}, "rows": {"type": "integer", "description": "Results per page (max 500)"}, }, }, ), Tool( name="search_services", description="Search for professional services and tradespeople", inputSchema={ "type": "object", "properties": { "search_string": {"type": "string", "description": "Search keywords"}, "category": {"type": "string", "description": "Services category number"}, "region": {"type": "integer", "description": "Region ID"}, "district": {"type": "integer", "description": "District ID"}, "suburb": {"type": "integer", "description": "Suburb ID"}, "page": {"type": "integer", "description": "Page number"}, "rows": {"type": "integer", "description": "Results per page (max 500)"}, }, }, ), # Listing retrieval tools Tool( name="get_listing_details", description="Get comprehensive details about a specific listing including title, description, price, photos, and seller info", inputSchema={ "type": "object", "properties": { "listing_id": {"type": "integer", "description": "The Trade Me listing ID"}, "return_metadata": {"type": "boolean", "description": "Include additional metadata"}, }, "required": ["listing_id"], }, ), Tool( name="get_listing_photos", description="Get all photos for a specific listing in various sizes", inputSchema={ "type": "object", "properties": { "listing_id": {"type": "integer", "description": "The Trade Me listing ID"}, }, "required": ["listing_id"], }, ), Tool( name="get_listing_questions", description="Get questions and answers (Q&A) for a specific listing", inputSchema={ "type": "object", "properties": { "listing_id": {"type": "integer", "description": "The Trade Me listing ID"}, "page": {"type": "integer", "description": "Page number for pagination"}, "rows": {"type": "integer", "description": "Number of results per page"}, }, "required": ["listing_id"], }, ), Tool( name="get_bidding_history", description="Get the bidding history for an auction listing with bid amounts and timestamps", inputSchema={ "type": "object", "properties": { "listing_id": {"type": "integer", "description": "The Trade Me listing ID"}, }, "required": ["listing_id"], }, ), Tool( name="get_shipping_options", description="Get available shipping methods and costs for a listing", inputSchema={ "type": "object", "properties": { "listing_id": {"type": "integer", "description": "The Trade Me listing ID"}, }, "required": ["listing_id"], }, ), Tool( name="get_similar_listings", description="Find listings similar to a specified listing based on category and attributes", inputSchema={ "type": "object", "properties": { "listing_id": {"type": "integer", "description": "The Trade Me listing ID"}, "rows": {"type": "integer", "description": "Number of results to return"}, }, "required": ["listing_id"], }, ), Tool( name="get_watchlist", description="Get the authenticated user's watchlist (requires authentication)", inputSchema={ "type": "object", "properties": { "category": {"type": "string", "description": "Filter by category number"}, "page": {"type": "integer", "description": "Page number"}, "rows": {"type": "integer", "description": "Results per page (max 500)"}, }, }, ), Tool( name="add_to_watchlist", description="Add a listing to the authenticated user's watchlist (requires authentication)", inputSchema={ "type": "object", "properties": { "listing_id": {"type": "integer", "description": "The Trade Me listing ID to watch"}, "email_option": {"type": "integer", "description": "Email notification preference (0=None, 1=Daily, 2=Immediate)"}, }, "required": ["listing_id"], }, ), Tool( name="remove_from_watchlist", description="Remove a listing from the authenticated user's watchlist (requires authentication)", inputSchema={ "type": "object", "properties": { "listing_id": {"type": "integer", "description": "The Trade Me listing ID to unwatch"}, }, "required": ["listing_id"], }, ), # Bidding and buying tools Tool( name="place_bid", description="Place a bid on an auction listing (requires authentication)", inputSchema={ "type": "object", "properties": { "listing_id": {"type": "integer", "description": "The Trade Me listing ID to bid on"}, "amount": {"type": "number", "description": "The bid amount in NZD"}, "shipping_option": {"type": "integer", "description": "Shipping option ID (optional)"}, }, "required": ["listing_id", "amount"], }, ), Tool( name="buy_now", description="Purchase a listing using Buy Now (requires authentication)", inputSchema={ "type": "object", "properties": { "listing_id": {"type": "integer", "description": "The Trade Me listing ID to purchase"}, "quantity": {"type": "integer", "description": "Number of items to purchase (default: 1)"}, "shipping_option": {"type": "integer", "description": "Shipping option ID"}, "accept_terms": {"type": "boolean", "description": "Accept seller's terms and conditions"}, }, "required": ["listing_id"], }, ), Tool( name="make_offer", description="Make a fixed price offer on a listing (requires authentication)", inputSchema={ "type": "object", "properties": { "listing_id": {"type": "integer", "description": "The Trade Me listing ID"}, "price": {"type": "number", "description": "The offer amount in NZD"}, "message": {"type": "string", "description": "Optional message to seller"}, }, "required": ["listing_id", "price"], }, ), Tool( name="withdraw_offer", description="Withdraw a previously made fixed price offer (requires authentication)", inputSchema={ "type": "object", "properties": { "offer_id": {"type": "integer", "description": "The offer ID to withdraw"}, }, "required": ["offer_id"], }, ), Tool( name="accept_offer", description="Accept an offer made on your listing - seller action (requires authentication)", inputSchema={ "type": "object", "properties": { "offer_id": {"type": "integer", "description": "The offer ID to accept"}, }, "required": ["offer_id"], }, ), Tool( name="decline_offer", description="Decline an offer made on your listing - seller action (requires authentication)", inputSchema={ "type": "object", "properties": { "offer_id": {"type": "integer", "description": "The offer ID to decline"}, }, "required": ["offer_id"], }, ), Tool( name="get_purchase_history", description="Get the authenticated user's purchase history (requires authentication)", inputSchema={ "type": "object", "properties": { "page": {"type": "integer", "description": "Page number for pagination"}, "rows": {"type": "integer", "description": "Results per page (max 500)"}, "filter": {"type": "string", "description": "Filter by status (All, EmailSent, PaymentReceived, GoodsShipped)"}, }, }, ), Tool( name="get_won_items", description="Get auctions the authenticated user has won (requires authentication)", inputSchema={ "type": "object", "properties": { "page": {"type": "integer", "description": "Page number for pagination"}, "rows": {"type": "integer", "description": "Results per page (max 500)"}, }, }, ), Tool( name="get_lost_items", description="Get auctions the authenticated user bid on but lost (requires authentication)", inputSchema={ "type": "object", "properties": { "page": {"type": "integer", "description": "Page number for pagination"}, "rows": {"type": "integer", "description": "Results per page (max 500)"}, }, }, ), Tool( name="get_bidding_on", description="Get items the authenticated user is currently bidding on (requires authentication)", inputSchema={ "type": "object", "properties": { "page": {"type": "integer", "description": "Page number for pagination"}, "rows": {"type": "integer", "description": "Results per page (max 500)"}, }, }, ), ] @app.call_tool() async def call_tool(name: str, arguments: Any) -> list[TextContent]: """Handle tool execution requests.""" client = get_client() try: # Route to appropriate tool function # Catalogue tools if name == "get_categories": result = await catalogue.get_categories(client, **arguments) elif name == "get_category_details": result = await catalogue.get_category_details(client, **arguments) elif name == "get_localities": result = await catalogue.get_localities(client, **arguments) elif name == "get_locality_details": result = await catalogue.get_locality_details(client, **arguments) elif name == "get_districts": result = await catalogue.get_districts(client, **arguments) elif name == "get_attributes": result = await catalogue.get_attributes(client, **arguments) elif name == "get_legal_notice": result = await catalogue.get_legal_notice(client, **arguments) # Search tools elif name == "search_general": result = await search.search_general(client, **arguments) elif name == "search_property_residential": result = await search.search_property_residential(client, **arguments) elif name == "search_property_rental": result = await search.search_property_rental(client, **arguments) elif name == "search_property_commercial": result = await search.search_property_commercial(client, **arguments) elif name == "search_motors": result = await search.search_motors(client, **arguments) elif name == "search_jobs": result = await search.search_jobs(client, **arguments) elif name == "search_services": result = await search.search_services(client, **arguments) # Listing retrieval tools elif name == "get_listing_details": result = await listings.get_listing_details(client, **arguments) elif name == "get_listing_photos": result = await listings.get_listing_photos(client, **arguments) elif name == "get_listing_questions": result = await listings.get_listing_questions(client, **arguments) elif name == "get_bidding_history": result = await listings.get_bidding_history(client, **arguments) elif name == "get_shipping_options": result = await listings.get_shipping_options(client, **arguments) elif name == "get_similar_listings": result = await listings.get_similar_listings(client, **arguments) elif name == "get_watchlist": result = await listings.get_watchlist(client, **arguments) elif name == "add_to_watchlist": result = await listings.add_to_watchlist(client, **arguments) elif name == "remove_from_watchlist": result = await listings.remove_from_watchlist(client, **arguments) # Bidding and buying tools elif name == "place_bid": result = await bidding.place_bid(client, **arguments) elif name == "buy_now": result = await bidding.buy_now(client, **arguments) elif name == "make_offer": result = await bidding.make_offer(client, **arguments) elif name == "withdraw_offer": result = await bidding.withdraw_offer(client, **arguments) elif name == "accept_offer": result = await bidding.accept_offer(client, **arguments) elif name == "decline_offer": result = await bidding.decline_offer(client, **arguments) elif name == "get_purchase_history": result = await bidding.get_purchase_history(client, **arguments) elif name == "get_won_items": result = await bidding.get_won_items(client, **arguments) elif name == "get_lost_items": result = await bidding.get_lost_items(client, **arguments) elif name == "get_bidding_on": result = await bidding.get_bidding_on(client, **arguments) else: raise ValueError(f"Unknown tool: {name}") return [TextContent(type="text", text=str(result))] except Exception as e: logger.error(f"Error executing tool {name}: {str(e)}") return [TextContent(type="text", text=f"Error: {str(e)}")] async def main() -> None: """Run the MCP server.""" logger.info("Starting Trade Me MCP Server") try: async with stdio_server() as (read_stream, write_stream): await app.run( read_stream, write_stream, app.create_initialization_options(), ) finally: # Cleanup if trademe_client is not None: trademe_client.close() logger.info("Trade Me MCP Server stopped") if __name__ == "__main__": asyncio.run(main())

Latest Blog Posts

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/giussepe198/trademe-mcp'

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