Skip to content

bug(mcp): mem_get_observation discards a found observation when the project is ambiguous #1470

Description

@northems

📝 Bug Description

From a working directory that holds several git repos (so project detection returns ambiguous), mem_get_observation always returns ambiguous_project, even for an ID that exists. The tool has no project argument, so there is no way to satisfy it from the call.

mem_search and mem_context work from the same directory because they accept project. The result is that an agent can see a 300-char preview of a memory but can never read the full content.

mem_update has the same shape. It resolves a write project before loading the observation (resolveWriteProjectWithProcessOverride, around L1689 in internal/mcp/mcp.go) and fails from an ambiguous directory, even though the target is fully identified by id. Its project argument is the new value to set, not a resolution override.

🔄 Steps to Reproduce

  1. Open a Claude Code session in a directory that contains more than one git repo and no .engram/config.json.
  2. mem_search(project: "some-project", query: "...") returns a preview with #1406.
  3. mem_get_observation(id: 1406) returns ambiguous_project.

✅ Expected Behavior

The observation is returned. The ID already identifies the record, and the observation carries its own Project.

❌ Actual Behavior

{"error_code":"ambiguous_project","message":"Cannot determine project: ambiguous project: multiple git repos found in cwd", ...}

GET /observations/1406 on the local HTTP server returns the full observation from the same machine, so the data is there.

Operating System

Windows

Engram Version

2.2.1 (Claude Code plugin 0.1.3)

Agent / Client

Claude Code

📋 Relevant Logs

_No response_

💡 Additional Context

Cause. handleGetObservation in internal/mcp/mcp.go (v2.2.1, around L2240) loads the observation first and only then resolves the project, for the response envelope:

obs, err := s.GetObservation(id)          // succeeds
...
detRes, detErr := resolveReadProjectWithProcessOverride(s, "", cfg.DefaultProject)
...
if detErr != nil {
    return readProjectErrorResult(activity, detRes, detErr), nil   // discards obs
}

The comment there says "No per-call override is possible for get-by-ID". The lookup does not depend on the project. The ambiguity only affects the envelope, but it discards a result that was already found.

Suggested fix. Either of these would solve it:

  • On a detection error, answer with the observation anyway and use obs.Project for the envelope.
  • Accept an optional project argument, as mem_search does, and use it as the override.

Why the directory is ambiguous. We keep the root ambiguous on purpose, as a guard rail, so that no save lands in a guessed project. Every write passes project explicitly. The read-by-ID path is the only one left with no way to comply.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions