{
  "markdown": "<!-- mcp-name: io.github.tiptreesystems/lacuna-research-mcp -->\n\n<p align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/tiptreesystems/lacuna-research-mcp/main/assets/banner.svg\" alt=\"Lacuna Research MCP — Empower your coding agent for machine learning research\" width=\"100%\" />\n</p>\n<p align=\"center\">\n  <a href=\"https://www.python.org/\"><img src=\"https://img.shields.io/badge/Python-3.11%2B-3776AB?logo=python&amp;logoColor=white\" alt=\"Python 3.11+\" /></a>\n  <a href=\"https://modelcontextprotocol.io/\"><img src=\"https://img.shields.io/badge/MCP-server-7C3AED\" alt=\"MCP server\" /></a>\n  <a href=\"https://github.com/tiptreesystems/lacuna-research-mcp/blob/main/LICENSE\"><img src=\"https://img.shields.io/badge/license-MIT-green.svg\" alt=\"MIT license\" /></a>\n</p>\n\n# Lacuna Research MCP\n\n### 🔬 Ground your coding agent in novel ideas, papers, and the ML landscape\n\n\n\nLacuna Research MCP gives AI researchers' coding agents:\n\n- **Novel research proposals.** Explore novel research ideas generated with [Alien Science](https://openreview.net/pdf?id=XZWkDET1ia).\n- **Research directions.** Navigate concept clusters with linked papers, authors, and proposals.\n- **Agent-ready literature.** Search recent papers in markdown with source links.\n- **Researcher intelligence.** Trace authors, publications, directions, impact, and related researchers.\n- **Landscape mapping.** Compare venues, institutions, leading researchers, and publication activity.\n\n[Lacuna](https://lacuna.tiptreesystems.com), built by [Tiptree Systems](https://tiptreesystems.com), is a research map of machine learning: a heterogeneous knowledge graph linking papers, research directions, authors, venues, institutions, and generated research proposals, with a source trail from every derived object back to the exact paper and page that produced it. Its pipeline reconciles scholarly records from OpenAlex, OpenReview, DBLP, and arXiv; extracts concept elements from paper text and clusters them into research directions ([Lacuna paper](https://arxiv.org/abs/2606.26246)); and samples novel research proposals from those directions with [Alien Science](https://arxiv.org/abs/2603.01092). The map spans more than **730,000 papers**, **190,000+ author profiles**, **38,000+ research directions**, and **3,000+ research proposals** built from over **15 million concept elements** — and it grows continuously as new arXiv and other AI papers are ingested.\n\n[Install](#install) · [First use](#first-use) · [Tools](#what-it-exposes) · [API reference](#wrapped-apis) · [Configuration](#environment-variables)\n\n## Install\n\nThe easiest way to install Lacuna Research MCP is to ask your coding agent, such as Codex or Claude Code:\n\n> Install and configure the `lacuna-research-mcp` package from PyPI for this client.\n\nFor manual setup, the instructions below use [`uvx`](https://docs.astral.sh/uv/guides/tools/) to run the latest tagged release from PyPI. Install [uv](https://docs.astral.sh/uv/getting-started/installation/) first; Lacuna Research MCP requires Python 3.11 or newer.\n\n### Codex\n\nAdd the server with the Codex CLI:\n\n```bash\ncodex mcp add lacuna-research -- uvx lacuna-research-mcp\n```\n\nAlternatively, add the following to `~/.codex/config.toml` (or to `.codex/config.toml` in a trusted project for project-only setup):\n\n```toml\n[mcp_servers.lacuna-research]\ncommand = \"uvx\"\nargs = [\"lacuna-research-mcp\"]\n```\n\nRun `codex mcp list` to verify the server is configured. The Codex app, CLI, and IDE extension share this configuration on the same machine.\n\n### Claude Code\n\nAdd the server for all of your projects with the Claude Code CLI:\n\n```bash\nclaude mcp add --scope user lacuna-research -- uvx lacuna-research-mcp\n```\n\nOmit `--scope user` to add it only to the current project. Alternatively, add the following under the top-level `mcpServers` object in `~/.claude.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"lacuna-research\": {\n      \"type\": \"stdio\",\n      \"command\": \"uvx\",\n      \"args\": [\"lacuna-research-mcp\"]\n    }\n  }\n}\n```\n\nRun `claude mcp get lacuna-research` to verify the server is configured.\n\n### Claude Desktop\n\nOpen **Settings → Developer → Edit Config**, then add the server under `mcpServers` in `claude_desktop_config.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"lacuna-research\": {\n      \"command\": \"uvx\",\n      \"args\": [\"lacuna-research-mcp\"]\n    }\n  }\n}\n```\n\nRestart Claude Desktop after saving the file.\n\n### Other MCP clients\n\nFor any client that supports local stdio MCP servers, use this standard configuration:\n\n```json\n{\n  \"mcpServers\": {\n    \"lacuna-research\": {\n      \"command\": \"uvx\",\n      \"args\": [\"lacuna-research-mcp\"]\n    }\n  }\n}\n```\n\n### Standalone command\n\nInstall the MCP server as a persistent command:\n\n```bash\nuv tool install lacuna-research-mcp\nlacuna-research-mcp\n```\n\nRun it without installing a persistent command:\n\n```bash\nuvx lacuna-research-mcp\n```\n\n### Latest development version\n\nPyPI contains tagged releases. To try the latest code from the `main` branch instead:\n\n```bash\nuvx --from git+https://github.com/tiptreesystems/lacuna-research-mcp.git lacuna-research-mcp\n```\n\n### Local development\n\nWith uv:\n\n```bash\ngit clone https://github.com/tiptreesystems/lacuna-research-mcp.git\ncd lacuna-research-mcp\nuv sync --extra dev\nuv run lacuna-research-mcp\n```\n\nWith pip:\n\n```bash\ncd <path-to-lacuna-research-mcp>\npython3 -m venv .venv\n. .venv/bin/activate\npython -m pip install -U pip\npython -m pip install -e .\n```\n\n## First use\n\nAfter connecting the server, call:\n\n1. `search_lacuna(query=\"LLM jailbreak defense\", search_type=\"hypothesis\", limit=10)`\n2. `search_lacuna(query=\"methods for detecting prompt injection attacks\", search_type=\"papers\", limit=10)` (production lexical+semantic paper ranking by default)\n3. `get_hypothesis(hypothesis_id_or_url=\"bd35de182c2325ae\")`\n4. `get_paper(artifact_id_or_url=\"art_79c57fbfec094f26b79c422cf08fed34\")` (defaults to `view=\"context\"`)\n5. `get_direction(cluster_id_or_url=25108)` (defaults to `view=\"context\"`)\n\n## Scope\n\nThe corpus covers machine learning and AI research: papers, research directions, authors' research output, venues, institutions, and generated research hypotheses. It does not contain biographies, news, or non-research web content. Agents should answer questions outside that scope from other sources.\n\n## What it exposes\n\n- `search_lacuna`\n  Uses Lacuna's public `/api/v1/search` endpoint for directions, papers, authors, venues, institutions, and hypotheses. Explicit paper searches (`search_type=\"paper\"`) use the server's production lexical+semantic ranker when the other ranking arguments remain at their defaults. Pass `search_type=\"hypothesis\"` (or `\"hypotheses\"` / `\"proposal\"` / `\"proposals\"`) for hypothesis search.\n- `get_hypothesis(hypothesis_id_or_url, view=\"context\")`\n  Hypothesis/proposal. `view=\"context\"` (default) is a compact single-fetch proposal context (summary, abstract, linked directions); `view=\"full\"` returns the server's version record with version history and signal counts. Proposal bodies are in `versions[].markdown`.\n- `get_direction(cluster_id_or_url, view=\"context\")`\n  Research direction/cluster. `view=\"context\"` (default) requests the compact agent-oriented summary; `view=\"full\"` returns the raw cluster record.\n- `get_direction_papers(cluster_id_or_url, page, limit, view=\"compact\")`\n  Paginated papers attached to a direction. `view=\"compact\"` (default) returns citation-ready rows (id, url, title, year, venue, a few authors, abstract snippet); `view=\"full\"` returns the raw upstream paper records.\n- `get_paper(artifact_id_or_url, view=\"context\", figure_limit=None)`\n  Paper lookup. `view=\"context\"` (default) requests the compact agent-oriented context; other views are `\"full\"`, `\"preview\"`, `\"blog\"`, `\"figures\"`, `\"concepts\"`, or `\"neighbors\"`. In context view, `figure_limit` caps the figure preview (server default 3; pass 0 to suppress previews while keeping a `figures_truncated` signal).\n- Author tools:\n  `get_author_context(…, view=\"context\")`, `get_author_papers`, `get_author_directions`, and `get_author_neighbors`. Start with `get_author_context`, which defaults to the compact agent-oriented view (capped papers plus a readable `impact_directions` list instead of raw `impact_clusters` telemetry). Use the dedicated papers, directions, and neighbors tools to page through those collections without repeating the author context. `view=\"full\"` returns the server-bounded full-shape context (collections remain capped at 100). Pass `include_neighbors=true` to explicitly include similar authors; this may add significant server latency.\n- Venue and institution tools:\n  `get_venue_context(…, view=\"context\")`, `get_institution_context(…, view=\"context\")`, `get_institution_authors`. Context tools default to compact (capped lists, duplicated blocks dropped; venue keeps a recent-activity slice that always includes the requested `year`). Use `get_institution_authors` to page through an institution's complete author list.\n\n## Wrapped APIs\n\n| MCP tool | Lacuna API endpoint |\n| --- | --- |\n| `search_lacuna(query, search_type, limit, offset, date_from, date_to, venue, sort, ranking_profile, fields)` | `GET /api/v1/search` (`fields` restricts/weights the text fields used for lexical ranking, e.g. `title^4,abstract`, and selects the experimental lexical ranker; for a default relevance-sorted paper search, this bypasses the production lexical+semantic ranker. Allowed fields are `title`, `abstract`, `summary`, `concepts`, `name`, `top_names`, `venue`, each valid only for the types that carry it — `title`: paper/cluster/venue/hypothesis; `abstract`/`summary`/`concepts`: paper; `name`: author/institution/venue; `top_names`: cluster/hypothesis; `venue`: paper/venue (`search_type=\"all\"` spans all). Weights must satisfy `0 < weight <= 100`. Unknown fields, out-of-range weights, type-incompatible fields, and `fields` combined with `ranking_profile=\"semantic\"` are rejected, since the server would otherwise silently drop, cap, or ignore them.) |\n| `get_hypothesis(hypothesis_id_or_url, view=\"context\")` | `view=\"context\"` → `GET /api/v1/context/hypothesis/{hypothesis_id}?view=compact`; `view=\"full\"` → `GET /api/v1/hypotheses/{hypothesis_id}` |\n| `get_direction(cluster_id_or_url, view=\"context\")` | `view=\"context\"` → `GET /api/v1/context/direction/{cluster_id}?view=compact`; `view=\"full\"` → `GET /api/v1/clusters/{cluster_id}` |\n| `get_direction_papers(cluster_id_or_url, page, limit, view=\"compact\")` | `GET /api/v1/clusters/{cluster_id}/papers?view=compact` (default) or `?view=complete` |\n| `get_paper(artifact_id_or_url, view=\"context\", figure_limit=None)` | `view=\"context\"` → `GET /api/v1/context/paper/{artifact_id}?view=compact` (`&figure_limit=N` when set); `view=\"full\"` → `GET /api/v1/papers/{artifact_id}`; `view=\"preview\"` → `…/preview`; `view=\"blog\"` → `…/blog`; `view=\"figures\"` → `…/figures`; `view=\"concepts\"` → `…/concepts`; `view=\"neighbors\"` → `…/neighbors` |\n| `get_author_papers(author_id_or_url, limit=50, offset=0)` | `GET /api/v1/authors/{author_id}/papers` |\n| `get_author_directions(author_id_or_url, limit=50, offset=0)` | `GET /api/v1/authors/{author_id}/directions` |\n| `get_author_context(author_id_or_url, view=\"context\", include_neighbors=false)` | `view=\"context\"` → `GET /api/v1/context/author/{author_id}?view=compact`; `view=\"full\"` → `GET /api/v1/context/author/{author_id}` (`include_neighbors=true` explicitly requests similar authors) |\n| `get_author_neighbors(author_id_or_url, limit=8, offset=0)` | `GET /api/v1/authors/{author_id}/neighbors` |\n| `get_venue_context(venue_key_or_url, year, view=\"context\")` | `view=\"context\"` → `GET /api/v1/context/venue/{vkey}[/{year}]?view=compact`; `view=\"full\"` → same route without `view` |\n| `get_institution_context(institution_key_or_url, view=\"context\")` | `view=\"context\"` → `GET /api/v1/context/institution/{ikey}?view=compact`; `view=\"full\"` → same route without `view` |\n| `get_institution_authors(institution_key_or_url, limit=50, offset=0)` | `GET /api/v1/institutions/{ikey}/authors` |\n\nFor id-or-URL arguments, pass either the raw id returned by `search_lacuna` or the corresponding Lacuna page URL. The MCP normalizes Lacuna-relative `url` and `*_url` fields to absolute URLs, and it also absolutifies Lacuna links inside fields named `summary_markdown`, `article_markdown`, `markdown`, `content`, or `description`.\n\n`get_author_context` is bounded server-side in both views. The default compact view returns a curated briefing; `view=\"full\"` returns the larger complete shape with embedded collections capped at 100. Use `get_author_papers` and `get_author_directions` rather than trying to page embedded context collections.\n\nSearch requests are capped at 50 results per call, and direction-paper page\nrequests are capped at 100 results per call.\nIn `search_lacuna`, `date_from` and `date_to` are inclusive publication-date\nbounds. Accepted formats are `YYYY`, `YYYY-MM`, and `YYYY-MM-DD`; for example,\n`date_from=\"2020\", date_to=\"2022-03\"` includes papers from January 1, 2020\nthrough March 31, 2022.\n\n`search_lacuna` exposes these ranking profiles:\n\n- `default` / `lexical`\n  The default profile for all searches. With `search_type=\"paper\"`, `sort=\"relevance\"`, and `fields` unset, it uses the server's production lexical+semantic ranker with graceful fallback. The MCP's default `search_type=\"all\"` uses the server's default lexical ranking instead.\n- `semantic`\n  Use for embedding-based paper retrieval. The semantic query omits the normal lexical ranking leg, but the server can still overlay exact-title lexical matches. Only supported for `paper` and `all` searches (only papers have semantic embeddings).\n- `bm25_title_abstract` / `bm25`\n  Use for lexical matching constrained to title and abstract. Rejected for `author` and `institution` searches (those records have no title or abstract fields).\n\nAll searches use the server default unless `ranking_profile` is provided. The MCP rejects unsupported profile/type combinations because the server would otherwise fall back to substring search and silently ignore the requested ranking profile.\n\n`sort` accepts `relevance` (default), `year_desc`, and `year_asc`. Year sorts cannot be combined with `ranking_profile=\"semantic\"` — the server would silently ignore the sort — so the MCP rejects that combination; for recent-and-relevant queries, keep semantic ranking and constrain recency with `date_from`/`date_to` instead.\n\n## Environment variables\n\n- `LACUNA_SITE_URL`\n  Defaults to `https://lacuna.tiptreesystems.com`\n- `LACUNA_MCP_TIMEOUT`\n  Defaults to `30`\n- `LACUNA_MCP_MAX_RETRIES`\n  Defaults to `2`; applies to timeouts, transport errors, and HTTP `429`, `502`, `503`, and `504` responses.\n- `LACUNA_MCP_USER_AGENT`\n  Defaults to `lacuna-research-mcp/{package_version}`\n- `LACUNA_MCP_BEARER_TOKEN`\n  Optional bearer token sent as `Authorization: Bearer ...` for private Lacuna deployments.\n- `LACUNA_MCP_LOG_LEVEL`\n  Defaults to `WARNING` (one of `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`). The default keeps normal operation quiet; lower it only for debugging, since `INFO`/`DEBUG` let the HTTP client log full request URLs — including the search query string — to stderr, which some MCP hosts retain.\n\nEnvironment variables are read once when the MCP server is created, or on the first direct tool/API call if the module is imported without calling `create_mcp()`.\n\n## Implementation layout\n\nThe server is a thin MCP adapter over Lacuna's HTTP API. The implementation is split by responsibility:\n\n- `lacuna_research_mcp/server.py`\n  MCPServer app creation, tool registration, lifespan cleanup, and the `lacuna-research-mcp` CLI entrypoint.\n- `lacuna_research_mcp/tools.py`\n  MCP tool functions. Each tool normalizes its inputs, calls the matching Lacuna API endpoint through the shared client helpers, and returns JSON-compatible data.\n- `lacuna_research_mcp/client.py`\n  Runtime HTTP access to Lacuna: the event-loop-bound `httpx.AsyncClient`, retry handling, error wrapping, JSON parsing, URL normalization on responses, and `api_get`/`api_object`/`api_payload`.\n- `lacuna_research_mcp/config.py`\n  Runtime constants, `RuntimeConfig`, package user-agent construction, and environment parsing.\n- `lacuna_research_mcp/ids.py`\n  Helpers that accept either raw ids or Lacuna page URLs and extract safe API path segments.\n- `lacuna_research_mcp/normalize.py`\n  Response post-processing for relative Lacuna URLs and markdown links.\n- `lacuna_research_mcp/errors.py`\n  User-facing exception type for Lacuna API access failures.\n\n## Notes\n\n- `get_paper` and `get_direction` default to `view=\"context\"`. These context views request Lacuna's compact agent-oriented payloads by default to keep MCP responses small. Paper context includes `summary_markdown` when available (otherwise `abstract`), authors, and figures; direction context includes `summary_markdown`, capped papers/authors/related directions, and truncation markers. Use `view=\"full\"` when you need the raw metadata, and the other paper views (`preview`, `blog`, `figures`, `concepts`, `neighbors`) when you want one isolated sub-resource.\n- An explicit relevance-sorted paper search with no custom `fields` defaults to the server's production lexical+semantic ranker. Set `ranking_profile=\"semantic\"` for embedding-based retrieval (with a possible exact-title overlay) or `\"bm25_title_abstract\"` for title-and-abstract lexical matching.\n- Search type aliases are normalized client-side, so `papers`, `directions`, and `hypotheses` are accepted and mapped to the server's singular values.\n- Most detail tools accept either the id returned by search or the corresponding Lacuna URL.\n- Relative Lacuna URLs in `url`/`*_url` response fields and fields named `summary_markdown`, `article_markdown`, `markdown`, `content`, or `description` are normalized to absolute URLs.\n- Venue and institution keys are opaque hashes (for example `d7bf22905bd6`), never human-readable names like `icml`. Find the key with `search_lacuna(search_type=\"venue\")` first, or pass a `/venue/...` page URL.\n\n## Citation\n\nIf you find our work helpful, feel free to cite the papers behind Lacuna's research-proposal generation and research map.\n\n**Research-proposal generation — Alien Science**\n\n```bibtex\n@inproceedings{artiles2026alien,\n  title     = {Alien Science: Sampling Coherent but Cognitively Unavailable Research Directions from Idea Atoms},\n  author    = {Artiles, Alejandro H. and Weiss, Martin and Brinkmann, Levin and Goyal, Anirudh and Rahaman, Nasim},\n  booktitle = {ICLR 2026 Workshop on Post-AGI Science and Society},\n  year      = {2026},\n  url       = {https://openreview.net/forum?id=XZWkDET1ia}\n}\n```\n\n**Research map — Lacuna**\n\n```bibtex\n@misc{weiss2026lacunaresearchmapmachine,\n  title         = {Lacuna: A Research Map for Machine Learning},\n  author        = {Martin Weiss and Miles Q. Li and Alejandro H. Artiles and Yacine Mkhinini and Chris Pal and Hugo Larochelle and Nasim Rahaman},\n  year          = {2026},\n  eprint        = {2606.26246},\n  archivePrefix = {arXiv},\n  primaryClass  = {cs.DL},\n  url           = {https://arxiv.org/abs/2606.26246}\n}\n```\n\n## License\n\nMIT. See [LICENSE](https://github.com/tiptreesystems/lacuna-research-mcp/blob/main/LICENSE).\n",
  "bytes": 19246,
  "sha": "7736c393524685a6895319a53e5c1d50afbf344e4cb31bb8e610d0dfc22b160d",
  "repo_slug": "tiptreesystems/lacuna-research-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_tiptreesystems_lacuna_research_c96ee655/readme"
}