The server is stateless and passes your key through to the BotShield API on every tool call, so a tool behaves like the matching API operation: same agent, same allowed categories, same error messages and codes. A request without a Bearer token gets HTTP 401.
If you rotate the agent’s key in the Console, update the key in your MCP client’s configuration at the same time. The previous key stops working immediately.
What the server covers
The server has five tools, all for Agents Ask:bind_session, check_binding, propose_resolution, check_resolution_status and cancel_resolution. They are listed under Tools.
BotShield Gate and Trusted Accounts have no MCP tools. A gate runs in the person’s browser through the web component, and a person secures an account by tapping the offer on your page. Neither is something an agent can start. Use the REST API and the widget for both.
Connect a client
Use any MCP client that supports remote servers over Streamable HTTP with a custom header.- Generic MCP client
- Claude Desktop
- OAuth 2.0 clients
Most clients that take a JSON server list accept this shape:Load the key from your client’s secret or environment mechanism rather than committing it to a file. Set the client’s tool-call timeout above 35 seconds; see Set the client timeout.
Tools
Every input is a flat scalar, a string or an integer, with no nested objects. That keeps the schemas easy for a model to fill and acceptable to strict tool catalogs.bind_session
Starts the one-time link with a human. Maps to POST /agent/bind-session.
Returns
status: "pending", a 6-character code, a claim_url, expires_at (10 minutes), and display_name. The agent should show the person both the code and the link.
bind_session always returns a code and a claim URL. It never returns an opaque_id, and it does not recognize a person who linked before, because BotShield cannot know who is in the conversation until they claim a code. Remembering the link is your agent’s job: store the opaque_id that check_binding returns against your own user record, pass that stored value to propose_resolution from then on, and do not call bind_session again for a person you already hold an opaque_id for.
check_binding
Checks whether the person has completed the link. Maps to GET /agent/check-binding.
Returns
status: pending, bound, expired, cancelled, not_found, or revoked. With bound it also returns the opaque_id.
The call holds until the person completes the link or the wait ends, whichever comes first, and answers as soon as the status leaves pending. While it returns pending, call it again right away; there is no need to pause between calls. Stop at any other status: expired and cancelled mean a new bind_session, and not_found means the code is wrong for this agent, which waiting does not fix.
propose_resolution
Asks the person to Confirm or Deny an action. Maps to POST /agentlink/inquire.
The detail row is sent only when both
detail_label and detail_value are present. Returns status: "queued", card_id, and ttl_at. The call returns as soon as the card is queued; the decision comes from check_resolution_status.
A ttl_seconds outside the range is not clamped. The tool returns an error with the code ttl_below_floor or ttl_above_ceiling, the same as the REST API. The tool does not take an Adaptive Card or a trusted_account_id. Call the REST API when you need those fields.
check_resolution_status
Reads the current state of a proposal. Maps to GET /agentlink/check-status.
Returns
status (queued, approved, denied, expired, cancelled), verdict (approve, denied, or null), resolution_jwt (the signed Proof of Resolution, or null), ttl_at, resolved_at, and ceremony_id.
The call holds while the proposal is queued and answers as soon as it reaches a final status, or when the wait ends. While it returns queued, call it again right away, until ttl_at. A person has to pick up their phone, so expect a few calls.
cancel_resolution
Withdraws a proposal that is still waiting. Maps to POST /agentlink/cancel.
Returns
status: "cancelled" with cancelled_at, or the current status with already_resolved: true if the proposal already had a final status.
Set the client timeout
check_binding and check_resolution_status are long-polls. With the default wait_seconds of 20, a call can take a little over 20 seconds to answer; with the maximum of 25, a little over 25. The server gives up on its own call to the BotShield API 10 seconds after the wait, so the longest a tool call can run is 35 seconds.
- Set your MCP client’s tool-call timeout above 35 seconds. 40 to 45 seconds works well. A shorter timeout cuts off a healthy wait, and the agent sees a client-side timeout instead of the person’s decision.
- If your client’s timeout cannot be raised that far, pass a smaller
wait_secondsso that the wait plus 10 seconds fits inside it.wait_seconds: 0makes the tool a single immediate check; leave a few seconds between calls in that case. - One held call replaces a dozen short polls, which matters in agent hosts that run one turn at a time.
Tool results
A tool result is one text block containing JSON.data.data.
Every failure is an MCP tool error: the result has isError: true, and its text is a JSON object with this shape.
The BotShield API reports handler errors inside an HTTP 200 response, at
data.error. The MCP server reads that envelope for you: an unknown opaque_id, a category the agent is not allowed, a revoked or wrong-environment key, and an out-of-range ttl_seconds all arrive as tool errors, never as a successful result that contains an error. The messages and codes are the ones listed in Propose an action and Link a human.
A 504 on propose_resolution means the outcome is unknown. Call the tool again with the same request_id: it is the idempotency key, so a proposal that did land is returned instead of duplicated.
The model asks; your code verifies
Tool calls are made by a language model, and a model cannot check a signature. Treat the MCP server as the way your agent reaches a human, not as the control that protects the action.- Keep the execution of the action in deterministic code that you own.
- Have that code take the
resolution_jwtand run the checks in Proof of Resolution before it does anything: signature, issuer, audience, expiry,jti, andverdict === "approve". - Never let a model’s summary of a tool result (“the user approved”) stand in for the proof.
- Subscribe to the
agents_ask.resolution.*webhooks if you want your backend to receive the proof without depending on the agent to pass it along.
Run a local MCP server from the SDK
Thebotshield-sdk npm package also runs as a local (stdio) MCP server. It exposes the SDK’s three action methods as propose_resolution, check_resolution_status, and cancel_resolution. It has no link tools, so obtain the opaque_id through the hosted server or the REST API. Node.js 20 or later is required.
--server-url; the package does not default to the production API. The --agent-key-auth value is sent as the Authorization header as written, so include Bearer . List every flag with:
Next steps
Register an agent
Create the production agent whose key you connect with.
Proof of Resolution
The verification your executing code must run.
Salesforce MCP setup
Register the server for Agentforce Employee agents.
TypeScript SDK
Call the same operations from your own code.
