MarzbanSDK
Resources

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:

PathPackageWhat it is
packages/sdkmarzban-sdkThe SDK itself. Published to npm.
packages/mcpmarzban-mcpMCP server built on top of the SDK. Published to npm, Docker Hub, and the MCP Registry.
packages/climarzban-cliCommand-line interface. Still an unpublished work in progress.
apps/docsmarzban-sdk-docsThis 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 test

Available scripts

Run from the repo root, these fan out across the workspace via Turborepo:

CommandDescription
pnpm buildBuild all packages
pnpm testRun the unit test suite for every package
pnpm lintRun ESLint across the workspace
pnpm lint:fixRun ESLint with --fix
pnpm types:checkType-check every package with tsc --noEmit
pnpm formatFormat the repo with Prettier
pnpm format:checkCheck formatting without writing

Package-specific scripts need --filter:

CommandDescription
pnpm --filter marzban-sdk test:watchWatch mode while developing
pnpm --filter marzban-sdk test:coverageRun tests with a coverage report
pnpm --filter marzban-sdk test:integrationRun integration tests against a real panel
pnpm --filter marzban-sdk codegenRegenerate the API client from the OpenAPI spec
pnpm --filter marzban-sdk devBuild 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 data

See 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 report

Hand-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:integration

They don't run as part of pnpm test and aren't held to the coverage threshold.

Build & verify

pnpm build
pnpm test
pnpm types:check

All three must pass before submitting a PR.

Submitting a pull request

  1. Fork the repository and create a new branch:
    git checkout -b feat/your-feature
  2. Make your changes.
  3. Add or update tests next to the code they cover (src/**/*.test.ts).
  4. Run pnpm test and pnpm build.
  5. Commit using Conventional Commits (enforced by commitlint):
    feat: add pagination to getUsers
    fix: handle missing token in AuthManager
    docs: add NestJS integration example
  6. Push and open a PR against the dev branch.

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 bumps

Types: 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 strict mode enabled.
  • ESLint + Prettier enforce style — run pnpm lint before committing.
  • No unnecessary type — prefer proper generics or unknown.
  • 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 codegen

If 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:check

Reproduce 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:mcp

Questions

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.

On this page