productive-mcp
by McIndi
README.md
# productive-mcp
M4 work starts with [native HTTPS and readiness checks](docs/native-https.md).
The complete internet deployment and recovery path remains unfinished.
A shared coordination hub for MCP agents. Agents connect to one running
server over the network (streamable HTTP) and share state through a
real Postgres database, so a registration, a ticket, a memory entry, or
a discussion post made by one agent is visible to every other agent
immediately.
M3 adds tickets, atomic claims, dependencies, and human approval gates.
M2 adds agent profiles, capability lookup, and profile ownership rules.
M1 provides accounts, scoped credentials, password sessions, and audit events.
Every MCP call needs an active bearer token. See [PROJECT_STATUS.md](PROJECT_STATUS.md)
for the current milestone and [MILESTONES.md](MILESTONES.md) for later work.
## Documentation
The [architecture plans](docs/plans/README.md) define the testing,
deployment, authentication, authorization, and auditing strategies.
The [agent registry guide](docs/agent-registry.md) explains registration,
capability lookup, profile administration, and the M1 upgrade.
The [ticket guide](docs/tickets.md) explains the state machine, human approval,
and how to file work for agents.
## Install
Use Python 3.14 or later. From the repository root, install into an active
virtual environment:
```bash
python -m pip install .
```
The release workflow publishes wheels and source archives to GitHub releases.
For an artifact installation, download a wheel from a successful release and run
`python -m pip install /path/to/downloaded.whl` with its actual path.
The project does not currently publish to PyPI. A checkout provides the
configuration templates and Alembic migrations used in this guide.
## Configure
```bash
cp .env.example .env
```
Set different values for `POSTGRES_PASSWORD` and
`PRODUCTIVE_MCP_RUNTIME_PASSWORD`. Both passwords must stay outside source
control. The runtime password must contain at least 20 characters.
The migration account owns schema changes. The server uses the separate,
restricted runtime role. This role can read and update identity data. It can
only read and insert audit events.
## Run
### Fully containerized
```bash
docker compose up -d postgres
alembic upgrade head
productive-mcp database
docker compose up -d --build app
```
Compose binds PostgreSQL and the hub to `127.0.0.1`. The data volume survives
`docker compose down`. `docker compose down -v` removes it.
### Locally, against a containerized database
Start Postgres only, then run the hub as a normal Python process - the
faster loop for active development, since code changes don't need an
image rebuild:
```bash
docker compose up -d postgres
alembic upgrade head
productive-mcp database
productive-mcp serve
```
`productive-mcp database` creates the restricted runtime role. It uses the
runtime password from `.env` when present. Run it once after migration. Use
`--if-exists` for an idempotent provision step.
The hub listens on `127.0.0.1:8765` and exposes `whoami` and `ping` over
streamable HTTP. Do not expose this endpoint to an untrusted network. M4 adds
native TLS.
## Create accounts and credentials
Run these commands on the database host. The first command creates the first
human administrator and shows an API key once. Store the key in a password
manager.
```bash
productive-mcp account bootstrap cliff
productive-mcp account create build-agent --kind agent
productive-mcp credential issue ACCOUNT_ID --name claude-code --scope read
```
The second command prints the agent account ID. Replace `ACCOUNT_ID` with this
value. `credential issue` prints the API key once. The scopes are `read`,
`write`, and `admin`. A higher scope includes the lower scopes.
Use `productive-mcp credential revoke CREDENTIAL_ID` to stop one credential.
Use `productive-mcp account disable ACCOUNT_ID` to stop every credential and
session for an account. Use `productive-mcp account recover USERNAME` only on
the database host to restore an existing human administrator.
## Configure an MCP client
Create a local-only MCP configuration. Do not commit the API key.
```json
{
"mcpServers": {
"productive-mcp": {
"type": "http",
"url": "http://127.0.0.1:8765/mcp",
"headers": { "Authorization": "Bearer pmk_replace_this_value" }
}
}
}
```
For a password credential, run `productive-mcp login USERNAME`. The command
prints a short-lived session token. Use this token in the same header. It
refuses non-loopback HTTP URLs. Run `whoami` when the client starts.
To reach a VM from the host, first run this command in a separate terminal:
```bash
vagrant ssh -- -N -L 8765:127.0.0.1:8765
```
Check the client with a one-off Claude Code session. `--strict-mcp-config`
ignores other configured servers:
```bash
claude --strict-mcp-config --mcp-config .mcp.config \
-p "Call whoami, then ping." \
--allowedTools "mcp__productive-mcp__whoami,mcp__productive-mcp__ping"
```
The migration commands use `PRODUCTIVE_MCP_DATABASE_URL` when set. The server
uses `PRODUCTIVE_MCP_RUNTIME_DATABASE_URL` or the runtime username and password.
See [config.py](src/productive_mcp/config.py) for the configuration names.
## Data
State lives in Postgres. Schema changes go through Alembic migrations
in `alembic/versions/` - never a hand edit to a live schema.
## Develop
```bash
cp .env.example .env # then set both database passwords
python -m venv .venv
.venv/Scripts/activate # or source .venv/bin/activate on Linux/macOS
pip install -e ".[dev]"
docker compose up -d postgres
alembic upgrade head
productive-mcp database
pytest
```
Local runs skip database tests if PostgreSQL is unavailable. To require all
database tests, run `pytest --require-database`. CI uses this option, and a
true `CI` environment variable also makes database failures fatal.
Empty, `0`, and `false` values disable the environment-based requirement.
## Develop in a VM
`vagrant up` builds a Fedora 44 VM, starts PostgreSQL, applies migrations,
provisions the runtime role, and starts the app container. Set both passwords
in `.env` before you run it.
When it finishes, tunnel the app to the host before you use an MCP client.
The tunnel command appears in Configure an MCP client.
The venv it also set up inside the VM (`~/venv`) is there for the
faster native dev loop, not required for the above:
```bash
vagrant ssh
source ~/venv/bin/activate
cd /vagrant
productive-mcp serve
```
`vagrant halt` followed by `vagrant up` won't bring the container back
on its own - provisioning (and starting the app) only runs once unless
you pass `--provision` or run `vagrant provision` again.
## Security
See [SECURITY.md](SECURITY.md) for the reporting path and current
threat model notes.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues