Tools
A tool is a function the server exposes for an agent to call. The agent picks the tool and supplies arguments; the server runs it and returns the result.
EDITED MCP's tools span three data domains — Competitive Market Data, Trend &
Research, and Messaging & Promotions — plus a set of mdmcp helpers. Some
resolve fuzzy concepts into stable identifiers (slug, id); others return
product data, analytics, article passages, or promotions directly. See
The data for the full map, or the Reference for every
tool's schema.
Discover tools
const { tools } = await client.listTools();
// → [{ name: "mdmcp-search_retailers", description: "...", inputSchema: {...},
// outputSchema: {...} }, ...]
Each tool exposes:
name— the stable identifier, used intools/calldescription— prose the agent reads to decide when to callinputSchema— JSON Schema for the argumentsoutputSchema— JSON Schema for the response, covering both branches of the envelope described in Result shapes below
Call a tool
const result = await client.callTool({
name: "mdmcp-search_brands",
arguments: { query: "Nike", search_limit: 5 },
});
// Read the payload from result.structuredContent — see Result shapes below.
// result.content also exists, but it is the LLM-facing channel, not the contract.
The auto-generated Reference lists every tool with its full schema and a live "Try it" widget.
Batch queries
The market-data search tools accept either a single string or a list of up to ten strings. Pass a list and the server runs the searches concurrently, returning results in the same order:
await client.callTool({
name: "mdmcp-search_retailers",
arguments: { query: ["Zara", "H&M", "Uniqlo"], search_limit: 3 },
});
// → one { retailers, search_limit, at_search_limit } wrapper per query, in
// order — search_retailers is the only search tool that wraps its hits; the
// others return one bare list per query
When you need to resolve several entities at once, batch — it's faster than serial calls and lighter on context.
Result shapes
A tool call that runs returns an object with exactly one of result or
error, read from structuredContent — the machine contract, which always
matches the tool's published outputSchema. Some refusals arrive on a second
channel instead, so check isError first:
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 Object.assign(new Error(body.error.message), body.error);
const brands = body.result; // matches the Brand model
Tools also return a content array of TextContent blocks. That channel is what
an LLM sees and it deliberately differs: on success it carries the payload
without the envelope, and on a refusal it carries a plain prose message rather
than JSON. Read structuredContent for anything you branch on — the one
exception being the isError path above, where it is null and the text is the
only message there is.
An in-band refusal arrives with isError: false and structuredContent.error, so
a client checking only isError reads it as a success — while a client checking
only the payload crashes on the isError cases. See
Concepts → Response shape for which refusals use which
channel, the error codes, and the Python .data caveat.
Errors
Server-side failures surface as JSON-RPC errors:
{
"jsonrpc": "2.0",
"id": 1,
"error": { "code": -32602, "message": "Invalid request parameters" }
}
See Operations → Errors for every code, the HTTP-level statuses, and retry guidance.