ENG-2597: Promote stlc-generated node SDK + MCP server (Stainless cutover)#96
Conversation
…over)
Sync the stlc-generated SDK from node-sdk-staging onto main, replacing the
Stainless-hosted generation pipeline.
SDK API (same OAS snapshot as go/python): Conversation.title, connections.revoke
docs, regenerated resources/tests.
MCP server (@hyperspell/hyperspell-mcp) — folds in the ENG-2260 work:
- code-execution default switched from Stainless's hosted sandbox
('stainless-sandbox') to 'local' (the hosted sandbox dies with Stainless)
- docs search now uses an in-memory index built from embedded SDK method data
(no Stainless API dependency)
- regenerated tools (code-tool, methods, options, instructions)
De-Stainless'ing: README MCP badges -> npx; .stats.yml dropped Stainless metadata.
Mechanics:
- version pinned to manifest 0.39.0 across all 5 version locations
(package.json, src/version.ts, packages/mcp-server/{package.json,manifest.json},
and the MCP server's advertised version in server.ts)
- release-please files preserved; staging repo refs rewritten to node-sdk
- embedded SDK-doc version refs in local-docs-search.ts left as-is (pre-existing
gen artifact, byte-identical to current main)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Firetiger has created a monitoring plan for this PR. |
| if [ -z "${RELEASE_PLEASE_TOKEN}" ]; then | ||
| errors+=("The RELEASE_PLEASE_TOKEN secret has not been set. Create a fine-grained GitHub PAT and add it as a repository secret.") | ||
| fi |
There was a problem hiding this comment.
RELEASE_PLEASE_TOKEN check always fails — secret never injected into environment
GitHub Actions secrets are not exposed as env vars automatically; .github/workflows/release-doctor.yml runs this script with no env: block, so RELEASE_PLEASE_TOKEN is always empty and the check always errors, blocking every release-please PR.
Prompt to fix with AI
Copy this prompt into your AI coding assistant to fix this issue.
In `.github/workflows/release-doctor.yml`, the step 'Check release environment' (around line 17-19) runs `bash ./bin/check-release-environment` but does not expose the `RELEASE_PLEASE_TOKEN` secret as an environment variable. GitHub Actions secrets are never auto-injected; without an explicit `env:` block the variable is always empty, causing the new check in `bin/check-release-environment` lines 5-7 to always fail.
Fix: add an `env:` block to that step:
```yaml
- name: Check release environment
env:
RELEASE_PLEASE_TOKEN: ${{ secrets.RELEASE_PLEASE_TOKEN }}
run: |
bash ./bin/check-release-environment
</details>
|
|
||
| ``` | ||
| claude mcp add hyperspell_hyperspell_mcp_api --header "x-hyperspell-api-key: My API Key" --header "X-As-User: My User ID" --transport http https://hyperspell.stlmcp.com | ||
| claude mcp add hyperspell_hyperspell_mcp_api --env HYPERSPELL_API_KEY="My API Key" HYPERSPELL_USER_ID="My User ID" -- npx -y @hyperspell/hyperspell-mcp |
There was a problem hiding this comment.
Fix
claude mcp add command: options must precede server name and each env var needs its own --env flag
--env HYPERSPELL_API_KEY=... HYPERSPELL_USER_ID=... passes the second var as a positional argument (parsed as the server name), not as an env var, causing the command to fail. Per Claude Code docs, all options must also come before the server name.
| claude mcp add hyperspell_hyperspell_mcp_api --env HYPERSPELL_API_KEY="My API Key" HYPERSPELL_USER_ID="My User ID" -- npx -y @hyperspell/hyperspell-mcp | |
| claude mcp add --env HYPERSPELL_API_KEY="My API Key" --env HYPERSPELL_USER_ID="My User ID" hyperspell_hyperspell_mcp_api -- npx -y @hyperspell/hyperspell-mcp |
Prompt to fix with AI
Copy this prompt into your AI coding assistant to fix this issue.
In packages/mcp-server/README.md at line 59, fix the `claude mcp add` command. The current command `claude mcp add hyperspell_hyperspell_mcp_api --env HYPERSPELL_API_KEY="My API Key" HYPERSPELL_USER_ID="My User ID" -- npx -y @hyperspell/hyperspell-mcp` has two bugs: (1) options must precede the server name, and (2) each env var needs its own --env flag. Replace with: `claude mcp add --env HYPERSPELL_API_KEY="My API Key" --env HYPERSPELL_USER_ID="My User ID" hyperspell_hyperspell_mcp_api -- npx -y @hyperspell/hyperspell-mcp`
| } | ||
|
|
||
| return asTextContentResult(await searchRemote(body, reqContext)); | ||
| return asTextContentResult(await searchLocal(body)); |
There was a problem hiding this comment.
Unconditional searchLocal call throws when local search not initialized
searchLocal throws 'Local search not initialized' if _localSearch is undefined, but initMcpServer only calls setLocalSearch when mcpOptions.docsSearchMode === 'local' is explicitly set (server.ts:70). Programmatic callers who omit docsSearchMode will include the docs tool by default yet never initialize _localSearch, causing search_docs to throw an unhandled error at runtime.
| return asTextContentResult(await searchLocal(body)); | |
| if (!_localSearch) { | |
| return asTextContentResult('Docs search is not initialized. Pass docsSearchMode: \'local\' when calling initMcpServer.'); | |
| } | |
| return asTextContentResult(await searchLocal(body)); |
Prompt to fix with AI
Copy this prompt into your AI coding assistant to fix this issue.
In `packages/mcp-server/src/docs-search-tool.ts`, line 76, the handler unconditionally calls `searchLocal(body)`. `searchLocal` throws `'Local search not initialized'` if `_localSearch` is undefined (line 51-52). But in `packages/mcp-server/src/server.ts` lines 70-74, `setLocalSearch` is only called when `params.mcpOptions?.docsSearchMode === 'local'`. Programmatic callers who don't set `docsSearchMode` will include the docs tool by default (server.ts:191) but never initialize `_localSearch`, making `search_docs` throw at runtime with no error handling in `executeHandler` (server.ts:200-209).
Fix: In `server.ts` `initMcpServer`, change the condition from `if (params.mcpOptions?.docsSearchMode === 'local')` to always initialize local search when docs tools are included — e.g., `if ((params.mcpOptions?.includeDocsTools ?? true) && params.mcpOptions?.docsSearchMode !== undefined ? params.mcpOptions.docsSearchMode === 'local' : true)`. Alternatively, add a null-guard in the handler at line 76 in `docs-search-tool.ts` to return a friendly error message instead of throwing.
Confidence Score: 2/5 - Changes NeededNot safe to merge — this PR promoting the stlc-generated Node SDK and MCP server has three MAJOR reliability and correctness issues that would cause real user-facing failures. The Key Findings:
Files requiring special attention
|
What
Promote the stlc-generated node SDK + MCP server from
node-sdk-stagingontomain, replacing the Stainless-hosted generation pipeline. node analogue of the proven go cutover.This is the largest of the four (it carries the
@hyperspell/hyperspell-mcpMCP server) and folds in the ENG-2260 MCP work.SDK diff (audited clean — same OAS snapshot as go/python)
Conversation.title,connections.revokedocs, regenerated resources/tests. No whole-file deletions.0.39.0across all 5 version locations:package.json,src/version.ts,packages/mcp-server/package.json,packages/mcp-server/manifest.json, and the MCP server's advertised version inpackages/mcp-server/src/server.ts(this last one is not a release-please extra-file but public had0.39.0— staging shipped a0.0.1placeholder, stamped back to avoid regressing the version the MCP server reports at runtime).local-docs-search.tsleft as-is — pre-existing gen artifact, byte-identical to current main.@hyperspell/hyperspell-mcpmigrates off Stainless's hosted infra:'stainless-sandbox'→'local'. The hosted sandbox goes away when Stainless is retired; local execution is the replacement. Callers can still passcodeExecutionModeexplicitly.De-Stainless'd
README MCP badges →
npx @hyperspell/hyperspell-mcp;.stats.ymldropped the Stainless spec URL + hashes. Residualstainlessstrings remain only in embedded docs, de-Stainless instruction text, and dev/CI tooling — gen-controlled, same as go/python.Publishes to npm, so order matters (same as python):
.github/workflows/release-please.yml(separate PR) usingPRODUCTION_REPO_TOKEN.release: x.y.zPR → merging that is the single, gated npm publish.🤖 Generated with Claude Code