Analyze Query (Research)
researchmcp-research_analyze_query
Analyse a research query into retrieval intent and (optionally) a date window.
The recommended first call for any topic or trend question — anything other
than "what has EDITED published lately". It turns a natural-language question
into the exact taxonomy slugs the search tools rank on (e.g. "trends in men's
SS27 tailoring" → topics=["tailoring"], gender=["men"],
season=["spring-summer"], year=["2027"]), which materially improves what
comes back from research_article_search / research_chunk_search /
research_image_search. Call it once per query and thread the result into each
search.
The rest of a discovery flow needs, in order: one of those three search tools,
then research_report_links to turn the report_ids you cite into public URLs
(an expected final step, not an on-request extra), and research_read_report
when the user wants the article text rather than a link. Load all of them
alongside this one — the link step is not optional, so discovering it only
after the search returns costs an avoidable round trip.
Still optional and composable — nothing rejects a search without it. The search
tools accept the same intent axes and start_date/end_date directly, so you
may derive them from your own reasoning, use this tool for just one of intent or
dates, or pass explicit dates and intent together (explicit dates set the
filter; intent always drives ranking).
Parameters
| Name | Type | Required | Constraints | Description |
|---|---|---|---|---|
query | string | yes | — | The full natural-language research query to analyse for topic/format intent and any time window. |
now | string | no | — | Anchor date for relative expressions like 'last 30 days'; defaults to today. Pass for reproducible results. |
Returns
- On success, all four fields below sit under
result: - {"result": {intent, date_range, interpretation, is_inferred}}.
- intent: the six axes (
article_types,topics,gender,season, year,moments), each a possibly-empty list of known slugs. Thread- these into the search tools' matching params.
- date_range: {start_date, end_date} (ISO YYYY-MM-DD, inclusive) for a
- genuinely temporal phrase (relative window, quarter, month range,
- single day), or null. A year, a season, or a named event is NOT returned as a
- date_range — it is surfaced in
intent(year/season/moments) - instead, and the search tools turn
year/momentsinto a window - automatically.
- interpretation: short human-readable summary of how the query parsed.
- is_inferred: true when a heuristic/LLM fallback or a future-date clamp
- was used — surface
interpretationto the user so they can confirm. - Errors:
- {"error": {code, message, field}}
codeisinvalid_inputwithfieldqueryfor malformed or blank- input (retrying it unchanged fails the same way);
internalwith no fieldwhen the analysis produced a payload we could not publish (a- retry returns the same error); or
upstream_unavailablewith no field, worth one retry, when the model returned output we could not- use — unparseable, or a window with a missing or mangled date.
- Resolving the caller's config, before any analysis, can also refuse
- with
upstream_unavailableornot_entitled(authorization refused).
Try it
Code examples
- curl
- TypeScript
- Python
curl -s https://mcp.edited.com/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "MCP-Protocol-Version: 2025-06-18" \
-H "x-api-key: $MCP_API_KEY" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "researchmcp-research_analyze_query",
"arguments": {
"query": "Nike",
"now": "example"
}
}
}'
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
const client = new Client({ name: "demo", version: "1.0.0" }, { capabilities: {} });
await client.connect(
new StreamableHTTPClientTransport(new URL("https://mcp.edited.com/mcp"), {
requestInit: { headers: { "x-api-key": process.env.MCP_API_KEY ?? "" } },
}),
);
const result = await client.callTool({
name: "researchmcp-research_analyze_query",
arguments: {
"query": "Nike",
"now": "example"
},
});
import os
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
headers = {"x-api-key": os.environ["MCP_API_KEY"]}
async with streamablehttp_client("https://mcp.edited.com/mcp", headers=headers) as (read, write, _):
async with ClientSession(read, write) as session:
await session.initialize()
result = await session.call_tool(
"researchmcp-research_analyze_query",
{"query": "Nike", "now": "example"},
)
Input schema
{
"type": "object",
"properties": {
"query": {
"description": "The full natural-language research query to analyse for topic/format intent and any time window.",
"type": "string"
},
"now": {
"description": "Anchor date for relative expressions like 'last 30 days'; defaults to today. Pass for reproducible results.",
"format": "date",
"type": "string"
}
},
"required": [
"query"
]
}
Output schema
Describes both branches of the envelope — result on success, error on a refusal. Validate against this rather than pattern-matching the prose above; see Concepts → Response shape.
{
"type": "object",
"properties": {
"result": {
"properties": {
"intent": {
"description": "Retrieval-intent axes extracted from the query.\n\nValues are slugs to pass to the matching parameter on a search tool, which\nis where each axis's accepted vocabulary is advertised.",
"properties": {
"article_types": {
"items": {
"type": "string"
},
"type": "array"
},
"topics": {
"items": {
"type": "string"
},
"type": "array"
},
"gender": {
"items": {
"type": "string"
},
"type": "array"
},
"season": {
"items": {
"type": "string"
},
"type": "array"
},
"year": {
"items": {
"type": "string"
},
"type": "array"
},
"moments": {
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"article_types",
"topics",
"gender",
"season",
"year",
"moments"
],
"type": "object"
},
"date_range": {
"anyOf": [
{
"description": "The window a query resolved to; a null bound is open-ended, never absent.",
"properties": {
"start_date": {
"anyOf": [
{
"format": "date",
"type": "string"
},
{
"type": "null"
}
]
},
"end_date": {
"anyOf": [
{
"format": "date",
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"start_date",
"end_date"
],
"type": "object"
},
{
"type": "null"
}
],
"description": "Null for a query with no genuinely temporal phrase — a year, a season or a named event is surfaced in intent instead."
},
"interpretation": {
"type": "string"
},
"is_inferred": {
"type": "boolean"
}
},
"required": [
"intent",
"date_range",
"interpretation",
"is_inferred"
],
"type": "object"
},
"error": {
"type": "object",
"description": "Present instead of `result` when the call was rejected. Branch on `code`; never string-match `message`.",
"properties": {
"code": {
"type": "string",
"enum": [
"invalid_input",
"not_entitled",
"upstream_unavailable",
"internal"
]
},
"message": {
"type": "string"
},
"field": {
"type": "string",
"description": "Offending parameter, when the failure is attributable to one."
},
"details": {
"type": "object",
"additionalProperties": true
}
},
"required": [
"code",
"message"
]
}
},
"oneOf": [
{
"required": [
"result"
]
},
{
"required": [
"error"
]
}
],
"x-fastmcp-wrap-result": true
}