From 954ce3099c58efa8c5071b6830dad969401a050a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 17 Jan 2026 07:45:42 +0000 Subject: [PATCH 01/15] chore(internal): update `actions/checkout` version --- .github/workflows/ci.yml | 6 +++--- .github/workflows/publish-npm.yml | 2 +- .github/workflows/release-doctor.yml | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bbf24437..2d544578 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/hyperspell-typescript' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Node uses: actions/setup-node@v4 @@ -41,7 +41,7 @@ jobs: contents: read id-token: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Node uses: actions/setup-node@v4 @@ -83,7 +83,7 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/hyperspell-typescript' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Node uses: actions/setup-node@v4 diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml index 44d68bca..7c6f5dd8 100644 --- a/.github/workflows/publish-npm.yml +++ b/.github/workflows/publish-npm.yml @@ -20,7 +20,7 @@ jobs: contents: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Node uses: actions/setup-node@v3 diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index d4928c11..f9ea8493 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -12,10 +12,11 @@ jobs: if: github.repository == 'hyperspell/node-sdk' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Check release environment run: | bash ./bin/check-release-environment env: NPM_TOKEN: ${{ secrets.HYPERSPELL_NPM_TOKEN || secrets.NPM_TOKEN }} + From a0df1be8d411efda7d9c06ba663e64a72e308de0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 23 Jan 2026 04:29:34 +0000 Subject: [PATCH 02/15] feat(api): api update --- .stats.yml | 4 +- MIGRATION.md | 8 +-- README.md | 14 ++--- api.md | 7 +-- src/client.ts | 10 ++-- src/resources/index.ts | 5 +- src/resources/memories.ts | 110 ++++++++++++++++++++++++++++---------- src/resources/shared.ts | 59 ++++++++++++++++++-- 8 files changed, 164 insertions(+), 53 deletions(-) diff --git a/.stats.yml b/.stats.yml index b7a86017..737339ac 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 23 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/hyperspell%2Fhyperspell-a73c73c4848db6cc0b836219f2ace7bc9f6b4611d36f9daa2158f8bd5a7d0864.yml -openapi_spec_hash: 4ef2aeca3ffe2c6e6fbca0770a69c6fb +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/hyperspell%2Fhyperspell-34d6c3c2efa9b9b88d95ff49ff64f45440720b14a046edf3fac953d6eb0d13a1.yml +openapi_spec_hash: 80ce00b9f30af244e0a331c3430ed5e4 config_hash: bd77d0b7029518c697756456d6854f07 diff --git a/MIGRATION.md b/MIGRATION.md index 3b0d7631..f4e786f4 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -234,8 +234,8 @@ The `for await` syntax **is not affected**. This still works as-is: ```ts // Automatically fetches more pages as needed. -for await (const memory of client.memories.list()) { - console.log(memory); +for await (const memoryListResponse of client.memories.list()) { + console.log(memoryListResponse); } ``` @@ -257,10 +257,10 @@ Page classes for individual methods are now type aliases: ```ts // Before -export class MemoriesCursorPage extends CursorPage {} +export class MemoryListResponsesCursorPage extends CursorPage {} // After -export type MemoriesCursorPage = CursorPage; +export type MemoryListResponsesCursorPage = CursorPage; ``` If you were importing these classes at runtime, you'll need to switch to importing the base class or only import them at the type-level. diff --git a/README.md b/README.md index 6e6fb8e3..46d3907d 100644 --- a/README.md +++ b/README.md @@ -167,13 +167,13 @@ List methods in the Hyperspell API are paginated. You can use the `for await … of` syntax to iterate through items across all pages: ```ts -async function fetchAllMemories(params) { - const allMemories = []; +async function fetchAllMemoryListResponses(params) { + const allMemoryListResponses = []; // Automatically fetches more pages as needed. - for await (const memory of client.memories.list({ collection: 'REPLACE_ME' })) { - allMemories.push(memory); + for await (const memoryListResponse of client.memories.list({ collection: 'REPLACE_ME' })) { + allMemoryListResponses.push(memoryListResponse); } - return allMemories; + return allMemoryListResponses; } ``` @@ -181,8 +181,8 @@ Alternatively, you can request a single page at a time: ```ts let page = await client.memories.list({ collection: 'REPLACE_ME' }); -for (const memory of page.items) { - console.log(memory); +for (const memoryListResponse of page.items) { + console.log(memoryListResponse); } // Convenience methods are provided for manually paginating: diff --git a/api.md b/api.md index d9277c64..278f87e3 100644 --- a/api.md +++ b/api.md @@ -62,20 +62,21 @@ Methods: Types: -- Memory - MemoryStatus +- MemoryListResponse - MemoryDeleteResponse - MemoryAddBulkResponse +- MemoryGetResponse - MemoryStatusResponse Methods: - client.memories.update(resourceID, { ...params }) -> MemoryStatus -- client.memories.list({ ...params }) -> MemoriesCursorPage +- client.memories.list({ ...params }) -> MemoryListResponsesCursorPage - client.memories.delete(resourceID, { ...params }) -> MemoryDeleteResponse - client.memories.add({ ...params }) -> MemoryStatus - client.memories.addBulk({ ...params }) -> MemoryAddBulkResponse -- client.memories.get(resourceID, { ...params }) -> Memory +- client.memories.get(resourceID, { ...params }) -> MemoryGetResponse - client.memories.search({ ...params }) -> QueryResult - client.memories.status() -> MemoryStatusResponse - client.memories.upload({ ...params }) -> MemoryStatus diff --git a/src/client.ts b/src/client.ts index 416a7912..31bf87af 100644 --- a/src/client.ts +++ b/src/client.ts @@ -30,15 +30,16 @@ import { } from './resources/evaluate'; import { Memories, - MemoriesCursorPage, - Memory, MemoryAddBulkParams, MemoryAddBulkResponse, MemoryAddParams, MemoryDeleteParams, MemoryDeleteResponse, MemoryGetParams, + MemoryGetResponse, MemoryListParams, + MemoryListResponse, + MemoryListResponsesCursorPage, MemorySearchParams, MemoryStatus, MemoryStatusResponse, @@ -810,12 +811,13 @@ export declare namespace Hyperspell { export { Memories as Memories, - type Memory as Memory, type MemoryStatus as MemoryStatus, + type MemoryListResponse as MemoryListResponse, type MemoryDeleteResponse as MemoryDeleteResponse, type MemoryAddBulkResponse as MemoryAddBulkResponse, + type MemoryGetResponse as MemoryGetResponse, type MemoryStatusResponse as MemoryStatusResponse, - type MemoriesCursorPage as MemoriesCursorPage, + type MemoryListResponsesCursorPage as MemoryListResponsesCursorPage, type MemoryUpdateParams as MemoryUpdateParams, type MemoryListParams as MemoryListParams, type MemoryDeleteParams as MemoryDeleteParams, diff --git a/src/resources/index.ts b/src/resources/index.ts index 23089d86..8ae020bb 100644 --- a/src/resources/index.ts +++ b/src/resources/index.ts @@ -24,10 +24,11 @@ export { } from './integrations/integrations'; export { Memories, - type Memory, type MemoryStatus, + type MemoryListResponse, type MemoryDeleteResponse, type MemoryAddBulkResponse, + type MemoryGetResponse, type MemoryStatusResponse, type MemoryUpdateParams, type MemoryListParams, @@ -37,7 +38,7 @@ export { type MemoryGetParams, type MemorySearchParams, type MemoryUploadParams, - type MemoriesCursorPage, + type MemoryListResponsesCursorPage, } from './memories'; export { Vaults, diff --git a/src/resources/memories.ts b/src/resources/memories.ts index 8e471527..8676c0c8 100644 --- a/src/resources/memories.ts +++ b/src/resources/memories.ts @@ -37,7 +37,7 @@ export class Memories extends APIResource { * @example * ```ts * // Automatically fetches more pages as needed. - * for await (const memory of client.memories.list()) { + * for await (const memoryListResponse of client.memories.list()) { * // ... * } * ``` @@ -45,8 +45,8 @@ export class Memories extends APIResource { list( query: MemoryListParams | null | undefined = {}, options?: RequestOptions, - ): PagePromise { - return this._client.getAPIList('/memories/list', CursorPage, { query, ...options }); + ): PagePromise { + return this._client.getAPIList('/memories/list', CursorPage, { query, ...options }); } /** @@ -129,7 +129,7 @@ export class Memories extends APIResource { * }); * ``` */ - get(resourceID: string, params: MemoryGetParams, options?: RequestOptions): APIPromise { + get(resourceID: string, params: MemoryGetParams, options?: RequestOptions): APIPromise { const { source } = params; return this._client.get(path`/memories/get/${source}/${resourceID}`, options); } @@ -182,9 +182,27 @@ export class Memories extends APIResource { } } -export type MemoriesCursorPage = CursorPage; +export type MemoryListResponsesCursorPage = CursorPage; -export interface Memory { +export interface MemoryStatus { + resource_id: string; + + source: + | 'collections' + | 'reddit' + | 'notion' + | 'slack' + | 'google_calendar' + | 'google_mail' + | 'box' + | 'google_drive' + | 'vault' + | 'web_crawler'; + + status: 'pending' | 'processing' | 'completed' | 'failed'; +} + +export interface MemoryListResponse { resource_id: string; source: @@ -199,7 +217,7 @@ export interface Memory { | 'vault' | 'web_crawler'; - metadata?: Memory.Metadata; + metadata?: MemoryListResponse.Metadata; /** * The relevance of the resource to the query @@ -209,7 +227,7 @@ export interface Memory { title?: string | null; } -export namespace Memory { +export namespace MemoryListResponse { export interface Metadata { created_at?: string | null; @@ -237,24 +255,6 @@ export namespace Memory { } } -export interface MemoryStatus { - resource_id: string; - - source: - | 'collections' - | 'reddit' - | 'notion' - | 'slack' - | 'google_calendar' - | 'google_mail' - | 'box' - | 'google_drive' - | 'vault' - | 'web_crawler'; - - status: 'pending' | 'processing' | 'completed' | 'failed'; -} - export interface MemoryDeleteResponse { chunks_deleted: number; @@ -294,6 +294,59 @@ export interface MemoryAddBulkResponse { success?: boolean; } +export interface MemoryGetResponse { + resource_id: string; + + source: + | 'collections' + | 'reddit' + | 'notion' + | 'slack' + | 'google_calendar' + | 'google_mail' + | 'box' + | 'google_drive' + | 'vault' + | 'web_crawler'; + + metadata?: MemoryGetResponse.Metadata; + + /** + * The relevance of the resource to the query + */ + score?: number | null; + + title?: string | null; +} + +export namespace MemoryGetResponse { + export interface Metadata { + created_at?: string | null; + + events?: Array; + + indexed_at?: string | null; + + last_modified?: string | null; + + status?: 'pending' | 'processing' | 'completed' | 'failed'; + + url?: string | null; + + [k: string]: unknown; + } + + export namespace Metadata { + export interface Event { + message: string; + + type: 'error' | 'warning' | 'info' | 'success'; + + time?: string; + } + } +} + export interface MemoryStatusResponse { providers: { [key: string]: { [key: string]: number } }; @@ -953,12 +1006,13 @@ export interface MemoryUploadParams { export declare namespace Memories { export { - type Memory as Memory, type MemoryStatus as MemoryStatus, + type MemoryListResponse as MemoryListResponse, type MemoryDeleteResponse as MemoryDeleteResponse, type MemoryAddBulkResponse as MemoryAddBulkResponse, + type MemoryGetResponse as MemoryGetResponse, type MemoryStatusResponse as MemoryStatusResponse, - type MemoriesCursorPage as MemoriesCursorPage, + type MemoryListResponsesCursorPage as MemoryListResponsesCursorPage, type MemoryUpdateParams as MemoryUpdateParams, type MemoryListParams as MemoryListParams, type MemoryDeleteParams as MemoryDeleteParams, diff --git a/src/resources/shared.ts b/src/resources/shared.ts index 1fc4772c..3a4a309f 100644 --- a/src/resources/shared.ts +++ b/src/resources/shared.ts @@ -1,9 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import * as MemoriesAPI from './memories'; - export interface QueryResult { - documents: Array; + documents: Array; /** * The answer to the query, if the request was set to answer. @@ -27,3 +25,58 @@ export interface QueryResult { */ score?: number | null; } + +export namespace QueryResult { + export interface Document { + resource_id: string; + + source: + | 'collections' + | 'reddit' + | 'notion' + | 'slack' + | 'google_calendar' + | 'google_mail' + | 'box' + | 'google_drive' + | 'vault' + | 'web_crawler'; + + metadata?: Document.Metadata; + + /** + * The relevance of the resource to the query + */ + score?: number | null; + + title?: string | null; + } + + export namespace Document { + export interface Metadata { + created_at?: string | null; + + events?: Array; + + indexed_at?: string | null; + + last_modified?: string | null; + + status?: 'pending' | 'processing' | 'completed' | 'failed'; + + url?: string | null; + + [k: string]: unknown; + } + + export namespace Metadata { + export interface Event { + message: string; + + type: 'error' | 'warning' | 'info' | 'success'; + + time?: string; + } + } + } +} From e192aa3c9ac16fb38cad2e56f2e46498eb11f8ee Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 24 Jan 2026 06:49:50 +0000 Subject: [PATCH 03/15] chore(internal): update lock file --- packages/mcp-server/yarn.lock | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/mcp-server/yarn.lock b/packages/mcp-server/yarn.lock index a592e9c7..fe102775 100644 --- a/packages/mcp-server/yarn.lock +++ b/packages/mcp-server/yarn.lock @@ -3302,9 +3302,6 @@ readable-stream@^3.4.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" -"replicate@file:../../dist": - version "2.0.0-alpha.74" - require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" From 090b5c1a28992d318a2904cabe73556f61e76edf Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 24 Jan 2026 06:52:04 +0000 Subject: [PATCH 04/15] chore(ci): upgrade `actions/github-script` --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2d544578..1cba3029 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,7 +57,7 @@ jobs: - name: Get GitHub OIDC Token if: github.repository == 'stainless-sdks/hyperspell-typescript' id: github-oidc - uses: actions/github-script@v6 + uses: actions/github-script@v8 with: script: core.setOutput('github_token', await core.getIDToken()); From 1c6bacc1daa1c93198ea61ec75325cf7dc1e7950 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 05:42:36 +0000 Subject: [PATCH 05/15] chore(internal): codegen related update --- packages/mcp-server/src/code-tool.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/mcp-server/src/code-tool.ts b/packages/mcp-server/src/code-tool.ts index 5966b4ff..afb5de19 100644 --- a/packages/mcp-server/src/code-tool.ts +++ b/packages/mcp-server/src/code-tool.ts @@ -4,6 +4,7 @@ import { McpTool, Metadata, ToolCallResult, asErrorResult, asTextContentResult } import { Tool } from '@modelcontextprotocol/sdk/types.js'; import { readEnv, readEnvOrError } from './server'; import { WorkerInput, WorkerOutput } from './code-tool-types'; +import { Hyperspell } from 'hyperspell'; const prompt = `Runs JavaScript code to interact with the Hyperspell API. @@ -54,7 +55,7 @@ export function codeTool(): McpTool { required: ['code'], }, }; - const handler = async (_: unknown, args: any): Promise => { + const handler = async (client: Hyperspell, args: any): Promise => { const code = args.code as string; const intent = args.intent as string | undefined; @@ -70,8 +71,8 @@ export function codeTool(): McpTool { ...(stainlessAPIKey && { Authorization: stainlessAPIKey }), 'Content-Type': 'application/json', client_envs: JSON.stringify({ - HYPERSPELL_API_KEY: readEnvOrError('HYPERSPELL_API_KEY'), - HYPERSPELL_BASE_URL: readEnv('HYPERSPELL_BASE_URL'), + HYPERSPELL_API_KEY: readEnvOrError('HYPERSPELL_API_KEY') ?? client.apiKey ?? undefined, + HYPERSPELL_BASE_URL: readEnv('HYPERSPELL_BASE_URL') ?? client.baseURL ?? undefined, }), }, body: JSON.stringify({ From 6054c99672bb1399b5db94931d20979394ab9f60 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 05:44:26 +0000 Subject: [PATCH 06/15] fix(mcp): allow falling back for required env variables --- packages/mcp-server/src/code-tool.ts | 7 +++++-- packages/mcp-server/src/server.ts | 7 +++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/mcp-server/src/code-tool.ts b/packages/mcp-server/src/code-tool.ts index afb5de19..1ed298cc 100644 --- a/packages/mcp-server/src/code-tool.ts +++ b/packages/mcp-server/src/code-tool.ts @@ -2,7 +2,7 @@ import { McpTool, Metadata, ToolCallResult, asErrorResult, asTextContentResult } from './types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import { readEnv, readEnvOrError } from './server'; +import { readEnv, requireValue } from './server'; import { WorkerInput, WorkerOutput } from './code-tool-types'; import { Hyperspell } from 'hyperspell'; @@ -71,7 +71,10 @@ export function codeTool(): McpTool { ...(stainlessAPIKey && { Authorization: stainlessAPIKey }), 'Content-Type': 'application/json', client_envs: JSON.stringify({ - HYPERSPELL_API_KEY: readEnvOrError('HYPERSPELL_API_KEY') ?? client.apiKey ?? undefined, + HYPERSPELL_API_KEY: requireValue( + readEnv('HYPERSPELL_API_KEY') ?? client.apiKey, + 'set HYPERSPELL_API_KEY environment variable or provide apiKey client option', + ), HYPERSPELL_BASE_URL: readEnv('HYPERSPELL_BASE_URL') ?? client.baseURL ?? undefined, }), }, diff --git a/packages/mcp-server/src/server.ts b/packages/mcp-server/src/server.ts index 2965bd0b..2d9df0bb 100644 --- a/packages/mcp-server/src/server.ts +++ b/packages/mcp-server/src/server.ts @@ -146,3 +146,10 @@ export const readEnvOrError = (env: string): string => { } return envValue; }; + +export const requireValue = (value: T | undefined, description: string): T => { + if (value === undefined) { + throw new Error(`Missing required value: ${description}`); + } + return value; +}; From d4c938b9490184c9de54a36e0635b3b7b24e8aeb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 29 Jan 2026 07:23:13 +0000 Subject: [PATCH 07/15] fix(docs): fix mcp installation instructions for remote servers --- README.md | 4 ++-- packages/mcp-server/README.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 46d3907d..14ce4b1b 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ It is generated with [Stainless](https://www.stainless.com/). Use the Hyperspell MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application. -[![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=hyperspell-mcp&config=eyJuYW1lIjoiaHlwZXJzcGVsbC1tY3AiLCJ0cmFuc3BvcnQiOiJzc2UiLCJ1cmwiOiJodHRwczovL2h5cGVyc3BlbGwuc3RsbWNwLmNvbS9zc2UifQ) -[![Install in VS Code](https://img.shields.io/badge/_-Add_to_VS_Code-blue?style=for-the-badge&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCA0MCA0MCI+PHBhdGggZmlsbD0iI0VFRSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMzAuMjM1IDM5Ljg4NGEyLjQ5MSAyLjQ5MSAwIDAgMS0xLjc4MS0uNzNMMTIuNyAyNC43OGwtMy40NiAyLjYyNC0zLjQwNiAyLjU4MmExLjY2NSAxLjY2NSAwIDAgMS0xLjA4Mi4zMzggMS42NjQgMS42NjQgMCAwIDEtMS4wNDYtLjQzMWwtMi4yLTJhMS42NjYgMS42NjYgMCAwIDEgMC0yLjQ2M0w3LjQ1OCAyMCA0LjY3IDE3LjQ1MyAxLjUwNyAxNC41N2ExLjY2NSAxLjY2NSAwIDAgMSAwLTIuNDYzbDIuMi0yYTEuNjY1IDEuNjY1IDAgMCAxIDIuMTMtLjA5N2w2Ljg2MyA1LjIwOUwyOC40NTIuODQ0YTIuNDg4IDIuNDg4IDAgMCAxIDEuODQxLS43MjljLjM1MS4wMDkuNjk5LjA5MSAxLjAxOS4yNDVsOC4yMzYgMy45NjFhMi41IDIuNSAwIDAgMSAxLjQxNSAyLjI1M3YuMDk5LS4wNDVWMzMuMzd2LS4wNDUuMDk1YTIuNTAxIDIuNTAxIDAgMCAxLTEuNDE2IDIuMjU3bC04LjIzNSAzLjk2MWEyLjQ5MiAyLjQ5MiAwIDAgMS0xLjA3Ny4yNDZabS43MTYtMjguOTQ3LTExLjk0OCA5LjA2MiAxMS45NTIgOS4wNjUtLjAwNC0xOC4xMjdaIi8+PC9zdmc+)](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22hyperspell-mcp%22%2C%22type%22%3A%22sse%22%2C%22url%22%3A%22https%3A%2F%2Fhyperspell.stlmcp.com%2Fsse%22%7D) +[![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=hyperspell-mcp&config=eyJuYW1lIjoiaHlwZXJzcGVsbC1tY3AiLCJ0cmFuc3BvcnQiOiJodHRwIiwidXJsIjoiaHR0cHM6Ly9oeXBlcnNwZWxsLnN0bG1jcC5jb20iLCJoZWFkZXJzIjp7IngtaHlwZXJzcGVsbC1hcGkta2V5IjoiTXkgQVBJIEtleSIsIlgtQXMtVXNlciI6Ik15IFVzZXIgSUQifX0) +[![Install in VS Code](https://img.shields.io/badge/_-Add_to_VS_Code-blue?style=for-the-badge&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCA0MCA0MCI+PHBhdGggZmlsbD0iI0VFRSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMzAuMjM1IDM5Ljg4NGEyLjQ5MSAyLjQ5MSAwIDAgMS0xLjc4MS0uNzNMMTIuNyAyNC43OGwtMy40NiAyLjYyNC0zLjQwNiAyLjU4MmExLjY2NSAxLjY2NSAwIDAgMS0xLjA4Mi4zMzggMS42NjQgMS42NjQgMCAwIDEtMS4wNDYtLjQzMWwtMi4yLTJhMS42NjYgMS42NjYgMCAwIDEgMC0yLjQ2M0w3LjQ1OCAyMCA0LjY3IDE3LjQ1MyAxLjUwNyAxNC41N2ExLjY2NSAxLjY2NSAwIDAgMSAwLTIuNDYzbDIuMi0yYTEuNjY1IDEuNjY1IDAgMCAxIDIuMTMtLjA5N2w2Ljg2MyA1LjIwOUwyOC40NTIuODQ0YTIuNDg4IDIuNDg4IDAgMCAxIDEuODQxLS43MjljLjM1MS4wMDkuNjk5LjA5MSAxLjAxOS4yNDVsOC4yMzYgMy45NjFhMi41IDIuNSAwIDAgMSAxLjQxNSAyLjI1M3YuMDk5LS4wNDVWMzMuMzd2LS4wNDUuMDk1YTIuNTAxIDIuNTAxIDAgMCAxLTEuNDE2IDIuMjU3bC04LjIzNSAzLjk2MWEyLjQ5MiAyLjQ5MiAwIDAgMS0xLjA3Ny4yNDZabS43MTYtMjguOTQ3LTExLjk0OCA5LjA2MiAxMS45NTIgOS4wNjUtLjAwNC0xOC4xMjdaIi8+PC9zdmc+)](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22hyperspell-mcp%22%2C%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fhyperspell.stlmcp.com%22%2C%22headers%22%3A%7B%22x-hyperspell-api-key%22%3A%22My%20API%20Key%22%2C%22X-As-User%22%3A%22My%20User%20ID%22%7D%7D) > Note: You may need to set environment variables in your MCP client. diff --git a/packages/mcp-server/README.md b/packages/mcp-server/README.md index 3e454fbc..656c6b69 100644 --- a/packages/mcp-server/README.md +++ b/packages/mcp-server/README.md @@ -41,14 +41,14 @@ For clients with a configuration JSON, it might look something like this: If you use Cursor, you can install the MCP server by using the button below. You will need to set your environment variables in Cursor's `mcp.json`, which can be found in Cursor Settings > Tools & MCP > New MCP Server. -[![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=hyperspell-mcp&config=eyJuYW1lIjoiaHlwZXJzcGVsbC1tY3AiLCJ0cmFuc3BvcnQiOiJzc2UiLCJ1cmwiOiJodHRwczovL2h5cGVyc3BlbGwuc3RsbWNwLmNvbS9zc2UiLCJlbnYiOnsiSFlQRVJTUEVMTF9BUElfS0VZIjoiU2V0IHlvdXIgSFlQRVJTUEVMTF9BUElfS0VZIGhlcmUuIiwiSFlQRVJTUEVMTF9VU0VSX0lEIjoiU2V0IHlvdXIgSFlQRVJTUEVMTF9VU0VSX0lEIGhlcmUuIn19) +[![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=hyperspell-mcp&config=eyJuYW1lIjoiaHlwZXJzcGVsbC1tY3AiLCJ0cmFuc3BvcnQiOiJodHRwIiwidXJsIjoiaHR0cHM6Ly9oeXBlcnNwZWxsLnN0bG1jcC5jb20iLCJoZWFkZXJzIjp7IngtaHlwZXJzcGVsbC1hcGkta2V5IjoiTXkgQVBJIEtleSIsIlgtQXMtVXNlciI6Ik15IFVzZXIgSUQifX0) ### VS Code If you use MCP, you can install the MCP server by clicking the link below. You will need to set your environment variables in VS Code's `mcp.json`, which can be found via Command Palette > MCP: Open User Configuration. -[Open VS Code](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22hyperspell-mcp%22%2C%22type%22%3A%22sse%22%2C%22url%22%3A%22https%3A%2F%2Fhyperspell.stlmcp.com%2Fsse%22%2C%22env%22%3A%7B%22HYPERSPELL_API_KEY%22%3A%22Set%20your%20HYPERSPELL_API_KEY%20here.%22%2C%22HYPERSPELL_USER_ID%22%3A%22Set%20your%20HYPERSPELL_USER_ID%20here.%22%7D%7D) +[Open VS Code](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22hyperspell-mcp%22%2C%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fhyperspell.stlmcp.com%22%2C%22headers%22%3A%7B%22x-hyperspell-api-key%22%3A%22My%20API%20Key%22%2C%22X-As-User%22%3A%22My%20User%20ID%22%7D%7D) ### Claude Code @@ -56,7 +56,7 @@ If you use Claude Code, you can install the MCP server by running the command be environment variables in Claude Code's `.claude.json`, which can be found in your home directory. ``` -claude mcp add hyperspell_mcp_api --env HYPERSPELL_API_KEY="Your HYPERSPELL_API_KEY here." HYPERSPELL_USER_ID="Your HYPERSPELL_USER_ID here." --transport sse https://hyperspell.stlmcp.com/sse +claude mcp add hyperspell_mcp_api --header "x-hyperspell-api-key: My API Key" --header "X-As-User: My User ID" --transport http https://hyperspell.stlmcp.com ``` ## Code Mode From db052dcd1a475b54cb7c302df23bcfaa73c969d0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 29 Jan 2026 07:28:37 +0000 Subject: [PATCH 08/15] chore(mcp): up tsconfig lib version to es2022 --- packages/mcp-server/tsconfig.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/mcp-server/tsconfig.json b/packages/mcp-server/tsconfig.json index 08a5d77d..e95247d7 100644 --- a/packages/mcp-server/tsconfig.json +++ b/packages/mcp-server/tsconfig.json @@ -2,8 +2,8 @@ "include": ["src", "tests", "examples"], "exclude": [], "compilerOptions": { - "target": "es2020", - "lib": ["es2020"], + "target": "es2022", + "lib": ["es2022"], "module": "commonjs", "moduleResolution": "node", "esModuleInterop": true, From 5b2927352218310b6e3a3ec4c950dc4e8d15cbf9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 3 Feb 2026 08:11:09 +0000 Subject: [PATCH 09/15] fix(client): avoid memory leak with abort signals --- src/client.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/client.ts b/src/client.ts index 31bf87af..23762797 100644 --- a/src/client.ts +++ b/src/client.ts @@ -550,9 +550,10 @@ export class Hyperspell { controller: AbortController, ): Promise { const { signal, method, ...options } = init || {}; - if (signal) signal.addEventListener('abort', () => controller.abort()); + const abort = controller.abort.bind(controller); + if (signal) signal.addEventListener('abort', abort, { once: true }); - const timeout = setTimeout(() => controller.abort(), ms); + const timeout = setTimeout(abort, ms); const isReadableBody = ((globalThis as any).ReadableStream && options.body instanceof (globalThis as any).ReadableStream) || From b7dde774dee58aaf475d07c08d8792753c09fc85 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 3 Feb 2026 08:13:51 +0000 Subject: [PATCH 10/15] chore(client): do not parse responses with empty content-length --- src/internal/parse.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/internal/parse.ts b/src/internal/parse.ts index 7c91bd39..af00cee3 100644 --- a/src/internal/parse.ts +++ b/src/internal/parse.ts @@ -29,6 +29,12 @@ export async function defaultParseResponse(client: Hyperspell, props: APIResp const mediaType = contentType?.split(';')[0]?.trim(); const isJSON = mediaType?.includes('application/json') || mediaType?.endsWith('+json'); if (isJSON) { + const contentLength = response.headers.get('content-length'); + if (contentLength === '0') { + // if there is no content we can't do anything + return undefined as T; + } + const json = await response.json(); return json as T; } From 027ce2cd7d3164f49134235a10446ed1e838d17f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 3 Feb 2026 08:22:16 +0000 Subject: [PATCH 11/15] chore(internal): support oauth authorization code flow for MCP servers --- packages/mcp-server/package.json | 4 ++++ packages/mcp-server/src/headers.ts | 4 +++- packages/mcp-server/src/http.ts | 9 +++++---- packages/mcp-server/src/options.ts | 1 + 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json index 89e47e24..36a059e6 100644 --- a/packages/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -34,10 +34,12 @@ "@cloudflare/cabidela": "^0.2.4", "@modelcontextprotocol/sdk": "^1.25.2", "@valtown/deno-http-worker": "^0.0.21", + "cookie-parser": "^1.4.6", "cors": "^2.8.5", "express": "^5.1.0", "fuse.js": "^7.1.0", "jq-web": "https://github.com/stainless-api/jq-web/releases/download/v0.8.8/jq-web.tar.gz", + "morgan": "^1.10.0", "qs": "^6.14.1", "typescript": "5.8.3", "yargs": "^17.7.2", @@ -50,9 +52,11 @@ }, "devDependencies": { "@anthropic-ai/mcpb": "^2.1.2", + "@types/cookie-parser": "^1.4.10", "@types/cors": "^2.8.19", "@types/express": "^5.0.3", "@types/jest": "^29.4.0", + "@types/morgan": "^1.9.10", "@types/qs": "^6.14.0", "@types/yargs": "^17.0.8", "@typescript-eslint/eslint-plugin": "8.31.1", diff --git a/packages/mcp-server/src/headers.ts b/packages/mcp-server/src/headers.ts index 2624322f..a924dab2 100644 --- a/packages/mcp-server/src/headers.ts +++ b/packages/mcp-server/src/headers.ts @@ -3,7 +3,7 @@ import { IncomingMessage } from 'node:http'; import { ClientOptions } from 'hyperspell'; -export const parseAuthHeaders = (req: IncomingMessage): Partial => { +export const parseAuthHeaders = (req: IncomingMessage, required?: boolean): Partial => { if (req.headers.authorization) { const scheme = req.headers.authorization.split(' ')[0]!; const value = req.headers.authorization.slice(scheme.length + 1); @@ -15,6 +15,8 @@ export const parseAuthHeaders = (req: IncomingMessage): Partial = 'Unsupported authorization scheme. Expected the "Authorization" header to be a supported scheme (Bearer).', ); } + } else if (required) { + throw new Error('Missing required Authorization header; see WWW-Authenticate header for details.'); } const apiKey = diff --git a/packages/mcp-server/src/http.ts b/packages/mcp-server/src/http.ts index dcfeba6a..42eb5002 100644 --- a/packages/mcp-server/src/http.ts +++ b/packages/mcp-server/src/http.ts @@ -2,8 +2,8 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp'; import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; - import express from 'express'; +import morgan from 'morgan'; import { McpOptions } from './options'; import { ClientOptions, initMcpServer, newMcpServer } from './server'; import { parseAuthHeaders } from './headers'; @@ -20,7 +20,7 @@ const newServer = ({ const server = newMcpServer(); try { - const authOptions = parseAuthHeaders(req); + const authOptions = parseAuthHeaders(req, false); initMcpServer({ server: server, clientOptions: { @@ -75,14 +75,15 @@ const del = async (req: express.Request, res: express.Response) => { export const streamableHTTPApp = ({ clientOptions = {}, - mcpOptions = {}, + mcpOptions, }: { clientOptions?: ClientOptions; - mcpOptions?: McpOptions; + mcpOptions: McpOptions; }): express.Express => { const app = express(); app.set('query parser', 'extended'); app.use(express.json()); + app.use(morgan('combined')); app.get('/', get); app.post('/', post({ clientOptions, mcpOptions })); diff --git a/packages/mcp-server/src/options.ts b/packages/mcp-server/src/options.ts index c66ad8ce..025280ec 100644 --- a/packages/mcp-server/src/options.ts +++ b/packages/mcp-server/src/options.ts @@ -35,6 +35,7 @@ export function parseCLIOptions(): CLIOptions { }) .option('port', { type: 'number', + default: 3000, description: 'Port to serve on if using http transport', }) .option('socket', { From ecf2c20a9d7aa7cfb1d5734a14ba75a4f5ebe0e8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 5 Feb 2026 05:37:56 +0000 Subject: [PATCH 12/15] chore(client): restructure abort controller binding --- src/client.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/client.ts b/src/client.ts index 23762797..fb8174a5 100644 --- a/src/client.ts +++ b/src/client.ts @@ -550,7 +550,7 @@ export class Hyperspell { controller: AbortController, ): Promise { const { signal, method, ...options } = init || {}; - const abort = controller.abort.bind(controller); + const abort = this._makeAbort(controller); if (signal) signal.addEventListener('abort', abort, { once: true }); const timeout = setTimeout(abort, ms); @@ -576,6 +576,7 @@ export class Hyperspell { return await this.fetch.call(undefined, url, fetchOptions); } finally { clearTimeout(timeout); + if (signal) signal.removeEventListener('abort', abort); } } @@ -720,6 +721,12 @@ export class Hyperspell { return headers.values; } + private _makeAbort(controller: AbortController) { + // note: we can't just inline this method inside `fetchWithTimeout()` because then the closure + // would capture all request options, and cause a memory leak. + return () => controller.abort(); + } + private buildBody({ options: { body, headers: rawHeaders } }: { options: FinalRequestOptions }): { bodyHeaders: HeadersLike; body: BodyInit | undefined; From 051f0428d7d30eadf1deb83214757e98e7cb86c0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 5 Feb 2026 05:38:53 +0000 Subject: [PATCH 13/15] chore(internal): refactor flag parsing for MCP servers and add debug flag --- packages/mcp-server/package.json | 1 + packages/mcp-server/src/http.ts | 27 ++++++++++++++++++++++----- packages/mcp-server/src/index.ts | 6 +++++- packages/mcp-server/src/options.ts | 28 +++++++++++++++------------- 4 files changed, 43 insertions(+), 19 deletions(-) diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json index 36a059e6..063c9c92 100644 --- a/packages/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -40,6 +40,7 @@ "fuse.js": "^7.1.0", "jq-web": "https://github.com/stainless-api/jq-web/releases/download/v0.8.8/jq-web.tar.gz", "morgan": "^1.10.0", + "morgan-body": "^2.6.9", "qs": "^6.14.1", "typescript": "5.8.3", "yargs": "^17.7.2", diff --git a/packages/mcp-server/src/http.ts b/packages/mcp-server/src/http.ts index 42eb5002..3d728ecc 100644 --- a/packages/mcp-server/src/http.ts +++ b/packages/mcp-server/src/http.ts @@ -4,6 +4,7 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp'; import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; import express from 'express'; import morgan from 'morgan'; +import morganBody from 'morgan-body'; import { McpOptions } from './options'; import { ClientOptions, initMcpServer, newMcpServer } from './server'; import { parseAuthHeaders } from './headers'; @@ -76,14 +77,26 @@ const del = async (req: express.Request, res: express.Response) => { export const streamableHTTPApp = ({ clientOptions = {}, mcpOptions, + debug, }: { clientOptions?: ClientOptions; mcpOptions: McpOptions; + debug: boolean; }): express.Express => { const app = express(); app.set('query parser', 'extended'); app.use(express.json()); - app.use(morgan('combined')); + + if (debug) { + morganBody(app, { + logAllReqHeader: true, + logAllResHeader: true, + logRequestBody: true, + logResponseBody: true, + }); + } else { + app.use(morgan('combined')); + } app.get('/', get); app.post('/', post({ clientOptions, mcpOptions })); @@ -92,9 +105,13 @@ export const streamableHTTPApp = ({ return app; }; -export const launchStreamableHTTPServer = async (options: McpOptions, port: number | string | undefined) => { - const app = streamableHTTPApp({ mcpOptions: options }); - const server = app.listen(port); +export const launchStreamableHTTPServer = async (params: { + mcpOptions: McpOptions; + debug: boolean; + port: number | string | undefined; +}) => { + const app = streamableHTTPApp({ mcpOptions: params.mcpOptions, debug: params.debug }); + const server = app.listen(params.port); const address = server.address(); if (typeof address === 'string') { @@ -102,6 +119,6 @@ export const launchStreamableHTTPServer = async (options: McpOptions, port: numb } else if (address !== null) { console.error(`MCP Server running on streamable HTTP on port ${address.port}`); } else { - console.error(`MCP Server running on streamable HTTP on port ${port}`); + console.error(`MCP Server running on streamable HTTP on port ${params.port}`); } }; diff --git a/packages/mcp-server/src/index.ts b/packages/mcp-server/src/index.ts index 0f6dd426..d75968e3 100644 --- a/packages/mcp-server/src/index.ts +++ b/packages/mcp-server/src/index.ts @@ -21,7 +21,11 @@ async function main() { await launchStdioServer(); break; case 'http': - await launchStreamableHTTPServer(options, options.port ?? options.socket); + await launchStreamableHTTPServer({ + mcpOptions: options, + debug: options.debug, + port: options.port ?? options.socket, + }); break; } } diff --git a/packages/mcp-server/src/options.ts b/packages/mcp-server/src/options.ts index 025280ec..74380833 100644 --- a/packages/mcp-server/src/options.ts +++ b/packages/mcp-server/src/options.ts @@ -4,6 +4,7 @@ import { hideBin } from 'yargs/helpers'; import z from 'zod'; export type CLIOptions = McpOptions & { + debug: boolean; transport: 'stdio' | 'http'; port: number | undefined; socket: string | undefined; @@ -15,17 +16,24 @@ export type McpOptions = { export function parseCLIOptions(): CLIOptions { const opts = yargs(hideBin(process.argv)) - .option('tools', { + .option('debug', { type: 'boolean', description: 'Enable debug logging' }) + .option('no-tools', { type: 'string', array: true, choices: ['code', 'docs'], - description: 'Use dynamic tools or all tools', + description: 'Tools to explicitly disable', }) - .option('no-tools', { + .option('port', { + type: 'number', + default: 3000, + description: 'Port to serve on if using http transport', + }) + .option('socket', { type: 'string', description: 'Unix socket to serve on if using http transport' }) + .option('tools', { type: 'string', array: true, choices: ['code', 'docs'], - description: 'Do not use any dynamic or all tools', + description: 'Tools to explicitly enable', }) .option('transport', { type: 'string', @@ -33,15 +41,8 @@ export function parseCLIOptions(): CLIOptions { default: 'stdio', description: 'What transport to use; stdio for local servers or http for remote servers', }) - .option('port', { - type: 'number', - default: 3000, - description: 'Port to serve on if using http transport', - }) - .option('socket', { - type: 'string', - description: 'Unix socket to serve on if using http transport', - }) + .env('MCP_SERVER') + .version(true) .help(); const argv = opts.parseSync(); @@ -57,6 +58,7 @@ export function parseCLIOptions(): CLIOptions { return { ...(includeDocsTools !== undefined && { includeDocsTools }), + debug: !!argv.debug, transport, port: argv.port, socket: argv.socket, From 581fb5e5aaf49b32facacc9adc28ca3797dbd117 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 5 Feb 2026 05:39:46 +0000 Subject: [PATCH 14/15] feat(mcp): add initial server instructions Adds generated MCP server instructions, to help agents get easy tasks on the first try. --- packages/mcp-server/src/http.ts | 10 +++---- packages/mcp-server/src/server.ts | 48 +++++++++++++++++++++++++++---- packages/mcp-server/src/stdio.ts | 4 +-- 3 files changed, 49 insertions(+), 13 deletions(-) diff --git a/packages/mcp-server/src/http.ts b/packages/mcp-server/src/http.ts index 3d728ecc..1f851cb6 100644 --- a/packages/mcp-server/src/http.ts +++ b/packages/mcp-server/src/http.ts @@ -9,7 +9,7 @@ import { McpOptions } from './options'; import { ClientOptions, initMcpServer, newMcpServer } from './server'; import { parseAuthHeaders } from './headers'; -const newServer = ({ +const newServer = async ({ clientOptions, req, res, @@ -17,12 +17,12 @@ const newServer = ({ clientOptions: ClientOptions; req: express.Request; res: express.Response; -}): McpServer | null => { - const server = newMcpServer(); +}): Promise => { + const server = await newMcpServer(); try { const authOptions = parseAuthHeaders(req, false); - initMcpServer({ + await initMcpServer({ server: server, clientOptions: { ...clientOptions, @@ -46,7 +46,7 @@ const newServer = ({ const post = (options: { clientOptions: ClientOptions; mcpOptions: McpOptions }) => async (req: express.Request, res: express.Response) => { - const server = newServer({ ...options, req, res }); + const server = await newServer({ ...options, req, res }); // If we return null, we already set the authorization error. if (server === null) return; const transport = new StreamableHTTPServerTransport(); diff --git a/packages/mcp-server/src/server.ts b/packages/mcp-server/src/server.ts index 2d9df0bb..f872f74a 100644 --- a/packages/mcp-server/src/server.ts +++ b/packages/mcp-server/src/server.ts @@ -17,23 +17,59 @@ import { HandlerFunction, McpTool } from './types'; export { McpOptions } from './options'; export { ClientOptions } from 'hyperspell'; -export const newMcpServer = () => +async function getInstructions() { + // This API key is optional; providing it allows the server to fetch instructions for unreleased versions. + const stainlessAPIKey = readEnv('STAINLESS_API_KEY'); + const response = await fetch( + readEnv('CODE_MODE_INSTRUCTIONS_URL') ?? 'https://api.stainless.com/api/ai/instructions/hyperspell', + { + method: 'GET', + headers: { ...(stainlessAPIKey && { Authorization: stainlessAPIKey }) }, + }, + ); + + let instructions: string | undefined; + if (!response.ok) { + console.warn( + 'Warning: failed to retrieve MCP server instructions. Proceeding with default instructions...', + ); + + instructions = ` + This is the hyperspell MCP server. You will use Code Mode to help the user perform + actions. You can use search_docs tool to learn about how to take action with this server. Then, + you will write TypeScript code using the execute tool take action. It is CRITICAL that you be + thoughtful and deliberate when executing code. Always try to entirely solve the problem in code + block: it can be as long as you need to get the job done! + `; + } + + instructions ??= ((await response.json()) as { instructions: string }).instructions; + instructions = ` + The current time in Unix timestamps is ${Date.now()}. + + ${instructions} + `; + + return instructions; +} + +export const newMcpServer = async () => new McpServer( { name: 'hyperspell_api', version: '0.30.0', }, - { capabilities: { tools: {}, logging: {} } }, + { + instructions: await getInstructions(), + capabilities: { tools: {}, logging: {} }, + }, ); -// Create server instance -export const server = newMcpServer(); - /** * Initializes the provided MCP Server with the given tools and handlers. * If not provided, the default client, tools and handlers will be used. */ -export function initMcpServer(params: { +export async function initMcpServer(params: { server: Server | McpServer; clientOptions?: ClientOptions; mcpOptions?: McpOptions; diff --git a/packages/mcp-server/src/stdio.ts b/packages/mcp-server/src/stdio.ts index f07696f3..47aeb0c9 100644 --- a/packages/mcp-server/src/stdio.ts +++ b/packages/mcp-server/src/stdio.ts @@ -2,9 +2,9 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js' import { initMcpServer, newMcpServer } from './server'; export const launchStdioServer = async () => { - const server = newMcpServer(); + const server = await newMcpServer(); - initMcpServer({ server }); + await initMcpServer({ server }); const transport = new StdioServerTransport(); await server.connect(transport); From 27ed29940a73cc73d6966c1fb2ebc6a8126b6221 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 5 Feb 2026 05:40:12 +0000 Subject: [PATCH 15/15] release: 0.31.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 29 +++++++++++++++++++++++++++++ package.json | 2 +- packages/mcp-server/package.json | 2 +- packages/mcp-server/src/server.ts | 2 +- src/version.ts | 2 +- 6 files changed, 34 insertions(+), 5 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 716d0046..8e3d9554 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.30.0" + ".": "0.31.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index f2334470..46aea751 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,34 @@ # Changelog +## 0.31.0 (2026-02-05) + +Full Changelog: [v0.30.0...v0.31.0](https://github.com/hyperspell/node-sdk/compare/v0.30.0...v0.31.0) + +### Features + +* **api:** api update ([a0df1be](https://github.com/hyperspell/node-sdk/commit/a0df1be8d411efda7d9c06ba663e64a72e308de0)) +* **mcp:** add initial server instructions ([581fb5e](https://github.com/hyperspell/node-sdk/commit/581fb5e5aaf49b32facacc9adc28ca3797dbd117)) + + +### Bug Fixes + +* **client:** avoid memory leak with abort signals ([5b29273](https://github.com/hyperspell/node-sdk/commit/5b2927352218310b6e3a3ec4c950dc4e8d15cbf9)) +* **docs:** fix mcp installation instructions for remote servers ([d4c938b](https://github.com/hyperspell/node-sdk/commit/d4c938b9490184c9de54a36e0635b3b7b24e8aeb)) +* **mcp:** allow falling back for required env variables ([6054c99](https://github.com/hyperspell/node-sdk/commit/6054c99672bb1399b5db94931d20979394ab9f60)) + + +### Chores + +* **ci:** upgrade `actions/github-script` ([090b5c1](https://github.com/hyperspell/node-sdk/commit/090b5c1a28992d318a2904cabe73556f61e76edf)) +* **client:** do not parse responses with empty content-length ([b7dde77](https://github.com/hyperspell/node-sdk/commit/b7dde774dee58aaf475d07c08d8792753c09fc85)) +* **client:** restructure abort controller binding ([ecf2c20](https://github.com/hyperspell/node-sdk/commit/ecf2c20a9d7aa7cfb1d5734a14ba75a4f5ebe0e8)) +* **internal:** codegen related update ([1c6bacc](https://github.com/hyperspell/node-sdk/commit/1c6bacc1daa1c93198ea61ec75325cf7dc1e7950)) +* **internal:** refactor flag parsing for MCP servers and add debug flag ([051f042](https://github.com/hyperspell/node-sdk/commit/051f0428d7d30eadf1deb83214757e98e7cb86c0)) +* **internal:** support oauth authorization code flow for MCP servers ([027ce2c](https://github.com/hyperspell/node-sdk/commit/027ce2cd7d3164f49134235a10446ed1e838d17f)) +* **internal:** update `actions/checkout` version ([954ce30](https://github.com/hyperspell/node-sdk/commit/954ce3099c58efa8c5071b6830dad969401a050a)) +* **internal:** update lock file ([e192aa3](https://github.com/hyperspell/node-sdk/commit/e192aa3c9ac16fb38cad2e56f2e46498eb11f8ee)) +* **mcp:** up tsconfig lib version to es2022 ([db052dc](https://github.com/hyperspell/node-sdk/commit/db052dcd1a475b54cb7c302df23bcfaa73c969d0)) + ## 0.30.0 (2026-01-16) Full Changelog: [v0.29.0...v0.30.0](https://github.com/hyperspell/node-sdk/compare/v0.29.0...v0.30.0) diff --git a/package.json b/package.json index 75244aaa..655d2f79 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hyperspell", - "version": "0.30.0", + "version": "0.31.0", "description": "The official TypeScript library for the Hyperspell API", "author": "Hyperspell ", "types": "dist/index.d.ts", diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json index 063c9c92..0e369567 100644 --- a/packages/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -1,6 +1,6 @@ { "name": "hyperspell-mcp", - "version": "0.30.0", + "version": "0.31.0", "description": "The official MCP Server for the Hyperspell API", "author": "Hyperspell ", "types": "dist/index.d.ts", diff --git a/packages/mcp-server/src/server.ts b/packages/mcp-server/src/server.ts index f872f74a..e97e47c6 100644 --- a/packages/mcp-server/src/server.ts +++ b/packages/mcp-server/src/server.ts @@ -57,7 +57,7 @@ export const newMcpServer = async () => new McpServer( { name: 'hyperspell_api', - version: '0.30.0', + version: '0.31.0', }, { instructions: await getInstructions(), diff --git a/src/version.ts b/src/version.ts index 91a9bb6d..b6314c28 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const VERSION = '0.30.0'; // x-release-please-version +export const VERSION = '0.31.0'; // x-release-please-version