Alexandria

io.github.The-40-Thieves由 GitHub 帳號 The-40-Thieves 發布v11.0.0更新於 Oct 27, 2026

Search, read, and cite across 152 libraries: 11 tools, 3 prompts, and a full-text resource.

概覽

AI 產生的概覽

Alexandria 讓助理在 152 個公共數位圖書館中檢索、閱讀、引用並綜合文獻,可選向量入庫與附引用的研究報告。

可以做什麼
  • 列出並檢查 152 個圖書館來源,查看各來源狀態與缺少的金鑰
  • 依標題、作者或關鍵字檢索指定來源,或將自然語言問題平行路由到最合適的來源
  • 讀取項目的全文或中介資料,並沿引用圖譜查找參考文獻與被引用文獻,可匯出 BibTeX、RIS 或 APA
  • 取得相似論文推薦,產生附行內引用的綜合答案或多輪研究報告
  • 將檢索到的文本分塊、評分並嵌入寫入 pgvector 向量資料庫,供後續語意檢索
  • 提供文獻回顧、事實查核與參考文獻核驗三個現成提示流程
適用情境
適合讓助理跨學術、政府、新聞、法律、文化與開發者檔案進行文獻與資料調查,並輸出附引用的答案或報告,而非原始搜尋結果。也適合用檢索到的開放取用文本建立本機向量知識庫。
執行需求
透過 npx 在本機執行,需要 Node.js;檢索與閱讀不需任何憑證。選用金鑰:OPENAI_API_KEY(或 ALEXANDRIA_BASE_URL 加 ALEXANDRIA_API_KEY)用於路由、綜合與嵌入;SUPABASE_URL 與 SUPABASE_SERVICE_ROLE_KEY 用於入庫;以及 CORE_API_KEY、GITHUB_TOKEN、GOVINFO_API_KEY、EUROPEANA_API_KEY、GOOGLE_BOOKS_API_KEY、NASA_ADS_API_KEY、COURTLISTENER_API_KEY、SEMANTIC_SCHOLAR_API_KEY 等來源金鑰。需要連線至各上游圖書館 API 的網路。
安裝前請注意
設定金鑰後,查詢文字與檢索摘錄會傳送給所設定的 LLM 與嵌入服務供應商;查詢也會依各來源自身條款送往上游圖書館。library_ingest 會將嵌入文本寫入你的 Supabase 資料庫,該服務角色金鑰具有相當大的寫入權限。來源 API 金鑰雖為免費註冊,仍應盡量縮小權限,例如 GITHUB_TOKEN 只需公開儲存庫讀取範圍。本機快取保存在執行機器的 data 目錄中。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 Alexandria,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

Alexandria

[npm version] [License: MIT] [CodSpeed] [Install in Cursor] [Install in VS Code] Install in Goose

A Model Context Protocol (MCP) server for querying, reading, and ingesting texts from 152 public digital libraries. Works with any MCP-compatible client (Claude Desktop, Cursor, VS Code Copilot, etc.).

Tools

ToolDescription
library_list_sourcesList all 152 sources with descriptions and full-text capabilities
library_ask(query, max_sources?, results_per_source?)Natural language search — routes your query to the best sources, searches in parallel, returns unified deduplicated results
library_search(query, source, limit?)Search a specific source by title, author, or keywords
library_read(id, source)Fetch full text or metadata for an item (200k char limit)
library_index(id, source)Dry run: chunk and score text quality without writing anything
library_ingest(id, source)Chunk → embed → store in your vector database. Idempotent.
library_recommend(id, limit?)Get similar papers via Semantic Scholar's recommendation engine (up to 500)
library_answer(query, max_sources?, results_per_source?, read_top?)Ask a question and get a synthesized answer with inline [n] citations, fused across sources with reciprocal rank fusion; warnings[] flags an uncited or all-dropped answer
library_research(query, depth?, breadth?, max_minutes?)Recursive multi-round research: generates queries, answers each, extracts learnings, and writes a final cited report over every source found
library_health_check(source?, cluster?)Report per-source health (ok, degraded, down, key_missing, unknown), merging this process's live error rate/latency with the last off-process probe run
library_citations(id, source, direction, limit?, format?)List the works an item cites (direction: "references") or the works that cite it (direction: "citations"), via OpenAlex's citation graph with OpenCitations as a fallback; format: "bibtex" | "ris" | "apa" also returns a formatted bibliography string

library_ask is the primary entry point. library_search is for targeted queries against a known source. library_index / library_ingest are for building a vector knowledge base from retrieved texts. library_answer and library_research synthesize a cited answer or report instead of returning raw results. library_health_check tells you whether a source is worth calling before you call it. library_citations walks the citation graph around an item and can export it as a bibliography.

library_ask, library_search, library_answer, library_research, library_health_check, and library_citations take a response_format: "concise" | "detailed" parameter (default concise); concise trims results and citations to the high-signal fields (title, source, id, year, hasFullText, url; answer/report + citations; name, cluster, status), detailed returns the full payload, including routing reasons, relevance scores, per-stage diagnostics, and per-source error rate/latency/quota usage. In detailed mode, library_search also attaches a resource_link content item for each full-text result, pointing at that item's library://doc/{source}/{id} resource (see below): a client with resource support can read the full text directly instead of a second library_read call.

Prompts

Three ready-made research workflows, surfaced by MCP clients as slash commands (Claude Code's /alexandria:<name>, VS Code's /alexandria.prompt). Each returns a single message naming the tools to call, in order. It does not call any tool itself.

PromptDescription
literature_review(topic, depth?)Survey a topic across sources and produce a cited report
fact_check_claim(claim)Check one claim against the library and report whether it is supported
verify_bibliography(references)Check that a list of references (one per line) resolves to real, findable items

Resources

library://doc/{source}/{id} reads the same text library_read(id, source) returns (including the open-access fallback below), addressed by the source/id pair library_search or library_ask returned. Clients that support MCP resources (Claude Code's @srv:uri, VS Code's Add Context) can pull an item's full text directly.

Sources (152)

152 sources across 19 clusters (36 hidden pending a key or config not present in this deployment). Full per-source detail, including auth env vars and last-verified dates, is generated in docs/sources.md.

ClusterSourcesHidden
academic223
ai_research30
archives42
culture83
developer175
economics113
geopolitical33
government73
law41
literature162
markets21
news_global52
news_regional150
real_estate22
science82
security140
standards30
video11
web73
Total15236

Credentials

Most tools query external library APIs directly and need no credentials at all. The two optional dependencies are scoped to specific tools:

OpenAI, optional (platform.openai.com)

Required by two tools only:

  • library_ask: uses gpt-4o-mini to route your natural language query to the right sources and generate optimized per-source search terms. Without this key, use library_search to query sources directly.
  • library_ingest: uses text-embedding-3-small to embed chunked text before writing to the vector store.

library_list_sources, library_search, library_read, library_index, and library_recommend all work without an OpenAI key.

Pointing roles at a gateway instead of OpenAI directly

Every LLM/embedding call (routing in library_ask, embeddings in library_ingest) goes through a small per-role provider table (src/utils/providers.ts, THE-318) instead of talking to OpenAI's SDK directly. There are five roles: router, synth, research, embeddings, rerank. Each is resolved from env in this order:

  1. ALEXANDRIA_<ROLE>_BASE_URL, ALEXANDRIA_<ROLE>_API_KEY, ALEXANDRIA_<ROLE>_MODEL (per-role overrides; <ROLE> is the role name upper-cased, e.g. ALEXANDRIA_ROUTER_BASE_URL)
  2. ALEXANDRIA_BASE_URL, ALEXANDRIA_API_KEY (shared defaults across every role)
  3. OPENAI_API_KEY, with baseURL defaulted to https://api.openai.com/v1

With only OPENAI_API_KEY set, every role resolves through step 3, which is exactly today's behavior (gpt-4o-mini for router/synth against api.openai.com, text-embedding-3-small for embeddings).

To route every role through a LiteLLM gateway instead:

env
ALEXANDRIA_BASE_URL=http://100.78.123.100:4001/v1ALEXANDRIA_API_KEY=sk-litellm-...

To route through Cloudflare AI Gateway (its OpenAI-compatible endpoint):

env
ALEXANDRIA_BASE_URL=https://gateway.ai.cloudflare.com/v1/<account_id>/<gateway_id>/openaiALEXANDRIA_API_KEY=<your OpenAI key, forwarded through the gateway>

Or point just one role at a gateway while the rest stay on OpenAI directly, e.g. ALEXANDRIA_ROUTER_BASE_URL + ALEXANDRIA_ROUTER_API_KEY for routing only.

See docs/cloudflare.md for the fuller Cloudflare integration guide: AI Gateway's newer unified endpoint, Workers AI for the embeddings/rerank roles, Tunnel + Access for a private /mcp, WAF rate limiting for a public one, R2 as an optional cache store, the Browser Run fetch tier below, and why Alexandria stays a hybrid (Node core, Cloudflare services) rather than a full Workers port.

When ALEXANDRIA_<ROLE>_BASE_URL/ALEXANDRIA_BASE_URL is set and OPENAI_API_KEY is also present, OPENAI_API_KEY is wired up as a one-shot fallback: a network error or 5xx from the gateway falls through to a direct OpenAI call once before the request fails. chatJSON (used by routing) always validates the model's response against a zod schema and retries once, on the same backend, with the validation error appended to the prompt, which helps when a gateway is proxying a smaller or local model that doesn't reliably follow the JSON contract on the first try. It requests response_format: json_object only against api.openai.com, or when ALEXANDRIA_<ROLE>_JSON_MODE=1 confirms the gateway/model supports it; otherwise it asks for JSON in the prompt instead.

Supabase — optional (supabase.com)

Required by one tool only:

  • library_ingest — writes chunked, embedded text into a pgvector table for semantic search. Without this, retrieved texts stay in-context and are not persisted anywhere.

Everything else — searching, reading, browsing, getting recommendations — queries external sources in real time and needs no database.

Source-specific keys

Some sources require their own API key. These are free registrations. Sources without a key listed here work without any credentials.

Env VarSource(s)Get It
CORE_API_KEYcorecore.ac.uk/services/api
COURTLISTENER_API_KEYcourtlistenercourtlistener.com/profile/tokens
GOVINFO_API_KEYgovinfo; also accepted by congress and regulations as a fallback for DATA_GOV_API_KEYapi.data.gov/signup. Does not cover smithsonian, which needs its own SMITHSONIAN_API_KEY from the same signup page
GOOGLE_BOOKS_API_KEYgooglebooksGoogle Cloud Console → APIs & Services → Books API
BHL_API_KEYbhlbiodiversitylibrary.org/getapikey
DIGITALNZ_API_KEYdigitalnzdigitalnz.org/developers
DPLA_API_KEYdplapro.dp.la/developers/api-codex
EUROPEANA_API_KEYeuropeanaapis.europeana.eu — test key immediate, personal ~1 week
GITHUB_TOKENrequired by githubsearch and githubmcp; optional for ghsa and openitigithub.com/settings/tokens, public repo read scope. githubsearch and githubmcp are hidden without it; ghsa falls back to 60s pacing and openiti to an unauthenticated search path
NASA_ADS_API_KEYnasaadsui.adsabs.harvard.edu/user/settings/token
SPRINGER_OA_API_KEY + SPRINGER_META_API_KEYspringerdev.springernature.com — same registration, two keys
ZENODO_API_KEYzenodozenodo.org/account/settings/applications/tokens/new — optional, increases rate limits
SMITHSONIAN_API_KEYsmithsonianapi.data.gov/signup. Its own key, separate from GOVINFO_API_KEY
SEMANTIC_SCHOLAR_API_KEYsemanticscholarsemanticscholar.org/product/api — optional, increases rate limits
TROVE_API_KEYtrovetrove.nla.gov.au/about/create-something/using-api — ~1 week approval
YOUTUBE_API_KEYyoutubeconsole.cloud.google.com — enable YouTube Data API v3; search only, transcripts need no key

Privacy Policy

Alexandria has no telemetry of its own: it runs on infrastructure you control, and nothing about your queries, results, or credentials is sent to the Alexandria project.

  • Queries go to the upstream public library sources the agent selects for a given request, using each source's own public API under that source's own terms.
  • library_ask, library_answer, library_research, and library_ingest, when an OpenAI (or OpenAI-compatible gateway) key is configured, also send your query text and retrieved excerpts to the LLM and embeddings provider the operator set up. See docs/cloudflare.md for the Cloudflare AI Gateway routing path and docs/fetch-tier-runtime.md for how outbound fetches to library sources are guarded.
  • Nothing is stored by the project itself. The operator's own data/ directory holds local caches (state DB, per-process read cache) on the machine running the server, and nowhere else.

Full text: PRIVACY.md.

Setup

bash
git clone https://github.com/The-40-Thieves/alexandria-mcpcd alexandria-mcpnpm installnpm run build

Copy .env.example to .env. Minimum configuration to run with no credentials (search and read only):

env
TRANSPORT=stdio

To enable library_ask:

env
TRANSPORT=stdioOPENAI_API_KEY=sk-...

To enable library_ingest:

env
TRANSPORT=stdioOPENAI_API_KEY=sk-...SUPABASE_URL=https://your-project.supabase.coSUPABASE_SERVICE_ROLE_KEY=eyJ...

Supabase Schema

Required only if using library_ingest:

sql
create table if not exists knowledge_chunks (  id bigserial primary key,  content text not null,  embedding vector(1536),  mcp_name text,  metadata jsonb,  created_at timestamptz default now());
create table if not exists source_docs (  id bigserial primary key,  source_url text not null,  mcp_name text not null,  title text,  source text,  chunk_count int,  indexed_at timestamptz,  unique (source_url, mcp_name));
create index if not exists knowledge_chunks_embedding_hnsw_idx  on knowledge_chunks using hnsw (embedding vector_cosine_ops);

If you set this schema up before Task 12, you had an ivfflat index instead (knowledge_chunks_embedding_idx) - docs/sql/match_chunks.sql drops it and creates the hnsw one shown above, since HNSW builds incrementally and needs no lists tuning constant as the table grows. If corpus-as-cache hits seem to be missing results a plain <=> scan would have found, raise the query-time hnsw.ef_search session setting (default 40) at the cost of a slower query - see the commented recommendation in docs/sql/match_chunks.sql.

Corpus as cache

library_answer can also read straight from knowledge_chunks - previously ingested text, already embedded - as one more ranked list next to the live per-source search, skipping the network entirely for a hit it already has the full text for. This only ever serves chunks from a source whose registry freshness is static or daily (never realtime), and only above ALEXANDRIA_CORPUS_MIN_SIM cosine similarity (default 0.92).

It needs one more piece of schema beyond the table above: run docs/sql/match_chunks.sql in the Supabase SQL editor. It defines match_knowledge_chunks(), the nearest-neighbor search function SupabaseVectorStoreProvider.query() calls via .rpc(), and the hnsw index above. This file was written against the current pgvector/supabase-js docs but has not been run against a live database - verify it against your own project before relying on it.

Claude Code (npx)

bash
claude mcp add --env OPENAI_API_KEY=sk-... alexandria -- npx -y @the-40-thieves/alexandria-mcp

Search and read work with no environment variables at all; the --env flag above is only needed to enable library_ask, library_answer, library_research, and library_ingest. See Credentials for the full list of optional keys.

Claude Desktop (stdio)

Minimum config (search and read only), using the published package via npx:

json
{  "mcpServers": {    "library": {      "command": "npx",      "args": ["-y", "@the-40-thieves/alexandria-mcp"],      "env": {        "TRANSPORT": "stdio"      }    }  }}

With library_ask and library_ingest enabled:

json
{  "mcpServers": {    "library": {      "command": "npx",      "args": ["-y", "@the-40-thieves/alexandria-mcp"],      "env": {        "TRANSPORT": "stdio",        "OPENAI_API_KEY": "sk-...",        "SUPABASE_URL": "https://your-project.supabase.co",        "SUPABASE_SERVICE_ROLE_KEY": "eyJ..."      }    }  }}

From a local checkout instead of the published package, replace command/ args with "command": "node", "args": ["/path/to/alexandria-mcp/dist/index.js"].

Railway (HTTP)

HTTP mode is off by default. Set TRANSPORT=http to serve Streamable HTTP on /mcp instead of speaking stdio, and PORT to choose the listen port (default 3000; Railway sets PORT for you). Every request gets its own MCP server and transport, so the endpoint is stateless and safe to run behind a load balancer.

Env VarValuesDefault
TRANSPORTstdio or httpstdio
PORTany port number, HTTP mode only3000

/mcp (only) is guarded: DNS-rebinding-safe Host/Origin validation (ALEXANDRIA_ALLOWED_ORIGINS, a comma-separated hostname list - loopback is always allowed regardless) and a per-client-IP rate limit (ALEXANDRIA_HTTP_RATE_LIMIT, default 60/minute, 429 with a JSON-RPC error body once exceeded). See docs/fetch-tier-runtime.md for the details and src/httpGuards.ts for the implementation.

Set ALEXANDRIA_ALLOWED_ORIGINS on any deployment reachable by a hostname other than loopback. With it unset, Host-header validation is off and the server logs one warning at startup saying so: a deployment's Host header is its own hostname, which cannot be in an allowlist that does not exist, so enforcing the check without one would 403 every request. Setting it is what turns DNS-rebinding protection on. The Origin check is unconditional either way, so a browser request carrying an Origin outside the list is always rejected.

POST /mcp requires content-type: application/json (anything else is 415) and a body no larger than 100 KiB (413, connection closed).

Behind a reverse proxy or PaaS edge (Cloudflare Tunnel, Railway's own edge, ...) the rate limiter's per-client key defaults to req.socket.remoteAddress, which is the proxy's address for every caller, not the caller's - set ALEXANDRIA_TRUSTED_PROXY=1 to key on CF-Connecting-IP (falling back to the rightmost X-Forwarded-For entry, the one appended by the last hop; the leftmost entry is whatever the original caller sent) instead. Only set this once /mcp is reachable exclusively through a proxy you trust to set those headers honestly - see docs/cloudflare.md's Tunnel and Access section.

Serves both eras of the MCP protocol on the same /mcp endpoint: createMcpHandler(factory, { legacy: 'stateless' }) (@modelcontextprotocol/server, adapted to node:http by toNodeHandler() from @modelcontextprotocol/node) answers a 2026-07-28 server/discover probe or per-request envelope on the modern path, and falls back to the same stateless idiom the pre-2026 SDK used for a 2025-era initialize handshake. One createServer() factory backs both. stdio uses the connection-pinned serveStdio(factory) from @modelcontextprotocol/server/stdio, which selects the era from the connection's opening exchange. Note: the 2025-era fallback path answers over text/event-stream (SSE) rather than a bare JSON body, since the SDK exposes no equivalent to v1's enableJsonResponse for that path. Any MCP client built on a Streamable HTTP transport (the SDK's own StreamableHTTPClientTransport included) already parses either format transparently.

Set those (plus any source keys) in the Railway dashboard and deploy:

bash
railway up

Locally the same thing is:

bash
TRANSPORT=http PORT=3000 npm start

Register in Claude Desktop:

json
{  "mcpServers": {    "library": {      "url": "https://your-service.up.railway.app/mcp"    }  }}

Health check: GET /health returns { status: "ok", version: "11.0.0", sources: { total: 152, visible: 116, hidden: 36, calls: 0, errors: 0 }, byKind: { rest: 118, hub: 0, rss: 22, mcp: 6, scrape: 6 }, quota: { day: "2026-09-02", reserved: 0, sources: 0, backend: "state" }, cache: { entries: 0 }, tools: 11 }.

Metrics: GET /metrics returns per-source counters (calls, errors, timeouts, cacheHits, quotaRejections, latencyMsTotal) and per-tool counters (invocations, llmCalls) as JSON, e.g. { "sources": { "arxiv": { "calls": 12, "errors": 0, "timeouts": 0, "cacheHits": 3, "quotaRejections": 0, "latencyMsTotal": 4210 } }, "tools": { "library_ask": { "invocations": 5, "llmCalls": 5 } } }. Only sources/tools actually called since the process started appear.

Install in other clients

All of these run the published package via npx; search and read work with no environment variables, and --env/env block additions enable library_ask, library_answer, library_research, and library_ingest the same way the Claude Code and Claude Desktop sections above do.

GitHub Copilot - .vscode/mcp.json:

json
{  "servers": {    "alexandria": {      "command": "npx",      "args": ["-y", "@the-40-thieves/alexandria-mcp"]    }  }}

Windsurf - mcp_config.json:

json
{  "mcpServers": {    "alexandria": {      "command": "npx",      "args": ["-y", "@the-40-thieves/alexandria-mcp"]    }  }}

Codex CLI:

bash
codex mcp add alexandria -- npx -y @the-40-thieves/alexandria-mcp

OpenCode - opencode.json:

json
{  "mcp": {    "alexandria": {      "type": "local",      "command": ["npx", "-y", "@the-40-thieves/alexandria-mcp"]    }  }}

Amazon Q - ~/.aws/amazonq/mcp.json (global) or q mcp add:

json
{  "mcpServers": {    "alexandria": {      "command": "npx",      "args": ["-y", "@the-40-thieves/alexandria-mcp"]    }  }}

Kiro - .kiro/settings/mcp.json:

json
{  "mcpServers": {    "alexandria": {      "command": "npx",      "args": ["-y", "@the-40-thieves/alexandria-mcp"]    }  }}

Gemini CLI:

bash
gemini extensions install https://github.com/The-40-Thieves/alexandria-mcp

Continue - add to the mcpServers array in your Continue config:

json
{  "name": "alexandria",  "command": "npx",  "args": ["-y", "@the-40-thieves/alexandria-mcp"]}

Adding Custom Providers

The pipeline is provider-agnostic. To add a new embedding model or vector store:

  1. Implement EmbeddingProvider or VectorStoreProvider from src/types.ts
  2. Add your implementation to src/pipeline/providers/
  3. Register it in src/pipeline/providers/index.ts
  4. Set EMBEDDING_PROVIDER or VECTOR_STORE_PROVIDER in your env
typescript
// Example: Ollama embedding providerimport type { EmbeddingProvider } from '../../types.js';
export class OllamaEmbeddingProvider implements EmbeddingProvider {  readonly dimensions = 768;
  async embed(texts: string[]): Promise<number[][]> {    // your implementation  }}

來源:README.md,提交 5950776

工具

0
SourceWeft 尚未在沙箱中啟動過該軟體包,因此其工具未知。

版本歷史

1
  1. v11.0.0最新Sep 16, 2026

評分

已滿足適用檢查項的 50%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通25 分

    請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。

  • 已宣告認證方式10 分

    請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。

  • 已列出工具15 分

    請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。

  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分