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_decision or message_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/mcp

Codex:

codex mcp add putnami --url https://mcp.putnami.cloud/mcp
codex mcp login putnami

In 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