GitHub workbench MCP server (dsh bundle): 23 MCP tools for repos, issues, PRs, code review, and search, with zero runtime dependencies and PAT/OAuth auth.
Install
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:JohnXu22786/github-mcp
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time. Only install sources you trust, and pin a commit (github:owner/repo#sha).
README
repogate — GitHub Developer Workbench (agent tool set)
repogate is a GitHub workbench for coding agents: it wraps the GitHub REST API into a set of MCP (Model Context Protocol) tools, letting agents perform repo queries, issue management, PR creation and merging, code review, search, and other common operations directly in a conversation.
- Zero runtime dependencies: uses only built-in Node.js capabilities (
fetch,node:test); no packages need to be installed to run; - Standard MCP stdio server: works with any MCP-capable client (dsh, Claude Code, Codex, opencode, etc.);
- Built for dsh: ships a dsh bundle (
cordis.patch.yml+ a self-developed bridge plugin), connected withdsh plugin addin one step — tools appear automatically in the model's tool list (mcp__repogate__*); - Dual auth channels: personal access token (PAT) and OAuth device authorization flow, with local token caching;
- Read-only mode: blocks all write operations with one switch, ideal for research-only sessions;
- Actionable errors: rate limits, invalid tokens, insufficient permissions, rejected parameters — every error includes a Chinese fix guide;
- Slim output for models: lists/search return concise summaries instead of full JSON, saving context tokens.
Quick Start
Option A: connect directly from any MCP client
# requires Node.js ≥ 18.17
REPOGATE_TOKEN=ghp_yourtoken node src/entry.js
Example config line using the official dsh bridge (also works in Claude Code / Codex MCP configs):
# dsh: insert into $DSH_HOME/profiles/<profile>/cordis.patch.yml
- insert:
- id: mcp-repogate
name: '@deepseek-ai/dsh-mcp-client'
config:
serverName: repogate
transport: stdio
command: node
args: ['/absolute/path/to/src/entry.js']
env:
REPOGATE_TOKEN: !!js process.env.REPOGATE_TOKEN ?? ''
After connecting, the model sees 23 tools such as gh_issue_fetch and gh_pr_merge
(generic MCP clients see bare names; under dsh they carry the mcp__repogate__ prefix, see below).
Option B: install as a dsh plugin bundle (recommended)
This plugin is declared as a dsh bundle (the dsh.bundle field in package.json). In the plugin checkout directory run:
dsh plugin --profile web add .
- On first use it initializes the
webprofile automatically and adds this package todsh.profile.bundles; - The
repogate/bridgeplugin defined in the package'scordis.patch.ymllaunches this MCP server directly inside the dsh process, and after the handshake registers all tools intoctx.tools— no manual config changes needed; - The token is inherited from the dsh process environment by default (
REPOGATE_TOKENorGITHUB_TOKEN); - Uninstall:
dsh plugin --profile web remove repogate.
After installing, restart dsh and simply say in a session:
"Look at the open issues of the octo/hello repo, close #12, then comment 'fixed, waiting for verification' on #12."
The corresponding tool call chain: mcp__repogate__gh_issue_browse → mcp__repogate__gh_issue_fetch →
mcp__repogate__gh_issue_edit → mcp__repogate__gh_issue_respond.
Note: dsh does not enable any MCP server by default (each server command is trusted code executed outside the sandbox); this plugin's bundle line is the "enable" action itself; only install trusted plugins.
Installing in DSH
dsh plugin --profile demo add github:JohnXu22786/github-mcp
A single command installs this plugin into the dsh demo profile from the GitHub repository. Integration, auth, and lifecycle details follow in the "dsh integration" section below.
dsh Integration (how a plugin-style harness loads it)
dsh uses the Cordis plugin framework; the unit of composition is a bundle: an npm package + a patch layer. The loading chain is as follows:
package.json (dsh.bundle.patch → ./cordis.patch.yml)
└─ a line in cordis.patch.yml: name: 'repogate/bridge'
└─ src/bridge/plugin.js (Cordis plugin, inject: ['tools'])
├─ spawns src/entry.js (the MCP server child process, stdio) using Node itself
├─ completes the initialize / tools/list handshake
└─ registers each tool as mcp__repogate__<tool name> into ctx.tools
- Tool interface: the tool name visible to the model =
mcp__<serverName>__<original tool name>,serverNamedefaults torepogate; - Events/skills: this plugin registers no events or skills; it only exposes capabilities through the
ctx.toolstool interface; - Lifecycle: handshake and registration happen during the plugin's
apply; on unload it kills the child process and unregisters all tools automatically (cleanup registered viactx.effect, so hot reloads/unloads leave no residue); - Two bridges to choose from: the bundle's built-in bridge
repogate/bridge(zero dependencies, works out of the box) and the official dsh@deepseek-ai/dsh-mcp-clientconfig line (seeexamples/overlay-for-dsh.yml.example); the tools are named and behave identically — pick either one, do not enable both; - Environment variables: dsh filters credential-type variables from the MCP child process environment, so the official bridge line needs the token written into the
envconfig; the built-in bridge's child process inherits the host environment, soREPOGATE_TOKENis passed through automatically.
Common dsh issues
| Symptom | Treatment |
|---|---|
| Tools don't appear in the list | Check whether the cordis.patch.yml line took effect (dsh --profile <name> --dump-config to inspect layers), confirm no errors in the startup log |
| 401 invalid token | Check the env.REPOGATE_TOKEN config; or ask the model to call mcp__repogate__gh_auth_login in the session to use OAuth |
| Want read-only | Configure args: ['--read-only'] on the bridge line, or append --read-only to the official line's args |
| pnpm ≥10 rejects prepare scripts on git installs | This plugin is pure JS with no build script, so it's not affected; install from checkout or tarball |
Tool List (23 tools)
| Domain | Tool | Purpose | Write op |
|---|---|---|---|
| Repo | gh_repo_fetch |
Repo details: default branch, stars, language, visibility | |
| Repo | gh_repo_browse |
List user/org/own repos (paginated) | |
| Issue | gh_issue_open |
Create an issue (title required, optional labels/assignee) | ✔ |
| Issue | gh_issue_fetch |
View full issue info | |
| Issue | gh_issue_browse |
Filter by state/labels/assignee/author (pages may exclude PRs) | |
| Issue | gh_issue_edit |
Edit title/body/state/assignee/labels | ✔ |
| Issue | gh_issue_respond |
Post a comment (works in PR threads too) | ✔ |
| PR | gh_pr_open |
Create a pull request (head/base/draft) | ✔ |
| PR | gh_pr_fetch |
PR details: mergeability, changed stats, review count | |
| PR | gh_pr_browse |
Filter by state/branch, sort, paginate | |
| PR | gh_pr_edit |
Edit title/body/state/draft/base branch | ✔ |
| PR | gh_pr_merge |
Merge (method/commit message/delete source branch) | ✔ |
| Review | gh_review_submit |
Submit a full review: approve / request_changes / comment | ✔ |
| Review | gh_review_comment |
Line-level diff comments (including range comments) | ✔ |
| Review | gh_review_fetch |
List all line-level comments | |
| Review | gh_review_browse |
List submitted full reviews | |
| Search | gh_search_repos |
Search repos with GitHub search syntax | |
| Search | gh_search_issues |
Search issues/PRs (type:pr distinguishes) |
|
| Search | gh_search_code |
Search code (requires token, returns file hits) | |
| Account | gh_whoami |
Current identity, token source, read-only mode, API quota | |
| Auth | gh_auth_login |
Start OAuth device authorization (requires configured clientId) | |
| Auth | gh_auth_check |
Poll authorization result once | |
| Auth | gh_auth_logout |
Clear the local token cache |
All tools take JSON Schema inputs (name/description/inputSchema) that models can discover on their own;
write tools are intercepted with a clear message in read-only mode.
Configuration
Priority: CLI flags > environment variables > config file > defaults. The config file is JSON, its path given by --config
or REPOGATE_CONFIG; see examples/repogate.config.json.example.
| Config item | Environment variable | Default |
|---|---|---|
| Access token | REPOGATE_TOKEN (also accepts GITHUB_TOKEN / GH_TOKEN) |
none |
| API base URL (enterprise instances) | REPOGATE_BASE_URL |
https://api.github.com |
| Read-only mode | REPOGATE_READ_ONLY (1/true/yes/on) |
false |
| Per-request timeout (ms) | REPOGATE_TIMEOUT_MS |
30000 |
| OAuth Client ID | REPOGATE_OAUTH_CLIENT_ID |
none |
| OAuth token cache file | REPOGATE_TOKEN_FILE |
~/.repogate/token.json when oauth.clientId is configured |
| Config file path | REPOGATE_CONFIG |
none |
| Debug logging (stderr) | REPOGATE_DEBUG |
false |
CLI flags: --config --token --read-only --base-url --timeout-ms
--oauth-client-id --token-file --debug --version --help.
Authentication
Personal access token (PAT)
Generate one in GitHub's Developer settings (check the repo permissions needed on a fine-grained token), then choose any of:
REPOGATE_TOKEN=ghp_xxx node src/entry.js # environment variable
node src/entry.js --token ghp_xxx # CLI flag
node src/entry.js --config repogate.config.json # config file (token field)
Token resolution order: --token > REPOGATE_TOKEN > GITHUB_TOKEN > GH_TOKEN > config file > cache file.
OAuth device authorization (token-free interactive login)
For those who'd rather not assemble a token by hand. You first need a GitHub App's Client ID (the device flow only requires a public client_id):
- Configure
oauth.clientId(config file orREPOGATE_OAUTH_CLIENT_ID); - Ask the model to call
gh_auth_login→ returns an authorization URL and a one-time code; - The user opens the URL in a browser, enters the code, and confirms;
- The model calls
gh_auth_check(may be called multiple times; each call checks once) → oncegranted, the token is written to the cache file, and all tools become available; the cached token survives process restarts; gh_auth_logoutclears the cache.
Note: the device authorization endpoint always uses github.com; for enterprise instances (custom
baseUrl) use a PAT. The token cache file is written with 0600 permissions; do not commit the cache file to version control.
Read-only Mode
node src/entry.js --read-only # or REPOGATE_READ_ONLY=1
When enabled, the 8 write tools (gh_issue_open / gh_issue_edit / gh_issue_respond /
gh_pr_open / gh_pr_edit / gh_pr_merge / gh_review_submit / gh_review_comment)
are intercepted after argument validation and return a [readonly] error explaining how to turn it off; query, search, and auth tools are unaffected.
Error Handling
All failures are returned as structured errors (MCP isError: true + structuredContent.error) in the form
[error code] reason. Common error codes and typical scenarios:
| Error code | Scenario | Guide |
|---|---|---|
auth |
Token missing/invalid (401) | Configure a token or use OAuth device authorization |
ratelimit |
Quota exhausted (403/429) | Report reset time or Retry-After seconds |
http |
404/403/422/409 etc. | Explain the specific cause (not found/no permission/params rejected/conflict) |
validation |
Argument validation failed | Point out which argument is invalid |
readonly |
Read-only mode blocks a write | Explain how to disable |
timeout |
Request timeout | Suggest increasing timeoutMs |
network |
Network-layer failure | Check the network and baseUrl |
The gateway automatically retries once on 502/503/504 and network jitter (idempotent read requests only; writes are not retried to avoid duplicate side effects);
if a 5xx still fails after retry, it returns an http error instead of failing silently.
Architecture and Layout
src/
├── entry.js entry: config parsing → assembly → start the stdio session
├── protocol/ protocol layer (MCP over stdio, line-delimited JSON-RPC 2.0)
│ ├── jsonrpc.js message encoding/decoding and classification
│ ├── transport.js stdin/stdout read/write loop (logs go to stderr only)
│ └── engine.js session engine: initialize / ping / tools/list / tools/call
├── core/ core layer
│ ├── config.js layered config merge (flag > env > config file > defaults)
│ ├── auth.js credential hub: token resolution + OAuth device auth state machine + cache
│ ├── gateway.js REST gateway: request assembly/retry/timeout/status-code mapping
│ └── errors.js unified error model with actionable hints
├── tools/ tool layer
│ ├── registry.js registry: argument validation (JSON Schema subset) + read-only gate + dispatch
│ ├── repo.js / issue.js / pull.js / review.js / search.js / account.js
│ └── index.js assembles the 23 tools
├── bridge/ dsh integration
│ ├── client.js MCP stdio client (initialize/list/call, cancellation and timeout)
│ └── plugin.js Cordis plugin: spawns the server and registers tools into ctx.tools
└── util/format.js output shaping: entity summaries, pagination detection, URL building
test/ tests (node:test, zero dependencies)
├── helpers/ fake fetch and a local mock API service
└── *.test.js protocol/gateway/config/auth/registry/tool/end-to-end (121 test cases)
Design highlights:
- Layered one-way dependencies: protocol layer → core layer → tool layer; tools don't know protocol details, and the protocol doesn't know API details;
- On-demand token resolution: OAuth authorization takes effect after completion without restart (the gateway holds a tokenResolver rather than a static token);
- One codebase, both ends:
bridge/client.jsand the server share the same JSON-RPC vocabulary, so handshake and call logic are consistent.
Development and Testing
node --test # run all 121 tests (including real child-process end-to-end)
node src/entry.js --help
Test coverage: protocol handshake and error paths (including interception of uninitialized sessions), gateway retry (idempotent methods only) and status-code mapping, the full OAuth state machine (including expiry), config priority, argument validation, the read-only gate, request construction and output shaping for all 23 tools, and an end-to-end chain of "real child process + local mock API".
Security Notes
- A token has the same power as the account; do not write it into logs, commit it to version control, or leak it to untrusted conversations;
- Under dsh, MCP server commands are trusted code outside the sandbox; install this plugin only from trusted sources;
- Read-only mode significantly reduces the risk of misuse; research-only sessions are recommended to enable it.
License
Links
More in this category
superdesigndev/treg★ 425
Tool catalog for agents: search ~2,600 external endpoints (SEO and SERP, backlinks, social, people and company enrichment, ad libraries, scraping) by the task you want done, read each one's parameters and per-call price, then call it with the credential injected server-side. Ships the skill plus an MCP row that stays disabled until TREG_TOKEN is set.
Lum1104/dsh-browser★ 198
Chrome sidebar extension that lets DSH operate your browser directly, no vision capabilities required.
zhaoolee/notes★ 142
Export DSH conversations as Smartisan Notes-style PNGs, or create and update Markdown notes in a configured account-scoped workspace.
liustack/modsearch★ 111
Web search bridge for text-only agents: ask the web or X, get structured JSON evidence (search, fetch, citations).
taxueseek/argo★ 91
Search built for agents: multilingual coverage across web, academic, code, shopping, finance, news, and encyclopedias.
Vladimir-Human/ru-marketplace-mcp#dsh★ 63
Skills and optional MCP rows for ten Russian marketplaces: price comparison across Wildberries, Detsky Mir and Yandex Market, plus per-source search, product cards and reviews. The 13 skills load on install; both MCP rows stay disabled until RU_MARKETPLACE_MCP_DIR points at a local clone, which needs Python 3.12+ and uv.