Connect Claude or Codex
You connect Claude or Codex to Putnami Cloud once, then ask it what waits for your validation and validate it from the conversation.
Putnami Cloud serves one MCP server at https://mcp.putnami.cloud/mcp. MCP
(Model Context Protocol) is how an AI host such as Claude or Codex calls tools
on a server. The server reads your workspaces, changes and missions, and makes
the four writes you already make in putnami.app. It holds no code, build or
deploy tool: your coding agent keeps the putnami CLI for those.
Never pre-authorize write tools
Never pre-authorize write tools. Your host asks you before it calls a tool. Keep that question for every write:
- Do not choose "always allow" for
approve_declarations,accept_gaps,answer_decisionormessage_mission. - Do not add a write tool, or the whole server, to an allow list.
- Do not connect the server in a session that runs without approvals.
A write approves, accepts or answers in your name. The model chooses a tool from what it reads, and what it reads includes text other people and agents wrote: a pull request title, a mission plan, a decision option. Your confirmation is the check between that text and a write. The server does not ask you a second time.
Read tools change nothing. You can let them run without asking, but only in a session that has no tool that sends data out, such as a web fetch or another connector. A read answers text other people and agents wrote, and that text can ask the model to send what it read somewhere else.
Connect your host
Each host adds a remote MCP server by its URL. Menu names change between host versions: if a name below differs, look for the place your host adds a remote or custom MCP server by URL.
| Host | Where to add https://mcp.putnami.cloud/mcp |
|---|---|
| claude.ai and the Claude desktop and mobile apps | Settings, Connectors, Add custom connector. On a Team or Enterprise plan, an owner adds the connector first. |
| Claude Code | The command below, then /mcp in a session to sign in. |
| ChatGPT | Developer mode, then a new connector with the URL and OAuth sign-in. |
| Codex (CLI and IDE extension) | The commands below. |
Claude Code:
claude mcp add --transport http putnami https://mcp.putnami.cloud/mcpCodex:
codex mcp add putnami --url https://mcp.putnami.cloud/mcp
codex mcp login putnamiIn Claude Code, an allow rule mcp__putnami admits every tool of the server,
writes included. Do not add it. Allow read tools one by one, for example
mcp__putnami__list_pending_decisions, and only in a session that has no tool
that sends data out, such as a web fetch or another connector.
Sign in
The host sends you to the Putnami sign-in page. You sign in with your Putnami account and consent to the host. One consent covers every workspace you belong to; each tool call names its workspace, and Putnami checks your permission there on every call.
| Scope | What it allows |
|---|---|
mcp.read |
The read tools |
mcp.act |
The write tools, together with mcp.read |
You can connect a host for reading only. When the host calls a write tool
without mcp.act, the server answers that the scope is missing and names
mcp.read mcp.act. A host that supports it then asks you to sign in again and
grant both.
The token your host holds never reaches Delivery, Intelligence, Source or
Identity. For each call, the server exchanges it for a token that lasts at
most five minutes, is bound to the workspace the call names, or to no
workspace for list_workspaces, and allows reading or acting, not both. A
write needs your host's token to hold at least 90 more seconds, so that a
call never stops midway because its token expired. With less, the server
asks your host to sign you in again first.
What the tools do
| Tool | Kind | What it does |
|---|---|---|
list_workspaces |
read | Lists your workspaces, and says in which ones you can validate. |
list_pending_decisions |
read | Lists what waits for you in one workspace: declarations to approve, gaps to accept, and decisions of your missions. Each item names the tool that resolves it and the commit or revision it was read at. |
get_change |
read | Reads one change: its stages to production, its CI verdict and revision, its gaps, and each declaration at the base and at the head. |
get_mission |
read | Reads one mission: its plan, sessions, decisions and messages. |
approve_declarations |
write | Approves the declaration files you name, at the head commit you read. |
accept_gaps |
write | Accepts the gaps you name, at the verdict revision you read. A project's gap stays accepted for every later change of the repository that has it; a change's gap, for this commit only. |
answer_decision |
write | Answers one decision of your mission with one of its options and an optional text. |
message_mission |
write | Sends one of your missions a message. |
Every write acts at exactly what you read. When the change moved since, or
does not hold what you named, the tool answers moved and writes nothing
more: read it again and decide again. approve_declarations sends one file
at a time, so the files it approved before it stopped stay approved, and its
answer names them. Delivery checks the head and the path of each file before
it stores its approval. When approve_declarations stops as unavailable
after it sent a file and its notes say Delivery may have stored that
approval, send the file again: it comes back in already_approved if it
was, else in approved. When a step that Delivery runs after it stored the
approval fails, the tool stops as unavailable too, and its notes say
Delivery stored that approval: send the file again to finish the step, and it
comes back in already_approved. accept_gaps sends its gaps in one
request. When it stops as unavailable after it sent them, its answer leaves out
accepted, and its notes say Delivery may have stored them: send the same
call again, and each gap whose acceptance was stored comes back in skipped.
answer_decision and message_mission each send one request too. When one
stops as unavailable after it sent it, its notes say Intelligence may have
stored it. Send the same answer again: Intelligence replays it, with
replayed set. Send the same message with the same idempotency_key:
Intelligence records it once. A write that Putnami refuses with
not_allowed, not_found, conflict, moved or invalid_params stored
nothing for what it refused.
invalid_params says Putnami refused the form of what the write sent: read
again, and send the arguments as the read answers them.
You approve only a declaration you could read whole. get_change offers
the approval of each waiting file whose text it quotes whole. A file whose
text it cut to fit, or that Delivery did not serve, names putnami.app in
approve_in: read and approve it there. So does every waiting file when no
approval call fits what get_change read: the head it read is no full
commit sha, the commit you named does not begin it, or the run of a commit
or a release is missing or has an id that approve_declarations does not
take. A note says so. A declaration in list_pending_decisions points to
get_change, which quotes its text.
You accept only a gap whose project and requirement you could read whole.
get_change and list_pending_decisions offer accept_gaps for a gap when
they quote both names whole, with no escape, and accept_gaps takes them.
A gap whose name is cut, shows an escape, is longer than 512 bytes or has a
space around it names putnami.app in accept_in, with a note: accept it
there. So does each gap of a verdict whose run or revision accept_gaps
refuses.
answer_decision names the option by its id. Before the call, your host
shows you the decision's question and the label of the option you chose,
as get_mission quotes them. The answer quotes the question and the label
of the option recorded.
An acceptance has the scope of its gap. A project's gap (no-requirement,
unknown-requirements, unproven-requirement) stays accepted for every
later change of the repository that has it, until a passing run of the
default branch no longer has it. A change's gap (unknown-impact,
unknown-declarations) stays accepted for this commit only: a new push asks
again. get_change and list_pending_decisions name the widest scope of
each gap in accepted_for, and accept_gaps in accepted_for_at_most:
repository or commit. When main proved a requirement at the change's
base, or when Delivery cannot tell, it keeps the acceptance of that
unproven-requirement for this commit only.
A write refuses a text or a name that holds a character you cannot see, or one that changes the order you read it in:
- The characters Unicode marks as default ignorable, such as a soft hyphen, a zero-width space, a bidirectional control, a variation selector, a Hangul filler or a Unicode tag character.
- Every other format character, such as the interlinear annotation characters U+FFF9 to U+FFFB, the Arabic number sign U+0600, or the Egyptian hieroglyph format controls U+13430 to U+1343F.
- Three blanks: the Braille pattern blank U+2800, the Khitan filler U+16FE4 and the musical null notehead U+1D159.
- A lone surrogate: half of a character, which shows as nothing or as a replacement mark.
So a write refuses a subdivision flag, such as the flag of Scotland, England or Wales: Unicode writes it with tag characters. A text also refuses a control character other than a line feed, a tab, or the carriage return of a line break. A name, a path, a project or a requirement refuses every control character and the line and paragraph separators.
Four joiners pass in a text only where they join what you see:
- The emoji and text variation selectors (U+FE0F, U+FE0E) pass right after
an emoji. After a digit,
#or*, they pass only before the keycap mark U+20E3. - The zero-width joiner (U+200D) passes between two pictographs: after a pictograph, a skin tone or the emoji variation selector, and before a pictograph. It also passes after a mark, between two letters of one joining script.
- The zero-width non-joiner (U+200C) passes between two letters of one joining script, after the marks of the first letter, if it has any.
The joining scripts are Arabic, which writes Persian, Syriac, N'Ko,
Devanagari, Bengali, Gurmukhi, Gujarati, Oriya, Tamil, Telugu, Kannada,
Malayalam and Sinhala. A mark counts as the script of the letter it sits
on, so an accented Latin letter is no joining letter. So emoji sequences,
keycaps and words of scripts such as Persian or Hindi pass, and a, a
variation selector and b do not. A name, a path, a project or a
requirement refuses all four joiners.
What your host shows you is what the write sends. The server finds the characters you cannot see, not the characters that look like others. A letter of another script that looks like a Latin one, such as the Cyrillic small letter o (U+043E) in a Latin word, and the no-break space U+00A0 are visible: a write takes them, an answer quotes them as they are, and the server does not detect them.
An answer shows each of these characters, each control character other than
a line feed or a tab, and each joiner outside the places a text keeps it,
as an escape such as \u{200B}, and counts them in hidden. A name shows
every joiner as an escape. A text that holds one shows each of its own
backslashes as \\, so a \u{…} its writer typed never reads as an escape.
| Write | Who may make it |
|---|---|
approve_declarations, accept_gaps |
A workspace admin |
answer_decision, message_mission |
The mission's owner only |
Delivery and Intelligence record each write as yours, made through the host you used.
What the tools never do
- Merge, close, draft or ready a pull request.
- Run, retry or cancel a CI run, or deploy.
- Read or change code, or build, test or lint.
- Approve every file by default: you name each file, at most 100 in one call.
- Accept a gap that only its owner or Putnami can close.
- Answer a decision or message a mission of another person.
- Keep anything: the server stores nothing between calls.
Limits
The server counts each limit per serving instance: it keeps the counts in memory and shares none between instances. The services behind it keep their own limits.
| Limit | Per person, per minute, per instance | Past it |
|---|---|---|
| Tool calls | 120 | rate_limited, with retry_after in seconds |
| Writes, each file sent counting one | 120 | rate_limited; a write stopped midway names what it did before |
One call sends its writes within 60 seconds. An approve_declarations call
still sending after that stops before the next file, answers unavailable,
and names the files it approved. Send the other files again.
Every answer says what it could not read. An empty list with partial set
does not mean that nothing waits. An answer holds at most 64 KiB. A page of
list_pending_decisions that does not fit holds fewer items rather than cut
a path, a project or a requirement: read the rest with its next_cursor. A
write answer that does not fit keeps the files and gaps that fit and counts
the others in unlisted: they hold as their list says.
Next
- Hosted CI: the verdict and the gaps the tools read
- Intelligence and reviews
- Notifications: how Putnami tells you that something waits