{
  "name": "happy-hear-audio",
  "version": "0.1.0",
  "url": "https://www.happyhearaudio.com/api/mcp",
  "transport": "streamable-http",
  "documentation": "https://www.happyhearaudio.com/developers/connector",
  "authorization": {
    "type": "oauth2",
    "authorization_server": "https://xofxhprcaobzjjttngyk.supabase.co/auth/v1",
    "resource_metadata": "https://www.happyhearaudio.com/api/mcp/oauth-protected-resource",
    "dynamic_client_registration": true,
    "pkce": "S256",
    "preregistered_public_client_id": "69891a84-9ac8-4161-9de1-2f3f5ba82485"
  },
  "limits": {
    "requests_per_minute_per_operator": 60,
    "daily_write_caps": {
      "start_cleaning": 30,
      "create_event": 10,
      "update_event": 50
    }
  },
  "capabilities": {
    "tools": {
      "listChanged": false
    }
  },
  "tools": [
    {
      "name": "search_events",
      "title": "Search my events",
      "description": "Find events by name, date range or status. Returns name, date, status, gallery link and message counts. Never returns guest audio, transcripts or phone numbers.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "query": {
            "description": "Part of the event name",
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          },
          "from": {
            "description": "Earliest event date, inclusive",
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
          },
          "to": {
            "description": "Latest event date, inclusive",
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "active",
              "archived"
            ]
          },
          "limit": {
            "default": 25,
            "type": "integer",
            "minimum": 1,
            "maximum": 100
          }
        }
      },
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_event",
      "title": "Get one event",
      "description": "Full status of one event: message counts by state (ready / cleaning / waiting / needs attention), call-in window, gallery link, whether the gallery was sent and opened. Message states only; never audio or text.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "event_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          }
        },
        "required": [
          "event_id"
        ]
      },
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "start_cleaning",
      "title": "Start cleaning an event",
      "description": "Runs \"Clean All\" on an event you own: starts audio cleaning for every message not cleaned yet. Safe to call twice; if a clean is already running it says so instead of starting another. Never re-cleans finished messages.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "event_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          }
        },
        "required": [
          "event_id"
        ]
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "create_event",
      "title": "Create an event",
      "description": "Creates a new event with a name and optional date. Applies the same rules as the dashboard (free first event, subscription, otherwise the event is created locked and needs payment in the dashboard). Returns the event id and dashboard link.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "event_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "event_date": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
          }
        },
        "required": [
          "event_name"
        ]
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "update_event",
      "title": "Update event name or date",
      "description": "Changes only the event name or the event date. Changing the date also moves the call-in window the way the dashboard does. Nothing else about the event can be changed here.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "event_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          },
          "event_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "event_date": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "event_id"
        ]
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_event_audio_status",
      "title": "Check audio status on an event",
      "description": "Answers \"does it have audio ready?\" for one event: message counts by state (ready, cleaning, waiting, needs attention, video) and one line per message, \"Message N: state\". No file names, no audio, no words, no guest details.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "event_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          }
        },
        "required": [
          "event_id"
        ]
      },
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_events_needing_attention",
      "title": "List events that need a look",
      "description": "Answers \"is anything stuck?\": your events with a message in needs-attention (failed, too short, too large or an unsupported file), or a message still cleaning more than 30 minutes after it arrived. An empty list means nothing needs you. A re-clean started on an older message can show as stuck for its first 30 minutes.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "limit": {
            "default": 25,
            "type": "integer",
            "minimum": 1,
            "maximum": 100
          }
        }
      },
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_gallery_link",
      "title": "Get the gallery link",
      "description": "The share link for one event's gallery (your own domain when it is verified, otherwise the Happy Hear Audio one) and whether guests need a share code to open it. Never the code itself.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "event_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          }
        },
        "required": [
          "event_id"
        ]
      },
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_gallery_stats",
      "title": "Get gallery stats",
      "description": "Answers \"how many people listened?\" for one event: visitors, plays and downloads so far, whether the gallery was sent, and the date it was first opened. Counts and dates only, never who.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "event_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          }
        },
        "required": [
          "event_id"
        ]
      },
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_plan_and_usage",
      "title": "Get my plan and usage",
      "description": "Which plan the account is on (per event, monthly or annual), the subscription status, its renewal or paid-through date, whether the free first event has been used, and how many events exist. No card or payment details.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    }
  ]
}