{
  "$schema": "https://modelcontextprotocol.io/schema/2025/well-known/mcp.json",
  "name": "SpotMe",
  "description": "Mexico's marketplace for storage and parking spaces. Read-only discovery tools for finding mini-bodegas, estacionamiento, bodegas comerciales, and naves industriales across 30+ Mexican cities. An optional admin tier (Firebase-bound OAuth) exposes incident-debugging tools for SpotMe staff.",
  "publisher": {
    "name": "Northern Land Domain S.A.P.I. de C.V.",
    "url": "https://www.spotme.mx",
    "country": "MX"
  },
  "endpoints": [
    {
      "url": "https://server.spotme.mx/mcp",
      "transport": "streamable-http",
      "protocolVersion": "2025-06-18"
    }
  ],
  "auth": {
    "type": "oauth2",
    "description": "OAuth 2.1 with PKCE (S256). Two scopes: `mcp:read` for the public discovery surface (auto-approve, no consent screen) and `mcp:admin` for SpotMe-staff-only debugging tools (Firebase Google sign-in, isAnyAdmin verified). Anonymous bearer access is also accepted as a fallback for crawlers and curl tests on the read scope, with stricter per-IP limits.",
    "authorization_servers": ["https://server.spotme.mx"],
    "metadata_endpoint": "https://server.spotme.mx/.well-known/oauth-authorization-server",
    "protected_resource_endpoint": "https://server.spotme.mx/.well-known/oauth-protected-resource/mcp",
    "scopes_supported": ["mcp:read", "mcp:admin"],
    "dynamic_client_registration": "https://server.spotme.mx/oauth/register",
    "anonymousLimits": {
      "requestsPerMinutePerIp": 20,
      "listingsPerCall": 3,
      "listingsPerDayPerIp": 30
    },
    "keyedLimits": {
      "requestsPerMinutePerVendor": 60,
      "listingsPerCall": 5,
      "listingsPerDayPerVendor": 200
    },
    "adminTier": {
      "scope": "mcp:admin",
      "trigger": "Append `?admin=1` to the MCP URL when adding the connector (e.g. `https://server.spotme.mx/mcp?admin=1`). The OAuth flow then renders a Firebase Google sign-in page; only Firebase UIDs with `isAnyAdmin === true` get an admin-bound token.",
      "tokenBinding": "Tokens carry the verified `firebaseUid`. Token storage is DynamoDB-backed (oauthtoken table) so tokens survive deploys.",
      "perAdminDailyCaps": "Distinct IDs per resource type (50 users, 30 teams, 100 checkouts, 50 locations, 100 configurations, 50 closed deals, 30 chats, 100 subscriptions, 100 hubspot-link lookups, 200 resolve-ids, 500 events). Hard ceiling of 500 total tool calls/day. 30 req/min sliding window.",
      "audit": "Every admin tool call writes an `mcp.admin.audit` log entry (firebaseUid, email, tool, entityId, args, found, durationMs, ip).",
      "killSwitch": "`MCP_ADMIN_DISABLED=true` → all admin tools 503 immediately. `MCP_ADMIN_REVOKED_UIDS=<csv>` → block specific admin UIDs from new sign-ins."
    }
  },
  "tools": {
    "public": [
      {
        "name": "search_listings",
        "description": "Find rentable spaces in a Mexican city by space type. Returns up to 5 listings (3 anonymous) with short URLs to spotme.mx.",
        "inputs": {
          "city": "string (required) — city slug or name",
          "spaceType": "self-storage | parking | warehouse | industrial-warehouse (optional)",
          "maxPriceMxn": "number (optional)",
          "minSizeM2": "number (optional)"
        }
      },
      {
        "name": "get_city_overview",
        "description": "Summary of SpotMe inventory in a Mexican city: counts by space type, top neighborhoods, and price ranges.",
        "inputs": {
          "city": "string (required)"
        }
      },
      {
        "name": "get_pricing_guide",
        "description": "Estimated monthly price range (MXN) for a space type in a Mexican city. Pure benchmark — does not return individual listings.",
        "inputs": {
          "city": "string (required)",
          "spaceType": "self-storage | parking | warehouse | industrial-warehouse (required)",
          "sizeM2": "number (optional)"
        }
      },
      {
        "name": "get_listing_summary",
        "description": "Look up a single SpotMe listing by short ID (the segment after spotme.mx/c/).",
        "inputs": {
          "nanoId": "string (required)"
        }
      }
    ],
    "admin": [
      {
        "name": "admin_resolve_id",
        "description": "Identify what kind of SpotMe entity an opaque string is (Firebase UID, email, UUID, Stripe ID, listing nanoId, HubSpot ID). Returns suggested next admin_get_* tool. Cheap router — use this first when you have a raw string.",
        "scope": "mcp:admin"
      },
      {
        "name": "admin_get_user",
        "description": "Internal user record for a Firebase UID — full record (admin sees unredacted via hideConfidentialUserInfo), recent listing/reservation/rent IDs, fiscal context (RFC, regimenFiscal, default fiscal type, retention pct), hubspotId. Stripe IDs hidden by default.",
        "scope": "mcp:admin"
      },
      {
        "name": "admin_get_team",
        "description": "Internal team record (corporate / broker host group) — members, listings, reservations, fiscal info, Stripe Connect account.",
        "scope": "mcp:admin"
      },
      {
        "name": "admin_get_location",
        "description": "Internal location (listing) record — address, configurations, host, team, attribution, hubspot/zoho IDs, draft/deactivated/deleted flags.",
        "scope": "mcp:admin"
      },
      {
        "name": "admin_get_configuration",
        "description": "Internal configuration (rentable unit) record by UUID or short nanoId — resolves the parent location and returns the raw config (admin shape).",
        "scope": "mcp:admin"
      },
      {
        "name": "admin_get_checkout",
        "description": "Internal checkout (rental) record — full status, dates, pricing, commission override, Stripe IDs, grace period / scheduled cancellation, attribution. Returns IDs for landlord/renter/teams/location/configuration/chat to chain into.",
        "scope": "mcp:admin"
      },
      {
        "name": "admin_get_closed_deal",
        "description": "Internal closed deal record — off-platform broker / traditional rental, includes renter contact details and matched configuration.",
        "scope": "mcp:admin"
      },
      {
        "name": "admin_get_subscription",
        "description": "Stripe subscription + recent invoices, cross-referenced with the SpotMe checkout that owns it. Provide either stripeSubscriptionId or checkoutId.",
        "scope": "mcp:admin"
      },
      {
        "name": "admin_get_events",
        "description": "Query analytics events by userId or sessionId, optionally filtered by EventAction. Use to debug attribution, funnel drop-off, or what a session actually did.",
        "scope": "mcp:admin"
      },
      {
        "name": "admin_get_chat",
        "description": "Pull a LangGraph WhatsApp / in-app chat thread for chatbot debugging. Returns participant IDs, recent messages with text content (older than 90 days = metadata only).",
        "scope": "mcp:admin"
      },
      {
        "name": "admin_get_hubspot_links",
        "description": "Translate a SpotMe userId to its HubSpot contact + owner mapping. Returns hubspotId + portal URL so you can hop into the HubSpot MCP for the actual deal/contact record.",
        "scope": "mcp:admin"
      }
    ]
  },
  "policies": {
    "readOnly": true,
    "noPagination": true,
    "redactsExactAddresses": true,
    "redactsHostContacts": true,
    "fundsAttribution": "Every public-tool result includes a spotme.mx URL — the LLM should surface it so users complete their rental on the platform.",
    "adminPiiHandling": "Admin tier returns full PII to authorized SpotMe staff for incident debugging. The MCP system prompt instructs the LLM to never expose raw renter/host PII (RFC, full names, phone, address) when relaying admin-tool output to a chat user — summarize or redact."
  },
  "languages": ["es", "en"],
  "contact": {
    "email": "contacto@spotme.mx",
    "issues": "https://www.spotme.mx/contacto"
  },
  "lastUpdated": "2026-04-30",
  "version": "0.3.0"
}
