Metadata-Version: 2.4
Name: polypack-mcp
Version: 0.1.25
Summary: MCP server exposing Polypack as persistent adaptive memory
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: mcp<1.10,>=1.9
Requires-Dist: anyio<4.10,>=4.5
Provides-Extra: polypack
Requires-Dist: polypack-db>=3.3.1; extra == "polypack"
Provides-Extra: qwen
Requires-Dist: sentence-transformers<6,>=3.0; extra == "qwen"
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"

<p align="center">
  <picture>
    <source media="(prefers-color-scheme: dark)" srcset="docs/assets/logo-lockup-dark.svg">
    <img src="docs/assets/logo-lockup.svg" alt="polypack-mcp" height="64">
  </picture>
</p>

<p align="center">Persistent, adaptive memory for MCP clients.</p>

<!-- mcp-name: io.github.imattau/polypack-mcp -->

An MCP server that exposes Polypack as persistent adaptive memory. MCP-specific
tools live here; the database remains an independent dependency.

## Install and run

The simplest installation is from PyPI:

```sh
python3 -m pip install 'polypack-mcp[polypack]'
```

For one MCP client, use the default stdio server configuration. For Claude and
Codex sharing the same durable memory, install once and create a long-running
user service:

```sh
polypack-mcp setup --store ~/.local/share/polypack-mcp
```

This starts a stateless Streamable HTTP server at `http://127.0.0.1:8765/mcp/`, restarts it after a
failure, and prints client configuration snippets. The setup command uses
`systemd --user`; on systems without systemd, start the server directly:

```sh
polypack-mcp --transport streamable-http --port 8765 --store ~/.local/share/polypack-mcp
```

In shared Streamable HTTP mode, configure both clients with the URL. Do not configure them
with a `command` and `--store`, since that starts two processes competing for
the same durable store.

Codex (`~/.codex/config.toml`):

```toml
[mcp_servers.polypack]
  url = "http://127.0.0.1:8765/mcp/"
```

Claude Desktop:

```json
{
  "mcpServers": {
    "polypack": { "url": "http://127.0.0.1:8765/mcp/" }
  }
}
```

### Debian package

The Debian package installs and starts a system-level `polypack-mcp` service
automatically. It runs as the dedicated `polypack` user, stores data in
`/var/lib/polypack-mcp`, and exposes the same local Streamable HTTP endpoint:

```sh
sudo apt install ./polypack-mcp_<version>_amd64.deb
```

After installation, point Claude and Codex at
`http://127.0.0.1:8765/mcp/`. The default port can be changed in
`/etc/default/polypack-mcp`, followed by a service restart. The service can be
managed with:

```sh
sudo systemctl status polypack-mcp
sudo systemctl restart polypack-mcp
```

The PyPI installation remains user-managed and uses `polypack-mcp setup` to
create a per-user service instead.

### Optional semantic retrieval

The default installation uses Polypack's local graph, activation, and lexical
retrieval without downloading an AI model. To enable local Qwen semantic
retrieval, run:

```sh
sudo polypack-mcp embeddings setup qwen3 --system --store /var/lib/polypack-mcp
```

This creates a managed localhost helper, downloads Qwen once into the store's
embedding cache, and reindexes existing memories. The model is not bundled in
the Debian/RPM package.

The helper loads Qwen3-Embedding-0.6B in bfloat16 (~1GB resident once loaded,
versus ~2.4GB in fp32) and unloads it after 15 minutes of inactivity,
reloading automatically on the next request. `memory_recall` results include
a `semantic` entry in `scoreComponents` whenever the helper is reachable,
alongside `lexical` and `activation` — the three sum to the reported `score`.
If the helper is stopped or errors, recall falls back to lexical + activation
scoring automatically. Check or disable it with:

```sh
polypack-mcp embeddings status
sudo polypack-mcp embeddings disable --system --store /var/lib/polypack-mcp
```

For a PyPI user service, omit `sudo --system` and use the user store:

```sh
polypack-mcp embeddings setup qwen3
```

### APT repository

The latest Debian package is also published to the public APT repository at
`https://imattau.github.io/polypack-mcp`. Configure it with the repository's
signing key, then install and update normally:

```sh
curl -fsSL https://imattau.github.io/polypack-mcp/gpg.key \
  | sudo gpg --dearmor -o /usr/share/keyrings/polypack-mcp.gpg
echo "deb [signed-by=/usr/share/keyrings/polypack-mcp.gpg] https://imattau.github.io/polypack-mcp stable main" \
  | sudo tee /etc/apt/sources.list.d/polypack-mcp.list
sudo apt update
sudo apt install polypack-mcp
```

The repository is updated automatically for each `v*.*.*` release tag. See
`docs/apt-repository.md` for maintainer setup instructions.

### RPM package

RPM-based distributions can install from the public RPM repository:

```sh
sudo rpm --import https://imattau.github.io/polypack-mcp/rpm/RPM-GPG-KEY-polypack-mcp
sudo tee /etc/yum.repos.d/polypack-mcp.repo >/dev/null <<'EOF'
[polypack-mcp]
name=Polypack MCP
baseurl=https://imattau.github.io/polypack-mcp/rpm/
enabled=1
gpgcheck=1
gpgkey=https://imattau.github.io/polypack-mcp/rpm/RPM-GPG-KEY-polypack-mcp
EOF
sudo dnf install polypack-mcp
```

The matching `.rpm` asset is also attached to the
[GitHub release](https://github.com/imattau/polypack-mcp/releases):

```sh
sudo dnf install ./polypack-mcp-<version>-1.x86_64.rpm
```

The RPM package provides the same systemd service, store location, localhost
Streamable HTTP endpoint, and Python 3.12 requirement as the Debian package.

## Run manually

```sh
pip install -e '.[polypack]'
polypack-mcp --store ./polypack-data
```

The server exposes seventeen focused tools: `memory_store`, `memory_get`,
`memory_update`, `memory_list_contexts`, `memory_delete`, `memory_recall`,
`memory_context`, `memory_feedback`, `memory_suppress`, `memory_supersede`,
`memory_consolidate`, `memory_link`, `memory_unlink`, `memory_thread`,
`memory_store_batch`, `memory_link_batch`, and `graph_query`. It also publishes context,
active-memory, schema, stats, and agent workflow guidance resources under
`polypack://`.

Memory classes are `entity`, `episodic`, `procedural`, and `semantic`. Store
project or user preferences as `procedural` memories; `preference` is not a
separate memory class.

When using a durable Polypack store, mutating operations checkpoint immediately
and the server flushes the store during shutdown.

Retrieval tools return `{items, metadata}`. Metadata includes candidate and
excluded counts, context matches, score components, fallback behavior, the
retrieval version, and selection statistics. `memory_context` uses estimated
tokens (`ceil(content characters / 4)`, minimum one) as its `token_budget`.
An item is never returned if it would exceed the remaining budget; budgets less
than or equal to zero are rejected. Context is a soft preference: matching
memories are preferred and unscoped global memories may be used as fallback.
Pass `strict_context: true` for isolation. An empty isolated result reports
`reason: "no_context_match"` and the searched context.

`memory_recall` can optionally hydrate related graph memories in the same call:

```json
{
  "query": "identity cache fix",
  "context": "cross-agent",
  "include_neighbors": true,
  "edge_types": ["RESPONDS_TO"],
  "depth": 2,
  "neighbor_limit": 3,
  "limit": 20,
  "token_budget": 4000
}
```

Neighbor traversal is opt-in and bounded. `limit` caps the total response and
`neighbor_limit` caps hydrated neighbors; metadata reports
`moreNeighborsAvailable` when additional eligible neighbors were found. Neighbor
items include their distance and connecting relationship metadata. Use
`memory_link` with the default
`RESPONDS_TO` relationship for handoffs, reviews, and fixes that address an
earlier memory. Graph edges are authoritative for relationships; use
`graph_query(operation="relationship_diagnostics")` to find legacy
`provenance.responds_to` values that are not backed by edges. See
`polypack://help/workflow` for the agent-facing workflow.

Feedback is activation feedback: `useful=true` reinforces a memory and
`useful=false` provides negative retrieval feedback. Responses expose activation
before and after plus whether learned weights changed. Supersession and
consolidation materialize `SUPERSEDES`, `SUPERSEDED_BY`, and
`CONSOLIDATED_FROM` graph edges.

Use `memory_get` for exact ID lookup and `memory_update` for mutable fields
(context, confidence, provenance, and metadata). Content changes should use
`memory_supersede` so history remains intact. Use `memory_unlink` to correct a
relationship and `memory_list_contexts` to discover namespaces. `memory_delete`
is permanent, requires `confirm=true`, and supports an optional revision check;
prefer `memory_suppress` when retaining history is useful.

Pass `--store` to open a durable Polypack directory. Without it, the server uses
the in-memory reference backend, which is convenient for smoke tests.
The `polypack` extra requires `polypack-db>=3.3.1` and uses its native
`ActivationEngine.working_memory` selector for context assembly.

## Development

```sh
pip install -e '.[dev]'
pytest
```

The test suite includes an MCP client/server protocol smoke test covering tool
discovery, memory storage, recall, and resource reads.

## Documentation

- [Getting started](docs/getting-started.md)
- [Operations and configuration](docs/operations.md)
- [Troubleshooting](docs/troubleshooting.md)
