heldly
Heldly is an AI-native scheduling agent for Claude.
От сообщества: Добавлен пользователем или импортирован; проверьте владельца перед подключениемРаботаетБез входаГлобальныйБесплатноМожет изменять данные
Что умеет
- Find Times: Preview candidate meeting slots (no holds placed). Returns 3 slots labeled `1`, `2`, `3` chronologically, each with `previous_event` / `next_event` — the host's nearest events on either si
- Propose Meeting: Propose a meeting to an external invitee (anyone outside the host's workspace). Heldly places tentative holds on every host's Google Calendar synchronously and persists the meeting. W
- List Meetings: List meetings you've proposed or are co-hosting, most recent first. Use `query` to find a specific meeting by invitee, host, or topic. Each row includes a `picker_url` the host can past
Какие данные видит
Нужен ли аккаунт
Не нужен: сервер работает без входа
Heldly is an AI-native scheduling agent for Claude. Tell Claude what meeting you want.
Heldly reads everyone's Google Calendar, places 3 holds across all hosts, sends a picker email to the invitee, and books the meeting when they choose.
Built for sales, partnerships, recruiting, and any external meeting that crosses orgs.
Список инструментов сервера (13)
Технические названия из tools/list. Нужны только разработчикам.
| find_times | Preview candidate meeting slots (no holds placed). Returns 3 slots labeled `1`, `2`, `3` chronologically, each with `previous_event` / `next_event` — the host's nearest events on either side, with gap + title. **Chat response — keep it SHORT and lead with the iframe.** The iframe is the primary surface; the host sees the three slots painted on their calendar above your message. Do NOT enumerate the slots in prose (no "Slot 1 — Tue May 26 · 3:00–3:30 PM" bullet list — that's redundant with what they can already see). Instead, write a one- or two-sentence framing that tells them how to proceed. Template: > Three options on your calendar above. Drag any slot to > move it to a different time, or click **Looks good →** > when you're happy and I'll grab a topic + agenda for the > invite. Adapt the wording to the conversation's tone. If a slot's `rationale` flags something worth highlighting ("right after your V2 walkthrough", "30 min gap from your standup"), you MAY mention one — but only when it genuinely helps the host judge placement, never as boilerplate per-slot bullets. **Do NOT ask for `meeting_topic` / `meeting_agenda` yet.** That step happens AFTER the host clicks "Looks good →" in the iframe (which sends a Confirm message — see `propose_meeting` for the format). Asking earlier is premature — the host hasn't decided on the times yet. **Time strings.** Use each slot's `local_day` ("Thu May 21") and `local_time` ("1:00–1:30 PM") — pre-formatted in the host's tz, DST-correct. Do NOT format `starts_at`/`ends_at` yourself; UTC→local conversion routinely gets DST wrong by ±1h. `rationale` opens with the same formatted day+time; `proposed_slot.{old,new}.display` does the same. Treat `starts_at`/`ends_at` as opaque identifiers for subsequent calls. **Rendering.** On Claude.ai an inline iframe shows the host's calendar context (their events + co-host busy + blackouts) for the visualized week, AND paints Heldly's 3 candidate slots in emerald with numbered badges. In propose mode the slot being replaced renders as a dashed-outline ghost in its old position while the candidate renders as the new proposal. The iframe is interactive: the host can drag any slot to a new cell (or click then click) to move it, validated locally against calendar events / co-host busy / kept-slot overlap / past days. Drops on lunch / off-hours rows ARE allowed (the host overriding their own preference). Intermediate moves stay local; the host's "Looks good →" click is the ONLY action that sends a chat message to Claude (see `propose_meeting` for the format). **Three call modes.** 1. **Fresh** — no `current_slots`, no `propose_label`. Server picks 3 slots. 2. **Propose change** — `current_slots` (with exact starts_at/ends_at from the previous call) + `propose_label` + narrowed `window_*`. Server keeps 2 committed, picks 1 new candidate, returns `proposed_slot: { label, old, new }`. Tell the host the diff using the `display` strings and WAIT for verbal yes — do NOT commit unilaterally. **Optional `target_starts_at`** when the host named a specific time in chat ("swap slot 2 for Wed 3 PM"): server uses that exact time as the new candidate (validated). On failure, `proposed_slot.new` is null and `target_failure_reason` explains why — surface the reason in chat and offer an algorithmic alternative. 3. **Commit** — `current_slots` only (kept + new), no `propose_label`. Server echoes; `proposed_slot` is null. **After the Confirm message arrives.** When the iframe sends: > These are the times I'd like: > Slot 1: Tue 3:00–3:30 PM > Slot 2: Wed 2:00–2:30 PM > Slot 3: Wed 4:00–4:30 PM …the host has locked in their slot choices. The visible message is intentionally natural-language only; the exact ISO `starts_at` / `ends_at` for each of the three final slots is delivered to you separately via `updateModelContext` (a model-only side channel — the host doesn't see it). Look for an `[heldly find_times iframe — final slot picks ...]` block in your context and use those ISO pairs verbatim when calling `propose_meeting`. If for some reason the context block isn't present (older host without the model-context capability), the ISOs are appended to the visible message instead. Your job at this point: ask for `meeting_topic` AND `meeting_agenda` if either is missing from prior chat. Draft both from context when you can and surface for confirmation rather than making the host type from scratch: > Got it — three times locked. Quick draft for the invite: > • **Topic:** Discovery call > • **Agenda:** We'll cover your team's current scheduling > pain points and how Heldly fits in. > > Sound right, or anything to tweak? Names stay OUT of the topic ("Discovery call", not "Discovery call with Sara"). Once topic + agenda are confirmed, call `propose_meeting` with `auto_send: true` and the three ISO {starts_at, ends_at} pairs from the hidden context block — that single call places holds AND fires the picker email. **Invitee timezone.** Default: assume invitee is in the host's tz; don't pass `invitee_timezone` on the first call. If the host volunteers a location, translate to IANA and re-call with `invitee_timezone` set. The response includes `signed_in_as` — the Heldly account email the MCP connector is authenticated as, and the Google Calendar Heldly will touch. Independent of which Claude account the host is signed into. For multi-account users (separate work + personal Google identities), name the calendar proactively when relaying results: "You're signed into Heldly as <email>; the meeting lands on that Google Calendar." |
| propose_meeting | Propose a meeting to an external invitee (anyone outside the host's workspace). Heldly places tentative holds on every host's Google Calendar synchronously and persists the meeting. When the invitee picks, Heldly confirms the chosen slot and deletes the siblings on every host's calendar in one shot — no manual cleanup needed. **BEFORE calling — both `meeting_topic` AND `meeting_agenda` MUST be host-confirmed.** No exceptions. The invitee reads both on the calendar invite they receive after picking; sending without them produces "Untitled" calendar entries with no context, which is worse than the host typing them manually. There is no inline form in the iframe — the chat is the only place they get gathered, so the gate has to be explicit. 1. **`meeting_topic`** (required by schema, max 120 chars). Draft from context if you can ("Discovery call", "Demo follow-up", "Q3 QBR"); otherwise ask. Show it to the host and get explicit confirmation BEFORE calling. Leave both parties' names OUT ("Discovery call", not "Discovery call with Adam"; "Heldly intro", not "Sestria × Adam intro") — the invitee is reading this and seeing their own name in the title reads weird. 2. **`meeting_agenda`** (schema-optional but treat as REQUIRED in chat flow; 2-4 sentences). Draft from context if you can; otherwise ask. Show it to the host and get explicit confirmation BEFORE calling. Write in second person where natural ("We'll cover...") and skip the invitee's name. The only legitimate reason to call without an agenda is an internal handoff between known parties where the topic alone is unambiguous ("30-min sync"); in that case, surface the intent ("calling with just the topic — no agenda needed for an internal sync, right?") and get explicit OK from the host first. 3. Call `find_times` first to preview slots; pass the chosen 3 here. **`auto_send` behavior.** Defaults to false: holds are placed but the picker email is NOT sent — confirm with the host, then call `send_invitee_email`. EXCEPTION: when the host has explicitly confirmed (e.g. clicked "Looks good →" in the iframe), pass `auto_send: true` and Heldly fires the picker email in this same call. Saves a round-trip. **Iframe Confirm flow (the typical path on Claude.ai).** When the host clicks "Looks good →" in the iframe, two things arrive in parallel: 1. **Visible chat message** (natural language only — keeps claude.ai's prompt-injection guard happy): > These are the times I'd like: > Slot 1: Tue 3:00–3:30 PM > Slot 2: Wed 2:00–2:30 PM > Slot 3: Wed 4:00–4:30 PM 2. **Hidden model-context block** (via `updateModelContext` — not visible to the host): > [heldly find_times iframe — final slot picks ...] > Slot 1: starts_at=2026-05-26T13:00:00.000Z ends_at=... > Slot 2: ... > Slot 3: ... > When you call propose_meeting next, pass these > starts_at/ends_at pairs verbatim as the slots array. Use the hidden ISO pairs when calling `propose_meeting`. If the host's environment doesn't support `updateModelContext`, the ISOs are appended to the visible message instead — same ISOs, just less elegant. The host may have dragged or click-moved slots in the iframe before clicking, so trust these ISOs as the final choices regardless of what `find_times` originally returned. NOW (not before) is when you collect `meeting_topic` AND `meeting_agenda` — they were intentionally deferred until the host locked the times. Draft both from prior chat context where you can, show as a single short confirmation block, and wait for the host's OK. After OK: call `propose_meeting` with the three ISO {starts_at, ends_at} pairs as the `slots` array (chronological, labels 1/2/3 follow), `auto_send: true`, and the confirmed topic + agenda. **Shareable mode (no invitee email).** For Slack/LinkedIn DMs, conference badges, etc., omit `invitee_email` entirely. Heldly returns a `picker_url` the host pastes anywhere; the invitee fills in their email on the picker page when they pick. Same calendar surgery, same confirmation flow. Don't call `send_invitee_email` in shareable mode. The response includes `signed_in_as` — the Heldly account email the MCP connector is authenticated as, and the Google Calendar Heldly will touch. Independent of which Claude account the host is signed into. For multi-account users (separate work + personal Google identities), name the calendar proactively when relaying results: "You're signed into Heldly as <email>; the meeting lands on that Google Calendar." If the response includes `onboarding_narration`, relay it verbatim as part of your reply to the host — it's first-person Heldly explaining what just happened on their calendar. The field auto-disappears after the host has booked 5 meetings (or manually dismissed in settings), so when it's absent, just answer normally. Don't paraphrase or merge it with other text — relay as a distinct line or short paragraph. |
| list_meetings | List meetings you've proposed or are co-hosting, most recent first. Use `query` to find a specific meeting by invitee, host, or topic. Each row includes a `picker_url` the host can paste into Slack / LinkedIn / a DM when the invitee says they never got the email — single-use, same token as the original picker. |
| recent_invitees | Return the host's recent invitees deduped by `invitee_email`, most-recent-first, each carrying the previous meeting's `last_meeting_topic` + `last_meeting_agenda` and `meeting_count`. Call this on any "book another with Sara" / "set up a follow-up with the Acme person" intent so you pull her email, name, and prior topic/agenda in ONE round trip instead of asking the host from scratch. Workflow on repeat bookings: call `recent_invitees` with `query` set to whatever the host said ("sara", "acme", "jane@"); pick the right row; surface the previous topic + agenda as suggestions ("Same as last time — 'Discovery call', covering ...?") and let the host accept or revise. Then call `find_times` and the rest of the flow normally. Shareable-link meetings (no `invitee_email`) are skipped — there's no addressable invitee to repeat with. Returns up to 25 distinct invitees. |
| get_meeting | Fetch the full details of a single meeting, including the timeline of events. |
| check_meeting_status | Quick status snapshot of a single meeting — the right tool for "did Sara book?" / "is the Acme meeting confirmed?". Returns `status` + `hint`, the `booked_slot` (with pre-formatted local time) if one was picked, the invitee name, and onboarding narration when relevant. For full meeting detail + the events timeline (proposed → emailed → picked → confirmed), use `get_meeting` instead. Heldly handles all calendar cleanup itself when the invitee picks or the meeting expires, so this tool is purely informational — there's no `holds_to_release` action to take. If the response includes `onboarding_narration`, relay it verbatim as part of your reply to the host — it's first-person Heldly explaining what just happened on their calendar. The field auto-disappears after the host has booked 5 meetings (or manually dismissed in settings), so when it's absent, just answer normally. Don't paraphrase or merge it with other text — relay as a distinct line or short paragraph. |
| send_invitee_email | Send the invitee picker email. This is both the **first-send** tool (after propose_meeting with auto_send=false, which is the default — call this once the host has confirmed the topic, agenda, and slots) AND the **resend / reminder** tool (if the host asks 'nudge Sara again'). Only valid while the meeting is in awaiting_invitee_pick. If the response includes `onboarding_narration`, relay it verbatim as part of your reply to the host — it's first-person Heldly explaining what just happened on their calendar. The field auto-disappears after the host has booked 5 meetings (or manually dismissed in settings), so when it's absent, just answer normally. Don't paraphrase or merge it with other text — relay as a distinct line or short paragraph. |
| cancel_meeting | Cancel a meeting. Heldly releases active picker slots and deletes every tentative or confirmed event we placed on any host's calendar — synchronously, in one call. The invitee gets a cancellation email if the meeting was already booked. Cannot cancel meetings that are already cancelled or expired. If the response includes `onboarding_narration`, relay it verbatim as part of your reply to the host — it's first-person Heldly explaining what just happened on their calendar. The field auto-disappears after the host has booked 5 meetings (or manually dismissed in settings), so when it's absent, just answer normally. Don't paraphrase or merge it with other text — relay as a distinct line or short paragraph. |
| update_meeting_details | Edit a meeting's topic, agenda, or invitee name WITHOUT re-emailing the invitee and WITHOUT changing any times. The right tool for typo fixes, agenda refinements, and correcting an invitee name the host wrote wrong. Allowed on `awaiting_invitee_pick` and `confirmed` meetings. Cancelled / expired meetings are terminal — propose a fresh one instead. What this tool touches: - The picker page (if status is awaiting_invitee_pick): the invitee sees the new topic/agenda next page load. - The Google Calendar event title and description on every active host calendar — patched in place. For awaiting meetings, that's one tentative per slot (3 events); for confirmed meetings, the single booked event. What this tool does NOT touch: - Slot times → use `reschedule_meeting`. - The invitee email → not editable here; a wrong email needs cancel + propose. - The host or co-host list → use `reschedule_meeting` with new co_host_emails (heavy), or add_co_host once we ship it. The invitee is NOT notified by Heldly. If the host wants to tell the invitee about the change, follow up with `send_invitee_email` (awaiting state) or send a separate note yourself. Topic and agenda should keep names OUT for the same reason as `propose_meeting` — both parties read them. If the response includes `onboarding_narration`, relay it verbatim as part of your reply to the host — it's first-person Heldly explaining what just happened on their calendar. The field auto-disappears after the host has booked 5 meetings (or manually dismissed in settings), so when it's absent, just answer normally. Don't paraphrase or merge it with other text — relay as a distinct line or short paragraph. |
| add_co_host | Add a workspace member as a co-host on an existing meeting without going through `reschedule_meeting` (which is heavy: deletes events + re-emails the invitee). The right tool for "actually loop Pat in on the Sara meeting too." Available on every plan. The new co-host must be a member of the host's workspace, have their Google Calendar connected, and have `allow_co_host` enabled (the default). **v1 scope: `awaiting_invitee_pick` meetings only.** Adding to a `confirmed` meeting isn't supported yet — for those, either call `reschedule_meeting` with the same times + the new co_host_emails, or have the host manually add the person as an attendee in their Google Calendar event. What this tool does: - Places a tentative event on the co-host's Google Calendar for every active slot (typically 3). - Persists the meeting_hosts row + slot_holds rows. - Sends the co-host the same "you've been added to ..." email `propose_meeting` sends to initial co-hosts. - Emits a `co_host_added` event on the meeting timeline. What this tool does NOT do: - Re-email the invitee → the picker URL stays the same; their existing tentative invites don't change. - Check the co-host's availability → if Pat is already busy at one of the slot times, the tentative just overlaps on his calendar. Heldly tells him; he can decline or rearrange. - Change times → use `reschedule_meeting` if the new co-host needs different slots. Max 6 hosts per meeting (1 primary + 5 co-hosts). If the co-host's email isn't in the workspace yet, call `invite_workspace_member` first, then retry once they've signed in + connected their calendar. If the response includes `onboarding_narration`, relay it verbatim as part of your reply to the host — it's first-person Heldly explaining what just happened on their calendar. The field auto-disappears after the host has booked 5 meetings (or manually dismissed in settings), so when it's absent, just answer normally. Don't paraphrase or merge it with other text — relay as a distinct line or short paragraph. |
| reschedule_meeting | Reschedule an existing meeting to new slots. Works for both: • `confirmed` meetings — Heldly deletes the booked event on every host's calendar, places fresh tentatives on the new slots, flips status back to awaiting_invitee_pick, and emails the invitee a 'moved to...' picker. • `awaiting_invitee_pick` meetings — Heldly tears down the old tentative holds on every host's calendar and replaces them with fresh holds on the new slots. The original picker URL keeps working. Cannot reschedule cancelled or expired meetings — those are terminal; propose a fresh meeting instead. BEFORE calling: (a) confirm with the host that they actually want to reschedule (this clears the existing calendar event in the confirmed case), (b) call `find_times` to get new candidate slots, (c) show the host the new times AND the reason copy you plan to send. Pass `notify_invitee: false` if the host wants to preview the new state before the email goes out (default is true). If the response includes `onboarding_narration`, relay it verbatim as part of your reply to the host — it's first-person Heldly explaining what just happened on their calendar. The field auto-disappears after the host has booked 5 meetings (or manually dismissed in settings), so when it's absent, just answer normally. Don't paraphrase or merge it with other text — relay as a distinct line or short paragraph. |
| invite_workspace_member | Invite a new member to your Heldly workspace (Business plan, admin only). Use this when the host wants to co-host a meeting with someone who isn't in the workspace yet — find_times and propose_meeting return `co_host_not_in_workspace` for those emails. The invitee receives an email with a Google sign-in link; once they sign in, they automatically join the workspace and you can co-host them. Each new seat is billed at the Team per-seat rate with proration. BEFORE calling, confirm with the host: 1. The email you're about to invite. 2. That adding this seat will increase their Stripe subscription by one Team seat (the response includes `monthly_cost_delta`). AFTER it returns, tell the host the new seat count + the per-month billing impact, and that the invitee needs to (a) sign in via the invite email and (b) connect their calendar before you can co-host them. |
| get_workspace_insights | Workspace-wide insights for managers (Team admins only). Returns the funnel (proposed → opened → picked → confirmed), per-week volume, and time-to-pick percentiles. Use when the host asks any of: • 'how many meetings did the team book last week?' • 'what's our pick rate this quarter?' • 'how fast are invitees picking?' Defaults to a 30-day window. Pass `since_days` for a different range. Pass `metric` to focus the response on one slice (funnel, volume, or time_to_pick). |