Robin MCP

Robin, inside your AI assistant

Operate your workplace from Claude, ChatGPT, Copilot, Gemini, or any AI assistant that supports MCP.

Add this URL to your assistant, then sign in with Robin.

https://mcp.robinpowered.com/mcp

Setup steps for Claude, ChatGPT, Gemini, Copilot and more.

How it works

Robin MCP is a server that speaks the Model Context Protocol (MCP), the open standard AI assistants use to reach outside tools and data. Point your assistant at it, sign in with your Robin account, and the assistant works with Robin on your behalf. It sees and does only what your own Robin account allows.

Connect your AI assistant

Any assistant that supports remote MCP servers with OAuth sign-in can use Robin. Each guide shows where to add the server URL and how to sign in.

Claude

Team or Enterprise plan? An Owner needs to add Robin under Organization settings → Connectors first. After that, each member connects with their own Robin account.

  1. Open Robin in the Claude directory and connect it
  2. Claude sends you to Robin to sign in
  3. To use it, open the + menu in a chat, choose Connectors, and switch Robin on
ChatGPT

Custom connectors need developer mode, which ChatGPT offers on its Plus, Pro, Business, Enterprise, and Education plans.

  1. Switch on developer mode: Settings → Security and login → Developer mode
  2. Open ChatGPT Plugins and press + to create a connector
  3. Give it a name and a short description, set Connection to URL, and paste:
https://mcp.robinpowered.com/mcp
  1. Save it. ChatGPT lists the Robin tools it found
  2. Sign in with your Robin account when asked
  3. In a new chat, pick Robin from the tools menu, or just mention Robin in your message
  4. ChatGPT checks with you before anything that changes data in Robin. Approve the actions you want
Gemini

Robin works with two Google products. Use the steps for the one you have.

Antigravity CLI (agy)

Antigravity CLI replaces Gemini CLI, and reads MCP servers from its own config file rather than Gemini CLI’s settings.

  1. You need Antigravity CLI installed
  2. Add Robin to ~/.gemini/config/mcp_config.json, or to a project’s .agents/mcp_config.json. If the file already lists servers, add the robin entry to its mcpServers:
{ "mcpServers": { "robin": { "serverUrl": "https://mcp.robinpowered.com/mcp" } } }
  1. Start agy and run /mcp to open the MCP manager. Robin should be listed
  2. Sign in to Robin when Antigravity asks

Gemini Enterprise

For Google Cloud admins adding Robin as a data store in Gemini Enterprise. The Gemini app itself only takes custom MCP servers on personal Google accounts, not work accounts, so use Gemini Enterprise for your organization.

  1. In the Gemini Enterprise console, open Data stores, create one, and pick Custom MCP Server as its source
  2. Set authentication to OAuth 2.0 and fill in:
MCP server URL
https://mcp.robinpowered.com/mcp
Authorization URL
https://auth.robinpowered.com/api/oauth/authorize
Token URL
https://auth.robinpowered.com/token
Registration URL
https://auth.robinpowered.com/register

Gemini Enterprise registers itself with Robin through this registration URL, so leave Client ID and Client secret empty.

  1. Name the connector, add a description and a location, and create it
  2. Use Actions → Reload custom actions to sign in to Robin and choose which Robin actions to turn on
  3. Connect the data store to the Gemini Enterprise apps or agents that should use Robin
Microsoft 365 Copilot

Coming soon. Robin is adding sign-in for Microsoft 365 Copilot, and the setup steps will appear here when it’s ready.

Microsoft Copilot Studio
  1. Open the agent you want to give Robin to and go to its Tools
  2. Choose Add a tool, then New tool, then Model Context Protocol
  3. Paste the server URL:
https://mcp.robinpowered.com/mcp
  1. Under Authentication, pick OAuth 2.0 with the Dynamic discovery type
  2. Create a connection, or reuse one, and sign in to Robin
  3. Select Add to agent
Grok

Custom connectors are for Grok Business and Enterprise. A team admin sets up Robin first. After that, each member connects with their own Robin account.

  1. Open grok.com/connectors (some accounts call this page Plugins), select New Connector, then Custom
  2. Paste this URL:
https://mcp.robinpowered.com/mcp
  1. Sign in with your Robin account when Grok asks
Perplexity

Custom remote connectors are a Perplexity Enterprise feature. An admin can add Robin for the whole organization under Enterprise settings → Permissions → Connectors permissions, or switch on Allow members to add custom connectors so people add it themselves.

  1. In Perplexity, go to Account settings → Connectors, select + Custom connector, then Remote
  2. Name it Robin and paste this as the MCP Server URL:
https://mcp.robinpowered.com/mcp
  1. Set Authentication to OAuth and Transport to Streamable HTTP. Perplexity finds Robin’s sign-in on its own
  2. Check the acknowledgement box and select Add
  3. Select the Robin card in Connectors and sign in with your Robin account
  4. An admin who added Robin for the organization then shares it from Enterprise settings → Permissions
Other MCP-supporting assistants

Robin works with any assistant that can add a remote MCP server and sign in with OAuth. Names and menus differ, but the steps are the same.

  1. Find where your assistant adds outside tools. It may call them connectors, integrations, apps, or MCP servers
  2. Add a remote server. If it asks for a transport, pick Streamable HTTP. Paste this URL:
https://mcp.robinpowered.com/mcp
  1. If it asks how to authenticate, pick OAuth
  2. Sign in with your Robin account when the assistant asks

What your assistant can do in Robin

Each tool below is an action an assistant can take once it's connected. Some depend on permissions in your Robin account, and every one runs as you.

Update Desk Reservation update_desk_reservation

Changes the time of an existing desk reservation in place — extend it, shorten it, or shift it — keeping the same booking rather than cancelling and rebooking. A booking that has not started keeps its id; one already in progress may be SPLIT by the service into an elapsed part and a new live booking, and the new part does not carry the original check-in (see updatedReservations). Pass only the bound(s) that change; an omitted start or end is kept as is. The new window goes through the same guard as a fresh booking: nothing entirely in the past, a slipped start bumped forward to now and reported. Works on the calling user's own, non-recurring bookings only — someone else's reservation, a recurring series, and an assigned desk are refused with the supported route instead. Performs the write immediately once the reservation and the new time are settled; returns the previous window beside the new one and a ready-made call to put it back.

ParameterTypeDescription
reservationId requiredstringThe id of the desk reservation to change, as returned by get_user_desk_reservations or book_desk. Must be the calling user's own, non-recurring booking.
startstringNew start as a complete ISO 8601 datetime, e.g. "2026-08-14T09:00:00-04:00". OMIT to keep the current start — an omitted start is never re-sent, which is what makes extending a booking that is already running safe. A new start that has already passed (while the booking still has time left) is bumped forward to the current instant and reported back as effectiveStartTime.
endstringNew end as a complete ISO 8601 datetime, strictly after the (new or current) start. OMIT to keep the current end. A change that would leave the whole booking in the past is refused.
timeZonestringIANA timezone a zone-less start/end is expressed in (e.g. America/New_York). Defaults to the reservation's own timezone, then the request's. Pass it only when the user is clearly speaking in a different zone.
Discard Stack Plan discard_stack_plan

Discard a stack plan and every floor-split proposal under it. There is no way to discard one proposal on its own. Scenario drafts built from a proposal are not affected. Confirm with the user first — this takes the whole plan.

ParameterTypeDescription
stackPlanId requiredstringThe plan to discard, with everything under it
Book Space book_space

Attaches a meeting room to an existing calendar event, for the meeting's own window. Only the organizer or someone who can edit the meeting can do this; for anyone else it refuses and says whose meeting it is. This performs the write immediately — settle the room with the user first; suggest_spaces shows what is free. A meeting holds one room: booking the same room onto the same event twice is safe (the second call reports the existing booking), and a meeting that already holds a different room is refused. To swap rooms, add more than one, or set buffers, use update_event: it does it in one call, and if the new room is refused outright nothing changes (a room can still decline afterwards). It never creates, moves or re-times the meeting, and refuses request-only rooms (those are requested in Robin). Undo with cancel_booking.

ParameterTypeDescription
eventId requiredstringThe existing calendar event to attach the room to, from list_calendar_events or suggest_spaces. The room is held for the MEETING's own window; there is nothing else to pass.
spaceId requiredstring | integerThe space to book, as a whole number, from suggest_spaces or get_space_details. A room NAME is not a space id.
End or Cancel Desk Reservation end_or_cancel_desk_reservation

Removes a desk reservation: cancels it outright when it has not started yet, or ends it at this moment when it is already in progress — one tool for both, and the result says which happened. This is the undo for book_desk. A reservation that is already over is refused, and a recurring booking requires you to say whether the user meant this one occurrence or the whole series — ask them, never assume. Works on the caller's own bookings and, with delegate permission, on someone else's. Returns the removed booking's desk, window, type and visibility, plus a ready-made book_desk call to put it back.

ParameterTypeDescription
reservationId requiredstringThe id of the desk reservation to remove, as returned by get_user_desk_reservations or book_desk. For a recurring booking this is the id of ONE occurrence — the occurrence the user is talking about — even when they want the whole series removed.
scope"this_occurrence" | "whole_series"For a recurring booking only: remove just the named occurrence, or the entire recurring series. REQUIRED when the reservation is part of a series — the call is refused without it rather than guessing, because the two are not undoable in one step. Ask the user which they mean and pass their answer; do not infer one from phrasing like 'cancel my Tuesday desk'. Ignored for a one-off booking.
notifyAssigneebooleanWhen removing SOMEONE ELSE'S reservation, whether Robin notifies them. Defaults to true — a person whose desk was cancelled for them should hear about it. Set false only if the user says they will tell them directly. Has no effect when removing the caller's own booking.
Find Scenario Planning Metric find_scenario_planning_metric

List the workplace analytics metrics available for scenario planning in this organization — one per question: commonly booked rooms by type (space_reservations_count, group by meeting__space_type), rooms consistently under capacity (undersized_meetings_count, group by meeting__space_name), desks used by an individual (desk_checkin_days_count, group by reservation__assignee_name), and desks a team reserves on a typical day (avg_desks_used_per_day: distinct desks reserved per day the group had a booking — reservations including no-shows, not check-ins, averaged over the days the group was in, not over all office days; group by reservation__assignee_department, reservation__assignee_manager_name, or reservation__level_name). Returns each metric’s id, description, allowed group-bys, allowed filters, and date-range behavior. These are the only metrics execute_scenario_planning_metric accepts; always call this first, for the building you will execute against, and use the ids and options it returns. Takes the building_id only; needs planning access to that building.

ParameterTypeDescription
building_id requiredintegerThe building to plan for (its id, as get_office_locations returns it). Required: the catalog is returned only to a planner on the building or a collaborator on one of its drafts or stack plans, and execute_scenario_planning_metric takes the same id.
Execute Scenario Planning Metric execute_scenario_planning_metric

Execute one of the scenario planning metrics (commonly booked rooms by type, rooms consistently under capacity, desks used by an individual, desks a team reserves on a typical day) for one building and return the result (a single value, or grouped rows when group_by is given) with the applied date range, filters, and provenance. The building is a filter in the query itself, so results cover only that building and building_id is echoed on the response; do not add the building to group_by. For how many desks a team reserves on a typical day, use avg_desks_used_per_day grouped by the lens you need (department, manager, or level) and say it counts reservations including no-shows, averaged over the days the group was in. It has a different basis from desk_checkin_days_count, which counts check-in days per person; do not present one as the other. Needs planning permission on the building, or collaborator access to one of its drafts or stack plans: viewers are refused (viewer_only), others too (no_planning_access), and collaborators may group and filter by place and time only, never by anything that names a person (aggregates_only, with the allowed dimensions in valid) — relay these in plain words and do not retry. A truncated result (truncated: true) was capped within the building, like any other grouped result; say so rather than retrying. Use find_scenario_planning_metric first for the metric ids and their allowed group-bys and filters; no other metric id is accepted. Validation failures return a structured error with the valid options — correct the arguments and retry.

ParameterTypeDescription
metric_id required"space_reservations_count" | "undersized_meetings_count" | "desk_checkin_days_count" | "avg_desks_used_per_day"Metric id from find_scenario_planning_metric. Only the scenario planning metrics are accepted; any other metric id is rejected.
building_id requiredintegerThe building to report on (its id, as get_office_locations returns it), the same one passed to find_scenario_planning_metric. Required: results cover this building only.
filtersobject[]Optional filter clauses (each: dimension + exactly one of equals/in/gt/contains/missing). Only selections from the metric's allowed_filter_selections in find_scenario_planning_metric are accepted.
group_bystring[]Optional grouping dimensions (1-3). Only the metric's allowed_group_bys from find_scenario_planning_metric are accepted. Grouped results are capped at 50 rows.
start_datestringOptional ISO start date (YYYY-MM-DD). Omit both dates for the default window (previous completed 30 days). Max range 1 year.
end_datestringOptional ISO end date (YYYY-MM-DD).
min_metric_valuenumberOptional minimum metric value; grouped rows below it are dropped.
Invite Users to Draft invite_users_to_draft

Invite people to a building draft as collaborators or viewers, so they can review or help build a plan before it is published. This NOTIFIES the people invited: invite only who the user named, and only when they asked for it. Requires an explicit accessLevel — 'collaborator' can change the draft, 'viewer' can only read it, and NEITHER can publish it: that stays with the draft's owner. Takes NO version: sharing is outside the draft's version chain, so there is nothing to chain here. First check who already has access — get_draft (include collaborators), or for a stack plan get_stack_plan (include collaborators) — since re-inviting someone emails them a second time for nothing.

ParameterTypeDescription
buildingDraftId requiredstringWhat to share. The parent building-draft id from create_building_draft, or a stack plan id from get_stack_plan or create_stack_plan — sharing a plan carries the floor splits under it. Never a floor draft id, and never an allocation id: share the plan those belong to instead.
userIds requiredstring[]Robin user ids of the people to invite. Resolve names or emails to ids first — with find_people, or search_entities where the client exposes it. Never guess an id: an invite is an email to a real person, so a wrong id is a message to the wrong colleague.
accessLevel required"collaborator" | "viewer"What the invited people may do. "collaborator" can CHANGE the draft — assignments, desk types, neighborhoods — and is what someone helping build the plan needs. "viewer" can only read it and changes nothing. NEITHER can publish: publishing stays with the draft's owner, so inviting someone never hands off the decision to go live, and there is no level here that would. Ask the user which of the two they mean rather than assuming; a request to "share a draft for review" is a viewer unless they say otherwise.
Search Entities search_entities

Find people, desks, spaces, or bookable resources in the caller's organization by name or email. Returns matching entities with their IDs, types, and location context, for use as inputs to other tools. Optionally restrict to specific entity types; omit entityTypes to search everything.

ParameterTypeDescription
query requiredstring
entityTypes"CUSTOM_RESOURCE" | "DESK" | "SPACE" | "USER"[] | null
limitinteger | null
Check Desk Availability check_desk_availability

Pre-flight a desk booking: for up to 100 desks and up to 30 time windows (at most 750 desk-window pairs per call), report each desk's availability status per window, the typed reasons it cannot be booked (policy violations, access, capacity — each carrying the rule's value and a plain-language explanation, so a rejection contains the rule it broke), the reservations occupying it, and any exclusion windows (released assigned desks — bookable as reverse_hotel when the window fits entirely inside one). For a multi-day or recurring plan, pass each day's span as its own window in ONE call instead of calling once per day. This answers whether SPECIFIC desks can be booked; it is not a floor sweep — narrow the desks first (find_desks by floor or zone) and bring their ids here. Availability is evaluated for the calling user unless userId is given, and each desk reports bookedByRequestingUser — the flag that separates a desk someone ELSE holds from one the evaluated user already holds themselves (private bookings included, also when checking on someone else's behalf), the latter being reschedulable in place even though the desk reads as not bookable. Assigned desks (types ["assigned","shared"]) are judged the way the office map judges them: bookable only as reverse_hotel on a day the assignee has released the desk — releasedForWindow, assignees, and bookAs report exactly that, and bookable already folds it in, so an unreleased assigned desk nobody sits at reads status AVAILABLE but bookable false. Zone-less window bounds are read in timeZone when given (pass the same value to book_desk), else the caller's timezone. ALWAYS check the chosen desk here before book_desk: booking rejections that this surfaces in advance include conflicts, advance-booking limits, max length, working hours, and floor capacity — and capacity is only enforced here, not at booking time.

ParameterTypeDescription
deskIds requiredstring | integer[]Desk ids to check (1-100), as returned by find_desks or search_entities. deskIds × windows may not exceed 750 per call.
windows requiredobject[]Time windows to evaluate (1-30), each an exact start/end span. For a multi-day or recurring plan, pass each day's span as its own window in one call instead of calling once per day — results come back per window, in request order. deskIds × windows may not exceed 750 per call; beyond that, split by dates or narrow the desks.
userIdstring | integerThe numeric Robin user id the availability is evaluated for (permissions, access, own-conflict checks), as returned by search_entities or get_current_user_info. Defaults to the calling user.
timeZonestringIANA timezone that zone-less window bounds are expressed in (e.g. America/New_York). Defaults to the caller's timezone; pass it explicitly when checking in a different building's zone — and pass the SAME value to book_desk afterwards, so both tools resolve the same absolute window. Ignored for bounds that carry their own offset or Z.
Get Office Locations get_office_locations

List every office location (building) in the caller's organization, with address and timezone. The id of each is the buildingId the building tools take. For a building's size and shape — floors, desk counts by booking type, neighborhoods — pass it to get_building. When only the floor ids and names are needed, get_location_levels is the one cheaper call.

No parameters required.

Get Meeting get_event

Reads one meeting by event id: its time, attendees, rooms, check-in, and what the caller is allowed to do to it (permissions). Read it before proposing any change or cancel, and only propose what permissions allows. Get the event id from list_calendar_events.

ParameterTypeDescription
eventId requiredstringThe meeting to read, as an event id from a tool result (list_calendar_events, book_space).
Book Desk book_desk

Books a desk for a settled desk and time window, and returns everything needed to report the booking back and offer an undo. Books for the current user by default, or for another person when forUser (their email) is given — which requires delegate permission. This tool performs the write immediately; confirm the desk, window, and reservation type first (check_desk_availability shows what is bookable and which reservationType to send as bookAs). It does not re-check availability itself, but the window goes through the same guard as the read tools: start and end may carry an offset or be zone-less (then read in timeZone, the request timezone, or the caller's profile timezone, in that order), a window that has already ended is refused, and a start that has merely passed is clamped to now and reported as effectiveStartTime. Assigned desks follow their own rule: one is bookable only as reverse_hotel, and only on a day its assignee has released it — with no release (or no assignee to release it) it is seated by an admin assignment instead and cannot be booked by anyone.

ParameterTypeDescription
deskId requiredstringThe id of the desk (seat) to book, as returned by find_desks or check_desk_availability. A numeric id.
start requiredstringReservation start as an ISO 8601 datetime, with an offset ("2026-09-10T09:00:00-04:00") or without ("2026-09-10T09:00:00"). A zone-less value is read as wall-clock time in timeZone when given, else the request timezone, else the calling user's profile timezone — the same chain check_desk_availability uses, so the window pre-flighted there is the window booked here. Bare dates and natural-language times are rejected. Resolve "today"/"tomorrow" against get_current_user_info's currentDateTime; a start that has already passed is clamped forward to now and reported as effectiveStartTime, and a window that has already ended is refused.
end requiredstringReservation end as an ISO 8601 datetime, strictly after start, in the same form as start.
reservationType required"hot" | "hoteled" | "reverse_hotel"The reservation type for this desk: 'hot' (short-term, same-day), 'hoteled' (booked for a defined period), or 'reverse_hotel' (an assigned desk on a day its assignee has released it). Use exactly what check_desk_availability reports as bookAs for the desk and window. An assigned desk that is not released has no type here — check_desk_availability reports it as bookAs null with the reason.
visibility"EVERYONE" | "JUST_ME"Who can see this reservation on the floor plan. Defaults to EVERYONE; use JUST_ME only if the user asked to keep it private.
forUserstringEmail of the person to book for, when booking on someone else's behalf. Omit to book for the current user. Resolve a name to an email with find_people; booking for others requires delegate permission.
timeZonestringIANA timezone the start/end are expressed in (e.g. America/New_York), and the zone the reservation is recorded in. Defaults to the request timezone, then the calling user's profile timezone; pass it explicitly when booking in a different building's zone — and pass the SAME value you gave check_desk_availability. Only when start and end both carry their own offset does it fall back to UTC.
Get Building get_building

Buildings and floors in one call. No buildingId: every building with id, address and timezone. With one: the building and its floors (levelId, desk count). include adds overview (active desks per floor by booking type), neighborhoods (colour slot, membership), neighborhoodColors (the slots update_draft accepts) and desks (booking types and who sits at each desk; planners only, 4 floors per call, narrow with levelIds). assignees: where those people sit (planners only). All live; no draft needed.

ParameterTypeDescription
buildingIdstringOmit to list every building in the organization. Pass one building's id to read it and its floors.
levelIdsstring | integer[]Only these floors of the building, by levelId; the facets then cover just them. Omit for every floor. Needs buildingId.
include"overview" | "neighborhoods" | "neighborhoodColors" | "desks"[]Extra facets. "overview": each floor's active and disabled desk counts and its active desks per booking type (needs buildingId). "neighborhoods": each floor's neighborhoods with name, colour slot and desk membership (needs buildingId). "neighborhoodColors": the colour slots this organization defines, the only values update_draft accepts for a neighborhood colour; org-wide, so it works with or without buildingId. "desks": every desk on each floor with its booking types and who sits there, read live; planners only, at most 4 floors per call, so narrow a bigger building with levelIds (needs buildingId).
assigneesobject[]People to look up, each by userId or email. Adds where each one sits on the live plan, with every desk they hold — the desks a move would release. Planners only. get_draft's assignees reads the same with a draft's changes applied. Needs buildingId.
Find Available Desks find_available_desks

Answer "what desks are free on floor X for this window" in one call: sweeps an entire level for a time window and returns the desks that are bookable by the calling user (with names, booking types, pod, amenities, and the reservationType to book them as), plus how many are busy or off-limits. Assigned desks follow the office map's rule: one is listed only when its assignee has released it for the window (then with bookAs reverse_hotel) — the rest are counted in assignedNotBookableCount. Optionally filter to desks with specific amenities — call once without deskAmenityIds to get the floor's amenity ids from levelDeskAmenities, then re-call with them. Use this instead of paging find_desks results through check_desk_availability in batches. A window that has already ended is refused; a start that has merely passed is clamped to now and reported as effectiveStartTime, which then replaces the requested start everywhere. Availability reflects the caller's own access and conflicts; still call check_desk_availability on the desk you pick before book_desk, since policy limits and floor capacity are only reported there.

ParameterTypeDescription
levelId requiredstring | integerThe level (floor) id to sweep, as returned by get_location_levels or find_desks
start requiredstringWindow start as an ISO 8601 datetime, e.g. "2026-08-27T09:00:00-04:00". Bare dates and natural-language times are rejected; a value without an offset or Z is read in the request timezone, then the calling user's profile timezone. Resolve "today"/"tomorrow" against get_current_user_info's currentDateTime rather than an assumed date — a start that has already passed is clamped forward to the current instant and returned as effectiveStartTime.
end requiredstringWindow end as an ISO 8601 datetime, strictly after start. A window that has already ended fails the call rather than being answered — nothing can be booked into it.
deskAmenityIdsstring | integer[]Optional amenity ids every returned desk must have, e.g. a monitor or a standing desk. Ids come from levelDeskAmenities in this tool's own result — call once without the filter to learn the floor's amenity vocabulary, then re-call with the ids. Multiple ids narrow further (a desk must satisfy the filter to count as available). Never guess an id from a name like "monitor".
Get User Desk Reservations get_user_desk_reservations

List a user's desk reservations within a time range: which desk (with its floor and building names), when, the reservation type, check-in state, and who booked it, sorted earliest-first. Omit userId for the calling user (it resolves automatically); resolve anyone else with search_entities. Times are ISO 8601 datetimes; use the start and end of the user's day (in their timezone) to answer questions about a specific day. For preference questions ("my usual desk", "which days am I normally in", "how often do I come in"), query a past range (4-8 weeks works well) and read the server-computed `analytics` block instead of counting reservations yourself.

ParameterTypeDescription
userIdstring | integerThe Robin user id to list desk reservations for, as returned by search_entities. OMIT for the calling user — it resolves automatically.
startTime requiredstringRange start as an ISO 8601 datetime, e.g. "2026-08-13T00:00:00Z"
endTime requiredstringRange end as an ISO 8601 datetime, after startTime
Get Draft Data get_draft_data

Read one floor's draft state: its desks (booking types, who the draft seats there) and neighborhoods (name, colour slot, desk membership), each flagged if it differs from the live plan, plus the draft's version. levelId is required; one floor per call. Not a pure read: it creates the floor's child draft on first call and syncs newly added seats, which is right after create_building_draft; repeating it changes nothing. With no draft in play, use get_building with include neighborhoods.

ParameterTypeDescription
buildingDraftId requiredstringThe parent building-draft id from create_building_draft (never a floor draft id)
levelId requiredstring | integerThe level (floor) id to read. Required — omitting it would read some other floor. Accepts the numeric levelId get_building or get_draft (include review) returns.
Get Building Hours get_building_hours

The building's working day, date by date: opening time frames for a range of calendar dates, with closed days (weekends, holidays, closures) coming back as empty. Use it to propose real booking hours instead of assuming 9:00–17:00, and to skip closed days when planning multi-day bookings — a day with open=false should never be booked. locationId defaults to the calling user's default building; all times are local wall-clock times in the building's own timezone (returned alongside).

ParameterTypeDescription
locationIdstring | integerThe building (location) id, as returned by get_office_locations. OMIT for the calling user's default building — it resolves automatically.
startDate requiredstringFirst calendar date of the range, "YYYY-MM-DD" — a date on the building's own calendar, not an instant, so no time or offset.
endDate requiredstringLast calendar date of the range, inclusive — same day as startDate for a single day, at most 31 days after it.
Cancel Meeting cancel_event

Cancels a meeting the caller organizes (or may cancel as a delegate) for EVERYONE: it is removed from every attendee's calendar and its rooms are released. Cannot be undone; confirm with the user first. For a recurring meeting, pass scope with the user's answer to 'just this one, this and following, or the whole series?' — the call is refused without it. To take a room off a meeting while the meeting goes on, use cancel_booking instead. After success, tell the user which scope was cancelled.

ParameterTypeDescription
eventId requiredstringThe meeting to cancel, as an event id from a tool result (list_calendar_events). For a recurring meeting, the id of the occurrence the user named.
scope"this_event" | "this_and_following" | "all_events"For a recurring event only: which occurrences the change applies to. 'this_event' — just the named occurrence; 'this_and_following' — the named occurrence and every later one; 'all_events' — the whole series. REQUIRED when the event is part of a series — the call is refused without it rather than guessing, because the three are not undoable in one step. Ask the user which they mean and pass their answer; do not infer one from phrasing like 'cancel my Tuesday sync'. Ignored for a one-off event.
Find People find_people

Find people and return the user ids the draft tools take, each with their default building. Filters AND together: userNameFilter (a PREFIX of name, surname, slug or email), departments (exact strings; include departments lists them with headcounts, so never guess one), managerIds (direct reports; ids, not names; include managers lists them), reportsOf (a manager's whole chain at every depth; combine only with departments), addedWithinDays, statuses (omit to exclude deactivated people). With buildingDraftId each person carries their access to it; add assignmentStatus UNASSIGNED for who still has no desk in it. accountUsersTotal is the full count, not the page: state it before seating a group.

ParameterTypeDescription
userNameFilterstring | nullA PREFIX match on name, surname, slug or email: "ana" finds Ana Kovač, "kova" finds her by surname, a fragment from the middle of a name finds nothing. Ask for a cleaner spelling rather than guessing.
departmentsstring[] | nullEXACT match against the free text an admin typed. Pass values from the departments facet verbatim: a guessed string returns zero people, which is indistinguishable from an empty team.
managerIdsstring[] | nullUser ids, never names; the managers facet lists the real set. Returns those managers' DIRECT reports only; reportsOf walks a whole chain.
reportsOfstring | nullA manager's user id. Returns everyone under them at every depth, each with their depth, the manager excluded. Combines with departments only. A deactivated person ends the walk below them.
addedWithinDaysinteger | nullPeople added to the organization in the last N days; 0 or null is no limit
statuses"ACTIVE" | "ADDED" | "DEACTIVATED" | "INVITE_EXPIRED" | "INVITE_PENDING"[] | nullOmit to exclude deactivated people, which is what seating wants. Pass DEACTIVATED to explain why someone expected is missing.
buildingDraftIdstring | nullA building draft from create_building_draft. Scopes the ANSWERS, not the search: people are found across the whole organization either way, and each is reported with their access to this draft.
assignmentStatus"ASSIGNED" | "UNASSIGNED" | nullWith buildingDraftId: filter by whether the person has a desk IN THAT DRAFT, with its pending changes applied. "UNASSIGNED" answers "who still needs a seat in this plan?"
include"departments" | "managers"[] | null"departments": every department string with its active headcount, the values the departments filter takes. "managers": everyone who manages someone, with the ids managerIds and reportsOf take.
offsetinteger | nullRow to start from; below 0 reads as 0
limitinteger | nullRows per page, 1 to 100; out-of-range values are clamped, not refused
Get Stack Plan get_stack_plan

Saved stack plans for a building — proposals, not current seating (read that with get_building (include desks)). No stackPlanId: list the building's saved plans (buildingId required), names and descriptions only. With one: the plan in full — its business-unit variations, every floor split with each floor's capacity against what it places there, what each split leaves short, and the building drafts derived from each; include collaborators adds who the plan is shared with. Read-only.

ParameterTypeDescription
stackPlanIdstringThe stack plan to read in full, from a listing or create_stack_plan. Omit to list a building's plans instead.
buildingIdstringThe building whose stack plans to list, when no stackPlanId is given. A stack plan never spans buildings.
include"collaborators"[]With stackPlanId only. "collaborators": who the plan is shared with and at what level, to check before invite_users_to_draft emails someone who already has access.
Get Draft get_draft

Read one building draft without changing it. Always returns its version (what the write tools take), its status and the link that opens it in Robin. assignees adds where those people sit with the draft's changes applied. include adds review (what publishing would change per floor, with the bookings it would cancel), collaborators (who it is shared with, at what level) and queue (who waits for a desk on levelId). Takes create_building_draft's buildingDraftId. Floor desks are get_draft_data.

ParameterTypeDescription
buildingDraftId requiredstringThe building-draft id from create_building_draft (never a floor draft id)
assigneesobject[]People to look up, each by userId or email. Adds where each one sits according to this draft, with its pending changes applied: the desks a move would release, or proof an assignment landed. get_building's assignees reads the live plan instead.
include"review" | "collaborators" | "queue"[]Extra facets. "review": what publishing would change per floor, with each floor's levelId, change counts, neighborhood deltas and the live bookings publishing would cancel; always read it before publish_draft. "collaborators": who the draft is shared with and at what level, to check before invite_users_to_draft emails someone who already has access. "queue": who is waiting for a desk on levelId (queues are per floor, so levelId is required).
levelIdstring | integerA floor of the draft: required for the queue facet, and lands the url on that floor.
Get Workflow Guide get_workflow_guide

Read the matching guide FIRST, before create_building_draft, update_draft, publish_draft or any stack-plan tool. stack_planning covers how teams are seated across a building today, consolidation and what-if layouts; scenario_planning covers seating or moving people, changing desks and neighborhoods, and publishing a draft. Returns which tool to call in which order, the draft lifecycle rules, and how to talk to the user while planning.

ParameterTypeDescription
topic required"scenario_planning" | "stack_planning"Which guide. "stack_planning": how business units are seated across a building today, consolidation and what-if layouts ("where does each team sit?", "show me a breakdown across my floors"). "scenario_planning": seating or moving people, changing desk types or neighborhoods, and publishing a draft ("Assign Sam to Floor 2", "Move the team to Pod 1").
Move Meeting move_event

Moves a meeting to a different room when the meeting lives on a room's calendar: its organizer room (get_event marks it isOrganizer). For any other room on a meeting, add, remove or swap it with update_event instead. Find the room with search_entities when the user names one; to offer replacement rooms, use suggest_spaces' windows form with the meeting's start and end, and its attendeeCount as capacity. Confirm the room with the user before calling. Keeps the meeting's time; to change the time, use update_event. A recurring meeting moves this occurrence only. Attendees are notified unless the user says not to. The move is submitted, not finished: tell the user it is on its way, and before changing the meeting further, find it again with list_calendar_events (on Microsoft it gets a new id). Never show ids to the user.

ParameterTypeDescription
eventId requiredstringThe meeting to move, as an event id from a tool result (list_calendar_events, get_event). For a recurring meeting, the id of the occurrence the user named.
spaceId requiredstring | integerThe room to move the meeting to: a SPACE hit's entityId from search_entities, or a room from suggest_spaces; never a name.
notifyAttendeesbooleanTell the attendees about the move. Defaults to true; pass false only when the user asks. Always true for Microsoft calendars.
Create Meeting create_event

Creates a NEW meeting on the user's calendar, optionally with rooms and people. For a meeting that already exists, add a room with book_space instead. Resolve names first with search_entities (people and rooms). When several rooms match, ask the user which, naming each one's building and floor — the meeting may be for someone else, somewhere else. Then confirm everything with the user in one message — time and zone, rooms, people, and anything you assumed — before calling: this books immediately. Request-only rooms and repeating meetings are refused. A meeting with no room needs a personal calendar Robin can book on; if there is none, the result says so — then ask the user for a room. If a room is taken, follow the returned guidance; do not try rooms one after another.

ParameterTypeDescription
titlestringThe meeting's title. Omit and Robin names it.
descriptionstringThe meeting's body text.
start requiredobjectFor an all-day meeting, midnight at the start of the first day.
end requiredobjectStrictly after start. For an all-day meeting, midnight at the start of the day after the last day.
spaceInviteesobject[]The rooms to book with the meeting. Empty for no room — that needs a personal calendar Robin can book on, and is refused otherwise.
inviteesobject | object[]The people to invite, each a userId or an email. Leave the user themselves out: they are added automatically.
isPrivatebooleanHide the meeting's details from other people. Omit to use the user's default (private when they may not book publicly).
recurrenceanyNot supported yet: any value here refuses the call. Repeating meetings are booked in Robin for now.
Update Meeting update_event

Changes an existing meeting for everyone on it: title, description, time, people, rooms and room buffers, or recurrence, in one call. Send only the changes the user asked for, as commands in `changes`. Resolve people and rooms by name with search_entities first; ask when a name matches several. A time without timeZone is read in the meeting's own zone. To swap a room, remove the old one and add the new one in the same call: if the new room is refused outright, nothing changes, but a room can still decline afterwards, so check with get_event before telling the user it is confirmed. The room the meeting itself lives on cannot be removed here. For a recurring meeting, pass scope with the user's answer to 'just this one, this and following, or the whole series?' — the call is refused without it. Confirm the change with the user before calling: dryRun: true reports exactly what the call would do, side effects included, without changing anything. Never show ids to the user.

ParameterTypeDescription
eventId requiredstringThe meeting to change, as an event id from a tool result (list_calendar_events, get_event). For a recurring meeting, the id of the occurrence the user named.
scope"this_event" | "this_and_following" | "all_events"For a recurring event only: which occurrences the change applies to. 'this_event' — just the named occurrence; 'this_and_following' — the named occurrence and every later one; 'all_events' — the whole series. REQUIRED when the event is part of a series — the call is refused without it rather than guessing, because the three are not undoable in one step. Ask the user which they mean and pass their answer; do not infer one from phrasing like 'cancel my Tuesday sync'. Ignored for a one-off event.
changes requiredobjectOnly the changes the user asked for; anything omitted stays as it is. At least one is required.
dryRunbooleantrue: check the change and report exactly what it would do, without changing anything. Use it to tell the user what will happen, including any side effects (spaces that declined coming off, buffers removed because the meeting becomes all-day), then call again without dryRun once they agree.
Update Draft update_draft

Apply changes to a building draft in one batch, under its optimistic lock: DESK changes (reservability, reservation types, assignments), NEIGHBORHOOD changes (name, colour slot, desk membership, create and remove) and QUEUE changes (add or remove people on a floor's waiting list). Takes create_building_draft's buildingDraftId and the draft's current version; returns the new version for the next update_draft or publish_draft. Before a neighborhood change read get_building (include neighborhoodColors: a colour is a named slot, not a hex) and get_draft_data (membership REPLACES what is there). Seating a queued person does not dequeue them: send a QUEUE remove for them in the same batch.

ParameterTypeDescription
buildingDraftId requiredstringThe parent building-draft id from create_building_draft (never a floor draft id)
version requiredintegerThe draft's CURRENT version: 0 for a fresh draft, otherwise the version returned by the previous update_draft. If the current version is unknown, re-fetch it via get_draft. Guessing (e.g. 1) fails on fresh drafts.
changes requiredobject | object | object[]The changes to apply: desk changes (the default kind), neighborhood changes and queue changes, in any mix. Neighborhood changes may span floors — the tool groups them. Desks are applied first, then neighborhoods, then queues.
Create Stack Plan create_stack_plan

Save a stack plan for a building along with the business-unit options it is weighing. Each option is a set of building-wide desk totals per business unit. Returns the plan id and an id per option, which update_stack_plan set_allocation needs to propose a floor split.

ParameterTypeDescription
buildingId requiredstringThe building being replanned. A stack plan never spans buildings.
name requiredstringWhat the replan is called
descriptionstringWhat the replan is for, in the planner's words
variations requiredobject[]The options this plan is weighing. Several is normal — that is what makes them comparable.
Publish Draft publish_draft

PUBLISHES a building draft to the LIVE floor plan: irreversible and visible to the whole workplace. Before calling, read get_draft (include review) and confirm with the user. Takes the draft's current version (from the latest update_draft) and the numeric levelIds to publish. EMAIL: the publisher always receives a summary and that cannot be turned off; every other email is off unless the user asked for it. sendNotifications emails the people affected (changed assignments and cancelled reservations, together); exportCSVUserIds emails a CSV of the changes to the recipients named. Publishing is ASYNCHRONOUS: a successful return means the publish was ACCEPTED. Pass waitSeconds (up to 30) to wait for it to settle and get the final status back; if it is still publishing after that, confirm later with get_draft. Never retry this tool or create another draft while a publish is in flight.

ParameterTypeDescription
buildingDraftId requiredstringThe parent building-draft id from create_building_draft (never a floor draft id)
version requiredintegerThe draft's CURRENT version — must be the version returned by the latest update_draft (or 0 for a never-updated draft). Never guess; a stale version is rejected.
levelIds requiredinteger | string[]Numeric level (floor) ids of the building to publish — NOT floorDraftIds. get_draft (include review) returns each floor's levelId.
sendNotificationsbooleanEmail the people this publish affects: those whose desk assignments changed AND those whose reservations get cancelled. One switch covers both groups — they cannot be notified separately. Off unless the user explicitly asked for it. This is separate from the summary the publisher always receives.
notifyAffectedAssigneesbooleanGranular form of sendNotifications for people whose assignments changed. Behaves identically — setting either notifies both groups — so prefer sendNotifications and leave this unset.
affectedAssigneesMessagestringOptional note added to the notification email, in markdown. Only the assignment email carries a message; there is no equivalent for people whose reservations were cancelled. Pass only text the user gave you.
notifyAffectedReserveesbooleanGranular form of sendNotifications for people whose reservations get cancelled. Behaves identically — setting either notifies both groups — so prefer sendNotifications and leave this unset.
exportCSVUserIdsstring[]User ids to EMAIL a CSV of the published changes to. A separate opt-in from the notifications above, with its own recipients: these are people the user nominates to receive a record of the move — typically facilities or ops colleagues, NOT the people being seated. Omit to send no CSV. Resolve names to ids with find_people, or search_entities where the client exposes it; never guess an id.
exportCSVMessagestringOptional note added to the CSV email, in markdown. Ignored when exportCSVUserIds is omitted. Pass only text the user gave you.
waitSecondsintegerHow long to wait for the publish to settle before returning, polling its status every 5 seconds and stopping at the first settled status. 0 returns as soon as the publish is accepted. Capped at 30 so the call fits the connector's timeout; a publish still in flight after that is confirmed later with get_draft.
Create Building Draft create_building_draft

Start a floor-plan draft for a building. Returns the buildingDraftId that every subsequent lifecycle call (update_draft, get_draft, publish_draft) requires — child floorDraftIds are never valid there. The new draft starts at version 0.

ParameterTypeDescription
name requiredstringHuman-readable name for the draft
buildingId requiredstringThe building (location) id to draft changes for
derivedFromstringThe allocation this draft implements, from get_stack_plan or update_stack_plan. Records which floor split the seating work came from. The allocation must belong to this same building.
Get Location Levels get_location_levels

Get an office location (building) by ID with its address and timezone, plus every floor with that floor's total desk count. Use it to resolve a floor name to the numeric levelId the draft tools take. deskCount includes inactive desks — to see individual desks, their booking types, and who sits on them, use find_desks with that levelId. For desk counts by booking type across floors, or each floor's neighborhoods, use get_building instead.

ParameterTypeDescription
locationId requiredstring
Find Desks find_desks

Resolve a desk reference like "Pod 1, Desk 2" to desk ids (deskName, optional podName and levelId; exact matches first, and if several match, ask the user), or omit deskName and pass levelId to list every desk on a floor with booking types and assignee count: an open assignable desk is deskTypes ["assigned","shared"] with assigneeCount 0. Reads the LIVE floor plan; for a floor as the draft would leave it use get_draft_data, and for desk counts alone use get_building (include overview).

ParameterTypeDescription
buildingId requiredstringThe building (location) id to search in
deskNamestringThe desk name as an admin says it, e.g. "Desk 2". Omit to list a floor's desks instead — levelId is then required.
podNamestringOptional pod/zone name to narrow the search, e.g. "Pod 1"
levelIdstring | integerOptional level (floor) id filter; required when deskName is omitted. Accepts the numeric levelId get_building or get_draft (include review) returns.
Get Current User Info get_current_user_info

The calling user's identity and context in one call: userId, name, email, timezone, organization, default building (defaultLocationId) and floor (defaultLevelId), and currentDateTime, the authoritative current instant. Takes no input. Call it FIRST for any request about the user themselves ("my desk", "my building") and for any relative date ("today", "next Tuesday"): resolve the date against currentDateTime in the user's timezone, because a booking in the past is accepted silently.

No parameters required.

Update Stack Plan update_stack_plan

Change a stack plan after create_stack_plan. action add_variation records one more set of building-wide desk totals on the plan: a variation freezes once a split implements it, so a revision is a new variation, not an edit or a second plan. action set_allocation proposes how a variation's desks sit across floors: with variationId and name it creates a split; with allocationId and the version last read it rewrites the floors sent (each replaces that floor outright, floors left out stay) or renames it. Returns what the split still leaves short against its variation (shortfalls, reported not refused) and each written floor against its real desk count. get_stack_plan reads it all back.

ParameterTypeDescription
action required"add_variation" | "set_allocation""add_variation": record one more set of building-wide desk totals on the plan. "set_allocation": create a floor split for a variation (variationId and name), or change an existing split (allocationId and version).
stackPlanId requiredstringThe plan, from get_stack_plan or create_stack_plan
namestringThe variation's or split's name, e.g. "Today's headcount + 10 Engineering" or "Consolidated". Required when creating either; on an existing split, renames it.
descriptionstring | nullWhy it is on the table, in the planner's words. On an existing split, null clears it and leaving it out keeps it.
businessUnitsobject[]add_variation only: what each business unit needs, building-wide. The whole set, not a delta from another variation.
variationIdstringset_allocation, creating: the variation the split implements, from get_stack_plan, create_stack_plan or add_variation. Several splits can implement one variation; that is how layouts get compared.
allocationIdstringset_allocation, changing: the existing split, from get_stack_plan or an earlier set_allocation.
versionintegerset_allocation, changing: the split's version last read, from get_stack_plan or the previous set_allocation on it. A stale value is refused rather than overwriting whoever got there first.
floorsobject[]set_allocation: the split. Creating, the starting split, which may be left out and filled in later. Changing, each floor sent REPLACES that floor outright and floors left out are untouched; a floor with no business units is cleared. Send only the floors to change.
List Calendar Events list_calendar_events

List the calling user's calendar events for a date, or a range of up to 14 days, with each event's headcount and whether it already has a space booked. Call this first for any request that names a meeting ("my 10am tomorrow", "Thursday's standup") — the eventId it returns is what suggest_spaces and book_space take. With withSpace: true it lists only the meetings on the caller's calendar that hold a room — there is no separate bookings tool — each with the spaceId that cancel_booking takes. Reads only the caller's own calendar, and refuses when no calendar is connected, so an empty list means free days (or, with withSpace, no room held).

ParameterTypeDescription
date requiredstringThe first (or only) date to list, as YYYY-MM-DD (e.g. "2026-09-16"), read in `timeZone`. A date, not an instant. Past dates are allowed.
endDatestringThe last date to include, inclusive, as YYYY-MM-DD. Omit for a single day. At most 14 days from date; split a longer span.
timeZonestringIANA timezone the dates are read in (e.g. America/New_York). Omit to use the caller's timezone; pass it to read a day as another building experiences it. Required when the caller's profile has no timezone — the call is refused rather than read in UTC.
userIdstring | integerOmit. This tool reads only the calling user's own calendar; another user's id is refused.
withSpacebooleanTrue to list only the meetings on the caller's calendar that hold a room ("what rooms do I have this week?", "cancel my room for Thursday"). Each such event carries its spaces with spaceId, ready for cancel_booking. Omit or false to list every event.
Show Stack Plan show_stack_plan

Draw a stack plan: one row per floor, every desk as a chip coloured by its group, with a legend. Use it for questions about a WHOLE building — how space divides across floors, where there is room, whether a team is split up — and to show a PROPOSED layout before anything is written to Robin. You supply the rows, so read the building first (get_building for the floors, then get_building with desks included for who sits where today, or get_draft_data per floor for a draft, joined to find_people for departments and managers) and pass the counts you worked out. Send the same numbers you describe in prose. Do not call this on a vague request: confirm which building and which breakdown first, since reading a building is many calls.

ParameterTypeDescription
lens required"department" | "manager" | "neighborhood"What the breakdown is by. Say which one in the conversation too.
floors requiredobject[]One entry per floor, in the order they should be listed — top floor first reads best. Include floors with desks but no allocation; omit floors with no desks at all.