Skip to main content
Glama

voc_full

Retrieves Amazon reviews for a given ASIN and performs AI analysis to produce a Voice of Customer report, highlighting listing improvements from customer feedback.

Instructions

One-shot: fetch reviews AND run AI analysis.

The default tool for "give me a VOC report on this ASIN" style requests. Internally equivalent to bash voc.sh ASIN — calls fetch.sh and analyze.sh in sequence.

Args: asin: 10-character ASIN. market: Market code or amazon.* domain (default: US). limit: Number of reviews to fetch (default 100, max 1000).

Returns: Same shape as analyze_reviews.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
asinYes
marketNoUS
limitNo

Implementation Reference

  • The core handler for voc_full: validates ASIN, runs voc.sh script via _run_script, and parses the markdown output via _parse_analyze_markdown. Returns a dict with asin, market, report_markdown, sentiment, pain_points, selling_points, tips, summary_zh, summary_en.
    def voc_full(asin: str, market: str = "US", limit: int = 100) -> dict[str, Any]:
        """One-shot: scrape + analyze. Equivalent to `bash voc.sh ASIN`."""
        asin = _validate_asin(asin)
        if limit < 1 or limit > 1000:
            raise ValueError(f"limit must be 1-1000, got {limit}")
    
        report_md = _run_script(
            "voc.sh",
            [asin, "--limit", str(limit), "--market", market],
        )
        return _parse_analyze_markdown(asin, market, report_md)
  • MCP tool registration via @mcp.tool() decorator. The voc_full function is declared as an MCP tool that delegates to tools.voc_full().
    @mcp.tool()
    def voc_full(asin: str, market: str = "US", limit: int = 100) -> dict:
        """One-shot: fetch reviews AND run AI analysis.
    
        The default tool for "give me a VOC report on this ASIN" style requests.
        Internally equivalent to `bash voc.sh ASIN` — calls fetch.sh and
        analyze.sh in sequence.
    
        Args:
            asin: 10-character ASIN.
            market: Market code or amazon.* domain (default: US).
            limit: Number of reviews to fetch (default 100, max 1000).
    
        Returns: Same shape as `analyze_reviews`.
        """
        return tools.voc_full(asin=asin, market=market, limit=limit)
  • The AnalyzeReport Pydantic schema defines the return type structure for voc_full (and analyze_reviews), with fields: asin, market, report_markdown, sentiment, pain_points, selling_points, tips, summary_zh, summary_en.
    class AnalyzeReport(BaseModel):
        """Report returned by `analyze_reviews` / `voc_full`.
    
        Always includes the raw markdown (`report_markdown`). When the report can
        be parsed cleanly, structured fields are populated; on parse failure the
        structured fields are None and the markdown is still returned verbatim.
        """
        asin: str
        market: str = "US"
        report_markdown: str
        sentiment: Optional[dict] = None
        pain_points: list[dict] = Field(default_factory=list)
        selling_points: list[dict] = Field(default_factory=list)
        tips: list[dict] = Field(default_factory=list)
        summary_zh: str = ""
        summary_en: str = ""
  • Helper function _validate_asin used by voc_full to validate and normalize the ASIN parameter.
    def _validate_asin(asin: str) -> str:
        """Validate ASIN shape. We accept lowercase from MCP clients but normalize
        to upper before passing to the shell scripts (which warn but proceed on
        lowercase) — this keeps our error messages clearer.
        """
        asin = asin.strip().upper()
        if not VALID_ASIN_RE.match(asin):
            raise ValueError(
                f"invalid ASIN {asin!r}: must be 10 alphanumeric characters (e.g. B08N5WRWNW)"
            )
        return asin
  • Helper function _parse_analyze_markdown used by voc_full to extract structured fields from the analyze.sh markdown output.
    def _parse_analyze_markdown(asin: str, market: str, report_md: str) -> dict[str, Any]:
        """Pull the structured fields analyze.sh embedded as `KEY: value` lines.
    
        The renderer in analyze.sh emits markers like `SENTIMENT_POSITIVE: 37`,
        `PAIN_POINT_1_ZH: ...`, `TIP_3_EN: ...` interleaved with the prose
        markdown. We grep them out into a flat dict, then assemble structured
        fields. Anything we can't parse stays accessible via `report_markdown`.
        """
        flat: dict[str, str] = {}
        for line in report_md.splitlines():
            m = _LINE_RE.match(line.strip())
            if m:
                flat.setdefault(m.group(1), m.group(2).strip())
    
        def grouped(prefix: str, suffixes: tuple[str, ...]) -> list[dict[str, str]]:
            items = []
            for i in range(1, 11):  # support up to 10; reports typically have 5
                row = {}
                for suf in suffixes:
                    key = f"{prefix}_{i}_{suf}"
                    if key in flat:
                        row[suf.lower()] = flat[key]
                if row:
                    items.append(row)
            return items
    
        sentiment: Optional[dict[str, int]] = None
        if {"SENTIMENT_POSITIVE", "SENTIMENT_NEUTRAL", "SENTIMENT_NEGATIVE"} <= flat.keys():
            try:
                sentiment = {
                    "positive": int(flat["SENTIMENT_POSITIVE"]),
                    "neutral": int(flat["SENTIMENT_NEUTRAL"]),
                    "negative": int(flat["SENTIMENT_NEGATIVE"]),
                }
            except ValueError:
                sentiment = None
    
        return {
            "asin": asin,
            "market": market,
            "report_markdown": report_md,
            "sentiment": sentiment,
            "pain_points": grouped("PAIN_POINT", ("ZH", "EN", "COUNT")),
            "selling_points": grouped("SELLING_POINT", ("ZH", "EN", "COUNT")),
            "tips": grouped("TIP", ("ZH", "EN")),
            "summary_zh": flat.get("SUMMARY_ZH", ""),
            "summary_en": flat.get("SUMMARY_EN", ""),
        }
Behavior3/5

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

With no annotations provided, the description carries the full burden. It explains the composite nature and returns shape, but does not disclose behavioral traits like read-only status, rate limits, error handling, or permissions. The limit constraint (max 1000) is mentioned, adding some transparency.

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 highly concise and well-structured: a single-sentence purpose, an explanatory note, and a clear args section. Every sentence adds value without redundancy, and the formatting is easy to scan.

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

Completeness3/5

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

The description references the output shape of analyze_reviews, which assumes knowledge of that sibling tool. Without an output schema or further explanation of the AI analysis results, the description is somewhat incomplete. Additionally, no behavioral details (e.g., error handling, idempotency) are provided, leaving gaps for a composite tool.

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

Parameters4/5

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

Schema description coverage is 0%, so the description compensates effectively. It specifies that asin is a 10-character ASIN, market is a market code or amazon.* domain, and limit is the number of reviews with default 100 and max 1000. This adds valuable meaning beyond the schema's titles and defaults.

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

Purpose4/5

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

The description clearly states it is a one-shot tool that fetches reviews and runs AI analysis, positioning it as the default for VOC report requests. It identifies the specific action (fetch + analyze) and resource (ASIN), differentiating from siblings like fetch_reviews and analyze_reviews by being a combined operation.

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

Usage Guidelines3/5

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

The description provides a clear usage context ('VOC report on this ASIN') and explains the internal composition, but does not explicitly state when not to use it or mention alternative sibling tools for separate fetch or analysis tasks. The guidance is adequate but lacks exclusions.

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/mguozhen/voc-amazon-reviews'

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