Thanks for helping improve UniFi MCP. Please follow the Code of Conduct in all project spaces.
- Search existing issues and discussions before opening a new one.
- For substantial features, behavior changes, or new tool categories, open an issue first so maintainers can confirm the direction before you invest time.
- Use GitHub Discussions for setup help, configuration questions, and general troubleshooting.
- Do not include UniFi credentials, API keys, tokens, controller addresses, or other private deployment details in public issues, discussions, pull requests, screenshots, or logs.
- Report suspected vulnerabilities privately through GitHub Security Advisories, not public issues.
For more support routing details, see SUPPORT.md.
New to the codebase? Read these in order:
- README.md -- project overview and installation
- QUICKSTART.md -- get a server running in 5 minutes
- docs/ARCHITECTURE.md -- monorepo layout, package responsibilities, and the tool/manager/connection layering
- AGENTS.md -- architecture rules, coding conventions, and permission/confirmation patterns enforced across servers
- docs/README.md -- full documentation index (tool loading modes, permissions, deployment)
The Monorepo Layout table below and the Development Workflow section are the fastest way to find where a change belongs once you know what you're changing.
- Python 3.13+
- Node.js 22+ for the Cloudflare Worker app
- uv (package manager)
git clone https://github.com/sirkirby/unifi-mcp.git
cd unifi-mcp
make syncThis installs all Python workspace packages in development mode plus the self-contained worker npm dependencies.
| Path | What | Makefile |
|---|---|---|
/ |
Workspace root | make test runs all tests |
apps/network/ |
Network MCP server | make test, make lint, make manifest |
apps/protect/ |
Protect MCP server | make test, make lint, make manifest |
apps/access/ |
Access MCP server | make test, make lint, make manifest |
apps/worker/ |
Cloudflare Worker gateway + npm CLI | make check |
packages/unifi-core/ |
Shared connectivity | Tested via root make core-test |
packages/unifi-mcp-shared/ |
Shared MCP patterns | Tested via root make shared-test |
The root Makefile delegates to app/package Makefiles:
make test # Run ALL tests (core + shared + relay + worker + apps)
make check # Run format check + lint + generated drift checks + tests
make build # Build deployable artifacts, including the worker typecheck
make sync # Install Python workspace + worker npm dependencies
make lint # Lint all packages
make format # Format all packages
make pre-commit # Format + lint + test
# Individual packages
make core-test # Run unifi-core tests
make shared-test # Run unifi-mcp-shared tests
make relay-test # Run unifi-mcp-relay tests
make worker-build # Install worker deps + typecheck the Worker app
make worker-check # Run worker CLI tests + TypeScript checks
make network-test # Run network server tests
make network-lint # Lint network server
make network-manifest # Regenerate network tools manifest
make protect-test # Run protect server tests
make protect-lint # Lint protect server
make protect-manifest # Regenerate protect tools manifest
make access-test # Run access server tests
make access-lint # Lint access server
make access-manifest # Regenerate access tools manifestFor focused work on the network server:
cd apps/network
make test # Run network tests
make lint # Lint network code
make format # Format network code
make manifest # Regenerate tools_manifest.json
make run-lazy # Run in lazy mode (default)
make run-eager # Run in eager mode
make run-meta # Run in meta_only mode
make pre-commit # Format + lint + testThe protect and access servers have the same targets:
cd apps/protect
make test # Run protect tests
make lint # Lint protect code
make format # Format protect code
make manifest # Regenerate tools_manifest.json
make run-lazy # Run in lazy mode (default)
make run-eager # Run in eager mode
make run-meta # Run in meta_only mode
make console # Start interactive dev console
make pre-commit # Format + lint + testcd apps/access
make test # Run access tests
make lint # Lint access code
make format # Format access code
make manifest # Regenerate tools_manifest.json
make run-lazy # Run in lazy mode (default)
make run-eager # Run in eager mode
make run-meta # Run in meta_only mode
make console # Start interactive dev console
make pre-commit # Format + lint + test- Add the manager method in
apps/network/src/unifi_network_mcp/managers/<domain>_manager.py - Add the tool function in
apps/network/src/unifi_network_mcp/tools/<category>.py - Add the tool name to
TOOL_MODULE_MAPinapps/network/src/unifi_network_mcp/utils/lazy_tool_loader.py - Run
make network-manifestfrom the repo root (ormake manifestfromapps/network/) - Add tests in
apps/network/tests/ - Commit code + manifest + tests together
- Add the manager method in
apps/protect/src/unifi_protect_mcp/managers/<domain>_manager.py - Add the tool function in
apps/protect/src/unifi_protect_mcp/tools/<category>.py - Run
make protect-manifestfrom the repo root (ormake manifestfromapps/protect/)- The manifest auto-discovers tools from
@server.tool()decorators; no manual map update needed
- The manifest auto-discovers tools from
- Add tests in
apps/protect/tests/ - Commit code + manifest + tests together
- Add the manager method in
apps/access/src/unifi_access_mcp/managers/<domain>_manager.py - Add the tool function in
apps/access/src/unifi_access_mcp/tools/<category>.py - Run
make access-manifestfrom the repo root (ormake manifestfromapps/access/) - Add tests in
apps/access/tests/ - Commit code + manifest + tests together
- Make the change in
packages/unifi-core/orpackages/unifi-mcp-shared/ - Run the relevant package tests:
make core-testormake shared-test - Run
make testto verify nothing breaks across the workspace
Before committing:
make pre-commit # Format + lint + all testsOr from the app directory:
cd apps/network && make pre-commitFormatting uses ruff with a 120-character line length.
- Fork the repository
- Create a feature branch:
git checkout -b feat/my-feature - Make changes, run
make pre-commit - Push and open a PR against
main - All PRs require passing CI (lint + test)
Do not push directly to main.
Commit message style:
feat:new featurefix:bug fixdocs:documentation onlyrefactor:code change that neither fixes a bug nor adds a featuretest:adding or updating testschore:maintenance (deps, CI, config)
# All tests
make test
# With coverage
cd apps/network && make test-cov
cd apps/protect && make test-cov
cd apps/access && make test-cov
# Specific test file
uv run --package unifi-network-mcp pytest apps/network/tests/unit/test_permissions.py -v
uv run --package unifi-protect-mcp pytest apps/protect/tests/unit/test_camera_tools.py -v
uv run --package unifi-access-mcp pytest apps/access/tests/unit/test_door_tools.py -vTests use pytest-asyncio for async support and aioresponses for HTTP mocking.
- Run
make pre-commitfrom root. - Determine release scope by package tag namespace (
core/v*,shared/v*,network/v*,protect/v*,access/v*,api/v*,relay/v*,worker/v*). - If a downstream package needs code from a newly released upstream package, update its
pyproject.tomldependency range before tagging. For example, Protect/API releases that require newunifi-coremodels must allow the newunifi-coreline. - Run
uv lock --checkand commit any dependency-bound changes before creating local tags. - Push tags in dependency order:
corefirst, thenshared, then app/API packages, thenrelay, thenworkerif the worker needs the released relay behavior. Wait for each upstream package to appear on PyPI before pushing dependents. - CI publishes to PyPI or npm, builds Docker images where applicable, and creates GitHub Releases.
Open an issue or discussion at https://github.com/sirkirby/unifi-mcp/issues