Announcing Beacon: Self-improving memory for coding agents
← All MCP servers
MCP server

io.github.xChuCx/agent-memory

mcp:xchucx/agent-memory

Git-native project memory for AI coding agents: Markdown source of truth, reviewable, secret-safe.

Verdict
Suspicious

Risky behavior the analysis could not rule out.

Analyzed Oct 26, 2026, 12:33 UTC at cb47941

Dispute security verdict

If you believe this verdict is incorrect, submit a dispute and we'll review it.

1
findings
medium
highest severity
1
threat classes
337
files scanned
2,721/2,721
checks satisfied
0
package advisories

Why this verdict

mcp-server . Suspicious
  • suspicious: unverified finding mcp-server:.:internal/adapters/cursor/agent-memory.mdc:2:HIJACK
skill .claude/skills/agent-memory No issues found
  • benign: 42 checks satisfied, no findings for skill:.claude/skills/agent-memory
skill internal/adapters/claude Inconclusive
  • inconclusive: gap no_dataflow_for_go at internal/adapters/claude/claude.go

Security findings 1

Agent hijacking

mediumUnverifiedlow confidence

The bundled Cursor rule description instructs the agent to call memory.fetch_context at the start of every coding task and to prefer it over reading raw source files, steering agent behavior per-task, though this matches the adapter's declared purpose.

At the start of every coding task call memory.fetch_context to load current state, conventions, and prior decisions.
vector:metadata
Refuted findings 13

Candidates the review stage examined and rejected, kept for transparency.

Persistence

criticalRefutedlow confidence

The Cursor adapter's user-invoked `agent-memory install cursor` writes an agent-instruction rule file into .cursor/rules (project or user-global), an agent control-plane location; this is the adapter's declared purpose and does not write to the .agent-memory store, so the DET contradiction with the memory-store claims is spurious.

const rulesDirRel = ".cursor/rules"
Why it was refuted

The .cursor/rules write happens only through the explicit Cursor adapter install, which the package documents as its purpose. It is not a memory-store write, so the detector contradiction is spurious.

Persistence

criticalRefutedlow confidence

The Gemini adapter's explicit install command writes a GEMINI.md agent-context file at the project root (not overwritten unless Force); this is the adapter's declared function, not a contradiction of the memory-store write claims.

const geminiFile = "GEMINI.md"
Why it was refuted

GEMINI.md is written only by the explicit Gemini adapter install, which the package documents as its purpose. It skips existing files unless Force is set and does not write to the memory store.

Code execution

criticalRefutedmedium confidence

The npm launcher downloads a prebuilt binary and its checksums file from the same GitHub release URL, so the integrity check has no independent trust anchor; it caches the binary with mode 0755 and executes it with spawnSync, giving a download-write-execute chain on first run.

fs.writeFileSync(tmp, bin, { mode: 0o755 });
Why it was refuted

Fetching and running a prebuilt binary is the launcher's declared core capability. The download is version-pinned to the project's own release and SHA-256 verified before it is cached and executed.

Supply chain

criticalRefutedmedium confidence

The npm launcher downloads a prebuilt binary at first run from GitHub releases, checks it only against a checksum file from the same release origin, caches it under ~/.cache and then executes it with all arguments; the behaviour is documented in the header comments but means the code that runs is not in the npm package.

const bin = await fetch(`${base}/${asset.name}`);
Why it was refuted

The first-run download from the project's own GitHub release is the declared function of this launcher. It is version-pinned and checksum-verified, and the package description states that the binary is not shipped in the package.

Persistence

criticalRefutedlow confidence

The install adapter writes or merges the repo's .mcp.json (agent MCP control-plane config) to register the agent-memory stdio server; this is the artifact's own server, documented in code comments, non-destructive, and run only via an explicit install command, so it is plausibly necessary but still a control-plane write.

const mcpConfigFile = ".mcp.json"
Why it was refuted

The .mcp.json write only registers the project's own stdio server, during an explicit install. The merge is non-destructive and documented in code comments, so the contradiction with the SKILL.md claim is spurious.

Code execution

highRefutedlow confidence

The undeclared eval harness passes the user-supplied $AGENT_CMD environment string plus a task through shell eval; this is dynamic execution, but it is the script's stated purpose and the command comes from the operator.

eval "$AGENT_CMD \"\$TASK\""
Why it was refuted

The eval in the dev-only benchmark harness runs an operator-supplied $AGENT_CMD, which the script header documents as its purpose. The task text is passed as an escaped variable, and no untrusted source reaches the sink.

Persistence

highRefutedlow confidence

The `agents` adapter's install command writes an embedded AGENTS.md rules file into the project root (an agent control-plane file). This is a user-invoked, declared install feature, and it does not actually contradict the listed claims about agent writes under .agent-memory/. It is flagged only as a persistent agent-instruction write.

const agentsFile = "AGENTS.md"
Why it was refuted

AGENTS.md is written only by the explicit `install agents` adapter, and the package documents this as its purpose. It does not touch .agent-memory, so the detector's contradiction with the store-write claims is spurious.

Persistence

highRefutedlow confidence

The explicit `merge-driver --install` command registers a git merge driver in the repo's git config and writes .gitattributes, a host-config write; it is user-invoked and documented in the command help.

if err := agentgit.InstallMergeDriver(root); err != nil {
Why it was refuted

The git config and .gitattributes writes happen only under the explicit `merge-driver --install` flag. The command help documents both, so this is declared, user-invoked setup.

Persistence

highRefutedlow confidence

The explicit `merge-driver --install` command writes a .gitattributes entry and registers a merge driver in the repo's git config, so git will later run the agent-memory binary on merges; this is a documented, user-invoked setup feature.

registers "merge.agent-memory.driver" in the repo's git config
Why it was refuted

The driver registration is user-invoked via `--install`, and the command's own help documents the .gitattributes and git config writes. Nothing is concealed.

Persistence

highRefutedlow confidence

The memory server ships code that writes a custom merge driver (invoking the `agent-memory` binary from PATH) into the repository's local .git/config. This is a host-config write beyond the declared memory tools (propose_update/status/fetch_context/ping), but it is openly documented and plausibly part of the memory-merge feature.

InstallMergeDriver registers the section-aware merge driver in the repo's
Why it was refuted

InstallMergeDriver is the documented, user-invoked implementation of the merge-driver install. Its comments state that it writes local git config and is run once per clone, so it is declared rather than concealed.

Supply chain

highRefutedlow confidence

The npm launcher is declared to fetch a prebuilt binary from a remote source at runtime and run it, so the code that actually executes is not shipped in the package and its source and integrity cannot be verified here.

Thin launcher that fetches the prebuilt agent-memory binary.
Why it was refuted

The package description openly declares the launcher's fetch-the-binary behavior. That is the declared core capability, not a concealed one, and the download is version-pinned and checksum-verified in the launcher code.

Privilege escalation

mediumRefutedlow confidence

The eval benchmark script documents running the agent with --dangerously-skip-permissions, an undeclared permission-bypass suggestion; it appears only in a comment as an example for a dev-only evaluation harness.

claude -p --dangerously-skip-permissions $AM_MCP --model sonnet
Why it was refuted

The flag appears only inside a header comment as an example of how an operator might set AGENT_CMD for headless Claude in a dev eval harness. The script never applies it itself and nothing instructs an agent to use it.

Supply chain

mediumRefutedlow confidence

The server contains a git clone helper that can fetch from a remote URL into external 'landscape' stores whose content is later blended into agent context; this is mutable remote content that the declared tool set does not mention. The comments indicate pinning and lockfile controls, and sandboxed copying (symlinks rejected).

source may be a remote URL or a
Why it was refuted

This is a plain git clone helper for the documented landscape-store and sync feature. It pins revisions and does not auto-ingest remote content as instructions. The multi-store capability is documented in the code comments.

Coverage gaps 89

Parts of the artifact the static analysis could not fully examine.

no_dataflow_for_go go file: string-literal extraction only
cmd/agent-memory/main.go
no_dataflow_for_go go file: string-literal extraction only
internal/adapters/agents/agents.go
no_dataflow_for_go go file: string-literal extraction only
internal/adapters/cursor/cursor.go
no_dataflow_for_go go file: string-literal extraction only
internal/adapters/gemini/gemini.go
no_dataflow_for_go go file: string-literal extraction only
internal/bench/fixture.go
no_dataflow_for_go go file: string-literal extraction only
internal/cli/apply.go
no_dataflow_for_go go file: string-literal extraction only
internal/cli/diff.go
no_dataflow_for_go go file: string-literal extraction only
internal/cli/digest.go
no_dataflow_for_go go file: string-literal extraction only
internal/cli/doctor.go
no_dataflow_for_go go file: string-literal extraction only
internal/cli/fetch.go
no_dataflow_for_go go file: string-literal extraction only
internal/cli/helpers.go
no_dataflow_for_go go file: string-literal extraction only
internal/cli/init.go
no_dataflow_for_go go file: string-literal extraction only
internal/cli/install.go
no_dataflow_for_go go file: string-literal extraction only
internal/cli/mcp.go
no_dataflow_for_go go file: string-literal extraction only
internal/cli/merge_driver.go
no_dataflow_for_go go file: string-literal extraction only
internal/cli/propose.go
no_dataflow_for_go go file: string-literal extraction only
internal/cli/rebase.go
no_dataflow_for_go go file: string-literal extraction only
internal/cli/rebuild_index.go
no_dataflow_for_go go file: string-literal extraction only
internal/cli/reject.go
no_dataflow_for_go go file: string-literal extraction only
internal/cli/review.go
no_dataflow_for_go go file: string-literal extraction only
internal/cli/root.go
no_dataflow_for_go go file: string-literal extraction only
internal/cli/status.go
no_dataflow_for_go go file: string-literal extraction only
internal/cli/store.go
no_dataflow_for_go go file: string-literal extraction only
internal/cli/sweep.go
no_dataflow_for_go go file: string-literal extraction only
internal/cli/sync.go
no_dataflow_for_go go file: string-literal extraction only
internal/cli/version.go
no_dataflow_for_go go file: string-literal extraction only
internal/cli/version_info.go
no_dataflow_for_go go file: string-literal extraction only
internal/cli/vtp.go
no_dataflow_for_go go file: string-literal extraction only
internal/config/manifest.go
no_dataflow_for_go go file: string-literal extraction only
internal/config/migrate.go
no_dataflow_for_go go file: string-literal extraction only
internal/config/overrides.go
no_dataflow_for_go go file: string-literal extraction only
internal/config/stores.go
no_dataflow_for_go go file: string-literal extraction only
internal/config/stores_lock.go
no_dataflow_for_go go file: string-literal extraction only
internal/fs/atomic.go
no_dataflow_for_go go file: string-literal extraction only
internal/fs/copydir.go
no_dataflow_for_go go file: string-literal extraction only
internal/fs/paths.go
no_dataflow_for_go go file: string-literal extraction only
internal/git/branch.go
no_dataflow_for_go go file: string-literal extraction only
internal/git/changed.go
no_dataflow_for_go go file: string-literal extraction only
internal/git/clone.go
no_dataflow_for_go go file: string-literal extraction only
internal/git/commit.go
no_dataflow_for_go go file: string-literal extraction only
internal/git/merge_driver.go
no_dataflow_for_go go file: string-literal extraction only
internal/index/incremental.go
no_dataflow_for_go go file: string-literal extraction only
internal/index/query.go
no_dataflow_for_go go file: string-literal extraction only
internal/index/ranking.go
no_dataflow_for_go go file: string-literal extraction only
internal/index/rebuild.go
no_dataflow_for_go go file: string-literal extraction only
internal/index/sqlite.go
no_dataflow_for_go go file: string-literal extraction only
internal/lock/lock.go
no_dataflow_for_go go file: string-literal extraction only
internal/logging/logging.go
no_dataflow_for_go go file: string-literal extraction only
internal/markdown/merge.go
no_dataflow_for_go go file: string-literal extraction only
internal/markdown/parse.go
no_dataflow_for_go go file: string-literal extraction only
internal/markdown/section_id.go
no_dataflow_for_go go file: string-literal extraction only
internal/markdown/splice.go
no_dataflow_for_go go file: string-literal extraction only
internal/markdown/validate.go
no_dataflow_for_go go file: string-literal extraction only
internal/mcp/propose.go
no_dataflow_for_go go file: string-literal extraction only
internal/mcp/server.go
no_dataflow_for_go go file: string-literal extraction only
internal/mcp/status.go
no_dataflow_for_go go file: string-literal extraction only
internal/mcp/tools.go
no_dataflow_for_go go file: string-literal extraction only
internal/memory/allowlist.go
no_dataflow_for_go go file: string-literal extraction only
internal/memory/autostage.go
no_dataflow_for_go go file: string-literal extraction only
internal/memory/digest.go
no_dataflow_for_go go file: string-literal extraction only
internal/memory/fetch.go
no_dataflow_for_go go file: string-literal extraction only
internal/memory/fetch_stores.go
no_dataflow_for_go go file: string-literal extraction only
internal/memory/index_gen.go
no_dataflow_for_go go file: string-literal extraction only
internal/memory/jaccard.go
no_dataflow_for_go go file: string-literal extraction only
internal/memory/nonce.go
no_dataflow_for_go go file: string-literal extraction only
internal/memory/operations.go
no_dataflow_for_go go file: string-literal extraction only
internal/memory/pii.go
no_dataflow_for_go go file: string-literal extraction only
internal/memory/provenance.go
no_dataflow_for_go go file: string-literal extraction only
internal/memory/rebase.go
no_dataflow_for_go go file: string-literal extraction only
internal/memory/rejection_log.go
no_dataflow_for_go go file: string-literal extraction only
internal/memory/routing.go
no_dataflow_for_go go file: string-literal extraction only
internal/memory/secrets.go
no_dataflow_for_go go file: string-literal extraction only
internal/memory/staging.go
no_dataflow_for_go go file: string-literal extraction only
internal/memory/status.go
no_dataflow_for_go go file: string-literal extraction only
internal/memory/sweep.go
no_dataflow_for_go go file: string-literal extraction only
internal/memory/sync.go
no_dataflow_for_go go file: string-literal extraction only
internal/memory/update.go
no_dataflow_for_go go file: string-literal extraction only
internal/schema/schema.go
no_dataflow_for_go go file: string-literal extraction only
internal/schema/validate_section.go
no_dataflow_for_go go file: string-literal extraction only
internal/vtp/jcs.go
no_dataflow_for_go go file: string-literal extraction only
internal/vtp/types.go
no_dataflow_for_go go file: string-literal extraction only
internal/vtp/vtp.go
no_dataflow_for_go go file: string-literal extraction only
spikes/s1-byte-preserving-markdown/splice.go
no_dataflow_for_go go file: string-literal extraction only
spikes/s2-mcp-sdk/main.go
no_dataflow_for_go go file: string-literal extraction only
spikes/s3-flock-cross-process/worker.go
no_dataflow_for_go go file: string-literal extraction only
spikes/s4-fts5-incremental/fixtures.go
no_dataflow_for_go go file: string-literal extraction only
spikes/s4-fts5-incremental/index.go
no_dataflow_for_dynamic_code line 102: invoked code is not structurally extracted
eval/behavioural/run.sh
no_dataflow_for_go go file: string-literal extraction only
internal/adapters/claude/claude.go