feat: optional shared/external MemPalace + fix validate.yml false-red on base changes
Added: - generate-config.py registers mempalace as a remote MCP endpoint when MEMPALACE_REMOTE_URL is set (MEMPALACE_REMOTE_TOKEN -> Bearer), else the local stdio command as before. Same env contract as pi-devbox's bridge. - docker-compose.mempalace.yml: optional shared server (mempalace-mcp --transport http), loopback-bound by default. - compose (both) + .env.example(.shared): MEMPALACE_REMOTE_URL/TOKEN + local-vs-external docs. Fixed: - validate.yml: skip validate-base/validate-omos on base-changing commits (base-change-warning now exports base_changed). Previously a commit that changed the base AND tightened smoke-test.sh in lockstep (v2.4.0 nano/micro) hard-failed against the stale base-latest until the release rebuilt it. actionlint clean. README + CHANGELOG (Unreleased).
This commit is contained in:
@@ -31,6 +31,19 @@ WORKSPACE_PATH=~/projects
|
|||||||
# Path to SSH keys on host
|
# Path to SSH keys on host
|
||||||
SSH_KEY_PATH=~/.ssh
|
SSH_KEY_PATH=~/.ssh
|
||||||
|
|
||||||
|
# ── MemPalace memory (local by default) ───────────────────────────
|
||||||
|
# By default each container runs its OWN MemPalace (a local stdio server;
|
||||||
|
# palace stored at ~/.mempalace). Uncomment the devbox-palace volume in
|
||||||
|
# docker-compose.yml to persist it across container recreation.
|
||||||
|
#
|
||||||
|
# To instead share ONE MemPalace across several containers / harnesses
|
||||||
|
# (pi + opencode + native), point every container at an external HTTP
|
||||||
|
# endpoint. When MEMPALACE_REMOTE_URL is set, no local mempalace-mcp is
|
||||||
|
# spawned and the devbox-palace volume is irrelevant.
|
||||||
|
# Serve it with: mempalace-mcp --transport http --host 0.0.0.0 --port 8765
|
||||||
|
# MEMPALACE_REMOTE_URL=http://mempalace.lan:8765/mcp
|
||||||
|
# MEMPALACE_REMOTE_TOKEN= # optional — sent as: Authorization: Bearer <token>
|
||||||
|
|
||||||
# ── LAN access from the container (host-OS-agnostic) ─────────────────
|
# ── LAN access from the container (host-OS-agnostic) ─────────────────
|
||||||
# On VM-backed hosts (macOS OrbStack / Docker Desktop, also Docker Desktop
|
# On VM-backed hosts (macOS OrbStack / Docker Desktop, also Docker Desktop
|
||||||
# on Windows) the container runs in a Linux VM and CANNOT reach the host's
|
# on Windows) the container runs in a Linux VM and CANNOT reach the host's
|
||||||
|
|||||||
@@ -27,6 +27,13 @@ GIT_USER_EMAIL=your.name@example.com
|
|||||||
# If you have per-user keys: SSH_KEY_PATH=~/<signum>/.ssh
|
# If you have per-user keys: SSH_KEY_PATH=~/<signum>/.ssh
|
||||||
# SSH_KEY_PATH=~/.ssh
|
# SSH_KEY_PATH=~/.ssh
|
||||||
|
|
||||||
|
# ── MemPalace memory ─────────────────────────────────────────────────
|
||||||
|
# Default: each container runs its own LOCAL palace. To share ONE external
|
||||||
|
# MemPalace across containers/harnesses, set the URL (no local server is
|
||||||
|
# spawned when set). MEMPALACE_REMOTE_TOKEN is an optional bearer token.
|
||||||
|
# MEMPALACE_REMOTE_URL=http://mempalace.lan:8765/mcp
|
||||||
|
# MEMPALACE_REMOTE_TOKEN=
|
||||||
|
|
||||||
# ── Locale (defaults to en_US.UTF-8) ────────────────────────────────
|
# ── Locale (defaults to en_US.UTF-8) ────────────────────────────────
|
||||||
# LANG=sv_SE.UTF-8
|
# LANG=sv_SE.UTF-8
|
||||||
# LANGUAGE=sv_SE:sv
|
# LANGUAGE=sv_SE:sv
|
||||||
|
|||||||
@@ -60,13 +60,19 @@ jobs:
|
|||||||
|
|
||||||
base-change-warning:
|
base-change-warning:
|
||||||
# Surfaces a warning when this commit changes base-image inputs
|
# Surfaces a warning when this commit changes base-image inputs
|
||||||
# (Dockerfile.base, rootfs/, entrypoint*.sh). validate.yml uses
|
# (Dockerfile.base, rootfs/, entrypoint*.sh) AND exports `base_changed` so
|
||||||
# Hub's base-latest as the parent for variant builds, so changes to
|
# validate-base/validate-omos can skip. validate.yml uses Hub's base-latest
|
||||||
# those files are NOT exercised here — only release tags rebuild the
|
# as the parent for variant builds, so a changed base is NOT exercised here
|
||||||
# base via docker-publish-split.yml.
|
# — worse, if the same commit tightens smoke-test.sh in lockstep with the
|
||||||
|
# base change (as the nano/micro commit did), those jobs HARD-FAIL against
|
||||||
|
# the stale base-latest: a false red that self-heals only once the release
|
||||||
|
# rebuilds base-latest. So we skip them here and defer full base validation
|
||||||
|
# to the release path (docker-publish-split.yml on tag push).
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: catthehacker/ubuntu:act-latest
|
image: catthehacker/ubuntu:act-latest
|
||||||
|
outputs:
|
||||||
|
base_changed: ${{ steps.detect.outputs.base_changed }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -74,18 +80,23 @@ jobs:
|
|||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
|
|
||||||
- name: Detect base-input changes
|
- name: Detect base-input changes
|
||||||
|
id: detect
|
||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
if ! git diff --name-only HEAD~1 HEAD 2>/dev/null \
|
if git diff --name-only HEAD~1 HEAD 2>/dev/null \
|
||||||
| grep -qE '^(Dockerfile\.base|rootfs/|entrypoint.*\.sh)$'; then
|
| grep -qE '^(Dockerfile\.base|rootfs/|entrypoint.*\.sh)$'; then
|
||||||
echo "No base-image inputs changed in this commit — validate.yml fully exercises the published base-latest."
|
echo "base_changed=true" >> "$GITHUB_OUTPUT"
|
||||||
exit 0
|
echo "::warning::This commit changes base-image inputs (Dockerfile.base, rootfs/, or entrypoint*.sh). validate.yml uses Hub's base-latest as the parent for variant builds, so the new base is NOT exercised by this workflow — validate-base/validate-omos are SKIPPED to avoid a false failure against the stale base-latest. Cut a release tag, or run a workflow_dispatch of docker-publish-split.yml against a test tag (e.g. v0.0.0-base-test, promote_latest=false) for end-to-end validation of the new base."
|
||||||
fi
|
|
||||||
echo "::warning::This commit changes base-image inputs (Dockerfile.base, rootfs/, or entrypoint*.sh). validate.yml uses Hub's base-latest as the parent for variant builds, so the new base is NOT exercised by this workflow. Cut a release tag, or run a workflow_dispatch of docker-publish-split.yml against a test tag (e.g. v0.0.0-base-test, promote_latest=false) for end-to-end validation of the new base."
|
|
||||||
echo "Changed base-input files:"
|
echo "Changed base-input files:"
|
||||||
git diff --name-only HEAD~1 HEAD | grep -E '^(Dockerfile\.base|rootfs/|entrypoint.*\.sh)$'
|
git diff --name-only HEAD~1 HEAD | grep -E '^(Dockerfile\.base|rootfs/|entrypoint.*\.sh)$'
|
||||||
|
else
|
||||||
|
echo "base_changed=false" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "No base-image inputs changed in this commit — validate.yml fully exercises the published base-latest."
|
||||||
|
fi
|
||||||
|
|
||||||
validate-base:
|
validate-base:
|
||||||
|
needs: [base-change-warning]
|
||||||
|
if: ${{ needs.base-change-warning.outputs.base_changed != 'true' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: catthehacker/ubuntu:act-latest
|
image: catthehacker/ubuntu:act-latest
|
||||||
@@ -147,6 +158,8 @@ jobs:
|
|||||||
bash scripts/smoke-test.sh opencode-devbox:ci-base --variant base
|
bash scripts/smoke-test.sh opencode-devbox:ci-base --variant base
|
||||||
|
|
||||||
validate-omos:
|
validate-omos:
|
||||||
|
needs: [base-change-warning]
|
||||||
|
if: ${{ needs.base-change-warning.outputs.base_changed != 'true' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: catthehacker/ubuntu:act-latest
|
image: catthehacker/ubuntu:act-latest
|
||||||
|
|||||||
@@ -6,6 +6,39 @@ Tags follow **independent semver** (since `v2.0.0`) — they version *this image
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **Optional shared/external MemPalace via `MEMPALACE_REMOTE_URL`.** MemPalace
|
||||||
|
can now be pointed at one shared HTTP endpoint instead of each container
|
||||||
|
running its own local server. Set `MEMPALACE_REMOTE_URL=http://<host>:8765/mcp`
|
||||||
|
(optionally `MEMPALACE_REMOTE_TOKEN`, sent as `Authorization: Bearer`) in
|
||||||
|
`.env`; `generate-config.py` then registers the `mempalace` MCP server as a
|
||||||
|
`remote` endpoint instead of the local `mempalace-mcp` stdio command (no local
|
||||||
|
server is spawned, and the `devbox-palace` volume becomes irrelevant). Leaving
|
||||||
|
it unset keeps the default local-per-container palace. Mirrors the same env
|
||||||
|
contract as pi-devbox's `mempalace.ts` bridge, so one palace can serve pi +
|
||||||
|
opencode + native.
|
||||||
|
- **`docker-compose.mempalace.yml`** — an optional shared MemPalace server
|
||||||
|
(`mempalace-mcp --transport http`) that several containers/harnesses can point
|
||||||
|
`MEMPALACE_REMOTE_URL` at. Binds to `127.0.0.1:8765` by default (the HTTP
|
||||||
|
transport is unauthenticated — keep it on a trusted network or behind a proxy).
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **`validate.yml` no longer reports a false failure on base-changing commits.**
|
||||||
|
`validate-base`/`validate-omos` build variants from the published
|
||||||
|
`base-latest`; when a commit changed base inputs *and* tightened
|
||||||
|
`smoke-test.sh` in lockstep (as v2.4.0's nano/micro did), those jobs
|
||||||
|
hard-failed against the still-stale `base-latest` until the release rebuilt it.
|
||||||
|
The `base-change-warning` job now exports a `base_changed` output and those two
|
||||||
|
jobs **skip** when base inputs changed, deferring full base validation to the
|
||||||
|
release path (`docker-publish-split.yml`). The scary red becomes a neutral skip
|
||||||
|
plus the existing warning.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## v2.4.0 — 2026-07-01
|
## v2.4.0 — 2026-07-01
|
||||||
|
|
||||||
Minor release. Adds two **non-modal editors** (`nano` + `micro`) alongside
|
Minor release. Adds two **non-modal editors** (`nano` + `micro`) alongside
|
||||||
|
|||||||
@@ -594,6 +594,28 @@ Add mempalace as an MCP server in your `opencode.jsonc` (inside `~/.config/openc
|
|||||||
|
|
||||||
This gives opencode access to 29 MCP tools for searching memory, querying the knowledge graph, managing wings/rooms/drawers, and agent diaries.
|
This gives opencode access to 29 MCP tools for searching memory, querying the knowledge graph, managing wings/rooms/drawers, and agent diaries.
|
||||||
|
|
||||||
|
### Sharing one palace across containers (external server)
|
||||||
|
|
||||||
|
By default each container runs its own local `mempalace-mcp`. To share **one**
|
||||||
|
palace across several containers/harnesses (pi + opencode + native), run the
|
||||||
|
bundled server and point clients at it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose -f docker-compose.mempalace.yml up -d # serves mempalace-mcp over HTTP
|
||||||
|
```
|
||||||
|
|
||||||
|
Then set in each client's `.env`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
MEMPALACE_REMOTE_URL=http://<reachable-host>:8765/mcp
|
||||||
|
# MEMPALACE_REMOTE_TOKEN=... # optional; sent as Authorization: Bearer
|
||||||
|
```
|
||||||
|
|
||||||
|
`generate-config.py` then registers mempalace as a `remote` MCP endpoint instead
|
||||||
|
of the local stdio command — no local `mempalace-mcp` is spawned and the
|
||||||
|
`devbox-palace` volume is irrelevant. ⚠ The HTTP transport is **unauthenticated**;
|
||||||
|
keep it on a trusted network or behind a reverse proxy that enforces the token.
|
||||||
|
|
||||||
### Basic usage
|
### Basic usage
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
# Shared MemPalace server (optional) — one palace for many clients.
|
||||||
|
#
|
||||||
|
# Runs `mempalace-mcp` over HTTP so several containers/harnesses (pi +
|
||||||
|
# opencode + native) can share ONE palace instead of each keeping its own.
|
||||||
|
# Point every client at it by setting, in that client's .env:
|
||||||
|
#
|
||||||
|
# MEMPALACE_REMOTE_URL=http://<reachable-host>:8765/mcp
|
||||||
|
#
|
||||||
|
# (see .env.example). When set, the client connects over HTTP and does NOT
|
||||||
|
# spawn its own local mempalace-mcp.
|
||||||
|
#
|
||||||
|
# Start: docker compose -f docker-compose.mempalace.yml up -d
|
||||||
|
# Stop: docker compose -f docker-compose.mempalace.yml down
|
||||||
|
# Logs: docker compose -f docker-compose.mempalace.yml logs -f
|
||||||
|
#
|
||||||
|
# Why reuse the devbox image? mempalace-mcp is already installed in it, and
|
||||||
|
# reusing it GUARANTEES the server's mempalace version matches the clients'
|
||||||
|
# (both are pinned by the same image build). Override with a slimmer image via
|
||||||
|
# MEMPALACE_SERVER_IMAGE if you prefer (it must provide `mempalace-mcp`).
|
||||||
|
#
|
||||||
|
# ⚠ SECURITY: mempalace-mcp's HTTP transport has NO authentication of its own.
|
||||||
|
# Do NOT expose port 8765 to an untrusted network. The default below binds to
|
||||||
|
# 127.0.0.1 (host loopback) only. To let sibling containers reach it, either
|
||||||
|
# attach them to the shared `mempalace-net` network (container-to-container, no
|
||||||
|
# host port needed — use http://mempalace-server:8765/mcp), or front it with a
|
||||||
|
# reverse proxy that enforces MEMPALACE_REMOTE_TOKEN as `Authorization: Bearer`.
|
||||||
|
|
||||||
|
name: mempalace-server
|
||||||
|
|
||||||
|
services:
|
||||||
|
mempalace:
|
||||||
|
image: ${MEMPALACE_SERVER_IMAGE:-joakimp/opencode-devbox:latest}
|
||||||
|
container_name: mempalace-server
|
||||||
|
# Bypass the devbox entrypoint (dev-shell/LAN/config setup) and run the
|
||||||
|
# HTTP MCP server directly. HOME + explicit --palace pin the data path so
|
||||||
|
# it does not depend on the image's default user/HOME. Runs as root so it
|
||||||
|
# can initialise the fresh named volume; the volume is dedicated to this
|
||||||
|
# server (clients reach it over HTTP, never by mounting it).
|
||||||
|
entrypoint: []
|
||||||
|
user: "0:0"
|
||||||
|
environment:
|
||||||
|
- HOME=/data
|
||||||
|
command:
|
||||||
|
- mempalace-mcp
|
||||||
|
- --transport
|
||||||
|
- http
|
||||||
|
- --host
|
||||||
|
- "0.0.0.0"
|
||||||
|
- --port
|
||||||
|
- "8765"
|
||||||
|
- --palace
|
||||||
|
- /data/.mempalace
|
||||||
|
restart: unless-stopped
|
||||||
|
# Loopback-only by default (see SECURITY note). Use "8765:8765" to expose on
|
||||||
|
# all host interfaces, or drop `ports:` entirely and rely on mempalace-net.
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:8765:8765"
|
||||||
|
volumes:
|
||||||
|
# The shared palace data — precious; back this up.
|
||||||
|
- mempalace-shared:/data/.mempalace
|
||||||
|
# Embedding-model cache (~79 MB, disposable) so search does not re-download.
|
||||||
|
- mempalace-shared-chroma:/data/.cache/chroma
|
||||||
|
networks:
|
||||||
|
- mempalace-net
|
||||||
|
healthcheck:
|
||||||
|
# A tools/list round-trip proves the server is answering MCP (python3 is
|
||||||
|
# always present — mempalace itself is a python tool in the image).
|
||||||
|
test:
|
||||||
|
- CMD
|
||||||
|
- python3
|
||||||
|
- -c
|
||||||
|
- "import urllib.request,json; d=json.dumps({'jsonrpc':'2.0','id':1,'method':'tools/list','params':{}}).encode(); r=urllib.request.Request('http://127.0.0.1:8765/mcp',data=d,headers={'Content-Type':'application/json','Accept':'application/json'}); urllib.request.urlopen(r,timeout=5).read()"
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 60s
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mempalace-shared:
|
||||||
|
mempalace-shared-chroma:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
mempalace-net:
|
||||||
|
name: mempalace-net
|
||||||
@@ -38,6 +38,10 @@ services:
|
|||||||
- TERM=xterm-256color
|
- TERM=xterm-256color
|
||||||
- GITEA_ACCESS_TOKEN=${GITEA_ACCESS_TOKEN:-}
|
- GITEA_ACCESS_TOKEN=${GITEA_ACCESS_TOKEN:-}
|
||||||
- GITEA_HOST=${GITEA_HOST:-}
|
- GITEA_HOST=${GITEA_HOST:-}
|
||||||
|
# MemPalace transport: empty = LOCAL per-container palace (default);
|
||||||
|
# set MEMPALACE_REMOTE_URL to share one external palace. See .env.example.
|
||||||
|
- MEMPALACE_REMOTE_URL=${MEMPALACE_REMOTE_URL:-}
|
||||||
|
- MEMPALACE_REMOTE_TOKEN=${MEMPALACE_REMOTE_TOKEN:-}
|
||||||
volumes:
|
volumes:
|
||||||
# Host workspace — user's project directory
|
# Host workspace — user's project directory
|
||||||
- ${WORKSPACE_PATH:-~/src}:/workspace
|
- ${WORKSPACE_PATH:-~/src}:/workspace
|
||||||
@@ -73,7 +77,9 @@ services:
|
|||||||
# Persist uv data (Python installs)
|
# Persist uv data (Python installs)
|
||||||
- devbox-uv:/home/developer/.local/share/uv
|
- devbox-uv:/home/developer/.local/share/uv
|
||||||
|
|
||||||
# Optional: persist MemPalace data (conversation memory, knowledge graph)
|
# Optional: persist MemPalace data (conversation memory, knowledge graph).
|
||||||
|
# LOCAL palace only (default). In EXTERNAL mode (MEMPALACE_REMOTE_URL set)
|
||||||
|
# the shared server owns the data, so this volume is irrelevant.
|
||||||
# - devbox-palace:/home/developer/.mempalace
|
# - devbox-palace:/home/developer/.mempalace
|
||||||
|
|
||||||
# Optional: persist ChromaDB embedding model cache (~79 MB)
|
# Optional: persist ChromaDB embedding model cache (~79 MB)
|
||||||
|
|||||||
+10
-1
@@ -36,6 +36,12 @@ services:
|
|||||||
- GITHUB_PERSONAL_ACCESS_TOKEN=${GITHUB_PERSONAL_ACCESS_TOKEN:-}
|
- GITHUB_PERSONAL_ACCESS_TOKEN=${GITHUB_PERSONAL_ACCESS_TOKEN:-}
|
||||||
- GITEA_ACCESS_TOKEN=${GITEA_ACCESS_TOKEN:-}
|
- GITEA_ACCESS_TOKEN=${GITEA_ACCESS_TOKEN:-}
|
||||||
- GITEA_HOST=${GITEA_HOST:-}
|
- GITEA_HOST=${GITEA_HOST:-}
|
||||||
|
# MemPalace transport: leave empty for a LOCAL per-container palace
|
||||||
|
# (default). Set MEMPALACE_REMOTE_URL to share ONE external MemPalace
|
||||||
|
# across containers/harnesses; MEMPALACE_REMOTE_TOKEN is an optional
|
||||||
|
# bearer token. See .env.example.
|
||||||
|
- MEMPALACE_REMOTE_URL=${MEMPALACE_REMOTE_URL:-}
|
||||||
|
- MEMPALACE_REMOTE_TOKEN=${MEMPALACE_REMOTE_TOKEN:-}
|
||||||
volumes:
|
volumes:
|
||||||
# Host workspace — mount your project here
|
# Host workspace — mount your project here
|
||||||
- ${WORKSPACE_PATH:-.}:/workspace
|
- ${WORKSPACE_PATH:-.}:/workspace
|
||||||
@@ -128,7 +134,10 @@ services:
|
|||||||
- devbox-nvim-data:/home/developer/.local/share/nvim
|
- devbox-nvim-data:/home/developer/.local/share/nvim
|
||||||
|
|
||||||
# Optional: persist MemPalace data (conversation memory, knowledge graph,
|
# Optional: persist MemPalace data (conversation memory, knowledge graph,
|
||||||
# embeddings). Without this, palace data is lost on container recreation.
|
# embeddings). Applies to the LOCAL palace only (the default, when
|
||||||
|
# MEMPALACE_REMOTE_URL is unset). Without this, palace data is lost on
|
||||||
|
# container recreation. In EXTERNAL mode the shared server owns the data,
|
||||||
|
# so this volume is irrelevant.
|
||||||
# - devbox-palace:/home/developer/.mempalace
|
# - devbox-palace:/home/developer/.mempalace
|
||||||
|
|
||||||
# Optional: persist ChromaDB embedding model cache (~79 MB, downloaded on
|
# Optional: persist ChromaDB embedding model cache (~79 MB, downloaded on
|
||||||
|
|||||||
@@ -16,9 +16,13 @@ Environment variables:
|
|||||||
OPENCODE_MODEL Optional. Overrides the provider default model.
|
OPENCODE_MODEL Optional. Overrides the provider default model.
|
||||||
AWS_REGION Bedrock only. Default: us-east-1.
|
AWS_REGION Bedrock only. Default: us-east-1.
|
||||||
AWS_PROFILE Bedrock only. Default: default.
|
AWS_PROFILE Bedrock only. Default: default.
|
||||||
|
MEMPALACE_REMOTE_URL Optional. Point MemPalace at a shared HTTP endpoint
|
||||||
|
instead of spawning a local stdio server.
|
||||||
|
MEMPALACE_REMOTE_TOKEN Optional. Bearer token for MEMPALACE_REMOTE_URL.
|
||||||
|
|
||||||
MCP servers are auto-registered for tools detected on PATH:
|
MCP servers are auto-registered for tools detected on PATH:
|
||||||
- mempalace (if installed) — enabled
|
- mempalace — LOCAL stdio if `mempalace-mcp` is installed, or EXTERNAL
|
||||||
|
(remote HTTP) when MEMPALACE_REMOTE_URL is set (shared palace)
|
||||||
- gitea-mcp (if installed) — registered but disabled by default
|
- gitea-mcp (if installed) — registered but disabled by default
|
||||||
|
|
||||||
Output path: $HOME/.config/opencode/opencode.jsonc
|
Output path: $HOME/.config/opencode/opencode.jsonc
|
||||||
@@ -80,13 +84,27 @@ def register_mcp_servers(config: dict) -> list[str]:
|
|||||||
"""
|
"""
|
||||||
servers: dict[str, dict] = {}
|
servers: dict[str, dict] = {}
|
||||||
|
|
||||||
# MemPalace — local-first AI memory (if installed).
|
# MemPalace — AI memory. Two modes, same env contract as the mempalace.ts
|
||||||
# `mempalace-mcp` is the entry-point binary shipped by the mempalace
|
# pi extension (mempalace-toolkit), so one shared MemPalace can serve pi +
|
||||||
# Python package. `uv tool install mempalace` places it on PATH as a
|
# opencode + native:
|
||||||
# shim whose shebang points at the isolated venv's Python, so system
|
# LOCAL (default): stdio subprocess. `mempalace-mcp` is the entry-point
|
||||||
# `python3 -m mempalace.mcp_server` (which would fail — system
|
# binary shipped by the mempalace Python package. `uv tool install
|
||||||
# python3 can't import from the uv venv) is unnecessary here.
|
# mempalace` places it on PATH as a shim whose shebang points at the
|
||||||
if shutil.which("mempalace-mcp"):
|
# isolated venv's Python, so system `python3 -m mempalace.mcp_server`
|
||||||
|
# (which would fail — system python3 can't import from the uv venv) is
|
||||||
|
# unnecessary here.
|
||||||
|
# EXTERNAL: set MEMPALACE_REMOTE_URL to a shared MemPalace HTTP endpoint
|
||||||
|
# (e.g. http://mempalace.lan:8765/mcp). MEMPALACE_REMOTE_TOKEN, if set,
|
||||||
|
# becomes a Bearer auth header. No local mempalace-mcp is spawned, so
|
||||||
|
# the binary need not even be installed in this mode.
|
||||||
|
remote_url = os.environ.get("MEMPALACE_REMOTE_URL", "").strip()
|
||||||
|
if remote_url:
|
||||||
|
entry: dict = {"type": "remote", "url": remote_url}
|
||||||
|
token = os.environ.get("MEMPALACE_REMOTE_TOKEN", "").strip()
|
||||||
|
if token:
|
||||||
|
entry["headers"] = {"Authorization": f"Bearer {token}"}
|
||||||
|
servers["mempalace"] = entry
|
||||||
|
elif shutil.which("mempalace-mcp"):
|
||||||
servers["mempalace"] = {
|
servers["mempalace"] = {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"command": ["mempalace-mcp"],
|
"command": ["mempalace-mcp"],
|
||||||
|
|||||||
Reference in New Issue
Block a user