Available tools
Reference for every Pushary MCP tool. Send push notifications, ask a person for approval and read the answer, with arguments, limits and return fields.
schedule_reminder
Schedule a one-time personal reminder, list pending reminders, or cancel one. Pushary stores it on the server and delivers to the authenticated creator's phone after the agent exits. It does not execute agent work or send campaigns.
{ "body": "Check the deploy", "inMinutes": 30, "requestId": "deploy-check-2026-10-09-1" }Keep body within 180 UTF-16 units and title within 100 after secret redaction (most emoji use two units). Oversized text is rejected rather than silently cut off. Use body plus exactly one of inMinutes (1–43,200, measured from the server clock) or at (a future ISO timestamp with an explicit UTC offset or Z, within 30 days). title, agentName, sessionId and machineId are optional. Never guess a timezone; confirm the returned UTC fireAt in the user's local time.
No arguments lists the creator's pending reminders. Listing and cancellation remain available when billing access closes; new schedules require an active Agent workspace. {"cancelReminderId":"..."} cancels a scheduled reminder; cancelled:false means it was not cancelled, including when dispatch has started. Responses include pending and a delivery hint; creation also returns reminder and duplicate. At most 25 reminders can be pending per user/site. env:"test" persists without delivery.
Use a unique requestId for each scheduling operation and reuse it unchanged on retries. The original reminder and time are returned even after it settles; changing the schedule or text with the same ID returns a conflict. SDKs generate an ID when omitted, but callers retrying a whole SDK call should supply their own stable ID. Older requests without an ID only receive short-window best-effort deduplication.
{"reminderId":"..."} retrieves your reminder after it leaves the pending list, including reason, settledAt and per-device delivery counts. SDKs expose this as client.reminders.get(id). dispatched means handed to the delivery path; sent means Expo accepted it; delivered means a provider receipt, not proof that a person saw it. A shared workspace API key identifies its creator; use each person's own key or OAuth connection for personal reminders.
Delivery normally follows within about a minute of the due time, requires a connected phone, and rechecks account access, membership and stop/mute. Retries reuse the existing durable push queue. Revoking the creating key cancels scheduled reminders; rotating a key preserves them. An online Mac may also announce it to the creator through the existing event stream. Scheduling success does not mean the notification was seen.
The same lifecycle is available through POST /api/v1/server/reminders (create or cancel) and GET /api/v1/server/reminders (list), client.reminders in the TypeScript and Python SDKs, and pushary_remind in Hermes. Agent and connector keys are supported; Partner bound keys are excluded.
These are the tools the Pushary MCP server gives your AI agent. MCP (Model Context Protocol) is the standard way an agent calls outside tools. With these tools an agent sends push notifications to your phone, asks you a question and waits for your answer, agrees a file boundary with you, and checks which of its parallel sessions is blocked.
Point any MCP client at the server with your API key. Most clients accept this shape:
{
"mcpServers": {
"pushary": {
"url": "https://pushary.com/api/mcp/mcp",
"headers": { "Authorization": "Bearer pk_xxx.xxx" }
}
}
}The endpoint speaks Streamable HTTP. npx pushary@latest setup writes this for Claude Code, Codex, Gemini CLI, Cursor and the other supported agents.
Which tools your key gets
| Key | Tools |
|---|---|
| Claude or ChatGPT connector | send_notification, schedule_reminder, ask_user, propose_scope, wait_for_answer, cancel_question |
Key from setup, pushary login, phone pairing or the Mac app | The six above, plus list_sessions |
| Key created in the dashboard | All of the above, plus the subscriber, campaign, template and flow tools |
| Key created in the dashboard, on a Partner or Enterprise plan | Adds the four Partner decision tools. Setup, pairing and connector keys do not get them |
A key bound to one of your end users gets only the Partner decision tools.
If your workspace has no active plan, send_notification, ask_user and propose_scope return a 403 with the code subscription_required. wait_for_answer and cancel_question keep working for questions already asked.
MCP push notifications
send_notification
Sends a one-way push notification to your connected phone and browser. Use it for a finished task, a failure, or an update you asked for. Use ask_user when you need an answer.
Prop
Type
Leave all three targeting fields empty to reach every connected device, including the phone app. context.type: "error" is delivered with high urgency. context.askQuestion embeds a question (up to 8000 characters, confirm, select with 2 to 6 options, or input) and returns a linkedCorrelationId you pass to wait_for_answer. The embedded question expires 10 minutes after it is created. Pushary removes secrets from the title and body before anything is stored or sent.
Returns sent (devices reached, web plus mobile) and delivery, with recipients for web and mobile and a status for a channel that reached nobody. Zero devices is a successful call, and warning says what to connect. When the push is held back by the kill switch, a snooze, your update setting or In agent mode, sent is 0 and suppressed says why. The notification is still recorded in the activity feed.
A task_complete notification that carries sessionId and machineId and no targeting is recorded as that session's completion instead. It returns completionId and status: "recorded", and delivery follows your update settings.
This sends real notifications to real people immediately.
Human-in-the-loop
ask_user
Sends a question to your phone and waits for the answer. Three question types: confirm (yes or no), select (pick one option) and input (free text).
Prop
Type
Hooks also fill some machine-populated fields: questions, toolPath, toolUseId, waitEndsAt, requestId, sessionToolAware and localSurfaceAvailable. A model calling the tool should leave them out.
Waits by default and returns the answer when one arrives within the policy window. Check answered and value: a recorded "no" is not approval. Updated clients follow handoffAction first when present, otherwise nextAction. While pending, keep calling wait_for_answer with the same correlationId. In Alert only (notify_only) mode the notification is awareness only and the decision stays in the current client. In agent mode keeps it in the client without a phone push. Ordinary full-access calls without waitEndsAt stay answerable until answered or cancelled and can receive one phone reminder after ten minutes, subject to delivery settings. Restricted connectors and native permission hooks retain their existing deadlines. Show the returned answerUrl when reporting a waiting question.
The result always has correlationId, question and type. Depending on the path it also carries:
| Field | Meaning |
|---|---|
answered, value, note | Whether you answered, the answer ("yes" or "no", the option, or the text), and any note you added |
status | answered, pending, cancelled, expired, missing, unavailable, notified, terminal or stopped. Only pending is a live wait |
timedOut | The first wait ended while the question was still live |
handoffAction, nextAction | What the agent does next. handoffAction wins when both are present |
deliveryMode, policyTimeoutMs, mode | The approval mode that applied, and its wait window |
delivery | Browsers and phones the question reached, with pending: true while delivery is still in flight |
suppressed | The phone push was held because a terminal on this machine is active |
noDevices | No phone, browser or Slack channel could receive the question. Do not wait; follow handoffAction |
answerSource | Where the answer came from, when known |
answerUrl, expiresInSeconds, warning, hint, held, env | The answer page, time left, a missing-channel warning, a next-step hint, why delivery was held, and the test flag |
For a tool approval, also pass toolTarget. The hook sets sessionId and machineId for you. See Human-in-the-loop for the complete flow.
propose_scope
Proposes the boundary of one agent run. Returns ratified, answered, correlationId, enforces and the stored contract.
Prop
Type
Only ratified: true establishes the contract. An answered refusal is different from no answer. Follow the returned handoff directives for an unanswered proposal. Scope narrows file access; it does not authorize risky actions within those paths.
enforces reports what the contract contains that can be checked, not what the gate on a given machine will do. An empty array means nothing in the contract is checked automatically: it is a recorded promise, and every action stays governed by the permission policy exactly as before. enforcementNote says what is true about the contract's limits; repeat it to the user.
Only file paths are enforced, and only on tool calls that carry one (Edit, Write, MultiEdit). Shell commands, reads, web requests and MCP tools carry no path, so a contract says nothing about them. A run that changes no files (marketing, sales, support, operations) belongs in promises: a campaign or account name in allowedPaths matches no file at all. Enforcement also needs a supported hook installation.
hookSeen: false means no hook has ever reported that sessionId. The contract is stored under a key the gate will never read, so it checks nothing. Send the session id your client reports for this run, or call list_sessions and match on the working directory.
After a Pushary release, one propose_scope call can fail until your client lists the tools again. An MCP client caches the tool list it fetched when the session opened. Pushary's tool schemas are strict, so a client still holding a list from before a release rejects a response that carries a field the release added.
It affects one call and fixes itself: the client lists the tools again and the next call succeeds. Restart the session, or reconnect the MCP server, to clear it at once. Long-lived sessions are the ones that see it, because they hold an old list.
wait_for_answer
Reads the answer or the current state of a question from ask_user, or of the linkedCorrelationId from send_notification. Waits until you answer or the timeout is reached. It never sends another question.
Prop
Type
Returns { answered: true, status: "answered", value } when answered, plus note and answerSource when present. Otherwise it distinguishes pending, cancelled, expired, missing and unavailable. Follow handoffAction when present, otherwise nextAction. Keep waiting with the same correlationId while pending. A poll ending is not expiry. Cancel only for an explicit handoff or when the answer is no longer needed. If cancellation returns handoffAction: "stop", stop. Otherwise, if cancellation returns false, poll once for 1 second and honor any answer that won the race. An unavailable state stops the handoff until it can be safely retried.
cancel_question
Cancels a pending question so it can no longer be answered.
Prop
Type
Returns { cancelled: true, correlationId } when a pending question was removed, with askHandoff: true when the session was marked as handing off to the current client. A false cancelled means the question was already answered, expired, missing or unavailable. When the state is unavailable, the response includes status: "unavailable" and handoffAction: "stop": stop rather than opening another answer surface. Cancelling never retracts an answer already recorded.
list_sessions
Read-only. Returns your site's live agent sessions, keyed by machine and session, and any pending approval questions, so you can see which of your parallel agents is blocked. It does not start, stop or steer agents, and it sends no notification.
Prop
Type
Returns { sessions, pendingQuestions }. Each session includes agentType, agentName, machineId, sessionId, computedStatus (active, idle, waiting, offline or errored), lastAction, lastSeenAt and a readable timeAgo. It sees only the API key's site, and no parameter can read another account. See the multi-agent control panel for what each status means.
This is the only agent-facing read of your fleet. It is safe to poll, writes nothing, and is the right tool for a supervising agent looking for the blocked session.
Partner decisions
Use these for your product's end users. They use the durable Decisions API, with a default lifetime of one hour and a maximum of 24 hours. They are separate from the operator-facing ask_user flow, and they need the Partner or Enterprise plan. See Partner setup.
enroll_end_user
Requires a full-access key on your trusted backend and takes externalId (1 to 256 characters). Agent and bound runtime keys cannot issue enrollment links. Returns externalId, universalLink, deepLink and expiresInSeconds. Show the link to the user; they finish enrollment in the Pushary app or a supported browser. Making a link alone does not register a device.
create_decision
Creates a decision without waiting. Requires question (up to 8000 characters) and an end user, through externalId or a bound key.
| Parameter | Meaning |
|---|---|
type | confirm (default), select or input |
options | 2 to 20 options of up to 200 characters, for a select question |
context | Background, up to 2,000 characters. Secrets are redacted |
agentName | Product or agent label, up to 120 characters |
idempotencyKey | Stable operation key, up to 200 characters. Reuse it only for retries |
expiresInSeconds | At least 60, at most 24 hours. Defaults to one hour |
callbackUrl | Signed answer webhook. Verify the X-Pushary-Signature HMAC before trusting the body |
requireReachable | Refuse with a 409 when the end user has no enrolled device or push subscription |
Returns decisionId, status, answered and pollUrl, plus reachability information when available. A fresh decision also includes decisionPageUrl. Only an answered confirm decision with an approving value is approval. The MCP tool exposes the parameters above; use REST or the SDK for structured action fields, presentation or approvalUrl.
get_decision
Takes decisionId and an optional waitSeconds (0 to 20). Returns the durable state. Poll the same id while it is pending, and stop on an answer, an expiry or a cancellation. Follow the returned state; a poll ending does not expire the decision.
cancel_decision
Takes decisionId. Returns { decisionId, cancelled, status }. A false cancelled means this call did not retract a pending decision; read the current state before deciding what happens in your workflow.
Use a bound key in a trusted per-user runtime. It fixes the recipient and limits access to creating, reading and cancelling that user's decisions. It cannot answer them. Keep it out of model-visible text anyway, because it can still open decisions and create enrollment links for that user.
Limits
| Limit | Value |
|---|---|
| Pending questions per agent session | 3 |
| Pending questions per site | 25 |
| Notifications per site | 10 every 5 minutes |
Ordinary full-access ask_user without a host deadline | Until answered or cancelled |
| Restricted connector questions and embedded notification questions | 10 minutes |
Longest single wait in ask_user, propose_scope or wait_for_answer | 55 seconds |
Connector keys have their own counter, so a shared connector cannot use up your CLI agent's limit. Over a limit, the tool returns an error that names it. Answer or cancel an open question to free a slot.
Subscribers
These tools need a key created in the dashboard.
list_subscribers
Lists push notification subscribers with optional filters. Returns a paginated list.
Prop
Type
get_subscriber
Gets full details of one subscriber by id, including browser, OS, location, tags and activity.
Prop
Type
count_subscribers
Gets subscriber counts by status: total, active and unsubscribed. Takes no parameters.
Campaigns
Templates
Flows
Next steps
Wire up human in the loop
The full ask, wait and hand-off flow, including what to do when nobody answers.
Call Pushary over REST
The same questions and notifications over plain HTTP, with an OpenAPI spec.