gengomcp
# gengomcp
An [MCP](https://modelcontextprotocol.io/) server (Python, stdio transport) that
lets an agent retrieve ACL conference papers about NLP from a [Qdrant](https://qdrant.tech/) vector
database. It combines **semantic search** (Sentence‑Transformers embeddings)
with **structured filtering** by bibliographic fields like publication year and
venue.
> **Qdrant access is currently limited.** This server queries a shared Qdrant
> collection of ACL NLP papers. If you'd like credentials to use it, please
> reach out to the project maintainer — access may be granted at a limited
> scale. You'll receive a `QDRANT_URL`, `QDRANT_KEY`, and
> `QDRANT_COLLECTION_NAME` to set in your MCP client's `env` field.
## Quick start
1. **Install** `gengomcp` from PyPI:
```bash
pip install gengomcp
```
2. **Configure credentials** in your MCP client's `env` field. You'll need
`QDRANT_URL`, `QDRANT_KEY`, and `QDRANT_COLLECTION_NAME` — see
[Wiring it into an MCP client](#wiring-it-into-an-mcp-client) for full config
examples.
3. **Use it.** Your agent can now call `search_papers`, `get_paper`,
`list_papers`, and `get_collection_info` to find ACL NLP conference papers.
### Wiring it into an MCP client
Any MCP client over **stdio** works. When installed from PyPI (`pip install
gengomcp` or `uv tool install gengomcp`), the `gengomcp` command is on your
PATH and runs independently of your working directory, so it's safe to launch
from anywhere.
Example for Claude Desktop (`claude_desktop_config.json`):
```json
{
"mcpServers": {
"gengomcp": {
"command": "gengomcp",
"args": []
}
}
}
```
#### Configuring credentials via the MCP client
Credentials (`QDRANT_URL`, `QDRANT_KEY`, `QDRANT_COLLECTION_NAME`) are read from
the process environment. Inject them **directly through your MCP client's `env`
field** — this is the recommended way to configure per-agent credentials:
```json
{
"mcpServers": {
"gengomcp": {
"command": "gengomcp",
"args": [],
"env": {
"QDRANT_URL": "https://<cluster>.cloud.qdrant.io",
"QDRANT_KEY": "<your-api-key>",
"QDRANT_COLLECTION_NAME": "papers_test"
}
}
}
}
```
> Credentials come from the MCP client's `env` field and are **never logged or
> hard-coded**. They live only on your machine — they are not sent to any
> third-party service.
Required variables (no defaults):
| Variable | Description |
| --- | --- |
| `QDRANT_URL` | Qdrant cluster URL |
| `QDRANT_KEY` | Qdrant API key |
| `QDRANT_COLLECTION_NAME` | Collection to search (e.g. `papers_test`) |
Optional variables (have defaults; not needed for basic use):
`EMBEDDING_MODEL`, `AUTO_CREATE_INDEXES`, `LOG_LEVEL`.
If a required variable is missing at startup, the server exits with a clear error
explaining how to set it.
#### Poolside (`pool`)
The server is registered to use the PyPI-installed `gengomcp` command with
credentials injected via the `env` field. Verify with:
```bash
pool mcp list # shows: gengomcp
pool mcp get gengomcp # shows the stored command + args + env vars
```
The config is stored under `mcp_servers` in `~/.config/poolside/settings.yaml`
(personal config). Credentials are passed via the `env` field and live only on
your machine — they are never sent to Poolside's servers. To remove the server
later:
```bash
pool mcp remove gengomcp
```
## Tools
| Tool | Purpose |
| --- | --- |
| `search_papers` | **Semantic search for ACL NLP papers.** USE when the user has a topic/question. Embeds `query` and returns the most similar papers, optionally narrowed by structured filters. |
| `get_paper` | USE to inspect a single ACL NLP paper in full detail (abstract, summaries, entities) when you already have its `paper_uuid` from a search result. |
| `list_papers` | USE to **browse/filter ACL NLP papers with no query text** — pure structured filtering + pagination (e.g. "all ACL 2024 papers"). |
| `get_collection_info` | USE first to discover available venues, years, fields of study, and vector names before building filters. |
### `search_papers` parameters
```
query str (required) search text
limit int = 10 (clamped 1..100)
vector_name str = "overview" one of overview/approach/challenge/outcome
year int exact publication year (e.g. 2026)
year_min / year_max int year range (inclusive)
year_gt / year_lt int year range (exclusive)
venue str substring match on the booktitle (e.g. "Annual Meeting")
collection_acronym str exact venue acronym, e.g. "ACL" / "EMNLP" / "NAACL"
collection_id str e.g. "2026.acl"
field_of_study list[str] membership on `field_of_studies` (e.g. ["Reasoning"])
author str name contained in `author_names`
min_score float only return results with similarity >= this value
```
All filters are AND‑combined, so you can layer them, e.g.
`search_papers(query="...", year_min=2020, collection_acronym="ACL")`.
### Example tool calls
```
search_papers(query="stress testing large language models",
vector_name="overview", year_min=2024, year_max=2026,
collection_acronym="ACL", limit=5)
get_paper(paper_id="000036a6-e2be-523e-8b8d-0f2cbe2b39e7")
list_papers(collection_acronym="EMNLP", year=2024, limit=20)
list_papers(field_of_study=["Reasoning"], author="Pan", limit=20, offset=<prev_uuid>)
```
## How it works
* **Secrets & config** — credentials are set via your MCP client's `env` field
(`QDRANT_URL`, `QDRANT_KEY`, `QDRANT_COLLECTION_NAME`). `QDRANT_KEY` is
passed directly to the Qdrant client and is never printed or hard-coded.
* **Payload indexes** — Qdrant **requires** a payload index to filter on a
field. This collection ships with no indexes, so the server creates the needed
ones **idempotently at startup** (non-destructive — it only adds indexes).
Disable with `AUTO_CREATE_INDEXES=0` if you manage indexes yourself.
* **Embeddings** — queries are embedded with Sentence‑Transformers using
`Snowflake/snowflake-arctic-embed-s`, the **only** model that matches this
collection's 384-dimensional index. The server can truncate+renormalise other
model outputs to the index dimensionality (matryoshka‑style) as a safety net,
but models in a different embedding space (e.g. the 768-dim `m-v1.5`) will
still fail to retrieve — see [The embedding model](#the-embedding-model).
* **Named vectors** — the `overview`/`approach`/`challenge`/`outcome` named
vectors in the collection are all **384-dimensional**.
### The embedding model
The collection's vectors are **384-dimensional** and were built with the
**Snowflake arctic-embed "s" model** (`Snowflake/snowflake-arctic-embed-s`).
This is the **only** model that produces embeddings in the correct space for
this index — it is the default and **should not be changed**.
Other models in the Snowflake family (e.g. `m-v1.5` at 768-dim or `l-v1.5` at
1024-dim) live in **different embedding spaces**. Even though the server can
truncate embeddings to the index dimensionality (matryoshka-style) as a safety
net, those models will **not** retrieve against this collection — keep
`EMBEDDING_MODEL` at its default unless you re-index with a different model.
## Project layout
```
gengomcp/
├── server.py # the MCP server (tools + Qdrant/Embeddings glue)
├── main.py # thin launcher
├── pyproject.toml # deps + `gengomcp` console script
├── uv.lock # pinned dependency versions
├── LICENSE # MIT
├── .env.example # template for all config vars (committed)
└── README.md
```
## Development / testing
```bash
uv run python -c "import server; print('ok')"
```
TDQS
Scored across 4 tools
Each tool has a distinct role: semantic search, ID-based retrieval, filter-based browsing, and schema discovery. There is no overlap in purpose; search_papers and list_papers differ meaningfully by query type.
All tool names follow a consistent verb_noun pattern with lowercase and underscores: search_papers, get_paper, list_papers, get_collection_info. This is predictable and easy to understand.
Four tools is well-scoped for a read-only ACL paper search and retrieval server. Each tool covers a core capability without unnecessary bloat.
The tool surface covers the full read-only lifecycle: discover schema, search semantically, browse with filters, and retrieve a specific paper by ID. There are no obvious missing operations for the stated purpose.