Skip to main content

Example workflow

A worked example showing how an agent uses EDITED MCP end-to-end. The user asks a fuzzy retail question; the agent uses MCP tools to resolve fuzzy concepts to stable IDs, then feeds those IDs into the market-data tools to fetch the answer — all over the same MCP connection.

The scenario​

"Compare dress assortments at Zara and H&M in the UK over the last quarter."

To answer this, the agent needs three things from the EDITED catalogue:

  1. Stable retailer slugs for "Zara" and "H&M" — and only the UK storefronts
  2. A product-category ID for "dresses"
  3. A market ID for the UK market

It can't guess these. They live in EDITED's taxonomy, behind MCP.

One unwrapper, used at every step​

Every tool returns exactly one of result or error, so a single helper covers all four calls below. A refusal arrives with isError: false, which is why this 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 retailers​

const result = await client.callTool({
name: "mdmcp-search_retailers",
arguments: {
query: ["Zara", "H&M"], // batch — both at once
search_limit: 3,
country_code: "UK",
},
});

// search_retailers wraps its hits in an object, so a batch gives you one
// wrapper per query — not a bare list of retailers.
const [zaraResult, hmResult] = unwrap(result);
// → zaraResult: { retailers: [...], search_limit: 3, at_search_limit: false }
// → zaraResult.retailers[0]: { name: "Zara (UK)", slug: "zara", region: { iso_code: "UK", ... }, ... }
// → hmResult.retailers[0]: { name: "H&M (UK)", slug: "hm", region: { iso_code: "UK", ... }, ... }

const retailers = [zaraResult.retailers[0].slug, hmResult.retailers[0].slug];
// → ["zara", "hm"]

The agent picks the top match for each query. Note we batch both queries in a single call — faster, fewer round-trips, less context burned.

Two things to notice, because they bite everyone once. search_retailers is the odd one out among the resolvers: it returns a retailers wrapper, while search_brands, search_markets and search_product_searches return a bare list. And the slug is not the name with a country suffix — Zara in the UK is zara, not zara-uk. Always read the slug you were given rather than constructing one.

Step 2 — Resolve the product category​

const result = await client.callTool({
name: "mdmcp-search_product_searches",
arguments: { query: "dresses", search_limit: 3 },
});

const categories = unwrap(result);
// → categories[0]: { id: 138, name: "Dresses", category: "dresses", vertical: "apparel", ... }

const categoryId = categories[0].id;

Step 3 — Resolve the market​

const result = await client.callTool({
name: "mdmcp-search_markets",
arguments: { query: "United Kingdom", search_limit: 1 },
});

const markets = unwrap(result);
// → markets[0]: { id: "UK", name: "United Kingdom (UK)" }

const marketId = markets[0].id;

Search the full country name, not the abbreviation. query: "UK" with search_limit: 1 comes back with Ukraine — a two-letter query is thin material for a vector search, and the one result you asked for isn't the one you meant. Asking for a few candidates and picking the match you recognise is the more robust habit generally.

Step 4 — Query the data​

With three resolved identifiers, the agent runs the comparison with market_data_table — another MCP call, so there's no separate API to wire up. The resolved slug and id values go straight in as filters:

const result = await client.callTool({
name: "mdmcp-market_data_table",
arguments: {
name: "average price for dresses at Zara and H&M in the UK",
metrics: ["avg_price", "product_count"],
start_date: "2026-04-01",
end_date: "2026-06-30",
filters: [
{ field: "retailer", op: "in", value: retailers }, // ["zara", "hm"] (step 1)
{ field: "product_searches", op: "in", value: [categoryId] }, // 138 (step 2)
{ field: "market", op: "in", value: [marketId] }, // "UK" (step 3)
],
group_by: [{ field: "retailer" }],
},
});

const data = unwrap(result);
// → {
// columns: ["Retailer", "Average Price ($) [avg/period]", "Product Count [avg/period]"],
// rows: [["H&M (UK)", 44.07, 4774.67], ["Zara (UK)", 51.99, 1277.67]],
// total_row_count: 2, currency: "USD", vertical: "apparel",
// granularity: "M", evaluated_window: "2026-04-01:2026-06-30", ...
// }
return data; // ← what the agent presents to the user

Every resolved identifier goes in as a filter, not as a top-level argument — filters is a list of { field, op, value } triples, and at least one is required. name is a free-text label for the table you're asking for, not an identifier. Dates are explicit start_date / end_date within the last two years; there is no date_range shorthand. The full grammar — every field, metric and operator — is in mdmcp-market_data_docs, one topic per call.

See How to query → Picking the right tool for the full market_data_table grammar — metrics, group-by, histograms, trends, and period-over-period comparisons.

Why resolve IDs first?​

You could ship every retailer slug, category ID, and market ID into the agent's system prompt. That doesn't scale — EDITED has thousands of each, and they change weekly.

Resolving on demand lets the agent look them up the same way a junior analyst would: ask "what's the slug for Zara UK?", get the answer, then query the data — every step over the same MCP connection. The agent stays small and current; the catalogue stays in EDITED's source of truth.

Patterns to steal​

  • Batch parallel resolutions. When the agent needs two or three lookups to answer one question, batch them in a single tools/call with a list of queries. EDITED MCP runs them concurrently.
  • Always pick the top match for fuzzy queries. Vector search ranks by semantic similarity. The first hit is usually right; if it isn't, your query was ambiguous.
  • Log the slugs/IDs the agent resolves into. That's what you'll need when reproducing a session for debugging.