Repository navigation
MCP resources: decks, due-today, recent (#166) - #186
Merged
Merged
Conversation
Spec for exposing decks, due-today, and recent as MCP resources so clients with @-pickers (Claude Code, Claude Desktop) can pin them into conversations. Also resolves resources/list and resources/templates/list 'method not implemented' log noise. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
12 TDD tasks covering McpResourceService rendering, URI helpers, handler registration in Program.cs, and integration tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lists active decks plus two static resources (due-today, recent) for the authenticated user. Render methods stubbed; subsequent tasks fill them in. Refs #166 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Implements RenderDeckAsync with header (name, counts, description), **Front:** / **Back:** card blocks, source footer, and SVG note. Empty and missing decks handled. Refs #166 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Refs #166 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Refs #166 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cards due now (or new), suspended cards/decks excluded, grouped by first alphabetical deck name with a (no deck) bucket at the end. Refs #166 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously the '## DeckName (N cards)' header was written before the inner card loop, so if truncation fired on the group's first card the header appeared with nothing under it. Now the header is written lazily, right before the first successful card append. Also adds the "rendered > 0" guard to match AppendCardsWithTruncation so a single oversized card still renders. Refs #166 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Excludes suspended cards. Each block carries Created date and first non-suspended deck name as metadata. Refs #166 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Refs #166 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Refs #166 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wires resources/list, resources/templates/list, resources/read into the MCP server via AddFasoltResources(). Server now advertises the resources capability. Eliminates 'method not implemented' log noise for clients that probe these endpoints. Refs #166 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
McpUserResolver throws UnauthorizedAccessException; before this fix it would propagate as a 500 from the resource handlers (the tool path is already covered by AddCallToolFilter). Wraps the resolve call in a small helper that re-throws as McpProtocolException with McpErrorCode.InvalidRequest so the client gets a clean JSON-RPC error. Refs #166 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Confirms McpResourceService resolves from the real DI container and produces the expected entries for a seeded user, and that the /mcp endpoint rejects unauthenticated callers. Refs #166 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wraps the assertions in try/finally so the DELETE always runs, avoiding orphaned rows in the shared test database when an assertion throws. Refs #166 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removes the ReviewService constructor parameter that was scaffolded in Task 1 but never used; the EF queries in the render methods went direct to AppDbContext. Test factory updated to match. Also updates the design spec: the per-card source footer is now '*Source: <file>*' without the '→ heading' suffix the original draft implied, since Card.SourceHeading is not part of the data model. Refs #166 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The tool path already does this via AddCallToolFilter in Program.cs; resource handlers had no equivalent so an unexpected exception (e.g. DB hiccup, EF projection failure) would surface to the SDK with no Seq log entry. Matches the tool pattern: info on each call, error on non-McpException / non-cancellation throws. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resource render queries selected the full FrontSvg/BackSvg columns (up to ~1MB each) only to null-check them for the "[has SVG image]" placeholder. Project a HasSvg boolean instead so the payloads stay in Postgres. Also add a hard pre-render cap to RenderDueTodayAsync (QueryHardCap, matching RenderRecentAsync) with soonest-due ordering so a large due backlog no longer materializes in full. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resource summaries/footers read counts from a separate COUNT query instead of the materialized page, so they stay accurate when the result set exceeds the 200-row page cap (e.g. "Showing 100 of 250 cards"). Also fix the deck list description to count only non-suspended cards, matching the rendered deck body. Refs #166 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #166.
Summary
Implements MCP resources for the Fasolt MCP server:
fasolt://deck/{deckId}— one entry per active deck the user ownsfasolt://due-today— singleton, cards due today grouped by deckfasolt://recent— singleton, cards created in the last 7 days, newest firstClients with
@-resource pickers (Claude Code, Claude Desktop) can now attach these into a conversation by deck name. The deck mention syntax in Claude Code is@fasolt:fasolt://deck/<id>, surfaced in the autocomplete by human deck name.Side effect: the
resources/listandresources/templates/list"method not implemented" log lines fromcodex-mcp-clientare gone — those endpoints now have real handlers.Architecture
Pure handler-based MCP registration via
WithListResourcesHandler,WithListResourceTemplatesHandler,WithReadResourceHandlerin a newAddFasoltResources()extension. All rendering logic lives inMcpResourceService(unit-testable). Auth pattern matches existing tools (McpUserResolver.GetUserId), with a localized try/catch that translatesUnauthorizedAccessExceptioninto a clean MCP error.Files:
fasolt.Server/Application/Services/McpResourceService.cs— rendering + queriesfasolt.Server/Api/McpResources/ResourceUris.cs— URI constants + parserfasolt.Server/Api/McpResources/McpResourceHandlers.cs— handler delegate registrationfasolt.Server/Program.cs—.AddFasoltResources()in theAddMcpServerchain +# Resourcesnote inServerInstructionsContent shape
Each card renders as:
```markdown
Front: essen
Back: to eat
Source: notes/german/verbs.md
```
Back:block omitted if back is empty*Source: ...*footer omitted if no source file[has SVG image — use get_card for full content](text-focused, keeps content bounded)Truncation: 100 cards / 80 KB budget per resource with
*Showing X of Y cards.*footer.Test plan
fasolt.Tests/McpResourceServiceTests.cscover deck/due-today/recent rendering, truncation, suspended-card and suspended-deck filtering, source/SVG handling, cross-user isolation, deck grouping with(no deck)bucket, lazy group-header emission on truncation boundaries.fasolt.Tests/McpResourceEndpointTests.csconfirmMcpResourceServiceresolves from the real DI container with a seeded user, and/mcprejects unauthenticated callers.References
docs/superpowers/specs/2026-05-28-mcp-resources-design.mddocs/superpowers/plans/2026-05-28-mcp-resources.md🤖 Generated with Claude Code