arxiv_recent
Fetch recent arXiv papers in a specified category, ordered by submission date. Customize search with recency filter and result limit.
Instructions
Fetch recent arXiv papers in a category, sorted by submission date (newest first). days filters by published date.
Common categories: cs.AI (general AI), cs.LG (machine learning), cs.CV (computer vision), cs.CL (NLP), cs.HC (HCI / UX), cs.RO (robotics), cs.NE (neural networks), stat.ML (statistical ML), eess.IV (image/video processing — medical imaging lives here), eess.SP (signal processing), q-bio.QM (quantitative biology).
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| category | Yes | ||
| days | No | ||
| max_results | No | ||
| response_format | No | markdown |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |
Implementation Reference
- trends_mcp.py:350-368 (registration)The @_maybe_tool decorator registers the 'arxiv_recent' tool with FastMCP when the 'arxiv' source is enabled. It sets the tool name, description with common arXiv categories, and read-only annotations.
@_maybe_tool( source="arxiv", name="arxiv_recent", description=( "Fetch recent arXiv papers in a category, sorted by submission date " "(newest first). `days` filters by `published` date.\n\n" "Common categories: cs.AI (general AI), cs.LG (machine learning), " "cs.CV (computer vision), cs.CL (NLP), cs.HC (HCI / UX), " "cs.RO (robotics), cs.NE (neural networks), stat.ML (statistical ML), " "eess.IV (image/video processing — medical imaging lives here), " "eess.SP (signal processing), q-bio.QM (quantitative biology)." ), annotations={ "readOnlyHint": True, "destructiveHint": False, "openWorldHint": True, "idempotentHint": False, }, ) - trends_mcp.py:369-407 (handler)The arxiv_recent async function implements the tool logic: validates input via ArxivRecentInput model, queries arXiv API for papers by category sorted by submission date, filters by the specified days window, and formats results as Markdown or JSON.
async def arxiv_recent( category: str, days: int = 7, max_results: int = 20, response_format: ResponseFormat = ResponseFormat.MARKDOWN, ) -> str: try: args = ArxivRecentInput( category=category, days=days, max_results=max_results, response_format=response_format, ) # Over-fetch because arXiv has no native date filter. fetch_n = min(args.max_results * 3, 100) params = { "search_query": f"cat:{args.category}", "sortBy": "submittedDate", "sortOrder": "descending", "start": 0, "max_results": fetch_n, } text = await _http_get_text(ARXIV_API, params=params, ttl=TTL_DEFAULT) papers = _parse_arxiv_atom(text) cutoff = _utc_now() - timedelta(days=args.days) filtered: list[dict[str, Any]] = [] for p in papers: try: pub_dt = datetime.fromisoformat(p["published"].replace("Z", "+00:00")) except ValueError: continue if pub_dt >= cutoff: filtered.append(p) if len(filtered) >= args.max_results: break header = f"arXiv `{args.category}` — 최근 {args.days}일 ({len(filtered)}건)" return _format(filtered, args.response_format, render_md=lambda x: _render_arxiv_md(x, header)) except Exception as e: return _handle_error(e, "arxiv_recent") - trends_mcp.py:282-287 (schema)ArxivRecentInput Pydantic model defines the input schema: 'category' (min 2, max 40 chars), 'days' (1-30, default 7), 'max_results' (1-50, default 20), and 'response_format' (markdown/json).
class ArxivRecentInput(BaseModel): model_config = ConfigDict(str_strip_whitespace=True, extra="forbid") category: str = Field(..., min_length=2, max_length=40, description="arXiv category, e.g. cs.AI, cs.HC, eess.IV") days: int = Field(7, ge=1, le=30) max_results: int = Field(20, ge=1, le=50) response_format: ResponseFormat = ResponseFormat.MARKDOWN - trends_mcp.py:299-330 (helper)_parse_arxiv_atom parses the arXiv Atom XML response into a list of paper dicts with id, url, title, summary, published, updated, authors, and primary_category.
def _parse_arxiv_atom(xml_text: str) -> list[dict[str, Any]]: root = ET.fromstring(xml_text) out: list[dict[str, Any]] = [] for entry in root.findall("atom:entry", ATOM_NS): eid = (entry.findtext("atom:id", default="", namespaces=ATOM_NS) or "").strip() title = (entry.findtext("atom:title", default="", namespaces=ATOM_NS) or "").strip() summary = (entry.findtext("atom:summary", default="", namespaces=ATOM_NS) or "").strip() published = (entry.findtext("atom:published", default="", namespaces=ATOM_NS) or "").strip() updated = (entry.findtext("atom:updated", default="", namespaces=ATOM_NS) or "").strip() authors = [ (a.findtext("atom:name", default="", namespaces=ATOM_NS) or "").strip() for a in entry.findall("atom:author", ATOM_NS) ] cats = [ c.attrib.get("term", "") for c in entry.findall("{http://arxiv.org/schemas/atom}primary_category") ] # Extract arXiv id from URL like http://arxiv.org/abs/2604.12345v1 arxiv_id = eid.rsplit("/", 1)[-1] if eid else "" out.append( { "id": arxiv_id, "url": eid, "title": title, "summary": summary, "published": published, "updated": updated, "authors": authors, "primary_category": cats[0] if cats else "", } ) return out - trends_mcp.py:333-347 (helper)_render_arxiv_md renders a list of arXiv papers into a Markdown-formatted string with numbered entries, links, author lists, and truncated abstracts.
def _render_arxiv_md(papers: list[dict[str, Any]], header: str) -> str: if not papers: return f"# {header}\n\n_결과 없음_" lines = [f"# {header}", f"_총 {len(papers)}건_", ""] for i, p in enumerate(papers, 1): authors = ", ".join(p["authors"][:4]) if len(p["authors"]) > 4: authors += f" 외 {len(p['authors']) - 4}명" lines.append( f"## {i}. [{p['title']}]({p['url']})\n" f"- `{p['id']}` · {p['primary_category']} · {_fmt_date(p['published'])}\n" f"- 저자: {authors}\n" f"- {_trim(p['summary'], 500)}\n" ) return "\n".join(lines)