Skip to main content

How to query

This page covers the patterns that apply across EDITED's MCP tools — how to resolve identifiers reliably, how to check data quality before running analytics, and how to pick the right tool for a given question. For the complete tool catalogue with parameters and schemas, see the Reference.


Identifier resolution​

Most EDITED query tools expect stable internal identifiers — slugs and integer IDs — rather than free-text names. Resolving these identifiers is a required step before calling any domain query tool, not optional pre-processing.

Resolving retailers: market first​

Retailer slugs are not globally unique. The slug for a retailer in one region may not follow a predictable pattern, and passing a retailer name directly to a query tool without first establishing market context risks matching the wrong regional storefront.

The reliable sequence for any retailer-scoped query:

  1. Call search_markets to resolve the market the user cares about
  2. Call search_retailers with country_code set to the market's code
  3. Use the returned slug in downstream query tools

Every tool returns exactly one of result or error, so the samples below share one unwrapper. A refusal arrives with isError: false, which is why it checks the payload and not just the flag — see Concepts → Response shape.

function unwrap(result) {
if (result.isError) {
// never completed — the text is prose, not JSON
throw new Error(result.content?.[0]?.text ?? "call failed");
}
const body = result.structuredContent;
if (body.error) throw new Error(`${body.error.code}: ${body.error.message}`);
return body.result;
}
// Step 1 — resolve the market. Search the full country name: "UK" with
// search_limit 1 comes back with Ukraine.
const marketResult = await client.callTool({
name: "mdmcp-search_markets",
arguments: { query: "United Kingdom", search_limit: 1 },
});
const market = unwrap(marketResult)[0];
// → { id: "UK", name: "United Kingdom (UK)" }

// Step 2 — resolve the retailer within that market.
// search_retailers wraps its hits, so read `.retailers` before indexing.
const retailerResult = await client.callTool({
name: "mdmcp-search_retailers",
arguments: { query: "Zara", search_limit: 3, country_code: market.id },
});
const retailer = unwrap(retailerResult).retailers[0];
// → { name: "Zara (UK)", slug: "zara", region: { iso_code: "UK", ... }, ... }

// Step 3 — use the slug in your query tool

search_retailers is the only resolver that wraps its results: it returns { retailers, search_limit, at_search_limit }, while search_brands, search_markets and search_product_searches return a bare list. And don't build a slug by hand — Zara in the UK is zara, not zara-uk.

Retailer hits carry match too, and it's the sharpest case: a bare Zara is exact on every country's Zara (…), so choose on region/country_code, not rank. And an exact retailer hit says only that EDITED knows the retailer — run get_retailer_coverage on the slug you pick before trusting it holds data over your window.

Resolving other entities​

Brands, product categories, markets, and sizing entities follow a simpler pattern — search, take the best hit, extract the identifier. Read that hit's match value before you trust it, though: rank alone isn't confirmation (see below).

What you haveTool to callIdentifier to extract
Brand name ("Nike")search_brandsslug
Product category ("dresses")search_product_searchesid
Market name ("Germany")search_marketsid
Sizing system ("EU Women's Apparel")search_size_groupid
Individual size ("M", "42")search_size_optionsid

Brands are global — no market scoping required. But a top hit isn't automatically your brand: when nothing matches the query strongly, the vector search fills the remaining slots with nearest-neighbour suggestions, so a plausible-looking name can rank first without being a real match. Every hit carries a match value — treat a semantic_only result as a candidate to verify, not a confirmed answer, and check it rather than trusting rank. The Reference lists the full set of match values.

Resolving retailers for the promotions domain​

The market-data domain and the messaging/promotions domain use different retailer identifiers — a slug from mdmcp-search_retailers is not a valid promo filter. Resolve the retailer again in the messaging domain with messagingmcp-list_retailers: match the request against the returned retailer_name, then pass that row's own retailer slug and a region it reports to the promo tools. Don't carry a market-data slug across the boundary.

Batching​

All identifier resolution tools accept either a single string or a list of up to ten strings. When you need to resolve multiple entities for the same query, batch them in a single call — it runs concurrently and saves round-trips.

// Resolve two retailers at once
const result = await client.callTool({
name: "mdmcp-search_retailers",
arguments: {
query: ["Zara", "H&M"],
search_limit: 3,
country_code: "UK",
},
});
// One result object per query, in the order you asked — and each one is a
// `retailers` wrapper, not a bare list.
const [zaraResult, hmResult] = unwrap(result);
const zara = zaraResult.retailers[0]; // → { name: "Zara (UK)", slug: "zara", ... }
const hm = hmResult.retailers[0]; // → { name: "H&M (UK)", slug: "hm", ... }

Once resolved, reuse identifiers within a session rather than calling the resolution tools again for the same entity.

Scoping resolution to a vertical​

search_product_searches resolves categories within a vertical, so it takes the same optional vertical parameter as the market-data query tools. Resolve categories under the same vertical you intend to query — otherwise the ids you get back won't match the data you ask for. See Choosing a vertical below.


Choosing a vertical​

Market data covers three verticals — apparel, beauty, and homeware. The vertical parameter is optional on market_data_options_search, market_data_table, search_product_searches, and get_retailer_coverage.

Omit it unless the user is explicitly asking about another vertical. When omitted it resolves to the account's default vertical, which is the right behaviour for most queries. When you do set it, set it consistently across every tool in the workflow — resolving a category under one vertical and querying another returns mismatched results.

Entitlements vary by account. All three values appear in every tool's schema, but requesting a vertical the account can't access returns an error rather than an empty result. Check before offering vertical-specific analysis:

const entitlements = await client.callTool({
name: "mdmcp-market_data_entitlements",
arguments: {},
});
// → { verticals: ["apparel", "beauty", "homeware"],
// default_vertical: "apparel", currency: "USD" }

Note the token is homeware, not home. A vertical is a top-level data segment, not a product category — categories such as dresses or mascara live within a vertical and are resolved with search_product_searches.


Coverage checking​

Before running analytics over a date window, call get_retailer_coverage to verify that EDITED's data for the retailers in scope is complete and fresh enough to support the question.

A clean aggregate from market_data_table can be silently wrong if a retailer had a coverage gap in the date range. The aggregate will not signal this on its own — it simply returns fewer products without explanation. get_retailer_coverage answers the question directly.

const coverage = await client.callTool({
name: "mdmcp-get_retailer_coverage",
arguments: {
retailers: ["zara", "hm"],
start_date: "2026-01-01",
end_date: "2026-03-31",
granularity: ["weekly"],
},
});

Results are classified as no_data, low_volume, or high_staleness for each period. If gaps exist, either narrow the date range, exclude the affected retailer, or surface the caveat to the user before presenting results.

When coverage checking matters most:

  • Any time-windowed trend or comparison query
  • Period-over-period comparisons where a gap in one period would skew the delta
  • Queries scoped to a single retailer, where a gap has nowhere to average out For a simple snapshot query ("what is Zara's current assortment"), coverage checking adds less value — the query is point-in-time and the user can see whether results look thin.

Picking the right tool​

Market data: options vs table​

The two Competitive Market Data query tools serve different question shapes.

Question typeExamplesUse
Specific products"Show me Zara's discounted dresses", "What are the cheapest in-stock trainers at H&M"market_data_options_search
Aggregates and analytics"Average price by retailer", "Share of discounted products this month", "How has product count changed week on week"market_data_table

A useful shortcut: if the user would want to see a list of products, use market_data_options_search. If they would want to see a number, a percentage, or a chart, use market_data_table.

market_data_table is the more powerful tool for analysis — it supports breakdowns by up to three dimensions, period-over-period comparisons, histograms, and percentiles. Use group_by: [] for a global snapshot with no bucketing.

Research: discovery before drill-down​

The Trend & Research tools are designed to be used in sequence. Going straight to a paragraph-level search or a full article read without a discovery step wastes context and often returns less relevant results.

The recommended sequence:

  1. research_analyze_query (optional) — if the user named a relative window ("last quarter") or a topic you'd rather have parsed for you, run it to get back ISO dates and retrieval intent, and pass those to the search tools. It's composable, not required: the search tools accept explicit dates and intent axes directly, so skip it when you already know them.
  2. research_article_search — find which reports are relevant. Returns one summary hit per report with a report_id. This is almost always the right first step for a topic query.
  3. research_chunk_search with report_ids — drill into specific reports to find the passages that answer the question. More precise than searching the full corpus.
  4. research_read_report — only when the user wants to read or summarise a full report. It returns the complete article body, which is a large payload; prefer chunk search when you only need specific passages. For image retrieval, research_image_search follows the same pattern: use after research_article_search and pass report_ids to search within relevant reports rather than the full corpus.

Messaging and promotions: structured and visual together​

The messaging and promotions tools are designed to be used together. The structured promo tools (count_promos, list_promos, aggregate_promos) give you numbers and records; messaging_search gives you the creative context those numbers came from; enrich_with_vm bridges them by fetching the visual context for a known image_id.

A typical pattern: count or aggregate promotions to answer the analytical question, then use list_promos to surface representative examples, then enrich_with_vm to show the creative alongside the data.

Calling a tool doesn't require a model​

It's easy to assume that because MCP is how AI agents reach EDITED's data, every MCP call has a model deciding which tool to use. It doesn't. MCP is a protocol — a server exposes tools, a client calls them — and nothing about that exchange requires an LLM to be the one choosing the tool or its arguments. Your own code can do that directly, deterministically, every time.

That gives you two ways to call a tool, not one:

  • Call it directly, when you already know which tool and which arguments you need. No model involved — same tool, same result, every run.
  • Let a model choose, when the right tool or the right arguments genuinely depend on something you only learn at runtime, and it's not practical to write out every case in advance. Default to calling the tool directly when you can. Reserve model-driven tool selection for the cases that actually need judgment — it costs more, runs slower, and won't behave identically twice.
// Known ahead of time -> call the tool directly, no model involved:
const result = await client.callTool({
name: "mdmcp-market_data_table",
arguments: {
retailers: ["zara"],
product_search_id: 138,
metric: "avg_price",
},
});
// Only knowable at runtime -> let a model choose:
const flaggedCategory = "dresses"; // would come from an earlier workflow step
const flaggedRetailer = "zara"; // would come from an earlier workflow step

const response = await fetch("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: "your-model-of-choice", // any MCP-compatible model works here
max_tokens: 1000,
messages: [{
role: "user",
content: `An inventory anomaly was flagged in ${flaggedCategory} for ${flaggedRetailer}. Check competitive pricing context for it.`,
}],
mcp_servers: [{ type: "url", url: EDITED_MCP_URL, name: "edited-mcp" }],
}),
});

Same server, same tool available either way — the difference is who's holding the steering wheel.


Using the server-defined prompts​

EDITED ships three server-defined prompts — one per domain — that encode recommended routing and orchestration logic. These are the fastest way to build a well-behaved agent — rather than re-implementing the routing rules in your own system prompt, compose these in and extend from them.

Fetch them at runtime with prompts/get:

const prompt = await client.getPrompt({
name: "mdmcp-market_data_query_guide",
arguments: {},
});
// prompt.messages — prepend to your conversation context

mdmcp-market_data_query_guide​

Covers the full workflow for Competitive Market Data queries: how to resolve filter values using the identifier resolution tools, how to route between market_data_options_search and market_data_table, and how to read the inline result envelope including the truncation signal.

Use this as the foundation for any agent that will answer market data questions.

researchmcp-research_orchestration​

Covers routing, stop conditions, source attribution, and image-description rules for the three research search tools (research_article_search, research_chunk_search, research_image_search). Encodes the discovery-before-drill-down pattern and the rules for when to stop searching and synthesise.

Use this as the foundation for any agent that will answer trend or research questions.

messagingmcp-messaging_orchestration​

Covers routing, tool budget, lean-query, and provenance-citation rules for the Messaging & Promotions tools — how to move between messaging_search and the promo tools (count_promos, list_promos, aggregate_promos, search_promo_text) and bridge them with enrich_with_vm. Consumer-specific concerns — persona, output format, citation style — stay your agent's responsibility.

Use this as the foundation for any agent that will answer messaging or promotions questions.

All three prompts are maintained server-side — when EDITED updates the guidance, your agent picks it up on the next listPrompts call without a redeploy. Browse the current prompt content in the Reference.