Bound shared MCP container lifecycle

This commit is contained in:
2026-07-24 13:03:09 -07:00
parent 8de9658b8c
commit 6177a995d5
17 changed files with 1351 additions and 117 deletions

View File

@@ -3,12 +3,14 @@
Context Kit supports assistants that can run local stdio MCP servers, HTTP MCP
servers, or both. The default transport split is simple:
- `context-web-search`: local stdio command.
- `context-web-search`: local HTTP MCP service.
- `context-docs`: local HTTP MCP service.
- `context-repomix`: local stdio command.
`bin/context-kit docs` is a stdio fallback for clients that cannot use HTTP MCP.
The included snippets cover Claude Code and OpenCode.
`bin/context-kit web-search` and `bin/context-kit docs` are stdio fallbacks for
clients that cannot use HTTP MCP. They bridge to the shared services and do not
stop those services when the client exits. The included snippets cover Claude
Code and OpenCode.
## Claude Code

View File

@@ -56,8 +56,10 @@ Only the variables below are part of the public configuration surface. Other
| Variable | Default | Purpose |
|---|---|---|
| `CONTEXT_KIT_DATA_DIR` | `$HOME/.local/share/context-kit` | Persistent docs indexes and model cache |
| `CONTEXT_KIT_COMPOSE_PROJECT` | `context-kit` | Docker Compose project and network prefix |
| `CONTEXT_KIT_COMPOSE_PROJECT` | `context-kit` | Shared-service ownership boundary and Compose name prefix |
| `CONTEXT_KIT_SEARXNG_PORT` | `8099` | Localhost SearXNG port |
| `CONTEXT_KIT_WEB_SEARCH_PORT` | `8777` | Localhost port for the long-lived web-search HTTP service |
| `CONTEXT_KIT_WEB_SEARCH_HTTP_URL` | `http://127.0.0.1:${CONTEXT_KIT_WEB_SEARCH_PORT}/mcp` | URL emitted into HTTP MCP install snippets |
| `CONTEXT_KIT_WEB_SEARCH_MAX_BYTES` | `52428800` | Max bytes `context-web-search` accepts and downloads per fetch |
| `CONTEXT_KIT_WEB_SEARCH_PROVIDER` | `searxng` | Default `search_web` provider; fallback order depends on this provider |
| `CONTEXT_KIT_WEB_SEARCH_HTTP_TIMEOUT` | `15000` | HTTP timeout in milliseconds for search providers |
@@ -76,26 +78,63 @@ Only the variables below are part of the public configuration surface. Other
| `CONTEXT_KIT_DOCS_LOCAL_SOURCES_DIR` | `${CONTEXT_KIT_DATA_DIR}/local-sources` | Machine-local llms.txt tree mounted read-only into docs-mcp |
| `CONTEXT_KIT_DOCS_LOCAL_SOURCES_PORT` | `8769` | Loopback port inside docs-mcp for serving local source files |
## Docker Ownership
One Compose project owns the shared `searxng`, `web-search-mcp`, and `docs-mcp`
services. Compose derives stable container and network names from
`CONTEXT_KIT_COMPOSE_PROJECT`; the default network is `context-kit_default`.
Compose's existing labels remain the ownership markers for SearXNG, docs, the
network, and `searxng-cache`. Their definitions are unchanged from
`origin/main`, avoiding a resource-recreation prompt. The new web-search service
also records `dev.context-kit.uid`.
`start`, `stop`, and `restart` use one canonical
`/tmp/context-kit-PROJECT.lock` directory, which must be a non-symlink directory
owned by the current uid with mode `0700`. Existing docs and web-search
containers must have the same uid; cross-user lifecycle control is rejected.
`start` passes `--no-recreate` to Compose. On failure it removes only service
containers that did not exist before the attempt, restores prior running/stopped
states by exact container ID, and leaves the deterministic network and cache
volume intact for reuse. `restart` operates on the same container IDs and uses
the same state restoration. Neither command replaces an existing container.
`stop` stops containers without removing them or their network.
The stdio bridge commands and Repomix create uniquely named client containers
with `dev.context-kit.lifecycle=client` and an invocation-specific owner label.
Their cleanup verifies that owner label before removing the exact container ID.
Web search runs stateless MCP sessions. Its front end accepts only loopback Host
values or the internal `web-search-mcp:8000` service name and returns 403 for any
request carrying Origin. It probes initialize and tools/list periodically; a
dead stdio backend terminates the container so Docker can restart it.
`restart` restarts existing container IDs, so it reloads bind-mounted docs source
files but does not apply rebuilt images or changed container environment. There
is intentionally no automatic replacement path while the old container cannot
be restored transactionally.
## TTL Guidance
`24h` is the default. Most reference docs do not need re-embedding more often,
and the shared service does not re-fetch sources until the TTL elapses.
Use shorter TTLs for fast-moving APIs:
Set a shorter TTL in `.env` for fast-moving APIs:
```sh
CONTEXT_KIT_DOCS_TTL=6h bin/context-kit restart
```dotenv
CONTEXT_KIT_DOCS_TTL=6h
```
Use longer TTLs for stable specs:
Set a longer TTL for stable specs:
```sh
CONTEXT_KIT_DOCS_TTL=30d bin/context-kit restart
```dotenv
CONTEXT_KIT_DOCS_TTL=30d
```
The docs-mcp container reads `CONTEXT_KIT_DOCS_TTL` at startup, so changes
require `bin/context-kit restart`. When freshness matters for one task, prefer
calling the `docs_refresh` MCP tool instead of lowering the global TTL.
The docs-mcp container environment is fixed when Compose creates it. A safe
same-ID `restart` does not apply a changed TTL; it takes effect only when a new
container is explicitly provisioned. When freshness matters for one task,
prefer `docs_refresh` instead of replacing the shared container.
## Browser CORS
@@ -103,12 +142,13 @@ calling the `docs_refresh` MCP tool instead of lowering the global TTL.
HTTP clients do not need CORS. If a browser-based local client must call the MCP
endpoint directly, allow only the exact local origin(s) it uses:
```sh
CONTEXT_KIT_DOCS_ALLOW_ORIGIN="http://127.0.0.1:3000 http://localhost:3000" \
bin/context-kit restart
```dotenv
CONTEXT_KIT_DOCS_ALLOW_ORIGIN="http://127.0.0.1:3000 http://localhost:3000"
```
Avoid `*`; the docs MCP is a local unauthenticated endpoint.
Avoid `*`; the docs MCP is a local unauthenticated endpoint. Like other
container-environment changes, this takes effect only on explicit provisioning
of a new docs container, not a same-ID `restart`.
## Source Profiles

View File

@@ -5,8 +5,11 @@ Context Kit is designed to be safe by default for local development.
## Defaults
- SearXNG is bound to `127.0.0.1` only.
- Web-search and docs MCP HTTP endpoints are bound to `127.0.0.1` only.
- No hosted API keys are required.
- The web-search MCP image runs as the non-root `node` user.
- Web-search MCP sessions are stateless. Its HTTP front end permits only
loopback/internal Host values and rejects every supplied Origin with 403.
- Repomix mounts only the current project read-only.
- Docs indexing stores data under `$HOME/.local/share/context-kit` unless you
override it.
@@ -36,10 +39,14 @@ Do not expose SearXNG or MCP servers to the public internet without a separate
review. The default setup is for localhost development.
The containers may bind to `0.0.0.0` internally, but the Compose file publishes
SearXNG and docs-mcp only on `127.0.0.1`. If you run the images outside the
provided Compose file, review port publishing, SearXNG's limiter/secret, and MCP
authentication separately.
SearXNG, web-search-mcp, and docs-mcp only on `127.0.0.1`. If you run the images
outside the provided Compose file, review port publishing, SearXNG's
limiter/secret, and MCP authentication separately.
Browser CORS for `context-docs` is disabled by default. Only set
`CONTEXT_KIT_DOCS_ALLOW_ORIGIN` for exact local origins that need direct browser
access; avoid wildcard origins for unauthenticated local MCP endpoints.
`context-web-search` does not expose browser CORS configuration. Browser requests
carry Origin and are rejected; CLI/server-side MCP clients omit Origin. A local
reverse proxy must preserve this policy and present an allowed loopback Host.

View File

@@ -6,8 +6,9 @@
bin/context-kit doctor
```
This checks Docker, Compose, images, the Docker network, SearXNG health, docs
HTTP readiness, and docs source configuration.
This checks Docker, Compose, images, the Docker network, SearXNG health, a real
web-search MCP initialize/tools-list exchange, docs HTTP readiness, and docs
source configuration.
For release-grade MCP protocol checks, run:
@@ -46,6 +47,65 @@ Build default images:
bin/context-kit build
```
## Repeated Per-Project Containers
Current OpenCode and Claude snippets connect web search and docs directly to the
shared HTTP services. If every project still starts a web-search container,
regenerate the snippet, update the assistant configuration, and restart the
assistant:
```sh
bin/context-kit install opencode
bin/context-kit install claude
```
For the upgrade from `origin/main`, build and start once. `start` creates only
the missing web-search service and refuses to recreate existing services:
```sh
bin/context-kit build
bin/context-kit start
```
Context Kit never performs a global Docker prune. Inspect labeled resources and
their owners explicitly:
```sh
docker ps -a --filter label=dev.context-kit=true \
--format 'table {{.ID}}\t{{.Names}}\t{{.Status}}\t{{.Label "dev.context-kit.lifecycle"}}\t{{.Label "dev.context-kit.owner"}}\t{{.Label "com.docker.compose.service"}}'
```
Containers from the old per-call web-search launcher have an empty Compose
service and no lifecycle/owner labels. After all old assistant processes are
stopped, remove only the exact legacy container IDs you verified; do not use a
name-pattern or global prune.
Lifecycle commands for one Compose project use a canonical, uid-owned lock and
reject cross-user control. Failed startup removes only newly-created service
containers, restores existing container states by ID, and never removes the
network or cache volume. `start` uses Compose `--no-recreate`, leaving an
existing container unchanged when its image or environment differs from the
current Compose model; it never silently replaces that container. Inspect the
shared services without deleting them:
```sh
bin/context-kit status
docker compose -p "${CONTEXT_KIT_COMPOSE_PROJECT:-context-kit}" -f compose.yml logs web-search-mcp docs-mcp searxng
```
Use the protocol-level doctor check. `/healthz` also performs initialize and
tools/list rather than trusting the proxy's static `/status` metadata:
```sh
bin/context-kit doctor
curl http://127.0.0.1:8777/healthz
```
`bin/context-kit status` lists legacy labeled containers that have neither a
Compose service nor lifecycle label. This is diagnostic only; Context Kit never
auto-removes them. Stop their old assistant owners before removing individually
verified container IDs.
## Fetch URL Says Max Download Bytes Is Too Big
If `fetch_url` fails before making a network request with an MCP validation error