Skip to main content
Glama

search.facets

Read-onlyIdempotent

Classify search results by section type, industry, audience, and tags, returning counts per value. Use for search result refinement UI and filter selection.

Instructions

[DEPRECATED: Use search.unified with include_facets: true instead] ファセット検索(絞り込みカウント表示)。検索結果をsectionType・industry・audience・tagsで分類し、各値の件数を返却します。検索結果の絞り込みUIやフィルタ選択に使用します。代替: search.unified({ query: '...', include_facets: true, facet_fields: ['sectionType'], enable_reranking: false, limit: 50 }) / Faceted search with filter counts. Classifies search results by sectionType, industry, audience, and tags, returning counts per value. Used for search result refinement UI and filter selection.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYes検索クエリ(自然言語、1-500文字) / Search query (natural language, 1-500 chars)
facet_fieldsNoファセットフィールド(デフォルト: 全フィールド) / Facet fields (default: all). sectionType: セクション/パーツタイプ, industry: 業種, audience: ターゲット, tags: タグ
limitNoファセット算出のベース結果数(1-50、デフォルト: 50) / Base result limit for facet computation (1-50, default: 50)
webPageIdNoWebページIDでフィルター / Filter by web page ID
industryNo業種フィルター / Industry filter (e.g., 'SaaS', 'E-commerce')
audienceNoターゲットオーディエンスフィルター / Target audience filter (e.g., 'Developer')
tagsNoタグフィルター / Tags filter

Implementation Reference

  • The main handler function for the search.facets tool. Validates input, calls searchUnifiedHandler, computes facet counts via computeFacetsFromResults, logs search activity, and returns facet counts with a deprecation warning.
    export async function searchFacetsHandler(input: unknown): Promise<SearchFacetsOutput> {
      const startTime = Date.now();
    
      // 1. 入力バリデーション / Input validation
      let validated: SearchFacetsInput;
      try {
        validated = searchFacetsInputSchema.parse(input);
      } catch (error) {
        logger.warn("[search.facets] Validation error", { error: (error as Error).message });
        return {
          success: false,
          error: {
            code: SEARCH_FACETS_ERROR_CODES.VALIDATION_ERROR,
            message: "Validation error",
          },
        };
      }
    
      const facetFields: FacetField[] =
        validated.facet_fields ?? ([...SUPPORTED_FACET_FIELDS] as FacetField[]);
    
      // 2. クエリタイプ分類 / Classify query type
      const queryType = classifyQueryType(validated.query);
    
      try {
        // 3. search.unified 実行 / Execute search.unified
        const searchResult = (await searchUnifiedHandler({
          query: validated.query,
          limit: validated.limit,
          webPageId: validated.webPageId,
          industry: validated.industry,
          audience: validated.audience,
          tags: validated.tags,
          enable_reranking: false, // ファセットカウントにはリランキング不要
        })) as SearchUnifiedOutput;
    
        if (!searchResult.success) {
          return {
            success: false,
            error: {
              code: SEARCH_FACETS_ERROR_CODES.SEARCH_FAILED,
              message: "Unified search failed",
            },
          };
        }
    
        // 4. ファセットカウント算出 / Compute facet counts
        const facets = computeFacetsFromResults(searchResult.data.results, facetFields);
    
        const searchTimeMs = Date.now() - startTime;
    
        // 5. 検索ログ記録(fire-and-forget) / Log search (fire-and-forget)
        logSearch({
          query: validated.query,
          queryType,
          services: ["facets"],
          resultCount: searchResult.data.total,
          topResultId: searchResult.data.results[0]?.id,
          filters: {
            facet_fields: facetFields,
            industry: validated.industry,
            audience: validated.audience,
            tags: validated.tags,
          },
          latencyMs: searchTimeMs,
          cacheHit: false,
        }).catch(() => {
          // fire-and-forget: エラーは logSearch 内部で処理済み
        });
    
        return {
          success: true,
          data: {
            facets,
            query_type: queryType,
            total_results: searchResult.data.total,
            searchTimeMs,
            _deprecation: {
              message:
                "search.facets is deprecated. Use search.unified with include_facets: true (and optionally facet_fields) instead. " +
                "Example: search.unified({ query: '...', include_facets: true, facet_fields: ['sectionType'], enable_reranking: false, limit: 50 })",
              removal_version: "v0.4.0",
              alternative: "search.unified with include_facets: true",
            },
          },
        };
      } catch (error) {
        const errorInstance = error instanceof Error ? error : new Error(String(error));
        logger.warn("[search.facets] Failed", {
          error: sanitizeErrorMessage(errorInstance),
        });
    
        return {
          success: false,
          error: {
            code: SEARCH_FACETS_ERROR_CODES.INTERNAL_ERROR,
            message: sanitizeErrorMessage(errorInstance),
          },
        };
      }
    }
  • Zod input schema for search.facets (searchFacetsInputSchema) defining query, facet_fields, limit, webPageId, industry, audience, tags.
    const facetFieldSchema = z.enum(["sectionType", "industry", "audience", "tags"]);
    
    /**
     * search.facets 入力スキーマ / search.facets input schema
     */
    export const searchFacetsInputSchema = z.object({
      /** 検索クエリ(自然言語、1-500文字) / Search query (natural language, 1-500 chars) */
      query: z.string().min(1).max(500),
      /** ファセットフィールド(デフォルト: 全フィールド) / Facet fields (default: all) */
      facet_fields: z.array(facetFieldSchema).min(1).optional(),
      /** 取得件数(1-50、デフォルト: 50) / Result limit for facet computation (1-50, default: 50) */
      limit: z.number().int().min(1).max(50).default(50),
      /** WebページIDでフィルター / Filter by web page ID */
      webPageId: z.string().uuid().optional(),
      /** 業種フィルター / Industry filter */
      industry: z.string().max(100).optional(),
      /** ターゲットオーディエンスフィルター / Target audience filter */
      audience: z.string().max(100).optional(),
      /** タグフィルター / Tags filter */
      tags: z.array(z.string()).max(10).optional(),
    });
    export type SearchFacetsInput = z.infer<typeof searchFacetsInputSchema>;
  • TypeScript output type (SearchFacetsOutput) with success/error discriminated union, including FacetCounts, query_type, total_results, searchTimeMs, and _deprecation.
    export type SearchFacetsOutput =
      | {
          success: true;
          data: {
            /** ファセットカウント / Facet counts */
            facets: FacetCounts;
            /** クエリタイプ / Query type */
            query_type: string;
            /** 検索結果総数(ファセット算出のベース) / Total results (base for facet computation) */
            total_results: number;
            /** 検索時間(ms) / Search time (ms) */
            searchTimeMs: number;
            /** 非推奨警告 / Deprecation warning */
            _deprecation: {
              message: string;
              removal_version: string;
              alternative: string;
            };
          };
        }
      | {
          success: false;
          error: {
            code: string;
            message: string;
          };
        };
  • Tool definition object (searchFacetsToolDefinition) with name 'search.facets', description, deprecation annotation, and JSON Schema inputSchema.
    export const searchFacetsToolDefinition = {
      name: "search.facets",
      description:
        "[DEPRECATED: Use search.unified with include_facets: true instead] " +
        "ファセット検索(絞り込みカウント表示)。検索結果をsectionType・industry・audience・tagsで" +
        "分類し、各値の件数を返却します。検索結果の絞り込みUIやフィルタ選択に使用します。" +
        "代替: search.unified({ query: '...', include_facets: true, facet_fields: ['sectionType'], enable_reranking: false, limit: 50 })" +
        " / Faceted search with filter counts. Classifies search results by sectionType, industry, " +
        "audience, and tags, returning counts per value. Used for search result refinement UI and filter selection.",
      annotations: {
        title: "Faceted Search",
        readOnlyHint: true,
        idempotentHint: true,
        openWorldHint: false,
        deprecated: true,
      },
      inputSchema: {
        type: "object" as const,
        properties: {
          query: {
            type: "string",
            description:
              "検索クエリ(自然言語、1-500文字) / Search query (natural language, 1-500 chars)",
            minLength: 1,
            maxLength: 500,
          },
          facet_fields: {
            type: "array",
            items: {
              type: "string",
              enum: ["sectionType", "industry", "audience", "tags"],
            },
            description:
              "ファセットフィールド(デフォルト: 全フィールド) / Facet fields (default: all). " +
              "sectionType: セクション/パーツタイプ, industry: 業種, audience: ターゲット, tags: タグ",
          },
          limit: {
            type: "number",
            description:
              "ファセット算出のベース結果数(1-50、デフォルト: 50) / Base result limit for facet computation (1-50, default: 50)",
            minimum: 1,
            maximum: 50,
            default: 50,
          },
          webPageId: {
            type: "string",
            format: "uuid",
            description: "WebページIDでフィルター / Filter by web page ID",
          },
          industry: {
            type: "string",
            maxLength: 100,
            description: "業種フィルター / Industry filter (e.g., 'SaaS', 'E-commerce')",
          },
          audience: {
            type: "string",
            maxLength: 100,
            description:
              "ターゲットオーディエンスフィルター / Target audience filter (e.g., 'Developer')",
          },
          tags: {
            type: "array",
            items: { type: "string" },
            maxItems: 10,
            description: "タグフィルター / Tags filter",
          },
        },
        required: ["query"],
      },
    };
  • The computeFacetsFromResults function that iterates over search results, extracts facet values from metadata (sectionType, industry, audience, tags), counts them, and returns sorted FacetCounts.
    export function computeFacetsFromResults(
      results: UnifiedSearchResultItem[],
      fields: FacetField[]
    ): FacetCounts {
      const facets: FacetCounts = {};
    
      for (const field of fields) {
        const counterMap = new Map<string, number>();
        const metadataKeys = FACET_METADATA_KEYS[field];
    
        for (const result of results) {
          if (!result.metadata) continue;
    
          for (const key of metadataKeys) {
            const value = result.metadata[key];
    
            if (value === undefined || value === null) continue;
    
            if (field === "tags" && Array.isArray(value)) {
              // tags: 配列を展開して個別にカウント
              // tags: flatten arrays and count individually
              for (const tag of value) {
                if (typeof tag === "string" && tag.length > 0) {
                  counterMap.set(tag, (counterMap.get(tag) ?? 0) + 1);
                }
              }
            } else if (typeof value === "string" && value.length > 0) {
              counterMap.set(value, (counterMap.get(value) ?? 0) + 1);
            }
          }
        }
    
        // カウント降順でソート / Sort by count descending
        const sorted: FacetCountItem[] = Array.from(counterMap.entries())
          .map(([value, count]) => ({ value, count }))
          .sort((a, b) => b.count - a.count);
    
        facets[field] = sorted;
      }
    
      return facets;
    }
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond annotations (readOnlyHint, idempotentHint), the description adds that the tool performs faceted search returning counts, and lists the specific facet fields. It also discloses the deprecation status, which is important behavioral information not captured by annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is very concise, with two sentences in each language. The deprecation notice is front-loaded, and every sentence serves a clear purpose (deprecation, functionality, use case, migration path). No unnecessary words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a deprecated tool with full schema coverage and annotations, the description adequately covers purpose, usage, and migration. It does not specify return format (no output schema), but that is acceptable given the deprecation and the alternative provided.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents parameters well. The description does not add significant semantic value beyond listing the four facet fields, which are also in the schema. Baseline is 3, and there is no substantial improvement.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it is a faceted search that returns counts for specific fields (sectionType, industry, audience, tags) and is used for UI refinement. It also distinguishes itself from the deprecated status and directs to search.unified, a sibling tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly marks the tool as deprecated and provides the exact alternative call using search.unified with include_facets and relevant parameters. It also describes typical use cases (filter selection), giving clear guidance on when to use this tool versus alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/ReftrixMCP'

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