Skip to main content
Glama

preference.hear

Collect user preferences for web design by presenting samples and updating profiles based on feedback to personalize recommendations.

Instructions

ユーザー嗜好ヒアリングセッション。feedbackなしでサンプル提示、feedbackありで嗜好プロファイル更新。User preference hearing session. Present samples without feedback, update profile with feedback.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
profile_idNoプロファイルID(省略時は新規作成) / Profile ID (create new if omitted)
feedbackNoフィードバック配列(存在する場合はモードB) / Feedback array (Mode B if present)
preference_textNo嗜好テキスト(Claudeエージェントが自然言語フィードバックから生成、10-1000文字) / Preference text (generated by Claude agent from natural language feedback, 10-1000 chars)
limitNo返却サンプル数(デフォルト1件) / Number of samples to return (default 1)
offsetNoスキップ数 / Number of samples to skip
exclude_idsNo除外するサンプルID配列(既評価済み) / Sample IDs to exclude (already evaluated)

Implementation Reference

  • The main handler function for the 'preference.hear' tool, which processes either a request for design samples (Mode A) or feedback submission (Mode B) by delegating to the Preference Service.
    export async function preferenceHearHandler(input: unknown): Promise<PreferenceHearOutput> {
      if (isDevelopment()) {
        logger.info("[MCP Tool] preference.hear called", {
          hasFeedback: !!(input as Record<string, unknown>)?.feedback,
        });
      }
    
      // 入力バリデーション / Input validation
      let validated: PreferenceHearInput;
      try {
        validated = preferenceHearInputSchema.parse(input);
      } catch (error) {
        if (error instanceof ZodError) {
          const errorMessage = error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ");
    
          logger.warn("[MCP Tool] preference.hear validation error", {
            errors: error.errors,
          });
    
          return {
            success: false,
            error: {
              code: PREFERENCE_MCP_ERROR_CODES.VALIDATION_ERROR,
              message: `Validation error: ${errorMessage}`,
            },
          };
        }
        throw error;
      }
    
      // サービスファクトリーチェック / Service factory check
      if (!preferenceServiceFactory) {
        logger.warn("[MCP Tool] preference.hear service factory not set");
    
        return {
          success: false,
          error: {
            code: PREFERENCE_MCP_ERROR_CODES.SERVICE_UNAVAILABLE,
            message: "Preference service is not available",
          },
        };
      }
    
      const service = preferenceServiceFactory();
    
      try {
        // モード判定: feedbackの有無で分岐
        // Mode determination: branch on feedback presence
        if (
          validated.feedback &&
          validated.feedback.length > 0 &&
          validated.preference_text &&
          validated.profile_id
        ) {
          // モードB: フィードバック受信 / Mode B: Receive feedback
          if (isDevelopment()) {
            logger.info("[MCP Tool] preference.hear Mode B: processing feedback", {
              feedbackCount: validated.feedback.length,
              preferenceTextLength: validated.preference_text.length,
            });
          }
    
          const result = await service.processFeedback(
            validated.profile_id,
            validated.feedback,
            validated.preference_text
          );
    
          if (isDevelopment()) {
            logger.info("[MCP Tool] preference.hear Mode B completed", {
              profileId: truncateId(result.profile_id),
              interactionCount: result.interaction_count,
            });
          }
    
          return {
            success: true,
            data: result,
          };
        } else {
          // モードA: サンプル提示 / Mode A: Present samples
          if (isDevelopment()) {
            logger.info("[MCP Tool] preference.hear Mode A: getting samples", {
              profileId: truncateId(validated.profile_id),
              limit: validated.limit,
              offset: validated.offset,
              excludeIds: validated.exclude_ids,
            });
          }
    
          const result = await service.getSamples({
            profileId: validated.profile_id,
            limit: validated.limit,
            offset: validated.offset,
            excludeIds: validated.exclude_ids,
          });
    
          if (isDevelopment()) {
            logger.info("[MCP Tool] preference.hear Mode A completed", {
              profileId: truncateId(result.profile_id),
              sampleCount: result.samples.length,
            });
          }
    
          return {
            success: true,
            data: result,
          };
        }
      } catch (error) {
        const errorInstance = error instanceof Error ? error : new Error(String(error));
        const errorCode = mapErrorToCode(errorInstance);
    
        // 全環境でログ出力(isDevelopmentガードなし)
        // Log in all environments (no isDevelopment guard)
        logger.warn("[MCP Tool] preference.hear error", {
          code: errorCode,
          error: errorInstance.message,
        });
    
        return {
          success: false,
          error: {
            code: errorCode,
            message: sanitizeErrorMessage(errorCode),
          },
        };
      }
    }
  • The definition of the 'preference.hear' tool, including its name, description, and input schema.
    export const preferenceHearToolDefinition = {
      name: "preference.hear",
      description:
        "ユーザー嗜好ヒアリングセッション。feedbackなしでサンプル提示、feedbackありで嗜好プロファイル更新。" +
        "User preference hearing session. Present samples without feedback, update profile with feedback.",
      annotations: {
        title: "Preference Hearing",
        readOnlyHint: false,
        idempotentHint: false,
        openWorldHint: false,
      },
      inputSchema: {
        type: "object" as const,
        properties: {
          profile_id: {
            type: "string",
            format: "uuid",
            description: "プロファイルID(省略時は新規作成) / Profile ID (create new if omitted)",
          },
          feedback: {
            type: "array",
            description:
              "フィードバック配列(存在する場合はモードB) / Feedback array (Mode B if present)",
            items: {
              type: "object",
              properties: {
                sample_id: {
                  type: "string",
                  format: "uuid",
                  description: "サンプルID / Sample ID",
                },
                rating: {
                  type: "string",
                  enum: ["positive", "negative", "neutral"],
                  description: "評価 / Rating",
                },
                comment: {
                  type: "string",
                  maxLength: 500,
                  description: "コメント(最大500文字) / Comment (max 500 characters)",
                },
              },
              required: ["sample_id", "rating"],
            },
          },
          preference_text: {
            type: "string",
            minLength: 10,
            maxLength: 1000,
            description:
              "嗜好テキスト(Claudeエージェントが自然言語フィードバックから生成、10-1000文字) / " +
              "Preference text (generated by Claude agent from natural language feedback, 10-1000 chars)",
          },
          limit: {
            type: "integer",
            minimum: 1,
            maximum: 10,
            default: 1,
            description: "返却サンプル数(デフォルト1件) / Number of samples to return (default 1)",
          },
          offset: {
            type: "integer",
            minimum: 0,
            default: 0,
            description: "スキップ数 / Number of samples to skip",
          },
          exclude_ids: {
            type: "array",
            items: { type: "string", format: "uuid" },
            maxItems: 50,
            description:
              "除外するサンプルID配列(既評価済み) / Sample IDs to exclude (already evaluated)",
          },
        },
      },
    };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/TKMD/reftrix-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server