Docs
KASPR Predict for Snowflake
Reference for the KASPR Predict Snowflake Native App: what it answers, how to install it and grant it, every procedure with its parameters and results, the managed MCP servers, pipeline gating, entitlements and usage, limits, errors and privacy.
Spec 0.5 · updated 3 October 2026 · beta · private preview · Product page · MCP server documentation
Overview
KASPR Predict for Snowflake is a Snowflake Native App: a timing planner for work that depends on the network, installed in your own account. It offers the same two capabilities as the hosted KASPR Predict MCP, as stored procedures in two schemas, each with a Snowflake-managed MCP server.
The TIMING schema 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. Its decision code is shared with the MCP server, and both pass the same test cases.
The PREDICT schema 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. The examples below call the app KASPR_PREDICT; use the name you gave it at installation.
| Object | Kind | Who can use it | For |
|---|---|---|---|
| TIMING.CHECK_TIMING | procedure | APP_PUBLIC | is there a materially better time? |
| TIMING.BEST_WINDOW | procedure | APP_PUBLIC | the steadiest hour in a window |
| TIMING.EXPLAIN_LAST_VERDICT | procedure | APP_PUBLIC | why the last verdict said what it said |
| TIMING.TIMING_WINDOWS | procedure returning a table | APP_PUBLIC | bands per hour for one place |
| TIMING.GET_ENTITLEMENT | procedure | APP_PUBLIC | your account, tier and caps |
| TIMING.KASPR_TIMING | managed MCP server | APP_PUBLIC | check_timing, best_window, explain_last_verdict |
| TIMING.SET_DAILY_CAP · TIMING.RUN_GOLDEN · TIMING.V_TIMING_USAGE | procedures, view | APP_ADMIN | a per-user cap, the shared self-test, every call |
| PREDICT.* | procedures, views, managed MCP server KASPR_PREDICT | APP_PUBLIC | jobs with a deadline |
Quick start
1. Get access
During the private preview the app is shared with your account as a private listing. Ask through the contact page with your account locator and cloud region, then install it from Snowsight (Data Products → Apps).
2. Grant it
Give the application role to the roles that call the app. The two task privileges are optional; they let PREDICT re-score held windows on a schedule. Grant them in Snowsight: open the app (Data Products → Apps), go to the Permissions tab and grant both.
GRANT APPLICATION ROLE KASPR_PREDICT.APP_PUBLIC TO ROLE ANALYST;
GRANT APPLICATION ROLE KASPR_PREDICT.APP_ADMIN TO ROLE SYSADMIN;3. Ask
Procedures run on the caller’s warehouse; an X-Small warehouse is enough. Object and array arguments are JSON strings.
CALL KASPR_PREDICT.TIMING.CHECK_TIMING('live_call',
'[{"label": "me", "query": "Melbourne"}, {"label": "Tim", "query": "Nairobi, KE"}]',
'2026-10-06T21:00:00+03:00',
'{"start": "2026-10-06T18:00:00+03:00", "end": "2026-10-07T00:00:00+03:00"}',
'{"local_hours": [0, 24]}',
'Australia/Melbourne');4. Check the installation
An admin can run the shared test cases inside your account; every case should pass.
CALL KASPR_PREDICT.TIMING.RUN_GOLDEN(); -- {"cases": 29, "failed": 0, "resolution_cases": 18, "resolution_failed": 0, ...}Roles and grants
APP_PUBLIC calls every procedure in TIMING and PREDICT, reads the PREDICT views and uses both managed MCP servers. APP_ADMIN is for administrators and adds three things: a per-user daily cap, every timing call in TIMING.V_TIMING_USAGE, and the shared self-test; grant it together with APP_PUBLIC.
The app requests two optional account privileges, EXECUTE MANAGED TASK and EXECUTE TASK, which you grant on the app’s Permissions tab in Snowsight. With them, PREDICT.SCHEDULE_HOLD creates a serverless task that re-scores open holds every fifteen minutes and suspends itself when none are left. Without them holds are not re-scored and are not charged, and PREDICT.V_HOLDS still shows a hold as released once its latest start has passed.
The app references no tables of yours and requests no external access integration.
Timing procedures (schema TIMING)
Read-only answers. Every call returns a verdict object (next section) as a VARIANT, and every outcome is a verdict, never an error: a bad request is invalid_request, a cap is rate_limited, and a failure inside the app is a silent no_recommendation. JSON arguments may be passed as strings, as shown.
CHECK_TIMING
CHECK_TIMING(TASK_CLASS, LOCATIONS, CANDIDATE_TIME, WINDOW, FEASIBILITY, USER_TIMEZONE) RETURNS VARIANT
Is a proposed time a usually rough hour on the networks involved, and is there a materially steadier hour nearby? Show text when it is present; on no_recommendation say nothing about timing.
Parameters
| Name | Type | Required | Meaning |
|---|---|---|---|
| TASK_CLASS | VARCHAR | yes | live_call, broadcast, upload_transfer, agent_batch, fixed_realtime or generic. broadcast, upload_transfer and agent_batch take exactly one location. |
| LOCATIONS | VARCHAR (JSON 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; otherwise the first one is. |
| CANDIDATE_TIME | VARCHAR | yes | ISO 8601 with offset; a time without an offset is read as UTC. Not earlier than one hour ago; no upper limit. |
| WINDOW | VARCHAR (JSON object) | no | { "start", "end" } with offsets, at most 12 hours, inside which an alternative may be proposed. Default: the candidate ± 3 hours. |
| FEASIBILITY | VARCHAR (JSON object) | no | { "local_hours": [from, to) } in whole hours, applied in each endpoint’s local time. Default [8, 20]. |
| USER_TIMEZONE | VARCHAR | 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, the user last, with offsets), band, confidence and source; alternative also names the limiting_endpoint.
- — reason_code (always), evidence_level, text (the one sentence to show, when present), explanation (for when someone asks why), data_vintage, spec_version, usage.
BEST_WINDOW
BEST_WINDOW(TASK_CLASS, LOCATIONS, WINDOW, FEASIBILITY, USER_TIMEZONE) RETURNS VARIANT
The steadiest feasible hour inside a window when no time has been proposed yet. A planning call: it returns no sentence to show except for insufficient_data and rate_limited. Separate from PREDICT.BEST_WINDOW, which ranks hours before a deadline.
Parameters
| Name | Type | Required | Meaning |
|---|---|---|---|
| TASK_CLASS | VARCHAR | yes | As for CHECK_TIMING. |
| LOCATIONS | VARCHAR (JSON array of 1–2) | yes | As for CHECK_TIMING. |
| WINDOW | VARCHAR (JSON object) | yes | { "start", "end" } with offsets, at most 12 hours; hours are stepped from start. |
| FEASIBILITY | VARCHAR (JSON object) | no | As for CHECK_TIMING. |
| USER_TIMEZONE | VARCHAR | 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.
EXPLAIN_LAST_VERDICT
EXPLAIN_LAST_VERDICT() RETURNS VARIANT
The calling user’s most recent CHECK_TIMING or BEST_WINDOW verdict within 24 hours, with its explanation; “No timing check has been made yet.” when there is none.
Returns
- — { "spec_version", "verdict", "explanation" }.
TIMING_WINDOWS
TIMING_WINDOWS(TASK_CLASS, LOCATION, START_TS, END_TS) RETURNS TABLE (HOUR_UTC, BAND, CONFIDENCE)
One row per whole hour from START_TS to END_TS (at most 48 hours) for one place: the band and the confidence, nothing else. Bad input or an unknown place gives an empty table.
Parameters
| Name | Type | Required | Meaning |
|---|---|---|---|
| TASK_CLASS | VARCHAR | no | Accepted for symmetry with the other procedures; not used. |
| LOCATION | VARCHAR | yes | A place name, or a JSON object with query or lat and lon. |
| START_TS, END_TS | VARCHAR | yes | ISO 8601 times. |
Returns
- — HOUR_UTC (TIMESTAMP_TZ), BAND (steady, mixed, congested, or NULL where the hour has no data), CONFIDENCE (high or medium).
GET_ENTITLEMENT
GET_ENTITLEMENT() RETURNS VARIANT
Your account (organisation and account name, the key KASPR uses for entitlements), tier, daily and per-minute caps, counted verdicts today, and how many of today’s distinct places you have used.
Returns
- — { "account", "tier", "daily_cap", "minute_cap", "verdicts_today", "places_today", "place_allowance", "spec_version", "engine" }.
Verdict semantics
Verdicts follow the shared timing-verdict specification, version 0.3, exactly as the hosted MCP server does: the same bands, rules, sentences and reason codes. Every hour of a place’s typical week is steady, mixed or congested relative to that place’s own week; a two-party call is judged by its worse side. A steadier time is suggested only when it is at least one band better and materially so, inside the window and the local hours allowed at every participant.
The full tables of verdict values, bands, sentences and reason codes are in the MCP documentation and apply here unchanged.
Scheduling procedures (schema PREDICT)
The scheduling procedures mirror the MCP scheduling server’s tools: EXPOSURE_CLASS (a function) classifies a task; BEST_WINDOW ranks the hours between the earliest start and a deadline and decides run now, wait or no good window, with tuning; LATENCY_FORECAST returns the hourly curve for up to 20 places; SCHEDULE_HOLD holds a window and re-scores it every fifteen minutes; REPORT_OUTCOME records how a job went, job metrics only.
Their JSON arguments (the task profile, locations, metrics) are passed as strings, every error carries a fallback of run now, and PREDICT.V_USAGE and PREDICT.V_HOLDS show your calls and holds. The daily allowance of distinct places covers these procedures too (see Entitlements and usage).
Managed MCP servers
The app creates two Snowflake-managed MCP servers. TIMING.KASPR_TIMING offers check_timing, best_window and explain_last_verdict with the same descriptions as the hosted timing server; PREDICT.KASPR_PREDICT offers the five scheduling tools. Both use APP_PUBLIC.
Clients reach them at your account’s REST endpoint and sign in with your account’s own OAuth, through a security integration you create. Object and array arguments are JSON strings, because managed MCP servers pass only scalar arguments to procedures.
In the endpoint, <account> is <org>-<account_name> in lower case with every underscore replaced by a hyphen (for example myorg-my-account): Snowsight shows the underscore form, but a hostname with underscores fails TLS verification. <app> is the name you gave the app when you installed it.
https://<account>.snowflakecomputing.com/api/v2/databases/<app>/schemas/TIMING/mcp-servers/KASPR_TIMING
https://<account>.snowflakecomputing.com/api/v2/databases/<app>/schemas/PREDICT/mcp-servers/KASPR_PREDICTConnect Claude Code (tested setup)
Run this once as ACCOUNTADMIN. It creates a role for everyday users and an OAuth client for Claude Code. Use a public client with PKCE, which has no secret: a confidential client fails at the token step with invalid_client.
Snowflake blocks ACCOUNTADMIN and SECURITYADMIN from OAuth sign-in, so sign in as a user whose default role is KASPR_USER. To create such a user without writing a password into SQL, create it without one and run ALTER USER <user> RESET PASSWORD; the user sets the password through the single-use link it returns.
CREATE ROLE IF NOT EXISTS KASPR_USER;
GRANT APPLICATION ROLE <app>.APP_PUBLIC TO ROLE KASPR_USER;
GRANT USAGE ON WAREHOUSE <warehouse> TO ROLE KASPR_USER;
GRANT ROLE KASPR_USER TO USER <user>;
ALTER USER <user> SET DEFAULT_ROLE = KASPR_USER DEFAULT_WAREHOUSE = <warehouse>;
CREATE SECURITY INTEGRATION KASPR_PREDICT_MCP
TYPE = OAUTH OAUTH_CLIENT = CUSTOM OAUTH_CLIENT_TYPE = 'PUBLIC'
OAUTH_REDIRECT_URI = 'http://localhost:33419/callback'
OAUTH_ALLOW_NON_TLS_REDIRECT_URI = TRUE -- the callback only reaches your own machine
OAUTH_ENFORCE_PKCE = TRUE
OAUTH_ISSUE_REFRESH_TOKENS = TRUE OAUTH_REFRESH_TOKEN_VALIDITY = 86400
ENABLED = TRUE;
DESC SECURITY INTEGRATION KASPR_PREDICT_MCP; -- copy OAUTH_CLIENT_IDclaude mcp add --transport http --scope user kaspr-timing \
https://<account>.snowflakecomputing.com/api/v2/databases/<app>/schemas/TIMING/mcp-servers/KASPR_TIMING \
--client-id '<OAUTH_CLIENT_ID>' --callback-port 33419Start a new Claude Code session, run /mcp, choose kaspr-timing and Authenticate, and sign in. Add kaspr-predict the same way with schemas/PREDICT/mcp-servers/KASPR_PREDICT. Other MCP clients work the same way: register their callback URL as OAUTH_REDIRECT_URI.
Chat in Snowsight (Snowflake Intelligence)
Anyone with access can ask scheduling questions in plain words in Snowflake Intelligence, the chat in Snowsight that Snowflake now also calls CoWork. Examples: “When is the best time this Tuesday evening for a video call between me in Melbourne and Tim in Nairobi?” or “Is 8 PM on Wednesday a good time to stream from Vienna?”. The agent picks check_timing, best_window or explain_last_verdict, fills in the arguments, and answers in plain language; when it does not know a place or a time zone, it asks first.
The app does not ship the agent, because an agent inside an app would need a warehouse privilege. You create it in your own account with one script: it makes the agent KASPR_SCHEDULER in SNOWFLAKE_INTELLIGENCE.AGENTS, with the app’s three timing procedures as tools and the same tool descriptions as the managed MCP server, and lets the role KASPR_USER (see Managed MCP servers) chat with it.
Questions run the app’s procedures in your account on your warehouse, so nothing leaves the account; Snowflake bills the Cortex usage of the chat to you. Each person’s questions count against their own daily allowance of places.
- Download the script and replace <app> with the name you gave the app and <warehouse> with a warehouse KASPR_USER can use.
- Run it as ACCOUNTADMIN.
- Sign in as a KASPR_USER user, open AI & ML → Snowflake Intelligence (or ai.snowflake.com), choose KASPR Scheduler, and ask.
Download kaspr_cortex_agent.sql →
CREATE OR REPLACE AGENT SNOWFLAKE_INTELLIGENCE.AGENTS.KASPR_SCHEDULER
PROFILE = '{"display_name": "KASPR Scheduler"}'
FROM SPECIFICATION
$$
models:
orchestration: auto
tools:
- tool_spec:
type: generic
name: check_timing
...
tool_resources:
check_timing:
type: procedure
identifier: <app>.TIMING.CHECK_TIMING
execution_environment: { type: warehouse, warehouse: <warehouse> }
...
$$;
GRANT USAGE ON AGENT SNOWFLAKE_INTELLIGENCE.AGENTS.KASPR_SCHEDULER TO ROLE KASPR_USER;
GRANT DATABASE ROLE SNOWFLAKE.CORTEX_USER TO ROLE KASPR_USER;Pipeline gating
A pipeline asks CHECK_TIMING with task class agent_batch before a network-heavy job and defers, never skips: on suggest_alternative it records the suggested start (the user’s entry in alternative.time_local, listed last) and stops; the next scheduled run after that time runs the job without asking again. Every other verdict, an error, or no answer at all runs the job now.
Example from the app’s test account, for Vienna on a Monday afternoon: at 3 PM the gate defers to 6 PM (“Deferring to 6 PM Vienna (Mon): current window congested, alternative mixed, confidence high.”), at 4 PM it waits, at 6 PM it runs.
-- inside your gate procedure (Snowflake Scripting)
CALL KASPR_PREDICT.TIMING.CHECK_TIMING('agent_batch',
'[{"label": "me", "query": "Vienna, AT"}]',
TO_VARCHAR(CURRENT_TIMESTAMP(), 'YYYY-MM-DD"T"HH24:MI:SSTZH:TZM'),
NULL, '{"local_hours": [0, 24]}', 'Europe/Vienna') INTO :v;
IF (v:verdict::STRING = 'suggest_alternative') THEN
-- store v:alternative:time_local[last]:time as RUN_AFTER and stop
ELSE
-- run the job now
END IF;Entitlements and usage
Every installation starts on the beta tier of the preview, which has no daily or per-minute caps on the timing procedures. Two guardrails apply to every tier: more than 15 distinct places per user per hour answers rate_limited, and each user has a daily allowance of distinct places across every procedure (50 on the beta and free tiers, 500 on pro, 5,000 on platform; places already used that day are free). Over the allowance the timing procedures answer rate_limited (area_limit_reached), TIMING_WINDOWS returns no rows, PREDICT.LATENCY_FORECAST refuses with area_limit, and PREDICT.BEST_WINDOW keeps its decision but leaves out the per-place numbers. KASPR can change an account’s tier from its side without a new release; GET_ENTITLEMENT shows the current one.
Every timing call is recorded per user, as account and user name, in the app’s own state, whatever the tier. Admins read it in TIMING.V_TIMING_USAGE: time, user, task class, verdict, reason code, confidence and whether the call counted toward a cap. Only calls that reach a verdict about the hour count; invalid and rate-limited requests do not.
An admin can set a per-user daily cap with SET_DAILY_CAP(n), and remove it with SET_DAILY_CAP(NULL). It can only lower the tier’s cap, never raise it.
Limits
- — At most two locations per call; one for broadcast, upload_transfer and agent_batch.
- — Windows of at most twelve hours; candidate times not earlier than one hour ago, with no upper limit.
- — TIMING_WINDOWS returns at most 48 hours per call.
- — A daily allowance of distinct places per user across every procedure: 50 on the beta and free tiers; places already used that day are free. GET_ENTITLEMENT shows how many are used.
- — A timing call takes a few seconds on an X-Small warehouse.
- — The forecast is a typical week from a frozen data vintage (2026-07-16), named in every answer; it changes only with a new vintage.
Errors
The timing procedures do not raise errors for bad input or internal failures; they answer with a verdict. Fix the request when the verdict is invalid_request; the reason code says what was wrong.
| reason_code | Meaning |
|---|---|
| missing_field | A required argument is missing. |
| bad_task_class | TASK_CLASS is not one of the six classes. |
| too_many_locations | More than two locations, or more than one for a single-endpoint class. |
| bad_location | A location is malformed or matches no place. |
| bad_time | A time could not be read. |
| time_in_past | The candidate or the window start is more than an hour ago. |
| window_too_long | The window is longer than twelve hours. |
| bad_timezone | USER_TIMEZONE is not an IANA zone. |
| bad_feasibility | local_hours is not a pair of whole hours. |
| area_limit | PREDICT.LATENCY_FORECAST only: the user’s daily allowance of distinct places is used up (the timing procedures answer rate_limited with reason area_limit_reached instead). |
| service_unavailable | Not a request error: a failure inside the app, answered as a silent no_recommendation that does not count. |
Data and privacy
- — The app reads only its own forecast data, shared from KASPR inside the application package; it references no tables of yours.
- — All computation runs in your account on your warehouse. The app requests no external access integration and makes no outbound calls.
- — The places, times and task classes you send, the verdicts with their explanations, and the usage records stay in the app’s own state tables in your account; old records are pruned automatically. KASPR does not see any of it.
- — Answers carry bands, times and a sentence: no measurements, no area identifiers, no observation counts.
Changelog
| Date | Change |
|---|---|
| 4 Oct 2026 | Chat in Snowsight: a script creates the KASPR Scheduler agent for Snowflake Intelligence in your account. Tested Claude Code setup for the managed MCP servers (public OAuth client with PKCE). Free public beta: no billing events. |
| 3 Oct 2026 | Spec 0.5: a daily allowance of distinct places per user across every procedure; plain-character labels (0.4); bounded place resolution (0.4). GET_ENTITLEMENT shows places_today and place_allowance. |
| 3 Oct 2026 | Timing procedures (CHECK_TIMING, BEST_WINDOW, EXPLAIN_LAST_VERDICT, TIMING_WINDOWS, GET_ENTITLEMENT) and the managed MCP server KASPR_TIMING, on the shared timing-verdict spec 0.3; cross-checked against the hosted MCP server. |
| 2 Oct 2026 | Scheduling procedures in PREDICT (EXPOSURE_CLASS, BEST_WINDOW, LATENCY_FORECAST, SCHEDULE_HOLD, REPORT_OUTCOME) and the managed MCP server KASPR_PREDICT; private preview. |
