Skip to content

fix(mcp): return owner's own private prompts from get_prompt + accept category name/slug#1215

Open
fawadafr wants to merge 2 commits into
f:mainfrom
fawadafr:fix/mcp-get-prompt-private-and-category
Open

fix(mcp): return owner's own private prompts from get_prompt + accept category name/slug#1215
fawadafr wants to merge 2 commits into
f:mainfrom
fawadafr:fix/mcp-get-prompt-private-and-category

Conversation

@fawadafr

@fawadafr fawadafr commented Jun 27, 2026

Copy link
Copy Markdown

Two small, self-contained fixes to the MCP server (src/pages/api/mcp.ts), each with tests.

1. get_prompt hides owners' own private prompts

get_prompt hard-filtered isPrivate: false, so an authenticated owner got "Prompt not found" when fetching their own private prompt — even though get_skill and search_prompts already surface owner-private entries. This was an inconsistency between the two read paths.

Extracted a shared buildPromptVisibilityFilter(authenticatedUser)
isPrivate:false OR (isPrivate:true AND authorId === me) for authenticated callers, public-only otherwise — and applied it to both get_prompt and get_skill so they stay in sync.

2. save_prompt / save_skill silently drop unrecognized categories

Category was resolved by exact slug only (findUnique({ where: { slug } })); a category name, a differently-cased slug, or code_review vs code-review was silently ignored, creating an uncategorized prompt with no feedback.

  • New findCategoryMatch() / resolveCategory() match by slug → name → slugified-name (all case-insensitive).
  • On no match, the save still succeeds but the response includes a warning listing valid slugs, instead of silently dropping the value.
  • New list_categories tool so clients can discover valid slugs before saving.
  • Added categorySlug to search_prompts and to the save_* success payloads so a category can be round-tripped.

Tests

src/__tests__/api/mcp-handler.test.ts adds unit coverage for findCategoryMatch (slug / name / slugify / whitespace / no-match) and buildPromptVisibilityFilter (including a regression test that an owner's filter does not hard-exclude their private prompts). All 17 tests pass.

No schema changes, no new dependencies, no breaking changes to existing tool signatures (the category arg now also accepts a name; previously-valid slugs behave identically).

🤖 Generated with Claude Code

Implemented MCP server fixes around prompt visibility and category handling.

  • Fixed get_prompt so authenticated owners can access their own private prompts, while unauthenticated users still only see public prompts.
  • Refactored prompt/skill visibility into a shared filter used by both get_prompt and get_skill.
  • Improved category matching for save_prompt and save_skill to resolve by slug, name, or slugified name in a case-insensitive way.
  • Added warnings when a category can’t be matched, while still allowing saves to succeed.
  • Added list_categories for client-side category lookup.
  • Included categorySlug in search_prompts and save success responses so category values can round-trip.
  • Updated tests to cover category matching and visibility filtering behavior.

fawadafr and others added 2 commits June 27, 2026 07:36
Previously the MCP save tools matched `category` by exact slug only and
silently dropped any unrecognized value, so prompts were created with no
category and no feedback. Clients also had no way to discover valid slugs:
search_prompts exposed only the category name, and there was no listing tool.

- Add `list_categories` tool returning {name, slug, description, promptCount}
- Resolve category by slug, name, or slugified-name (forgiving match)
- Return a `warning` when a category is provided but unmatched, instead of
  failing silently — naming the valid slugs
- Expose `categorySlug` in search_prompts and the save responses for round-trip
- Unit-test the matcher (findCategoryMatch) for the resolution cases

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SxPKXfHMaqgtmitAQC1Zjx
get_prompt hard-filtered `isPrivate: false`, so the authenticated owner got
"Prompt not found" for their own private prompts — while search_prompts and
get_skill already include the owner's private entries. This blocked reading a
private prompt's full content (e.g. to score it against an eval).

Extract buildPromptVisibilityFilter() (public OR owner's-own-private) and use
it in both get_prompt and get_skill, replacing the duplicated inline blocks.
Add unit tests for the helper incl. a regression check.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DL6AEgE5FypVamGVTZhS8a
@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds findCategoryMatch and buildPromptVisibilityFilter exported helpers and an internal resolveCategory function to the MCP API. Applies these across get_prompt, get_skill, save_prompt, and save_skill tools; adds categorySlug to responses; introduces a new list_categories tool; and adds corresponding tests.

MCP Category Support

Layer / File(s) Summary
Category helpers and visibility filter
src/pages/api/mcp.ts
Defines CategoryRow type, findCategoryMatch (multi-step slug/name matching), buildPromptVisibilityFilter (Prisma where fragment), and internal resolveCategory returning { categoryId, warning? }.
Tool updates: get_prompt, get_skill, search_prompts, save_prompt, save_skill
src/pages/api/mcp.ts
Refactors get_prompt and get_skill to use buildPromptVisibilityFilter; adds categorySlug to search_prompts results; updates save_prompt and save_skill to resolve category via resolveCategory and include warning and categorySlug in success responses.
New list_categories MCP tool
src/pages/api/mcp.ts
Adds list_categories tool querying db.category and returning name/slug/description/prompt counts as JSON with error handling.
Tests
src/__tests__/api/mcp-handler.test.ts
Adds category mock to the db mock; adds describe blocks testing findCategoryMatch (exact/case-insensitive/slugified/null cases) and buildPromptVisibilityFilter (unauthenticated, owner, private visibility regression).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • f/prompts.chat#1118: Modifies the same get_prompt DB query path in src/pages/api/mcp.ts, overlapping with the visibility filter refactor in this PR.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the two main changes: fixing owner access to private prompts and allowing category name/slug matching.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/pages/api/mcp.ts`:
- Around line 1424-1445: The list_categories response is leaking hidden prompt
volume through the unfiltered promptCount value. In the category query and
mapping inside the list_categories handler in mcp.ts, either remove promptCount
from the returned category payload or compute it using the same
caller-visibility constraints used by the prompt lookup tools so only visible
prompts are counted.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f5570d71-a251-498e-891e-61513e2c9013

📥 Commits

Reviewing files that changed from the base of the PR and between 75b6f33 and 1e7e42c.

📒 Files selected for processing (2)
  • src/__tests__/api/mcp-handler.test.ts
  • src/pages/api/mcp.ts

Comment thread src/pages/api/mcp.ts
Comment on lines +1424 to +1445
const categories = await db.category.findMany({
orderBy: [{ order: "asc" }, { name: "asc" }],
select: {
name: true,
slug: true,
description: true,
_count: { select: { prompts: true } },
},
});

return {
content: [
{
type: "text" as const,
text: JSON.stringify({
count: categories.length,
categories: categories.map((c) => ({
name: c.name,
slug: c.slug,
description: c.description || null,
promptCount: c._count.prompts,
})),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the Category -> Prompt relation and existing count/filter test coverage.
fd -a 'schema.prisma$' . -x sed -n '/model Category/,/}/p' {}
fd -a 'schema.prisma$' . -x sed -n '/model Prompt/,/}/p' {}
rg -n -C3 'list_categories|_count:\s*\{\s*select:\s*\{\s*prompts|promptCount|buildPromptVisibilityFilter' src/pages/api/mcp.ts src/__tests__/api/mcp-handler.test.ts

Repository: f/prompts.chat

Length of output: 12962


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check whether this repo already uses relation-count filtering elsewhere.
rg -n '_count:\s*\{\s*select:\s*\{\s*[^}]+where:' src

Repository: f/prompts.chat

Length of output: 152


Filter promptCount by caller visibility, or drop it. list_categories currently counts every related prompt, so callers can infer hidden prompt volume from category stats. Use the same visibility filter as the prompt lookup tools.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/api/mcp.ts` around lines 1424 - 1445, The list_categories response
is leaking hidden prompt volume through the unfiltered promptCount value. In the
category query and mapping inside the list_categories handler in mcp.ts, either
remove promptCount from the returned category payload or compute it using the
same caller-visibility constraints used by the prompt lookup tools so only
visible prompts are counted.

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