Sess
Use Sess to have an agent ask a person through a private hosted link and continue with their verified answer.
Create a Sess form/link for approval, decision, or details with
sess_request, or a short question with up to 12 fields viasess_ask.Ask one person or many respondents through one link, using fields such as text, number, integer, boolean, choice, multi-choice, rating, records, headings, text blocks, and 1–5 action buttons.
Return a private
humanUrl, handle, revision, and expiry; anyone with the link can answer aslink_holder.Wait for and read answers with
sess_response, receiving only receipt-verified answers; validate human input before acting.Show a follow-up step on the same link with
sess_publish, or finish/close the page withsess_completeafter an answer.Use the hosted Streamable HTTP server or a local stdio server with
SESS_API_KEY, across supported MCP clients and custom agents.Configure link expiry, multi-respondent Ask limits, wait ceilings, state storage, API origin, and handle stable MCP tool errors.
This server has a remotely hosted endpoint. Open its connector listing for connection details, authentication requirements, and setup instructions:
Sess MCP server (@sess.si/mcp)
Let your agent ask a person and continue with their answer.
Install: add https://mcp.sess.si/mcp to your agent as an MCP server, and sign in to Sess when it asks.
Run: ask your agent "Ask me on my phone what time works for a call tomorrow, using Sess."
You'll see a Sess link in the reply: open it on your phone, answer, and your agent carries on with your answer.
Claude Code
Run this in your terminal:
claude mcp add --transport http sess https://mcp.sess.si/mcpClaude Code opens Sess in your browser. Sign in and choose Allow. Using Sess on a second computer reconnects it there and disconnects the first.
Or run it on your machine. Put your Sess API key in SESS_API_KEY (from your Sess account, under API
keys), then run this in the same terminal. Never paste the key into a chat.
claude mcp add --scope local sess -- npx -y @sess.si/mcp@0.2.0Related MCP server: mcp-surveys
claude.ai
In Claude, open Customize, then Connectors, choose Add custom connector and paste:
https://mcp.sess.si/mcpClaude opens Sess. Sign in and choose Allow.
ChatGPT
In ChatGPT, open Plugins, choose Add custom MCP server and paste:
https://mcp.sess.si/mcpChatGPT opens Sess. Sign in and choose Allow. Some ChatGPT plans allow only tools that read. Sess creates a link for you, so your plan needs to allow tools that make changes.
Grok
Grok Build: remote MCP
Run this in your terminal:
grok mcp add --transport http sess https://mcp.sess.si/mcpSign in to Sess when Grok Build asks, and choose Allow.
If it doesn't ask, type /mcps, select sess and press i.
Paste this into Grok Build:
Ask me on my phone what time works for a call tomorrow, using Sess.Grok Build asks before it uses each Sess tool. Choose Always allow.
grok.com: Connectors
On grok.com/connectors, choose New Connector, then Custom, and enter:
https://mcp.sess.si/mcpSign in to Sess when Grok asks, and choose Allow.
Cursor
Paste this into Cursor's mcp.json (.cursor/mcp.json in a project, or ~/.cursor/mcp.json):
{
"mcpServers": {
"sess": { "url": "https://mcp.sess.si/mcp" }
}
}Cursor opens Sess in your browser. Sign in and choose Allow.
Or run it on your machine. Put your Sess API key in SESS_API_KEY (from your Sess account, under API
keys), then use this entry instead. Never paste the key into a chat.
{
"mcpServers": {
"sess": {
"command": "npx",
"args": ["-y", "@sess.si/mcp@0.2.0"],
"env": { "SESS_API_KEY": "${env:SESS_API_KEY}" }
}
}
}Antigravity
Put your Sess API key in SESS_API_KEY (from your Sess account, under API keys), then run this in
your terminal:
agy mcp add sess npx -y @sess.si/mcp@0.2.0Antigravity asks before it uses a Sess tool, usually once or twice the first time. Choose Allow, or Always allow if you'd rather not be asked again. Never paste the key into a chat. Sign in to Antigravity with your Google account, or use a Gemini API key.
OpenClaw
Sess asks you through your Claw. The agent gets four tools and uses them only when a decision is
yours. OpenClaw uses the Sess plugin, @sess.si/openclaw, rather than this server.
Install. OpenClaw warns that the package comes from npm, not ClawHub. That is expected: Sess is published on npm with a signed build. Answer
y.openclaw plugins install npm:@sess.si/openclaw@0.1.0 --no-enable openclaw sess setup --yesGive the Gateway your key. It reads the key from its own environment file. Sess never stores it. The first line waits for you to paste the key and press Enter; nothing shows on screen.
read -rs K && printf 'SESS_API_KEY=%s\n' "$K" >> ~/.openclaw/.env && unset K && chmod 600 ~/.openclaw/.env openclaw gateway restartAsk. In any chat, say something like: Plan my weekend and check with me through Sess before you decide. Your Claw sends you a Sess link. Answer it on any device, then tell the chat to continue. The answer it uses is verified.
Check: openclaw sess doctor shows the setup without contacting Sess or printing your key.
Cline
In Cline, open MCP Servers, then Configure MCP Servers, and add this to
cline_mcp_settings.json:
{
"mcpServers": {
"sess": {
"type": "streamableHttp",
"url": "https://mcp.sess.si/mcp"
}
}
}When Cline connects, sign in to your Sess account in the browser. No key goes in the file.
Or run it on your machine with this entry instead. Type your Sess API key (from your Sess account,
under API keys) into the file yourself, in place of your Sess API key. Never paste the key into a
chat.
{
"mcpServers": {
"sess": {
"command": "npx",
"args": ["-y", "@sess.si/mcp@0.2.0"],
"env": { "SESS_API_KEY": "your Sess API key" }
}
}
}Your own agent
Connect your MCP client to the hosted server over Streamable HTTP. It signs in with your Sess account.
https://mcp.sess.si/mcpOr add this as a local MCP server in your agent:
npx -y @sess.si/mcp@0.2.0Your agent asks before it starts the server. The server reads your Sess API key from the
SESS_API_KEY environment variable; never paste it into a chat.
No MCP? Add Sess to your agent shows a tool and handler on the Sess SDK for an agent you build on a model API.
First interaction: approve before acting
Have your agent check with you before it commits a change or sends a reply. Ask it something like:
"Before you commit, use Sess to show me what changed and the commit message. Wait for my approval and commit with the message I approve."
The agent calls sess_request and shows you a Sess link at once. Open it on your phone. You
see what changed, and the proposed message in a field you can edit. Change a line if you like, then
approve, or stop. The agent waits with sess_response and continues only on the verified answer:
it commits with the message you approved, or does nothing if you stopped. If you are slower than
its 45-second wait, it checks again for about two minutes, then tells you the form is still open;
answer it and say continue.
The same works for a reply: "Before you send this reply, use Sess to show me the draft so I can edit and approve it."
A synthetic sample of what the agent sends (sample content, not from a real repository):
{
"mode": "interactive",
"title": "Approve this commit?",
"source": {
"kind": "sess_surface",
"components": [
{
"type": "record",
"label": "What changed (sample)",
"fields": {
"src/greeting.js": "Greeting now uses the user's first name",
"test/greeting.test.js": "One new test for the first-name greeting"
}
},
{
"type": "field",
"name": "message",
"label": "Commit message",
"multiline": true,
"schema": { "type": "string", "minLength": 1, "maxLength": 2000 },
"value": "Greet users by first name\n\nAdds a test for the new greeting."
}
]
},
"actions": [
{ "id": "approve", "label": "Approve and commit", "intent": "approve" },
{ "id": "stop", "label": "Stop", "intent": "reject" }
]
}Text blocks are shown as plain paragraphs, so send a summary of the change rather than a raw diff; line breaks are kept in multiline fields.
Tools (sess-mcp-tools/5)
Tool | Input | Result |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
After an answer, sess_complete is the rule, not an option: when sess_response returns state
answered and the agent has no follow-up question for the person, it must call sess_complete
next, so the person's page shows that the agent has their answer. Until then the page keeps waiting
for the next step. Only to show a follow-up does the agent call sess_publish instead. The server
instructions and both tool descriptions say this.
While there is no answer yet, the host calls sess_response again, up to three times after its
first call (about two minutes more at the 45-second ceiling), then asks the person to say continue. The
server instructions, the sess_response description and the text of a waiting or needs_resume
result say this. The server keeps no count; the host does.
sess_ask is a shortcut for sess_request: it turns a question and up to 12 simple fields
(text, number, integer, boolean, choice, multi_choice, rating) into an ordinary interaction,
checks it with the same rules and limits, and runs the same request operation. It creates exactly
one Session, returns the link and handle at once, never waits and never returns an answer. Continue
with sess_response, sess_publish and sess_complete as usual.
Advanced: expires_in sets how long the link stays open, in whole seconds from creation: from 30
up to your plan's link lifetime (Sess checks the ceiling). The result's expiresAt is when the link
stops taking answers, as created; on a link of 20 hours or less the person may ask for more time, up
to a fixed horizon. While the link is lapsed (past that time but still extendable) sess_response
reports waiting. It reports expired once the link has closed for good with no answer: it is
final, and nothing was completed. An answer accepted before the end is still returned as answered.
Advanced: respondents: { max } on sess_ask asks many people through one link (a Sess Ask):
share the link with everyone who should answer; each person gets their own page. A respondent is a
browser, not a person. The cap is exact: it is checked when a person starts, so it cannot be
overshot. respondents also takes name (a required "Your name" field), expires_in (the one
deadline, instead of the top-level expires_in) and respondent_expires_in; each person's page
completes when their answer is accepted. The result carries interaction.ask.id. sess_response
on that handle reads once, without waiting, and returns interaction.ask: whether it is open, the
counts, and each person's verified answer so far. Its state is waiting while answers may still
come and closed once none can. sess_publish and sess_complete refuse an Ask's handle.
Input schemas are closed: unknown properties, unsafe keys (__proto__, constructor,
prototype), more than 8 levels of nesting, an interaction over 32 KiB, more than 24 fields or 5
actions are rejected, never truncated. interaction is the Sess interactive Surface used by
@sess.si/sdk (heading, text, record and field components). Tools never accept an API
origin, key, workspace, Session ID or callback URL.
Keep it short
A person reads the form on a phone. Every field a person reads carries its purpose, style, limit
and examples in the tool schema, and sess_ask's description carries one complete good call.
Lengths are Unicode code points:
What | Style | At most |
Title ( | A question or a task, about 40 | 80 |
Button (action | A verb, 1–3 words | 24 |
Field label, option label, | Short | 60 |
Help (field | One hint | 160 |
| One or two sentences | 280 |
| Short | 60 |
| A few words | 200 |
| As written | 40 / 200 |
| Plain text | 4,000 |
A choice without an option_labels entry shows its enum ID, so that ID is held to 60 too (named at
its enum entry, e.g. /interaction/source/components/2/schema/enum/1); a long ID with a short
label is fine.
A value over its limit is refused before anything is sent or saved, never truncated, with
INVALID_ARGUMENTS and the value's field (a JSON pointer into the arguments, such as
/question or /interaction/actions/0/label; for a record key it ends with that key), the
limit and its length. The message is Sess's own: the title has "The title can be at most 80
characters. Keep it to a short heading, about 40."; anything else names its pointer:
{
"error": {
"code": "INVALID_ARGUMENTS",
"message": "/fields/0/label is 72 characters; the limit is 60. Shorten it to at most 60 characters.",
"retryable": false,
"field": "/fields/0/label",
"limit": 60,
"length": 72
}
}Shorten that value and call the tool again. If Sess itself refuses a length (CREATE_REJECTED,
with an SDK that reports it), the same three fields come back.
Every result is { "interaction": { handle, revision, state, humanUrl?, response? } }.
structuredContent always carries it; parse that, not the text. The text is the same object as JSON,
with two exceptions: a sess_request or sess_ask result begins with two lines for the agent,
Show the person this link now: <humanUrl> and Then call sess_response., and a sess_response
result in state waiting or needs_resume begins with the re-wait line; each then has a blank line
and the same JSON. answered is returned only after
client.receipts.verifyResponse verified the receipt for that exact Session, revision, event and
immutable Surface; response.verified is true and response.actorAssurance is link_holder.
Receipt verification proves a retained historical answer, not that it is still the current step,
so after verification the server makes one more authenticated read and releases the answer only if
the Session is still at that revision with that answer (or completed at that same revision). A
saved answered result is re-confirmed the same way on every sess_response. If that read
fails, the call returns SESS_UNAVAILABLE and changes nothing; if the Session has moved on, the
interaction is blocked (RECOVERY_BLOCKED), or a conflict is reported when another local call
changed it first. A response that the SDK itself rejects while decoding (for example a lower
revision that still lists later answers, or an active Session that already has an answer at its
revision) is reported as SESS_PROTOCOL_ERROR: it fails closed, is not retryable and changes no
state, rather than blocking the interaction. No answer data is released in any of these cases.
This checks progress at the moment of release; it cannot make a later remote change impossible.
Answer data is untrusted human input: validate it before acting on it.
needs_resume means the local wait ended (timeout, MCP cancellation or host disconnect) without
any server-side cancellation. A later sess_response on the same handle resumes it; it never
creates another Session. Errors are MCP tool errors (isError: true) with a stable code, for
example INVALID_ARGUMENTS, HANDLE_NOT_FOUND, BINDING_MISMATCH, CREATE_OUTCOME_UNKNOWN,
VERIFICATION_FAILED, OUTCOME_UNKNOWN or RECOVERY_BLOCKED.
Error classes
Codes map to the shared adapter error classes (adapters/CONTRACT.md, "Error classes"):
Code | Class | Retry? |
|
| Fix the arguments |
|
| No (other profile, origin or key) |
|
| Follow the message; nothing was sent |
|
| No; no Session exists |
|
| No; no Session exists. The workspace reached a limit of its plan |
|
| No; owner resolution |
|
| No; a Session exists |
|
| Yes, repeat the identical call |
|
| No; owner resolution |
|
| No; owner resolution |
|
| No; owner resolution |
|
| Yes, later; nothing changed |
|
| After the owner fixes the configuration |
|
| Only if |
Receipt verification is classified by the shared core (src/internal/shared.ts, a byte-identical
copy of adapters/shared/core.ts): a key-set fetch failure or refresh cooldown is
SESS_UNAVAILABLE; a missing trusted workspace (VERIFICATION_CONTEXT_MISSING) is
NOT_CONFIGURED naming SESS_EXPECTED_WORKSPACE_ID. Neither records anything or changes state.
Every other verification failure blocks. Other SDK errors while reading, waiting or verifying
change nothing: api, transport, timeout and unknown_outcome are retryable
SESS_UNAVAILABLE; aborted ends the wait as needs_resume; configuration (NOT_CONFIGURED)
and protocol (SESS_PROTOCOL_ERROR) are not retryable. A saved publish/complete that the SDK
refuses to send because of its own deterministic checks (not_sent with kind configuration)
blocks like a definitive rejection.
Configuration (environment of the server process only)
Variable | Meaning |
| Required. Read on the first tool call, never at startup or from arguments. |
| Default |
| Profile name, default |
| Absolute state directory. Default |
| Lower the 45 000 ms wait ceiling. |
| Receipt issuer if it differs from the API origin. |
| Trusted workspace for a runtime that does not return |
| Testing only: |
Security and support boundary
Owner-private, single profile. stdio carries no authenticated conversation identity, so this server does not invent one. Anyone who can call its tools acts for the configured key. Run it only for your own private client. Handles are bound to the profile, API origin and a credential generation (a keyed digest stored per profile; the key itself is never written). A handle from another profile, origin or key is rejected.
The Sess link is a bearer capability. It is returned only by
sess_request(or its shortcutsess_ask), to your private client. It is never written to stderr diagnostics, which carry only fixed event names and codes. Anyone with the link can answer aslink_holder.Durable local records. Each handle has a file (mode 0600) written by temp file, fsync, rename and directory fsync. The create intent is saved before the create request, and the exact publish/complete request (body,
If-Match, idempotency key) before dispatch. A lost create outcome is never retried (CREATE_OUTCOME_UNKNOWN); a lost publish/complete outcome is reconciled by repeating the identical call, which replays the saved request. A replay of an already confirmed publish or complete succeeds only with the identical expected revision and body; anything else is refused without a request. A deterministic rejection (for example a conflict or validation error) moves the record toblocked.blockedandcreate_unknownrecords need owner review; the server never leaves those states on its own (see below). If a confirmed create cannot be saved locally,CREATED_NOT_SAVEDsays not to repeat the request. After completion the link and answer data are removed from this server's own local record; that clears its copy only, and the hosted Session and receipt follow their own retention. At most 100 unfinished interactions are kept per profile.No wake-up. A wait is one bounded read of at most 45 seconds. The host is told to call again up to three times, then stop. The server cannot wake an agent after its turn ends; the person says continue.
Not in this package: an HTTP server or bin flag, a hosted server, OAuth, MCP Tasks, Apps, elicitation, resources or prompts (the HTTP composition below is a library for a hosting runtime, not a runnable server). One server process per profile is supported; concurrent processes on the same profile are not coordinated. Rotating the key makes earlier handles unusable (
BINDING_MISMATCH).Receipt-key rotation window. The SDK refreshes its cached receipt key set at most once per 10 seconds. If Sess starts signing with a new key and an answer signed with it is verified within 10 seconds of this server's previous key-set fetch, the SDK reports
UNKNOWN_KEY_ID, and the interaction is blocked (VERIFICATION_FAILED) even though the answer may be valid. Use the owner procedure below; fixing this needs an SDK change.
HTTP composition (library for a hosting runtime; not a deployed endpoint)
@sess.si/mcp/http exports createSessMcpHttpHandler(options), a stateless Streamable HTTP
handler for the same five tools. It is built on createMcpHandler from
@modelcontextprotocol/server with its default stateless legacy fallback, so one endpoint serves
2026-07-28 clients and 2025-era clients (initialize answers 2025-11-25; no session ID). Each
request gets a fresh server and adapter. Nothing in this package starts a listener; the hosting
runtime mounts handler.fetch (web-standard) or handler.node (node:http).
The hosting runtime supplies the authority; this package never decides it:
authenticate(request)(required) sees the method, URL and headers, verifies the request's credentials and returns a principal context:workspaceId,principalId,clientId,grantId,grantGeneration,scopesand acredential()provider for Sess API calls. If it returns nothing, the answer is401withWWW-Authenticate: Bearer resource_metadata="…"; a principal without everyrequiredScopesentry gets403 insufficient_scope. The handler never derivesauthInfofrom headers and never sends the MCP bearer token to Sess or to a tool.handleStore(binding, principal)(required) returns a handle store bound to that authority. Handles are bound to origin, workspace, principal, client, grant and grant generation: another principal, host or workspace getsHANDLE_NOT_FOUND; a revoked or re-issued grant (new generation) getsBINDING_MISMATCH; a token refresh (same generation) keeps the handles. The operations re-check every loaded record, whatever the store does. The durable hosted store is not in this package.
In front of the SDK entry, in this order on both faces:
A header-only preflight before any body byte is read: the
Hostallowlist (required), an exactOriginallowlist (requests withoutOrigin, as non-browser clients send them, are accepted), then exactly oneauthenticatecall and the scope check. A rejection is answered at once, even while the client is still sending; the Node face then closes the connection.A bounded body (default 256 KiB: a larger declared
Content-Lengthor streamed body gets413; non-JSON gets415from the SDK) read under the request deadline.The raw-body unsafe-key guard, answered in the request's protocol era.
The deadline (requestTimeoutMs, default 60 s) is absolute from the moment the request enters
the handler. A body that has not arrived by then gets 408 and reading stops (the Node face
closes the connection); an authenticate hook that has not answered gets 503. During tool
execution the same deadline ends an in-flight sess_response wait as needs_resume; a create,
publish or complete already sent is never abandoned mid-flight. The timer is cleared when the
response completes or fails, or when the client disconnects. The Sess link is returned to the
agent in the tool result exactly as over stdio and never appears in diagnostics.
Resolving a blocked or unconfirmed interaction (owner procedure)
The tool error names the state (RECOVERY_BLOCKED with a reason, CREATE_OUTCOME_UNKNOWN or
CREATED_NOT_SAVED). The server never repairs these records itself.
Find the record:
<state dir>/profiles/<profile>/handles/<handle>.json. It can contain the Sess link and answer data; do not paste it into chats or issues.Check the Session in Sess (developer portal or an owner API read using
sessionIdfrom the record, when present). Forcreate_intent/create_unknown/CREATED_NOT_SAVED, look for a Session created at that time before asking the agent to create another one.Decide in Sess what should happen to the Session, then remove the local record file to retire the handle. Keep a private copy if you need the evidence. There is no command that resumes a blocked handle in this version; start a new interaction with
sess_requestif needed.
Development
The adapter is an independent package with its own lockfile. package.json always declares the
exact SDK version from the repository's single pin, release/sdk-candidate.json (name, version,
filename, SHA-256, SHA-512). package-lock.json is a maintainer file only; npm never ships it to
consumers. The lock resolves the SDK to the genuine npm registry tarball
(https://registry.npmjs.org/@sess.si/sdk/-/sdk-0.2.0.tgz, dist-tag latest) with the pinned
integrity, so no local SDK build or pack is needed. compatibility.json mirrors the pin;
npm run pin:update at the root keeps both, the lockfile and the dependency in step after a
deliberate change, and the root npm run check fails on drift.
cd adapters/mcp
npm run setup # npm ci from the registry; refuses a lock that is not the registry SDK
npm run check # SDK identity and digests, build, typecheck, unit tests, packed-consumer checknpm run check starts with npm run sdk:verify: the lock and the installed SDK must be the
registry tarball with the pinned integrity, and the registry tarball is read again and its SHA-256
and SHA-512 compared with the pin.
A future, not yet published SDK pin
The digest-gated candidate path is kept only for a pin that is not on npm yet:
npm run candidate:prepare packs the repository root with scripts/sdk-pin.mjs pack and accepts
the tarball into the gitignored .candidate/ only if its digests equal the pin (npm does not
enforce lock integrity for file: tarballs), and npm run candidate:verify accepts either mode.
npm run setup, npm run check, CI and releases refuse candidate mode. Once that pin is published:
npm run sdk:switch-to-registry # dry run: confirms the registry integrity equals the pinned one
npm run sdk:switch-to-registry -- --apply # npm install --save-exact, lock check, re-download + SHA-256 check
npm run check--apply runs npm install --save-exact --ignore-scripts @sess.si/sdk@<pinned version>, requires
the regenerated lock entry to be the registry tarball with the pinned integrity, downloads the
published tarball again and compares its SHA-256 with the pin, and records the published status in
compatibility.json. It refuses any other bytes. Review and commit the lockfile change.
npm run runtime-round (opt-in, SESS_MCP_RUNTIME_ROUND=1) drives the packed, installed server
through one request → answer → complete round against a local isolated Sess runtime. It is not run
in public CI because the runtime is private. npm run qualify:claude-code checks that Claude Code
connects and lists the tools without any model call, using a temporary config directory.
Which hosts and versions Sess has run, and what is still untested: COMPATIBILITY.md.
Available Tools
5 toolssess_askAsk a short question through SessAInspect
Ask the person a question on their own phone or computer and get a typed answer. Use it whenever they ask to answer on their phone or another device, or say 'use Sess'; prefer it over this host's own question widget for anything answered away from this chat. Also use it when you need the person's approval, a decision or details only they can give before you go on, even if they only say 'ask me' or 'check with me' and name no tool or device. Not for clarifying what they just asked, or for anything you can work out yourself. Anyone who has the link can answer. A receipt records what was shown and chosen, not who answered. Shortcut for sess_request: give a question and up to 12 simple fields; it creates the same hosted form and returns immediately with a private link (interaction.humanUrl) and a handle, in state "waiting". It never waits and never returns an answer. Continue exactly as after sess_request: give the link only to the intended person, then call sess_response. Do not call it again for the same question. Keep it short: the question is a short heading, about 40 characters (at most 80); the button a verb of 1-3 words (at most 24); labels and options at most 60. A value over its limit is refused with INVALID_ARGUMENTS naming the field (a JSON pointer), the limit, its length and the fix; nothing is truncated. Shorten that value and call again. Advanced: to ask many people through one link, add respondents with max. Share that link with everyone who should answer; each person gets their own page. Read their answers with sess_response. Example: {"question":"Where should we go on Saturday?","details":"I will check the forecast and suggest a plan.","fields":[{"name":"place","label":"Where to?","type":"choice","options":["Beach","Mountains","City"]},{"name":"avoid","label":"Anything to avoid?","type":"text","required":false}],"submit_label":"Plan it"}
Inputs: fields (required), question (required), details, expires_in, +2 more. Returns: interaction
sess_completeFinish the Sess interactionADestructiveIdempotentInspect
Required once you are done with the person’s answer: until you call it, their page keeps waiting for your next step. It closes the page to new answers and does not delete what was given. Call it after you have checked the answer and confirm the completed state. If the outcome is unknown, repeat the identical call.
Inputs: handle (required), expected_revision (required), response_event_id (required). Returns: interaction
sess_publishShow the next Sess stepAIdempotentInspect
Replace the form with the next interaction after a verified answer, keeping the same private link. Requires the verified answer for expected_revision. If the outcome is unknown, repeat the identical call.
Inputs: handle (required), interaction (required), expected_revision (required), response_event_id (required). Returns: interaction
sess_requestAsk a person through SessAInspect
Use it when you need the person's approval, a decision or details only they can give before you go on, even if they only say 'ask me' or 'check with me' and name no tool or device. Not for clarifying what they just asked, or for anything you can work out yourself. It is the full form, with approve and reject buttons if you need them; sess_ask is shorter for a plain question. Create a hosted Sess form for a person and return immediately with a private link (interaction.humanUrl) and a handle. Give the link only to the intended person in this private conversation; anyone holding it can answer. A receipt records what was shown and chosen, not who answered. Then call sess_response with the handle and revision. Do not call sess_request again for the same question. Keep it short: a title of about 40 characters (at most 80), button labels a verb of 1-3 words (at most 24), field labels at most 60, help one hint (at most 160). A value over its limit is refused with INVALID_ARGUMENTS naming the field (a JSON pointer), the limit, its length and the fix; nothing is truncated. Shorten that value and call again.
Inputs: interaction (required), expires_in. Returns: interaction
sess_responseRead the person’s Sess answerARead-onlyIdempotentInspect
Wait a bounded time (default and maximum 45 s; wait_ms 0 reads once) for the answer to exactly this revision. state "answered" means the answer passed full Sess receipt verification. state "expired" means the link closed with no answer: it is final; create a new form if you still need one. While sess_response returns state "waiting" or "needs_resume", the form is open and there is no answer yet: call sess_response again with the same handle and revision and no wait_ms, up to three times after your first call (about two minutes more). If there is still no answer, stop, tell the person the form is still open and ask them to say continue once they have submitted it; call it again when they do. Do not poll beyond that, and do not ask the same question again. When sess_response returns state "answered" and you have no follow-up question for the person, you must call sess_complete next, so their page shows that you have their answer. Call sess_publish instead only to show them a follow-up. For a handle from sess_ask with respondents (an Ask), it reads once, without waiting, and returns interaction.ask: whether it is open, the counts and each person’s verified answer so far. Read it again later for more answers; state "closed" means no more answers will come. Do not call sess_publish or sess_complete for an Ask: each person’s page completes on its own.
Inputs: handle (required), revision (required), wait_ms. Returns: interaction
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 5 tools
sess_request and sess_ask are explicitly overlapping — the latter is described as a 'shortcut for sess_request', and its description repeats the identical approval/decision paragraph, leaving the agent to choose on form size alone. sess_response, sess_publish and sess_complete are reasonably well delineated by state transitions, but sess_ask also doubles as a multi-respondent 'Ask' with a different sess_response behavior, which muddies boundaries further.
All five tools use a consistent sess_ prefix and snake_case, which is easy to pattern-match. Minor deviation: four names are verbs (request, publish, complete, ask) while sess_response is a noun, breaking the verb_noun rhythm slightly but not the readability.
Five tools is well-scoped for a form/interaction lifecycle: create, read answer, advance, finalize, plus a lightweight create variant. Nothing feels padded or missing at the count level.
The core lifecycle is covered end to end (create → poll response → publish follow-up → complete), with states like waiting/expired/closed handled. Gaps: there is no explicit cancel/abandon operation for a form the agent no longer wants, and no way to inspect a form's current content or list outstanding handles — agents must work around this via sess_response polling.
Maintenance
Related MCP Connectors
Human-input bridge for AI agents with voice-first answer links, MCP tools, and HTTP APIs.
Give any AI agent a way to ask a person — for approval, a decision, an answer or a review.
Let your AI agent ask a person for a decision, a date or a photo, and get a structured answer back.
Talk to your public-facing AI from any MCP client — Claude, ChatGPT, Cursor, Cline, Windsurf.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceAn MCP server that inverts AI-human interaction: AI agents can ask humans questions via a browser console, blocking until answered.MIT
- AlicenseNot gradedqualityCmaintenancePublic MCP server for agent-created, human-friendly, short-lived surveys. Enables agents to ask structured questions and retrieve answers.2MIT
- AlicenseNot gradedqualityAmaintenanceMCP server that provides an ask_human tool so AI agents can request decisions from users via chat apps, with deadlines, defaults, and an append-only audit log.93 npmApache 2.0
- AlicenseNot gradedqualityCmaintenanceEnables any MCP-compatible AI agent to ask humans questions in real time via a mobile-friendly PWA with push notifications, operating local-first without third-party services.MIT