Contributing to FocusMCP core
April 30, 2026 · View on GitHub
Thank you for your interest in FocusMCP. This document explains how to contribute.
AI-assisted contributions
FocusMCP was largely built with Claude Code. We encourage and welcome AI-assisted PRs.
You don't need to hide it. If Claude wrote the code, just say so in the PR description
(Generated with Claude Code, Co-authored by GPT-4, whatever's accurate). Bonus points
for including the prompt or the key instructions you used.
What we care about, regardless of who wrote it:
- Tests pass
- Types are strict (no
any, no@ts-ignorewithout a comment) - Lint is green (
pnpm lint) - Coverage >= 80% (100% on critical modules)
- Commit messages follow Conventional Commits
- PR has a clear description — "what, why, how to verify"
- You understand the diff and can discuss design during review
What gets you rejected:
- Obviously untested AI slop (generated code that doesn't run)
- PRs with no description, just "here's some code"
- Hidden AI use that makes review confusing
We don't care if you used AI, we care if the PR is good.
Code of Conduct
All contributors are expected to follow the Code of Conduct.
Architecture — the golden rule
@focus-mcp/core is the brains. The CLI and other hosts are thin wrappers.
Rule: any non-trivial logic belongs in core, never in the CLI or other consumers.
The three pillars of core:
- Registry — brick lifecycle state machine (
registered → started → running → stopped) - EventBus — routes tool calls between bricks, applies rate limiting, permission checks, tracing
- Router — translates MCP protocol (JSON-RPC) to internal bus events and back
Additional packages in this monorepo:
packages/sdk—defineBrick()helper for brick authors (browser-compatible)packages/validator— conformance test runner for bricks
The core is designed to be browser-compatible. Do not add Node.js-only imports to packages/core or packages/sdk. Node-specific adapters live in the CLI.
The update-checker pattern (packages/core/src/update-checker/) is the reference for background tasks that must not block the main event loop.
Git workflow
main ← stable releases only (never commit directly)
develop ← integration branch (persistent, never force-delete)
feat/* ← feature branches, branch FROM develop
fix/* ← bug fix branches, branch FROM develop
docs/* ← documentation branches
- Fork the repo and branch from
develop(git checkout -b feat/my-feature origin/develop). - Code with TDD: write tests BEFORE the code (Red → Green → Refactor).
- Open a PR targeting
develop.mainis release-only. - Auto-merge is enabled: once CI passes and at least one review is approved, the PR merges automatically.
- Never force-push to
developormain.
PRs must target
develop, notmain. Thedevelopbranch is permanent — never force-delete it.
Commit conventions
Enforced by commitlint (config/commitlint.config.js):
| Rule | Value |
|---|---|
| Types allowed | feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert, release |
| Header max length | 100 characters |
| Body max line length | disabled |
| Footer max line length | disabled |
| Subject case | lowercase (not UPPER, not PascalCase, not Start Case) |
Scope is the package or subsystem: feat(registry): ..., fix(event-bus): ..., docs(sdk): ....
Quality gates
Before opening a PR:
pnpm lint
pnpm typecheck
pnpm test # coverage >= 80% global, >= 95% on EventBus/Registry
pnpm build
pnpm reuse # REUSE compliance (SPDX headers)
Never use --no-verify to bypass these checks — CI enforces them regardless.
Standards
- TypeScript strict (configured in
config/tsconfig.base.json) - TDD strict — coverage thresholds enforced in CI
- Conventional Commits:
feat,fix,docs,chore,refactor,test,perf,build,ci,style,revert - SPDX headers in every source file —
SPDX-FileCopyrightText+SPDX-License-Identifierset toMIT - REUSE compliance verified in CI
- No
console.log: use the logger from@focus-mcp/core - No
any: TypeScript strict + BiomenoExplicitAny node:import protocol: always prefix Node built-ins withnode:- Browser-compatible core: no Node.js-only APIs in
packages/coreorpackages/sdk
npm scope
All packages are published under the @focus-mcp scope (with hyphen):
@focus-mcp/core@focus-mcp/sdk@focus-mcp/validator
Never write @focusmcp (without hyphen) in new code, docs, or commit messages.
Common pitfalls
- Browser-compat breakage — importing
node:fs,node:path, or any Node built-in inpackages/coreorpackages/sdkbreaks browser consumers. Use dependency injection or move the logic to the CLI. - EventBus/Registry coverage drop — these modules have a >= 95% coverage threshold. Touching them without full tests will fail CI.
develop↔maindivergence — if CI reports thatdevelopis behindmain, wait for the maintainer to run the back-merge workflow. Do not manually mergemaininto your branch.- update-checker side effects — background checks must be non-blocking and must not throw unhandled rejections. Mirror the existing update-checker pattern.
- ADR missing — significant architectural decisions (new pillar, protocol change, new cross-cutting concern) require a new ADR in
docs/adr/.
Developing a brick
See packages/sdk/README.md for the brick authoring guide.
Bricks developed in this repo are primitives and infrastructure. Domain-specific bricks belong in the marketplace.
Architecture Decision Records (ADR)
Any significant architectural decision must be documented in docs/adr/.
Format: MADR.
Reporting a bug / proposing a feature
Open an issue using the appropriate template: bug or feature.
Security
Vulnerabilities must be reported privately — see SECURITY.md.
Release process
See docs/RELEASE.md for the full release guide (for maintainers).
The core must be released before the CLI when both are updated together. The marketplace may depend on @focus-mcp/sdk — always release core first.