# Project export: Scope

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: TreeHacks 2026
- Tagline: Unified knowledge store for teams and their AI tools
- Devpost: https://devpost.com/software/scope-2l7yub
- GitHub: https://github.com/batyr0601/treehacks26
- Demo: https://www.allthescope.com/
- Video: https://www.youtube.com/embed/u7FCFx8A5yo?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 5 GitHub contributor(s) — ZealSV (23 commits), Tony Li (9 commits), Timur Takhtarov (4 commits), Batyr Zhangabylov (3 commits), Claude Opus 4.6 (1 commits)

## Devpost submission (written by the team)

### Inspiration

Every team building with AI hits the same wall: your AI tools are smart in isolation, but completely amnesiac across sessions and teammates. One engineer decides to go API-first in a Cursor chat, another picks a database in Claude, and a third makes a design call over Slack, and none of those decisions are accessible to anyone else's AI. The result is constant re-explaining, duplicated research, and contradictory choices that only surface during code review or a frustrated standup. We realized the bottleneck isn't intelligence, it's information. Modern AI agents lack a shared, persistent memory, and that gap gets worse the more agents and people you add. We wanted to build the connective tissue that lets an entire org's AI tools reason from the same evolving context.

### What it does

Scope is a shared knowledge layer for teams using AI. You save decisions, context, and knowledge once, and any AI tool, Cursor, Claude Code, Poke, or custom agents can pull it back instantly with the right permissions. Memories are organized into workspaces with role-based access control, so managers control what's visible and to whom. Under the hood, Scope models memory as a knowledge graph: each memory is a node, and relationships, relevance scores, and temporal metadata are stored as edges. When a query comes in, vector search identifies entry points, then an LLM-guided traversal branches across the graph to pull in only the context that actually matters for that task, not just keyword matches, but semantically connected decisions and history. Scope tracks what changed and who's seen it, so teammates and agents always know what's new. It exposes everything as both a REST API and an MCP server, meaning any MCP-compatible tool can store and search team memory out of the box. We also built a CLI (scope init) that auto-configures Claude Code hooks so memories sync without any manual effort, engineers just code, and Scope keeps the org in sync behind the scenes.

### How we built it

The backend is FastAPI with PostgreSQL for structured data and Neo4j + Graphiti for the knowledge graph layer. Graphiti gives us hybrid retrieval, vector similarity, BM25 keyword search, and graph traversal in one pipeline, powered by OpenAI embeddings. Auth uses bearer tokens with SHA-256 hashed API keys and full workspace-scoped ACLs. The frontend is Next.js 15 / React 19 with Shadcn UI, Zustand for state, and TanStack Query for data fetching, which lets you register, manage workspaces, search memories, and invite teammates from the browser. We also used Reagraph for graph visualization for memories. We built two MCP servers: one general-purpose server with 10 tools for Cursor and Claude Desktop (search, store, list workspaces), and a Poke-specific server with 15 tools that add team messaging, meeting scheduling suggestions, and daily summary generation. We also wrote a Python SDK and a CLI tool that configures Claude Code hooks to auto-push local AI memories to the shared workspace on every relevant file write.

### Challenges we ran into

The hardest problem was bridging local and shared memory. Claude Code writes memories to local markdown files, getting those to sync upstream without duplicating, losing metadata, or breaking the user's workflow required building a diffing mechanism, a sync state tracker, and hooks that fire only on relevant file changes. Getting the granularity right (when is a memory "new" vs. "updated"?) was surprisingly tricky. Graphiti's search returns knowledge graph edges (extracted facts), not our original Memory records, so we had to work around a join gap where search results lost tags, scope, creator info, and timestamps. Reconciling the graph layer with the relational layer without sacrificing query speed was a constant tension. We also wrestled with per-user visibility of changes. "What's new" is relative, what's new to Alice isn't new to Bob, so we needed per-user read tracking across shared workspaces, which added real complexity to the access log and notification system.

### Accomplishments we're proud of

We shipped a full product loop in a hackathon: API, SDK, CLI, two MCP servers, a frontend dashboard, and a working Poke integration. A developer can run scope init in any project, and from that point on, every AI tool they use shares the same memory, no config, no copy-pasting, no standups to re-sync. We're proud of the graph-based retrieval. It's not just semantic search, it's traversal. When you ask "why did we choose Postgres?", Scope doesn't just find that memory; it walks the graph to surface the performance benchmarks, the alternative options that were rejected, and the teammate who made the call. That kind of contextual depth is what separates a real memory system from a vector store with a search bar. We also got the Poke MCP integration working end-to-end: Poke users can ask conversational questions ("what did the team decide about the launch?"), Get daily "spokes" summarizing what everyone accomplished, and even have Poke suggest when a meeting is actually necessary based on disagreements or major code changes it detects in the shared memory.

### What we learned

We learned that memory is not storage, it's structure. A flat vector database gives you recall, but teams need hierarchy (workspaces, permissions), temporality (what changed and when), and relationships (why decisions connect). Building that graph layer was the difference between a demo and something teams would actually trust. We also learned how powerful MCP as a distribution layer is. Instead of building integrations one-by-one, exposing Scope as an MCP server meant that Cursor, Claude Code, Poke, and any future MCP client could plug in immediately. The protocol did the distribution work for us. Finally, we learned that the real unlock isn't giving AI more memory, it's giving teams more memory. The moment two people's AI tools share context, the compounding effect is immediate: less repeated work, faster onboarding, and decisions that actually stick.

### What's next

Short term: production deployment with proper auth (OAuth, not just API keys), a hosted version so teams don't need to self-host, and a polished scope watch daemon that continuously syncs in the background instead of relying on hooks. Medium term: expand integrations beyond dev tools, connect to Slack, Notion, and Google Docs so memories flow in from where decisions actually happen, not just where code gets written. We also want to build automatic memory agents that scan for stale or conflicting memories and suggest merges or pruning. Long term: We want Scope to be the default memory layer for every AI agent in an organization. Not just dev teams, sales, ops, product, support. Any team running parallel AI workflows hits the same information gap. Our vision is that every AI tool an org uses reads and writes to Scope, so the entire company's AI operates with one shared, evolving understanding. The era of containerized conversations is over.

## README (from the GitHub repository)

# Recall

**A shared memory layer for teams that use AI at work.**

Right now, important context ends up scattered across Slack, docs, and random AI chats — so people keep re-explaining the same background and redoing the same research. Recall is a place to **save key decisions and context once**, and then your AI tools can **pull it back instantly** later — for you or your teammates — **with the right permissions**.

- **User-first**: Your API key is tied to you (register once). You can be in multiple workspaces (teams); add teammates by email.
- **Memories** can be tagged as `decision` or `context`; responses include **who can see what** (`visible_to_user_ids`).
- FastAPI backend, PostgreSQL + pgvector, Python SDK + MCP server for Cursor.

## Quick start

1. **Install** (use a venv so you don’t hit system Python):
   ```bash
   python3 -m venv .venv
   source .venv/bin/activate   # On Windows: .venv\Scripts\activate
   pip install -r requirements.txt
   # or: uv sync
   ```
   Use `pip install` (not `pip3`) after activating the venv so you use the venv’s Python.

2. **Postgres with pgvector**
   ```bash
   docker run --name recall-db -e POSTGRES_USER=recall -e POSTGRES_PASSWORD=recall \
     -e POSTGRES_DB=recall_dev -p 5433:5432 -d ankane/pgvector:latest
   ```

3. **Environment**
   ```bash
   cp env.example .env
   # Set OPENAI_API_KEY for embeddings; set RECALL_API_BASE_URL / RECALL_API_KEY if using MCP
   ```

4. **Run API**
   ```bash
   uvicorn api.main:app --reload
   ```

Or run everything with Docker:
```bash
docker compose up --build
```

## API keys (per user)

- **Register** to get a user and API key plus a default workspace:
  ```bash
  curl -X POST http://localhost:8000/v1/register \
    -H "Content-Type: application/json" \
    -d '{"name": "Alice", "email": "alice@example.com"}'
  ```
  Response includes `user_id`, `workspace_id`, and `api_key` — save the key; it’s only returned once.

- **Alternatively**, create a workspace without registering (anonymous). Note: memory store/query require a **user-linked** key (from register).

Use the key on all other requests:
```bash
curl -H "Authorization: Bearer YOUR_API_KEY" http://localhost:8000/v1/workspaces
```

## Smoke test

With the API running, run the smoke test harness (exits 0 only if all requests succeed):

```bash
./scripts/test_api.sh
```

See [TESTING.md](TESTING.md) for more.

## Main endpoints

- **Auth & users:** `POST /v1/register` — register user, get API key + default workspace (no auth)
- **Workspaces (teams):** `POST /v1/workspaces` — create workspace; `GET /v1/workspaces` — list **all workspaces you’re in** (when using a user-linked key)
- **Members:** `POST /v1/workspaces/{id}/members` — add teammate by `email` or `user_id`; `GET /v1/workspaces/{id}/members` — list members; `DELETE /v1/workspaces/{id}/members/{user_id}` — remove
- **Agents (metadata):** `POST /v1/workspaces/{id}/agents`, `GET /v1/workspaces/{id}/agents`
- **Memories:** `POST /v1/memories` — store (optional `memory_type`: `"decision"` | `"context"`); `POST /v1/memories/query` — semantic search; `GET /v1/memories/{id}` — get (responses include `visible_to_user_ids`)
- **API keys:** `POST /v1/workspaces/{id}/api-keys`, `GET`, `POST .../revoke`

## SDK

```python
from sdk.client import RecallClient

client = RecallClient(base_url="http://localhost:8000")

# Option A: register (per-user key)
data = client.register("Alice", email="alice@example.com")
# client now has api_key set

# Option B: create workspace
# data = client.create_workspace("demo")
# client.set_api_key(data["api_key"])

client.store_memory(
    workspace_id=data["workspace_id"],
    content="Found the artifact in sector 7",
    scope="private",
    tags=["artifact"],
)
# optional: agent_id="cursor" to record which tool created it
results = client.query_memories(
    workspace_id=data["workspace_id"],
    query="artifact",
)
print(results)
client.close()
```

## MCP (Cursor)

Set `RECALL_API_BASE_URL` and `RECALL_API_KEY` (e.g. from register or workspace creation), then point Cursor at the Recall MCP server. Tools: `recall_search_memories`, `recall_store_memory`, `recall_get_memory`, `recall_smart_search`, `recall_list_workspaces`, `recall_list_agents`, `recall_create_agent`.

## Layout

- `api/` — FastAPI app, routes, services, models
- `sdk/` — Python client
- `mcp_server/` — MCP tools for Cursor


## Detected evidence (automated analysis)

Indexed codebase: 162 recognized source files, 543 KB.
- CSS (language) — detected in the code
- FastAPI (technology) — detected in the code
- Next.js (technology) — detected in the code
- OpenAI (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- Anthropic (technology) — claimed on Devpost, not found in the code
- Docker (technology) — claimed on Devpost, not found in the code
- AI coding agent: Claude Code — evidence: config files committed to the repository; commit authorship or trailers

## Codebase structure (from repository index)

### Files (120 of 178)

```
.claude/hooks/scope-sync.log
.claude/hooks/scope-sync.py
.claude/mcp_servers.json
.gitignore
api/__init__.py
api/api/__init__.py
api/api/routes/__init__.py
api/api/routes/api_keys.py
api/api/routes/auth.py
api/api/routes/facts.py
api/api/routes/graph.py
api/api/routes/memories.py
api/api/routes/pruning.py
api/api/routes/summary.py
api/api/routes/updates.py
api/api/routes/workspaces.py
api/config.py
api/main.py
api/models.py
api/schemas.py
api/services/__init__.py
api/services/api_key_service.py
api/services/enrichment.py
api/services/fact_expansion_service.py
api/services/memory_service.py
api/services/pruning_service.py
api/services/update_context_filter.py
api/services/workspace_service.py
api/utils/__init__.py
api/utils/api_keys.py
api/utils/auth.py
api/utils/database.py
api/utils/graphiti_client.py
CLAUDE.md
cli/__init__.py
cli/config.py
cli/main.py
cli/sync.py
cli/templates.py
docker-compose.yml
Dockerfile
env.example
frontend/components.json
frontend/next-env.d.ts
frontend/next.config.ts
frontend/package.json
frontend/postcss.config.mjs
frontend/src/animations/animated-card.tsx
frontend/src/animations/animated-list.tsx
frontend/src/animations/page-transition.tsx
frontend/src/animations/shimmer.tsx
frontend/src/animations/variants.ts
frontend/src/api/agents.ts
frontend/src/api/api-keys.ts
frontend/src/api/auth.ts
frontend/src/api/client.ts
frontend/src/api/memories.ts
frontend/src/api/types.ts
frontend/src/api/workspaces.ts
frontend/src/app/(app)/dashboard/page.tsx
frontend/src/app/(app)/layout.tsx
frontend/src/app/(app)/workspace/[id]/agents/loading.tsx
frontend/src/app/(app)/workspace/[id]/agents/page.tsx
frontend/src/app/(app)/workspace/[id]/keys/loading.tsx
frontend/src/app/(app)/workspace/[id]/keys/page.tsx
frontend/src/app/(app)/workspace/[id]/layout.tsx
frontend/src/app/(app)/workspace/[id]/loading.tsx
frontend/src/app/(app)/workspace/[id]/memories/loading.tsx
frontend/src/app/(app)/workspace/[id]/memories/page.tsx
frontend/src/app/(app)/workspace/[id]/page.tsx
frontend/src/app/(app)/workspace/[id]/settings/loading.tsx
frontend/src/app/(app)/workspace/[id]/settings/page.tsx
frontend/src/app/auth/page.tsx
frontend/src/app/globals.css
frontend/src/app/layout.tsx
frontend/src/app/page.tsx
frontend/src/components/agent/agent-card.tsx
frontend/src/components/agent/agent-create-dialog.tsx
frontend/src/components/agent/agent-list.tsx
frontend/src/components/agent/agent-select.tsx
frontend/src/components/api-key/api-key-create-dialog.tsx
frontend/src/components/api-key/api-key-row.tsx
frontend/src/components/api-key/api-key-table.tsx
frontend/src/components/landing/feature-accordion.tsx
frontend/src/components/landing/graph-showcase.tsx
frontend/src/components/landing/minimap.tsx
frontend/src/components/landing/needle-grid.tsx
frontend/src/components/landing/spider-grid.tsx
frontend/src/components/landing/team-flow.tsx
frontend/src/components/landing/tool-beam.tsx
frontend/src/components/layout/app-shell.tsx
frontend/src/components/layout/navbar.tsx
frontend/src/components/layout/page-header.tsx
frontend/src/components/layout/sidebar.tsx
frontend/src/components/layout/theme-toggle.tsx
frontend/src/components/memory/memory-card.tsx
frontend/src/components/memory/memory-create-dialog.tsx
frontend/src/components/memory/memory-detail-panel.tsx
frontend/src/components/memory/memory-graph.tsx
frontend/src/components/memory/memory-list.tsx
frontend/src/components/memory/memory-priority-badge.tsx
frontend/src/components/memory/memory-scope-badge.tsx
frontend/src/components/memory/memory-search.tsx
frontend/src/components/memory/updates-dialog.tsx
frontend/src/components/providers/query-provider.tsx
frontend/src/components/providers/theme-provider.tsx
frontend/src/components/shared/auth-guard.tsx
frontend/src/components/shared/copy-button.tsx
frontend/src/components/shared/empty-state.tsx
frontend/src/components/shared/tag-input.tsx
frontend/src/components/ui/badge.tsx
frontend/src/components/ui/button.tsx
frontend/src/components/ui/card.tsx
frontend/src/components/ui/command.tsx
frontend/src/components/ui/dialog.tsx
frontend/src/components/ui/dropdown-menu.tsx
frontend/src/components/ui/form.tsx
frontend/src/components/ui/input.tsx
frontend/src/components/ui/label.tsx
frontend/src/components/ui/popover.tsx
[58 more files omitted for size]
```

### Dependencies

- frontend/package.json: @eslint/eslintrc@^3, @hookform/resolvers@^5.2.2, @radix-ui/react-slot@^1.2.4, @tailwindcss/postcss@^4.1.18, @tanstack/react-query@^5.90.21, @types/node@^22, @types/react@^19, @types/react-dom@^19, axios@^1.13.5, class-variance-authority@^0.7.1, clsx@^2.1.1, cmdk@^1.1.1, eslint@^9, eslint-config-next@15.3.3, lucide-react@^0.564.0, motion@^12.34.0, next@^15.5.12, next-themes@^0.4.6, radix-ui@^1.4.3, react@^19.0.0, react-dom@^19.0.0, react-hook-form@^7.71.1, reagraph@^4.30.8, sonner@^2.0.7, tailwind-merge@^3.4.0, tailwindcss@^4.1.18, typescript@^5, zod@^4.3.6, zustand@^5.0.11
- poke_mcp/requirements.txt: fastmcp@>=2.12.0, httpx@>=0.27, python-dotenv@>=1.0,<2
- pyproject.toml: alembic@>=1.13,<2, fastapi@>=0.111,<1, fastmcp@>=0.2,<0.3, graphiti-core@>=0.27, httpx@>=0.27,<0.28, openai@>=1.13,<2, psycopg[binary]@>=3.1, pydantic@>=2.11,<3, pydantic-core@>=2.16,<3, python-dotenv@>=1.0,<2, sqlalchemy@>=2.0,<3, uvicorn[standard]@>=0.30,<0.31
- requirements.txt: alembic@>=1.13,<2, fastapi@>=0.111,<1, fastmcp@>=0.2,<0.3, graphiti-core@>=0.27, httpx@>=0.27,<0.28, openai@>=1.13,<2, psycopg[binary]@>=3.1,<3.2, pydantic@>=2.11,<3, pydantic-core@>=2.16,<3, python-dotenv@>=1.0,<2, sqlalchemy@>=2.0,<3, uvicorn[standard]@>=0.30,<0.31

### Recent commits (newest first)

- fixes
- Merge pull request #3 from batyr0601/poke
- check contradiction is an mcp tool
- poke working
- poke changes
- Merge branch 'labratory'
- Merge branch 'labratory' of https://github.com/batyr0601/treehacks26 into labratory
- Merge pull request #2 from batyr0601/labratory
- feat: endpoint to get daily user summary
- fixing deployment
- feat: pruning and frontend interface for polling updates and pruning
- landing
- graph view, scope logic for cli
- Merge conflict_alerts into labratory
- hero edit
- line
- claude hook should work
- new ui
- fix hook init
- Merge branch 'new-updates' into labratory

## Key source files (fetched from GitHub, selected and truncated for size)

### CLAUDE.md

```markdown
# Scope — Shared Team Memory

This project is connected to **Scope**, a shared memory layer for your team's AI agents.
Memories from all AI tools (Claude Code, Cursor, Codex, Windsurf, Copilot, Gemini) are
synced to the team workspace so everyone benefits from each other's context.

## How it works
- Run `scope push` to sync memories from any AI tool to the team workspace.
- Claude Code auto-syncs via hooks when memory files change.
- Memories are labeled with your username and source tool so teammates know the origin.
- Use the Scope MCP tools to search or store memories explicitly.

## MCP Tools available
- `recall_smart_search` — Search team memories (auto-detects workspace)
- `recall_search_memories` — Search with explicit workspace_id
- `recall_store_memory` — Save a decision or context to team memory
- `recall_list_workspaces` — List workspaces you belong to

## Guidelines
- When you discover an important pattern, architectural decision, or gotcha, store it as a memory.
- Use `memory_type: "decision"` for design choices and `memory_type: "context"` for general knowledge.
- Use `scope: "global"` for things the whole team should see, `scope: "private"` for personal notes.

```

### GRAPHITI.md

```markdown
# Graphiti Integration

Recall's memory layer now uses [Zep's Graphiti](https://github.com/getzep/graphiti) as its
knowledge backbone, replacing the previous pgvector-based embedding search. Graphiti is an
open-source framework for building temporally-aware knowledge graphs designed specifically as
a memory layer for AI agents. It processes data as discrete episodes in real-time, immediately
updating the knowledge graph without batch recomputation.

## Why Graphiti over pgvector

The vector database approach stored memories as flat embeddings and retrieved them via cosine
similarity. This works for simple lookups but loses the relationships between concepts — when
you search for "why did we choose React?", you get the decision but not the people involved,
the alternatives considered, or the context that led to it. Graphiti extracts entities and
relationships from every memory, building a structured knowledge graph that preserves these
connections. It also combines three search methods (vector similarity, BM25 keyword search,
and graph traversal) into a single hybrid search, which consistently outperforms pure vector
search in retrieval benchmarks.

Graphiti's temporal model is the other key advantage. Every fact in the graph carries timestamps
for when it became true and when it was superseded. When someone says "we switched from Redux
to Zustand last month," Graphiti marks the old Redux edge as invalid and creates a new Zustand
edge with the correct time bounds. Point-in-time queries ("what was our stack in January?")
work out of the box.

## Architecture

The integration keeps PostgreSQL for auth, workspaces, API keys, and access control. Graphiti
(backed by Neo4j) handles all memory content indexing and search. When a memory is stored, it
goes into both systems: PostgreSQL for metadata and ACL, Graphiti as an episode that gets
processed into entities and relationships. When querying, the search goes through Graphiti's
hybrid retrieval and returns extracted facts ranked by relevance.

## Running

```bash
docker compose up -d          # starts PostgreSQL, Neo4j, and the API
```

Neo4j Browser is available at http://localhost:7474 for visualizing the knowledge graph.
Try `MATCH (n)-[r]->(m) RETURN n, r, m` to see all entities and their relationships.

## Test Data

Run the test script to populate a sample Slack thread between Tony and Muzart discussing
the migration from pgvector to Graphiti:

```bash
python scripts/test_graphiti.py
```

## MCP Inspector

```bash
RECALL_API_KEY=<your-key> npx @anthropic-ai/mcp-inspector python -m mcp_server.run_stdio
```

Use `recall_store_memory` to add content and `recall_search_memories` or `recall_smart_search`
to query the knowledge graph.

```

### requirements.txt

```
fastapi>=0.111,<1
uvicorn[standard]>=0.30,<0.31
sqlalchemy>=2.0,<3
psycopg[binary]>=3.1,<3.2
alembic>=1.13,<2
pydantic>=2.11,<3
pydantic-core>=2.16,<3
httpx>=0.27,<0.28
python-dotenv>=1.0,<2
openai>=1.13,<2
fastmcp>=0.2,<0.3
graphiti-core>=0.27

```

### Dockerfile

```
FROM python:3.11-slim

ENV PYTHONUNBUFFERED=1 \
    PIP_NO_CACHE_DIR=1

WORKDIR /app

RUN apt-get update && apt-get install -y --no-install-recommends build-essential && rm -rf /var/lib/apt/lists/*

COPY requirements.txt .
RUN pip install --upgrade pip && pip install -r requirements.txt

COPY api ./api
COPY sdk ./sdk
COPY pyproject.toml .

EXPOSE 8000

CMD ["uvicorn", "api.main:app", "--host", "0.0.0.0", "--port", "8000"]

```

### pyproject.toml

```
[project]
name = "scope"
version = "0.1.0"
description = "Scope — shared team memory for AI agents"
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
  "fastapi>=0.111,<1",
  "uvicorn[standard]>=0.30,<0.31",
  "sqlalchemy>=2.0,<3",
  "psycopg[binary]>=3.1",
  "alembic>=1.13,<2",
  "pydantic>=2.11,<3",
  "pydantic-core>=2.16,<3",
  "httpx>=0.27,<0.28",
  "python-dotenv>=1.0,<2",
  "openai>=1.13,<2",
  "fastmcp>=0.2,<0.3",
  "graphiti-core>=0.27"
]

[project.scripts]
scope = "cli.main:main"

[tool.setuptools.packages.find]
include = ["cli*", "sdk*", "api*", "mcp_server*", "poke_mcp*"]

[tool.uvicorn]
app = "api.main:app"
reload = true
port = 8000
host = "0.0.0.0"

```

### docker-compose.yml

```yaml
services:
  db:
    image: ankane/pgvector:latest
    environment:
      POSTGRES_USER: recall
      POSTGRES_PASSWORD: recall
      POSTGRES_DB: recall_dev
    ports:
      - "5433:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U recall -d recall_dev"]
      interval: 2s
      timeout: 5s
      retries: 10

  neo4j:
    image: neo4j:5
    environment:
      NEO4J_AUTH: neo4j/password
    ports:
      - "7474:7474"
      - "7687:7687"
    volumes:
      - neo4jdata:/data
    healthcheck:
      test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:7474 || exit 1"]
      interval: 5s
      timeout: 3s
      retries: 10
      start_period: 40s

  api:
    build: .
    depends_on:
      db:
        condition: service_healthy
      neo4j:
        condition: service_healthy
    env_file:
      - .env
    environment:
      DATABASE_URL: postgresql+psycopg://recall:recall@db:5432/recall_dev
      NEO4J_URI: bolt://neo4j:7687
      NEO4J_USER: neo4j
      NEO4J_PASSWORD: password
    ports:
      - "8000:8000"
    command: uvicorn api.main:app --host 0.0.0.0 --port 8000

  poke-mcp:
    build:
      context: ./poke_mcp
    depends_on:
      - api
    environment:
      RECALL_API_BASE_URL: http://api:8000
      RECALL_API_KEY: ${RECALL_API_KEY:-}
      RECALL_USER_ID: ${RECALL_USER_ID:-}
      PORT: "8001"
    ports:
      - "8001:8001"

volumes:
  pgdata:
  neo4jdata:

```

### poke_mcp/requirements.txt

```
fastmcp>=2.12.0
httpx>=0.27
python-dotenv>=1.0,<2

```

### poke_mcp/Dockerfile

```
FROM python:3.12-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY server.py .

EXPOSE 8001

CMD ["python", "server.py"]

```

### frontend/package.json

```
{"name":"scope-frontend","version":"0.1.0","private":true,"scripts":{"dev":"next dev --turbopack","build":"next build","start":"next start","lint":"next lint"},"dependencies":{"@hookform/resolvers":"^5.2.2","@radix-ui/react-slot":"^1.2.4","@tanstack/react-query":"^5.90.21","axios":"^1.13.5","class-variance-authority":"^0.7.1","clsx":"^2.1.1","cmdk":"^1.1.1","lucide-react":"^0.564.0","motion":"^12.34.0","next":"^15.5.12","next-themes":"^0.4.6","radix-ui":"^1.4.3","react":"^19.0.0","react-dom":"^19.0.0","react-hook-form":"^7.71.1","reagraph":"^4.30.8","sonner":"^2.0.7","tailwind-merge":"^3.4.0","zod":"^4.3.6","zustand":"^5.0.11"},"devDependencies":{"@eslint/eslintrc":"^3","@tailwindcss/postcss":"^4.1.18","@types/node":"^22","@types/react":"^19","@types/react-dom":"^19","eslint":"^9","eslint-config-next":"15.3.3","tailwindcss":"^4.1.18","typescript":"^5"}}
```

### api/main.py

```python
from __future__ import annotations

import asyncio
import logging
from contextlib import asynccontextmanager

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

from api.api.routes import api_keys, auth, facts, graph, memories, pruning, summary, updates, workspaces
from api.config import get_settings
from api.services.pruning_service import periodic_pruning_task
from api.utils import graphiti_client
from api.utils.database import init_db

logger = logging.getLogger(__name__)

settings = get_settings()


@asynccontextmanager
async def lifespan(app: FastAPI):
    # Startup
    init_db()
    graphiti_client.init()
    prune_task = asyncio.create_task(periodic_pruning_task())
    yield
    # Shutdown
    prune_task.cancel()
    graphiti_client.close()


app = FastAPI(title=settings.app_name, version="0.1.0", lifespan=lifespan)

app.add_middleware(
    CORSMiddleware,  # ty:ignore[invalid-argument-type]
    allow_origins=["http://localhost:3000"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


@app.get("/health", tags=["system"])
def health_check() -> dict[str, str]:
    return {"status": "ok"}


app.include_router(auth.router)
app.include_router(memories.router)
app.include_router(workspaces.router)
app.include_router(api_keys.router)
app.include_router(updates.router)
app.include_router(facts.router)
app.include_router(graph.router)
app.include_router(pruning.router)
app.include_router(summary.router)

```

[153 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]