Docs
KASPR Predict MCP
Reference for the hosted MCP servers at mcp.kasprdata.com: what they answer, how to install them in an MCP client, every tool with its parameters and results, the HTTP routes, limits, errors, privacy and terms.
Spec 0.5 · updated 3 October 2026 · beta · How the numbers are computed · Product page
Overview
KASPR Predict MCP is a timing planner for work that depends on the network. It is served as two Model Context Protocol servers over streamable HTTP, behind one bearer key. Any MCP client that can send an Authorization header can use them: Claude Code, Cursor, and Claude Desktop through the mcp-remote bridge.
The timing server (kaspr-timing) answers one question for calls, live streams, big uploads, agent batches and fixed live events: given a task at a proposed time and place, is there a materially better time? It answers in fixed wording, in every participant’s local time, and says nothing when nothing is better. It never blocks the time you proposed.
The scheduling server (kaspr-predict) ranks the hours between now and a deadline for batch jobs, agent loops and transfers: run now with timeout, retry and cache tuning for the hour, wait, or no good window. It can hold a chosen window and re-score it until it starts.
Both read the same model: KASPR’s typical week for every country, region and district, 168 hourly values of how usually rough the connection is, from a frozen data vintage named in every answer. Neither is a live measurement of the network.
| Server | Endpoint | Tools | For |
|---|---|---|---|
| kaspr-timing | https://mcp.kasprdata.com/timing/mcp | check_timing, best_window, explain_last_verdict | calls, live streams, uploads, agent batches, fixed events |
| kaspr-predict | https://mcp.kasprdata.com/mcp | exposure_class, latency_forecast, best_window, schedule_hold, report_outcome | batch jobs and agent loops with a deadline |
| HTTP | POST https://mcp.kasprdata.com/verdict and /verdict/best_window | the timing verdicts without MCP | scripts, pipelines, cross-checks |
Quick start
1. Get a key
Sign in and create a trial key under Account → API keys: it starts with kmcp_, works on every server, and answers ten calls over 72 hours. Developer and team keys with monthly allowances are issued by hand during the beta; ask through the contact page. Using a key means you accept the terms below.
2. Claude Code
One command per server. Afterwards /mcp inside Claude Code lists kaspr-timing with three tools and kaspr-predict with five.
claude mcp add --transport http kaspr-timing https://mcp.kasprdata.com/timing/mcp --header "Authorization: Bearer <your key>"
claude mcp add --transport http kaspr-predict https://mcp.kasprdata.com/mcp --header "Authorization: Bearer <your key>"2. Cursor and other clients with a JSON configuration
Add one or both servers to the client’s MCP configuration (for Cursor, .cursor/mcp.json).
{
"mcpServers": {
"kaspr-timing": {
"url": "https://mcp.kasprdata.com/timing/mcp",
"headers": { "Authorization": "Bearer <your key>" }
},
"kaspr-predict": {
"url": "https://mcp.kasprdata.com/mcp",
"headers": { "Authorization": "Bearer <your key>" }
}
}
}2. Claude Desktop
Claude Desktop reaches remote servers through the mcp-remote bridge; add this to claude_desktop_config.json, and a second entry with the other URL if you want both.
{
"mcpServers": {
"kaspr-timing": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://mcp.kasprdata.com/timing/mcp", "--header", "Authorization: Bearer <your key>"]
}
}
}3. Ask
Ask the question you would ask anyway. The client picks the tool. For example: “Tim in Nairobi wants a video call at 9 PM his time. I’m in Melbourne. Is that a good hour?”
The answer should name the hour in both places and either propose a steadier one or say nothing about timing. If you get a 401, the key is missing or has been revoked; if a tool is missing from the list, the client has not reloaded the server.
Authentication
Every request to /timing/mcp, /mcp, /verdict and /verdict/best_window must carry Authorization: Bearer <key>. A missing or revoked key answers HTTP 401 with a JSON body whose error code is unauthorized; nothing else is returned.
Keys are tied to a tier (free, developer, team) that sets the scheduling server’s monthly credits and request rate. The timing server is not metered during the beta. Keys are personal: do not share them or embed them in client-side code. Tell us at once if a key may have leaked, and we rotate it.
Only bearer keys are supported today. Clients that require OAuth for remote servers, such as the connector screens in ChatGPT and claude.ai, cannot add these servers yet; that is planned.
Over stdio, for the local server, the key comes from the KASPR_MCP_KEY environment variable instead of a header.
Timing tools (kaspr-timing)
Three read-only tools. Descriptions are the server’s own; the client sees them verbatim. Every call returns a verdict object (next section) in structuredContent and the same JSON as text, and every outcome is a verdict, never an exception: a bad request is invalid_request, a cap is rate_limited, a backend failure is a silent no_recommendation.
check_timing
Checks whether a proposed time for a network-sensitive task (a call, a live stream, a large upload, an agent batch job, a fixed live event) is a usually rough hour on the networks involved, and whether a materially steadier hour exists nearby. You classify the task into `task_class`; the service never infers it. Give every participant’s location and the proposed time with its UTC offset; the service converts time zones itself. Returns a verdict object. How to use it: if `verdict` is `no_recommendation`, say nothing about timing and carry on; if `text` is present (`suggest_alternative`, `advise_only`, `insufficient_data`, `rate_limited`), show `text` verbatim, as the whole of your timing advice; never add timing advice of your own, never quote numbers that are not in `text`, and never claim a benefit beyond what `text` says. Keep `explanation` for when the user asks why (or call `explain_last_verdict`). The verdict never blocks anything: the user’s original time always stays available.
Parameters
| Name | Type | Required | Meaning |
|---|---|---|---|
| task_class | string | yes | live_call, broadcast, upload_transfer, agent_batch, fixed_realtime or generic. broadcast, upload_transfer and agent_batch take exactly one location. |
| locations | array of 1–2 | yes | Each with a label (1–40 characters: letters, digits, space, _ . ' -; labels come back in the answer, so nothing else is accepted) and either query (a place name, ≤ 80 characters, e.g. “Nairobi, KE”) or lat and lon. The location labelled me is the user’s own endpoint; otherwise the first one is. |
| candidate_time | string | yes | ISO 8601 with offset; a time without an offset is read as UTC. Not earlier than one hour ago; no upper limit. |
| window | object | no | { start, end } with offsets, at most 12 hours, inside which an alternative may be proposed. Default: the candidate ± 3 hours. |
| feasibility | object | no | { local_hours: [from, to) } in whole hours, applied in each endpoint’s local time. Default [8, 20]. |
| user_timezone | string | no | IANA zone for the user’s clock, e.g. Australia/Melbourne. Default: the user endpoint’s zone. |
Returns
- — verdict: suggest_alternative, no_recommendation, advise_only, insufficient_data, invalid_request or rate_limited.
- — candidate and alternative: each with time_local (every endpoint, user last, with offsets), band, confidence and source; alternative also names the limiting_endpoint. Null when not evaluated.
- — reason_code (always), evidence_level (with a suggestion or advice), text (the one sentence to show, when present), explanation (shown only when asked), data_vintage, usage.
best_window
Finds the steadiest hour inside a window for a network-sensitive task when no time has been proposed yet (a planning call; it returns no sentence to show). You classify the task into `task_class`. Give every participant’s location and the window (at most 12 hours) with UTC offsets. Returns a verdict object: `best_window` with `best` (the steadiest feasible hour, in every endpoint’s local time) when one hour is materially steadier than the rest; `no_recommendation` when the window is uniform (then `best` is still reported but any hour is as good) or when no hour fits the allowed local hours; `insufficient_data` or `rate_limited` with `text` to show verbatim. On `best_window`, propose `best` as the time; on `no_recommendation`, say nothing about timing. Keep `explanation` for when the user asks why. Never invent timing advice beyond the verdict.
Parameters
| Name | Type | Required | Meaning |
|---|---|---|---|
| task_class | string | yes | As for check_timing. |
| locations | array of 1–2 | yes | As for check_timing. |
| window | object | yes | { start, end } with offsets, at most 12 hours; hours are stepped from start. |
| feasibility | object | no | As for check_timing. |
| user_timezone | string | no | As for check_timing. |
Returns
- — verdict: best_window, no_recommendation, insufficient_data, invalid_request or rate_limited; best in place of candidate and alternative.
- — reason_code: best_hour, window_uniform (best still reported), no_feasible_hour, insufficient_coverage, or a request or cap code.
- — explanation, data_vintage, usage; no text on a planning call except for insufficient_data and rate_limited.
explain_last_verdict
Returns the reasoning behind the most recent timing verdict given to this user (within 24 hours): the verdict and its `explanation`. Call it only when the user asks why a time was or was not suggested, and show `explanation` as given. If no verdict has been made yet, it says so.
Parameters
| Name | Type | Required | Meaning |
|---|---|---|---|
| — | No parameters. |
Returns
- — spec_version, verdict and explanation of the last verdict for this key within 24 hours; otherwise verdict no_recommendation with the explanation “No timing check has been made yet.”
Verdict semantics
Every hour of a place’s typical week is one of three bands, from the chance that the hour is rough relative to that place’s own week. For two endpoints the hour is judged by the worse side, and that side is named. A suggestion needs an alternative that is at least one band steadier and materially so, inside the window and inside the local hours allowed at every endpoint; the nearest such hour wins, earlier before later. Otherwise the verdict is silent. A fixed event is never moved: it gets advice when its hour is usually congested, nothing otherwise.
Confidence comes from how much measurement backs the place: high or medium are usable; a place with low confidence falls back to its region, then its country, and if none qualifies the verdict is insufficient_data. Every verdict carries data_vintage, the date of the model data behind it.
The sentences are fixed. A client shows text verbatim when it is present and says nothing about timing when it is absent; explanation is kept for when the user asks why. The same words come back from any client and from the HTTP routes.
Verdict values
| verdict | Meaning | text |
|---|---|---|
| suggest_alternative | A materially steadier hour exists; the alternative is named. | yes |
| no_recommendation | Nothing is materially better, or nothing feasible; say nothing about timing. | no |
| advise_only | A fixed event at a usually congested hour; advice, no other time. | yes |
| best_window | Planning call: best is the steadiest feasible hour in the window. | no |
| insufficient_data | A place with too little measurement to say anything reliable. | yes |
| invalid_request | The request could not be evaluated; reason_code says why. | no |
| rate_limited | A cap was reached; nothing was evaluated. | yes |
Bands and confidence
| Value | Meaning |
|---|---|
| steady | Usually a calm hour for that place, relative to its own week. |
| mixed | Neither calm nor rough by that place’s standards. |
| congested | Usually one of that place’s rougher hours. |
| high | The place’s own measurement, well supported. |
| medium | Enough measurement to judge, possibly inherited from the region. |
| low | Not enough to judge; the verdict falls back to a larger area or says insufficient_data. |
The sentences
Examples of the fixed wording, with places and times filled in by the server:
- — Call: “9 PM Nairobi (5 AM Melbourne (Tue)) is usually a congested hour on Nairobi networks; 7 PM Nairobi (3 AM Melbourne (Tue)) is typically steadier for a video call. Propose 7 PM Nairobi instead, or keep 9 PM Nairobi?”
- — Upload: “3 PM Vienna is usually a congested hour on your network. If it’s not urgent, starting at 6 PM Vienna should be noticeably faster. Continue now or schedule for 6 PM Vienna?”
- — Live stream: “3 PM Vienna is usually a congested hour on your network for live streaming. 6 PM Vienna is typically steadier. Keep 3 PM Vienna, or move it?”
- — Agent batch: “Deferring to 6 PM Vienna: current window congested, alternative mixed, confidence high.”
- — Fixed event: “It’s a fixed time so there’s nothing to change, but 2 PM Vienna is usually a congested hour on your network. If you can, use a wired connection and close cloud backups beforehand.”
- — Too little data: “I don’t have enough measurements for Norfolk Island to say anything reliable about 10 AM Melbourne, so I’d book it as planned.”
Reason codes
| reason_code | With | Meaning |
|---|---|---|
| endpoint_congested_hour | suggest_alternative | The proposed hour is congested at the limiting endpoint. |
| endpoint_mixed_hour | suggest_alternative | The proposed hour is mixed and a steady hour is near. |
| no_material_difference | no_recommendation | No hour in the window is materially steadier. |
| no_feasible_alternative | no_recommendation | The hour is not steady but no alternative fits the window and local hours. |
| fixed_time_congested | advise_only | A fixed event at a usually congested hour. |
| best_hour | best_window | One hour in the window is a band steadier than its worst. |
| window_uniform | no_recommendation | Every feasible hour in the window has the same band; best is still reported. |
| no_feasible_hour | no_recommendation | No hour in the window fits the allowed local hours. |
| insufficient_coverage | insufficient_data | No area with usable confidence for an endpoint, or no data for that hour. |
| service_unavailable | no_recommendation | The data could not be reached; silent, not counted. |
| daily_cap_reached, minute_cap_reached, spread_limit_reached, area_limit_reached | rate_limited | A per-key cap; see Limits. |
| missing_field, bad_task_class, too_many_locations, bad_location, bad_timezone, bad_time, time_in_past, window_too_long, bad_feasibility | invalid_request | What was wrong with the request, in the order it is checked. |
Scheduling tools (kaspr-predict)
Five tools for work with a deadline. Credits meter the forecast and scheduling calls; classification is free, and run now answers are free. Every error carries recommendation run_now with fallback true, so an agent is never left without an answer.
exposure_class
Classifies a task as none, low, high or critical, and infers its profile (kind, duration, whether it is interactive, retry policy). Call it before any long-running, retry-heavy, real-time or human-facing task; for high or critical, call best_window next.
Parameters
| Name | Type | Required | Meaning |
|---|---|---|---|
| description | string | yes | What the task is, in plain words (≤ 4,000 characters). |
| kind | string | no | A task kind from the taxonomy, if the client already knows it. |
| duration_min | integer | no | 1–1440. |
| interactive | boolean | no | Whether a person is waiting on it. |
| retry_policy | object | no | timeout, retries, backoff, if known. |
Returns
- — class, inferred_profile and suggested_next. Free.
latency_forecast
The hourly chance of a rough connection, with jitter and latency levels, for up to 20 places over up to 720 hours. For when the agent needs the whole curve rather than a decision.
Parameters
| Name | Type | Required | Meaning |
|---|---|---|---|
| locations | array of 1–20 | yes | Each with place (free text), area_id, lat and lon, a region code or a cloud region name, and an optional role. |
| horizon_h | integer | no | 1–720, default 24. Free keys: up to 24. |
| resolution_min | 15 or 60 | no | Default 60. |
| metrics | array | no | Which metrics to include. |
| pair | boolean | no | Score the two places as one path. |
Returns
- — series per place with hourly points, the places as resolved, issued_at (the data vintage), notes and credits_charged (1 per place per 24 hours; pair doubles).
best_window
Ranked windows between the earliest start and the deadline, a comparison with running now, and the decision: run now, wait, or no good window, with timeout, retry, backoff and cache tuning for the hour. Calls and live sessions with a deadline that leaves room to choose are ranked too, by the chance of a degraded session; a session already under way is only flagged, never delayed.
Parameters
| Name | Type | Required | Meaning |
|---|---|---|---|
| profile | object | yes | The task profile, usually inferred_profile from exposure_class: kind, duration_min, interactive, and optionally retry_policy, cache_ttl_s, baseline_ms. |
| locations | array of 1–20 | yes | As for latency_forecast, with roles such as origin, destination, cloud_region or participant. |
| deadline | datetime | yes | ISO 8601; a naive time is UTC. Up to 30 days ahead. |
| not_before | datetime | no | Earliest start, default now. |
| n_windows | integer | no | 1–10, default 3. |
| compare_to_now | boolean | no | Include the now comparison, default true. |
| business_hours, min_gap_min | no | Accepted for compatibility; not part of the decision rule. |
Returns
- — recommendation (run_now, wait, no_good_window), windows with local times, score, confidence and expected saving, now, tuning, must_start_by, recheck_at, explanation, notes, calibrated, credits_charged (0 for run now; otherwise 1, plus 1 per place beyond two).
schedule_hold
Holds a chosen window, re-scores it every fifteen minutes against the forecast service, and notifies the session (or a callback URL) when its score drops.
Parameters
| Name | Type | Required | Meaning |
|---|---|---|---|
| window_id | string | yes | A window id from best_window, issued to the same key. |
| ttl_h | number | no | How long to keep the hold, up to 720 hours. |
| reschedule_policy | string | no | notify_only (default) or auto_shift_within_deadline. |
| callback_url | string | no | An https URL to POST re-score events to. |
Returns
- — hold_id, status, the re-score interval, next check, credits_remaining. 1 credit.
report_outcome
Records what happened for a held or issued window, so the model gets better. Opt-in, job metrics only; refunds one credit, once per window.
Parameters
| Name | Type | Required | Meaning |
|---|---|---|---|
| hold_id or window_id | string | one of | What the outcome is for. |
| started_at, ended_at | datetime | no | When it ran. |
| retries | integer | no | How many retries it took. |
| failed | boolean | no | Whether it failed. |
| quality | string | no | good, choppy, failed or unknown, for sessions. |
| followed_recommendation, cost_units | no | Optional detail. |
Returns
- — credited (1 or 0) and credits_remaining.
Resources on the scheduling server: kaspr://taxonomy (task kinds and their defaults), kaspr://coverage (places covered, by level), kaspr://calibration (the decision parameters and whether they are calibrated), kaspr://changelog. Prompt: schedule_task(task), which walks an agent through exposure_class and best_window.
HTTP routes
The timing verdicts are also plain HTTP, for scripts, pipelines and cross-checks: POST /verdict for check_timing and POST /verdict/best_window for best_window, with the same bearer key, a JSON body in the shape of the tool parameters, and the verdict object as the response. Every verdict is HTTP 200, including invalid_request and rate_limited; only a missing key is 401.
{
"task_class": "live_call",
"locations": [{ "label": "me", "query": "Melbourne" }, { "label": "Tim", "query": "Nairobi" }],
"candidate_time": "2026-10-06T21:00:00+03:00",
"feasibility": { "local_hours": [0, 24] },
"user_timezone": "Australia/Melbourne"
}{
"spec_version": "0.3",
"verdict": "suggest_alternative",
"data_vintage": "2026-07-16",
"candidate": { "time_local": [{ "label": "Tim", "time": "2026-10-06T21:00+03:00", "tz": "Africa/Nairobi" }, { "label": "me", "time": "2026-10-07T05:00+11:00", "tz": "Australia/Melbourne" }], "band": "congested", "confidence": "high", "source": "typical_week" },
"alternative": { "time_local": [ … ], "band": "mixed", "confidence": "high", "source": "typical_week", "limiting_endpoint": "Tim" },
"reason_code": "endpoint_congested_hour",
"evidence_level": 1,
"text": "9 PM Nairobi (5 AM Melbourne (Wed)) is usually a congested hour on Nairobi networks; 7 PM Nairobi (3 AM Melbourne (Wed)) is typically steadier for a video call. Propose 7 PM Nairobi instead, or keep 9 PM Nairobi?",
"explanation": "Based on KASPR's typical weekly pattern for Nairobi networks (data up to July 2026), not a live forecast. …",
"usage": { "tier": "beta", "verdicts_today": 1, "cap": null }
}curl -s -X POST https://mcp.kasprdata.com/verdict \
-H "Authorization: Bearer $KASPR_MCP_KEY" -H "content-type: application/json" \
-d '{"task_class":"live_call","locations":[{"label":"me","query":"Melbourne"},{"label":"Tim","query":"Nairobi"}],"candidate_time":"2026-10-06T21:00:00+03:00","feasibility":{"local_hours":[0,24]},"user_timezone":"Australia/Melbourne"}'Limits
- — Timing server: no daily or per-minute caps during the beta. A key that touches more than 15 distinct places in an hour is answered rate_limited until the hour passes, as a guard against enumeration.
- — Timing requests: at most 2 locations (exactly 1 for broadcast, upload_transfer and agent_batch); a window of at most 12 hours; a candidate no earlier than one hour ago; no upper limit on how far ahead.
- — Scheduling server: credits per month by tier (free 500, developer 20,000, team 250,000) and requests per minute (10, 60, 300); free keys can forecast up to 24 hours ahead for up to 2 places; best_window takes up to 5, 10 or 20 places by tier; deadlines up to 30 days ahead; holds up to 720 hours, at most 20 active per key. The whole-week route used by the client library needs a developer or team key.
- — Place queries: a plain name (“Nairobi”, “Vienna”), “City, CC” or “City, Country” (“Nairobi, KE”), a district with its city and country (“Prahran, Melbourne, AU”), or coordinates; at most four comma-separated parts, ≤ 80 characters. Five micro-territories are not covered.
- — Requests per key: the key’s per-minute limit (10 for free keys) applies to the timing tools as well and comes back as a rate_limited verdict; request bodies are capped (64 KB on the routes); at most 20 active holds per key.
- — Distinct places per day: a key may touch 50 (free), 500 (developer) or 5,000 (team) different places per UTC day across every tool; places already looked up that day cost nothing. Over the allowance, the scheduling decision and its tuning still come but without per-place numbers, raw forecasts are refused, and the timing tools answer rate_limited with “You’ve checked a lot of different places today, so I’d go ahead as planned.”
Errors
The timing server never raises: every outcome is a verdict object over HTTP 200, and a client shows text when it is present and otherwise says nothing. The scheduling server answers structured errors with recommendation run_now and fallback true, so an agent can always carry on.
| Where | Code | Meaning |
|---|---|---|
| any | unauthorized (HTTP 401) | No key, or a revoked one, in the Authorization header. |
| timing | invalid_request | The request could not be evaluated; reason_code names the first problem found, from missing_field to bad_feasibility. |
| timing | rate_limited | A cap; text says to go ahead as planned. |
| timing | no_recommendation / service_unavailable | The data could not be reached; silent and not counted. |
| scheduling | no_coverage | A place could not be resolved or is not modelled. |
| scheduling | horizon_exceeded | A deadline or horizon beyond the limit for the key. |
| scheduling | invalid_profile | A malformed profile, deadline or window id. |
| scheduling | quota_exhausted, rate_limited | Credits or requests per minute used up. |
| scheduling | backend_unavailable | The forecast service could not be reached; run now. |
Data and privacy
- — The hosted servers never measure you. They answer from the model and your request alone. The timing server returns no numbers: bands, times and a sentence.
- — The model is KASPR’s typical week for each place, from a frozen data vintage named in every answer. Verdicts say so in their own words (“usually”, “typically”, “not a live forecast”).
- — We log each call: the key, the tool, the task class, the verdict, its confidence and source, the number of places, and timing. Place names and coordinates you send are resolved and are not kept beyond the request log needed to operate the service and enforce the terms.
- — In the on-device mode of the scheduling server, the rolling series of your own latency measurements lives in one file on your machine, readable only by you; the only call to KASPR is a forecast by place id. Outcome reports are opt-in and carry job metrics only.
Terms
Use of the MCP servers, the HTTP routes and everything they return is governed by the KASPR Schedule terms and conditions, including the additional terms for the KASPR Predict MCP. A key is issued to someone who has accepted them, and using a key, directly or through an agent you configure, is acceptance of them. In short: the outputs are statistical tendencies from past measurement, not live conditions, not advice, and not a promise that any hour will be better than another; you and your agents decide what to do with them, at your own risk.
Changelog
| Date | Change |
|---|---|
| 3 Oct 2026 | Spec 0.5: a daily allowance of distinct places per key on every tool; the whole-week route for developer and team keys. |
| 3 Oct 2026 | Spec 0.4: labels restricted to plain characters, place resolution bounded; per-minute limit on the timing tools, body-size and hold limits, hardening after a security review. |
| 3 Oct 2026 | Spec 0.3: shared place resolution (“City, CC” and “City, Country” queries), wording refinements, no caps on the timing server during the beta. Timing server and HTTP routes deployed. |
| 3 Oct 2026 | Spec 0.2: the timing server (check_timing, best_window, explain_last_verdict) on /timing/mcp; verdicts from the typical week’s local rough-hour probability. |
| 2 Oct 2026 | Scheduling server: deadlines up to 30 days; calls and sessions with a deadline that leaves room to choose are ranked; explanations name the slot in every participant’s local time. |
| 1 Oct 2026 | Scheduling server (exposure_class, latency_forecast, best_window, schedule_hold, report_outcome) on /mcp; hosted beta with bearer keys. |
