feat: add vote_flashcard MCP tool

This commit is contained in:
didier committed 2026-06-05 17:04:05 -04:00
1 parent cd8e872491
commit a40542f7b4
4 files changed
+143

No files matched your search

+87
View File
@@ -54,6 +54,7 @@ func main() {
registerSearchFlashcard(mcpServer, searchEngine)
registerUpdateFlashcard(mcpServer, sqliteStore, searchEngine, logger)
registerDeleteFlashcard(mcpServer, sqliteStore, searchEngine, logger)
registerVoteFlashcard(mcpServer, sqliteStore, logger)
streamableServer := server.NewStreamableHTTPServer(mcpServer)
@@ -334,6 +335,92 @@ func handleDeleteFlashcard(sqlite *sqliteStore, engine SearchEngine, logger *zap
}
}
func registerVoteFlashcard(mcpServer *server.MCPServer, sqlite *sqliteStore, logger *zap.Logger) {
mcpServer.AddTool(
mcp.NewTool("vote_flashcard",
mcp.WithDescription("Vote on a flashcard. Rate it from -3 (worst) to 3 (best). Requires detailed context about who is voting and why."),
mcp.WithNumber("public_id",
mcp.Required(),
mcp.Description("Public identifier of the flashcard to vote on"),
),
mcp.WithNumber("value",
mcp.Required(),
mcp.Description("Vote value from -3 to 3"),
),
mcp.WithString("user",
mcp.Required(),
mcp.Description("Who is voting"),
),
mcp.WithString("repo",
mcp.Required(),
mcp.Description("Repository context for the vote"),
),
mcp.WithString("message",
mcp.Required(),
mcp.Description("Justification or context for the vote"),
),
),
handleVoteFlashcard(sqlite, logger),
)
}
func handleVoteFlashcard(sqlite *sqliteStore, logger *zap.Logger) server.ToolHandlerFunc {
return func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
args, ok := request.Params.Arguments.(map[string]any)
if !ok {
return mcp.NewToolResultError("invalid arguments"), nil
}
publicID := parseInt64(args["public_id"])
if publicID == 0 {
return mcp.NewToolResultError("public_id is required and must be a positive integer"), nil
}
value := int(parseInt64(args["value"]))
if value < -3 || value > 3 {
return mcp.NewToolResultError("value must be between -3 and 3"), nil
}
user := parseOptionalString(args["user"])
if user == "" {
return mcp.NewToolResultError("user is required"), nil
}
repo := parseOptionalString(args["repo"])
if repo == "" {
return mcp.NewToolResultError("repo is required"), nil
}
message := parseOptionalString(args["message"])
if message == "" {
return mcp.NewToolResultError("message is required"), nil
}
latest, err := sqlite.getLatestByPublicID(ctx, publicID)
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("failed to query: %v", err)), nil
}
if latest == nil {
return mcp.NewToolResultError(fmt.Sprintf("no flashcard found with public_id %d", publicID)), nil
}
if latest.DeleteTimestamp != "" {
return mcp.NewToolResultError(fmt.Sprintf("flashcard %d is deleted", publicID)), nil
}
v, err := sqlite.saveVote(ctx, latest.ID, value, user, repo, message)
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("failed to save vote: %v", err)), nil
}
output, err := json.MarshalIndent(v, "", " ")
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("failed to marshal: %v", err)), nil
}
return mcp.NewToolResultText(string(output)), nil
}
}
func handleStoreFlashcard(sqlite *sqliteStore, engine SearchEngine, logger *zap.Logger) server.ToolHandlerFunc {
return func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
args, ok := request.Params.Arguments.(map[string]any)