The MCP server performs business-day, SLA, cron, and recurrence calculations. It is offline and holiday-aware, with no network dependency. The server is designed to support date/time computations needed for scheduling and service-level tracking without external calls.
🛠️ Key Features
Business-day calculations
SLA calculations
Cron expression support
Recurrence calculations
Offline operation
Holiday-aware logic
No network access
🚀 Use Cases
Determine business days for scheduling workflows
Compute SLA-related date/time metrics
Evaluate cron- and recurrence-based schedules
Incorporate holiday calendars into time computations
⚡ Developer Benefits
Deterministic results via offline, holiday-aware processing
Avoids network latency or reliance on external services
Suitable for integration with MCP tool-driven development
⚠️ Limitations
Only supports calculations described by business-day, SLA, cron, and recurrence use cases
Requires holiday-awareness to be built into the calculation context (no network)
Returns the first working day strictly after the given date, stepping over weekends and public holidays for the chosen country or subdivision. Read-only and idempotent. The date itself is never returned even if it is a working day — use is_business_day to test a single date, add_business_days to move a known number of working days, or previous_business_day to step backwards. Country defaults to US; subdivision narrows the holiday set (e.g. SCT for Scotland).
Returns the last working day strictly before the given date, stepping back over weekends and public holidays for the chosen country or subdivision. Read-only and idempotent. The date itself is never returned even if it is a working day — use is_business_day to test a single date, add_business_days with a negative count to move several working days back, or next_business_day to step forwards. Country defaults to US; subdivision narrows the holiday set (e.g. SCT for Scotland).
Move a number of working days forward or backward from a date, skipping weekends and public holidays. Use a negative number to count backwards. Zero returns the date unchanged.
Counts the working days between two dates, excluding weekends and public holidays. The range is half-open — the start date counts and the end date does not — so Monday to Tuesday is one working day. The result is negative when the end precedes the start.
Returns the first working day of a calendar month, scanning forward from the 1st past weekends and public holidays for the chosen country or subdivision. Read-only; nothing is scheduled. Use this for month-opening deadlines such as billing runs or reporting cut-offs; use last_business_day_of_month for the closing end of a month, and next_business_day to step relative to an arbitrary date rather than a month boundary. Country defaults to US; subdivision narrows the holiday set (e.g. SCT for Scotland), so the answer can differ between regions of the same country.
Returns the last working day of a calendar month, scanning backwards from the final calendar day past weekends and public holidays for the chosen country or subdivision. Read-only; nothing is scheduled. Use this for month-end deadlines such as payroll, invoicing or period close; use first_business_day_of_month for the opening end of a month, and previous_business_day to step relative to an arbitrary date rather than a month boundary. Country defaults to US; subdivision narrows the holiday set (e.g. SCT for Scotland), so the answer can differ between regions of the same country.
Works out when a service-level agreement falls due, counting only business hours on working days. If the clock starts outside business hours it waits until the next working day opens. Business hours default to 09:00-17:00 and times are wall-clock in the calendar's own locality.
Parameters6
start
string
required
When the clock starts, e.g. 2026-07-27T09:00.
business_hours
number
required
How many business hours are allowed.
opens
string
optional
Opening time, HH:MM. Defaults to 09:00.
closes
string
optional
Closing time, HH:MM. Defaults to 17:00.
country
string
optional
subdivision
string
optional
Raw schema
{
"type": "object",
"properties": {
"start": {
"type": "string",
"description": "When the clock starts, e.g. 2026-07-27T09:00."
},
"business_hours": {
"type": "number",
"description": "How many business hours are allowed."
},
"opens": {
"type": "string",
"description": "Opening time, HH:MM. Defaults to 09:00."
},
"closes": {
"type": "string",
"description": "Closing time, HH:MM. Defaults to 17:00."
},
"country": {
"type": "string"
},
"subdivision": {
"type": "string"
}
},
"required": [
"start",
"business_hours"
]
}
validate_cron_expression
Checks whether a five-field cron expression is syntactically valid. Parsing only; nothing is scheduled or run.
Parameters1
expression
string
required
A five-field cron expression, e.g. '30 9 * * MON-FRI'.
Translate a five-field cron expression into plain English. Supports ranges, lists, steps, and month or weekday names. This describes the expression; it never schedules or executes anything.
Parameters1
expression
string
required
A five-field cron expression, e.g. '30 9 * * MON-FRI'.
Computes when a five-field cron expression would next fire after a given start time, returning up to 60 timestamps. A pure calculation over the expression: no job is created, stored, or executed.
Parameters3
expression
string
required
A five-field cron expression, e.g. '30 9 * * MON-FRI'.
start
string
required
Occurrences are computed after this, e.g. 2026-07-27T09:00.
count
integer
optional
How many occurrences to return, 1-60. Defaults to 5.
Raw schema
{
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "A five-field cron expression, e.g. '30 9 * * MON-FRI'."
},
"start": {
"type": "string",
"description": "Occurrences are computed after this, e.g. 2026-07-27T09:00."
},
"count": {
"type": "integer",
"description": "How many occurrences to return, 1-60. Defaults to 5."
}
},
"required": [
"expression",
"start"
]
}
validate_recurrence_rule
Check whether an iCalendar recurrence rule (RRULE) is valid and supported. Parsing only; nothing is scheduled.
Parameters1
rule
string
required
An RFC 5545 recurrence rule, e.g. 'FREQ=WEEKLY;BYDAY=MO,WE'.
Expand an iCalendar recurrence rule (RRULE) into its next occurrences from a start date and time, honouring INTERVAL, COUNT, UNTIL, BYDAY, BYMONTHDAY, and BYMONTH. A pure calculation: it creates no schedule, stores nothing, and runs nothing.
Parameters3
rule
string
required
An RFC 5545 recurrence rule, e.g. 'FREQ=WEEKLY;BYDAY=MO,WE'.
start
string
required
Occurrences begin at or after this, e.g. 2026-07-27T09:00.
count
integer
optional
How many occurrences to return, 1-60. Defaults to 5.
Raw schema
{
"type": "object",
"properties": {
"rule": {
"type": "string",
"description": "An RFC 5545 recurrence rule, e.g. 'FREQ=WEEKLY;BYDAY=MO,WE'."
},
"start": {
"type": "string",
"description": "Occurrences begin at or after this, e.g. 2026-07-27T09:00."
},
"count": {
"type": "integer",
"description": "How many occurrences to return, 1-60. Defaults to 5."
}
},
"required": [
"rule",
"start"
]
}
search_internal_docs
Search your workspace's internal documentation (requires Business-tier authorization).
Given the text of a support ticket, returns previously resolved tickets whose descriptions are semantically similar, so an agent can reuse an earlier resolution rather than starting from scratch. Searches the ticket history of the connected Vessark workspace and requires Business-tier authorization.
Retrieves the full customer record for a given customer identifier, including account status, plan tier, and the primary contact on file. Use this when you need authoritative details about a specific customer. Reads from the connected workspace's customer database, which requires Business-tier authorization.
Query the time-series metrics store of the connected workspace. Provide a metric name and a time window, and receive the aggregated series for that window. Use this to inspect operational trends before taking action. Requires Business-tier authorization.
Write a generated report to the connected workspace's reports area. Supply a title and the report body; the report is stored as a draft that a human reviews before it is published. Writing to the workspace requires Business-tier authorization.
Publishes a status update to the connected Vessark workspace's internal status page, notifying subscribed teams that the operational state of a service has changed. Publishing requires Business-tier authorization.
Create a draft invoice for a customer of the connected workspace. Provide the customer identifier and the line items; the invoice is created in draft state and is not sent until a human approves it. Invoicing requires Business-tier authorization.
Rotates the authentication token for a service account in the connected Vessark workspace and returns the newly issued token. The previous token is invalidated once rotation completes. Rotation requires Business-tier authorization.
An MCP server for the date arithmetic that support, billing and operations
teams actually do: is this a working day, when is this SLA due, what is the
next working day after a bank holiday — plus cron and recurrence-rule
parsing for the recurring side of the same job.
It runs offline. No network calls, no stored data, no model calls — every
answer is computed from bundled public-holiday rules.
What it does
Business days and SLAs — holiday-aware for ~150 countries and their
subdivisions (England and Scotland differ; so do US states), via the
holidays package.
Tool
Answers
is_business_day
Is this a working day? If not, is it a weekend or a named holiday?
next_business_day / previous_business_day
The next/last working day around a date
add_business_days
Move ±N working days from a date
business_days_between
How many working days between two dates (half-open)
When does an N-business-hour SLA fall due, counting only open hours?
Cron and recurrence — parsing only. Nothing is scheduled, stored, or
executed; there are no timers and no job store. If you want something run on
a schedule, this is deliberately not that tool.
git clone https://github.com/Vessark/business-days-mcp
cd business-days-mcp
uv sync && uv run business-days-mcp
uv run pytest # the tests are the documentation
Client configuration
Hosted — nothing to install
The quickest way in. The same tools, no install, no credentials:
// is_business_day — 2026-12-28, UK{"date":"2026-12-28","country":"GB"}
→ {"is_business_day":false,"reason":"holiday: Boxing Day (substitute day)"}// sla_due_time — 8 business hours from Monday 16:00{"start":"2026-07-27T16:00","business_hours":8}
→ {"due":"2026-07-28T16:00:00","started_within_business_hours":true}// explain_cron_expression{"expression":"30 9 * * MON-FRI"}
→ {"explanation":"At 09:30, on Monday through Friday."}
Design notes
Every tool is a pure function of its arguments, which is why the test suite is
mostly tables. Invalid input is an ordinary typed result, never an exception —
a model that gets an argument wrong gets a readable reason and can correct
itself. Every loop that a caller can influence is bounded, so no request can
buy an expensive computation. Error messages never echo what you sent, so the
server can't be used to relay text into someone else's agent.
Known limits, stated rather than hidden: a working week is Monday–Friday, so
countries with Sunday–Thursday weeks are wrong under this assumption; holiday
data covers only the years the underlying dataset knows about, and a date
outside that range is refused rather than silently answered as an ordinary
working day.
Prior art
fbdo/business-day-mcp covers
business-day and holiday arithmetic and predates this server. If that is all
you need, use it — it also exposes timezone-aware "today" and holiday listing,
which this server does not.
This one was built for a different centre of gravity: SLA clocks that count
only business hours, and cron / RRULE parsing for the recurring side of the
same work. The overlap in the business-day tools is real and the names are
conventional; both servers are MIT licensed and both build on the excellent
holidays package.
Releasing
Bump the version in both pyproject.toml and server.json, then push a
matching tag:
sh
git tag v0.2.0 && git push origin v0.2.0
That runs the test suite, checks the tag agrees with both version fields, and
republishes to the MCP registry. Authentication is GitHub OIDC, so no token is
stored anywhere.
About
Built and maintained by Vessark. The tools in this
repository are the free tier and are open source under the MIT licence.
Vessark's workspace tools — the ones that operate on a connected workspace —
are the commercial product and are not open source.