feat(skills): self-service skill submission with admin review - #36605
feat(skills): self-service skill submission with admin review#36605devin-ai-integration[bot] wants to merge 2 commits into
Conversation
Non-admin registrations land as approval_status=pending_review and disabled, so only skills an admin approves reach marketplace.json and the public Skill Hub. Admins approve or reject with notes through new /claude-code/plugins/{name}/approve and /reject routes, and the Skills page gets a submit button for everyone plus a pending queue with approve and reject for admins.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
|
|
Greptile SummaryThis PR adds self-service Claude Code skill submission, administrator review, ownership-scoped visibility, and publication filtering
Confidence Score: 3/5The PR should not merge until approval is made conditional on the exact skill content the administrator reviewed Concurrent approval and submitter update requests can leave changed manifest content active and publicly visible without review Files Needing Attention: litellm/proxy/anthropic_endpoints/claude_code_endpoints/claude_code_marketplace.py
|
| Filename | Overview |
|---|---|
| litellm/proxy/anthropic_endpoints/claude_code_endpoints/claude_code_marketplace.py | Implements ownership, review, and publication logic, but approval can race with submitter updates and publish unreviewed content |
| litellm/proxy/public_endpoints/public_endpoints.py | Restricts the public Skill Hub to skills that are both approved and enabled |
| litellm-proxy-extras/litellm_proxy_extras/migrations/20260812000000_add_claudecodeplugin_approval_status/migration.sql | Adds nullable review metadata, an active default for legacy rows, and an approval-status index |
| schema.prisma | Adds approval and review fields consistently with the proxy and migration schema copies |
| litellm/proxy/_types.py | Allows authenticated users through route-level checks for submission and owned-resource operations while review routes remain excluded |
| ui/litellm-dashboard/src/app/(dashboard)/skills/_components/ClaudeCodePluginsPanel.tsx | Adds self-service submission controls, administrator review filtering, and review actions |
| ui/litellm-dashboard/src/components/networking.tsx | Adds the dashboard API wrapper for approving and rejecting submitted skills |
Reviews (1): Last reviewed commit: "feat(skills): self-service skill submiss..." | Re-trigger Greptile
| plugin: Final[_PluginRecord] = await repository.table.update( | ||
| where={"name": plugin_name}, # mutable-ok: prisma query arguments must be plain dicts | ||
| data={ # mutable-ok: prisma query arguments must be plain dicts | ||
| "approval_status": approval_status, | ||
| "review_notes": review_notes, | ||
| "reviewed_by": user_api_key_dict.user_id, | ||
| "reviewed_at": reviewed_at, | ||
| "enabled": approval_status == SKILL_ACTIVE, | ||
| "updated_at": reviewed_at, | ||
| }, | ||
| ) |
| existing: Final[_PluginRecord | None] = await repository.table.find_unique( | ||
| where={"name": plugin_name} # mutable-ok: prisma query arguments must be plain dicts | ||
| ) | ||
| if not existing: | ||
| raise _error_response(404, f"Plugin '{plugin_name}' not found") | ||
|
|
||
| if _as_approval_status(existing.approval_status) == approval_status: | ||
| raise _error_response(400, f"Skill '{plugin_name}' is already {approval_status}") | ||
|
|
||
| reviewed_at: Final = datetime.now(timezone.utc) | ||
| plugin: Final[_PluginRecord] = await repository.table.update( |
There was a problem hiding this comment.
Review path queries database directly
_record_review directly reads and updates the table, bypassing the required request-path database helper convention and increasing regression risk.
Rule Used: What: In critical path of request, there should be... (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
TLDR
Problem this solves:
How it solves it:
User Flow
Before: a developer who wants their team's skill on the gateway cannot add it themselves, so an admin has to do every submission by hand
After: the same developer submits it themselves, and it stays private until an admin approves it
"approval_status": "pending_review"and"enabled": falseclaude plugin installcannot pick it up yetAnother user who has nothing to do with the submission cannot see a pending or rejected skill at all: GET http://localhost:4000/claude-code/plugins omits it and GET http://localhost:4000/claude-code/plugins/{name} returns 404 for them, while the submitter and admins can read it
Relevant issues
Linear ticket
Pre-Submission checklist
@greptileaito re-request a review after pushing changes)Delays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Screenshots / Proof of Fix
Captured against a live proxy on localhost:4000 with the Admin UI dev server on localhost:3000, at commit 7652824
Submitting as an internal user, then as an admin:
The pending submission is not served anywhere public, and an unrelated internal user cannot see it:
Enabling an unapproved skill is refused, approving publishes it:
Rejecting keeps it private and stores the note the submitter reads:
UI screenshots for the submit form, the pending badge, the admin review queue, the approve and reject dialogs, and the submitter's view of a rejection are in the docs PR: BerriAI/litellm-docs#870
Type
🆕 New Feature
Caveats (if any)
Final Attestation
Link to Devin session: https://app.devin.ai/sessions/99fe8136193b4643b27bccdea40a1f07
Requested by: @yassin-berriai