Files
fastmcp-feedback/src/fastmcp_feedback/mixins/submission.py
T
rsp2k c655949bbb Embed feedback, tool errors, event text and opt-in LLM text for similarity search
EmbeddingSink queues text from each batch on its own bounded queue and
upserts vectors into a new {prefix}embeddings table (pgvector on Postgres,
JSON elsewhere). mw.similar() and mw.similar_feedback() search it.
submit_feedback records a feedback.submitted event; record_llm_call keeps
prompt and completion only with capture_llm_text=True.
2026-09-30 00:22:14 -06:00

123 lines
4.7 KiB
Python

"""Submission mixin for feedback collection."""
from datetime import UTC, datetime
from typing import TYPE_CHECKING, Any
from ..insights import record_feedback_submission
from ..models import SubmitFeedbackRequest, create_feedback_from_request
from .base import (
BaseFeedbackMixin,
FeedbackDatabase,
FeedbackInsights,
logger,
mcp_tool,
)
if TYPE_CHECKING:
from ..instrumentation import InstrumentationMiddleware
class SubmissionMixin(BaseFeedbackMixin):
"""Mixin for feedback submission functionality.
Pass an InstrumentationMiddleware to link each submission to the tool
calls that led up to it and record a ``feedback.submitted`` event for it.
"""
def __init__(
self,
database: FeedbackDatabase,
insights: FeedbackInsights | None = None,
instrumentation: "InstrumentationMiddleware | None" = None,
):
super().__init__(database, insights)
self.instrumentation = instrumentation
@mcp_tool(
description="Submit feedback about your MCP experience - Use this tool when you encounter issues, have suggestions for improvement, or want to report positive experiences. AI models should proactively use this to help improve future MCP sessions. PRIVACY: Only include information you've already shared via previous MCP calls. Ask human permission before including any new sensitive data."
)
async def submit_feedback(self, request: SubmitFeedbackRequest) -> dict[str, Any]:
"""Submit feedback about the MCP server, tools, or interaction experience.
AI models should use this tool to:
- Report when tools don't work as expected (type='bug')
- Suggest improvements to tool functionality (type='improvement')
- Request new capabilities that would be helpful (type='feature')
- Ask questions about unclear tool behavior (type='question')
PRIVACY GUIDELINES:
- Only include information already shared in this MCP session
- Avoid including sensitive data not previously disclosed
- When in doubt, ask the human for permission first
- Focus on the specific technical issue, not broader context
This helps improve the MCP server and tools for future sessions.
Args:
request: Feedback submission request with type, title, description, submitter.
Returns:
Dictionary with submission result including feedback_id if successful.
"""
start_time = datetime.now(UTC)
try:
# Create feedback from request
feedback = create_feedback_from_request(request)
# Save to database
async with self.database.session() as session:
session.add(feedback)
session.commit()
session.refresh(feedback)
feedback_id = str(feedback.id)
# Record analytics (without PII)
if self.insights.enabled:
record_feedback_submission(
self.insights,
feedback_type=request.type,
has_contact=bool(request.contact_info),
title_length=len(request.title),
description_length=len(request.description),
source="mcp_tool",
)
linked_calls = 0
if self.instrumentation is not None:
# Feeds an EmbeddingSink, so similar reports can be found.
self.instrumentation.record_event(
"feedback.submitted",
key=feedback_id,
attrs={
"type": request.type,
"title": request.title,
"description": request.description,
"submitter": request.submitter,
},
)
links = await self.instrumentation.capture_recent_calls()
if await self.instrumentation.link_feedback(feedback_id, links):
linked_calls = len(links)
self._record_tool_usage("submit_feedback", start_time, True)
logger.info(f"Feedback submitted successfully: {feedback_id}")
response = {
"success": True,
"feedback_id": feedback_id,
"message": "Feedback submitted successfully",
}
if self.instrumentation is not None:
response["linked_calls"] = linked_calls
return response
except Exception as e:
logger.error(f"Failed to submit feedback: {e}")
self._record_tool_usage(
"submit_feedback", start_time, False, type(e).__name__
)
return {"success": False, "error": str(e)}