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
9 changes: 8 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@
"source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=cached"
],
"postCreateCommand": "bash -lc '/opt/scripts/post-create.sh'",
"forwardPorts": [7411, 5433, 6380],
"portsAttributes": {
"7411": { "label": "MCP Memory Server", "onAutoForward": "silent" },
"5433": { "label": "MCP Memory Postgres", "onAutoForward": "silent" },
"6380": { "label": "MCP Memory Redis", "onAutoForward": "silent" }
},
"customizations": {
"vscode": {
"settings": {
Expand All @@ -24,6 +30,7 @@
}
},
"features": {
"ghcr.io/devcontainers/features/git:1": {}
"ghcr.io/devcontainers/features/git:1": {},
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
}
}
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,8 @@ HIMALAYA_SIGNATURE="Sent from terminal"
# iMessage bridge (optional) - macOS host reachable via Tailscale
# IMSG_HOST="mac-mini"
# IMSG_USER="tommy"

# MCP Memory Server (optional, used with --profile memory)
# MCP_EMBED_PROVIDER="stub" # "stub" or "openai"
# MCP_LOG_LEVEL="info"
# MCP_PG_PASSWORD="mempass"
34 changes: 25 additions & 9 deletions .github/workflows/codespaces-image.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# Builds and publishes the Agentainer devcontainer image for GitHub Codespaces.
# Builds and publishes Agentainer images for GitHub Codespaces.
# Requires repo secrets:
# - GHCR_PAT (PAT with packages:write, read + repo scope)
# OR use GITHUB_TOKEN with proper permissions in the same org.
#
# Output image:
# Output images:
# ghcr.io/<owner>/<repo>/agentainer:latest
# ghcr.io/<owner>/<repo>/agentainer:<sha>
# ghcr.io/<owner>/<repo>/mcp-memory:latest
# ghcr.io/<owner>/<repo>/mcp-memory:<sha>

name: Build Agentainer Codespaces Image

Expand Down Expand Up @@ -42,23 +44,37 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GHCR_PAT || github.token }}

- name: Compute image name
- name: Compute image names
id: meta
run: |
set -euo pipefail
OWNER="$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')"
REPO="$(echo '${{ github.event.repository.name }}' | tr '[:upper:]' '[:lower:]')"
echo "image=ghcr.io/${OWNER}/${REPO}/agentainer" >> "$GITHUB_OUTPUT"
echo "agentainer_image=ghcr.io/${OWNER}/${REPO}/agentainer" >> "$GITHUB_OUTPUT"
echo "mcp_memory_image=ghcr.io/${OWNER}/${REPO}/mcp-memory" >> "$GITHUB_OUTPUT"

- name: Build and push (multi-arch)
- name: Build and push Agentainer (multi-arch)
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
push: true
platforms: linux/amd64,linux/arm64
tags: |
${{ steps.meta.outputs.image }}:latest
${{ steps.meta.outputs.image }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
${{ steps.meta.outputs.agentainer_image }}:latest
${{ steps.meta.outputs.agentainer_image }}:${{ github.sha }}
cache-from: type=gha,scope=agentainer
cache-to: type=gha,mode=max,scope=agentainer

- name: Build and push MCP Memory Server (multi-arch)
uses: docker/build-push-action@v6
with:
context: ./services/mcp-memory
file: ./services/mcp-memory/Dockerfile
push: true
platforms: linux/amd64,linux/arm64
tags: |
${{ steps.meta.outputs.mcp_memory_image }}:latest
${{ steps.meta.outputs.mcp_memory_image }}:${{ github.sha }}
cache-from: type=gha,scope=mcp-memory
cache-to: type=gha,mode=max,scope=mcp-memory
43 changes: 43 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,46 @@ jobs:
- name: Teardown
if: always()
run: docker compose down

test-mcp-memory:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Start MCP Memory backing services
working-directory: services/mcp-memory
run: docker compose up -d postgres redis

- name: Wait for services
run: |
for i in $(seq 1 30); do
docker exec mcp-memory-postgres pg_isready -U memuser -d memorydb && break
sleep 1
done
docker exec mcp-memory-redis redis-cli ping

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install test dependencies
working-directory: services/mcp-memory
run: pip install -r requirements-test.txt

- name: Run MCP Memory Server tests
working-directory: services/mcp-memory
env:
PG_DSN: postgresql://memuser:mempass@localhost:5433/memorydb
REDIS_URL: redis://localhost:6380/0
EMBED_PROVIDER: stub
EMBED_DIM: "1536"
BLOB_ENDPOINT_URL: ""
MIGRATIONS_DIR: ./migrations
run: pytest tests/ -v --tb=short

- name: Teardown
if: always()
working-directory: services/mcp-memory
run: docker compose down
22 changes: 21 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.PHONY: up down shell rebuild logs himalaya playwright whisper start-services \
test run fetch dockerhost install-hooks
test run fetch dockerhost install-hooks \
memory-up memory-down memory-logs memory-health memory-test

# ── Core lifecycle ─────────────────────────────────────────────────
up:
Expand Down Expand Up @@ -50,6 +51,25 @@ whisper:
test:
docker exec -it agentainer bash -lc "cd /workspace && pytest tests/ -v --tb=short"

# ── MCP Memory Server (opt-in profile) ────────────────────────────
memory-up:
docker compose --profile memory up -d --build

memory-down:
docker compose --profile memory down

memory-logs:
docker compose --profile memory logs -f mcp-memory

memory-health:
@curl -sf http://localhost:7411/health | python3 -m json.tool || echo "ERROR: mcp-memory not reachable"

memory-test:
cd services/mcp-memory && docker compose up -d postgres redis && \
sleep 3 && \
pip install -q -r requirements-test.txt && \
pytest tests/ -v --tb=short

# ── Secrets hygiene ────────────────────────────────────────────────
install-hooks:
cp scripts/pre-commit-secrets.sh .git/hooks/pre-commit
Expand Down
69 changes: 69 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,74 @@ services:
- agentainer-home:/home/dev
- /var/run/docker.sock:/var/run/docker.sock

# --- Profile: MCP Memory Server (opt-in) ---
# Usage: docker compose --profile memory up -d
# Standalone: docker compose -f services/mcp-memory/docker-compose.yml up -d
mcp-memory:
build:
context: ./services/mcp-memory
dockerfile: Dockerfile
container_name: mcp-memory
profiles:
- memory
environment:
- PG_DSN=postgresql://memuser:mempass@mcp-memory-postgres:5432/memorydb
- REDIS_URL=redis://mcp-memory-redis:6379/0
- EMBED_DIM=1536
- EMBED_PROVIDER=${MCP_EMBED_PROVIDER:-stub}
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
- BLOB_BUCKET=bellie-blobnlie
- LOG_LEVEL=${MCP_LOG_LEVEL:-info}
- MIGRATIONS_DIR=/app/migrations
depends_on:
mcp-memory-postgres:
condition: service_healthy
mcp-memory-redis:
condition: service_healthy
ports:
- "7411:8000"
restart: unless-stopped
healthcheck:
test: ["CMD", "python", "-c", "import httpx; r=httpx.get('http://localhost:8000/health'); r.raise_for_status()"]
interval: 15s
timeout: 5s
retries: 5
start_period: 10s

mcp-memory-postgres:
image: pgvector/pgvector:pg16
container_name: mcp-memory-postgres
profiles:
- memory
environment:
POSTGRES_DB: memorydb
POSTGRES_USER: memuser
POSTGRES_PASSWORD: ${MCP_PG_PASSWORD:-mempass}
volumes:
- mcp-pgdata:/var/lib/postgresql/data
ports:
- "5433:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U memuser -d memorydb"]
interval: 5s
timeout: 3s
retries: 5
restart: unless-stopped

mcp-memory-redis:
image: redis:7-alpine
container_name: mcp-memory-redis
profiles:
- memory
ports:
- "6380:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
restart: unless-stopped

volumes:
agentainer-home:
mcp-pgdata:
37 changes: 37 additions & 0 deletions services/mcp-memory/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# MCP Memory Server — Environment Variables
# Copy to .env and fill in values (do not commit .env)

# ── PostgreSQL ───────────────────────────────────────────────────────
PG_DSN=postgresql://memuser:mempass@localhost:5433/memorydb
PG_MIN_POOL=2
PG_MAX_POOL=20

# ── Redis ────────────────────────────────────────────────────────────
REDIS_URL=redis://localhost:6380/0

# ── Embeddings ───────────────────────────────────────────────────────
# Provider: "stub" (deterministic, for dev/test) or "openai" (production)
EMBED_PROVIDER=stub
EMBED_DIM=1536
# Required only if EMBED_PROVIDER=openai:
# OPENAI_API_KEY=sk-...
# OPENAI_EMBED_MODEL=text-embedding-3-small

# ── Blob Store (S3-compatible / bellie-blobnlie) ─────────────────────
# Leave BLOB_ENDPOINT_URL empty to use local filesystem fallback
BLOB_ENDPOINT_URL=
BLOB_BUCKET=bellie-blobnlie
BLOB_ACCESS_KEY=
BLOB_SECRET_KEY=
BLOB_REGION=us-east-1

# ── Cache TTLs ───────────────────────────────────────────────────────
WORKING_SET_TTL=21600
WORKING_SET_MAX=50
SEARCH_CACHE_TTL=600

# ── Server ───────────────────────────────────────────────────────────
HOST=0.0.0.0
PORT=8000
LOG_LEVEL=info
MIGRATIONS_DIR=/app/migrations
24 changes: 24 additions & 0 deletions services/mcp-memory/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
FROM python:3.12-slim

WORKDIR /app

# Install system dependencies for asyncpg
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 --no-cache-dir -r requirements.txt

COPY migrations/ /app/migrations/
COPY app/ /app/app/

ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1

EXPOSE 8000

HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD python -c "import httpx; r=httpx.get('http://localhost:8000/health'); r.raise_for_status()"

CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "2"]
Loading
Loading