Skip to content

feat(mcp): add Resources for push-based context delivery#84

Merged
wcatz merged 3 commits intomainfrom
feat/mcp-resources
Mar 20, 2026
Merged

feat(mcp): add Resources for push-based context delivery#84
wcatz merged 3 commits intomainfrom
feat/mcp-resources

Conversation

@wcatz
Copy link
Copy Markdown
Owner

@wcatz wcatz commented Mar 20, 2026

Summary

Adds two MCP Resources to the Ghost server, closing the gap between the TUI (push-based memory injection) and MCP clients like Claude Code (previously pull-only).

Problem

When Claude Code uses Ghost via MCP, memories are only loaded when Claude actively calls ghost_project_context. After context compaction, Claude may not know to re-call it — losing session context silently. The TUI doesn't have this problem because BuildSystemBlocks injects memories into every request automatically.

Solution

MCP Resources can be pinned by clients so their content is automatically included in every request — the protocol-correct equivalent of Block 3 in the TUI prompt.

New Resources

ghost://project/{project_id}/context (ResourceTemplate)

  • Returns top 20 memories + learned context for a project
  • project_id resolves by name (e.g. dingo) or hash ID
  • Includes _global memories automatically (same as GetTopMemories)
  • Pin this in Claude Code: always available, survives compaction

ghost://memories/global (Resource)

  • Returns all cross-project memories saved via ghost_save_global
  • Personal preferences, global conventions, toolchain facts

Changes

  • internal/mcpserver/mcpserver.goregisterResources() + buildProjectContext() helper
  • internal/mcpserver/mcpserver_test.go — 5 new tests
  • docs/architecture.md — updated MCP data flow diagram

Tests

All green, go vet clean.

Summary by CodeRabbit

  • New Features

    • Added MCP resources to fetch project context and global memories via URI-style resources.
    • Client/extension now aborts prior streaming requests before starting new ones.
  • Improvements

    • Session streaming now cleanly supersedes prior streams instead of rejecting requests.
    • Unified VS Code keybinding to a single shortcut across platforms.
  • Tests

    • Added tests covering project-context and global-memory assembly and resource behavior.
  • Documentation

    • Updated architecture docs to reflect expanded MCP interface and resources.

wcatz added 2 commits March 20, 2026 06:05
- Server: replace 409 rejection with context cancellation of active stream
- VSCode: abort in-flight request before sending new message
- Keybinding: simplify ghost.openEditor to alt+g
Adds two MCP Resources to the Ghost server alongside the existing tools:

- ghost://project/{project_id}/context (ResourceTemplate)
  Returns top 20 memories + learned context for a project.
  project_id resolves by name or hash ID.
  Clients (Claude Code) can pin this resource so it survives
  context compaction without requiring a tool call.

- ghost://memories/global (Resource)
  Returns all cross-project memories saved via ghost_save_global.
  Personal preferences, global conventions, toolchain facts.

Also extracts buildProjectContext() helper for testability.
Adds 5 tests covering: memories, empty state, global inclusion,
learned context, and panic-free registration.

Fixes the core gap: MCP was pull-only (Claude must ask), while
the TUI pushes memories into every prompt via BuildSystemBlocks.
Resources make Ghost's memory reliably available to external
clients after context compaction.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 20, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7456ffcf-b4bb-46ea-8a1a-8567ece262f5

📥 Commits

Reviewing files that changed from the base of the PR and between 2acc6ba and 1d352f0.

📒 Files selected for processing (3)
  • docs/architecture.md
  • internal/mcpserver/mcpserver.go
  • internal/mcpserver/mcpserver_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/mcpserver/mcpserver_test.go
  • docs/architecture.md

📝 Walkthrough

Walkthrough

Adds two MCP resources for project and global memory retrieval, centralizes project-context assembly, registers resources during server init, updates chat streaming to use per-request cancelable contexts that supersede prior streams, and tweaks VS Code extension keybinding and client-side abort behavior.

Changes

Cohort / File(s) Summary
MCP docs
docs/architecture.md
Documented expanded MCP interface: additional tools and two new pushable/pinnable resources (ghost://project/{project_id}/context, ghost://memories/global) and updated sequence flow.
MCP server implementation & tests
internal/mcpserver/mcpserver.go, internal/mcpserver/mcpserver_test.go
Added registerResources() and buildProjectContext(); resource handlers for ghost://project/{project_id}/context and ghost://memories/global; centralized project-context assembly using store lookups; comprehensive tests validating assembled context, learned context, and global memory inclusion.
Chat streaming lifecycle
internal/server/chat.go
chatState now holds a per-request context.CancelFunc; handlers derive a cancellable ctx from request context; new streams supersede previous ones by calling previous cancel(); cancellation/select logic updated to use derived ctx.
VS Code extension (client)
vscode-ghost/package.json, vscode-ghost/src/chat-webview.ts
Unified keybinding for ghost.openEditor to alt+g across platforms; ChatWebview.handleSend now calls this.abortFn?.() to cancel any in-flight stream before starting a new one.

Sequence Diagram

sequenceDiagram
    participant Client as MCP Client
    participant Server as MCP Server
    participant Store as Memory Store

    Client->>Server: Request resource ghost://project/{id}/context
    activate Server
    Server->>Server: Parse URI → project_id
    Server->>Store: GetTopMemories(ctx, project_id, 20)
    Store-->>Server: Project memories
    Server->>Store: GetLearnedContext(ctx, project_id)
    Store-->>Server: Learned context
    Server->>Server: buildProjectContext() → assembled text
    Server-->>Client: Project context resource
    deactivate Server

    Client->>Server: Request resource ghost://memories/global
    activate Server
    Server->>Store: GetTopMemories(ctx, "_global", 50)
    Store-->>Server: Global memories
    Server->>Server: Format memory list
    Server-->>Client: Global memories resource
    deactivate Server
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰
In burrows bright I hop and sing,
New resources bloom, memories bring,
Project context, global store,
Streams reset and run once more,
Hooray for tiny rabbit things! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding MCP Resources for push-based context delivery, which is the primary focus of the PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/mcp-resources
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
internal/mcpserver/mcpserver_test.go (1)

257-268: TestNew_RegistersResources should assert registration effects, not just construction.

This currently overlaps with TestNew_CreatesServer and won’t fail if resource registration is accidentally removed but New() still succeeds. Consider asserting a read/discovery path for both resource URIs.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/mcpserver/mcpserver_test.go` around lines 257 - 268, Update
TestNew_RegistersResources to verify that New() actually registers resources
instead of only constructing the server: after creating store and logger and
calling New(store, logger) (symbol New and variable srv), assert that known
resource endpoints are discoverable by invoking the server's discovery/read
handlers or making test HTTP requests against the server/router for the expected
resource URIs (use the same URIs used elsewhere in the package), and check
responses/status codes or returned resource metadata to confirm registration;
fail the test if the discovery/read path for each expected resource URI is
missing or returns an error.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/architecture.md`:
- Line 78: The docs reference to internal/mcpserver/server.go is stale; update
the package map entry in docs/architecture.md to point to the actual
implementation file internal/mcpserver/mcpserver.go (replace server.go with
mcpserver.go) so links/navigation resolve correctly and the MCP server entry
matches the real symbol.

In `@internal/mcpserver/mcpserver.go`:
- Line 588: The code currently calls s.store.GetTopMemories(ctx, "_global", 50)
which only returns the top 50 global memories and truncates "all" cross-project
memories; update the handler to retrieve the full set instead of a fixed 50.
Replace the single GetTopMemories call with either (a) a call to a store method
that returns all global memories (e.g., s.store.GetAllMemories(ctx, "_global")
or similarly named method), or (b) implement pagination by repeatedly calling
s.store.GetTopMemories (or a paginated store API) and appending results to the
memories slice until no more results are returned; ensure the variable memories
still holds the complete set before further processing. Reference:
s.store.GetTopMemories and the memories variable in this handler.
- Around line 555-572: The handler currently calls s.buildProjectContext(ctx,
projectID) which swallows datastore/read errors and returns an empty string,
making real errors look like an empty project; change buildProjectContext to
return (string, error) and update the ReadResource handler(s) to check that
error and return a non-OK error (wrapped with context) instead of embedding an
empty Text field—specifically update the anonymous ReadResource func shown and
the analogous handler around lines 611-624 to call buildProjectContext(ctx,
projectID), handle a non-nil error by returning nil, fmt.Errorf("reading project
context %q: %w", projectID, err), and only populate ResourceContents.Text when
err == nil. Ensure you propagate the underlying store error (not mask it) and
keep resolveProjectID usage unchanged.

---

Nitpick comments:
In `@internal/mcpserver/mcpserver_test.go`:
- Around line 257-268: Update TestNew_RegistersResources to verify that New()
actually registers resources instead of only constructing the server: after
creating store and logger and calling New(store, logger) (symbol New and
variable srv), assert that known resource endpoints are discoverable by invoking
the server's discovery/read handlers or making test HTTP requests against the
server/router for the expected resource URIs (use the same URIs used elsewhere
in the package), and check responses/status codes or returned resource metadata
to confirm registration; fail the test if the discovery/read path for each
expected resource URI is missing or returns an error.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6c537d30-7cfc-485e-9209-074f79e97916

📥 Commits

Reviewing files that changed from the base of the PR and between eca2dde and 2acc6ba.

📒 Files selected for processing (6)
  • docs/architecture.md
  • internal/mcpserver/mcpserver.go
  • internal/mcpserver/mcpserver_test.go
  • internal/server/chat.go
  • vscode-ghost/package.json
  • vscode-ghost/src/chat-webview.ts

Comment thread docs/architecture.md Outdated
Comment thread internal/mcpserver/mcpserver.go
"toolchain facts. These apply to all projects. " +
"Add entries via the ghost_save_global tool.",
}, func(ctx context.Context, req *mcp.ReadResourceRequest) (*mcp.ReadResourceResult, error) {
memories, err := s.store.GetTopMemories(ctx, "_global", 50)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Global memories resource is capped at 50, not “all.”

The handler fetches only top 50 global memories. If the intended behavior is truly “all cross-project memories,” this truncates output and can drop important pinned context.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/mcpserver/mcpserver.go` at line 588, The code currently calls
s.store.GetTopMemories(ctx, "_global", 50) which only returns the top 50 global
memories and truncates "all" cross-project memories; update the handler to
retrieve the full set instead of a fixed 50. Replace the single GetTopMemories
call with either (a) a call to a store method that returns all global memories
(e.g., s.store.GetAllMemories(ctx, "_global") or similarly named method), or (b)
implement pagination by repeatedly calling s.store.GetTopMemories (or a
paginated store API) and appending results to the memories slice until no more
results are returned; ensure the variable memories still holds the complete set
before further processing. Reference: s.store.GetTopMemories and the memories
variable in this handler.

- Fix stale server.go reference in architecture.md → mcpserver.go
- buildProjectContext now returns (string, error), propagates store errors
  instead of silently returning empty string
- Update global resource description: 'top 50' not 'all' (honest cap)
- Strengthen TestNew_RegistersResources to exercise handler logic,
  not just check server construction
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