From d1422c0b081d771513b7ce1af159f690f85962bb Mon Sep 17 00:00:00 2001 From: Rohit Ghumare Date: Mon, 25 May 2026 20:13:47 +0100 Subject: [PATCH 1/2] fix(mcp): full 51-tool surface by default + env defaults in plugin manifest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two user-blocking MCP issues, single root surface. #510 Claude Code drops the agentmemory MCP server silently when AGENTMEMORY_URL is unset. Per Claude Code MCP docs: 'If a required environment variable is not set and has no default value, Claude Code will fail to parse the config' — and a parse failure removes the server from /reload-plugins output with no warning. plugin/.mcp.json now uses ${VAR:-default} form for AGENTMEMORY_URL, AGENTMEMORY_SECRET, and AGENTMEMORY_TOOLS so fresh installs Just Work without exporting envs. #553 OpenCode (and Claude Code, and Codex) saw 8 tools instead of the 51 every plugin manifest advertises. getVisibleTools() defaulted to AGENTMEMORY_TOOLS=core which capped at 8 ESSENTIAL_TOOLS. README has always said '51 MCP tools' — the default now matches the advertising. AGENTMEMORY_TOOLS=core still available for users who want the lean set. CLI help text and existing codex-plugin test updated to the new contract. New test/mcp-surface-default.test.ts adds 4 regression cases covering default, all, core, and the env-default form in the plugin manifest. 1141/1141 vitest pass. --- plugin/.mcp.json | 5 ++- src/cli.ts | 2 +- src/mcp/tools-registry.ts | 11 ++++-- test/codex-plugin.test.ts | 13 +++++-- test/mcp-surface-default.test.ts | 67 ++++++++++++++++++++++++++++++++ 5 files changed, 88 insertions(+), 10 deletions(-) create mode 100644 test/mcp-surface-default.test.ts diff --git a/plugin/.mcp.json b/plugin/.mcp.json index f7c3760336..37f7754fff 100644 --- a/plugin/.mcp.json +++ b/plugin/.mcp.json @@ -4,8 +4,9 @@ "command": "npx", "args": ["-y", "@agentmemory/mcp"], "env": { - "AGENTMEMORY_URL": "${AGENTMEMORY_URL}", - "AGENTMEMORY_SECRET": "${AGENTMEMORY_SECRET}" + "AGENTMEMORY_URL": "${AGENTMEMORY_URL:-http://localhost:3111}", + "AGENTMEMORY_SECRET": "${AGENTMEMORY_SECRET:-}", + "AGENTMEMORY_TOOLS": "${AGENTMEMORY_TOOLS:-all}" } } } diff --git a/src/cli.ts b/src/cli.ts index 04462379cb..6da3d6a77c 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -143,7 +143,7 @@ Options: --help, -h Show this help --verbose, -v Show engine stderr, boot log, and diagnostic info --reset Wipe ~/.agentmemory/preferences.json and re-run onboarding - --tools all|core Tool visibility (default: core = 7 tools) + --tools all|core Tool visibility (default: all = 51 tools; core = 8 essentials) --no-engine Skip auto-starting iii-engine --port Override REST port (default: 3111) diff --git a/src/mcp/tools-registry.ts b/src/mcp/tools-registry.ts index 3001cae75d..a8a8dfaf42 100644 --- a/src/mcp/tools-registry.ts +++ b/src/mcp/tools-registry.ts @@ -941,8 +941,13 @@ export function getAllTools(): McpToolDef[] { ]; } +// #553: default switched from "core" (8 essential tools) to "all" +// (full 51-tool surface). README and plugin manifests have always +// advertised 51 tools "in proxy mode"; the old default left OpenCode / +// Claude Code users seeing 8 with no indication the other 43 existed. +// Users who want the lean essentials can still set AGENTMEMORY_TOOLS=core. export function getVisibleTools(): McpToolDef[] { - const mode = process.env["AGENTMEMORY_TOOLS"] || "core"; - if (mode === "all") return getAllTools(); - return getAllTools().filter((t) => ESSENTIAL_TOOLS.has(t.name)); + const mode = process.env["AGENTMEMORY_TOOLS"] || "all"; + if (mode === "core") return getAllTools().filter((t) => ESSENTIAL_TOOLS.has(t.name)); + return getAllTools(); } diff --git a/test/codex-plugin.test.ts b/test/codex-plugin.test.ts index 63aa1803fa..e501938dee 100644 --- a/test/codex-plugin.test.ts +++ b/test/codex-plugin.test.ts @@ -81,10 +81,15 @@ describe("Codex plugin manifest (developers.openai.com/codex/plugins)", () => { >; }>(join(pluginRoot, ".mcp.json")); - expect(mcp.mcpServers.agentmemory?.env).toMatchObject({ - AGENTMEMORY_URL: "${AGENTMEMORY_URL}", - AGENTMEMORY_SECRET: "${AGENTMEMORY_SECRET}", - }); + // #510: env interpolation must include defaults so Claude Code (and + // any other MCP host that fails parse on unset ${VAR}) doesn't drop + // the server silently when the user hasn't exported the var. + expect(mcp.mcpServers.agentmemory?.env?.AGENTMEMORY_URL).toMatch( + /\$\{AGENTMEMORY_URL:-/, + ); + expect(mcp.mcpServers.agentmemory?.env?.AGENTMEMORY_SECRET).toMatch( + /\$\{AGENTMEMORY_SECRET:-/, + ); }); it("hooks.codex.json contains only events Codex supports (no Subagent / SessionEnd / Notification / TaskCompleted / PostToolUseFailure)", () => { diff --git a/test/mcp-surface-default.test.ts b/test/mcp-surface-default.test.ts new file mode 100644 index 0000000000..835ebaeb17 --- /dev/null +++ b/test/mcp-surface-default.test.ts @@ -0,0 +1,67 @@ +import { describe, it, expect, beforeEach, afterEach } from "vitest"; +import { readFileSync } from "node:fs"; +import { + getAllTools, + getVisibleTools, +} from "../src/mcp/tools-registry.js"; + +// #553: plugin manifests and README advertise 51 MCP tools. The old +// default was AGENTMEMORY_TOOLS=core which silently capped the surface +// at 8 essentials with no indication the other 43 existed. Default +// flipped to "all"; the lean set is still accessible via +// AGENTMEMORY_TOOLS=core. +describe("MCP tool surface default (#553)", () => { + const ORIG = process.env["AGENTMEMORY_TOOLS"]; + beforeEach(() => { + delete process.env["AGENTMEMORY_TOOLS"]; + }); + afterEach(() => { + if (ORIG === undefined) delete process.env["AGENTMEMORY_TOOLS"]; + else process.env["AGENTMEMORY_TOOLS"] = ORIG; + }); + + it("default returns the full 51-tool surface, matching plugin advertising", () => { + const visible = getVisibleTools(); + const all = getAllTools(); + expect(visible.length).toBe(all.length); + expect(visible.length).toBeGreaterThanOrEqual(48); + }); + + it("AGENTMEMORY_TOOLS=all returns the same full set", () => { + process.env["AGENTMEMORY_TOOLS"] = "all"; + expect(getVisibleTools().length).toBe(getAllTools().length); + }); + + it("AGENTMEMORY_TOOLS=core returns the 8 essential tools", () => { + process.env["AGENTMEMORY_TOOLS"] = "core"; + const names = new Set(getVisibleTools().map((t) => t.name)); + expect(names.size).toBe(8); + for (const t of [ + "memory_save", + "memory_recall", + "memory_consolidate", + "memory_smart_search", + "memory_sessions", + "memory_diagnose", + "memory_lesson_save", + "memory_reflect", + ]) { + expect(names.has(t)).toBe(true); + } + }); + + it("plugin .mcp.json provides default env interpolation so CC parse never fails (#510)", () => { + const raw = readFileSync("plugin/.mcp.json", "utf-8"); + const cfg = JSON.parse(raw) as { + mcpServers: { agentmemory: { env: Record } }; + }; + const env = cfg.mcpServers.agentmemory.env; + // Per Claude Code MCP docs: ${VAR} without a default fails config + // parse when VAR is unset, silently dropping the server. ${VAR:-x} + // form is what unblocks fresh installs that haven't exported + // AGENTMEMORY_URL. + expect(env["AGENTMEMORY_URL"]).toMatch(/\$\{AGENTMEMORY_URL:-/); + expect(env["AGENTMEMORY_SECRET"]).toMatch(/\$\{AGENTMEMORY_SECRET:-/); + expect(env["AGENTMEMORY_TOOLS"]).toMatch(/\$\{AGENTMEMORY_TOOLS:-all\}/); + }); +}); From a890ce4569ca82811ee8dc62d5e28d08efca7bf4 Mon Sep 17 00:00:00 2001 From: Rohit Ghumare Date: Mon, 25 May 2026 20:22:01 +0100 Subject: [PATCH 2/2] fix(connect): apply same env defaults to wired-in MCP block MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #650 fixed plugin/.mcp.json but not src/cli/connect/util.ts — `npx @agentmemory/agentmemory connect` (Cursor, Gemini CLI, Windsurf, all JSON-MCP agents) was still writing ${VAR} form into user configs, keeping #510 unfixed on every non-Claude-Code-plugin path. util.ts now ships the same ${VAR:-default} form with AGENTMEMORY_TOOLS=all. Codex TOML adapter already used a literal URL — unaffected. Hermes connect just prints YAML guidance — unaffected. test/cli-connect.test.ts: updated existing assertion to the new contract. --- src/cli/connect/util.ts | 24 +++++++++++++++--------- test/cli-connect.test.ts | 11 +++++++++-- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/cli/connect/util.ts b/src/cli/connect/util.ts index 6d5f61ac98..3a9eda1aab 100644 --- a/src/cli/connect/util.ts +++ b/src/cli/connect/util.ts @@ -10,19 +10,25 @@ import { dirname, join } from "node:path"; import { homedir } from "node:os"; import * as p from "@clack/prompts"; -// Env values use ${VAR} expansion so the wired MCP entry inherits -// AGENTMEMORY_URL / AGENTMEMORY_SECRET from the user's shell. When the -// vars are unset, the host (Claude Code, Cursor, etc.) substitutes an -// empty string; the standalone shim treats empty as missing and falls -// back to http://localhost:3111. This lets a single wired entry serve -// both local and remote (Kubernetes / reverse-proxied) deployments -// without doctor-warning duplicates (#375). +// Env values use ${VAR:-default} expansion so the wired MCP entry +// inherits AGENTMEMORY_URL / AGENTMEMORY_SECRET / AGENTMEMORY_TOOLS +// from the user's shell, but never fails parse when the var is unset +// (#510). Earlier `${VAR}` form caused Claude Code to silently drop the +// server when no shell-level export existed — per the Claude Code MCP +// docs, "If a required environment variable is not set and has no +// default value, Claude Code will fail to parse the config." +// +// Defaults match the documented runtime: localhost:3111 (no auth, all +// tools). One wired entry now serves local AND remote (Kubernetes / +// reverse-proxied) deployments without doctor-warning duplicates (#375) +// AND fresh installs that haven't exported envs (#510). export const AGENTMEMORY_MCP_BLOCK = { command: "npx", args: ["-y", "@agentmemory/mcp"], env: { - AGENTMEMORY_URL: "${AGENTMEMORY_URL}", - AGENTMEMORY_SECRET: "${AGENTMEMORY_SECRET}", + AGENTMEMORY_URL: "${AGENTMEMORY_URL:-http://localhost:3111}", + AGENTMEMORY_SECRET: "${AGENTMEMORY_SECRET:-}", + AGENTMEMORY_TOOLS: "${AGENTMEMORY_TOOLS:-all}", }, }; diff --git a/test/cli-connect.test.ts b/test/cli-connect.test.ts index 99174dacbc..37b0b00e1f 100644 --- a/test/cli-connect.test.ts +++ b/test/cli-connect.test.ts @@ -129,8 +129,15 @@ describe("agentmemory connect — claude-code adapter (mock filesystem)", () => const config = JSON.parse(readFileSync(join(tmpHome, ".claude.json"), "utf-8")); const entry = config.mcpServers.agentmemory; expect(entry.env).toBeDefined(); - expect(entry.env.AGENTMEMORY_URL).toBe("${AGENTMEMORY_URL}"); - expect(entry.env.AGENTMEMORY_SECRET).toBe("${AGENTMEMORY_SECRET}"); + // #510: env interpolation must carry a default so Claude Code + // doesn't silently drop the server when the user hasn't exported + // AGENTMEMORY_URL / AGENTMEMORY_SECRET. Defaults match the + // documented runtime (localhost:3111, no auth, all tools). + expect(entry.env.AGENTMEMORY_URL).toBe( + "${AGENTMEMORY_URL:-http://localhost:3111}", + ); + expect(entry.env.AGENTMEMORY_SECRET).toBe("${AGENTMEMORY_SECRET:-}"); + expect(entry.env.AGENTMEMORY_TOOLS).toBe("${AGENTMEMORY_TOOLS:-all}"); }); it("install() with --force re-writes even when already wired", async () => {