Skip to main content
Glama
dragosh29

Edays MCP server

by dragosh29

Edays MCP server

An MCP server that lets Claude, ChatGPT and other MCP clients work with an Edays absence management system: employees, absences, absence types, entitlement balances, rotas, public holiday and custom day patterns and groups, and (when enabled) booking, updating and deleting absences. It is built from the public API V2 documentation at developer.e-days.co.uk. Edays publishes no OpenAPI document for API V2, only JSON examples, so the tests validate against schemas written from those examples and check the schemas against the examples themselves.

Once it's connected, an HR or line manager on the system can ask things like:

  • "Who is off next week, and is anything still waiting for approval?"

  • "How much holiday does Dana Barrett have left this year, and how many sick days has she had in the last three months?"

  • "Which team is Willie in, and who approves his leave?"

  • "What rota is Priya on, and which public holiday pattern applies to her?"

  • With writes enabled: "Book Dana two days' holiday on 13 and 14 October." / "Approve Willie's pending holiday request."

Tools

Tool

What it does

API calls

list_users

Every user on the system, filtered locally by part of the name or partner ID; leavers skipped unless asked. The endpoint is not marked as paged in the documentation; the paging headers are read anyway and further pages fetched if the system does page it, with a paging note saying so. Returns partner_id (the key the other tools take) and edays_id (the GUID absences use).

GET /api/v2/users (and ?page=n if it pages)

get_user

One user by partner ID with their groups and their authorisation hierarchy (step one and two authorisers and alternates).

GET /api/v2/users/{id}/, /groups, /authorisation

list_absences

Absence records system-wide or for one user, with status, start and end, duration and absence type ID, in whole API pages up to max_results, continued with page. Filters date_from, date_to, record_type, absence_type_id, and on the system-wide endpoint user_id, group_id, created_since, modified_since, sent as the documented query parameters.

GET /api/v2/absences, or GET /api/v2/users/{id}/absences with partner_user_id

get_absence

One absence record by GUID.

GET /api/v2/absences/{id}

list_absence_types

Absence types with record type (planned, unplanned) and booking and calendar flags; the API returns Custom Day Groups (5) and Public Holiday Groups (6) from the same endpoint.

GET /api/v2/absencetypes

get_user_entitlements

A user's deducting balances (annual entitlement, transfers, pending, booked, taken, untaken, remaining, per booking period and element), summing balances (year to date, last 6 and 3 months, last 30 days) and entitlement pots.

GET /api/v2/users/{id}/entitlements/deducting, /summing, /pots

get_user_rota

A user's rota assignments with start dates, and their public holiday and custom day patterns, named from the system's lists.

GET /api/v2/users/{id}/rotas, /publicholidays, /customdays, GET /api/v2/lists/rotas, /publicholidays, /customdays

list_public_holidays

The public holiday patterns and custom day patterns held in the system (id and name; API V2 does not expose the dates inside a pattern).

GET /api/v2/lists/publicholidays, /customdays

list_groups

Group types (Country, Location, Team...) and the groups in each, or one type.

GET /api/v2/grouptypes, GET /api/v2/grouptypes/{type}/groups

book_absence

Creates an absence for a user GUID with the documented body (UserId, AbsenceTypeId, Details, Status, StartTime, EndTime, IsOpen). Writes only.

POST /api/v2/absences

update_absence

Fetches an absence and PUTs the documented body with your changes merged in: type, status (the only documented way to approve, reject or cancel through API V2), start, end, open flag, details. Writes only, marked destructive.

GET /api/v2/absences/{id}, PUT /api/v2/absences/{id}, then GET again when the PUT returns no body

cancel_absence

Deletes an absence record with the documented DELETE. Writes only, marked destructive.

DELETE /api/v2/absences/{id}

There are no approve_absence or reject_absence tools because API V2 documents no such endpoints; the "Managing Absences" section says the status of an existing record is changed with PUT /api/v2/absences/{id}, which is what update_absence does with status: "Approved" or "Rejected".

Not covered on purpose: creating, editing, patching and deleting users, marking leavers and reinstating, user settings and email notifications, roles and bulk roles, rota, public holiday and custom day assignment, entitlement adjustments, authorisation hierarchies (writes), user templates, group and group type writes, bulk user-group membership, global entitlement configuration and user balances, SSO certificates and IdP configuration, and the remaining lookup lists.

Related MCP server: mcp-server-personio

Setup

Requires Node 18 or later.

npm install
npm run build

You need API client credentials for your Edays system. As the Authentication section documents: create a dedicated user in Edays, on its Roles tab select the account type Api Client, and generate a Client ID and Client Secret there (the secret is shown once; regenerating it replaces the old one). The server exchanges them for a Bearer access token at https://YOUR-SYSTEM.e-days.co.uk/token (OAuth 2.0 client credentials, form-encoded), which the documentation says is valid for one hour.

YOUR-SYSTEM is the subdomain of the address you sign in at.

Claude Desktop: add this to claude_desktop_config.json:

{
  "mcpServers": {
    "edays": {
      "command": "node",
      "args": ["/absolute/path/to/edays-mcp/dist/index.js"],
      "env": { "EDAYS_SYSTEM": "your-system", "EDAYS_CLIENT_ID": "your-client-id", "EDAYS_CLIENT_SECRET": "your-client-secret" }
    }
  }
}

Claude Code:

claude mcp add edays -e EDAYS_SYSTEM=your-system -e EDAYS_CLIENT_ID=your-client-id -e EDAYS_CLIENT_SECRET=your-client-secret -- node /absolute/path/to/edays-mcp/dist/index.js

Variable

Required

Meaning

EDAYS_SYSTEM

yes, unless EDAYS_BASE_URL is set

The subdomain of your Edays system: acme for https://acme.e-days.co.uk. Letters, digits and hyphens only; anything else (such as the full host name) stops the server at start-up, whether or not EDAYS_BASE_URL is set.

EDAYS_CLIENT_ID

yes

The Api Client user's Client ID, sent in the token request body.

EDAYS_CLIENT_SECRET

yes

The Api Client user's Client Secret, sent in the token request body. Never logged or included in an error message.

EDAYS_ALLOW_WRITES

no

true to register book_absence, update_absence and cancel_absence. Off by default.

EDAYS_BASE_URL

no

Overrides the system URL, e.g. https://acme.e-days.co.uk. Used by the tests.

Safety defaults

  • Read-only unless EDAYS_ALLOW_WRITES=true. Read tools carry the MCP readOnlyHint annotation; update_absence and cancel_absence are marked destructive (a PUT replaces the whole record, and DELETE removes it); book_absence is not.

  • Employee records are third-party personal and HR data. By default a user's Email, Login, HomeEmail, HomePhone, WorkPhone, WorkPhoneExt, HomeAddress, NextOfKin, NextOfKinContactDetails, Dob, PayrollNumber, EmployeeNumber, SsoUserId, ClientProvidedId and AnnualPay are not returned, an absence's PayrollNumber and EmployeeNumber are not returned, and an entitlement's Login is not returned. In every other free-text field (names, job titles, absence type, entitlement, rota, pattern, group and group type names, and Edays' own error messages, including the excerpt of a non-JSON body) email addresses are replaced with [email redacted], phone-number-like sequences with [phone redacted] and UK postcodes with [postcode redacted]. Dates inside free text are not redacted (a date in a rota or group name is far more likely to be a schedule than a date of birth; the Dob field itself is withheld). include_contact_details=true returns all of it as stored. Names and partner IDs are always returned as stored, including the authoriser partner IDs on get_user: the partner ID is the key every user endpoint is addressed by, so on a system whose partner IDs are login email addresses those addresses appear by default. The phone match is a heuristic: it covers international numbers written with + or 00 (including the +44 (0)7700 … form), UK numbers with a bracketed area code such as (020) 7946 0958, and UK-style 0… numbers of 9 to 11 digits with spaces, dots or hyphens between groups; other digit strings that happen to start with 0 are redacted too, while GUIDs, numeric IDs and timestamps are left alone. The postcode match is upper case only (CF64 3DH, SW1A 1AA, EC1A1BB), so another code written in that shape would be redacted too. Bank and payment details do not exist in API V2.

  • The access token is held in memory only, refreshed a minute before its documented one-hour expiry, and fetched afresh once when a call answers 401; it never appears in logs or error messages, and neither does the client secret. Only a JSON message from the token endpoint is ever passed on, never its raw body: a 200 without an access_token where the server looks is reported with the body's top-level key names (a bare value could be the token under another key), and a 5xx from POST /token is retried like a GET (fetching a token is idempotent) and reported without the gateway's HTML.

  • list_absences returns whole API pages, never part of one. The page size sent is min(100, max_results), and a call stops before a page that could take it past max_results (allowing for a shorter last page when the total says fewer remain), so count can be below max_results. The result carries page_size, next_page and a note saying to call again with that page and the same max_results, because page numbers only line up for one page size. The end of the list is judged from the records actually received against the documented edays-pagination-total header, not from the edays-pagination-page-size header alone: no maximum page size is documented, and a cap that still echoed the requested size would otherwise end a list after one page. A continuation call that starts on a short page therefore confirms the end with one extra request, which returns an empty page.

  • IDs are checked before any call is made: partner IDs may be any single path segment (spaces included, as in the documented "Phil Jones"; no slashes, control characters or leading or trailing spaces; at most 200 characters) and are URL-encoded; absence and user GUIDs must look like GUIDs; absence type IDs must be positive integers. The four names documented directly under /api/v2/users/ (autosetupauthorisers, recalculateauthorisationhierarchy, authorisation, applyRotaToUsers) are refused as user IDs in any letter case: the first two change data when fetched with GET (they add users to the authoriser role and reassign pending requests), so a read-only tool must never reach them. date_from/date_to take YYYY-MM-DD (or the documented YYYYMMDD) and real calendar dates only. Absence times take YYYY-MM-DD HH:MM or ISO 8601 with a T and are sent in the documented YYYY-MM-DD HH:MM form; no time zone is accepted because none is documented. 12/08/2026, a bare year, 25:00 and a trailing Z are refused locally. book_absence and update_absence refuse an end before the start, update_absence refuses a call with no change, and list_absences refuses the system-wide filters together with partner_user_id, because the per-user endpoint documents only recordtype, absencetype, datestart and dateend.

  • Edays does not document a rate limit anywhere on the API V2 page. Requests are spaced 250 ms apart (about four per second). A 429 is retried at most twice for any method, including POST /api/v2/absences, on the assumption that a rate-limited request was not processed. The retry waits for Retry-After (whole or fractional seconds, or an HTTP-date; 2 s then 4 s when the header is absent or unreadable). Each wait is capped at 10 seconds so a single request stays well under the MCP client's default 60-second request timeout: if Edays asks for a longer wait the call gives up at once and the message says how long to wait. The cap is per request, not per tool call: list_absences can make up to 20 requests in one call and get_user_rota up to six, so a long run of 429s across those could still exceed the client's timeout.

  • 502, 503 and 504 are retried the same way for GET and for POST /token only; when all three attempts fail the error says the service may be unavailable and to try again in a few minutes, without the gateway's HTML. A POST, PUT or DELETE on an absence is never retried after a gateway error, because the request may already have been processed and a retry could book the same absence twice; the error says to check with list_absences or get_absence before repeating it.

  • A 200 whose body is not JSON (a proxy or a login page in the way) is reported as an error naming EDAYS_SYSTEM / EDAYS_BASE_URL, never as an empty list or an empty record; the 60-character excerpt of the body in that message goes through the same contact redaction as everything else.

  • Rejected client credentials produce a message that says which variables to fix and where the credentials come from; a 401 that survives a token refresh says to check the Api Client user; a 403 says the user's roles do not allow the operation; a 400 passes on Edays' message and, if present, its ModelState validation errors field by field.

Tests

npm test

The test suite:

  1. Extracts the documentation into spec.json on the first run: test/extract-examples.mjs fetches https://developer.e-days.co.uk/, drops the parts of the page that sit in HTML comments (they are not published), and records every "Resource URL" with its "Supported HTTP Methods" line and every "Example ... Request/Response" JSON block (119 examples on 90 resources; 6 examples are not valid JSON on the page and are recorded as such; a repeated Resource URL continues the same resource, and the one resource with no methods line, the bulk authorisation PATCH, takes its method from its example and is marked as inferred). The JSON schemas in test/schemas.mjs were written by hand from those examples (every documented key required, no other keys, types as shown); the first check validates each schema against the documented example it came from (26 examples), confirms every endpoint this server calls is documented with that method, that the two data-changing GET endpoints under /api/v2/users/ are indeed documented as such, and that the mock's client ID and secret are obviously fake values that do not appear on the documentation page. The second check validates every fixture record against the schemas.

  2. Starts a local mock of the system: POST /token with the documented form-encoded client-credentials grant (answering the documented one-element array, or a plain object when told to), a 400 invalid_client for wrong credentials, a 401 for any API call without a token the mock issued, the endpoints used here with the documented page/pagesize paging and the four edays-pagination-* headers (absences capped at 4 per page so lists span several pages; on request the page-size header echoes the requested size instead, and GET /api/v2/users pages too), 404s for unknown IDs, POST /api/v2/absences answering 201 (with the record, or with no body when told to), 204 on PUT and DELETE, injected failures on any endpoint including /token, and a one-off 429 on the first GET /api/v2/absencetypes. The third check validates the mock's token and list, detail and write responses against the schemas.

  3. Starts the built server and drives it over stdio with the official MCP client: 29 checks (32 in the whole suite) covering every tool, tool annotations, the token fetched with the documented body before the first API call and reused on every call as a Bearer header, a revoked token refreshed once with the call retried, a token near expiry refreshed before it expires, the object-shaped token response, page-based pagination stopping at edays-pagination-total and returning whole pages only (following the tool's own continuation notes from a first page and from a later one yields every record exactly once; max_results equal to the total fetches the short last page; a page-size header that echoes the requested size while serving fewer does not end the walk early), list_users following the paging headers when GET /api/v2/users pages and saying so, every documented list_absences filter passed through exactly (datestart/dateend from YYYY-MM-DD and YYYYMMDD, recordtype, absencetype, userId, groupId, dateCreated, dateModified) with bad dates (including a mix of the two date forms) and IDs refused before any call, the four reserved endpoint names under /api/v2/users/ (read from the documentation, in any letter case) refused by every user tool with no request made, a partner ID with a space sent as one encoded segment and the single-user URL sent with its documented trailing slash, the per-user endpoint with its three filters and the refusal of the others, redaction of contact and HR details and of emails, phone numbers and postcodes typed into names, job titles, rota and group names by default and their return on request, authoriser partner IDs returned as stored, absence types named for record types 1, 2, 5 and 6, entitlement balances with booking period and time unit names, rotas and patterns named from the lists with the lists cached, the POST and PUT bodies validated against the schemas from the documented POST and PUT examples (times normalised, Details sent empty on a booking when omitted and left out of a PUT when not given), the DELETE, the 429 retry waiting for Retry-After in the seconds, fractional-seconds and HTTP-date forms, giving up after three attempts on a persistent 429 and at once on a Retry-After above the cap, a 429 on POST retried once, a 502 retried for GET and never for POST, PUT or DELETE, a 401 that survives a token refresh reported with the Api Client advice, a GET failing three times with 503 reported with advice and without the gateway HTML, a 503 on POST /token retried and reported the same way, a 429 on POST /token without Retry-After retried after the 2 s fallback, a 200 from POST /token without a token reported with key names only (never a value), Edays' own error text passed on with contact details redacted and ModelState errors listed, the 403 message, a 200 with a non-JSON body reported as an error with the excerpt redacted, the write gate with the variable unset and set to false, the 400 for wrong client credentials naming the variables without echoing the secret, a bad EDAYS_SYSTEM or a missing secret stopping the server at start-up, EDAYS_SYSTEM alone producing https://<system>.e-days.co.uk (read from the start-up line; no request is made to that host), and that every request either carried the documented form body to /token with no Authorization header or carried a Bearer token the mock issued to a documented method and path, none of them a reserved endpoint name in place of a user.

Status

This is a working prototype. It has not yet been run against the live API, because it was built without an Edays system or Api Client credentials. Everything below is taken from the documentation page and should be confirmed on a real system:

  • The token endpoint: whether the response is the one-element array shown in the documented example or a plain object (both are accepted), what a wrong Client ID or Secret answers (the mock uses the OAuth 2.0 400 invalid_client; the page documents nothing), and whether the API answers 401 for an expired token (the server refreshes once on a 401 and a minute before the documented hour either way).

  • Whether GET /api/v2/users/{partnerUserId} returns EdaysId. The documented example omits it while the list example has it, so get_user reports no edays_id and list_users does; the mock follows the examples. The server sends the documented URL with its trailing slash (the mock accepts both forms).

  • Whether GET /api/v2/users pages. The page marks only the two absence lists as paged, but that marker is not exhaustive (the /usergroups endpoint says in prose that it pages at 500), so list_users reads the paging headers and fetches further pages if the first answer is short of edays-pagination-total; untested against a real system, as is how long the call takes on a large one. Whether it includes leavers (IsLeaver is filtered locally), and whether a real record ever carries null where the examples show empty strings (the formatter copes; the schemas allow null only for EmploymentStartDate and ContinuousStartDate, where the examples show it).

  • Whether a partner ID with a space ("Phil Jones" in the documented authorisation example) really is addressable as /api/v2/users/Phil%20Jones/; the server accepts and encodes it.

  • Paging: the query parameter is written pageSize in the Paging text and pagesize in every example; the server sends pagesize. No maximum page size is documented (the default is 50, the paging example shows 500); the server asks for at most 100 and ends the list when the records received reach edays-pagination-total, taking a page shorter than requested as the end only when that header is missing. What a page past the end returns is not documented (the mock answers 200 with an empty list).

  • The datestart/dateend filters: whether a record must start inside the range or merely overlap it, and whether the bounds are inclusive (the mock uses inclusive overlap). The formats of dateCreated and dateModified and whether groupId takes the group's GUID or its partner ID are not documented; those three values are sent exactly as given. userId is assumed to be the GUID shown as UserId on absence records and EdaysId on users (the examples show the same value for both).

  • POST /api/v2/absences: whether it answers 200 or 201, what the body is (the page documents neither; the tool formats a body that looks like an absence record and passes anything else through), and whether a Location header is sent. Which Status values it accepts on creation (the example uses Pending; the tool offers the seven texts from /api/v2/lists/recordstatus) and whether the caller's roles allow booking for others.

  • PUT /api/v2/absences/{id}: whether it answers 200, 201 or 204 (all documented; the tool re-reads the record when no body comes back), and what happens to Details when the key is omitted, since GET does not return it. Whether setting Status to Approved, Rejected or Cancelled through PUT really approves, rejects or cancels the request as the UI would, including notifications.

  • DELETE /api/v2/absences/{id}: whether the record is removed or kept as Cancelled, and what the response is beyond the documented 204.

  • The time zone of StartTime/EndTime (none is documented; the server sends the values as given) and the "midnight to midnight" rule for day-based absences quoted from the Managing Absences section.

  • The record type discriminator names 5 (Custom Day Group) and 6 (Public Holiday Group) come from the Absence Types section; 1 and 2 from the lists section. Booking period and time unit names come from the documented /api/v2/lists/bookingperiods and /api/v2/lists/timeunits examples and are mapped locally rather than fetched.

  • The documented example for GET /api/v2/users/{partnerUserId}/customdays is not valid JSON ({ [ "Pattern": 3 ] }); the server reads that endpoint like publicholidays ([{"Pattern": n}]).

  • Error bodies: none are documented. The server reads Message, message, error, error_description, ExceptionMessage and ModelState and redacts contact details from them regardless.

  • Rate limits: nothing is documented anywhere on the page (the word does not appear), so the 250 ms spacing here is a guess on the polite side, and a 429 on POST is retried on the assumption that a rate-limited request was not processed.

  • Which roles the Api Client user needs for each endpoint; a 403 is reported with that explanation but the page does not describe the role model.

Going to production

This version runs locally over stdio, with the customer's own Api Client credentials. For customers to connect from claude.ai or ChatGPT without handling credentials, the next step is a remote server (Streamable HTTP) behind OAuth, hosted by Edays, and then a listing in the Claude and ChatGPT connector directories.

Licence

MIT. Built by Alexandru Dragoș (alexandru.dragos96@gmail.com) with an AI agent (Claude) working under his direction.

Available Tools

9 tools
get_absenceGet an absenceA
Read-only

One absence record by its GUID: user, absence type, status, start and end, duration, time unit, created and modified dates. Payroll and employee numbers only with include_contact_details.

ParametersJSON Schema
NameRequiredDescriptionDefault
absence_idYesAbsence ID (GUID)
include_contact_detailsNoInclude payroll and employee numbers, and stop redacting email addresses, phone numbers and UK postcodes typed into names and other text

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=true, so safety is covered. The description adds real behavioral context beyond that: payload/employee numbers and unredacted contact data appear only when include_contact_details is set, which tells the agent the default response is redacted.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two compact sentences with the identifier and returned fields front-loaded, then the conditional flag behavior. The enumerated field list is dense but each item is informative; nothing is wasted.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description usefully enumerates the returned fields and flags the conditional redaction, which is the key behavioral detail. It is nearly complete; only error/not-found behavior and any pagination-free nature remain unaddressed.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so both parameters are already fully documented in the schema (including the redaction behavior of include_contact_details). The description restates that linkage but adds no syntax, format, or edge-case detail beyond what the schema provides; baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource+retrieval key ('One absence record by its GUID') and enumerates the returned fields, which cleanly separates it from the sibling list_absences. An agent can tell immediately this is a single-record fetch rather than a filtered list.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The phrase 'One absence record by its GUID' implies the single-record retrieval use case and contrasts implicitly with list_absences, but it never states when to prefer this over listing or what happens if the GUID is unknown. No explicit alternatives or exclusions are given.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_userGet a userA
Read-only

One employee by partner ID, with the groups they belong to (team, location and so on) and their authorisation hierarchy (who approves their requests: step one and step two authorisers and alternates, as partner IDs). Contact and HR details only with include_contact_details.

ParametersJSON Schema
NameRequiredDescriptionDefault
include_groupsNoAlso fetch the user's groups (one extra call)
partner_user_idYesThe user's partner ID (partner_id from list_users)
include_authorisersNoAlso fetch the user's authorisation hierarchy (one extra call)
include_contact_detailsNoInclude email, login, phones, address, next of kin, date of birth, payroll and employee numbers, SSO and client-provided IDs and annual pay, and stop redacting email addresses, phone numbers and UK postcodes typed into names and other text

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations cover read-only and open-world safety, but the description adds genuinely useful behavior: contact and HR details are withheld/redacted unless include_contact_details is set. That privacy default is a meaningful trait not carried by the annotations. It doesn't cover error behavior for missing users, keeping it from a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single dense sentence that front-loads the core identity (one employee by ID) before enumerating the optional payloads. No filler, though the heavily parenthetical authorisation clause is slightly harder to parse than it needs to be.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 4-parameter read with no output schema, the description adequately describes what comes back and the default redaction behavior. It omits what happens when the user is not found or when include flags are false, which would fully close the gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so all four parameters (including the detailed include_contact_details redaction note) are already documented. The description restates the contact-details gating and the composite meaning of the group/authoriser flags, adding only marginal value. Baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (get) and resource (one employee by partner ID) and enumerates the payload: groups and authorisation hierarchy. This cleanly distinguishes it from list_users and other siblings at a glance.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by the singular scope ('One employee by partner ID'), which signals this is the single-lookup counterpart to list_users. However, it never explicitly says when to prefer this over list_users or that partner IDs come from list_users, leaving routing to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_user_entitlementsGet a user's entitlement balancesB
Read-only

Entitlement balances for one user. Deducting entitlements (holiday and the like, which count down: annual entitlement, transfers, pending, booked, taken, untaken, remaining, per booking period and element) and summing entitlements (sickness and the like, which count up: year to date, last 6 and 3 months, last 30 days), plus the entitlement pots they belong to. Booking period and time unit numbers are named from the documented lists (Current, MinusOne, PlusOne; Days, Minutes, Hours).

ParametersJSON Schema
NameRequiredDescriptionDefault
include_potsNoAlso fetch the user's entitlement pots (one extra call)
partner_user_idYesThe user's partner ID
include_contact_detailsNoInclude the user's login, and stop redacting email addresses, phone numbers and UK postcodes typed into names and other text

TDQS

B3.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only cover the safety profile (readOnlyHint, openWorldHint), yet the description adds real behavioral context: which balance types count down (annual, transfers, pending, booked, taken, untaken, remaining) versus up (sickness: YTD, 6/3 months, 30 days), that pots are also returned, and how booking period/time unit values are named. The include_pots extra-call cost is only in the schema, not the description.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The single dense paragraph is not front-loaded and packs enumerations into parentheticals, making it hard to parse. Some length is defensible since there is no output schema, but the structure wastes that budget.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description does carry the return-shape burden and enumerates balance fields and pots reasonably well. However, it omits error/missing-user behavior and the booking-period vocabulary it introduces maps to no visible input, leaving the agent unsure whether those values are parameters or output fields.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so all three parameters are already documented, establishing the baseline of 3. The description's 'booking period and time unit numbers' sentence references values that do not correspond to any input parameter, which adds confusion rather than meaning for the actual parameters.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening line 'Entitlement balances for one user' names a specific resource and scope, and the singular 'one user' contrasts implicitly with the list_* siblings. It is clear what the tool fetches, though it never states the verb (get/read) explicitly and buries the scope in a long second sentence.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no when-to-use guidance: nothing tells the agent when to prefer this over get_user, get_user_rota, or get_absence. The only hint is the phrase 'for one user', leaving the agent to infer the trigger condition.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_user_rotaGet a user's rota, public holidays and custom daysB
Read-only

The rota (working pattern) assignments of one user with their start dates, named from the system's rota list, and with include_patterns the public holiday and custom day patterns applied to the user, named from those lists. Up to six API calls.

ParametersJSON Schema
NameRequiredDescriptionDefault
partner_user_idYesThe user's partner ID
include_patternsNoAlso fetch the user's public holiday and custom day patterns
include_contact_detailsNoInclude nothing extra (rota and pattern names are the system's own text), and stop redacting email addresses, phone numbers and UK postcodes typed into names and other text

TDQS

B3.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=true, so safety is covered. The description adds genuinely useful behavioral context beyond that: it discloses that the call may fan out to 'up to six API calls,' which matters for latency/cost planning, and that returned names come from the system's own rota and pattern lists.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single long, dense, run-on sentence that is grammatically hard to parse ('named from the system's rota list, and with include_patterns the public holiday...'). It is not padded with filler, but the structure obscures rather than front-loads the meaning.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only tool with no output schema and fully documented parameters, the definition covers what the agent needs: what is returned, the conditional pattern fetch, and the API-call cost. It is slightly incomplete on return shape, but annotations cover safety and the schema covers inputs, so little is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all three parameters fully, and the baseline is 3. The description does reinforce what include_patterns pulls in (public holiday and custom day patterns), but it adds little about include_contact_details or partner_user_id beyond what the schema states.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: it returns one user's rota (working pattern) assignments with start dates, optionally plus public holiday and custom day patterns. It is clear what the tool retrieves, but it makes no attempt to distinguish itself from siblings like get_user_entitlements or get_user, so it falls short of the 5 mark.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no explicit when-to-use guidance, no statement of prerequisites, and no mention of any alternative tool the caller might want instead. The only implied usage cue is the include_patterns toggle, which is already documented in the schema rather than used to route the agent.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_absencesList absencesA
Read-only

Absence records (holiday, sickness and other absence types) across the system or for one user, with status, start and end, duration and the absence_type_id (name it with list_absence_types). Filters are sent to the API as documented: date_from/date_to (datestart/dateend), record_type (1 planned, 2 unplanned), absence_type_id, and on the system-wide endpoint also user_id (Edays GUID), group_id, created_since and modified_since. With partner_user_id the per-user endpoint GET /api/v2/users/{id}/absences is used instead, which documents only the first three filters. Whole API pages of min(100, max_results) records are returned; the note says how to continue. Payroll and employee numbers only with include_contact_details.

ParametersJSON Schema
NameRequiredDescriptionDefault
pageNo1-based page to start from (for continuing a previous call)
date_toNoEnd of the date range, YYYY-MM-DD (sent as dateend=YYYYMMDD)
user_idNoOnly records for this Edays user GUID (the userId filter; not with partner_user_id)
group_idNoOnly records whose user is in this group (the groupId filter, e.g. a location; not with partner_user_id). The documentation does not say whether this is the group's GUID or partner ID; the value is sent as given
date_fromNoStart of the date range, YYYY-MM-DD (sent as datestart=YYYYMMDD)
max_resultsNoMaximum number of records to return; also sets the API page size (up to 100)
record_typeNo1 for planned absences (holiday), 2 for unplanned (sickness); omitted returns all
created_sinceNoThe dateCreated filter: 'filter out records created before' this value. Its format is not documented; the value is sent as given (the other date filters use YYYYMMDD). Not with partner_user_id
modified_sinceNoThe dateModified filter: 'filter out records modified before' this value. Format not documented; sent as given. Not with partner_user_id
absence_type_idNoAbsence type ID (integer, see list_absence_types)
partner_user_idNoOnly this user's absences, via GET /api/v2/users/{partnerUserId}/absences
include_contact_detailsNoInclude payroll and employee numbers, and stop redacting email addresses, phone numbers and UK postcodes typed into names and other text

TDQS

A3.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond the readOnly/openWorld annotations: it discloses pagination behavior (whole API pages of min(100, max_results)), a continuation note, which filters are supported on each endpoint, the documented format-mismatch caveat for date filters, and the redaction behavior governed by include_contact_details. It falls short of explaining what happens when conflicting filters are combined or result truncation specifics.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single dense paragraph covering many facts — record fields, filter routing, endpoint switch, pagination, and redaction — with no headings or bullet grouping. Every sentence carries weight, but the sentence structure is overloaded and the key filtering distinction is buried mid-paragraph.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 12-parameter, filter-heavy list tool with no output schema and annotations covering only read-only/open-world status, the description supplies the routing, pagination, and redaction context an agent needs. It is not fully complete: it omits the default max_results cap behavior when results exceed the page, and does not confirm whether filters can be freely combined.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema already documents the 12 parameters; the description adds real routing value by mapping date_from/date_to to datestart/dateend, distinguishing the system-wide endpoint filters (user_id, group_id, created_since, modified_since) from the three filters on the per-user endpoint, and flagging the date-format uncertainty.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (list) and resource (absence records), names the covered absence types, and identifies the endpoint used when partner_user_id is supplied. It does not directly distinguish from the sibling get_absence, though the singular/plural naming and endpoint detail make the collection-level purpose evident.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage guidance is implied rather than explicit: it notes that absence_type_id should be resolved via list_absence_types and that partner_user_id switches to the per-user endpoint, but it never states when to use list_absences versus get_absence or list_users. No exclusions, prerequisites, or typical scenarios are given.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_absence_typesList absence typesA
Read-only

The absence types configured on the system (Holiday, Sickness and so on) with their record type (1 planned, 2 unplanned) and booking and calendar-visibility flags. The API also returns Custom Day Groups (record type discriminator 5) and Public Holiday Groups (6) from the same endpoint; they are kept unless record_type filters them out.

ParametersJSON Schema
NameRequiredDescriptionDefault
record_typeNoOnly types with this record type discriminator: 1 planned, 2 unplanned, 5 custom day groups, 6 public holiday groups
include_contact_detailsNoInclude nothing extra (type names are the system's own text), and stop redacting email addresses, phone numbers and UK postcodes typed into names and other text

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already establish readOnlyHint=true/openWorldHint=true, so the safety profile is covered. The description adds genuine behavioral context beyond that: additional record-type discriminators (5 and 6) surface from the same endpoint and are retained unless record_type filters them, which is a non-obvious return-set behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, front-loaded with the primary resource and then the endpoint quirk; no filler or repetition of the schema.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only, two-optional-parameter list tool with full schema coverage and no output schema, the description covers scope and the surprising sibling data well. Minor gap: no explicit note on ordering or response shape, though that is not required without an output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3; the description nevertheless adds meaning by explaining that record_type filtering removes the extra group types from the result rather than merely narrowing a column. It also confirms type names are the system's own text, complementing the include_contact_details parameter.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('absence types configured on the system') and enumerates what is returned: record type discriminator plus booking and calendar-visibility flags. It also clarifies the non-obvious scope (Custom Day Groups and Public Holiday Groups come from the same endpoint), which helps an agent distinguish this from siblings like list_public_holidays.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by the content it lists, but there is no explicit when-to-use guidance or naming of alternatives (e.g., list_public_holidays, list_groups). An agent can infer this is the reference/config lookup for absence types, but nothing routes it against siblings.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_groupsList group types and groupsA
Read-only

The group types on the system (Country, Location, Team and so on) and the groups within each (England, Nottingham, Programming), with partner IDs. One call for the types plus one per type for its groups; give group_type_partner_id to fetch a single type.

ParametersJSON Schema
NameRequiredDescriptionDefault
group_type_partner_idNoOnly this group type's groups (its partner ID, e.g. loc)
include_contact_detailsNoInclude nothing extra (group names are the system's own text), and stop redacting email addresses, phone numbers and UK postcodes typed into names and other text

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations cover the safety profile (readOnlyHint, openWorldHint), and the description adds useful operational context beyond them: the unusual one-call-per-type amplification pattern and the redaction behavior implied for include_contact_details. It stops short of describing pagination or output shape, but the amplification disclosure is the important behavioral trait here.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with what is returned, then the call pattern, then the parameter hint. No padding sentences, though the parenthetical examples make it slightly denser than needed.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, yet the description conveys what comes back (type names, group names, partner IDs) and the multi-call fan-out needed to assemble groups per type. That is enough for an agent to call it correctly, with only pagination/limits left unspecified.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so baseline is 3. The description reinforces that group_type_partner_id narrows to a single type's groups, matching the schema, but adds no format or syntax detail beyond it.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Names a specific resource (group types and the groups within each), gives concrete examples (Country, Location, Team; England, Nottingham, Programming), and states the returned field (partner IDs). This clearly separates it from siblings like list_absence_types or list_users.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explains the working pattern explicitly: 'One call for the types plus one per type for its groups; give group_type_partner_id to fetch a single type.' This tells the agent both the default usage and the narrowing option, though it doesn't state when to avoid the tool or name an alternative for edge cases.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_public_holidaysList public holiday and custom day patternsA
Read-only

The public holiday patterns held in the system (for example 'UK Public Holidays') and, with include_custom_days, the custom day patterns (for example 'Christmas Shutdown'), as id and name. The API lists the patterns only; the dates inside a pattern are not exposed by API V2.

ParametersJSON Schema
NameRequiredDescriptionDefault
include_custom_daysNoAlso list custom day patterns
include_contact_detailsNoInclude nothing extra (pattern names are the system's own text), and stop redacting email addresses, phone numbers and UK postcodes typed into names and other text

TDQS

A3.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare readOnlyHint and openWorldHint; the description adds a genuinely useful limitation: the API exposes pattern names only and 'the dates inside a pattern are not exposed by API V2'. That pre-empts an agent expecting date data, which the annotations do not convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, front-loaded with the primary resource and output shape, and the API limitation is placed last as a caveat. The first sentence is dense but every clause carries information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description usefully states the return shape ('as id and name') and a key data limitation. Both parameters are covered by the schema, so the only gap is that include_contact_details' redaction behavior is never surfaced in prose.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents both booleans; baseline is 3. The description adds an example of what include_custom_days returns ('Christmas Shutdown') but says nothing about include_contact_details and its unusual redaction-stripping semantics.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Specific verb plus resource: it lists 'public holiday patterns held in the system' and, with a flag, 'custom day patterns', with concrete examples of each. The resource is clearly distinct from the sibling list tools (groups, absence types, users), though no sibling is named explicitly.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The phrase 'with include_custom_days, the custom day patterns' implies when the optional mode applies, but there is no explicit when-to-use/when-not guidance or naming of an alternative tool. Usage is inferable rather than stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_usersList usersA
Read-only

Employees on this Edays system with partner_id (the key other tools take), name, job title, leaver flag, settings template, start dates, FTE and hours per day. GET /api/v2/users is not marked as paged in the documentation; the whole list is fetched (following the edays-pagination-* headers page by page if the system does page it) and filtered locally: query matches part of the name or partner ID, and leavers are skipped unless asked for. Contact and HR details (email, login, phones, address, next of kin, date of birth, payroll and employee numbers, pay) only with include_contact_details.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryNoCase-insensitive fragment of the person's name or partner ID
max_resultsNoMaximum number of users to return
include_leaversNoInclude users flagged IsLeaver
include_contact_detailsNoInclude email, login, home and work phones and emails, address, next of kin, date of birth, payroll and employee numbers, SSO and client-provided IDs and annual pay, and stop redacting email addresses, phone numbers and UK postcodes typed into names and other text

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare readOnly and openWorld; the description adds critical behavior: pagination handling, local filtering semantics, leaver default, and conditional PII redaction. These details go well beyond the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the returned data fields, then behavioral constraints. The second sentence is dense but each clause earns its place; there is minor verbosity in the contact-detail parenthesis.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, so the description must carry return-value context; it lists the fields and conditional contact details, and explains pagination and filtering. Sufficient for an agent to call correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema already documents each parameter. The description reinforces query and include_leavers behavior but adds little parameter-specific meaning beyond the schema's own descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

It names the specific resource (employees on the Edays system) and enumerates many exact fields returned, including partner_id and its role as the key other tools use. This clearly distinguishes it from single-user siblings like get_user.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explains local filtering, leaver exclusion, and when contact details are included, giving clear usage context. However, it never explicitly names an alternative tool (e.g., get_user for one employee) or states when not to use this list endpoint.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 9 tool updatesv0.1.0
    • First observedget_absence
    • First observedget_user
    • First observedget_user_entitlements
    • First observedget_user_rota
    • First observedlist_absence_types
    • First observedlist_absences
    • First observedlist_groups
    • First observedlist_public_holidays
    • First observedlist_users

TDQS

A3.8/5.0

Scored across 9 tools

Disambiguation4/5

Most tools target clearly distinct resources and verbs (list_users vs get_user, list_absences vs get_absence, entitlements vs rota). The main overlap is conceptual: list_absence_types also returns custom day and public holiday groups, blurring its boundary with list_public_holidays, but the descriptions call this out and help steer selection.

Naming Consistency5/5

Every tool follows a predictable get_/list_ + resource pattern in snake_case (list_users, get_user, get_user_rota), including consistent handling of nested resources like user entitlements and rota.

Tool Count5/5

9 tools is well-scoped for an HR/absence domain, each covering a distinct facet (holidays, groups, absence types, users, absences, entitlements, rota) without redundant entries.

Completeness3/5

The surface is entirely read-only: there is no way to book, update, delete, or approve an absence, yet the domain is fundamentally about managing absences, leaving an obvious lifecycle gap. Read coverage of users, absences, entitlements and rota is solid, so an agent can inspect but not act.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • -
    license
    Not graded
    quality
    Not graded
    maintenance
    Enables interaction with employee management systems through a standardized MCP interface. Supports comprehensive employee operations including CRUD operations, search, filtering by level/status, and data synchronization.
    -
  • A
    license
    A
    quality
    B
    maintenance
    MCP server for the Personio HR API with employee and HR profiles, enabling self-service and HR operations like managing absences, attendances, documents, and organizational data.
    10
    Apache 2.0
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables managing Personio HR and recruiting data through MCP tools, including employees, absences, time tracking, documents, custom reports, and recruiting workflows such as jobs, candidates, and applications.
    19 npm
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables Claude and other MCP clients to query and register HR data from TramitApp, including employees, clockings, absences, shifts, and vacation balances, with multi-company support.
    MIT