List Reports (Research)
researchmcp-research_list_reports
Browse what has been published lately, newest-first by publish date.
This is a browsing tool, not a discovery tool — reach for it only when the question is about recency itself: "what has EDITED published lately", "what reports do we have from last month". It orders by publish date with no topic, audience or coverage filter, so it will not surface the reports that define a topic even when they exist.
For anything topic-shaped — a trend, a category, a season's direction — do not
start here: call research_analyze_query and then research_article_search
(drilling in with research_chunk_search), which rank on topic, audience and
season rather than date order.
The date window is the report's publish date, not the season it is about: a report covering SS27 typically publishes months earlier, so a calendar-SS27 window would miss it.
Both dates are optional; research_analyze_query converts a
natural-language window into them. Omit cursor on the first call.
"what reports do we have from last month", "list reports since January", "anything published this week".
Parameters
| Name | Type | Required | Constraints | Description |
|---|---|---|---|---|
start_date | string | no | — | Inclusive lower bound (YYYY-MM-DD). |
end_date | string | no | — | Inclusive upper bound (YYYY-MM-DD). |
limit | integer | no | default: 20 · 1–100 | Page size. |
cursor | string | no | — | Opaque pagination token from a previous response's next_cursor. |
Returns
- On success: {"result": {"total": int, "reports": list[ReportSummary],
- "next_cursor": str | None}}. Each report carries
report_id, title,date,modified,excerpt, andtags— the report's- curated taxonomy tags (
article_types,topics,gender,season, year,moments), which tell you a "SS27 Buyers' Guide" is menswear- before you open it. An untagged axis is an empty list. For a public
- link to a report_id call
research_report_links; for the full article - text call
research_read_report. totalis the number of reports on this page (it feeds themcp.tool.result_countmetric); usenext_cursor(None on the- last page) to gauge whether more exist.
- On retrieval failure or invalid cursor/limit: {"error": {code, message}}
codeisinvalid_input,upstream_unavailable,internal(our own bad data; a retry returns the same error), ornot_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_list_reports",
"arguments": {
"start_date": "example",
"end_date": "example",
"limit": 20,
"cursor": "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_list_reports",
arguments: {
"start_date": "example",
"end_date": "example",
"limit": 20,
"cursor": "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_list_reports",
{"start_date": "example", "end_date": "example", "limit": 20, "cursor": "example"},
)
Input schema
{
"type": "object",
"properties": {
"start_date": {
"description": "Inclusive lower bound (YYYY-MM-DD).",
"format": "date",
"type": "string"
},
"end_date": {
"description": "Inclusive upper bound (YYYY-MM-DD).",
"format": "date",
"type": "string"
},
"limit": {
"default": 20,
"description": "Page size.",
"maximum": 100,
"minimum": 1,
"type": "integer"
},
"cursor": {
"description": "Opaque pagination token from a previous response's `next_cursor`.",
"type": "string"
}
}
}
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": {
"total": {
"type": "integer"
},
"reports": {
"items": {
"properties": {
"report_id": {
"type": "integer"
},
"title": {
"type": "string"
},
"date": {
"format": "date",
"type": "string"
},
"modified": {
"format": "date-time",
"type": "string"
},
"excerpt": {
"type": "string"
},
"tags": {
"description": "The parent report's curated taxonomy tags, keyed by the public intent axes.\n\nPopulated on every search hit, browse row and report lookup so a consumer can\nsee a report's audience, season and subject matter *at discovery time* — a\ntitle alone does not say whether a \"SS27 Buyers' Guide\" is menswear or\nwomenswear, and grounding a womenswear claim in a menswear report is the\nfailure this prevents.\n\nThe values are drawn from the vocabulary the search tools' intent params\naccept, so a tag read off a hit can usually be threaded straight back in to\nnarrow a follow-up search. Two limits on that, both worth knowing before\ntreating a threaded tag as a guaranteed narrowing:\n\n- **The vocabularies are not identical.** These are curated report-side\n labels, so a value can exist here that the params do not recognise (prod\n carries ``moments: [\"pride\"]``, which is outside ``MomentsParam``'s enum).\n Threading such a value is ignored by ``normalise_intent`` or rejected by the\n param schema — never a silent narrowing.\n- **``year`` and ``moments`` are not pure ranking axes.** Unlike the other\n four, they also derive the publish-date window when no explicit dates are\n given (see ``retrieval/date_window.py``), so threading one back moves the\n filter as well as the boost.\n\nEvery axis is always present on the model; an untagged axis is an empty list.\nCoverage is uneven and heavily skewed by report class — buyers' guides are\nclose to fully tagged for ``gender``, while a large minority of the corpus\ncarries no taxonomy at all — so an empty axis is common and means \"not\ncurated\", never \"out of scope\". (One near-exception: a topic tag that exists\nonly as an all-encompassing label is dropped rather than surfaced, matching\nwhat the scorer sees — see ``canonicalise_topics``.)\n\nTwo exceptions, both id re-fetches: the chunk and image indices carry no\ntaxonomy tags and ``get_chunk`` / ``get_image`` do not spend a second query\nresolving them, so the field stays at its empty default there — and the\nresearch_get_chunk / research_get_image tools serve ``ChunkRow`` / ``ImageRow``,\nwhich have no ``tags`` field at all, rather than let an empty axis read as\n\"this report is untagged\". So `tags` is absent from those two tools' published\nschemas; wherever it *is* published, an empty axis does mean untagged.",
"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"
}
},
"type": "object"
}
},
"required": [
"report_id",
"title",
"date",
"modified",
"excerpt"
],
"type": "object"
},
"type": "array"
},
"next_cursor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"total",
"reports",
"next_cursor"
],
"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
}