Contributing¶
Thanks for your interest in improving @zereight/mcp-gitlab! This page
explains how to set up the project locally, the conventions we follow, and
how to get your change merged.
Quick links
Ways to contribute¶
- Report a bug — open a GitHub issue with steps to reproduce, the client you're using (Claude Code, VS Code, Cursor, etc.), the transport (stdio / SSE / streamable HTTP), and the relevant environment variables.
- Request a feature — open an issue describing the GitLab capability you want exposed and why it matters for your workflow.
- Submit a fix or new tool — fork the repo and open a pull request (details below).
- Improve the docs — this site is generated from
docs/. PRs that clarify a setup guide, fix a broken link, or add a missing example are very welcome.
Development setup¶
Prerequisites¶
| Tool | Version | Notes |
|---|---|---|
| Node.js | 22.21.1 | Pinned in .nvmrc. Use nvm use to match. |
| npm | bundled with Node 22 | |
| Git | any recent version | |
| Python 3.10+ | (only for docs) | Required if you want to preview the docs site. |
| GitLab account | optional | Needed only for live integration tests. |
Clone, install, build¶
git clone https://github.com/zereight/gitlab-mcp.git
cd gitlab-mcp
nvm use # or: nvm install
npm install
npm run build # compiles TypeScript to build/
Run the server locally¶
# Personal Access Token (stdio transport, the default for local clients)
GITLAB_PERSONAL_ACCESS_TOKEN=glpat-xxxx \
GITLAB_API_URL=https://gitlab.com/api/v4 \
node build/index.js
Watch mode for faster iteration:
See Environment Variables for the complete list of flags the server understands.
Project structure¶
.
├── index.ts # Main MCP server entry point — tool dispatch
├── config.ts # Env var + CLI argument parsing
├── schemas.ts # Zod schemas for every tool's input
├── tools/registry.ts # allTools[] + TOOLSET_DEFINITIONS (grouping)
├── auth-retry.ts # OAuth token refresh on 401
├── oauth.ts # Local-browser OAuth2 flow
├── oauth-proxy.ts # MCP OAuth proxy for remote clients
├── gitlab-client-pool.ts # Dynamic API URL connection pool
├── stateless/ # Multi-pod stateless OAuth helpers
├── utils/ # Shared helpers (schema, tool-args, etc.)
├── test/ # Mock + live test suites
├── docs/ # MkDocs source — published to GitHub Pages
├── scripts/ # Maintenance scripts (release, doc-gen)
└── .github/workflows/ # CI pipelines
When adding a new tool:
- Define the input schema in
schemas.ts— usez.object({…}).describe(...)on each field; the generated docs depend on those descriptions. - Register it in
tools/registry.ts— add toallToolswith aname,description, andinputSchema, then add the name to the appropriateTOOLSET_DEFINITIONSentry. - Implement the dispatch case in
index.tsunder the relevantswitchblock. - Add a test under
test/that actually calls the tool (callTool("name")or equivalent). - Fork PRs can skip regenerating the Tools Reference and tool invocation coverage (
make tools-docs); a bot syncs them after merge (see below).
Testing¶
npm run test:mock # Fast unit + mock-server tests. Run these locally.
npm run test:consumer-smoke # Pack + install smoke test (same as CI).
npm run test:all # mock + live; live tests need a real GitLab token.
npm run test:schema # JSON schema validation.
npm run test:oauth # OAuth2 flow against the mock server.
npm run test:stateless # Multi-pod / stateless OAuth helpers.
CI runs test:mock, test:consumer-smoke, and npx tsc --noEmit on every PR.
Make sure those pass locally before pushing.
Live integration tests¶
GITLAB_PERSONAL_ACCESS_TOKEN=glpat-xxxx \
GITLAB_API_URL=https://gitlab.com/api/v4 \
npm run test:live
Live tests touch a real GitLab instance — use a throwaway sandbox project, not anything you care about.
Code style¶
| Tool | Command | Purpose |
|---|---|---|
| Prettier | npm run format |
Formats *.{js,ts,json,md}. |
| Prettier (check) | npm run format:check |
CI gate. |
| ESLint | npm run lint |
TypeScript lint. |
| ESLint (autofix) | npm run lint:fix |
Auto-fix where possible. |
| TypeScript | npx tsc --noEmit |
Type check. |
Conventions enforced by CI:
- No stray
console.login*.ts(test files are exempt). Use proper logging. - No
TODO/FIXME/XXXcomments in committed code — open an issue instead. - Strict TypeScript —
npm run buildemits withtsconfig.build.json(tests and scripts stay out ofbuild/).tsc --noEmitstill typechecks the fulltsconfig.json, including tests.
Submitting changes¶
-
Branch from
main. Use a descriptive name: -
feat/<short-description>for new features or new tools fix/<short-description>for bug fixesdocs/<short-description>for docs-only changes-
chore/<short-description>for refactors, deps, CI tweaks -
Commit messages — short, present-tense, lowercase prefix matching the branch type:
feat: add list_dependency_proxy_blobs tool
fix: handle 429 rate-limit on get_merge_request
docs: clarify OAuth redirect URI for Cursor
We don't enforce Conventional Commits strictly, but the changelog
generator (npm run changelog) groups commits by these prefixes.
-
Pull request — open against
main. In the description, include: -
What changed (one paragraph).
- Why — link an issue if there is one.
- How tested — which
npm testcommands ran, and any manual verification with a real MCP client (Claude Code, Cursor, etc.). -
Breaking changes — call them out explicitly.
-
CI must pass — the PR workflow runs the mock test suite, type check, lint, Docker build, formatting check, and
npm audit. Address all real failures; existing "warning" steps (e.g., the TODO grep) are informational. -
Review — a maintainer will review. We try to respond within a few days. Small, focused PRs get merged fastest.
Documentation contributions¶
This site is generated from docs/ with
MkDocs Material.
make serve # preview locally at http://127.0.0.1:8000/gitlab-mcp/
make build # strict build (same as CI)
make tools-docs # regenerate tool pages + docs/reference/tool-coverage.md
When changing tools, schemas, or toolsets:
The per-group Tools Reference pages and
tool invocation coverage are generated from
tools/registry.ts, the Zod schemas, and the tests under test/. If your PR
comes from a fork, you don't need to regenerate them:
- On a PR from a fork, a generated-docs diff in the
docs-buildjob is only a notice. It does not fail the job. - After merge, the
tool-docs-sync.ymlworkflow runsnpm run docs:toolsonmainand, if the output changed, opens anautomation/tool-docsPR asgithub-actions[bot].
To preview the pages locally, run make tools-docs. For a PR from a branch
in this repository, docs-build still fails on drift, so commit the result.
The docs CI workflow regenerates the tool pages, then rebuilds and deploys
the site on every push to main that touches docs/, mkdocs.yml,
requirements-docs.txt, a .ts file, test/, or package.json /
package-lock.json.
Hosted site (maintainers, one-time): GitHub Pages must use
Build and deployment → Source: GitHub Actions, not deploy from main.
See GitHub Pages Setup for details and
verification steps.
When changing prose docs:
Just edit the markdown under docs/ and run make serve to preview.
mkdocs build --strict (run by CI) will fail on broken internal links, so
keep cross-references accurate.
Release process¶
Stable releases ship weekly, every Tuesday at 18:55 US Eastern, when something
user-facing has merged since the last release. Security fixes and fixes for
outages or severe regressions ship as out-of-band hotfixes. A PR merged to
main ships in the next release that is actually cut once it meets the
stable-release conditions
(reviewed PR, green CI, maintainer approval where required).
Releases are cut by maintainers via scripts/release.sh and published to
npm via the npm-publish.yml workflow, plus Docker Hub, the MCP Registry, and
the MCP Bundle. External contributors don't need to do anything for release —
the maintainer will handle versioning and the changelog. See
RELEASING.md
for the full process, approval rules, and supported versions.
Getting help¶
- Questions about usage — open a GitHub Discussion or a discussion-style issue.
- Sensitive reports (security, conduct) — email the maintainers via the address listed in the repo's
package.json/ npm page rather than filing a public issue.
Thanks for contributing!