diff --git a/README.md b/README.md index 3c1851676..24072dbc2 100644 --- a/README.md +++ b/README.md @@ -359,13 +359,13 @@ Browser-native hive-mind for CLI coding agents - Claude Code, Codex, Gemini, and -[![banner-gif](https://github.com/user-attachments/assets/867d9329-ce9a-496f-ab1e-15c77974e5fa)](https://github.com/tt-a1i/evermemos-mcp) +[![banner-gif](https://github.com/user-attachments/assets/8e217d39-5d15-4c6c-9b54-3e83add4e0f2)](https://github.com/EverMind-AI/EverOS-MCP) -#### AI Coding Assistants With EverOS +#### MCP Server for AI Coding Assistants -Universal long-term memory layer for AI coding assistants, powered by EverOS. +Plug EverOS memory into Claude Code, Codex, Claude Desktop or Cursor with `uvx everos-mcp`. Works with EverOS Cloud and with this self-hosted server. -[Code](https://github.com/tt-a1i/evermemos-mcp) +[Code](https://github.com/EverMind-AI/EverOS-MCP) · [Setup guide](docs/mcp.md) diff --git a/docs/index.md b/docs/index.md index e5bd160d1..fb4eab17e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -24,6 +24,7 @@ already know what you want to do and need to know exactly how. | [knowledge.md](knowledge.md) | Knowledge base module — upload, search, taxonomy, storage layout | | [reflection.md](reflection.md) | Reflection — offline memory consolidation: enable, schedule, storage, triggering | | [cli.md](cli.md) | `everos` CLI subcommands + env var conventions | +| [mcp.md](mcp.md) | Connect MCP clients (Claude Code, Codex, Cursor, Claude Desktop) through the official `everos-mcp` server | | [storage_layout.md](storage_layout.md) | Memory-root tree + frontmatter chassis + EntryId encoding | | [prompt_slots.md](prompt_slots.md) | PromptSlot loader — bundled default prompts (Layer 1 live; app/runtime overlays planned) | | [configuration.md](configuration.md) | TOML / env-var configuration reference | diff --git a/docs/mcp.md b/docs/mcp.md new file mode 100644 index 000000000..285d88e2a --- /dev/null +++ b/docs/mcp.md @@ -0,0 +1,84 @@ +# Connect MCP clients + +EverOS exposes its memory to MCP (Model Context Protocol) clients — Claude Code, Codex, +Claude Desktop, Cursor and any other MCP host — through the official **everos-mcp** +server: [PyPI `everos-mcp`](https://pypi.org/project/everos-mcp/) · +[source](https://github.com/EverMind-AI/EverOS-MCP). + +`everos-mcp` is a separate process that runs next to your EverOS server. It speaks MCP to +the host over stdio and calls this server's `/api/v2/memory/*` endpoints. It does not start +or stop EverOS for you. + +- [Prerequisites](#prerequisites) +- [Add the server to your client](#add-the-server-to-your-client) +- [What the assistant gets](#what-the-assistant-gets) +- [Behaviour on a self-hosted server](#behaviour-on-a-self-hosted-server) +- [Remote hosts](#remote-hosts) + +## Prerequisites + +1. `everos init` done and the LLM / embedding API keys filled in (`everos.toml`). +2. `everos server start` running — default `http://127.0.0.1:8000`. Start it before the + host launches the MCP server, and keep it running. +3. [`uv`](https://docs.astral.sh/uv/getting-started/installation/) installed; `uvx` runs + `everos-mcp` without a manual install. + +No API key is needed when `EVEROS_BASE_URL` points at your own server. + +## Add the server to your client + +```bash +# Claude Code +claude mcp add everos -e EVEROS_BASE_URL=http://127.0.0.1:8000 -- uvx everos-mcp + +# Codex +codex mcp add everos --env EVEROS_BASE_URL=http://127.0.0.1:8000 -- uvx everos-mcp +``` + +Claude Desktop, Cursor and other JSON-configured clients: + +```json +{ + "mcpServers": { + "everos": { + "command": "uvx", + "args": ["everos-mcp"], + "env": { "EVEROS_BASE_URL": "http://127.0.0.1:8000" } + } + } +} +``` + +Optional environment variables: `EVEROS_USER_ID` (memory owner, default `default-user`), +`EVEROS_APP_ID` / `EVEROS_PROJECT_ID` (scope, default `default`). The full list is in the +[everos-mcp README](https://github.com/EverMind-AI/EverOS-MCP#configuration). + +## What the assistant gets + +Seven tools, backed by this server's `/api/v2/memory/*` endpoints: `get_profile`, +`add_memory`, `search_memory`, `list_memories`, `record_trajectory`, +`recall_agent_experience` and `forget_session` (not yet available here, see below). The +server also ships an autonomy protocol through MCP `instructions`, so capable hosts load the +profile at session start, save new facts and recall context without being asked. The current tool list and the protocol are +documented in the [everos-mcp README](https://github.com/EverMind-AI/EverOS-MCP#tools); +this page only covers what is specific to a self-hosted EverOS. + +## Behaviour on a self-hosted server + +- **`forget_session` is not available yet.** Self-hosted EverOS has no session-scoped delete + endpoint ([#483](https://github.com/EverMind-AI/EverOS/issues/483)); the tool reports this + instead of failing. Memories are Markdown files under the memory root (`~/.everos/` by + default) — delete them there. +- **New memories become searchable shortly after saving.** `add_memory` returns once + extraction has written the Markdown; the search index catches up a moment later. + `list_memories` shows new entries sooner than `search_memory`. +- **Trajectories only become cases when they pass the quality gate.** Short or + straightforward runs are stored as episodes but produce no reusable case, so + `recall_agent_experience` stays empty until a substantial task has been recorded. + +## Remote hosts + +To serve MCP over HTTP instead of stdio (hosts on another machine, or OAuth-only hosts), +run `everos-mcp --transport http` on the machine that can reach your EverOS server and put +it behind your gateway. Configuration is described in the +[everos-mcp README](https://github.com/EverMind-AI/EverOS-MCP#configuration). diff --git a/docs/use-cases.md b/docs/use-cases.md index 4abf09d8f..12d00b843 100644 --- a/docs/use-cases.md +++ b/docs/use-cases.md @@ -44,13 +44,13 @@ Browser-native hive-mind for CLI coding agents - Claude Code, Codex, Gemini, and -[![banner-gif](https://github.com/user-attachments/assets/867d9329-ce9a-496f-ab1e-15c77974e5fa)](https://github.com/tt-a1i/evermemos-mcp) +[![banner-gif](https://github.com/user-attachments/assets/8e217d39-5d15-4c6c-9b54-3e83add4e0f2)](https://github.com/EverMind-AI/EverOS-MCP) -#### AI Coding Assistants with EverOS +#### MCP Server for AI Coding Assistants -Universal long-term memory layer for AI coding assistants, powered by EverOS. +Plug EverOS memory into Claude Code, Codex, Claude Desktop or Cursor with `uvx everos-mcp`. Works with EverOS Cloud and with this self-hosted server. -[Code](https://github.com/tt-a1i/evermemos-mcp) +[Code](https://github.com/EverMind-AI/EverOS-MCP) · [Setup guide](mcp.md) diff --git a/src/everos/entrypoints/cli/commands/init_cmd.py b/src/everos/entrypoints/cli/commands/init_cmd.py index 5854e14e0..b2568dcb5 100644 --- a/src/everos/entrypoints/cli/commands/init_cmd.py +++ b/src/everos/entrypoints/cli/commands/init_cmd.py @@ -19,6 +19,8 @@ from everos.config.settings import resolve_root from everos.entrypoints.cli._log_setup import configure_cli_logging +from .server import MCP_GUIDE_URL + _EVEROS_TEMPLATE = Path(__file__).resolve().parents[3] / "config" / "default.toml" _OME_TEMPLATE = Path(__file__).resolve().parents[3] / "config" / "default_ome.toml" @@ -97,3 +99,6 @@ def init( typer.echo(f" 1. Edit {everos_toml} — fill in API keys (see comments inside)") root_flag = f" --root {resolved}" if root else "" typer.echo(f" 2. Run: everos server start{root_flag}") + typer.echo( + f" 3. Connect an MCP client (Claude Code, Codex, Cursor): {MCP_GUIDE_URL}" + ) diff --git a/src/everos/entrypoints/cli/commands/server.py b/src/everos/entrypoints/cli/commands/server.py index c3f5c0bf3..31644ac6c 100644 --- a/src/everos/entrypoints/cli/commands/server.py +++ b/src/everos/entrypoints/cli/commands/server.py @@ -23,6 +23,25 @@ no_args_is_help=True, ) +# The wheel ships only ``src/everos``, so the guide has to be linked, not pathed. +MCP_GUIDE_URL = "https://github.com/EverMind-AI/EverOS/blob/main/docs/mcp.md" + +_WILDCARD_BINDS = {"0.0.0.0", "::", ""} + + +def mcp_base_url(host: str, port: int) -> str: + """The ``EVEROS_BASE_URL`` a client on this machine should use. + + ``host`` is the bind address: a wildcard bind (``0.0.0.0`` / ``::``) is + not something a client can connect to, so it becomes loopback; IPv6 + literals are bracketed so the result is a valid URL. + """ + if host in _WILDCARD_BINDS: + host = "127.0.0.1" + elif ":" in host and not host.startswith("["): + host = f"[{host}]" + return f"http://{host}:{port}" + @app.command("start") def start( @@ -81,6 +100,12 @@ def start( bootstrap_logger = logging.getLogger("everos.cli.server") bootstrap_logger.info("starting everos on %s:%d", host_resolved, port_resolved) + bootstrap_logger.info( + "once up, MCP clients connect with: uvx everos-mcp, " + "EVEROS_BASE_URL=%s (guide: %s)", + mcp_base_url(host_resolved, port_resolved), + MCP_GUIDE_URL, + ) if host_resolved == "0.0.0.0": bootstrap_logger.warning( "binding to 0.0.0.0 exposes the API on all interfaces; EverOS " diff --git a/tests/unit/test_entrypoints/test_cli/test_init_command.py b/tests/unit/test_entrypoints/test_cli/test_init_command.py index da2c9e040..ed3ff68c0 100644 --- a/tests/unit/test_entrypoints/test_cli/test_init_command.py +++ b/tests/unit/test_entrypoints/test_cli/test_init_command.py @@ -110,6 +110,9 @@ def test_output_shows_next_steps(runner: CliRunner, tmp_path: Path) -> None: result = runner.invoke(app, ["init", "--root", str(target)]) assert "Next steps" in result.output assert "everos server start" in result.output + assert ( + "https://github.com/EverMind-AI/EverOS/blob/main/docs/mcp.md" in result.output + ) # ``os`` imported above just to keep ruff from complaining; remove if Ruff diff --git a/tests/unit/test_entrypoints/test_cli/test_server_command.py b/tests/unit/test_entrypoints/test_cli/test_server_command.py index 025e15ad7..5f410f3fa 100644 --- a/tests/unit/test_entrypoints/test_cli/test_server_command.py +++ b/tests/unit/test_entrypoints/test_cli/test_server_command.py @@ -132,3 +132,44 @@ def test_start_with_root_option( result = CliRunner().invoke(root_app, ["server", "start", "--root", str(tmp_path)]) assert result.exit_code == 0, result.stdout assert "kwargs" in captured + + +@pytest.mark.parametrize( + ("host", "port", "expected"), + [ + ("127.0.0.1", 8000, "http://127.0.0.1:8000"), + ("0.0.0.0", 8000, "http://127.0.0.1:8000"), + ("::", 9000, "http://127.0.0.1:9000"), + ("::1", 8000, "http://[::1]:8000"), + ("10.0.0.5", 8765, "http://10.0.0.5:8765"), + ], +) +def test_mcp_base_url_is_something_a_client_can_dial( + host: str, port: int, expected: str +) -> None: + assert server_mod.mcp_base_url(host, port) == expected + + +def test_start_logs_mcp_hint_with_client_url(captured: dict[str, object]) -> None: + import logging + + records: list[str] = [] + + class _Collect(logging.Handler): + def emit(self, record: logging.LogRecord) -> None: + records.append(record.getMessage()) + + handler = _Collect() + logger = logging.getLogger("everos.cli.server") + logger.addHandler(handler) + try: + result = CliRunner().invoke( + root_app, ["server", "start", "--host", "0.0.0.0", "--port", "9000"] + ) + finally: + logger.removeHandler(handler) + assert result.exit_code == 0, result.stdout + hint = [r for r in records if "uvx everos-mcp" in r] + assert hint, records + assert "EVEROS_BASE_URL=http://127.0.0.1:9000" in hint[0] + assert server_mod.MCP_GUIDE_URL in hint[0]