oeis-mcp-server
by cyanheads
README.md
<div align="center">
<h1>@cyanheads/oeis-mcp-server</h1>
<p><b>Identify integer sequences by terms, search the OEIS, read formulas, programs, b-files, cross-refs via MCP. STDIO or Streamable HTTP.</b>
<div>6 Tools • 1 Resource</div>
</p>
</div>
<div align="center">
[](./CHANGELOG.md) [](./LICENSE) [](https://github.com/users/cyanheads/packages/container/package/oeis-mcp-server) [](https://modelcontextprotocol.io/) [](https://www.npmjs.com/package/@cyanheads/oeis-mcp-server) [](https://www.typescriptlang.org/) [](https://bun.sh/)
</div>
<div align="center">
[](https://github.com/cyanheads/oeis-mcp-server/releases/latest/download/oeis-mcp-server.mcpb) [](https://cursor.com/en/install-mcp?name=oeis-mcp-server&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBjeWFuaGVhZHMvb2Vpcy1tY3Atc2VydmVyIl19) [](https://vscode.dev/redirect?url=vscode:mcp/install?%7B%22name%22%3A%22oeis-mcp-server%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40cyanheads%2Foeis-mcp-server%22%5D%7D)
[](https://www.npmjs.com/package/@cyanheads/mcp-ts-core)
</div>
---
## Overview
Integer sequences from the [On-Line Encyclopedia of Integer Sequences](https://oeis.org/) (OEIS). Identify a sequence from a few observed terms, search with OEIS's own query syntax, read an entry's formulas, generating functions, and programs, page through extended terms from its b-file, and walk its cross-references. Answers come from the OEIS entries themselves, each linked to its page. Runs as a stdio process or a local Streamable HTTP server, with no API key.
### Tools
| Tool | Description |
|:---|:---|
| `oeis_identify_sequence` | Identify sequences that contain a run of consecutive terms, in OEIS relevance order, with the index where the run begins |
| `oeis_search_sequences` | Search with OEIS query syntax: words, quoted phrases, term lists, and prefixes such as `keyword:` and `author:` |
| `oeis_get_sequence` | Fetch one entry by A-number: terms, offset, keywords, formulas, programs, comments, references, links, and cross-references |
| `oeis_get_terms` | List terms a(n) with their indices from the entry's b-file, or from its data line when it has none |
| `oeis_get_cross_refs` | List the sequences an entry cross-references, or the entries that mention it |
| `oeis_list_reference` | Decode keyword flags, search syntax, and identifier and offset conventions, with no upstream call |
### Resources
| Resource | Description |
|:---|:---|
| `oeis://sequence/{aNumber}` | One entry by A-number as JSON, always whole |
The same record is available through `oeis_get_sequence` for clients that only call tools.
## Capability reference
### `oeis_identify_sequence` <sub>tool</sub>
- `terms`: up to 60 consecutive terms separated by commas or spaces (a bracketed list or a trailing `...` is accepted), each an integer of at most 200 digits or `_` for one unknown term; `matchSigns` (default `false`) ignores signs unless set
- Up to 10 candidates per page, `start` 0–100 in steps of 10; each row carries `matchStartIndex`, the n where the run begins in that entry's data line (absent when the run isn't there)
- No match and "too many results" both return an empty page, and the `notice` says which and how to retry: drop leading terms, divide out a common factor, or add terms
---
### `oeis_search_sequences` <sub>tool</sub>
- `query` (1–1,000 characters) is sent as written: words, `"quoted phrases"`, term lists, prefixes such as `keyword:`, `author:`, `name:`, `formula:`, `xref:`, and `id:`, `|` for OR, and a leading `-` to exclude
- `sort`: `relevance` (default), `number`, `created`, or `modified`; 10 per page, `start` 0–100 in steps of 10
- Reports `totalCount` and `effectiveQuery`, the query as OEIS parsed it; the zero-hit notice names any prefix that is not an OEIS prefix
---
### `oeis_get_sequence` <sub>tool</sub>
- Returns `name`, `terms`, `offset` / `firstIndex`, `keywords`, `author`, `legacyIds`, `referenceCount`, `revision`, `created` / `modified`, `url`, and `bFileUrl`, plus eight sections: `comments`, `formulas`, `examples`, `programs`, `references`, `links`, `crossReferences`, `extensions`
- When the sections exceed 24,000 characters of serialized JSON, `kind: "outline"` returns the core fields with a sized `sections` list; pass `sections` (e.g. `["formulas", "programs"]`) to get the chosen ones whatever their size
- An unknown A-number fails as `sequence_not_found`; withdrawn (`dead`), reserved, and recycled entries come back with a `notice`
---
### `oeis_get_terms` <sub>tool</sub>
- `fromIndex` (default: the first available index) and `limit` 1–1,000 (default 100); a slice also stops at about 100,000 bytes, and `nextFromIndex` continues it
- `source: "bfile"` reads the first 1 MiB of the entry's b-file, with `bFileCut: true` when the file is larger; `source: "data"` means the entry has no b-file and the terms are its data line
- Terms come back as `{ n, value }`, `value` an exact decimal string; an unknown A-number fails as `sequence_not_found`
---
### `oeis_get_cross_refs` <sub>tool</sub>
- `direction: "outgoing"` (default) lists the A-numbers named in the entry's cross-reference lines, with the `note` written beside each and the `lines` verbatim; `"incoming"` lists the entries that mention the A-number
- 10 rows per page, `start` 0–100 in steps of 10; every row carries `url`, and resolved rows add the name, terms, offset, and keywords
- When the name lookup for an outgoing page is rate-limited, unavailable, or times out, the rows come back `resolved: false` with a retry notice instead of failing; `sequence_not_found` applies to `outgoing` only
---
### `oeis_list_reference` <sub>tool</sub>
- `topic`: `keywords` (flags such as `nonn`, `core`, `tabl`, `cons`), `search_syntax` (prefixes, operators, wildcards, sort orders, paging), or `identifiers` (A-numbers, legacy M/N numbers, offsets, b-files)
- Returns `entries` (`name`, `description`) and `notes` from static tables
---
### `oeis://sequence/{aNumber}` <sub>resource</sub>
- The full entry as `application/json`: the record `oeis_get_sequence` returns with `kind: "full"`, never outlined
- `aNumber` takes the same forms as the tools (`oeis://sequence/A108` reads A000108); an unknown A-number fails as `sequence_not_found`
## Features
Built on [`@cyanheads/mcp-ts-core`](https://github.com/cyanheads/mcp-ts-core): stdio and Streamable HTTP transports, pluggable auth (`none` / `jwt` / `oauth`), swappable storage (`in-memory`, `filesystem`, `Supabase`, `Cloudflare KV/R2/D1`), structured logging with optional OpenTelemetry tracing.
OEIS-specific:
- One oeis.org client: entry records as JSON, searches in OEIS's internal text format, which tells "no results" apart from "too many results" and states the match total, and b-files read directly
- One request at a time, at least 10 seconds apart, shared by every tool, with an in-memory cache in front (see [Pacing and caching](#pacing-and-caching))
- Forgiving A-numbers: `A000045`, `a000045`, `A45`, `45`, and an oeis.org sequence URL all resolve in every tool; a legacy M/N book number is pointed to `oeis_search_sequences`
- Exact terms: every term and b-file value is a decimal string, so values past 2^53 keep every digit
- Pasted term runs are normalized: brackets, a trailing `...`, the Unicode minus sign, and mixed comma and space separators
Agent-friendly output:
- Paging context on every page: `truncated`, `shown`, and `cap`, plus `totalCount` and `effectiveQuery` where OEIS reports them, and one `notice` composed from the input (drop leading terms, a common factor, an unrecognized prefix, the next `start`)
- Typed failures with recovery hints: `sequence_not_found`, `pacer_shed`, and `upstream_rate_limited`, the rate-limit errors carrying `retryAfter` when one is known; an outgoing cross-reference page whose name lookup fails still returns every A-number, marked `resolved: false`
- Discriminated outputs: `kind: "full" | "outline"`, `source: "bfile" | "data"`, and per-row `resolved`, so callers branch on data, not string parsing
- Provenance and safe rendering: every record and row carries its `https://oeis.org/A######` URL; contributor-written text renders as blockquotes and code fences, and only `http` and `https` link URLs are kept
## Pacing and caching
oeis.org asks automated clients to wait 10 seconds between requests. The server keeps to that: it sends one upstream request at a time and starts each at least 10 seconds after the last, across every tool. Results are cached in memory, so a repeated lookup returns at once and costs oeis.org nothing: records for 24 hours (then revalidated with a conditional request), search pages for 1 hour, and b-file reads for 7 days.
A call with nothing cached waits its turn in the queue. When the wait would pass `OEIS_QUEUE_MAX_WAIT_MS` (default 30,000 ms), the call fails with `pacer_shed` and a `retryAfter` instead of hanging. If oeis.org answers 429, the server holds every queued call back before trying again, and reports `upstream_rate_limited` once its retries are spent.
## Known limitations
- The server is built for local use at this pace: one upstream request per 10 seconds per server process. Concurrent uncached calls queue and, past the wait budget, fail with `retryAfter`.
- Anonymous paging is capped upstream at 110 results per query (`start` ≤ 100); A000045's 6,161 incoming references, for example, are reachable only through their first 110.
- A query OEIS judges too broad returns no rows at all ("Too many results"); this is common for short queries such as a single word (`prime`) or a two-term run. The server reports it but cannot page it.
- OEIS searches a misspelled or unknown prefix as plain words instead of rejecting it; the zero-hit notice names any prefix that is not an OEIS prefix.
- Term identification searches the data line only (about three screen lines of terms), not b-files; a run that starts beyond the data line is not found.
- `oeis_get_terms` reads at most the first 1 MiB of a b-file.
- `oeis_get_cross_refs` outgoing shares the `start` ≤ 100 bound, so an entry naming more than 110 distinct A-numbers lists only the first 110; `lines` still names them all.
- Records are cached for up to 24 hours, then revalidated.
## Data and licensing
OEIS content is licensed [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) under the [OEIS End-User License Agreement](https://oeis.org/LICENSE). When you reuse it, credit "The On-Line Encyclopedia of Integer Sequences" with a link to [https://oeis.org/](https://oeis.org/) or to the sequence page; every record the server returns carries its `https://oeis.org/A######` URL for that purpose. ShareAlike applies: adaptations of OEIS content must be shared under the same license.
This server is an independent project and is not affiliated with or endorsed by the OEIS Foundation.
## Getting started
Add the following to your MCP client configuration file.
```json
{
"mcpServers": {
"oeis-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/oeis-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
```
Or with npx (no Bun required):
```json
{
"mcpServers": {
"oeis-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/oeis-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
```
Or with Docker:
```json
{
"mcpServers": {
"oeis-mcp-server": {
"type": "stdio",
"command": "docker",
"args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/oeis-mcp-server:latest"]
}
}
}
```
For Streamable HTTP, set the transport and start the server:
```sh
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcp
```
### Prerequisites
- [Bun v1.4.0](https://bun.sh/) or higher (or Node.js v24+).
- No API key or account: the server reads public oeis.org data.
### Installation
1. **Clone the repository:**
```sh
git clone https://github.com/cyanheads/oeis-mcp-server.git
```
2. **Navigate into the directory:**
```sh
cd oeis-mcp-server
```
3. **Install dependencies:**
```sh
bun install
```
4. **Configure environment:**
```sh
cp .env.example .env
# optional: adjust OEIS_QUEUE_MAX_WAIT_MS, the transport, or the log level
```
## Configuration
| Variable | Description | Default |
|:---|:---|:---|
| `OEIS_QUEUE_MAX_WAIT_MS` | Longest a call waits in the oeis.org request queue before failing with `pacer_shed` and a `retryAfter`, in ms. Each call's 50 s deadline also has to cover the request itself, so waits stop at about 35,000 ms whatever the setting. | `30000` |
| `MCP_TRANSPORT_TYPE` | Transport: `stdio` or `http`. | `stdio` |
| `MCP_HTTP_PORT` | HTTP server port. | `3010` |
| `MCP_SESSION_MODE` | HTTP session mode: `stateless`, `stateful`, or `auto`. | `stateless` |
| `MCP_AUTH_MODE` | Authentication: `none`, `jwt`, or `oauth`. | `none` |
| `MCP_LOG_LEVEL` | Log level (`debug`, `info`, `warning`, `error`, etc.). | `info` |
| `LOGS_DIR` | Directory for log files (Node.js only). | `<app-root>/logs` |
| `STORAGE_PROVIDER_TYPE` | Storage backend: `in-memory`, `filesystem`, `supabase`, `cloudflare-kv/r2/d1`. | `in-memory` |
| `OTEL_ENABLED` | Enable [OpenTelemetry](https://github.com/cyanheads/mcp-ts-core/tree/main/docs/telemetry). | `false` |
See [`.env.example`](./.env.example) for every server setting and the common framework overrides.
## Running the server
### Local development
- **Build and run the production version**:
```sh
# One-time build
bun run rebuild
# Run the built server
bun run start:http
# or
bun run start:stdio
```
- **Run checks and tests**:
```sh
bun run devcheck # Lints, formats, type-checks, and more
bun run test # Runs the test suite
```
## Project structure
| Directory | Purpose |
|:---|:---|
| `src/index.ts` | `createApp()` entry point: registers the tools and resource, sets the server instructions, and starts the OEIS service. |
| `src/config` | Server-specific environment variable parsing and validation with Zod. |
| `src/mcp-server/tools` | Tool definitions (`*.tool.ts`). Six tools. |
| `src/mcp-server/resources` | Resource definitions. The `oeis://sequence/{aNumber}` resource. |
| `src/mcp-server/shared` | Schemas shared across definitions (A-number input, paging, summary rows) and Markdown helpers for contributor-written text. |
| `src/services/oeis` | oeis.org client: pacer, retries, fetch boundaries, LRU cache, record normalization, and the internal-format parser. |
| `tests/` | Unit tests mirroring the `src/` structure; upstream responses come from fixtures, never the live site. |
## Development guide
See [`CLAUDE.md`](./CLAUDE.md) for development guidelines and architectural rules. The short version:
- Handlers throw, framework catches — no `try/catch` in tool logic
- Use `ctx.log` for logging and `ctx.enrich` for notices, totals, and paging context
- Register new tools and resources in the barrels at `src/mcp-server/*/definitions/index.ts`
- Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields
## Contributing
Issues are welcome. Run checks and tests before submitting:
```sh
bun run devcheck
bun run test
```
## License
This project is licensed under the Apache 2.0 License. See the [LICENSE](./LICENSE) file for details.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessResponsive