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
5 changes: 3 additions & 2 deletions plugin/.mcp.json
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 <N> Override REST port (default: 3111)

Expand Down
24 changes: 15 additions & 9 deletions src/cli/connect/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}",
},
};

Expand Down
11 changes: 8 additions & 3 deletions src/mcp/tools-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
11 changes: 9 additions & 2 deletions test/cli-connect.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down
13 changes: 9 additions & 4 deletions test/codex-plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)", () => {
Expand Down
67 changes: 67 additions & 0 deletions test/mcp-surface-default.test.ts
Original file line number Diff line number Diff line change
@@ -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<string, string> } };
};
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\}/);
});
});