May I?

One question, held for 00:00

An agent is waiting to hear back from you.

It hit the part of the job only a person can settle. A yes or no, a pick between options, a sentence in your own words. May I? holds the question until one authenticated human answers, and then the agent resumes exactly where it parked. Approvals are one kind of question — the kind whose yes comes back as a signed receipt.

Start freeRead the docs

Open source · Apache-2.0 · MCP-native · hosted or on your own metal

What she is looking at

type
select
prompt
"Two builds passed CI for the 2.4 release. The hotfix alone is safer; the full release closes more tickets. Which one ships?"
options
full-release · hotfix-only · hold
expires
in 30 minutes
asked by
release-bot, over MCP, under a consent you can revoke

She picks the hotfix. The answer goes back to the agent as a signed event, the parked run resumes with her choice in hand, and the release goes out on the safer build. Had this been an approval — money moving, code shipping — the yes would also carry a receipt bound to the exact action she read.

What it does

Three shapes of question. One contract.

Approve or deny.

The receipt-bearing kind. The agent files the exact action it intends to take, and a yes comes back as a signed receipt bound to that action, the evidence, and the person. Your executor checks the receipt before anything runs.

Pick an option.

Up to twenty options, each with a label and description written for a person, not a parser. Allow a freeform answer alongside the list when the right choice might not be on it.

Answer in words.

A question and a text box. Release notes, a judgement call, the name of the customer to ring first. The answer comes back exactly as typed.

All three take the same request — a prompt, an expiry, an optional suggested person, an optional callback — and resolve the same way: answered, expired, or cancelled, delivered as one signed event. Approvals are the one with a receipt on top.

The whole of it

Your agent asks. A person answers. The work resumes.

The agent submits a question to May I?, which holds it until a person answers; the signed answer goes back to the agent, which resumes the parked work from where it stopped. HELDYour agentparked, over MCPMay I?the question, frozennotify · answer · signOne named humanYour agentresumed, answer in handmay i?signed answerno credentials, no execution, ever cross this line
May I? sits beside the work rather than in front of it. It never holds your credentials and never calls your API. The request returns pending in milliseconds, the answer arrives whenever the person gets to it, and the run that was parked picks up from the same line. Nothing waits in memory; the pause is durable.

Nobody at the keyboard

Unattended agents can finally ask someone.

A scheduled agent has no chat window and no user watching it. When it hits something a person should settle, it has always had two options: guess, or give up. May I? gives it a third. It files the question, parks, and the question lands as a push on someone's phone. The answer can come back three minutes later or on Monday morning — the run resumes either way, from exactly where it stopped, with nothing held open in between.

One honest note if you run on Eve: Eve deliberately keeps ask_questionout of task-mode and cron runs, because there is no user session to intercept, so the built-in path will not ask on a schedule by itself. You hand the run to the May I? channel from a schedule handler, or write a tool of your own that calls May I? directly. Either way the parking and the resume are the same.

Where it fits

Drop-in for Eve. A small SDK for everything else.

Any agent · @mayiapp/sdk

const input = await mayi.inputs.request({
  type: "select",
  prompt: "Which build ships?",
  options: [
    { id: "full", label: "Full release" },
    { id: "hotfix", label: "Hotfix only" },
  ],
  expiresInSeconds: 1800,
  callback: { url, state },
}, { idempotencyKey });
// returns PENDING; a signed callback resumes parked work.
// no callback? poll GET /api/inputs/:id instead.

Eve agents · @mayiapp/eve

// agent/channels/mayi.ts
import { mayiChannel } from "@mayiapp/eve";

export default mayiChannel({
  getAccessToken: () =>
    credentials.getAccessToken("mayi"),
});
// every ask_question — confirmation, select, text —
// now reaches a person. approve/deny still mints
// a receipt through the approvals path.

The Eve adapter is one channel definition: every human ask the agent raises routes to May I?, and approve/deny confirmations keep their receipt semantics untouched. Everything else — any framework, any language — is the SDK or plain HTTP against the same endpoints, with the same signed resolution either way.

Trust & verifiability

An agent can't claim an answer it never got.

Every resolution is signed by May I?, and every answer mints a durable attestation: who answered, what they said, and when. Both verify offline against published keys, so "a human said yes" is a claim anyone can check — at execution time, or in an incident review a year later.

Approvals carry a receipt on top, bound to the exact action, the evidence files, the approver, and the policy of the day. Edit the amount after approval or swap a file, and the receipt stops validating before your executor moves anything. Expiry is a denial; silence never becomes a yes.

None of this is the pitch. It is the floor under it — the reason an answer relayed through an agent is worth the same as one you heard yourself.

Hosted

Start asking in an afternoon.

Push notifications, the apps, key rotation, and the pager are ours. Free while you are small enough that a person can still read every question.

Start free

Self-hosted

One service and a database.

Same code, your infrastructure, your signing keys, no question ever leaving your network. Apache-2.0, and the answers verify the same either way.

Read the source

Asked before

Does May I? need access to my systems?+

No. It holds questions and returns signed answers. For approvals, your executor already has the credentials and keeps them; it just checks a signature before it runs. May I? never gets a key to the thing it is protecting.

What happens if nobody answers?+

The question expires on the deadline the agent asked for, and expiry is its own resolution — the agent is told, it is never left guessing. For approvals, expiry is a denial. Silence never becomes a yes.

Can an approver be tricked by a persuasive agent?+

The agent writes the prompt, and that is the one field it fully controls. For approvals, the call, the arguments and the file hashes are all rendered from the request itself, so the persuasion has nowhere to reach. She reads the action, not the argument for it.

How fast is it?+

The question is pending within a few hundred milliseconds and the notification lands about as fast as any other push. The rest of the latency is a human reading, which is the part you are paying for.

What if I want it on my own infrastructure?+

Run it there. Apache-2.0, one service and a Postgres database. The hosted version is the same code with the pager on our side.

The pause is the product.

Hosted, or self-hosted on your own infrastructure. Either way, May I? holds the question, keeps the proof, and never touches the thing itself.

Open the appDocs