Contributing
How to contribute to MarzbanSDK — reporting bugs, submitting PRs, and development setup in the pnpm/Turborepo monorepo.
Thank you for your interest in contributing to MarzbanSDK! All contributions are welcome — bug fixes, features, documentation improvements, and test coverage.
Reporting issues
Open an issue on GitHub and include:
- A clear description of the problem.
- Steps to reproduce (code snippet or minimal repo link preferred).
- Expected vs. actual behaviour.
- The SDK version you're on and the package it's in (
marzban-sdk,marzban-mcp, ...).
Repository layout
MarzbanSDK is a monorepo managed with pnpm workspaces and Turborepo:
| Path | Package | What it is |
|---|---|---|
packages/sdk | marzban-sdk | The SDK itself. Published to npm. |
packages/mcp | marzban-mcp | MCP server built on top of the SDK. Published to npm, Docker Hub, and the MCP Registry. |
packages/cli | marzban-cli | Command-line interface. Still an unpublished work in progress. |
apps/docs | marzban-sdk-docs | This documentation site. |
Development setup
You'll need Node ≥ 24 and pnpm 11 (the exact version is pinned in the root package.json's packageManager field — run any pnpm command and Corepack will fetch it automatically).
git clone https://github.com/Ilmar7786/marzban-sdk.git
cd marzban-sdk
pnpm install
pnpm build && pnpm testAvailable scripts
Run from the repo root, these fan out across the workspace via Turborepo:
| Command | Description |
|---|---|
pnpm build | Build all packages |
pnpm test | Run the unit test suite for every package |
pnpm lint | Run ESLint across the workspace |
pnpm lint:fix | Run ESLint with --fix |
pnpm types:check | Type-check every package with tsc --noEmit |
pnpm format | Format the repo with Prettier |
pnpm format:check | Check formatting without writing |
Package-specific scripts need --filter:
| Command | Description |
|---|---|
pnpm --filter marzban-sdk test:watch | Watch mode while developing |
pnpm --filter marzban-sdk test:coverage | Run tests with a coverage report |
pnpm --filter marzban-sdk test:integration | Run integration tests against a real panel |
pnpm --filter marzban-sdk codegen | Regenerate the API client from the OpenAPI spec |
pnpm --filter marzban-sdk dev | Build in watch mode |
There's no root-level codegen script — it only exists on packages/sdk, so it always needs --filter marzban-sdk (or pnpm turbo run codegen).
Local Marzban panel
To try changes against a real Marzban panel, spin up a disposable one in Docker:
pnpm local:up # start
pnpm local:logs # follow logs
pnpm local:down # stop
pnpm local:reset # stop and wipe dataSee local/README.md for details. This is also what the integration test suite runs against.
Testing
The SDK and MCP server are covered by Vitest. Unit tests live next to the code they cover, as src/**/*.test.ts.
pnpm --filter marzban-sdk test # run once
pnpm --filter marzban-sdk test:watch # watch mode while developing
pnpm --filter marzban-sdk test:coverage # run with a coverage reportHand-written code is held at 100% coverage — statements, branches, functions, and lines — in both packages/sdk and packages/mcp. Generated code (src/gen/**), type-only files (**/types.ts, **/*.types.ts), and barrel files (**/index.ts) are excluded from coverage on purpose.
Every feature and bug fix must come with tests, and pnpm --filter marzban-sdk test:coverage must stay green.
Integration tests are separate — test/integration/**/*.integration.test.ts, run with their own config against a real panel:
pnpm local:up
pnpm --filter marzban-sdk test:integration
pnpm --filter marzban-mcp test:integrationThey don't run as part of pnpm test and aren't held to the coverage threshold.
Build & verify
pnpm build
pnpm test
pnpm types:checkAll three must pass before submitting a PR.
Submitting a pull request
- Fork the repository and create a new branch:
git checkout -b feat/your-feature - Make your changes.
- Add or update tests next to the code they cover (
src/**/*.test.ts). - Run
pnpm testandpnpm build. - Commit using Conventional Commits (enforced by commitlint):
feat: add pagination to getUsers fix: handle missing token in AuthManager docs: add NestJS integration example - Push and open a PR against the
devbranch.
PRs target dev, not main. dev is the integration branch — it merges into main periodically. Both branches require the ci check to pass.
Commit message format
type(scope?): short description
Optional body.
BREAKING CHANGE: description ← for major version bumpsTypes: feat, fix, docs, refactor, test, chore, perf, build, ci.
Scopes: sdk, cli, mcp, docs, deps, ci, release, changelog. A scope can be a comma-separated list — if a fix in packages/sdk also changes marzban-mcp's behaviour, scope it fix(sdk,mcp): ... so it surfaces in mcp's changelog too.
A Husky pre-commit hook runs lint-staged (ESLint + Prettier on staged files), and commit-msg runs commitlint against these rules.
Code style
- All code is in TypeScript with
strictmode enabled. - ESLint + Prettier enforce style — run
pnpm lintbefore committing. - No unnecessary
type — prefer proper generics orunknown. - No comments that explain what the code does — only why when it's non-obvious.
- MDX content under
apps/docs/content/is excluded from Prettier and formatted by hand — Prettier's MDX printer can mangle fenced code blocks inside JSX.
Regenerating the API client
packages/sdk/src/gen/ is auto-generated from the OpenAPI spec by kubb. Do not edit it manually — a fresh generation wipes the directory clean:
# Update packages/sdk/openapi/openapi.json first, then:
pnpm --filter marzban-sdk codegenIf you're fixing a bug in a generated file, fix the template or the spec instead.
Some schemas in packages/sdk/openapi/openapi.json are hand-patched on top of what upstream Marzban publishes — see Spec Patches before touching them, and add a regression test alongside any new patch.
CI
Every PR against main or dev runs on Node 24:
pnpm turbo run lint types:check test build
pnpm format:checkReproduce it locally with the same two commands. A separate integration workflow runs the integration suites against a Docker panel — it's informational and doesn't block merging.
Releases
There's no manual tagging. A release is triggered by a version bump in a package's package.json landing on main: git-cliff generates the changelog, and a GitHub Release creates the tag (sdk-v*, mcp-v*), which publishes to npm. Preview the changelog for a pending bump locally:
pnpm changelog:sdk
pnpm changelog:mcpQuestions
Start a discussion in GitHub Discussions or open an issue. For how the workspace, CI, and releases actually work under the hood, docs/ in the repository is the canonical source.
Spec Patches
Patches applied on top of upstream Marzban's OpenAPI spec, and why — so you know when a type differs from what the panel actually returns.
Changelog
MarzbanSDK and marzban-mcp release history — generated directly from each package's CHANGELOG.md, so it can't drift from what's on npm or GitHub Releases.