MeetBetr finds the times a group of friends is actually free and turns them into plans they answer in one tap. The MeetBetr connector lets an AI assistant do the same for a person who connects their account.
It's an MCP server with three tools: find free times, propose a plan and check a plan. It is consent-first. An assistant sees only the free windows a person's friends already share with that person, never calendar entries, and nothing is sent until the person confirms.
| What | Value |
|---|---|
| Server URL | https://mcp.meetbetr.com (also answers at /mcp) |
| Transport | Streamable HTTP. POST JSON-RPC; replies are application/json. Stateless: no session ID, no server-sent stream (GET returns 405). |
| Protocol versions | 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05 |
| Authorization | OAuth 2.1, authorization code with PKCE (S256). Every request needs Authorization: Bearer …. |
| Who can connect | Anyone with a free MeetBetr account (iPhone app, phone-number sign-in). |
An unauthenticated request gets 401 with a WWW-Authenticate header pointing to the resource metadata, so MCP clients discover everything on their own.
| Endpoint | URL |
|---|---|
| Protected resource metadata (RFC 9728) | https://mcp.meetbetr.com/.well-known/oauth-protected-resource |
| Authorization server metadata (RFC 8414) | https://mcp.meetbetr.com/.well-known/oauth-authorization-server |
| Authorize | https://mcp.meetbetr.com/authorize |
| Token | https://mcp.meetbetr.com/token |
| Dynamic client registration (RFC 7591) | https://mcp.meetbetr.com/register |
| Revocation (RFC 7009) | https://mcp.meetbetr.com/revoke |
client_id). Public clients use token_endpoint_auth_method: none; client_secret_post and client_secret_basic also work. Partners who prefer a pre-registered client can email us.localhost, 127.0.0.1) or an app's own scheme; matched exactly.state is returned unchanged, with iss (RFC 9207).https://mcp.meetbetr.com./authorize sends them to meetbetr.com/connect. They sign in with their phone number and texted code, then allow each permission with its own switch.| Scope | What the person is told | Tool |
|---|---|---|
availability.read | See when you and your friends are free | find_free_times |
plans.read | See your plans and who's replied | check_plan |
plans.write | Send plans you confirm | propose_plan |
People see and disconnect their connected assistants at meetbetr.com/connect. Access also ends if they delete their MeetBetr account.
Times come back in the person's own time zone as ISO 8601 with offsets, plus a readable when. Friends are named the way they appear in the person's MeetBetr ("Nate", "Lindsay K"), or by a saved crew ("soccer team"). Phone numbers are never accepted or returned.
When the person and some of their friends are all free. It uses only the free windows those friends share with this person. Friends who don't share come back as not_sharing, and assistants must not guess for them. It returns up to 5 windows, spread across different days. If nothing works for everyone, it returns the best windows missing one person, marked missing. With days, it looks for whole free days in a row, up to a year out, for trips and weekends away.
| Argument | Type | Notes |
|---|---|---|
friends | string[] | Up to 20 names or crews. Leave it out to ask who's free: every friend who shares, most people first |
duration_minutes | integer | 15 to 720, default 120 |
days | integer | 1 to 14 whole days in a row, instead of hours |
from, to | YYYY-MM-DD | Default: today through 2 weeks (2 months with days) |
part_of_day | enum | any (7am–11pm), morning, lunch, afternoon, evening |
days_of_week | enum | any, weekdays, weekends |
// "When are Nate and Lindsay free for dinner next week?"
{ "friends": ["Nate", "Lindsay"], "part_of_day": "evening", "from": "2026-09-28", "to": "2026-10-04" }
→ { "time_zone": "America/Los_Angeles",
"windows": [ { "start": "2026-09-29T18:30:00-07:00", "end": "2026-09-29T22:00:00-07:00",
"when": "Tue, Sep 29, 6:30 PM – 10:00 PM", "free": ["You", "Nate", "Lindsay"] }, … ],
"friends": [ { "name": "Nate", "status": "shares_free_times" }, … ], "notes": [] }
Always two calls. The first, with the plan, returns a draft and sends nothing. The assistant shows the person exactly what will go out, and only after they say yes calls again with { "draft_id": "…", "confirm": true }. Invitees on MeetBetr get a notification. The result also carries a link and a ready-to-send text for anyone who isn't on MeetBetr, which the person sends themselves. Sending the same draft twice returns the same plan.
| Argument | Type | Notes |
|---|---|---|
title | string | Up to 80 characters |
times | { start, duration_minutes? }[] | 1 to 5 options. start is ISO 8601; without an offset it's read in the person's zone |
invitees | string[] | MeetBetr friends or crews, up to 20 people |
place, details | string | Optional |
draft_id + confirm | string, boolean | The second call; the draft is good for 30 minutes |
{ "title": "Tacos at Guisados", "times": [{ "start": "2026-10-02T19:00", "duration_minutes": 120 }],
"invitees": ["Nate", "Lindsay"], "place": "Guisados" }
→ { "status": "draft", "draft_id": "…", "summary": "\"Tacos at Guisados\" at Guisados, Fri, Oct 2, 7:00 PM – 9:00 PM. To Nate and Lindsay…",
"warnings": [], "next_step": "Nothing has been sent. …" }
{ "draft_id": "…", "confirm": true }
→ { "status": "sent", "plan_id": "…", "link": "https://meetbetr.com/p/…", "share_text": "…" }
If a proposed time stopped working between the two calls (a calendar changed), the send is refused with conflict and the next best times. A plan for 8 or more people needs an Event Pass or MeetBetr Plus. Without one it returns needs_pass, and the person finishes it in the app. Assistants never buy anything.
How a plan is going: collecting, locked or cancelled, who's in, who can't make it, who hasn't replied (for the plan's host), votes per time, the leading time, and the locked time and place. Pass a plan link, its id, or words from its title. Without plan, it lists the person's current plans.
// "Did everyone reply to taco night?"
{ "plan": "taco night" }
→ { "title": "Taco night", "status": "collecting", "going": ["Marcus", "Tyler"], "cant_make_it": ["Sofia"],
"waiting_on": ["Kevin"], "leading_time": "Sat, Oct 3, 7:00 PM – 9:00 PM", … }
Tool failures come back as a normal result with isError: true, a code, and a plain sentence the assistant can say out loud.
| Code | Meaning |
|---|---|
friend_not_found | No friend by that name, or more than one; the result lists the closest matches |
not_allowed | The person didn't allow this permission when connecting |
needs_pass | A big plan: finish it in the MeetBetr app |
conflict | A proposed time stopped working; nothing was sent |
draft_not_found, draft_expired | Make the draft again |
plan_not_found | No plan the person hosts or was invited to matches |
rate_limited | Too many calls or plans; try later |
bad_request | An argument couldn't be read; the message says which |
An expired or revoked access token gets HTTP 401; refresh it, or connect again.
propose_plan needs a confirmed second call.See the full privacy policy and terms.
Questions, a pre-registered client, or a test account for review: support@meetbetr.com.