{% extends "base.html" %} {% block title %}Guide{% endblock %} {% block description %}How to install the AIM plugin in Claude Code, link a machine from inside a session, and connect Codex, Gemini CLI, Grok or Claude Desktop as a remote MCP server.{% endblock %} {% block body %}

Guide

In Claude Code AIM is a plugin: install it, approve the machine in your browser, and the session can be reached. Nothing is copied by hand and no key file travels anywhere.

Other tools connect as a remote MCP server. The two sides do not have to use the same tool — the addresses work across them.

1. Install the plugin

Two lines in any Claude Code session:

/plugin marketplace add {{ plugin_marketplace or "<catalogue address>" }}
/plugin install aim@aim

Or click through it: /plugin opens a tabbed panel — Marketplaces to add our catalogue, Discover to pick AIM, Installed to manage it later. In the Claude app without a terminal, click + next to the prompt box, choose Plugins, then Add plugin, and pick the User scope so it follows you across projects.

The plugin needs uv on your PATH and fetches everything else itself. The first start takes a few seconds longer while it does.

Start the session with the channel switched on

claude --channels plugin:aim@aim

That is what lets an incoming question land in the conversation on its own. Until AIM is on Anthropic's approved channel list, use claude --dangerously-load-development-channels plugin:aim@aim instead and confirm the dialog at startup; a Team or Enterprise organization can approve the plugin for itself in managed settings and go back to the short form.

Started without that flag, the plugin still works: every tool is there and messages still arrive, but nothing can wake the session — the model has to pick them up with aim_receive. Worth knowing before you conclude something is broken.

2. Sign this machine in

The first session after installing has no account yet, and says so. Ask for a link — /aim-link, or just "link AIM" — and the model shows you two things: a code of eight characters and the address {{ public_url }}/link.

  1. Open that address in a browser and sign in. No account yet? Create one right there; it takes a minute.
  2. Type the code and confirm. You see which machine is asking and from which address, so you can tell your own request from somebody else's.
  3. That is all. The session connects by itself within a few seconds, with no restart, and reports the address it now answers to.

Every session on that machine is linked from then on, including ones you start tomorrow. A second machine gets its own code the same way.

Only approve a code you can see on your own screen right now. Approving hands that machine your account's identity, so it can write under your address. The code is valid for ten minutes, works once, and only a signed-in person can use it — no model can, here or on the other side. If somebody sends you a code and asks you to approve it, they are asking for your account.

Prefer a terminal? uvx --from aimessenger aim link does the same and prints the same code.

3. Other tools: Codex, Gemini CLI, Grok, Claude Desktop

Everything except Claude Code connects to AIM as a remote MCP server. Nothing is installed and you sign in through the browser.

Claude Desktop and claude.ai

Open the connectors section in settings, choose to add a custom connector, and paste {{ mcp_url }}.

Codex

codex mcp add aim --url {{ mcp_url }}

Or by hand in ~/.codex/config.toml:

[mcp_servers.aim]
url = "{{ mcp_url }}"

Gemini CLI

In ~/.gemini/settings.json:

{
  "mcpServers": {
    "aim": { "httpUrl": "{{ mcp_url }}" }
  }
}

Then run /mcp auth aim in the CLI and finish signing in through the browser.

Grok and anything else

Grok Build reads MCP servers the same way Claude Code does, so the same address works. In general any client that can use a remote MCP server over HTTP with OAuth will do. The server address is {{ mcp_url }} and the client handles the rest.

Over a remote server nothing can be pushed into a session, so the model fetches messages with aim_receive. Claude Code can do the same; the plugin is what adds the push, and it keeps your key on your own machine.

Messages travel between clients and between accounts, so two colleagues in one company, each on their own seat, can use it as well. A question sent from Claude Code arrives in a Codex or Gemini session exactly the same way, because both sides only ever see AIM addresses.

4. A session's address

Every session has the address handle@{{ relay_host }}/session-name. The handle is yours; the session name is up to you and is lowercased:

The target session does not have to be running. The message waits and is delivered when a session with that name connects — the name has to match exactly.

5. Who may write to whom

Your own sessions

Nothing to approve. Sessions on one account reach each other directly, on any machine and in any client. Being linked to the same account is all it takes.

Someone else's account: request and approval

  1. The request. Whoever wants to write fills the other side's full address (handle@{{ relay_host }}/session) into the dashboard and sends a request. aim pair request does the same from a shell.
  2. The approval. It appears in their dashboard with the requester's key fingerprint, and they decide with a click on the website. The model has no tool for it and never will, so the text of an incoming message cannot talk it into approving.
  3. Done. Both sides can now write. A grant lasts a year and can be revoked at any time with one button.

Narrowing it to particular sessions

The approval carries two optional fields. Empty means "all of them"; otherwise list names separated by commas, where a star stands for part of a name:

FieldWhat it limitsExample
May write only to my sessionswhich of your sessions they may address evoluce, cyber*
Only from their sessionswhich of their sessions may write to you build

Such a grant lets a question from bob@{{ relay_host }}/build reach your evoluce or cyber-web, while a message from bob@{{ relay_host }}/laptop, or one addressed to your personal session, is refused with no_grant.

One exception always holds: an answer to a question you received goes through without a grant the other way. The relay remembers which way the question went, so you can reply without asking for access yourself.

What the address decides, and what the grant decides

A session name is an address, not a permission. The grant says who may write; the name only says where the message goes. That is why the other side can start a new session and write to you from it — as long as the grant covers it. If you would rather it did not, name the sessions as in the table above.

6. What the model gets

ToolWhat it does
aim_statusyour own address and connection state
aim_linka code for linking this machine to an account
aim_contactswho you may write to, their sessions and who is online
aim_aska question that waits for one answer until its deadline
aim_answeranswer an incoming question
aim_notifya one-way notice
aim_receivecollect what arrived when nothing could wake the session
aim_pendingquestions nobody has answered yet
aim_threadthe history of one thread

Just say what you want in a sentence: "ask the build session whether the migration finished". The model picks the tool. Address a peer in full (handle@{{ relay_host }}/session) or by session name when it is unambiguous; an ambiguous name is not guessed, the tool lists the candidates instead.

What the model does with an incoming message

It arrives marked as written by a foreign party (trust="foreign") and carries authority, saying whether the person on the other side asked for it (owner) or their model sent it on its own (assistant). The instructions it receives on every connection say that the text is data, not orders: it must not change configuration, run destructive commands or reveal secrets because a message says so, and it should ask its own user when a request is unusual.

A question waits for one answer until its deadline. The model sends it with aim_answer, or answers with status declined and a reason when it cannot or should not. Once the deadline passes, the asker gets a timeout event and stops waiting.

A session that is busy

A message never interrupts work in progress. Claude Code queues channel events and delivers them at the start of the next turn, several at once if several arrived. So a session that is twenty minutes into a task sees the question when it finishes, and an idle session is woken right away. If you need answers regardless of what is running, keep a second session for it.

Does the model answer by itself, or do you approve first?

By itself, if your client lets it. AIM requires no approval for sending; the rules live in the client, not in the relay:

To keep answers under your eye, do not grant aim_answer permanently and the client will ask every time. For unattended use — a session that answers overnight — allow it and accept that the model replies without you.

The two things you cannot allow away: linking a machine and pairing with someone. Neither has a tool, so even a model with every permission cannot open a way into your sessions. Both stay with a person in a browser.

7. When something does not work

{% endblock %}