---
summary: "Fix: six error-contract gaps (statusless failures, 400-family InvalidParams drift, multi-key sort corruption, nullable display_name, 429 budget vs. throttle, resolve_name's missing comma_in_filter_value entry); mcp-ts-core ^0.11.0, typescript ^7.0.2."
breaking: false
security: false
---

# 0.7.5 — 2026-07-26

## Added

- **`.github/FUNDING.yml`** — GitHub Sponsors and Buy Me a Coffee links.

## Changed

- **`package.json` author / `LICENSE` copyright** — updated to `Casey Hand <casey@caseyjhand.com>` / `Casey Hand @cyanheads`.
- **OpenAlex docs links** — `src/index.ts`'s API Docs link and both README references to the rate-limits/authentication guide now point at `developers.openalex.org` (OpenAlex retired `docs.openalex.org`).
- **README** — documents the multi-key `sort` form, `display_name` nullability, that `group_by` returns the aggregable subset of `filter` rather than the same set, and splits the HTTP-status-to-error-class bullet into its actual 400/422 codes.
- Skills and orchestration workflow docs re-synced from the framework (`api-canvas`, `api-config`, `api-utils`, `field-test`, `git-wrapup`, `orchestrations`, `tool-defs-analysis`, plus the field-test-fix/fix-wrapup-release/maintenance-release workflow docs).
- `devcheck.config.json` — outdated-dependency allowlist emptied (the `@cyanheads/mcp-ts-core` hold is lifted with this release adopting ^0.11.0).
- `.gitignore` — `.vscode/*` now allowlists `settings.json`/`extensions.json` explicitly instead of a single glob; `data/` anchored to the repo root (`/data/`).
- `Dockerfile` — the conditional `@hono/otel` install now uses `--omit=dev --ignore-scripts`.
- `.env.example` — documents the new `MCP_LOG_RATE_LIMIT_THRESHOLD`/`MCP_LOG_RATE_LIMIT_WINDOW_MS` framework env vars.

## Fixed

- **Upstream error classification repaired for statusless failures and 400-family contracts** — timeouts, network failures, and malformed response bodies (empty/HTML/invalid JSON) previously bubbled as an unclassified re-throw with no reason or recovery hint; `throwNormalizedRequestError` now falls back to the framework error's own `code` when no HTTP status is present, routing them through the same reason table as status-mapped failures, with domain-worded messages (`OpenAlex did not respond within 10s for /works`) in place of the framework's fetch-plumbing text. Separately, six 400-family contract entries across all four tools declared `ValidationError` while the service actually throws `InvalidParams` for HTTP 400 (`httpStatusToErrorCode(400)`); corrected, leaving `upstream_validation_failed` (422) as the only entry still mapped to `ValidationError`. ([#53](https://github.com/cyanheads/openalex-mcp-server/issues/53))
- **Multi-key `sort` no longer corrupts the descending marker on non-trailing keys** — `normalizeSort` treated a comma-separated sort string as one field, so `-publication_year,cited_by_count` silently moved the descending marker onto the last key. Each key is now normalized independently before rejoining. ([#52](https://github.com/cyanheads/openalex-mcp-server/issues/52))
- **`display_name` widened to nullable** — OpenAlex holds no title for paratext and other untitled records (roughly 0.5% of works); `EntityRecord.display_name` and the `search_entities`/`get_citation_graph` output schemas now admit `null` instead of failing output validation on the whole page. `resolve_name` stays non-nullable, deliberately — autocomplete matches by display-name text, and no null has been observed there. ([#51](https://github.com/cyanheads/openalex-mcp-server/issues/51))
- **429 daily-budget exhaustion now distinguished from per-second throttling** — `classifyRateLimitReason` discriminates the two upstream 429 shapes on the message. A budget 429 now resolves to `upstream_budget_exhausted` and sets `data.retryable: false`, so the framework's `withRetry` stops burning its full attempt budget against a wall that won't clear until the midnight-UTC reset. ([#54](https://github.com/cyanheads/openalex-mcp-server/issues/54))
- **Non-ID filter values get a targeted recovery** — a name passed where an ID-valued filter is expected (`'Einstein' is not a valid OpenAlex ID.`) now maps to the new `upstream_invalid_id_value` reason across all four upstream-calling tools, including `resolve_name` (its own `filters` parameter reaches the same autocomplete validation). ([#49](https://github.com/cyanheads/openalex-mcp-server/issues/49))
- **`resolve_name` now declares `comma_in_filter_value`** — the reason is thrown from the `buildFilterString` pre-flight shared by all filter-accepting tools, but only 3 of 4 declared it in their `errors[]`; `resolve_name` callers were getting the error with `data.recovery` absent. ([#58](https://github.com/cyanheads/openalex-mcp-server/issues/58))

## Dependencies

- `@cyanheads/mcp-ts-core` ^0.10.14 → ^0.11.0
- `typescript` ^6.0.3 → ^7.0.2
- `@biomejs/biome` ^2.5.0 → ^2.5.5, `@types/node` ^26.0.0 → ^26.1.1, `ignore` ^7.0.5 → ^7.0.6, `tsc-alias` ^1.8.17 → ^1.9.1, `vitest` ^4.1.9 → ^4.1.10
- Transitive lockfile re-resolve pulls in updated `hono`, `jose`, `express-rate-limit`, and related packages. `bun audit` carries 2 known transitive advisories with no in-range fix — `@hono/node-server <2.0.5` (via `@cyanheads/mcp-ts-core`) and `brace-expansion <=5.0.7` (via `depcheck › minimatch`).
