The bulk-first design is the strength — issue_ids plurals across close, reopen, move, assignee, label and milestone edits mean a board reshuffle is a single request rather than a loop, and zenhub_get_workspace_overview is the entry point the rest of the list assumes you called first. The thing to weigh is zenhub_query_potentially_dangerous: it executes arbitrary GraphQL including mutations, and its only protection is an instruction in its own description telling the caller to obtain explicit approval, which is guidance to a model rather than a check. One documented dead end is worth noting too — zenhub_get_repository_collaborators states that repository collaborators are not available through the ZenHub API and points at zenhub_get_workspace_users instead.
A ZenHub workspace client with 51 tools covering issue creation and editing, bulk assignee, label, estimate and milestone changes, epics and sub-issues, workspaces and repositories, sprints, pipelines, blocking dependencies, label creation on both the GitHub and ZenHub side, user lookup, search, and a raw GraphQL escape hatch.
- Bulk editing as the default shape: zenhub_close_issues, zenhub_reopen_issues, zenhub_move_issue, zenhub_add_assignees_to_issues, zenhub_remove_assignees_from_issues, zenhub_add_labels_to_issues, zenhub_remove_labels_from_issues and zenhub_add_milestone_to_issues all take issue_ids rather than a single issue, and zenhub_set_multiple_estimates takes an estimates list.
- Board position as a parameter: zenhub_move_issue takes pipeline_id and position, and zenhub_reopen_issues takes the same pair, so reopening an issue lands it somewhere chosen rather than wherever it was.
- Epics with children and dates: zenhub_create_epic can take epic_child_ids at creation time, zenhub_add_subissues_to_epic attaches existing issues to a parent_epic_id afterwards, and zenhub_update_epic_dates sets start_date and end_date.
- A single call that orients you in a workspace: zenhub_get_workspace_overview returns pipelines with issue counts, repositories with issue counts, the default repository and its issue types, epic summaries and workspace users — and zenhub_search_issues_in_repository says outright to call it first for the repository and pipeline IDs it needs.
- Sprints and milestones kept apart: zenhub_create_sprint takes start_date, end_date, timezone and settings, zenhub_update_sprint can change state, and zenhub_add_issues_to_sprints maps issue_ids onto sprint_ids, while zenhub_create_milestone and zenhub_update_milestone carry due_date and start_date on the repository side.
- Blocking relationships as data: zenhub_create_issue_dependency and zenhub_delete_issue_dependency take the blocking and blocked issue numbers with their repository GitHub IDs, so a dependency can span repositories.
- Labels on both sides of the integration: zenhub_create_github_label writes to a repository_id and zenhub_create_zenhub_label writes to a workspace_id, with zenhub_get_repository_labels and zenhub_get_workspace_labels reading each side back.
- Search scoped to wherever you are looking: zenhub_search_issues filters inside one pipeline_id by title, labels or assignees, zenhub_search_issues_in_repository spans repo_ids and pipeline_ids within a workspace, and zenhub_get_workspace_issues pages the whole workspace with after.
- A fallback for everything the list does not cover: zenhub_query_potentially_dangerous runs a custom GraphQL query with variables.
ZENHUB_API_KEY. Past that, identifiers are the real prerequisite: the write tools address a workspace_id, repository_id, pipeline_id or issue_ids, and zenhub_get_user_organizations, zenhub_get_user_workspaces and zenhub_get_workspace_overview are how you come by them. zenhub_get_viewer confirms which ZenHub and GitHub identity the key resolves to.
One command plus a key — npx zenhub-mcp-server, then supply credentials
