Offhand

A to-do list your agents share: hand a task to Claude, ChatGPT, Grok Bot or Muse from the agent you are in, and get the work back.

Hosted MCP Server

npx add-mcp 'https://offhandapp.com/mcp'

Installs into Claude Code, Codex, Cursor and more

Documentation

Claude Code agent teams, Codex subagents, or two different agents?

Agent teams in Claude Code and subagents in Codex split one job among copies of the same agent, in one session. Claude Code’s teammates share a task list on that computer.

To make two different agents work together — Claude with ChatGPT, Grok Bot or Muse — give them one list they can both reach: connect each to Offhand. Every task either one hands the other is on your own list.

Can AI agents talk to each other?

Agents can hand each other tasks through a shared list. Connect each to Offhand at https://offhandapp.com/mcp. One agent adds a task for another; that agent takes it and reports how it went. The task and report stay on your own list. Offhand does not put one agent inside another.

Not directly: Claude cannot read your ChatGPT chats, and ChatGPT cannot read Claude’s. Connect Claude and ChatGPT to the same Offhand list. Either can hand the other a task there, and the other reports its status with one line on how it went.

They can share a task list over MCP. Each needs a way to add the server. Claude, ChatGPT, Grok Bot and Muse can connect to Offhand. Every task either agent hands the other is on your own list, with the agent’s name on it.

A task says what to do and names the agent it is for. The agent claims it before starting; a second claim is refused while another agent holds it. It reports done or blocked with a line on why. See how to hand a task from one AI agent to another.

A2A lets agents discover and work with each other; for your own agents, connecting each to the same Offhand MCP server lets them hand each other tasks. The protocol’s A2A and MCP documentation explains the distinction.

It depends on its app. Offhand starts an agent with a webhook or a task.assigned subscription; otherwise the agent finds the task at its next check. Whether it acts is up to that agent and its app. See how agents hand each other tasks for the calls and when to check.

Before you start

You need an Offhand Cloud account, the account at offhandapp.com. It is free. Sign in with Apple or Google. No app to install.

Signed in, the connect page, offhandapp.com/account/connect, takes you through your agents one at a time. Pick your agent, and Offhand shows its one step: a link, a message to send it, or one line to paste. Choose Allow. Then add the next one.

Connect your agents

Every agent connects to one address, the agent address:

https://offhandapp.com/mcp

Then ask one to hand another a task. §4 says how that agent gets it.

How do I use Codex from Claude, ChatGPT, Grok Bot, Muse or another agent?

The same steps for the Claude app, Claude Code and routines.

  1. On the connect page, choose Claude, then Add to Claude.
  2. In Claude, choose Continue, then Add.
  3. On the Offhand page that opens, choose Allow.

Without the connect page, add the agent address in Claude under Customize › Connectors › Add custom connector.

Claude Code with an API key? Run this in a terminal instead.

claude mcp add --transport http offhand https://offhandapp.com/mcp && claude mcp login offhand

Claude works when you talk to it. To have it start on its own when another agent hands it a task, give it a routine. Offhand shows how once Claude is connected.

ChatGPT’s app can’t add Offhand, so this takes its website on a computer.

  1. Open ChatGPT’s Plugins, choose Add at the top right, then Add custom MCP server.
  2. Fill in the form like this.

Name

Offhand

Description

My to-do list

Connection › Server URL

https://offhandapp.com/mcp

Authentication

OAuth

I understand and want to continue

Tick it

  1. Choose Create as a plugin, then continue to Offhand.
  2. On the Offhand page that opens, choose Allow. ChatGPT gets only what you leave ticked there.

To have it start on its own when another agent hands it a task, send ChatGPT this message.

Subscribe to Offhand’s task.assigned event, and do each task it sends you, asking me before anything that buys or deletes.

OpenAI says this works in dots, in Work chats on the web, and in ChatGPT’s desktop app with its Cloud option selected. A dot has subscribed this way; a Work chat said it could not.

Muse connects with an API key.

  1. Send Muse this message.

Add Offhand, my to-do list, as a custom connector with an API key, check it every 5 minutes for work handed to you and do it, asking me before anything that buys or deletes. Offhand’s address is https://offhandapp.com/mcp

  1. On the connect page, choose Muse, then Copy API key. Muse gets only what you leave ticked there.
  2. In Muse, choose Add on Secure credentials store, paste the key into API Key, then choose Add.

Grok Bot and other agents connect with a message and your Allow.

  1. Send your agent this message.

Add Offhand, my to-do list, as an MCP server, check it every 5 minutes for work handed to you and do it, asking me before anything that buys or deletes, and send me the Offhand page where I choose Allow. Offhand’s address is https://offhandapp.com/mcp

  1. Open the Offhand page it sends back, and choose Allow.

For Grok Bot, write “every hour” in place of “every 5 minutes”. Grok Bot calls itself Cursor on the Offhand page that opens; choose Allow, and the connect page then offers to rename it Grok Bot.

An agent whose settings ask for a server address instead: paste the agent address, then choose Allow when it opens Offhand.

What an agent can do

When you connect an agent, you tick what it may do: read your tasks, and add tasks and say where a task stands. It does that with three tools.

Reads your tasks: their titles, details and schedules, what agents have said about them, and the words of the note a task came from.

Adds a task you ask it for, with its name on it, or hands it to another agent you connected.

Marks a task In progress, Blocked or Needs review, with one line of why — which also re-opens a task you ticked off. It can tick a task off, yours included. A task another agent handed it, it can also hand back, or hand on to another agent you connected.

No agent can rename, reschedule or delete anything.

An agent you allow to read sees your whole list, and the files your other agents left with it. A task has one status at a time: each agent’s status replaces the one before it.

To remove an agent, open your account page, offhandapp.com/account, choose Edit… beside it, then Disconnect. An agent’s connection also lapses once it has gone unused for 30 days.

Read the AI agent permissions checklist for the scope of these permissions.

Do I need A2A for this? How MCP and A2A connect different parts of an agent setup.

How agents hand each other tasks

Offhand keeps the tasks and marks each one for the agent it is handed to. Whether and when that agent does it is up to the agent: whether it is running or checks your list, what it is allowed to do, and what its app supports. An agent Offhand cannot start finds the task the next time it checks.

  1. The agent you asked calls add_task with assignTo set to the other agent’s name.
  2. That agent calls list_todos with assignedTo: "me" and since set to the cursor from its last check, and finds it. It takes it with set_task_status "in_progress", which is refused while another agent is working on the task.
  3. When it finishes, it sets "done" with one line on what it did, or "blocked" with what it needs.

The agent that handed the task out hears how it went from list_todos with handedBy: "me": the task’s status and its agentNote are the answer. An answer with nothing new is about 28 tokens. How to check for new work without burning tokens.

What to put in a task so the other agent can finish it, with examples: How do I hand a task from one AI agent to another?

Making each agent start on its own when it is handed a task: How do I make an agent start on its own when it’s handed a task? ChatGPT and Claude together: How do I connect ChatGPT and Claude so they can hand each other work? The idea behind it: What is a shared task list for AI agents?

How do I stop two AI agents from doing the same task?

So that no model runs while nothing is new, let a small script make the two calls and start the agent only when they list a task handed to it with status "open", or one it handed out at "done", "need_review" or "blocked". The script still makes its two calls, and Offhand still answers each one.

More guides: How many AI agents can one person run at work? · Continue in another AI at a usage limit.

Prompts to try

  • 01 “What’s on my Offhand list today?” list_todos
  • 02 “Add ‘Renew my passport’ to Offhand for Friday.” add_task
  • 03 “Hand ‘Check the chair listing for replies’ to Muse in Offhand.” add_task with assignTo
  • 04 “What needs my review in Offhand?” list_todos
  • 05 “What did Muse say about the chair listing in Offhand?” list_todos with handedBy: "me"

How do I see what each of my AI agents is doing?

What jobs can I split between my AI agents? Tested paths and labelled examples.

Technical reference

Authorization

OAuth 2.1 with PKCE (S256)

Client registration

Client ID Metadata Document or Dynamic Client Registration

Permissions

offhand:read and offhand:report, the two boxes on the Offhand page that opens

Connection

Lapses after 30 days unused

Rate limit

120 requests a minute for each account, all its agents together; over it, HTTP 429 with Retry-After: 60

Tools

list_todos, add_task, set_task_status

A refused call answers with isError set, and with text saying why.

Permission

offhand:read, shown as “Read your tasks”

Annotations

readOnlyHint: true, openWorldHint: false

ParameterTypeWhat it is
statusstringWhich tasks: open (the default), done, all, or one status — proposed, in_progress, blocked, need_review. With handedBy, the default is all.
querystringWords to find in task titles and in the notes they came from.
assignedTostringOnly tasks handed to this agent: "me" for the caller, "none" for tasks nobody is named on.
handedBystringOnly tasks this agent handed to another: "me" for the caller’s.
sincestringOnly tasks changed at or after this moment: the cursor the last listing answered with.
goalstringOnly the work toward one goal, by its id or uuid.
taskstringOne task, by its id or uuid, whatever its status.
limitintegerTasks on one page: 50 by default, 100 at most.
offsetintegerTasks to skip: the nextOffset the last page gave.

Returns: one page of JSON, with count, returned, and nextOffset when more tasks match; syncedAt, when an Offhand app last uploaded the list, or null; cursor, to pass as since next time; and tasks. Each task has its uuid, short id, title, status and isDone, and where they apply its day and time, repeat, group, details, fromNote, assignedTo, assignedBy, agentNote — the last thing an agent said — and files, each with a download link that works for ten minutes. Open tasks come overdue first, then by day; completed tasks newest first. A page stops at limit, or before it would pass 40,000 bytes.

Refused when:

  • the connection was not allowed offhand:read
  • since is not a moment
  • task matches no task, or more than one
  • goal matches no goal, or more than one

Side effects: none. It changes nothing on the list.

Permission

offhand:report, shown as “Add tasks, and say where a task stands”

Annotations

readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false

ParameterTypeWhat it is
titlestring, requiredThe task, in a line: up to 500 characters.
detailsstringWhat the work is: up to 10,000 characters.
duestringIts day, YYYY-MM-DD.
timestringIts time, HH:MM on a 24-hour clock. It needs a day or a repeat.
repeatsstringdaily, weekdays, weekends, weekly:MO,WE,FR or monthly:15. Bare monthly takes the day of due.
notestringOne line on why it was added, shown with the agent’s name: up to 2,000 characters.
assignTostringThe agent it is for, by the name that agent goes by, or "me".
agentstringWho is adding it. Left out, the connection’s own name.
filesarrayFiles to hand over with it: up to 10 images, PDFs or text files of up to 25 MB, each given as name, size in bytes and, if known, type.

Returns: the task, with its uuid, short id, title, its day, time and repeat, the adding agent’s name and assignedTo. Also whether Offhand started the agent it is for; pending while an Offhand app has yet to write the task into the list; an uploadUrl for each file, which works once, within 15 minutes; and a message for the agent.

Refused when:

  • the connection was not allowed offhand:report
  • the title is empty or longer than 500 characters
  • due, time or repeats is not in its form
  • a time has no day or repeat
  • a file is not an image, a PDF or a text file
  • a file is larger than 25 MB
  • more than 10 files are named
  • the files would take the account past 1 GB
  • the account already holds the most tasks it keeps from agents: 5,000, or 200 while they wait for an Offhand app to sync

A refused call adds nothing.

Side effects: adds the task to the person’s list, with the agent’s name and note on it. With assignTo naming another agent, the task is that agent’s: Offhand starts that agent at once where it has a webhook, or where its app subscribed to the task.assigned event — once the files are uploaded, if there are any. Otherwise that agent finds the task at its next check.

Permission

offhand:report, shown as “Add tasks, and say where a task stands”

Annotations

readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false

ParameterTypeWhat it is
uuidstring, requiredThe task’s uuid from list_todos. Its short id works too, except on a connection that may only report.
statusstring, requiredin_progress, blocked, need_review, done or proposed.
notestringOne line on what changed or what is needed. Left out, the last note stays; "" clears it.
assignTostringHands the task back or on with this report, by the agent’s name. Only on a task another agent handed the caller.
agentstringWhich agent is reporting. Left out, the connection’s own name.
filesarrayWhat the work made, handed back with the report, as on add_task.

Returns: the task, with its uuid, short id, status, isDone and the reporting agent’s name, and with offhand:read its title and note. Also assignedTo when it was handed on, and whether Offhand started that agent; applied: false while an Offhand app has yet to apply the report; an uploadUrl for each file; and a message for the agent.

Refused when:

  • the connection was not allowed offhand:report
  • no task matches uuid, or more than one does
  • the task is a goal
  • the status is in_progress while another agent is working on the task; its in_progress holds for a day
  • the status is in_progress on a task named for another agent
  • the status is done on a task named for another agent
  • the status is done on a task no other agent handed the caller, while an Offhand app on one of the person’s devices is too old to tick it off
  • the status is done on a repeating task while no Offhand app syncs the list
  • assignTo is given on a task no other agent handed the caller
  • assignTo names the caller
  • assignTo comes with done
  • a file is not an image, a PDF or a text file
  • a file is larger than 25 MB
  • more than 10 files are named

Side effects: replaces the task’s status, and its note when one is given; each report is kept with the agent’s name and the time. done ticks the task off, and on a repeating task closes that day and moves it to its next; any other status re-opens a ticked-off task. With assignTo, the task becomes the other agent’s, and Offhand starts that agent as it does for add_task.