Skip to content

feat(mcp): serve notes as MCP resources via their memory:// URLs - #1394

Merged
phernandez merged 12 commits into
mainfrom
feat/note-resources
Aug 30, 2026
Merged

feat(mcp): serve notes as MCP resources via their memory:// URLs#1394
phernandez merged 12 commits into
mainfrom
feat/note-resources

Conversation

@phernandez

Copy link
Copy Markdown
Member

Follow-up to #1389/#1393 (#610 line of work).

What

Every memory:// URL Basic Memory hands out now answers the standard MCP resources/read. New resource template:

memory://{project}/{path*}   →  the note's raw markdown, frontmatter included

The identifier may be a permalink, a title, or a file path — resolved through the same knowledge/resolve endpoint the tools use, so there is no direct file access and no traversal surface.

Why

We advertise memory:// URLs everywhere — the assistant guide, manual pages, conversation handoffs — and some of them were resources (ai_assistant_guide, memory://man, project info) while note URLs returned Unknown resource. A client that tried the standard MCP way to read the very URI we handed it got an error. Same papercut class as the search(3) alias finding on #1389: the URI invites a read we didn't answer.

Overlaps are answered, not fought over

FastMCP's precedence between overlapping template matches is version-sorted, not specificity-sorted — effectively undefined for unversioned templates. So the notes handler behaves correctly whichever template wins:

  • memory://man/... → delegates to the manual page handler (identical bytes either way)
  • memory://{ws}/{proj}/info → served by project_info first; a genuine note named .../info is still read when no such workspace project exists (test covers both directions)

Error behavior

  • Unknown note → ResourceError pointing at search_notes
  • Unknown project → the routing error, surfaced (including the no-cloud-credentials case)
  • Binary file → ResourceError steering to the read_content tool; only text comes back byte-exact

Found and fixed along the way

The new tests read resources through a real client session instead of calling handlers directly — and that immediately exposed a pre-existing break: the memory://{workspace}/{project}/info resource returned a Pydantic model, which FastMCP's resource runtime rejects (contents must be str, bytes, or list[ResourceContent]), so every served read of project info failed on main. It now returns the validated response as JSON text; its direct-call tests parse the JSON.

Tests

tests/mcp/test_note_resources.py reads through a real in-memory client session (live Context, exactly as production) rather than mcp.read_resource() — that distinction caught a session_id explosion during development. Module at 100% coverage.

Not in this PR

  • Workspace-qualified note URIs (memory://workspace/project/path) — the first segment is the project; a qualified fallback is a follow-up.
  • resources/subscribe for live note updates — the story resources unlock that tools can't; future slice.
  • Cloud exposure rides basic-memory-cloud#1900 (same import).

🤖 Generated with Claude Code

https://claude.ai/code/session_014pmKq6bqCi6Zp6BTHuZjrp

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 30, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-30T23:10:22.979141Z 7312652 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a5e4f8d727

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/basic_memory/mcp/resources/notes.py
Comment thread src/basic_memory/mcp/resources/notes.py Outdated
Comment thread src/basic_memory/mcp/resources/notes.py Outdated
phernandez added a commit that referenced this pull request Aug 30, 2026
Codex review of #1394, first pass, all three findings:

- The template treated its first segment as a project unconditionally, so
  an unprefixed permalink like memory://docs/roadmap failed or read the
  wrong project. Reading now goes through resolve_project_and_path — the
  same routing the tools use — so a project prefix routes to the project
  and anything else resolves in the active/default project.
- A note whose canonical permalink ends in /info was unreachable served
  (the {workspace}/{project}/info template wins that shape): project_info
  now falls back to the note when the workspace/project route does not
  resolve, so the extensionless permalink reads whichever template wins.
- ToolError no longer collapses to 'No note': only a confirmed not-found
  gets that message; auth, server, and transport failures keep their
  actionable cause.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pmKq6bqCi6Zp6BTHuZjrp
Signed-off-by: phernandez <paul@basicmachines.co>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2ff3f0e125

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/basic_memory/mcp/resources/notes.py Outdated
Comment thread src/basic_memory/mcp/resources/notes.py Outdated
phernandez added a commit that referenced this pull request Aug 30, 2026
Codex review of #1394, second pass:

- The client was opened for the default project before routing, so a URI
  naming a cloud-mode project resolved entities on the default backend.
  A first segment that names a configured project now opens that
  project's own client (its transport, auth, and workspace), with errors
  surfacing rather than falling back; an unconfigured segment uses the
  default client and resolve_project_and_path's active-project fallback,
  as before.
- Nothing reserves 'man' as a project name: when no manual page matches,
  memory://man/<path> now falls through to a note in a project really
  named man, and when both miss, the manual's hint is the error.
- Entity resolution is strict for resources: a resources/read returns
  the addressed document or an error, never the fuzzy-search guess the
  tools use for suggestions — writing the route test surfaced that a
  miss could fuzzy-match an unrelated note.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pmKq6bqCi6Zp6BTHuZjrp
Signed-off-by: phernandez <paul@basicmachines.co>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a260d1ba73

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/basic_memory/mcp/resources/notes.py Outdated
Comment thread src/basic_memory/mcp/resources/project_info.py
phernandez added a commit that referenced this pull request Aug 30, 2026
…auses

Codex review of #1394, third pass:

- With permalinks_include_project=False the first URI segment is a
  directory even when it collides with a configured project's name — the
  active project owns unprefixed permalinks, so pre-routing now happens
  only when the config says memory URLs carry a project prefix.
- Not-found is now its own type (NoteNotFoundError): the manual-namespace
  and /info-shape fallbacks swap in their own error only when the note is
  confirmed missing; an operational note failure (auth, server,
  transport) keeps its cause through both dispatchers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pmKq6bqCi6Zp6BTHuZjrp
Signed-off-by: phernandez <paul@basicmachines.co>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c5e5c95859

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/basic_memory/mcp/resources/notes.py Outdated
Comment thread src/basic_memory/mcp/resources/notes.py Outdated
phernandez added a commit that referenced this pull request Aug 30, 2026
Codex review of #1394, fourth pass: the not-found mapping covered the
whole routing-and-read block, so a stale configured project whose backend
answers 'Project not found' was reported as a missing note — and the
man/info fallbacks would treat it as a confirmed miss. The mapping now
wraps only the strict entity-resolve call; routing and content-read
failures keep their actionable cause.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pmKq6bqCi6Zp6BTHuZjrp
Signed-off-by: phernandez <paul@basicmachines.co>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fa4ded9178

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/basic_memory/mcp/resources/notes.py Outdated
phernandez added a commit that referenced this pull request Aug 30, 2026
…lient

Codex review of #1394, fifth pass: the pre-routing helper only inspected
local config.projects, so a workspace-qualified URI like
memory://personal/main/docs/report opened the default project's client
and resolved on the wrong transport. Routing now uses the same
detect_project_from_memory_url_prefix the tools call before creating
their client — covering configured local projects and workspace routes
alike — still gated on permalinks_include_project, and with detection
failures surfacing through the existing error mapping.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pmKq6bqCi6Zp6BTHuZjrp
Signed-off-by: phernandez <paul@basicmachines.co>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 436834130e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/basic_memory/mcp/resources/project_info.py

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e6ad6433b7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/basic_memory/mcp/resources/notes.py Outdated
phernandez added a commit that referenced this pull request Aug 30, 2026
Codex review of #1394, seventh pass: the permalinks_include_project gate
skipped prefix detection entirely, so with prefixes disabled a
workspace-qualified URI opened the default project's transport. Cloud
permalinks stay workspace-qualified regardless of that flag (see
test_team_workspace_write_stores_complete_permalink_when_project_prefixes_disabled),
so detection now always runs and the flag drops only a bare
configured-local-project match — the directory-collision case it was
added for.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pmKq6bqCi6Zp6BTHuZjrp
Signed-off-by: phernandez <paul@basicmachines.co>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7361bf6f82

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/basic_memory/mcp/resources/project_info.py Outdated
phernandez added a commit that referenced this pull request Aug 30, 2026
Codex review of #1394, eighth pass: response.json() and model_validate
both raise ValueError, so a reachable info route answering with a
malformed payload fell into the note fallback and could serve unrelated
markdown in place of project statistics. Payload failures now raise a
ResourceError naming the route; the fallback triggers only for routing
failures, as intended.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pmKq6bqCi6Zp6BTHuZjrp
Signed-off-by: phernandez <paul@basicmachines.co>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 58b799d8e0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/basic_memory/mcp/resources/notes.py
Comment thread src/basic_memory/mcp/resources/project_info.py Outdated
phernandez added a commit that referenced this pull request Aug 30, 2026
Codex review of #1394, ninth pass: forced-local transports
(streamable-http, sse) surface an unknown compound workspace/project
route as ToolError rather than ValueError/RuntimeError, so the /info
note fallback never ran and an extensionless info-note URI was
unreadable on those transports. A ToolError naming a missing route now
enters the fallback; every other ToolError keeps its cause.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pmKq6bqCi6Zp6BTHuZjrp
Signed-off-by: phernandez <paul@basicmachines.co>
phernandez and others added 11 commits August 30, 2026 17:55
The memory://{workspace}/{project}/info resource returned a Pydantic
model, which FastMCP's resource runtime rejects (contents must be str,
bytes, or list[ResourceContent]) — every served read failed; its tests
only ever called the handler directly, so nothing noticed. Return the
validated response as JSON text and parse it in the direct-call tests.
Found by the note-resource tests, which read through a real session.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pmKq6bqCi6Zp6BTHuZjrp
Signed-off-by: phernandez <paul@basicmachines.co>
Basic Memory hands out memory:// URLs everywhere — pages, prompts,
handoffs — but only the guide, the manual, and project info answered
resources/read; a note URL returned Unknown resource. Close the gap:

- memory://{project}/{path*} returns the note's raw markdown exactly as
  it sits on disk, frontmatter included. The identifier may be a
  permalink, a title, or a file path (resolved through the same
  knowledge/resolve endpoint the tools use, so there is no direct file
  access and no traversal surface).
- Overlaps are answered, not fought over: template precedence between
  overlapping matches is not guaranteed, so memory://man/... delegates
  to the manual and {workspace}/{project}/info-shaped URIs are served by
  project_info first, falling back to a genuine note named .../info when
  no such workspace project exists.
- Unknown notes raise a ResourceError pointing at search_notes; unknown
  projects surface the routing error; binary files are steered to the
  read_content tool (only text is byte-exact).
- Server instructions note that any memory://<project>/<path> URL reads
  as a resource.

Tests exercise the served path through a real client session (a live
Context, as production has), plus the man and info overlaps, the
info-named-note fallback, error branches, and the binary steer; the new
module is at 100% coverage.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pmKq6bqCi6Zp6BTHuZjrp
Signed-off-by: phernandez <paul@basicmachines.co>
Codex review of #1394, first pass, all three findings:

- The template treated its first segment as a project unconditionally, so
  an unprefixed permalink like memory://docs/roadmap failed or read the
  wrong project. Reading now goes through resolve_project_and_path — the
  same routing the tools use — so a project prefix routes to the project
  and anything else resolves in the active/default project.
- A note whose canonical permalink ends in /info was unreachable served
  (the {workspace}/{project}/info template wins that shape): project_info
  now falls back to the note when the workspace/project route does not
  resolve, so the extensionless permalink reads whichever template wins.
- ToolError no longer collapses to 'No note': only a confirmed not-found
  gets that message; auth, server, and transport failures keep their
  actionable cause.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pmKq6bqCi6Zp6BTHuZjrp
Signed-off-by: phernandez <paul@basicmachines.co>
Codex review of #1394, second pass:

- The client was opened for the default project before routing, so a URI
  naming a cloud-mode project resolved entities on the default backend.
  A first segment that names a configured project now opens that
  project's own client (its transport, auth, and workspace), with errors
  surfacing rather than falling back; an unconfigured segment uses the
  default client and resolve_project_and_path's active-project fallback,
  as before.
- Nothing reserves 'man' as a project name: when no manual page matches,
  memory://man/<path> now falls through to a note in a project really
  named man, and when both miss, the manual's hint is the error.
- Entity resolution is strict for resources: a resources/read returns
  the addressed document or an error, never the fuzzy-search guess the
  tools use for suggestions — writing the route test surfaced that a
  miss could fuzzy-match an unrelated note.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pmKq6bqCi6Zp6BTHuZjrp
Signed-off-by: phernandez <paul@basicmachines.co>
…auses

Codex review of #1394, third pass:

- With permalinks_include_project=False the first URI segment is a
  directory even when it collides with a configured project's name — the
  active project owns unprefixed permalinks, so pre-routing now happens
  only when the config says memory URLs carry a project prefix.
- Not-found is now its own type (NoteNotFoundError): the manual-namespace
  and /info-shape fallbacks swap in their own error only when the note is
  confirmed missing; an operational note failure (auth, server,
  transport) keeps its cause through both dispatchers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pmKq6bqCi6Zp6BTHuZjrp
Signed-off-by: phernandez <paul@basicmachines.co>
Codex review of #1394, fourth pass: the not-found mapping covered the
whole routing-and-read block, so a stale configured project whose backend
answers 'Project not found' was reported as a missing note — and the
man/info fallbacks would treat it as a confirmed miss. The mapping now
wraps only the strict entity-resolve call; routing and content-read
failures keep their actionable cause.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pmKq6bqCi6Zp6BTHuZjrp
Signed-off-by: phernandez <paul@basicmachines.co>
…lient

Codex review of #1394, fifth pass: the pre-routing helper only inspected
local config.projects, so a workspace-qualified URI like
memory://personal/main/docs/report opened the default project's client
and resolved on the wrong transport. Routing now uses the same
detect_project_from_memory_url_prefix the tools call before creating
their client — covering configured local projects and workspace routes
alike — still gated on permalinks_include_project, and with detection
failures surfacing through the existing error mapping.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pmKq6bqCi6Zp6BTHuZjrp
Signed-off-by: phernandez <paul@basicmachines.co>
The resource tests lived in tests/mcp with an in-memory client and a few
monkeypatched paths; nothing in test-int drove resources/list and
resources/read through the full MCP Client -> server -> FastAPI ->
database flow. Cover the manual (listing, index, page spellings), a note
read back at its project-prefixed, unprefixed, and file-path URIs, the
workspace info template, and a miss surfacing as a missing-note error.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pmKq6bqCi6Zp6BTHuZjrp
Signed-off-by: phernandez <paul@basicmachines.co>
Codex review of #1394, seventh pass: the permalinks_include_project gate
skipped prefix detection entirely, so with prefixes disabled a
workspace-qualified URI opened the default project's transport. Cloud
permalinks stay workspace-qualified regardless of that flag (see
test_team_workspace_write_stores_complete_permalink_when_project_prefixes_disabled),
so detection now always runs and the flag drops only a bare
configured-local-project match — the directory-collision case it was
added for.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pmKq6bqCi6Zp6BTHuZjrp
Signed-off-by: phernandez <paul@basicmachines.co>
Codex review of #1394, eighth pass: response.json() and model_validate
both raise ValueError, so a reachable info route answering with a
malformed payload fell into the note fallback and could serve unrelated
markdown in place of project statistics. Payload failures now raise a
ResourceError naming the route; the fallback triggers only for routing
failures, as intended.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pmKq6bqCi6Zp6BTHuZjrp
Signed-off-by: phernandez <paul@basicmachines.co>
Codex review of #1394, ninth pass: forced-local transports
(streamable-http, sse) surface an unknown compound workspace/project
route as ToolError rather than ValueError/RuntimeError, so the /info
note fallback never ran and an extensionless info-note URI was
unreadable on those transports. A ToolError naming a missing route now
enters the fallback; every other ToolError keeps its cause.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pmKq6bqCi6Zp6BTHuZjrp
Signed-off-by: phernandez <paul@basicmachines.co>
@phernandez
phernandez force-pushed the feat/note-resources branch from a3ff0c3 to e7ec6d4 Compare August 30, 2026 22:56

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e7ec6d49ca

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/basic_memory/mcp/resources/notes.py
Codex review of #1394, tenth pass: the man template registers before the
notes template and wins the tie for memory://man/..., so the note
fallback sitting in note_resource never ran on the served path — a
project genuinely named man was unreachable through resources/read.
manual_page now owns the fallback (confirmed note miss restores the
manual's index hint; operational failures keep their cause) and the
notes handler simply delegates, keeping both templates' answers
identical whichever one matches.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pmKq6bqCi6Zp6BTHuZjrp
Signed-off-by: phernandez <paul@basicmachines.co>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7312652431

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/basic_memory/mcp/resources/man.py
@phernandez
phernandez merged commit c8881e7 into main Aug 30, 2026
29 checks passed
phernandez added a commit that referenced this pull request Aug 30, 2026
Codex review of #1394, first pass, all three findings:

- The template treated its first segment as a project unconditionally, so
  an unprefixed permalink like memory://docs/roadmap failed or read the
  wrong project. Reading now goes through resolve_project_and_path — the
  same routing the tools use — so a project prefix routes to the project
  and anything else resolves in the active/default project.
- A note whose canonical permalink ends in /info was unreachable served
  (the {workspace}/{project}/info template wins that shape): project_info
  now falls back to the note when the workspace/project route does not
  resolve, so the extensionless permalink reads whichever template wins.
- ToolError no longer collapses to 'No note': only a confirmed not-found
  gets that message; auth, server, and transport failures keep their
  actionable cause.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pmKq6bqCi6Zp6BTHuZjrp
Signed-off-by: phernandez <paul@basicmachines.co>
phernandez added a commit that referenced this pull request Aug 30, 2026
Codex review of #1394, second pass:

- The client was opened for the default project before routing, so a URI
  naming a cloud-mode project resolved entities on the default backend.
  A first segment that names a configured project now opens that
  project's own client (its transport, auth, and workspace), with errors
  surfacing rather than falling back; an unconfigured segment uses the
  default client and resolve_project_and_path's active-project fallback,
  as before.
- Nothing reserves 'man' as a project name: when no manual page matches,
  memory://man/<path> now falls through to a note in a project really
  named man, and when both miss, the manual's hint is the error.
- Entity resolution is strict for resources: a resources/read returns
  the addressed document or an error, never the fuzzy-search guess the
  tools use for suggestions — writing the route test surfaced that a
  miss could fuzzy-match an unrelated note.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pmKq6bqCi6Zp6BTHuZjrp
Signed-off-by: phernandez <paul@basicmachines.co>
phernandez added a commit that referenced this pull request Aug 30, 2026
…auses

Codex review of #1394, third pass:

- With permalinks_include_project=False the first URI segment is a
  directory even when it collides with a configured project's name — the
  active project owns unprefixed permalinks, so pre-routing now happens
  only when the config says memory URLs carry a project prefix.
- Not-found is now its own type (NoteNotFoundError): the manual-namespace
  and /info-shape fallbacks swap in their own error only when the note is
  confirmed missing; an operational note failure (auth, server,
  transport) keeps its cause through both dispatchers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pmKq6bqCi6Zp6BTHuZjrp
Signed-off-by: phernandez <paul@basicmachines.co>
phernandez added a commit that referenced this pull request Aug 30, 2026
Codex review of #1394, fourth pass: the not-found mapping covered the
whole routing-and-read block, so a stale configured project whose backend
answers 'Project not found' was reported as a missing note — and the
man/info fallbacks would treat it as a confirmed miss. The mapping now
wraps only the strict entity-resolve call; routing and content-read
failures keep their actionable cause.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pmKq6bqCi6Zp6BTHuZjrp
Signed-off-by: phernandez <paul@basicmachines.co>
phernandez added a commit that referenced this pull request Aug 30, 2026
…lient

Codex review of #1394, fifth pass: the pre-routing helper only inspected
local config.projects, so a workspace-qualified URI like
memory://personal/main/docs/report opened the default project's client
and resolved on the wrong transport. Routing now uses the same
detect_project_from_memory_url_prefix the tools call before creating
their client — covering configured local projects and workspace routes
alike — still gated on permalinks_include_project, and with detection
failures surfacing through the existing error mapping.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pmKq6bqCi6Zp6BTHuZjrp
Signed-off-by: phernandez <paul@basicmachines.co>
phernandez added a commit that referenced this pull request Aug 30, 2026
Codex review of #1394, seventh pass: the permalinks_include_project gate
skipped prefix detection entirely, so with prefixes disabled a
workspace-qualified URI opened the default project's transport. Cloud
permalinks stay workspace-qualified regardless of that flag (see
test_team_workspace_write_stores_complete_permalink_when_project_prefixes_disabled),
so detection now always runs and the flag drops only a bare
configured-local-project match — the directory-collision case it was
added for.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pmKq6bqCi6Zp6BTHuZjrp
Signed-off-by: phernandez <paul@basicmachines.co>
phernandez added a commit that referenced this pull request Aug 30, 2026
Codex review of #1394, eighth pass: response.json() and model_validate
both raise ValueError, so a reachable info route answering with a
malformed payload fell into the note fallback and could serve unrelated
markdown in place of project statistics. Payload failures now raise a
ResourceError naming the route; the fallback triggers only for routing
failures, as intended.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pmKq6bqCi6Zp6BTHuZjrp
Signed-off-by: phernandez <paul@basicmachines.co>
@phernandez
phernandez deleted the feat/note-resources branch August 30, 2026 23:32
phernandez added a commit that referenced this pull request Aug 30, 2026
Codex review of #1394, ninth pass: forced-local transports
(streamable-http, sse) surface an unknown compound workspace/project
route as ToolError rather than ValueError/RuntimeError, so the /info
note fallback never ran and an extensionless info-note URI was
unreadable on those transports. A ToolError naming a missing route now
enters the fallback; every other ToolError keeps its cause.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pmKq6bqCi6Zp6BTHuZjrp
Signed-off-by: phernandez <paul@basicmachines.co>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant