Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Keep the build context lean. The multi-stage Dockerfile only needs
# package*.json, tsconfig.json, src/, schema/, prompts/, and scripts/.

node_modules
dist
coverage

# Local state and secrets — never bake into the image
.env
.env.*
!.env.example
.mcp.json
.claude
.git
.github

# Docs and internal — not needed at runtime
docs
*.md
!README.md
!CLAUDE.md
!GRAPH_SCHEMA.md

# Local backup / data dirs (in case anyone keeps one inside the repo)
backups
graph-memory

# Test output
test-results

# Editor / OS
.DS_Store
Thumbs.db
.vscode
.idea
41 changes: 41 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Release

# Publishes the graph-memory-mcp container image to GHCR on every v* tag push,
# tagged with both the version and `latest`. The image is what the
# install-primary.sh script pulls — no clone or local build required for users.

on:
push:
tags:
- 'v*'

permissions:
contents: read
packages: write

jobs:
publish-image:
name: Publish container image
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5

- uses: docker/setup-buildx-action@v3

- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: |
ghcr.io/stevepridemore/graph-memory-mcp:${{ github.ref_name }}
ghcr.io/stevepridemore/graph-memory-mcp:latest
cache-from: type=gha
cache-to: type=gha,mode=max
16 changes: 7 additions & 9 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,13 @@ You MAY write to the graph during conversation for **high-confidence, explicit**
| Mentioned in context but not stated directly | **don't write** — let dream handle at 0.3 |

### Use specific relationship types — not just RELATED_TO
| Relationship | When to use |
|-------------|-------------|
| WORKS_ON | Person → Project |
| USES / USES_TECH | Person/Project → Technology |
| KNOWS_ABOUT | Person → Concept/Technology |
| PREFERS | Person → Preference |
| DECIDED_FOR | Person/Project → Decision |
| PARTICIPATED_IN | Person → Event |
| RELATED_TO | Only when nothing else fits |

The full vocabulary (node types and edge verbs) lives in
[`GRAPH_SCHEMA.md`](GRAPH_SCHEMA.md) at the project root. Read that
file before writing edges so you pick a specific verb (`ABOUT`,
`PART_OF`, `IMPLEMENTS`, `INSPIRED_BY`, `DEPENDS_ON`, etc.) over
generic `RELATED_TO`. `RELATED_TO` is the fallback only — use it when
no specific verb fits.

### Always include provenance
- `source_type`: `"conversation"`
Expand Down
34 changes: 30 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
# ---- Build stage: compile TypeScript ---------------------------------------
FROM node:22-bookworm-slim AS build

WORKDIR /build

COPY package*.json tsconfig.json ./
RUN npm ci

COPY src/ ./src/
RUN npm run build

# ---- Runtime stage ---------------------------------------------------------
FROM node:22-bookworm-slim

# Debian-slim is required (not alpine) because onnxruntime-node — used by
Expand All @@ -6,19 +18,32 @@ FROM node:22-bookworm-slim

WORKDIR /app

# wget for healthcheck, ca-certificates for HTTPS model download from huggingface.co
# wget for healthcheck, ca-certificates for HTTPS model download from huggingface.co,
# python3 for the prompt/skill path-substitution helper that the install script runs,
# openssl for self-signed TLS cert generation on first run (see docker/entrypoint.sh).
RUN apt-get update && apt-get install -y --no-install-recommends \
wget ca-certificates \
wget ca-certificates python3 openssl \
&& rm -rf /var/lib/apt/lists/*

# Production dependencies only
COPY package*.json ./
RUN npm ci --omit=dev && npm cache clean --force

# Pre-built JavaScript (run `npm run build` before `docker compose build`)
COPY dist/ ./dist/
# Compiled JavaScript from the build stage
COPY --from=build /build/dist ./dist
COPY schema/ ./schema/

# Canonical prompts and helper scripts. The entrypoint copies prompts/ out to
# the host-mounted /root/graph-memory/prompts/ on first start so the user's
# scheduled tasks can read them from a stable host path.
COPY prompts/ ./prompts/
COPY scripts/sync-dream-skill.py ./scripts/sync-dream-skill.py

# Entrypoint shim: seeds the host data dir on first run, then exec's the
# server. Always runs (idempotent — no-op if the target already exists).
COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh

ENV MCP_TRANSPORT=http
ENV MCP_PORT=3847

Expand All @@ -27,4 +52,5 @@ EXPOSE 3847
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s \
CMD wget -qO- --no-check-certificate https://127.0.0.1:3847/health || exit 1

ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD ["node", "dist/mcp-server/index.js"]
202 changes: 202 additions & 0 deletions GRAPH_SCHEMA.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
# Graph Schema (vocabulary)

Concise reference for the node types and edge verbs used in the memory
graph. Optimized for the agent (Claude) to scan when writing edges, and
for humans skimming the project.

For the full reference (weight math, decay functions, validity windows,
init Cypher, example queries) see
[`docs/GRAPH_SCHEMA_REFERENCE.md`](docs/GRAPH_SCHEMA_REFERENCE.md).

## Conventions

- Every node carries the `:Entity` label plus its specific type label.
- Edges are directed in storage; symmetric edges are stored once and
queried without an arrow (see "Directionality" below).
- Prefer a specific verb over `RELATED_TO`. `RELATED_TO` is the
fallback for "connected somehow but nothing else fits."
- All nodes share these common properties: `id`, `name`, `subtype`,
`confidence` (0.0–1.0), `times_mentioned`, `first_seen`, `last_seen`,
`tenant_id`, optionally `embedding` (384-dim).

## Status legend

- ✅ **In use** — appears in the current graph and is documented here.
- 🆕 **Proposed** — added during the 2026-05-10 vocabulary expansion.
Dream + in-conversation writes should start using these; a future
retyping campaign will backfill existing `RELATED_TO` edges.
- 🤔 **Consolidation candidate** — overlaps with another type and may
be merged in a future cleanup. Use the canonical sibling unless the
distinction is meaningful for your case.

## Node types

| Type | Use when | Notes |
|---|---|---|
| `Person` ✅ | A human (user, colleague, family, contact) | Subtypes: `individual`, `contact`, `group` |
| `Organization` ✅ | A company, agency, team, or institution | E.g. FBBE, Anthropic |
| `Project` ✅ | A bounded body of work, initiative, or codebase | Subtypes: `active`, `paused`, `completed`, `abandoned` |
| `Feature` ✅ 🤔 | A sub-component of a Project | Overlaps with `Project`; use only when the feature has its own lifecycle worth tracking separately. Otherwise prefer Project + describe the feature in properties. |
| `Concept` ✅ | An abstract idea, pattern, framework, or technology category | The fallback for "named thing that isn't an instance" |
| `Technology` ✅ 🤔 | A specific tool, language, library, or platform | Overlaps with `Concept`. Currently a categorized Concept; the live graph uses both. Convention going forward: prefer `Technology` for concrete tech (React, Neo4j); `Concept` for abstractions (LLM-wiki-pattern, MVC). |
| `Decision` ✅ | A choice made or position taken | Often emitted by the dream extractor from "we decided…" / "I chose…" statements. |
| `Reasoning` ✅ | The why behind a Decision | Pairs with Decision via `LED_TO`; lighter than Decision itself. |
| `Preference` ✅ | A stated user preference or rule | Person `PREFERS` Preference. Has `domain`, `key`, `value` properties. |
| `Event` ✅ | A point-in-time happening | Meeting, milestone, release, incident |
| `Fact` ✅ | A standalone piece of knowledge | Description-heavy. Best paired with `ABOUT` to whatever it's a fact *about*. |
| `Artifact` ✅ | A created/authored output (doc, file, transcript, gist) | Subtype of Object — distinct because authorship matters. Pair with `AUTHORED` / `PRODUCED`. |
| `Object` ✅ 🤔 | A "thing in the world" that isn't covered by a more specific type | Heavy overlap with `Resource` and `Infrastructure`. The live graph leans on this as a catch-all; consider whether your case is really `Resource`, `Infrastructure`, or `Artifact` first. |
| `Resource` ✅ 🤔 | A consumable or referenceable thing | Overlaps with `Object`. Currently rare in graph (1 node). Candidate for merge into `Object` unless it earns its keep. |
| `Infrastructure` ✅ 🤔 | A server, host, network device, deployment target | Overlaps with `Object`. Currently rare (1 node). Candidate for merge into `Object` with `subtype: infrastructure`. |
| `Alias` ✅ | An alternate name pointing at a canonical entity | Used by alias resolution; rarely created directly. |

**Consolidation summary:** `Object` / `Resource` / `Infrastructure`
overlap heavily — the latter two have only 1–2 nodes each. A future
cleanup can collapse them into `Object` with subtypes. `Concept` /
`Technology` is a softer overlap; the rule above (concrete = Technology,
abstract = Concept) keeps both useful.

## Edge verbs

Verbs are grouped by purpose. Direction notation: `A → B` means the
edge points from A to B.

### People & roles

| Verb | Direction | Use when |
|---|---|---|
| `WORKS_ON` ✅ | Person → Project | Person actively contributes to a project |
| `WORKS_AT` ✅ | Person → Organization | Employment |
| `REPORTS_TO` ✅ | Person → Person | Org-chart reporting line |
| `STAKEHOLDER_IN` ✅ | Person → Project/Decision | Has interest but isn't owner |
| `KNOWS` 🆕 | Person ↔ Person | General acquaintance (symmetric) |
| `COLLABORATES_WITH` 🆕 | Person ↔ Person | Active working relationship (symmetric) |
| `FAMILY_OF` 🆕 | Person ↔ Person | Family tie; use a `role` property (`spouse`, `sibling`, `parent`) |
| `MENTOR_OF` 🆕 | Person → Person | Mentorship/teaching |

### Knowledge, preferences, decisions

| Verb | Direction | Use when |
|---|---|---|
| `KNOWS_ABOUT` ✅ | Person → Concept/Technology | Subject-matter familiarity |
| `PREFERS` ✅ | Person → Preference | Stated preference |
| `DECIDED_FOR` ✅ | Person/Project → Decision | Owns or made a decision |
| `LED_TO` ✅ | Event/Decision → Outcome | Causal arrow |
| `INVOLVED_IN` ✅ | Person → Event/Project | Lighter than `PARTICIPATED_IN` |

### Tech & dependencies

| Verb | Direction | Use when |
|---|---|---|
| `USES` ✅ | * → Tool/Object | General usage |
| `USES_TECH` ✅ | Project → Technology | Specifically a technology dependency |
| `DEPENDS_ON` ✅ | * → * | Functional dependency |
| `IMPLEMENTS` 🆕 | Project/Class → Concept/Interface | Concrete realization of a pattern; in code, "class implements interface" |
| `EXTENDS` 🆕 | Class → Class | Code-level class inheritance (Java `extends`) |
| `INSPIRED_BY` 🆕 | Project → Concept/Project | Origin/influence without inheritance |
| `BUILDS_ON` 🆕 | * → * | Direct extension at the idea level |
| `DERIVED_FROM` 🆕 | * → * | Descended from another (forks, extracted concepts) |

### Composition & taxonomy

| Verb | Direction | Use when |
|---|---|---|
| `CONTAINS` 🆕 | * → * | Composition (repo contains file; project contains feature) |
| `PART_OF` 🆕 | * → * | Inverse of `CONTAINS`; pick one direction per fact, don't store both |
| `INSTANCE_OF` 🆕 | Object → Concept | Concrete thing of an abstract category |
| `CATEGORIZED_AS` 🆕 | * → Concept | Lighter classification when `INSTANCE_OF` is too strong |

### Reference & description (covers the biggest current `RELATED_TO` bucket)

| Verb | Direction | Use when |
|---|---|---|
| `ABOUT` 🆕 | Fact/Artifact → * | The fact or document is *about* its subject |
| `DESCRIBES` 🆕 | Artifact → * | Artifact describes its subject (stronger authoring intent than `ABOUT`) |
| `DOCUMENTS` 🆕 | Artifact → Project/Object | Specifically reference documentation |
| `ATTRIBUTED_TO` 🆕 | Fact/Quote → Person | Source of a statement or observation |

### Authorship, production, governance

| Verb | Direction | Use when |
|---|---|---|
| `PRODUCED` ✅ | Person/Project → Artifact/Event | Authorship of an output |
| `AUTHORED` 🆕 | Person → Artifact | Wrote/created a doc, post, or code |
| `CREATED` 🆕 | Person → Project/Object | Brought something into existence (broader than `AUTHORED`) |
| `AFFECTS` 🆕 | Decision → Object/Project | A decision applies to or constrains a target |
| `GOVERNS` 🆕 | Decision/Policy → * | Stronger than `AFFECTS` — the target is *bound by* the decision |

### Lifecycle & ordering

| Verb | Direction | Use when |
|---|---|---|
| `SUPERSEDES` ✅ | * → * | Bi-temporal replacement (with `valid_at`) |
| `REPLACES` 🆕 | Object → Object | Successor; less formal than `SUPERSEDES` (no bi-temporal contract) |
| `DEPRECATED_BY` 🆕 | Object → Object | Inverse of `REPLACES`; the source is on its way out |
| `BLOCKS` 🆕 | Issue/Task → Issue/Task | Forward dependency |
| `BLOCKED_BY` 🆕 | Issue/Task → Issue/Task | Inverse of `BLOCKS` |
| `RESOLVED_BY` 🆕 | Issue/Fact → Decision/Event | Closes a contradiction or open question |

### Place & runtime

| Verb | Direction | Use when |
|---|---|---|
| `LOCATED_IN` 🆕 | Person/Object → Place | Geographic placement |
| `DEPLOYED_TO` 🆕 | Project → Infrastructure/Place | Runtime deployment target |

### Events & temporal

| Verb | Direction | Use when |
|---|---|---|
| `PARTICIPATED_IN` ✅ | Person → Event/Organization | Membership / past involvement |
| `OCCURRED_DURING` ✅ | Event → Event/Time | Temporal containment |
| `TRIGGERED_BY` ✅ | Event/Decision → Event/Cause | What caused this |

### Identity & contradiction

| Verb | Direction | Use when |
|---|---|---|
| `ALIAS_OF` ✅ | * ↔ * | Same thing, different name (kept un-merged); symmetric |
| `CONTRADICTS` ✅ | * ↔ * | Mutually exclusive facts surfaced for human review |
| `RELATED_TO` ✅ | * → * | **Fallback only** — use a specific verb if one fits |

## Directionality

Neo4j stores every relationship with a direction, but you can query
without one. Three patterns:

1. **Symmetric verbs** (`KNOWS`, `COLLABORATES_WITH`, `FAMILY_OF`,
`ALIAS_OF`, `CONTRADICTS`, `RELATED_TO`): store one edge, query
without an arrow (`MATCH (a)-[r:KNOWS]-(b)`). Direction in storage
is meaningless.
2. **Inverse-pair verbs** (`CONTAINS`/`PART_OF`, `BLOCKS`/`BLOCKED_BY`,
`REPLACES`/`DEPRECATED_BY`): only store one direction per fact —
pick the canonical (typically active voice → forward arrow) and let
queries follow either.
3. **Asymmetric verbs** (`AUTHORED`, `EXTENDS`, `INSPIRED_BY`,
`WORKS_ON`, etc.): direction is meaningful and unique. Only one edge.

Never store both directions of the same fact — it doubles storage and
the weights drift out of sync over time.

## When to write to the graph during conversation

See the rules in [`CLAUDE.md`](CLAUDE.md). Short version:

- Write only for **high-confidence, explicit** statements (weight 0.7).
- Always include provenance: `source_type: "conversation"` and the
current `source_session` if available.
- Never write secrets (API keys, passwords, tokens). Note existence
only.
- Defer ambiguous or inferred context to the dream process at weight
0.3.

## Adding a new verb or node type

1. Decide whether it's rare enough to warrant `RELATED_TO` (or
`Object`), or whether it deserves a name.
2. If it deserves a name, add it here with status 🆕, direction, and a
one-sentence "use when."
3. Update `~/.claude/GRAPH_SCHEMA.md` if the new type is genuinely
universal (applies in any project), not just this one.
4. After it sees real use across multiple sessions, change status to ✅.

Loading