part.search
Search UI components like buttons, cards, and links using text queries with hybrid semantic and full-text search. Filter results by component type and search mode to find design elements.
Instructions
UIコンポーネントパーツ(ボタン、カード、リンク等)をセマンティック検索します。テキストクエリ(e5-base + full-text)によるハイブリッド検索を提供。partType(16種類)やsearchMode(visual/text/hybrid)でフィルタリング可能です。 / Search UI component parts (buttons, cards, links, etc.) by text query. Provides hybrid search via e5-base + full-text. Filterable by partType (16 types) and searchMode (visual/text/hybrid).
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | No | テキスト検索クエリ(1-500文字) / Text search query (1-500 chars) | |
| image_url | No | 画像URLによるビジュアル検索(将来対応予定) / Visual search by image URL (future support) | |
| part_type | No | パーツタイプでフィルター(16種類) / Filter by part type (16 types) | |
| web_page_id | No | WebページIDでフィルター / Filter by web page ID | |
| limit | No | 取得件数(1-100、デフォルト: 20) / Result limit (1-100, default: 20) | |
| offset | No | オフセット(0以上、デフォルト: 0) / Offset (0+, default: 0) | |
| search_mode | No | 検索モード(デフォルト: hybrid) / Search mode (default: hybrid) | hybrid |
| min_similarity | No | 最小類似度閾値(0-1、デフォルト: 0.3) / Min similarity threshold (0-1, default: 0.3) |
Implementation Reference
- The main handler function for the part.search MCP tool, responsible for input validation, calling the search service, and formatting the response.
export async function partSearchHandler(input: unknown): Promise<PartSearchOutput> { const startTime = Date.now(); if (isDevelopment()) { logger.info("[MCP Tool] part.search called", { query: (input as Record<string, unknown>)?.query, image_url: (input as Record<string, unknown>)?.image_url ? "(provided)" : undefined, }); } // 入力バリデーション / Input validation let validated: PartSearchInput; try { validated = partSearchInputSchema.parse(input); } catch (error) { if (error instanceof ZodError) { const errorMessage = error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join(", "); logger.warn("[MCP Tool] part.search validation error", { errors: error.errors, }); return { success: false, error: { code: PART_SEARCH_ERROR_CODES.VALIDATION_ERROR, message: `Validation error: ${errorMessage}`, }, }; } throw error; } // サービス取得 / Get service let searchService: ReturnType<typeof getPartSearchService>; try { searchService = getPartSearchService(); } catch { return { success: false, error: { code: PART_SEARCH_ERROR_CODES.SERVICE_UNAVAILABLE, message: "Part search service is not available", }, }; } try { // 検索オプション構築 / Build search options // exactOptionalPropertyTypes: undefinedを明示的に渡さない const options: PartSearchOptions = { limit: validated.limit, offset: validated.offset, minSimilarity: validated.min_similarity, searchMode: validated.search_mode, }; if (validated.part_type) { options.partType = validated.part_type; } // WebページIDフィルタはPartSearchOptionsにないため、 // 将来的な拡張ポイントとして保持 // webPageId filter is not in PartSearchOptions yet, // kept as future extension point let result: PartSearchResult; if (validated.search_mode === "visual" && validated.image_url) { // ビジュアル検索: imageUrlをreferencePartIdとして使用できるか確認 // 現在の実装は referencePartId(既存パーツID)のみ対応 // Visual search: currently only supports referencePartId (existing part ID) // imageUrl direct search is a future extension return { success: false, error: { code: PART_SEARCH_ERROR_CODES.VALIDATION_ERROR, message: "Visual search by imageUrl is not yet supported. Use text or hybrid mode, or provide an existing part ID via part.inspect + visual search.", }, }; } if (validated.query) { // テキストまたはハイブリッド検索 / Text or hybrid search const embedding = await searchService.generateQueryEmbedding(validated.query); if (embedding && validated.search_mode !== "text") { // ハイブリッド検索: ベクトル + 全文検索 / Hybrid: vector + fulltext result = await searchService.searchPartsHybrid(validated.query, embedding, options); } else if (embedding) { // テキストベクトル検索のみ / Text vector search only result = await searchService.searchParts(embedding, options); } else { // Embedding生成失敗 / Embedding generation failed if (isDevelopment()) { logger.warn( "[MCP Tool] part.search: embedding generation failed, returning empty results" ); } return { success: true, data: { results: [], total: 0, query: { text: validated.query }, searchTimeMs: Date.now() - startTime, }, }; } } else { // queryもimageUrlもない場合(Zodの.refineで防がれるはず) // Neither query nor imageUrl (should be prevented by Zod .refine()) return { success: true, data: { results: [], total: 0, query: {}, searchTimeMs: Date.now() - startTime, }, }; } // 結果フォーマット / Format results // include_styles/include_html はスキーマに未定義のためデフォルトfalse // Currently partSearchInputSchema doesn't have include_styles/include_html // Part search results omit computedStyles and htmlSnippet by default const mappedResults = formatSearchResult(result, false, false); const searchTimeMs = Date.now() - startTime; if (isDevelopment()) { logger.info("[MCP Tool] part.search completed", { query: validated.query, searchMode: validated.search_mode, resultCount: mappedResults.length, total: result.total, searchTimeMs, }); } return { success: true, data: { results: mappedResults, total: result.total, query: { ...(validated.query ? { text: validated.query } : {}), ...(validated.image_url ? { imageUrl: validated.image_url } : {}), }, searchTimeMs, }, }; } catch (error) { const errorInstance = error instanceof Error ? error : new Error(String(error)); const errorCode = mapErrorToCode(errorInstance); logger.warn("[MCP Tool] part.search error", { code: errorCode, error: sanitizePartSearchError(error), }); return { success: false, error: { code: errorCode, message: sanitizePartSearchError(error), }, }; } } - The definition of the part.search tool, including its schema and description.
export const partSearchToolDefinition = { name: "part.search", description: "UIコンポーネントパーツ(ボタン、カード、リンク等)をセマンティック検索します。" + "テキストクエリ(e5-base + full-text)によるハイブリッド検索を提供。" + "partType(16種類)やsearchMode(visual/text/hybrid)でフィルタリング可能です。" + " / Search UI component parts (buttons, cards, links, etc.) by text query. " + "Provides hybrid search via e5-base + full-text. " + "Filterable by partType (16 types) and searchMode (visual/text/hybrid).", annotations: { title: "Part Search", readOnlyHint: true, idempotentHint: true, openWorldHint: false, }, inputSchema: { type: "object" as const, properties: { query: { type: "string", description: "テキスト検索クエリ(1-500文字) / Text search query (1-500 chars)", minLength: 1, maxLength: 500, }, image_url: { type: "string", format: "uri", description: "画像URLによるビジュアル検索(将来対応予定) / Visual search by image URL (future support)", }, part_type: { type: "string", enum: [ "button", "link", "image", "video", "form", "input", "heading", "card", "navigation", "footer", "cta", "hero_image", "icon", "badge", "tag", "avatar", ], description: "パーツタイプでフィルター(16種類) / Filter by part type (16 types)", }, web_page_id: { type: "string", format: "uuid", description: "WebページIDでフィルター / Filter by web page ID", }, limit: { type: "number", description: "取得件数(1-100、デフォルト: 20) / Result limit (1-100, default: 20)", minimum: 1, maximum: 100, default: 20, }, offset: { type: "number", description: "オフセット(0以上、デフォルト: 0) / Offset (0+, default: 0)", minimum: 0, default: 0, }, search_mode: { type: "string", enum: ["visual", "text", "hybrid"], default: "hybrid", description: "検索モード(デフォルト: hybrid) / Search mode (default: hybrid)", }, min_similarity: { type: "number", description: "最小類似度閾値(0-1、デフォルト: 0.3) / Min similarity threshold (0-1, default: 0.3)", minimum: 0, maximum: 1, default: 0.3, }, }, }, }; - apps/mcp-server/src/tools/index.ts:643-643 (registration)The registration of the part.search tool in the main tools index file.
"part.search": partSearchHandler,