Vacation TrackerDeveloper Docs
MCP Server

Tools

Full reference for every Vacation Tracker MCP tool. Parameters, examples, error shapes, and what each tool does not do.

The MCP server exposes 13 tools: six read tools and seven write tools. Every tool:

  • Accepts names or IDs wherever a person, department, location, leave type, or label is referenced. "me" refers to the caller. Ambiguous name matches return an error with candidate IDs.
  • Accepts dates as YYYY-MM-DD. Where noted, it also accepts the shortcuts today, this_week, next_week, this_month, next_month, last_week, and last_month, resolved in the caller's location timezone.
  • Returns JSON as a text block and as structuredContent, in the same shapes as API v2, so ids a read tool returns can be passed straight back into a write tool.
  • Hides pagination. When a list exceeds the internal cap, the response sets truncated: true; narrow the filters to see the rest.
  • Returns failures as tool results with isError: true, not as JSON-RPC errors. See Errors.

The examples below show the tool call arguments and the JSON the tool returns. Responses are abridged: items carry the full API v2 fields.

Read tools

get_workspace_info

Scope: mcp:workspace:read · Minimum role: any

Returns the workspace's departments, locations, leave types, and labels in one call. Assistants usually call it once at the start of a conversation to resolve names to IDs.

ParameterTypeDescription
include("departments" | "locations" | "leaveTypes" | "labels")[] (optional)Sections to return. Omit or pass an empty array to get all four.
withWorkWeekboolean (optional)Attach each location's work week (working days and hours). Defaults to false.

Example request

{ "include": ["departments", "locations"], "withWorkWeek": true }

Example response

{
  "departments": [{ "id": "team-abc", "name": "Engineering" }],
  "locations": [
    {
      "id": "location-xyz",
      "name": "Belgrade Office",
      "timezone": "Europe/Belgrade",
      "isDefault": true,
      "workWeek": { "days": [1, 2, 3, 4, 5] }
    }
  ]
}

A section your role cannot read comes back as { "unavailable": "Your role cannot view this in Vacation Tracker" } instead of failing the call. Regular employees get unavailable for departments, locations, and labels.

Does NOT: return users, leaves, or holidays. The result is stable for the conversation, so don't call it on every question.

find_users

Scope: mcp:users:read · Minimum role: any

Finds people by partial name or email, department, location, or label. You see only the users your role reaches: regular employees see only themselves.

ParameterTypeDescription
querystring (optional)Partial match against name or email.
departmentstring (optional)Department name (partial match) or id.
locationstring (optional)Location name (partial match) or id.
labelstring (optional)Label name (partial match) or id.
status"active" | "inactive" | "all" (optional)Defaults to "active". Use "all" to include deactivated users.
limitinteger 1–200 (optional)Defaults to 50.

Example request

{ "query": "sarah", "department": "Engineering" }

Example response

{
  "users": [
    { "id": "usr_123", "name": "Sarah Chen", "email": "sarah@acme.com", "locationId": "location-xyz" }
  ],
  "totalMatches": 1,
  "truncated": false
}

Does NOT: return balances, quotas, or leave (use get_user_details and get_leaves). Call it once per person with a specific query, not in a loop over a list of names.

get_user_details

Scope: mcp:users:read · Minimum role: any

One person's profile, leave balances, quotas, labels, work week, and the leave types they can request. This is the tool for "how many vacation days do I have left?" and for checking which leave types someone can request before request_leave.

ParameterTypeDescription
userstring (optional)Name (partial match), email, or user id. Defaults to "me", the caller.
include("profile" | "balances" | "quotas" | "labels" | "workWeek" | "leaveTypes")[] (optional)Sections to return. Defaults to all of them.

Requestable leave types come from the location's leave policies, with their rules (isReasonRequired, isApprovalRequired, allowLeaveInPast, and shortestLeaveInterval, which also tells whether half days or hours are allowed). You can always read the policies of your own location. When the policies are not readable for your role, they come from the balances and are marked "source": "from balances".

Balances and quotas are the person's own numbers: they include personal quota overrides (isCustom in quotas), prorating, rollover, and seniority and role entitlements. Use them for how many days someone gets or has left; the quotas in leave policies are location defaults. On your own balances and quotas, leave types hidden from coworkers show their real names.

Example request

{ "include": ["balances", "leaveTypes"] }

Example response

{
  "user": { "id": "usr_123", "name": "Sarah Chen", "email": "sarah@acme.com" },
  "balances": [
    { "leaveTypes": [{ "leaveTypeId": "lt-123", "leaveTypeName": "Vacation" }] }
  ],
  "leaveTypes": {
    "source": "leavePolicies",
    "items": [
      {
        "leaveTypeId": "lt-123",
        "leaveTypeName": "Vacation",
        "isReasonRequired": false,
        "isApprovalRequired": true,
        "shortestLeaveInterval": "HALF-DAY",
        "allowLeaveInPast": false
      }
    ]
  }
}

Sections your role cannot view are marked unavailable. A regular employee gets unavailable for workWeek, because location work weeks need location access.

Does NOT: list leaves (use get_leaves) or change anything (use manage_user). A user outside your reach fails with USER_NOT_FOUND.

get_leaves

Scope: mcp:leaves:read · Minimum role: any

Lists approved, pending, or denied leaves and TOIL requests in a date range, or fetches one by id. Results match what the caller sees on the Vacation Tracker wallchart, including hidden leave types shown without their real type and reasons blanked where the app blanks them.

ParameterTypeDescription
fromstring (optional)Start of the range: YYYY-MM-DD or a date shortcut, which covers its whole period. Required unless id is given.
tostring (optional)End of the range: YYYY-MM-DD or a date shortcut. Defaults to the end of from.
userstring (optional)User name, email, "me", or user id.
departmentstring (optional)Department name or id.
locationstring (optional)Location name or id.
leaveTypestring (optional)Leave type name or id.
status"approved" | "pending" | "denied" | "all" (optional)Defaults to "approved".
kind"leave" | "toil" | "all" (optional)Defaults to "leave".
idstring (optional)Return one leave or TOIL request by id. Dates are not required then.

If a name filter cannot be resolved with your role (for example a department name for a regular employee), pass the id instead.

Example request

{ "from": "next_week", "status": "all", "kind": "all" }

Example response

{
  "leaves": [
    {
      "kind": "leave",
      "id": "leave_789",
      "user": { "id": "usr_123", "name": "Sarah Chen" },
      "leaveType": { "id": "lt-123", "name": "Vacation" },
      "startDate": "2026-05-04",
      "endDate": "2026-05-08",
      "departmentId": "team-abc",
      "locationId": "location-xyz"
    }
  ],
  "dateRange": { "from": "2026-05-04", "to": "2026-05-10" },
  "totalMatches": 1,
  "truncated": false
}

Each item carries kind and id, which update_leave and decide_leave accept. An id that does not exist or is not visible to you returns NOT_FOUND.

Does NOT: answer "how many days does X have left" (use get_user_details), or show leave your wallchart would not show you.

get_holidays

Scope: mcp:workspace:read · Minimum role: any

Public holidays for a calendar year, per location. Omit location to get every location's holidays you can see, each row tagged with its location. Regular employees can only read their own location's holidays.

ParameterTypeDescription
yearinteger (optional)Calendar year, for example 2026. Defaults to the current year in the location's timezone.
locationstring (optional)Location name (partial match) or id. Omit to include every location you can see.

Example request

{ "year": 2026, "location": "Belgrade" }

Example response

{
  "year": 2026,
  "holidays": [
    { "name": "Statehood Day", "date": "2026-02-15", "locationId": "location-xyz", "locationName": "Belgrade Office" }
  ],
  "totalMatches": 1
}

Does NOT: return personal leave (use get_leaves). Asking for another location as a regular employee returns FORBIDDEN.

get_workspace_settings

Scope: mcp:workspace:read · Minimum role: any

Reads workspace configuration: leave policies per location, automations, notifications, and scheduled reports. Items keep their Vacation Tracker ids, so they can be passed back to manage_leave_setup and manage_automations.

ParameterTypeDescription
include("leavePolicies" | "automations" | "notifications" | "scheduledReports")[] (optional)Sections to read. Defaults to all four.
locationstring (optional)Location name (partial match) or id. Limits leave policies to that location. Regular employees and approvers cannot look locations up by name: leave it out to get the policies of your own location.
automationTypestring (optional)Read only this automation type: blackout-period, maximum-users-away, seniority-entitlement, entitlement-by-role, probation-period, leave-duration-limit, or leave-request-notice-period. Defaults to every type.

Example request

{ "include": ["leavePolicies", "automations"], "location": "Belgrade", "automationType": "blackout-period" }

Example response

{
  "leavePolicies": [
    {
      "location": { "id": "location-xyz", "name": "Belgrade Office" },
      "policies": [{ "leaveTypeId": "lt-123", "isActive": true, "isApprovalRequired": true }]
    }
  ],
  "automations": {
    "blackout-period": [{ "id": "auto-1", "name": "Year-end freeze" }]
  }
}

Sections your role cannot view are marked unavailable. Everyone gets the leave policies of their own location, listed first for every role except Admin. Policy quotas are location defaults; a person's own quota and balance are in get_user_details. Location admins also get the policies of the locations they manage; other locations are skipped.

Does NOT: change settings (use manage_leave_setup and manage_automations).

Write tools

Write tools wait for Vacation Tracker to finish the change before answering. See Write answers for the three possible outcomes.

request_leave

Scope: mcp:leaves:write · Minimum role: any

Requests leave or logs TOIL (time off in lieu) for the caller or someone else. Use mode: "preview" first to show the user the working days, balance before and after, blocking errors, automation warnings, and whether approval is needed; submit only after they confirm, unless they already confirmed the exact request. Booking for someone else needs approver or admin rights over that person.

ParameterTypeDescription
mode"preview" | "submit" (required)preview checks the request without saving it; submit books it.
kind"leave" | "toil" (optional)Defaults to "leave". "toil" logs time off in lieu earned.
userstring (optional)Whose leave: name, email, or user id. Defaults to "me".
leaveTypestring (required)Leave type name or id.
startDatestring (required)First day, YYYY-MM-DD.
endDatestring (optional)Last day, YYYY-MM-DD. Defaults to startDate.
partDay"half_day" | "hours" (optional)Single-day part-day leave: half_day with startTime, lasting half of the person's working hours that day (4 hours when the day has none), as in the Vacation Tracker app; or hours with startTime and endTime. Omit for full days.
startTimestring (optional)HH:MM, required with partDay: "half_day" or "hours".
endTimestring (optional)HH:MM, only with partDay: "hours". Must be after startTime.
reasonstring (optional)Reason for the leave.
overrideAutomationsstring[] (optional)Automation warning codes to override. Only after showing the warnings to the user and getting their explicit agreement. Leave only.

Example request (preview)

{ "mode": "preview", "leaveType": "Vacation", "startDate": "2026-05-04", "endDate": "2026-05-08" }

Example response (preview)

{
  "canSubmit": true,
  "blockingErrors": [],
  "notes": [],
  "automationWarnings": [],
  "workingDays": 5,
  "balance": { "leaveType": "Vacation", "remainingBefore": 12, "remainingAfter": 7, "unit": "days" },
  "approvalRequired": true,
  "reasonRequired": false
}

Example request (submit)

{ "mode": "submit", "leaveType": "Vacation", "startDate": "2026-05-04", "endDate": "2026-05-08" }

Example response (submit)

{
  "status": "completed",
  "leave": { "id": "leave_789", "leaveTypeId": "lt-123", "startDate": "2026-05-04", "endDate": "2026-05-08" }
}

When you book for someone else, balance problems that would block the owner are reported under notes instead of blockingErrors, exactly as the web app treats them; overlaps and zero-day requests still block. If submit returns AUTOMATION_OVERRIDE_REQUIRED, show every warning to the user and resubmit with overrideAutomations listing the warning codes only if they agree.

Does NOT: preview TOIL (submit it directly once the user confirms), edit existing requests (use update_leave), or approve them (use decide_leave).

update_leave

Scope: mcp:leaves:write · Minimum role: any

Edits, cancels, or deletes an existing leave or TOIL request (id from get_leaves). Use cancel for approved or pending leave the owner no longer needs; use delete to remove the request entirely, which is usually an admin action.

ParameterTypeDescription
kind"leave" | "toil" (optional)Defaults to "leave".
idstring (required)The leave or TOIL request id, as returned by get_leaves.
action"edit" | "cancel" | "delete" (required)What to do.
changesobject (optional)Edit only, at least one field: leaveType (name or id), startDate, endDate (YYYY-MM-DD), partDay (startHour, startMinute, endHour, endMinute; null makes it a full-day request), reason.
reasonstring (optional)Cancel only: why the request is cancelled.

Example request

{ "id": "leave_789", "action": "edit", "changes": { "endDate": "2026-05-07" } }

Example response

{ "status": "completed", "leave": { "id": "leave_789", "startDate": "2026-05-04", "endDate": "2026-05-07" } }

A delete answers { "status": "completed" } with no resource.

Does NOT: approve or deny (use decide_leave) or create new requests (use request_leave). An edit with no changes is rejected before anything is sent.

decide_leave

Scope: mcp:leaves:write · Minimum role: Approver

Approves or denies a pending leave or TOIL request. With multi-level approval, an approval may leave the request open until the next approver decides; the answer then carries a message saying so.

ParameterTypeDescription
kind"leave" | "toil" (optional)Defaults to "leave".
idstring (required)The id of the request to decide.
decision"approve" | "deny" (required)Approve or deny.
reasonstring (optional)Note for the requester. Recommended when denying.

Example request

{ "id": "leave_789", "decision": "deny", "reason": "Release week, please pick other dates." }

Example response

{ "status": "completed", "leave": { "id": "leave_789", "startDate": "2026-05-04", "endDate": "2026-05-08" } }

Does NOT: decide requests you are not an approver for (the call returns FORBIDDEN), or edit the request.

manage_user

Scope: mcp:users:write · Minimum role: Location admin

Changes one user. Pass only the fields the chosen action needs; missing fields return MISSING_FIELDS before anything is sent.

ParameterTypeDescription
userstring (required)Name, email, or id of the user to change.
actionstring (required)update_profile, move_location, move_department, activate, deactivate, assign_label, remove_label, make_location_admin, remove_location_admin, set_work_week, reset_work_week, or set_quota.
profileobject (optional)update_profile: name, firstName, lastName, email, startDate, endDate, employeeId, birthday, isAdmin, isNameLocked, locale.
locationstring (optional)move_location: target location name or id.
departmentstring (optional)move_department: target department name or id.
labelstring (optional)assign_label and remove_label: label name or id.
managedLocationsstring[] (optional)make_location_admin: names or ids of the locations the user will manage.
workWeekobject (optional)set_work_week: days (0 = Sunday), optional workHours per day, or a multi-week schedule.
quotaobject (optional)set_quota: leaveType (name or id), amount, and period ("current" or "next").

Example request

{ "user": "Marko", "action": "move_location", "location": "Belgrade Office" }

Example response

{ "status": "completed", "user": { "id": "usr_456", "name": "Marko Petrović", "locationId": "location-xyz" } }

Does NOT: create or invite users, or change users outside your reach (location admins manage only their locations' users).

manage_organization

Scope: mcp:workspace:write · Minimum role: Location admin

Creates, updates, or deletes departments, locations, location holidays, and labels, or replaces a location's work week. People and locations can be given by name, email, or id.

ParameterTypeDescription
entity"department" | "location" | "location_work_week" | "holiday" | "label" (required)What to change.
action"create" | "update" | "delete" (required)location_work_week only supports update.
targetstring (optional)Name or id of the record to update or delete; for holidays, the holiday id.
locationstring (optional)Location name or id. Required for holiday and location_work_week.
fieldsobject (optional)Values to set, under the key matching entity. Required for create and update.

Fields per entity:

  • department: name, approvers (user plus optional levels; required on create, replaces all approvers on update), users (replaces all members), parent, isDefault, watchersToNotify, keepLeaveRequestsOpen.
  • location: name, timezone (required on create), resetQuotas, yearStartMonth, yearStartDay, firstDayOfWeek, isDefault, users, workWeek (create only).
  • location_work_week: days, workHours, resetUserOverrides.
  • holiday: name, startDate (required on create), endDate, isHalfDay, transferIfFallsOnNonWorkingDay, recurring, recurringRules.
  • label: name, color.

Example request

{
  "entity": "holiday",
  "action": "create",
  "location": "Belgrade",
  "fields": { "holiday": { "name": "Labour Day", "startDate": "2026-05-01" } }
}

Example response

{ "status": "completed", "holiday": { "id": "hol-1", "name": "Labour Day", "startDate": "2026-05-01" } }

API v2 validation errors come back with field details. Only Admins manage labels.

Does NOT: manage leave types or policies (use manage_leave_setup) or automations (use manage_automations).

manage_leave_setup

Scope: mcp:workspace:write · Minimum role: Location admin

Creates, updates, or deletes leave types, and enables, updates, or disables a leave type's policy at a location (quota, rollover, approval, accrual, brought-forward expiration, and TOIL settings). enable replaces the whole policy; update changes only the fields sent. The leave type and policy items get_workspace_settings returns can be passed back as fields; read-only keys are ignored.

ParameterTypeDescription
entity"leave_type" | "leave_policy" (required)What to change.
actionstring (required)leave_type: create, update, or delete. leave_policy: enable, update, or disable.
targetstring (optional)leave_type update and delete: leave type name or id.
locationstring (optional)leave_policy: location name or id.
leaveTypestring (optional)leave_policy: leave type name or id.
fieldsobject (optional)Leave type fields (name, color, isActive, calendarStatus, awayStatus, awayStatusIcon, awayStatusMessage, and on create locationIds and initialPolicy) or leave policy fields (quotaUnit, daysPerYear, hoursPerYear, maxRolloverDays, maxRolloverHours, hasUnlimitedDays, isApprovalRequired, isReasonRequired, hideLeaveType, allowLeaveInPast, negativeBalanceAllowed, negativeBroughtForwardAllowed, shortestLeaveInterval, accrual, broughtForwardExpiration, toil, resetUserOverrides).

Example request

{
  "entity": "leave_policy",
  "action": "update",
  "location": "Belgrade",
  "leaveType": "Vacation",
  "fields": { "daysPerYear": 25 }
}

Example response

{ "status": "completed", "leavePolicy": { "leaveTypeId": "lt-123", "isActive": true } }

Only Admins manage leave types. Confirm with the user before deleting a leave type or disabling a policy.

Does NOT: change individual users' quotas (use manage_user with set_quota).

manage_automations

Scope: mcp:workspace:write · Minimum role: Location admin

Creates, updates, or deletes an automation (blackout periods, maximum users away, seniority entitlement, entitlement by role, probation periods, leave duration limits, notice periods), a leave notification, or a scheduled report. Use get_workspace_settings to find existing items and their ids.

ParameterTypeDescription
entity"automation" | "notification" | "scheduled_report" (required)What to manage.
action"create" | "update" | "delete" (required)What to do.
automationTypestring (optional)Required when entity is automation: blackout-period, maximum-users-away, seniority-entitlement, entitlement-by-role, probation-period, leave-duration-limit, or leave-request-notice-period.
idstring (optional)Id of the item. Required for update and delete.
fieldsobject (optional)The API v2 request body. On update send only the fields to change. Required for create and update.

Reference fields in fields accept names or ids and are resolved to ids: locationIds, departmentIds, labelIds, labelId, and managersReminders (user names or emails). The tool's input schema lists the main fields per automation type, notification frequency, and report type.

Example request

{
  "entity": "automation",
  "action": "create",
  "automationType": "blackout-period",
  "fields": {
    "name": "Year-end freeze",
    "leaveTypeIds": ["lt-123"],
    "startDate": "2026-12-20",
    "endDate": "2026-12-31",
    "recurring": true,
    "locationIds": ["Belgrade Office"]
  }
}

Example response

{ "status": "completed", "automation": { "id": "auto-1", "name": "Year-end freeze" } }

Only Admins manage automations. An unknown automationType is rejected before anything is sent; API v2 validation errors come back with field details.

Does NOT: manage leave policies (use manage_leave_setup) or webhooks.

Write answers

Every write tool answers in one of three ways:

  • Completed: the change is done. The result carries "status": "completed" and the updated resource under its name (leave, toil, user, holiday, and so on). Deletes answer { "status": "completed" }.

  • Pending: Vacation Tracker accepted the change but is still processing it after about 20 seconds:

    {
      "status": "pending",
      "operationId": "mcp_6f1c…",
      "message": "Vacation Tracker accepted the change and is still processing it. Check the result with the matching read tool in a few seconds; do not submit it again."
    }

    Check the outcome with the matching read tool (for example get_leaves). Do not resubmit: the change is already queued.

  • Failed: an error result, described below. Nothing was changed.

Errors

A tool failure is a normal tool result with isError: true. Its JSON text (and structuredContent) carries an error object with a stable code, a human-readable message, a resolution the assistant can follow, and optional details, plus a support block:

{
  "error": {
    "code": "MISSING_FIELDS",
    "message": "The move_location action needs: location.",
    "resolution": "Ask the user for location and call manage_user again with them.",
    "details": { "missing": ["location"] }
  },
  "support": {
    "email": "developers@vacationtracker.io",
    "feedback_url": "https://www.votito.com/public/ideabank/..."
  }
}

Ambiguous names return candidate matches so the assistant can retry with an id. Error codes come from the API v2 error catalog plus a few MCP ones; see Troubleshooting for the common codes.

JSON-RPC errors are reserved for protocol problems (malformed requests, unknown methods); authentication and plan problems are HTTP errors. See Troubleshooting.

Unavailable sections

Read tools that return several sections (get_workspace_info, get_user_details, get_workspace_settings) do not fail when your role cannot read one of them. That section comes back as:

{ "unavailable": "Your role cannot view this in Vacation Tracker" }

The other sections are returned normally. The assistant should tell the user that part is not visible to them rather than guess.

On this page