Skip to content
Skills Library
AI & agents · Tool integration

setup-maple-agent-loop

/setup-maple-agent-loop

Connect a coding agent to Maple's MCP server and Stop hook so it can read, wait for and resolve visual review comments, and verify the connection. Use when adding `@maple-kit/mcp` to `.mcp.json`, wiring `maple-stop-hook`, or working out why an agent cannot see Maple comments or stops while they are still open.

Repository
maple ↗
Licence
Apache-2.0
Last repository change
20.10.2026

Assessment and security

100/ 100Class: good
Format25 / 25
Frontmatter with name and description, valid name, description up to 1,024 characters, name matching the folder.
Discoverability25 / 25
Description explains when to use the skill, has a reasonable length and uses third person.
Content35 / 35
At least 150 words and at most 500 lines, headings, step lists, examples or code.
Hygiene15 / 15
No placeholders such as TODO, no Windows paths, with a licence.
Riskno signals detected in the scan
Files in this package
1
Scripts
0
HTML comments
0
URLs in SKILL.md
2
Deprecation

No signals detected. Repository changed on 20.10.2026, without references to retired models or APIs.

Licence

Apache-2.0, otwarta: można używać i zmieniać, z zachowaniem licencji i informacji o zmianach.

What it does and when to use it

Connect a coding agent to Maple's MCP server and Stop hook so it can read, wait for and resolve visual review comments, and verify the connection. Use when adding `@maple-kit/mcp` to `.mcp.json`, wiring `maple-stop-hook`, or working out why an agent cannot see Maple comments or stops while they are still open.

Original author description

Connect a coding agent to Maple's MCP server and Stop hook so it can read, wait for and resolve visual review comments, and verify the connection. Use when adding `@maple-kit/mcp` to `.mcp.json`, wiring `maple-stop-hook`, or working out why an agent cannot see Maple comments or stops while they are still open.

How it works

  1. 1. Know the two binaries
  2. 2. Pick the token, and how the gate moves
  3. 3. Add the server to .mcp.json
  4. 4. Add the Stop hook
  5. 5. Verify
  6. Troubleshooting
Original SKILL.md headings · 219 lines, 1,633 words
SKILL.md preview
---
name: setup-maple-agent-loop
description: Connect a coding agent to Maple's MCP server and Stop hook so it can read, wait for and resolve visual review comments, and verify the connection. Use when adding `@maple-kit/mcp` to `.mcp.json`, wiring `maple-stop-hook`, or working out why an agent cannot see Maple comments or stops while they are still open.
---

# Connect an agent to Maple

Maple's agent half is one MCP server and one Claude Code Stop hook, both in
`@maple-kit/mcp`. The server lets the agent read and resolve comments; the hook
stops it calling itself finished while comments are open. This file is the
wiring. `docs/agent-loop.md` is the design, and the `maple-review` skill is how
to act on the comments once they arrive. Every `docs/` and `packages/` path
here is in the Maple repository, at <https://github.com/maple-kit/maple>.

Throughout, `acme/web` is the repository and `web-482` the branch under review.
Substitute your own.

## 1. Know the two binaries

| Bin               | What it is                                                 |
| ----------------- | ---------------------------------------------------------- |
| `maple-mcp`       | The stdio MCP server. Takes no arguments; reads the env.   |
| `maple-stop-hook` | Reads Claude Code's hook payload on stdin, answers stdout. |

Both need Node 24 or later. Run them with
`npx -y -p @maple-kit/mcp <bin>`; the package name is not a bin, so
`npx @maple-kit/mcp` alone does not work.

## 2. Pick the token, and how the gate moves

**`GITHUB_TOKEN`** is what reads and writes the comments. Maple keeps them in a
pull-request conversation comment, so the token needs `Pull requests: Read and
write` on the repository (a fine-grained token; `Metadata: Read` comes with
it). Read-only is enough to list, but `resolve_comment` rewrites that comment
and fails without write.

**`MAPLE_URL`** is optional, and it is how the check moves on a resolve. Set it
to the mount URL of a deployed Maple route whose host turned on `gateRefresh`
(the `setup-maple-org` skill), usually the preview under review:
`https://web-482.preview.acme.dev/api/maple`. `resolve_comment` then asks that
route to republish `maple/visual-review`, sending the branch and
`GITHUB_TOKEN`. That token needs push access to the repository for the route to
act on it. No gate credential is ever on this machine; `docs/github-auth.md`
says why. Skip it and the check updates on the next push instead.

**`MAPLE_GATE_TOKEN`** is for CI only: the gate App's own installation token,
never `GITHUB_TOKEN`, and it expires an hour after it is minted. Do not set it
next to `MAPLE_URL`; the server refuses to start with both.

Neither token goes in a file. Export them into the shell that starts the
client, for example with `op run --env-file .env -- claude`, and reference
them from `.mcp.json` with `${VAR}`.

## 3. Add the server to `.mcp.json`

**With the Maple Claude Code plugin installed, skip this section and the
next.** The plugin runs the server and the Stop hook, and both read
`GITHUB_TOKEN`, `MAPLE_GITHUB_OWNER` and `MAPLE_GITHUB_REPO` from the
environment Claude Code starts in, so export all three there, and `MAPLE_URL`
too for the server to refresh the gate. Go to section 5.

```json
{
  "mcpServers": {
    "maple": {
      "command": "npx",
      "args": ["-y", "-p", "@maple-kit/mcp", "maple-mcp"],
      "env": {
        "GITHUB_TOKEN": "${GITHUB_TOKEN}",
        "MAPLE_GITHUB_OWNER": "acme",
        "MAPLE_GITHUB_REPO": "web"
      }
    }
  }
}
```

What the server reads, from `packages/mcp/src/config.ts`:

| Variable                 | Required | Notes                                                                                                           |
| ------------------------ | -------- | --------------------------------------------------------------------------------------------------------------- |
| `GITHUB_TOKEN`           | yes      | Section 2.                                                                                                      |
| `MAPLE_GITHUB_OWNER`     | yes      | Organisation or user.                                                                                           |
| `MAPLE_GITHUB_REPO`      | yes      | Repository name.                                                                                                |
| `MAPLE_STORE`            | no       | `github` when a forge variable is set, else `file`, the comments under `.maple/`. Anything else fails.          |
| `MAPLE_GITHUB_API`       | no       | API base URL, for GitHub Enterprise Server.                                                                     |
| `MAPLE_URL`              | no       | Section 2. The route's mount URL. Unset means no gate refresh on resolve.                                       |
| `MAPLE_GATE_TOKEN`       | no       | Section 2. CI only; not with `MAPLE_URL`.                                                                       |
| `MAPLE_GATE_APP_ID`      | no       | The gate App's id. Only read when `MAPLE_GATE_TOKEN` is set.                                                    |
| `MAPLE_REQUIRE_APPROVAL` | no       | With `MAPLE_GATE_TOKEN`, exactly `true` to hold for an approval. The route's own setting wins with `MAPLE_URL`. |

A missing required value makes the server exit at startup, so the client shows
it as a failed server rather than a tool error.

`MAPLE_BRANCH` is **not** read by the server. Every tool takes the branch as an
argument instead:

| Tool                  | Arguments                         | Returns                                                         |
| --------------------- | --------------------------------- | --------------------------------------------------------------- |
| `list_comments`       | `branch`, `statuses?`             | Every comment on the branch, newest first, filtered by status.  |
| `wait_for_comments`   | `branch`, `cursor?`, `timeoutMs?` | `{ status: "comments" \| "timeout", cursor, comments }`         |
| `get_comment_context` | `id`, `branch`                    | The comment, its anchor rungs, its conditions, any mock replay. |
| `resolve_comment`     | `id`, `sha`, `note?`              | The updated comment. Refreshes the gate if configured.          |

`statuses` is any of `open`, `resolved`, `needs_reverify`, `orphaned`.
`branch` is the pull request's **head branch name**, not its number.

## 4. Add the Stop hook

The plugin installs this already. Without it, in `.claude/settings.json`:

```json
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "MAPLE_GITHUB_OWNER=acme MAPLE_GITHUB_REPO=web npx -y -p @maple-kit/mcp maple-stop-hook"
          }
        ]
      }
    ]
  }
}
```

**The hook does not get `.mcp.json`'s `env`.** It is a separate process that
inherits only Claude Code's own environment. It needs `GITHUB_TOKEN`,
`MAPLE_GITHUB_OWNER` and `MAPLE_GITHUB_REPO` there or in the command, as
above. `GITHUB_TOKEN` comes from the shell the client was started in
(section 2); never inline it. With neither `MAPLE_GITHUB_OWNER` nor
`MAPLE_GITHUB_REPO` set, the hook reads the local store instead: the comments
under `.maple/` for the checked-out branch. It lets every stop through where
there is no `.maple/` folder, none for the branch, or no repository.

The branch is `MAPLE_BRANCH` when it is set, and otherwise the branch checked
out in the session's working directory.

The hook blocks while any comment is `open` or `needs_reverify`, and hands the
agent a numbered list naming each comment's id, location and first line.
`resolved` and `orphaned` comments do not block. It blocks at most eight stops
in a row, counted per session, then lets the session end and says what is
still open. A stop that no block caused, such as the end of your next prompt,
starts the count again.

## 5. Verify

Do all four. The first two pass on a server that is pointed at the wrong
branch, because an unknown branch returns an empty list rather than an error.

1. Pick a pull request whose conversation has a Maple comment (one with a
   ` ```maple ` fence) and at least one open comment in it. Note its head
   branch.
2. Restart the client and confirm `maple` is listed as connected with five
   tools (`/mcp` in Claude Code).
3. Ask the agent to call `list_comments` with that branch and
   `statuses: ["open"]`. It should return the comments you see in the fence,
   with matching ids. Then call `get_comment_context` with one `id` and the same
   `branch`; it should return `anchors` and `conditions`.
4. Check the hook from a shell, on that branch, with the same environment the
   client has:

   ```sh
   echo '{}' | MAPLE_BRANCH=web-482 MAPLE_GITHUB_OWNER=acme MAPLE_GITHUB_REPO=web \
     npx -y -p @maple-kit/mcp maple-stop-hook
   ```

   It should print `{"decision":"block","reason":"1 Maple review comment(s) are still open…"}`.
   With nothing open it prints `{}`. Then end a turn in the client and watch it
   refuse to stop.

## Troubleshooting

**The agent stops although comments are open.** The hook failed or looked at
the wrong branch, and Claude Code treats a failing Stop hook as a non-blocking
error. Run step 4 by hand. A `… is not set` error means the hook's environment
is missing a variable (section 4). `{}` on a branch you know has open comments
with a forge means `MAPLE_GITHUB_OWNER` and `MAPLE_GITHUB_REPO` are both unset
in the hook's environment (it then reads `.maple/`), `MAPLE_BRANCH` is not the
head branch name, or the hook ran from a checkout on another branch.

**The agent keeps being sent back and cannot finish.** It gets eight tries in
a row, then the hook lets it stop. The count lives in a small file per session
under the system temp directory (`maple-stop-hook/`); if that directory cannot
be written, the hook fails and Claude Code lets the agent stop. Resolve or
reply to each comment, or interrupt the session and say what is left.

**`list_comments` returns `[]` on a pull request that has comments.** The
`branch` argument is wrong: a PR number, a preview hostname or a typo all
resolve to no pull request, which is an empty list, not an error. Pass the head
branch name exactly.

**`wait_for_comments` returns `{ "status": "timeout" }`.** That is a result,
not a failure. The wait is clamped to 55 seconds (1 s minimum, 30 s default) to
stay under every client's 60-second tool ceiling. Call it again with the
returned `cursor`; omit `cursor` only on the first call, to drain what is
already there.

**Server fails to start: `… is not set; Maple's MCP server cannot start
without it.`** A required variable from section 3 is missing or empty. Claude
Code passes a `${VAR}` with no default through literally when the client's
shell lacks it, so the server starts with the placeholder as the value and
fails later against GitHub; `${VAR:-}` expands to empty and fails here instead.

**`resolve_comment` fails with a 403 or 404.** `GITHUB_TOKEN` lacks `Pull
requests: Read and write`, or cannot see the repository. GitHub answers 404 for
a repository a token cannot see.

**Resolves succeed but `maple/visual-review` does not move.** A gate refresh
never fails a resolve; it writes the reason to the server's stderr, which the
client keeps in its MCP log. With `MAPLE_URL` set, the line names the route's
answer: `403` means `GITHUB_TOKEN` cannot push to the repository, `401` that
GitHub did not accept it, and `404` that `MAPLE_URL` is not the route's mount
URL or its host has not turned on `gateRefresh`. With `MAPLE_GATE_TOKEN`, a
`401` is the token's hour running out; use `MAPLE_URL` instead. A 403 on the
check run usually means `MAPLE_GATE_APP_ID` is missing; `docs/gate.md` has that
case.

**Server fails to start: `MAPLE_URL and MAPLE_GATE_TOKEN are both set`.**
Remove `MAPLE_GATE_TOKEN` from the agent's environment; it belongs in CI.

Versions

The database has no other skills named setup-maple-agent-loop.

Database snapshot: 13.11.2026 · package version 97e61236a5a1 (SHA-256 digest of the entire package)

Downloads in the last 30 days: 0 · ranking

Report a problem with this skill: harmful content, incorrect descriptions or an author’s removal request.

Similar skills

  • 100maple-review
    Read the visual review comments Maple posted on a pull request and turn them into a worklist. Use when a PR body or comment contains a ```maple fenced block, when asked to act on design or UX review feedback on a preview deployment, or when the merge is blocked by the maple/visual-review check.
  • 100setup-maple-org
    Register and install the Maple GitHub App for an organisation, wire the SDK route to it, and verify that a reviewer can sign in from a preview and post a pull-request comment. Use when setting up Maple for the first time in an organisation, installing the Maple GitHub App,…
  • 100talk-maple-aind-devcon-welcome
    Summarizes Simon Maple's AI Native DevCon welcome and explains the conference framing: context window, latent space, tool pool, hallway track, attendee goals, and practical learning themes. Use when the user asks about the event opening, conference themes, AI-native development…
  • 90scenario-mcp-connection-setup
    Use when connecting an AI agent to the public Scenario MCP server or diagnosing its tool discovery and authorization setup.
  • 100setup-notion
    Connect Claude Code to Notion's official hosted MCP, complete OAuth, verify the intended workspace with a read, and stop before any write. Use when the user asks to connect Notion, set up Notion, export to Notion, or diagnose an unavailable Notion connection.
  • 100debug-local
    Debug the Lightdash app using PM2 logs, Maple traces, and browser automation. Use when investigating issues, tracking down bugs, understanding request flow, or correlating frontend actions with backend behavior.

Feedback and reports

Your message goes to Bartosz Sokoliński. An email address is needed only for a reply. See the privacy policy.