Perseus MCP Server: End-User Guide
This guide explains how to install, run, and use the perseus MCP server in tools like Cursor or Claude Desktop.
What You Get
The server provides tools for Greek text research against Perseus CTS and Scaife search. It is designed as a local MCP bridge: your LLM client starts this server, discovers its tools, and can then call those tools with structured arguments instead of relying on ad hoc web browsing or copied URLs.
Use it when you want an LLM to help with tasks such as:
- finding Perseus CTS URNs for authors and works;
- retrieving Greek passages by citation;
- moving to neighboring passages;
- searching Greek text with Unicode Greek or Beta Code input;
- keeping the upstream Perseus/Scaife response available for verification.
The current implementation exposes twelve text-returning tools:
get_passage(urn)get_passage_plus(urn)get_passage_plaintext(urn)get_valid_references(urn, level=None)get_capabilities()list_text_groups(language=None, query=None, limit=100)get_author_resources(author, language=None)get_work_resources(urn_or_title)get_label(urn)get_first_urn(urn)get_prev_next_urn(urn)search_perseus(query, language="greek", query_format="auto")
Raw CTS operations return XML text, search_perseus returns Scaife JSON text,
discovery helpers return locally shaped JSON text, and
get_passage_plaintext returns readable passage text.
Prerequisites
- Python 3.11+
- uv recommended (or pip)
- An MCP-capable client (Cursor, Claude Desktop, MCP Inspector)
Install
From the project root:
uv sync
Alternative:
pip install -e .
Run the Server
uv run server.py
Quick Tool Testing (Inspector)
npx @modelcontextprotocol/inspector uv run server.py
Use the inspector UI to call tools and verify responses.
Use with the LLM Client of Your Choice
MCP separates the tool server from the model. This repository does not choose an LLM for you. Instead, it exposes tools that any MCP-capable client can attach to a model from its supported providers. The same local server command is the key piece of configuration:
uv --directory /full/path/to/Perseus-mcp run server.py
When configuring a client, map that command into the client’s MCP-server config. Most clients ask for the same conceptual fields:
| Field | Value |
|---|---|
| Server name | perseus |
| Command | uv |
| Arguments | --directory, /full/path/to/Perseus-mcp, run, server.py |
| Environment | usually empty |
| Transport | stdio/local process, if the client asks |
After connection, ask your client to list MCP tools or inspect the perseus
server. You should see tools such as get_author_resources,
get_passage_plaintext, get_prev_next_urn, and search_perseus.
Prompting pattern for any LLM
For reliable results, ask the model to use the tools in a research sequence:
- Discover: call
get_author_resources("urn:cts:greekLit:tlg0012", language="greek")orlist_text_groups(language="greek", query="Homer"). - Select a URN: choose an edition/work URN from the JSON or XML result.
- Fetch or navigate: call
get_passage_plaintext(...),get_valid_references(...), orget_prev_next_urn(...). - Verify: when precision matters, call
get_passage(...)orget_passage_plus(...)to inspect the raw CTS XML.
If your preferred LLM application does not support MCP natively, you can still
use the notebooks or write a small Python adapter with FastMCP’s Client to call
these tools and pass the results into that application manually. The optional
examples/06_openrouter_llm_mcp_interaction.ipynb notebook demonstrates such a
client-side adapter for OpenRouter; it requires an OpenRouter API key, unlike
the MCP server and its public Perseus/Scaife upstream calls.
To configure that optional notebook, copy .env.example to .env in the
project root and replace the placeholder:
OPENROUTER_API_KEY=sk-or-v1-...
Get your API key at openrouter.ai. See
OpenRouter’s API key documentation for
authentication details.
Notebook 06_ loads the project-root .env file without overriding an existing
environment variable. The .env file is ignored by Git and must not be
committed.
You may save and commit notebook 06_ with its LLM and tool-call outputs so
they render on GitHub. The notebook file does not store Python variables or
kernel memory, and the implementation does not print the key. Review visible
outputs and run this secret scan before committing:
rg "sk-or-v1-[A-Za-z0-9_-]{20,}" examples/06_openrouter_llm_mcp_interaction.ipynb
The command should produce no output. It does not match the documented
sk-or-v1-... placeholder.
Configure Cursor
Edit your mcp.json:
- macOS:
~/Library/Application Support/Cursor/mcp.json - Windows:
%APPDATA%\Cursor\mcp.json
Example:
{
"mcpServers": {
"perseus": {
"command": "uv",
"args": ["--directory", "/full/path/to/Perseus-mcp", "run", "server.py"],
"env": {}
}
}
}
Restart Cursor, then confirm the perseus server and tools appear.
Configure Claude Desktop
Claude Desktop is an MCP-capable host for local stdio servers. The latest
Claude-related project documentation already treats Claude Desktop like Cursor
and MCP Inspector: it launches the same local perseus server and then exposes
the discovered tools to the selected Claude conversation. The only Claude-specific
part is where you place the JSON configuration and how you restart/debug the
desktop app.
Open Claude Desktop settings, go to Developer, and choose Edit Config. You can also edit the configuration file directly:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
Add or merge the perseus entry under mcpServers:
{
"mcpServers": {
"perseus": {
"command": "uv",
"args": [
"--directory",
"/full/path/to/Perseus-mcp",
"run",
"server.py"
],
"env": {}
}
}
}
Use an absolute project path. If Claude Desktop cannot find uv, replace
"uv" with the full executable path returned by which uv on macOS or
where uv on Windows. Save the file and completely quit and reopen Claude
Desktop so it reloads the MCP server list.
After restart, open a new Claude conversation and look for the MCP/tool indicator
or ask Claude to list available MCP tools. You should see the same perseus
tools documented above, including get_author_resources,
get_passage_plaintext, get_prev_next_urn, and search_perseus.
Generic MCP JSON Example
Some clients use a JSON structure similar to Cursor’s but with different file locations. If your client supports local stdio MCP servers, adapt this shape:
{
"mcpServers": {
"perseus": {
"command": "uv",
"args": [
"--directory",
"/full/path/to/Perseus-mcp",
"run",
"server.py"
],
"env": {}
}
}
}
Check your client’s documentation for the exact config-file path and whether it
uses mcpServers, servers, or another top-level key. The command and argument
values are the important part.
Discovery Tools
The discovery helpers are useful when you do not yet know the exact CTS URN to fetch:
list_text_groups(language=None, query=None, limit=100)lists author/textgroup matches and their works. Uselanguage="greek"orlanguage="latin"to focus the inventory, andqueryto match author names, textgroup URNs, or work titles.get_author_resources(author, language=None)returns detailed JSON for a matching author or textgroup, including work URNs, titles, languages, editions, translations, and other resource URNs.get_work_resources(urn_or_title)narrows directly to a work title or work URN and returns its editions/translations/resources with author context.get_passage_plaintext(urn)fetches a passage through CTS and extracts readable text from the returned XML.
Examples:
list_text_groups(language="greek", query="Homer")list_text_groups(language="latin", query="Ovid")get_author_resources("urn:cts:greekLit:tlg0012", language="greek")get_work_resources("Iliad")get_passage_plaintext("urn:cts:greekLit:tlg0012.tlg001.perseus-grc1:1.1")
URN Tips
Typical CTS URN patterns:
- Work level:
urn:cts:greekLit:tlg0012.tlg001 - Passage level:
urn:cts:greekLit:tlg0012.tlg001:1.1-1.10 - Edition-specific:
urn:cts:greekLit:tlg0012.tlg001.perseus-grc1:1.1
Do not assume that an edition URN returned by Scaife search is also available
from Perseus CTS. Discover CTS editions first with get_author_resources(...)
or get_work_resources(...). For example, recorded notebook runs have shown a
Perseus CTS Iliad edition ending in perseus-grc1 while Scaife search results
used perseus-grc2; either inventory may change.
Recommended workflow:
- Start with
list_text_groups(...),get_author_resources(...), orget_work_resources(...)to discover useful URNs without parsing the full capabilities XML manually. - Use
get_label(urn)for human-readable metadata. - Use
get_valid_references(urn)and optionallylevelto discover citations. - Fetch text with
get_passage(...),get_passage_plus(...), orget_passage_plaintext(...). - Navigate using
get_prev_next_urn(...).
get_first_urn(...) and get_prev_next_urn(...) normally request the
corresponding CTS operations. If Perseus returns malformed HTML for those
operations, the server derives a well-formed XML result from
GetValidReff.
Troubleshooting
- HTTP 4xx/5xx: Remote service may be unavailable, URN may be invalid, or endpoint behavior may have changed.
- No tools in client: Verify the command/path in your MCP config, and ensure
uv --directory /full/path/to/Perseus-mcp run server.pyworks manually. - Client connects but the model does not call tools: explicitly ask the model to use the
perseusMCP tools, or use the client’s tool picker/approval UI if it has one. - Wrong model/provider: model choice is controlled by your LLM client, not by this server. Keep this MCP server config the same and choose the desired model in the client.
- Search mismatch:
search_perseusaccepts Unicode Greek or Beta Code for Greek queries. For ambiguous ASCII, setquery_format="betacode"orquery_format="unicode". Thelanguageargument controls query normalization but is not currently sent to Scaife as a corpus language filter. - Unexpected edition URN: Scaife search and Perseus CTS do not always expose the same edition identifiers. Use the discovery tools before calling CTS passage or navigation tools.