Remove legacy SeiJS docs and migrate MCP search - #319
Conversation
Remove the in-repo Mintlify stack and route MCP documentation search and links through docs.sei.io. Co-authored-by: Cursor <cursoragent@cursor.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #319 +/- ##
==========================================
+ Coverage 79.75% 82.55% +2.80%
==========================================
Files 83 79 -4
Lines 1309 1330 +21
Branches 217 225 +8
==========================================
+ Hits 1044 1098 +54
+ Misses 259 226 -33
Partials 6 6 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Clean, well-scoped migration off the in-repo Mintlify/Trieve docs stack to the official docs.sei.io MCP endpoint — the dependency-injected client makes search_docs testable and removes a startup-time network dependency, and I verified no dangling mintlify/trieve/sei-js.docs.sei.io references, no leftover asyncapi lockfile entries, and no impact on the wallet-stdio or SSE-session security invariants. No blockers; the notes concern a per-call MCP handshake, a hardcoded remote tool name, unverified passthrough of remote content, and whether docs.sei.io actually covers the @sei-js docs this PR deletes.
Findings: 0 blocking | 12 non-blocking | 6 posted inline
Blockers
- None at the file/PR level.
Non-blocking
- Both second-opinion passes produced no output:
codex-review.mdandcursor-review.mdare empty files, so nothing from Codex or Cursor could be merged into this review. - Capability regression:
search_sei_js_docs(Trieve-backed, scoped to the @sei-js libraries) is removed and not replaced by a dedicated tool. MCP clients now get a singlesearch_docspointed at the main Sei docs. Please confirm docs.sei.io indexes the @sei-js package documentation — if it doesn't, agents lose the ability to look up @sei-js APIs entirely, which is the main functional risk in this PR. - Link rot / decommissioning: the repo is now clean of
sei-js.docs.sei.ioreferences (verified), but already-published npm versions of all six packages, plus any third-party links, still point at that host. Worth confirming the Mintlify project is being retired with redirects to docs.sei.io rather than left to 404. Same question forhttps://docs.sei.io/ai/mcp-server, the new README target — I could not verify it resolves from this environment. packages/mcp-server/package.jsondeclares@modelcontextprotocol/sdk: ^1.7.0, butclient/streamableHttp.js(and the two-argClient.connect(transport, options)overload this PR relies on) only exist in later 1.x releases. The lockfile resolves 1.17.5 so CI passes, yet a consumer whose tree resolves 1.7.x would fail at import time. This floor is already too low for the existingserver/streamableHttp.jsimport, so it's pre-existing — but this PR adds a second dependency on the newer API and is a good moment to raise the minimum.- Removing
pnpm.overridesfor@asyncapi/*is correct as far as I can tell — those pins were transitive to the now-deletedmintCLI, andpnpm-lock.yamlcontains zeroasyncapientries after the change. Flagging only because the review guidelines call out those pins as a deliberate addition; no action needed unless they were kept for an advisory unrelated tomint. - Test coverage for the new module is thinner than the code it replaces (which had ~10 cases).
src/tests/docs/server.test.tscovers the happy path and aconnectrejection, but not:callToolrejecting, a non-Error value being thrown (theString(error)branch), orclient.close()throwing (the deliberately swallowedcatch {}). Per the guidelines this doesn't fail a gate since mcp-server has no Codecov target, but theclose()-throws branch in particular is the kind of cleanup path worth pinning. - 6 suggestion(s)/nit(s) flagged inline on specific lines.
Reuse and validate the docs MCP session, constrain remote output, and report the actual client version so search remains safe across upstream changes. Co-authored-by: Cursor <cursoragent@cursor.com>
PR SummaryMedium Risk Overview
Docs for MCP tools/context that lived in the deleted site are removed with the site; consumers are pointed at official docs.sei.io. Reviewed by Cursor Bugbot for commit b26c003. Bugbot is set up for automated code reviews on this repo. Configure here. |
|
Addressed the AI review follow-ups in
A live query also confirmed |
There was a problem hiding this comment.
Clean, well-scoped migration: the broken docs.sei-apis.io/search backend and the Mintlify/Trieve integration are replaced with a pooled MCP client against https://docs.sei.io/mcp, dead code and the in-repo docs site are removed, and the new module has real unit tests plus a changeset. No blocking defects found; the remaining notes are resilience/verification questions around the new client lifecycle and the npm tarball.
Findings: 0 blocking | 10 non-blocking | 4 posted inline
Blockers
- None at the file/PR level.
Non-blocking
- The Cursor review file (
cursor-review.md) is empty — that pass produced no output, so this review merges only Claude's findings with Codex's single P2. - No test exercises the real
Client/StreamableHTTPClientTransport. Every case insrc/tests/docs/server.test.tsinjects a hand-rolledDocsMcpClientFactory, so the@modelcontextprotocol/sdkbump (^1.7.0 → ^1.17.5) and the actual connect/onclose/callToolwiring are unverified by CI. The PR description says a live search was confirmed manually; consider a smoke test (or at least noting the manual check in the changeset) since this is the exact class of breakage PLT-843 was. Per guidelines §4, mcp-server has no Codecov target, so this is on merits only. - Dropping the
pnpm.overridespins for@asyncapi/*reverts deliberate supply-chain hardening (per the CHANGELOGs, the vector wasmint→@asyncapi/parser). I verified this is safe as landed:mintis gone andpnpm-lock.yamlno longer resolves any@asyncapi/*package. Worth calling out in the PR body so the removal isn't read later as an accidental regression of that fix. - README changes ship in the npm tarballs for
create-sei,ledger,precompilesandsei-global-wallet(all four listREADME.mdinfiles), but only@sei-js/mcp-serverhas a changeset. Those packages will keep advertising the now-deadsei-js.docs.sei.iolinks on npm until an unrelated release. Per guidelines §3 this is a question, not a defect — the repo has precedent for patch changesets across all six. - The PR deletes ~19k lines of
.mdxthat backedsei-js.docs.sei.io. Confirm that host redirects todocs.sei.io(or is retired deliberately) — inbound links from published npm READMEs of already-released versions will otherwise 404 permanently. - Positive note, no action needed:
DOCS_MCP_URLis a hardcoded HTTPS constant with no env override, unlike the*_RPC_URLoverrides incore/chains.ts. Keeping it non-overridable is the right call for a surface whose text is inlined straight into the model's context. - 4 suggestion(s)/nit(s) flagged inline on specific lines.
Keep shared sessions alive across per-query failures, use the client close hook, and validate discovered tool schemas before invocation. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
The Mintlify/Trieve removal and the search_docs rewrite onto https://docs.sei.io/mcp are clean and well-tested, with no leftover references to the deleted docs site or the dropped mint/trieve/@asyncapi deps. One blocking issue: under the stateless streamable-http transport a fresh McpServer is built per request, so each docs search opens an outbound MCP session to docs.sei.io that is never closed.
Findings: 1 blocking | 7 non-blocking | 3 posted inline
Blockers
- None at the file/PR level.
- 1 blocking issue(s) flagged inline on specific lines.
Non-blocking
- The Cursor second-opinion file (
cursor-review.md) is empty — that pass produced no output, so this review merges only my findings with Codex's. - I could not run
pnpm build:all/pnpm test:allin this environment (nonode_modulesinstalled; Codex reported the same). The newsrc/tests/docs/server.test.tsreads as well-targeted — session reuse, reconnect-on-close, tool discovery + fallback, non-text filtering, truncation, and cleanup-failure paths are all covered — but the suite is unverified here. Per the guidelinesmcp-serverhas no Codecov target, so this is a note, not a gate. @modelcontextprotocol/sdkjumps^1.7.0→^1.17.5, a large minor range on the package that also backsstdio.ts,http-sse.ts, andstreamable-http.ts. None of those files change here; worth confirming the existinghttp-ssesession-isolation tests still pass against the new SDK before merging.- The PR deletes the entire in-repo
@sei-jsdocs site and thesearch_sei_js_docstool, and the newsearch_docsdescription claims coverage of "@sei-js references". Worth confirming that the per-package content (precompiles, ledger, sei-global-wallet, create-sei, x402) actually landed on docs.sei.io and is indexed by its MCP endpoint — otherwise this is a net capability loss for the tool, not just a backend swap. The package READMEs now point only at GitHub/NPM, so there is no fallback. - A changeset is present (
.changeset/remove-docs-integrations.md,minorfor@sei-js/mcp-server), which satisfies guideline 3. The rootpackage.jsonchange (dropping thedocsscript,mint, and the@asyncapipnpm.overrides) is build-tooling only and@asyncapino longer appears inpnpm-lock.yaml, so no additional changeset is needed there. - 2 suggestion(s)/nit(s) flagged inline on specific lines.
Tie outbound docs clients to the local server lifecycle and recover expired remote sessions without disrupting ordinary query failures. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit b26c003. Configure here.
| } catch (error) { | ||
| if (client && error instanceof StreamableHTTPError && (error.code === 404 || error.code === 410)) { | ||
| await closeClient(client); | ||
| } |
There was a problem hiding this comment.
Session recovery never triggers
Medium Severity
The new session-expiry recovery closes the pooled docs client only when callTool/listTools rejects with StreamableHTTPError and status 404/410. The lockfile pins @modelcontextprotocol/sdk@1.17.5, where streamable HTTP send() rejects with a plain Error for non-OK responses (no .code), so instanceof StreamableHTTPError stays false. After a remote session expires, the stale client remains cached and later search_docs calls keep failing until process restart.
Reviewed by Cursor Bugbot for commit b26c003. Configure here.
There was a problem hiding this comment.
Solid, well-tested migration: the .npmignore docs/ removal is the genuine root-cause fix for the broken search_docs tool, and dropping the @asyncapi overrides is safe because mint was the only path to @asyncapi/parser (lockfile removes 14 asyncapi lines, adds none). No blockers; a few resilience nits in the new docs MCP client and some docs-migration follow-ups worth confirming.
Findings: 0 blocking | 11 non-blocking | 4 posted inline
Blockers
- None at the file/PR level.
Non-blocking
- The Cursor second-opinion pass produced no output (
cursor-review.mdis empty), so that lens contributed nothing to this review. Codex reported no material issues. - Per-request session churn on
streamable-http:StreamableHttpTransportcallsgetServer()for every POST (stateless design) andmcpServer.close()on response close, so eachsearch_docsinvocation now costs a freshconnect+listTools+callToolagainst docs.sei.io — three round trips where the old implementation did a singlefetch. Fine for stdio (the connection is reused for the process lifetime), but worth knowing before recommending the HTTP transports for docs-heavy workloads. Caching the discovered tool name at module scope, or hoisting the docs client out of the per-request server, would cut it back to one. docs/contributing.mdxwas the only contributor setup/PR guide in the repo and there is no rootCONTRIBUTING.mdto replace it. After this PR a new contributor has no in-repo instructions for local setup or the PR process. Worth adding a short rootCONTRIBUTING.mdor pointing at wherever that content now lives on docs.sei.io.- Changeset scope: the changeset covers
@sei-js/mcp-serveronly, but the published READMEs ofcreate-sei,ledger,precompiles, andsei-global-walletalso changed (README is in each package'sfiles). Those stalesei-js.docs.sei.iolinks stay live on npm until each package's next unrelated release. Per the repo guidelines this is a question rather than a defect — the repo has precedent for patch changesets across all six packages for release-note visibility (the@asyncapipinning), so consider the same here. - Already-published npm READMEs and the wider web link to
sei-js.docs.sei.io/*. Removing the source site here doesn't take down the deployment — please confirm the Mintlify project is decommissioned with redirects to the matching docs.sei.io pages, otherwise those links rot rather than move. - Informational, not a regression:
search_docspipes docs.sei.io content verbatim into the model's context, so that endpoint is a prompt-injection surface. The new code actually narrows it versus the old Trieve path — non-text blocks are dropped,structuredContentis discarded, and output is capped at 40k chars. No change requested; noting it so the mitigation is deliberate rather than incidental. - Verified and looks right:
.npmignore'sdocs/pattern was excludingdist/**/docs/from the tarball (afilesallowlist does not override.npmignoreinside included directories), which is why the shippedsearch_docswas broken. Removing@asyncapifrompnpm.overridesis also safe —mintwas the only route to@asyncapi/parserand the lockfile now has zero asyncapi entries. Note thatpnpm build:all/pnpm test:allcould not be re-run in this checkout (dependencies not installed), so the test plan is taken at the author's word. - 4 suggestion(s)/nit(s) flagged inline on specific lines.
|
|
||
| return sanitizeSearchResult(result); | ||
| } catch (error) { | ||
| if (client && error instanceof StreamableHTTPError && (error.code === 404 || error.code === 410)) { |
There was a problem hiding this comment.
[suggestion] The cached client is only invalidated on StreamableHTTPError with code 404/410. Any other transport-level death — ECONNRESET, TLS failure, the remote process restarting behind a load balancer — leaves the broken client memoized in clientPromise, and every subsequent search_docs call fails for the lifetime of the process. StreamableHTTPClientTransport routes request failures through onerror, not onclose, so the onclose hook on line 97 doesn't reliably cover this.
The old implementation had no such state: each call was a fresh fetch, so a transient outage self-healed on the next query. The test keeps the shared session available after an individual tool failure shows you deliberately don't want to tear down the session for one bad query, which is reasonable — but that case is a tool-level error (the remote responded), which is distinguishable from a connection-level one. Consider also invalidating when the error isn't a well-formed remote tool error.
Separately, even in the handled 404/410 path the user still gets an error for the query that triggered the reconnect. One transparent retry after closeClient would turn a visible failure into a no-op for the caller.
| const properties = tool.inputSchema.properties; | ||
| return properties !== undefined && 'query' in properties; | ||
| }; | ||
| const preferred = tools.find((tool) => tool.name === PREFERRED_REMOTE_DOCS_SEARCH_TOOL && acceptsQuery(tool)); |
There was a problem hiding this comment.
[nit] PREFERRED_REMOTE_DOCS_SEARCH_TOOL reads as though there's a fallback, but there isn't — if search_sei_docs isn't advertised under exactly that name, search_docs hard-fails. That's a deliberate choice (the does not dispatch queries to a renamed lookalike tool test pins it, and refusing to guess at an arbitrary remote tool is the safe default), so this is naming only: REQUIRED_ or EXPECTED_ would signal the actual behaviour.
Worth flagging the coupling regardless: a rename on the docs.sei.io side silently breaks docs search for every installed copy of this package, and the failure only surfaces at call time. An alert or contract test against the live endpoint would catch that before users do.
| }; | ||
|
|
||
| const selectRemoteSearchTool = async (client: DocsMcpClient): Promise<string> => { | ||
| const { tools } = await client.listTools(undefined, { timeout: CONNECT_TIMEOUT_MS }); |
There was a problem hiding this comment.
[nit] CONNECT_TIMEOUT_MS is used as the listTools timeout here. It works, but the name says otherwise — tool discovery is a separate round trip from the handshake and may warrant a different budget. A DISCOVERY_TIMEOUT_MS constant (even if initially the same 10s) would keep the two independently tunable.
|
|
||
| const safeText = | ||
| text.length > MAX_DOCS_RESPONSE_CHARS | ||
| ? `${text.slice(0, MAX_DOCS_RESPONSE_CHARS - TRUNCATION_NOTICE.length)}${TRUNCATION_NOTICE}` |
There was a problem hiding this comment.
[nit] text.slice() cuts at a UTF-16 code-unit boundary, so a doc page containing an emoji or other astral-plane character right at the 40k mark can leave a lone surrogate at the seam. Harmless for the model, but it can trip strict UTF-8 encoders downstream. Trimming back to the last whitespace boundary would sidestep it and read better in the truncated output.
Superseded: latest AI review found no blocking issues.


Summary
search_docsbacked by the officialhttps://docs.sei.io/mcpendpoint.@sei-js/mcp-serverchangeset.Linear
search_docstool is brokenTest plan
pnpm build:allpnpm test:alldocs.sei.io