github-tools
# github-tools
GitHub tools for coding agents: issues, project boards, pull requests,
repositories, releases and Actions. One implementation, delivered to each
agent harness in the form that suits it best.
| Harness | Delivery | Pinned by |
| -------------- | ------------------------------------------------ | --------------------------------------------- |
| OpenCode V2 | native plugin (npm package, with skills) | the version in the package |
| OpenCode V1 | native plugin (npm package, no skills) | the version in the package |
| Claude Code | plugin: MCP server and skills | the release tag, as the ref |
| Codex | plugin: MCP server and skills | the release tag, as the ref |
| Grok Build | plugin: MCP server and skills | the release tag, as `@ref` |
| Antigravity | plugin: MCP server and skills (not yet verified) | the release tag's tree URL (not yet verified) |
| Hermes Agent | native plugin (Python, calls the command line) | the release commit, as the SHA |
| other MCP host | MCP server from npm | the version in the package |
0.3.0 moved the package from `craftsamo/agent-github` to `craft-agent/github-tools` and renamed it: npm `@craftsamo/github-tools` (was `@craftsamo/agent-github`, now removed from npm), plugin and MCP server `github-tools`, programs `github-tools` and `github-tools-cli`, settings `GITHUB_TOOLS_*`. Allow rules, plugin installs and MCP configurations written for 0.2.0 change; the tool IDs do not.
The tools need [Node.js](https://nodejs.org/) 24 or later and the
[GitHub CLI](https://cli.github.com/), authenticated as you.
[`docs/conventions.md`](docs/conventions.md) describes how the tools are named
and how they behave.
## Install
Install from a release, never from the default branch: a release is a tag whose
commit carries the built `dist/`, and the default branch does not. In the
commands below, `<version>` is a release's version (`1.2.3`) and `<sha>` is the
commit its tag names:
```sh
gh api repos/craft-agent/github-tools/commits/v<version> --jq .sha
```
### OpenCode
OpenCode V2 takes the package and its options in `opencode.json`:
```json
{
"plugins": [
{
"package": "@craftsamo/github-tools@<version>",
"options": { "toolsets": ["issues", "pull_requests", "projects"] }
}
],
"permissions": [{ "action": "github_*", "resource": "*", "effect": "ask" }]
}
```
OpenCode V1 takes the package as a string, or as a pair with its options:
```json
{
"plugin": [["@craftsamo/github-tools@<version>", { "toolsets": ["issues", "pull_requests", "projects"] }]],
"permission": { "github_*": "ask" }
}
```
OpenCode allows any action no rule names, so the plugin's request is granted
silently unless a rule says `ask`. Read tools never ask, so the `github_*` glob
is safe.
V1 registers no skills. Unpack the package and copy its `skills/*` folders into
`~/.config/opencode/skills/`:
```sh
npm pack @craftsamo/github-tools@<version>
tar -xzf craftsamo-github-tools-<version>.tgz
mkdir -p ~/.config/opencode/skills
cp -r package/skills/* ~/.config/opencode/skills/
rm -rf package craftsamo-github-tools-<version>.tgz
```
### Claude Code
```sh
claude plugin marketplace add craft-agent/github-tools#v<version>
claude plugin install github-tools@craft-agent
```
### Codex
```sh
codex plugin marketplace add craft-agent/github-tools --ref v<version>
codex plugin add github-tools@craft-agent
```
Codex turns `-` into `_` in server names, so it shows the tools as
`mcp__github_tools__github_issue_*`. It starts the MCP server in the plugin's
install directory, so name issues as `owner/repo#N` or pass `repo` (a bare number
fails there with a message saying so).
### Grok Build
```sh
grok plugin install craft-agent/github-tools@v<version> --trust
```
### Antigravity
Install from the tree of the release tag, never from the default branch, which
has no built `dist/`. The pinning and the install command are not yet verified
on a real install.
```sh
agy plugin install https://github.com/craft-agent/github-tools/tree/v<version>
```
### Hermes Agent
Hermes installs a commit, not a tag, and the plugin installs disabled:
```sh
hermes plugins install craft-agent/github-tools --ref <sha>
hermes plugins enable github-tools
```
If a Hermes gateway is already running, restart it (`hermes gateway restart`) to
load the plugin.
### Any other MCP host
Add the server to the host's MCP configuration. Flags go in `args`:
```json
{
"mcpServers": {
"github-tools": {
"command": "npx",
"args": ["-y", "@craftsamo/github-tools@<version>", "--toolsets", "issues,pull_requests,projects"]
}
}
}
```
## Tools and skills
Each toolset comes with a skill that tells an agent how to use its tools. A
toolset with no tools yet registers none (0.2.0 ships the `issues` tools).
### Issues (`issues`)
| Tool | What it does | Class |
| ---------------------- | ----------------------------------------------------------------------------------------- | ---------- |
| `github_issue_read` | `view` one issue in full, or `list` a repository's issues or a parent's sub-issues | read |
| `github_issue_create` | create an issue with its kind, labels, assignees, milestone, parent and blocked-by issues | add |
| `github_issue_edit` | change the title, body (or append to it), kind, labels, assignees or milestone | overwrite |
| `github_issue_comment` | comment on an issue | add |
| `github_issue_close` | close with a reason (`completed`, `not_planned`, `duplicate`) | overwrite |
| `github_issue_reopen` | reopen a closed issue | overwrite |
| `github_issue_relate` | set the parent, sub-issues, blocked-by and blocking issues, sending only what differs | idempotent |
| `github_issue_develop` | create a branch on GitHub linked to the issue, or return the one already linked | idempotent |
The skill is [`github-issues`](skills/github-issues/SKILL.md), shown as
`github-tools:github-issues` in Claude Code and `github-tools:issues` in Hermes.
An issue's kind is the organization's Issue Type of that name when it defines
one, otherwise a `type:<kind>` label.
## Reads and writes
Every read tool is named `github_<domain>_read`. Reads can run unasked; writes
are confirmed by the harness.
| Harness | Reads without asking |
| ----------- | ------------------------------------------------------------------------------------------------------------------------- |
| OpenCode | always; the plugin asks only before a write |
| Claude Code | the rule `mcp__plugin_github-tools_github-tools__*_read` in `permissions.allow` of `~/.claude/settings.json` or a project |
| Codex | always; Codex runs tools that declare `readOnlyHint` unasked, as the read tools do |
| Grok Build | `grok --allow 'MCPTool(github-tools__*_read)'` |
| Antigravity | an Allow rule `mcp(github-tools/<tool>)` for each read tool; not yet verified |
| Hermes | always; the plugin asks only before a write |
Where a write is asked about, and where it is not:
- **OpenCode** grants any action no rule names, so a write runs unasked unless
a rule asks for it: add the `github_*` rule shown under [OpenCode](#opencode).
- **Codex** under `codex exec`, and **Grok Build** under `grok -p`, refuse a
write they cannot ask about, unless it is allowed beforehand.
- **Hermes** `hermes chat -q` refuses a write it cannot ask about; `hermes -z`
and `--yolo` skip approval, so writes then run unasked.
## Settings
Every entry point takes the same settings. A flag wins over its environment
variable, and an option or setting wins over the variable too.
| Setting | Flag | Variable | Meaning |
| --------------- | ----------------- | ---------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `toolsets` | `--toolsets` | `GITHUB_TOOLS_TOOLSETS` | `issues`, `pull_requests`, `projects`, `repos`, `releases`, `actions` or `all`; default `issues` and `pull_requests` |
| `read_only` | `--read-only` | `GITHUB_TOOLS_READ_ONLY` | registers no tool that writes; wins over every other setting |
| `tools` | `--tools` | `GITHUB_TOOLS_TOOLS` | single tools to add by ID, whatever their toolset |
| `exclude_tools` | `--exclude-tools` | `GITHUB_TOOLS_EXCLUDE_TOOLS` | single tools to remove by ID; wins over `tools` and `toolsets` |
| `confirm` | `--confirm` | `GITHUB_TOOLS_CONFIRM` | MCP server only: `harness` (default) or `elicitation`, as `docs/conventions.md` describes |
Lists are comma-separated in a flag or a variable. GitHub Actions does not let a
secret or configuration variable be named `GITHUB_*`, so in a workflow pass a
value through `env:` from a secret of another name, e.g.
`GITHUB_TOOLS_READ_ONLY: ${{ secrets.TOOLS_READ_ONLY }}` (setting such an
environment variable in `env:` is allowed). Where each harness reads them:
- **OpenCode** takes the plugin `options` shown above, with lists as arrays and
`read_only` as a boolean.
- **Hermes** takes `plugins.entries.github-tools.settings` in `config.yaml`:
```yaml
plugins:
entries:
github-tools:
settings:
toolsets: [issues, pull_requests, projects]
read_only: false
exclude_tools: [github_pr_merge]
```
- **Claude Code and Antigravity** start the plugin's server without flags; Claude
Code passes the client's environment, so set the variables in the environment
the harness runs in.
- **Codex and Grok Build** pass no environment to a plugin's MCP server, so the
`GITHUB_TOOLS_*` variables have no effect there. To change settings in Codex,
add the server under `[mcp_servers]` in Codex's config with flags in `args` (as
in [Any other MCP host](#any-other-mcp-host)) instead of using the plugin.
## Layout
- `src/core/` — the tools, free of any harness.
- `src/mcp/` — the MCP server, built to `dist/mcp.js` and run by `github-tools`.
- `src/opencode/` — the OpenCode plugin, built to `dist/opencode.js`.
- `src/cli/` — the command line the Hermes plugin calls, built to `dist/cli.js`
and run by `github-tools-cli`.
- `src/hermes/`, `plugin.yaml` and `__init__.py` — the Hermes plugin.
- `skills/` — one skill per toolset, `skills/<name>/SKILL.md`, which every
harness that takes skills loads from the package or plugin root.
- `plugin.json`, `mcp.json`, `.mcp.json`, `.claude-plugin/`, `.agents/plugins/`,
`.antigravity-plugin/` and `mcp_config.json` — the plugin manifests of the
other harnesses. The root `.mcp.json` is the configuration Grok Build reads
(with `${CLAUDE_PLUGIN_ROOT}`, the variable both Grok Build and Claude Code
expand); when working on this repository in Claude Code, decline the project
MCP server it offers from that file.
How a release is made is in [`RELEASING.md`](RELEASING.md).
## License
[MIT](LICENSE)
TDQS
Scored across 8 tools
Each tool has a distinct primary purpose (create, read, edit, comment, close, reopen, relate, develop), and the descriptions clearly delineate state transitions (close/reopen) from field edits (edit) and from relation management (relate). Minor overlap exists since close/reopen can post a comment and create/edit/relate all touch labels, assignees, and parent issues, but the boundaries are explained well enough that misselection is unlikely.
All eight tools follow the identical github_issue_<verb> pattern with short, predictable verbs (close, reopen, relate, develop, read, create, edit, comment). There are no style deviations or mixed conventions.
Eight tools is well within the ideal 3-15 range and each tool maps to a real issue-lifecycle operation, so nothing feels redundant or padded. The surface is tightly scoped to issue management.
The core issue lifecycle is fully covered: create, read (view/list), edit, comment, close, reopen, plus relation and linked-branch management. Minor gaps remain, such as no dedicated comment listing/editing, no label or milestone management tools (only create_milestone flags), and no lock/transfer/pin operations.