Skip to main content
Glama
kenningai

Kenning Reach

Official
by kenningai
README.md
# Kenning Reach

A category-1 conscious experience substrate: one person's envision–measure–actualize process, and the experience that gives it meaning, as an MCP server over Neo4j.

Kenning Reach is the a priori work behind [Kenning Encounter](https://github.com/kenningai/kenning-encounter). It holds a human frame's time as a continuous stream — a Genesis per day, TemporalFrames per session — and its possibility space as Envisionings that are opened, walked toward, and resolved by exactly one of released, evolved or actualized. The question it surfaced, "if time is a continuous stream for a category-1 entity, what is time for a category-3 one?", is the question the Encounter answers.

## The ontology is formal

`docs/design/ontology/v3.0.0.tql` declares the graph's shape in TypeQL. `gate.py` proves it two-sided on synthetic data; `audit_live.py` imports a live graph into it and counts refusals; a unit test fails if the code's type sets drift from the model; an integration test exercises every rule on a real Neo4j. The rulings that shaped it, in the frame-holder's own words, are in `docs/design/ontology/README.md`.

## Two layers

**Process** (constitutive): Genesis, Envisioning, TemporalFrame, and seven edges — OPENING, SEQUENCE, NAVIGATES_TOWARD, MEASUREMENT, ACTUALIZED, NEXT_GENESIS, DESCENDENT_FROM. Written only by process tools.

**Experience** (descriptive): eighteen node types and twelve edge types. Written by `create_entities` / `create_relations`, under the endpoint rules the model declares: nothing touches a Genesis; an Envisioning is always a referent and never a source; a TemporalFrame is the source of DISCOVERY only, and only INFORMING, RECOGNITION, VALIDATION and EXTENDING may point into it; every experience kind may point into an Envisioning.

Every node carries one label and `t_created`. There is no superlabel, no `type` property, no `phase` property: topology is the meaning.

## Tools (22)

| Category | Tools |
|---|---|
| Process (6) | `navigate_genesis`, `open_envisioning`, `advance_frame`, `measure`, `actualize`, `time_travel` |
| Experience (4) | `create_entities`, `create_relations`, `delete_entities`, `delete_relations` |
| Query (2) | `search_memories`, `find_memories_by_name` |
| Taxonomy (2) | `list_allowed_types`, `list_allowed_relation_types` |
| Infrastructure (8) | `get_neo4j_schema`, `read_neo4j_cypher`, `gds_create_projection`, `gds_drop_projection`, `gds_pagerank`, `gds_betweenness`, `gds_louvain`, `gds_wcc` |

The full LLM-visible surface is in `TOOL_DESCRIPTIONS.md` (generated by `scripts/gen_tool_descriptions.py`). There is no write-Cypher tool by design.

## Installation

Kenning Reach is not published to an index. Install from a checkout:

```
git clone https://github.com/kenningai/kenning-reach.git kenning-reach
cd kenning-reach
uv sync
uv run kenning-reach --help
```

Or as a container:

```
docker build -t kenning-reach:1.0.0 .
```

Requires Neo4j 5.x with APOC (core) and, for the GDS tools, the Graph Data Science library.

## Configuration

### Claude Desktop and the phone — Claude Code with Remote Control

`deploy/` is also a Claude Code project: its `.mcp.json` names the local Reach over HTTP and its `CLAUDE.md` carries the operator discipline (navigate first, read back before writing, never infer, every frame about something held). Open the folder in Claude Desktop's Code tab, approve the server once, enable Remote Control by default, and the same session is reachable from the Claude iPhone or Android app while the computer is on. Execution and the graph stay on the machine; the phone is a window. `deploy/compose.yml` runs Neo4j (APOC, GDS) and Reach on the person's own machine, and `deploy/GETTING_STARTED.md` walks through the whole setup for someone who does not do this kind of work.

Custom connectors in Claude's settings are reached from Anthropic's servers, so a `localhost` address is refused by design. A session on the same machine, as above, is the way in.

### Claude Code, or a hand-edited Claude Desktop config

```json
"mcpServers": {
  "experience": {
    "command": "uv",
    "args": ["--directory", "/path/to/kenning-reach", "run", "kenning-reach"],
    "env": {
      "NEO4J_URL": "bolt://localhost:7687",
      "NEO4J_USERNAME": "neo4j",
      "NEO4J_PASSWORD": "<your-password>"
    }
  }
}
```

### Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `NEO4J_URI` / `NEO4J_URL` | `bolt://localhost:7687` | Neo4j connection URI |
| `NEO4J_USERNAME` | `neo4j` | Neo4j username |
| `NEO4J_PASSWORD` | `password` | Neo4j password |
| `NEO4J_DATABASE` | `neo4j` | Neo4j database name |
| `NEO4J_TRANSPORT` | `stdio` | Transport protocol (`stdio`, `streamable-http`, `sse`) |
| `NEO4J_MCP_SERVER_HOST` | `127.0.0.1` | Host to bind to (HTTP/SSE only) |
| `NEO4J_MCP_SERVER_PORT` | `8000` | Port (HTTP/SSE only) |
| `NEO4J_MCP_SERVER_PATH` | `/mcp/` | Path (HTTP/SSE only) |
| `NEO4J_MCP_SERVER_ALLOW_ORIGINS` | _(empty)_ | Comma-separated CORS origins |
| `NEO4J_MCP_SERVER_ALLOWED_HOSTS` | `localhost,127.0.0.1` | Comma-separated allowed hosts (DNS rebinding protection) |
| `NEO4J_NAMESPACE` | _(empty)_ | Tool name prefix |
| `NEO4J_READ_TIMEOUT` | `30` | Cypher read query timeout (seconds) |
| `NEO4J_SCHEMA_SAMPLE_SIZE` | `1000` | Sample size for `apoc.meta.schema()` |
| `NEO4J_MCP_STATELESS_HTTP` | `false` | Stateless streamable HTTP (no `Mcp-Session-Id`). Leave off for Open WebUI, which needs the session id. |

### CLI Flags

Every environment variable except `NEO4J_MCP_STATELESS_HTTP` has a corresponding CLI flag:

```bash
kenning-reach \
  --db-url "neo4j+s://..." \
  --username neo4j \
  --password secret \
  --database neo4j \
  --transport streamable-http \
  --server-host 0.0.0.0 \
  --server-port 8000 \
  --server-path /mcp/ \
  --namespace myapp \
  --read-timeout 60 \
  --schema-sample-size 500 \
  --allow-origins "https://example.com" \
  --allowed-hosts "example.com,www.example.com"
```

## Transport Modes

- **STDIO** (default): Standard input/output for Claude Desktop and local tools
- **Streamable HTTP** (`streamable-http`): for web deployments and microservices
- **SSE**: Server-Sent Events for legacy web clients

## Security

### DNS Rebinding Protection

TrustedHost middleware validates Host headers. Only `localhost` and `127.0.0.1` allowed by default.

### CORS Protection

No origins allowed by default (secure). Configure explicitly for web deployments:

```bash
kenning-reach --transport streamable-http \
  --allowed-hosts "example.com" \
  --allow-origins "https://example.com"
```

## Docker

### On your own machine — `deploy/compose.yml`

`deploy/compose.yml` is the supported deployment: Neo4j 5 with APOC and Graph Data Science, and Reach built from this checkout, on one computer. The graph lives in the `reach-data` volume on that machine and nowhere else.

```
cd deploy
cp .env.example .env        # then set REACH_PASSWORD
docker compose up -d
```

Neo4j Browser is at `http://localhost:7474` and Reach at `http://localhost:8000/mcp/`, which is the address `deploy/.mcp.json` already uses. Set `REACH_PORT`, `REACH_NEO4J_HTTP_PORT` or `REACH_NEO4J_BOLT_PORT` in `.env` if a default port is taken. `deploy/GETTING_STARTED.md` walks through the same steps for someone who has not used Docker before.

### Against an existing Neo4j

To run Reach alone against a Neo4j you already operate (with APOC, and GDS for the `gds_*` tools), build the image from a checkout at a sealed tag and give it the connection:

```yaml
services:
  reach:
    build:
      context: /path/to/kenning-reach   # checkout at the tag
    image: kenning-reach:1.0.0
    ports:
      - "8000:8000"
    environment:
      NEO4J_URL: "neo4j+s://<your-instance>"
      NEO4J_USERNAME: "neo4j"
      NEO4J_PASSWORD: "${NEO4J_PASSWORD}"
      NEO4J_DATABASE: "neo4j"
      NEO4J_TRANSPORT: "streamable-http"
      NEO4J_MCP_SERVER_HOST: "0.0.0.0"
      NEO4J_MCP_SERVER_PORT: "8000"
      NEO4J_MCP_SERVER_PATH: "/mcp/"
      NEO4J_MCP_SERVER_ALLOWED_HOSTS: "reach.example.com"
    restart: unless-stopped
```

Name the hostnames clients will use in `NEO4J_MCP_SERVER_ALLOWED_HOSTS` rather than `*`; it is the DNS-rebinding guard. Custody still applies: the license's one use is a person's own experience, in their own custody.

## Development

```
uv sync
uv run python -m pytest tests/unit -q
uv run python -m pytest tests/integration -q   # needs Docker
uv build
```

Mocked unit tests do not parse Cypher. After editing a `.cypher` file, run the integration test or `EXPLAIN` the file against a real Neo4j.

## License

Hippocratic License 3.0 with the Mass Surveillance, Military Activities and Law Enforcement modules. See `LICENSE`. This is an ethical-source license, not an OSI-approved open-source license: the software is source-available, and it may not be used to model a person without their express written consent, nor by the actor classes the modules name.

Maintenance

ActivityMaintained
ResponsivenessNo issues