MCP Server

pifini

net.pifini/pifini

What this MCP does

Searches events and places across the Caribbean and Latin America, provides listings and reviews, and creates hosted ticket payment links.

create_ticket_checkout
Create ticket checkout link
Create a secure Stripe-hosted payment link for event tickets. Returns a checkout URL — give it to the human to complete payment themselves; each ticket is then emailed with its QR code and its own link, which opens on any phone and can be saved to WhatsApp. Get ticket_type_id values from get_event. Always confirm the exact tickets and total with the human before calling this.
Input schema
{'type': 'object', 'required': ['event_slug', 'items', 'buyer_email'], 'properties': {'items': {'type': 'array', 'items': {'type': 'object', 'required': ['ticket_type_id', 'quantity'], 'properties': {'quantity': {'type': 'number', 'description': '1-20'}, 'ticket_type_id': {'type': 'string', 'description': 'Ticket type id from get_event'}}}, 'description': 'Ticket selections'}, 'buyer_name': {'type': 'string', 'description': "Buyer's name (optional)"}, 'event_slug': {'type': 'string', 'description': 'Event slug'}, 'buyer_email': {'type': 'string', 'description': 'Email to deliver tickets to (required — ask the human)'}}}
Output schema
{'type': 'object', 'properties': {'event': {'type': 'string'}, 'total': {'type': 'number'}, 'currency': {'type': 'string'}, 'order_id': {'type': 'string'}, 'checkout_url': {'type': 'string'}, 'instructions': {'type': 'string'}}}
get_event
Get event details
Full details for one event by slug: description, venue, date/time, and available ticket types with prices and remaining stock. Use the slug from search_events.
Read only Idempotent
Input schema
{'type': 'object', 'required': ['slug'], 'properties': {'slug': {'type': 'string', 'description': 'Event slug from search_events'}}}
Output schema
{'type': 'object', 'properties': {'url': {'type': 'string'}, 'city': {'type': ['string', 'null']}, 'slug': {'type': 'string'}, 'price': {'type': ['object', 'null']}, 'title': {'type': 'string'}, 'country': {'type': ['string', 'null']}, 'ends_at': {'type': ['string', 'null']}, 'summary': {'type': ['string', 'null']}, 'website': {'type': ['string', 'null']}, 'category': {'type': ['string', 'null']}, 'currency': {'type': 'string'}, 'timezone': {'type': ['string', 'null'], 'description': 'IANA timezone of the venue, e.g. America/Mexico_City. Null when unknown.'}, 'starts_at': {'type': 'string', 'description': 'Exact start instant, ISO-8601 UTC. Do NOT format this for a human — it is the wrong calendar day in most of the region. Use local_date / local_time.'}, 'local_date': {'type': 'string', 'description': 'The date the event actually happens where it happens, YYYY-MM-DD. Quote this one.'}, 'local_time': {'type': ['string', 'null'], 'description': 'Local start time, HH:MM 24h. Null when start_time_known is false — say nothing about the time in that case.'}, 'description': {'type': ['string', 'null']}, 'entry_model': {'type': 'string'}, 'ticket_types': {'type': 'array', 'items': {'type': 'object', 'required': ['ticket_type_id', 'name', 'price', 'currency'], 'properties': {'name': {'type': 'string'}, 'price': {'type': 'number'}, 'currency': {'type': 'string'}, 'remaining': {'type': ['number', 'null']}, 'description': {'type': ['string', 'null']}, 'sales_end_at': {'type': ['string', 'null']}, 'ticket_type_id': {'type': 'string'}}}}, 'location_name': {'type': ['string', 'null']}, 'organizer_url': {'type': ['string', 'null']}, 'attendee_count': {'type': 'number'}, 'organizer_name': {'type': ['string', 'null']}, 'cover_image_url': {'type': ['string', 'null']}, 'start_time_known': {'type': 'boolean', 'description': 'False when only the date is known and the stored hour is a placeholder. Never present a start time when this is false.'}}}
get_place
Get place details + reviews
Full details for one place by slug, including its most recent published reviews if any exist. listing_type distinguishes a curated entry from an unreviewed directory listing (see search_places) — recent_reviews is often an empty array, which means no reviews yet, not a low-quality place. Use the slug from search_places.
Read only Idempotent
Input schema
{'type': 'object', 'required': ['slug'], 'properties': {'slug': {'type': 'string', 'description': 'Place slug from search_places'}}}
Output schema
{'type': 'object', 'properties': {'url': {'type': 'string'}, 'city': {'type': ['string', 'null']}, 'name': {'type': 'string'}, 'slug': {'type': 'string'}, 'type': {'type': 'string'}, 'address': {'type': ['string', 'null']}, 'country': {'type': ['string', 'null']}, 'summary': {'type': ['string', 'null']}, 'website': {'type': ['string', 'null']}, 'category': {'type': ['string', 'null']}, 'avg_rating': {'type': ['number', 'null']}, 'price_tier': {'type': ['string', 'null']}, 'description': {'type': ['string', 'null']}, 'is_verified': {'type': 'boolean'}, 'is_sponsored': {'type': 'boolean'}, 'listing_type': {'enum': ['curated', 'directory'], 'type': 'string'}, 'review_count': {'type': 'number'}, 'recent_reviews': {'type': 'array', 'items': {'type': 'object', 'required': ['rating', 'verified_attendee', 'date'], 'properties': {'body': {'type': ['string', 'null']}, 'date': {'type': 'string'}, 'title': {'type': ['string', 'null']}, 'rating': {'type': 'number'}, 'verified_attendee': {'type': 'boolean'}, 'verification_basis': {'enum': ['ticket_scan', 'transaction', 'rsvp', 'geo_checkin', 'manual', 'none'], 'type': 'string', 'description': "How the reviewer's visit was established. ticket_scan and transaction are strong evidence, rsvp and geo_checkin are weaker, manual is self-declared and none is an unverified opinion. Describe the review accordingly."}}}}}}
list_categories
List place categories
Discover valid category values for search_places. Pifini's places directory uses Overture Maps' taxonomy — over 1,000 specific values like caribbean_restaurant, church_cathedral, landmark_and_historical_building — rather than broad buckets, so guessing a category string will usually miss. Call this first with a rough query (e.g. 'coffee' or 'beach') to find the exact value, then pass it to search_places' category parameter.
Read only Idempotent
Input schema
{'type': 'object', 'properties': {'limit': {'type': 'number', 'description': 'Max results, 1-100 (default 30)'}, 'query': {'type': 'string', 'description': "Rough term to match against category values, e.g. 'coffee', 'beach'"}, 'country': {'type': 'string', 'description': 'ISO-3166 alpha-2 country code — only return categories present in that country'}}}
Output schema
{'type': 'object', 'required': ['count', 'categories'], 'properties': {'count': {'type': 'number'}, 'categories': {'type': 'array', 'items': {'type': 'object', 'properties': {'count': {'type': 'number', 'description': 'How many active places have this category'}, 'label': {'type': 'string', 'description': 'Human-readable label'}, 'category': {'type': 'string', 'description': "Exact value to pass to search_places' category parameter"}}}}}}
search_events
Search events
Search upcoming events (festivals, carnivals, concerts, food + cultural events) across the Caribbean, Latin America, and beyond. Returns event names, dates, locations, category, price (when known), and page URLs. Strongest coverage: Barbados, Trinidad, Jamaica, Bahamas, Mexico.
Read only Idempotent
Input schema
{'type': 'object', 'properties': {'city': {'type': 'string', 'description': 'City name filter'}, 'limit': {'type': 'number', 'description': 'Max results, 1-50 (default 10)'}, 'query': {'type': 'string', 'description': 'Free-text search over title and description'}, 'offset': {'type': 'number', 'description': 'Skip this many matching results (for paging past the limit)'}, 'country': {'type': 'string', 'description': 'ISO-3166 alpha-2 country code, e.g. BB, TT, JM'}, 'category': {'enum': ['music', 'arts', 'culture', 'festival', 'sports', 'food', 'business', 'nightlife'], 'type': 'string', 'description': 'Filter by event category'}, 'near_lat': {'type': 'number', 'description': 'Latitude to search near (pair with near_lng)'}, 'near_lng': {'type': 'number', 'description': 'Longitude to search near (pair with near_lat)'}, 'radius_km': {'type': 'number', 'description': 'Search radius in km around near_lat/near_lng (default 25, max 500)'}, 'entry_model': {'enum': ['free', 'ticketed', 'external', 'mixed', 'unknown'], 'type': 'string', 'description': "Filter by how someone gets in: 'free' = confirmed free entry, 'ticketed' = Pifini sells tickets, 'external' = paid but sold elsewhere, 'mixed' = umbrella event with both free and paid components, 'unknown' = not yet confirmed either way."}, 'include_past': {'type': 'boolean', 'description': 'Include past events (default false)'}, 'starts_after': {'type': 'string', 'description': "Earliest start. A bare date ('2026-08-01') matches the event's LOCAL calendar date in its own timezone and is inclusive — this is what you want for 'what's on this weekend'. A full ISO timestamp ('2026-08-01T18:00:00Z') is compared as an exact UTC instant instead."}, 'starts_before': {'type': 'string', 'description': "Latest start, same rules as starts_after. A bare date is inclusive of that whole local day, so starts_after='2026-08-13' with starts_before='2026-08-13' returns everything happening on the 13th where it happens."}}}
Output schema
{'type': 'object', 'required': ['count', 'total', 'has_more', 'events'], 'properties': {'count': {'type': 'number', 'description': 'Number of events in this page of results'}, 'total': {'type': 'number', 'description': 'Total events matching the filters, across all pages. Use this before claiming how many exist.'}, 'events': {'type': 'array', 'items': {'type': 'object', 'properties': {'url': {'type': 'string'}, 'city': {'type': ['string', 'null']}, 'slug': {'type': 'string'}, 'price': {'type': ['object', 'null'], 'properties': {'max': {'type': ['number', 'null']}, 'min': {'type': 'number'}, 'note': {'type': ['string', 'null']}, 'currency': {'type': 'string'}}}, 'title': {'type': 'string'}, 'country': {'type': ['string', 'null']}, 'ends_at': {'type': ['string', 'null']}, 'summary': {'type': ['string', 'null']}, 'category': {'type': ['string', 'null']}, 'timezone': {'type': ['string', 'null'], 'description': 'IANA timezone of the venue, e.g. America/Mexico_City. Null when unknown.'}, 'starts_at': {'type': 'string', 'description': 'Exact start instant, ISO-8601 UTC. Do NOT format this for a human — it is the wrong calendar day in most of the region. Use local_date / local_time.'}, 'local_date': {'type': 'string', 'description': 'The date the event actually happens where it happens, YYYY-MM-DD. Quote this one.'}, 'local_time': {'type': ['string', 'null'], 'description': 'Local start time, HH:MM 24h. Null when start_time_known is false — say nothing about the time in that case.'}, 'distance_km': {'type': ['number', 'null'], 'description': 'Only present when near_lat/near_lng were given'}, 'entry_model': {'type': 'string'}, 'attendee_count': {'type': 'number'}, 'organizer_name': {'type': ['string', 'null']}, 'cover_image_url': {'type': ['string', 'null']}, 'start_time_known': {'type': 'boolean', 'description': 'False when only the date is known and the stored hour is a placeholder. Never present a start time when this is false.'}}}}, 'has_more': {'type': 'boolean', 'description': 'True when more matches exist beyond this page.'}, 'next_offset': {'type': ['number', 'null'], 'description': 'Pass as `offset` to fetch the next page. Null when this is the last page.'}}}
search_places
Search places and businesses
Search Pifini's places directory across the Caribbean and Latin America — restaurants, bars, beaches, attractions, venues, hotels, tour operators, professionals, and services. Every result carries listing_type: 'curated' is a small, editorially reviewed set of flagship entries; 'directory' is the much larger set sourced from open map data (name, category, location, contact info) that has no reviews yet — say so rather than implying a recommendation the data doesn't back up. Category is a specific Overture taxonomy value (e.g. caribbean_restaurant, church_cathedral), not a broad bucket — call list_categories first if unsure of the exact value. Results marked is_sponsored are paid placements — disclose that when recommending.
Read only Idempotent
Input schema
{'type': 'object', 'properties': {'city': {'type': 'string', 'description': 'City name filter'}, 'limit': {'type': 'number', 'description': 'Max results, 1-50 (default 10)'}, 'query': {'type': 'string', 'description': 'Free-text search over name and summary'}, 'offset': {'type': 'number', 'description': 'Skip this many matching results (for paging past the limit)'}, 'country': {'type': 'string', 'description': 'ISO-3166 alpha-2 country code'}, 'category': {'type': 'string', 'description': "Filter by exact category value (e.g. 'caribbean_restaurant'). Use list_categories to discover valid values — there are 1,000+, not a small fixed set."}, 'near_lat': {'type': 'number', 'description': 'Latitude to search near (pair with near_lng)'}, 'near_lng': {'type': 'number', 'description': 'Longitude to search near (pair with near_lat)'}, 'radius_km': {'type': 'number', 'description': 'Search radius in km around near_lat/near_lng (default 25, max 500)'}, 'entity_type': {'enum': ['venue', 'place', 'attraction', 'business', 'professional', 'service', 'city', 'country', 'experience', 'government', 'region', 'transport'], 'type': 'string', 'description': 'Filter by broad type of place'}}}
Output schema
{'type': 'object', 'required': ['count', 'total', 'has_more', 'places'], 'properties': {'count': {'type': 'number', 'description': 'Number of places in this page of results'}, 'total': {'type': 'number', 'description': 'Total places matching the filters, across all pages. Use this before claiming how many exist.'}, 'places': {'type': 'array', 'items': {'type': 'object', 'properties': {'url': {'type': 'string'}, 'city': {'type': ['string', 'null']}, 'name': {'type': 'string'}, 'slug': {'type': 'string'}, 'type': {'type': 'string'}, 'country': {'type': ['string', 'null']}, 'summary': {'type': ['string', 'null']}, 'category': {'type': ['string', 'null']}, 'avg_rating': {'type': ['number', 'null']}, 'price_tier': {'type': ['string', 'null']}, 'distance_km': {'type': ['number', 'null']}, 'is_verified': {'type': 'boolean'}, 'is_sponsored': {'type': 'boolean'}, 'listing_type': {'enum': ['curated', 'directory'], 'type': 'string'}, 'review_count': {'type': 'number'}}}}, 'has_more': {'type': 'boolean', 'description': 'True when more matches exist beyond this page.'}, 'next_offset': {'type': ['number', 'null'], 'description': 'Pass as `offset` to fetch the next page. Null when this is the last page.'}}}
Changed
create_ticket_checkout
Sept. 25, 2026, 2:59 a.m.
Added
create_ticket_checkout
Sept. 17, 2026, 12:54 p.m.
Added
list_categories
Sept. 17, 2026, 12:54 p.m.
Added
get_place
Sept. 17, 2026, 12:54 p.m.
Added
search_places
Sept. 17, 2026, 12:54 p.m.
Added
get_event
Sept. 17, 2026, 12:54 p.m.
Added
search_events
Sept. 17, 2026, 12:54 p.m.