- はじめに:「Grok を TypeScript から使う」に公式の道ができた
- 公開されたものを整理する
- これまで TypeScript から Grok を使う3つの道
- 新しくできること①:xAI 独自のツールを、型に守られて書ける
- 新しくできること②:「誰がツールを動かすか」を分けて受け取れる
- 新しくできること③:リモート MCP を、MCP クライアントなしで使える
- 新しくできること④:文章・画像・動画・音声・Batch を1つのクライアントで
- 新しくできること⑤:読み上げの「演出タグ」の間違いを、送る前に見つける
- 新しくできること⑥:生成途中の JSON を、閉じる前から画面に出す
- 新しくできること⑦:本番運用で困る細部に、最初から答えがある
- 実際に始める手順
- OpenAI・Anthropic の SDK と書き比べるときの観点
- トラブルシューティング
- まとめ
- 参考リソース
はじめに:「Grok を TypeScript から使う」に公式の道ができた
Grok(xAI の大規模言語モデル)を Node.js や Next.js のサーバーから呼びたいとき、これまでは少し遠回りが必要でした。xAI が公式に出している SDK(API を呼ぶための道具一式)は Python 版だけ で、TypeScript では OpenAI の SDK の接続先を差し替えるか、Vercel の AI SDK を使うか、fetch を自分で書くしかありませんでした。
2026年10月2日(米国時間)、xAI の GitHub 組織から公式の TypeScript SDK @xai-official/sdk が公開されました。ライセンスは Apache 2.0 で、ソースコードも読めます。ただし本人たちが「Experimental(実験版)」と明記しており、公開当日にクラス名が変わる破壊的変更も入りました。
この記事では、機能の一覧を並べるのではなく、「これまで何ができなかったのか」「公開されたことで何が新しくできるようになったのか」 に絞って整理します。

図1:上段がこれまでの3つの道、下段が公式 SDK。機能の数より「xAI 自身が API の形のまま型を付けて保守する」ことが大きな変化です
先に結論を3つにまとめます。
- 型が守ってくれる範囲が広がった:X 検索やリモート MCP など xAI 独自の道具を、書いている途中に TypeScript が検査してくれます。OpenAI SDK の流用では、ここが型エラーになっていました。
- xAI の API の形そのままに、依存パッケージなしで使える:文章・画像・動画・音声・ファイル・Batch・トークン数え・モデル価格の一覧までが、1つのクライアントにまとまっています。
- 実務の細かい困りごとに専用の道具がある:読み上げタグの打ち間違い検査、生成途中の JSON の表示、出力前だけの自動再試行など、自作すると手間がかかる部分が最初から入っています。
想定読者は、TypeScript で AI を使ったアプリやエージェント(自分で道具を使いながら作業を進める AI の仕組み)を作っている方、これから作ってみたい方です。
この記事の確認範囲
この記事の内容は、次の範囲で確認しています。
| 項目 | 内容 |
|---|---|
| 確認日 | 2026年10月4日 |
| SDK の版 | @xai-official/sdk 0.2.1(npm の latest) |
| 比較に使ったもの | openai 7.27.0、TypeScript 5.8.3(strict) |
| 実行環境 | MacBook(Apple シリコン)、Node.js 22.23.2 |
| 筆者が実際に試したこと | インストール、型検査(tsc)、API キーを使わない関数の実行、ダミーのキーでの1回の通信 |
| 試していないこと | xAI の API キーを使った生成(文章・画像・動画・音声)。該当するコードは型検査の通過までを確認しています |
本文中の「実測」「筆者の Mac で確認」は上の範囲での結果です。それ以外は公式リポジトリの README・CHANGELOG・ソースコードと docs.x.ai に基づく説明です。
公開されたものを整理する
まず、何がいつ公開されたのかを確認しておきます。

公式リポジトリ xai-org/xai-sdk-ts(2026年10月4日 筆者撮影)。説明文では提供元を「SpaceXAI」と表記しています
npm に公開された版は次のとおりです。わずか5時間ほどの間に3つの版が出ています。
| 版 | 公開日時(UTC) | 主な変更 |
|---|---|---|
| 0.1.0-rc.1 | 2026-09-29 | 公開候補版。ツール呼び出しのイベントを「自分で実行するもの」と「xAI が実行するもの」に分割 |
| 0.1.0 | 2026-10-02 16:58 | 最初の正式公開 |
| 0.2.0 | 2026-10-02 18:28 | 破壊的変更:クラス名を xAI から SpaceXAI に変更 |
| 0.2.1 | 2026-10-02 22:18 | 構造化出力の検証、生成途中の JSON 読み取り、読み上げタグの検査、出力前の再試行を追加 |
動かすための条件は、Node.js 22.13 以上 と ESM(import 文を使う形式)のプロジェクト です。実行時に必要な依存パッケージは 0 個 で、筆者の環境ではインストール後の容量が 676KB でした。
なお、筆者の Node.js 22.23.2 では CommonJS の require() からも読み込めました。これは Node.js 22.12 以降の「ESM を require できる」機能によるもので、SDK が公式にうたっている使い方ではありません。新しく作るなら ESM にしておくのが安全です。
これまで TypeScript から Grok を使う3つの道
「何が新しいか」を正しく言うには、これまでの状態を正確に押さえる必要があります。docs.x.ai のクイックスタートが案内していた JavaScript の方法は、OpenAI SDK と Vercel AI SDK の2つでした。これに fetch の自作を加えた3つが、これまでの選択肢です。
| 観点 | ① OpenAI SDK の流用 | ② Vercel AI SDK(@ai-sdk/xai) | ③ 公式 SDK(今回) |
|---|---|---|---|
| 作っているところ | OpenAI | Vercel | xAI |
| 追加で入るパッケージ | openai | ai と @ai-sdk/xai など | なし |
| X 検索などの独自ツール | 型エラーになる(後述) | 対応 | 対応 |
| 動画・音声・Batch | xAI の形式に合わないため自作 | 対応(Batch・リアルタイム音声は実験的) | 対応 |
| トークン数え・モデル価格の一覧・API キー照会 | 自作 | 筆者の調べでは見当たらない | 対応 |
| 型の作り方 | OpenAI の API に合わせた型 | AI SDK 共通の抽象化を通す | xAI の API 定義から生成 |
| 安定度 | 安定 | 安定(5.x) | 実験版(0.2.1) |
ここで注意したいのは、② の Vercel AI SDK は、すでに多くの機能に対応していた という点です。動画や音声も使えたので、「TypeScript では動画が作れなかった」と言うのは正しくありません。
では何が変わったのかというと、次の2点です。
- 一番手軽だった ① の道で起きていた「型の壁」がなくなった
- xAI 自身が、自社 API の形をそのまま TypeScript の型にして保守するようになった
② は複数の AI 会社を同じ書き方で使えるのが長所ですが、その分だけ「AI SDK 流の書き方」に合わせる必要があります。③ は xAI 専用の代わりに、xAI の API にある項目をそのままの名前で扱えます。どちらが優れているかではなく、用途で選ぶものです。
ここからは、③ で新しくできるようになったことを7つに分けて見ていきます。
新しくできること①:xAI 独自のツールを、型に守られて書ける
いちばん分かりやすい変化から始めます。xAI の API には、モデルが X(旧 Twitter)の投稿を検索する X 検索(x_search) というツールがあります。これを、これまでの書き方と公式 SDK で比べました。

図2:上段が OpenAI SDK を流用した場合、下段が公式 SDK。どちらも筆者の Mac で tsc を実行した結果です
OpenAI SDK(7.27.0)の型には、x_search というツールの種類が含まれていません。そのため、REST API としては受け付けられる内容でも、TypeScript の段階で TS2769(呼び出し方が合わない)エラーになります。as any などで型を外せば送れますが、その代わりに オプション名の綴り間違いも検査されなくなり、実行して API からエラーが返るまで気付けません。
公式 SDK では @xai-official/sdk/tools からヘルパー関数を読み込んで書きます。
import { SpaceXAI } from "@xai-official/sdk";
import { mcp, webSearch, xSearch } from "@xai-official/sdk/tools";
const client = new SpaceXAI();
const stream = await client.responses.create({
model: "grok-4.7",
input: "今週発表された TypeScript 向け AI SDK を、X と Web の両方で調べて3行でまとめて",
tools: [
// X の投稿検索(xAI 側で実行)。期間と対象アカウントを絞れる
xSearch({ allowed_x_handles: ["xai"], from_date: "2026-09-28" }),
// Web 検索(xAI 側で実行)
webSearch(),
// リモート MCP サーバー(xAI が接続して呼び出す)
mcp({ server_url: "https://mcp.deepwiki.com/mcp", server_label: "deepwiki" }),
],
stream: true,
});
const response = await stream
// xAI 側で実行されたツール呼び出し(自分では何もしなくてよい)
.on("server_tool_call", (call) => console.error(`[xAI が実行] ${call.type}`))
.on("citation", (c) => console.error(`[出典] ${JSON.stringify(c)}`))
.on("text", (text) => process.stdout.write(text))
.done();
console.log("\nサーバー側ツールの使用回数:", response.usage.num_server_side_tools_used);
このコードは型検査を通ることを確認済みです(API キーがないため実行結果は未確認)。ヘルパーが作るのはただのオブジェクトで、実際に xSearch({ allowed_x_handles: ["xai"], from_date: "2026-09-28" }) の中身を表示すると {"allowed_x_handles":["xai"],"from_date":"2026-09-28","type":"x_search"} でした。魔法のような変換はしておらず、API に送る形を、型付きで組み立ててくれる道具 だと考えると分かりやすいです。
型検査がどこまで間違いを拾うのかも試しました。次の画面は、わざと間違えたコードに tsc をかけた実際の出力です。
![ターミナルで npx tsc を実行した結果。読み上げタグの [laff] に対して did you mean [laugh] と提案するエラー、whisper タグの閉じ忘れのエラー、OpenAI SDK で x_search が型に合わないエラー、MCP の require_approval が存在しないというエラー、モデル名 grok-4.77 に対して grok-4.7 を提案するエラーが並んでいる](https://takuya-genai.com/wp-content/uploads/2026/10/xsdk-shot-tsc-errors.png)
筆者の Mac で tsc を実行した出力(見やすさのため、同じ理由のエラーの一部を省略して整形)
拾えたもの・拾えなかったものをまとめると、次のとおりです。
| わざと入れた間違い | 型検査の結果 |
|---|---|
モデル名を grok-4.77 と打ち間違える(KnownModelId 型を使った場合) | エラー。grok-4.7 を提案 |
MCP に未対応の require_approval を書く | エラー |
読み上げタグ [laugh] を [laff] と打ち間違える | エラー。[laugh] を提案(後述) |
| X 検索で「許可リスト」と「除外リスト」を同時に指定する | エラーにならない(README には同時に使えないと記載) |
すべてを型で防げるわけではありません。特に最後の項目は実行してから API に断られるはずなので、README の注意書きもあわせて読む必要があります。
新しくできること②:「誰がツールを動かすか」を分けて受け取れる
エージェントを作るときに混乱しやすいのが、ツールを 誰が実行するのか です。公式 SDK は、ここをイベント名ではっきり分けています。

図3:tools に並べたツールは、xAI 側で動くもの(右)と自分のアプリで動くもの(左)に分かれます
xAI 側で動くツール(Web 検索、X 検索、Python のコード実行、コレクション検索、画像生成、リモート MCP、ツール検索)は、結果が回答に組み込まれて返ってきます。こちらは何もしなくてよく、経過を見たければ server_tool_call を聞けば十分です。
一方、自分で書いた関数(function)と、モデルが書いたコマンドを自分の環境で実行する shell は、こちらで実行して結果を返す 必要があります。これらは client_tool_call として届きます。0.1.0 の直前(9月29日)にイベントがこの2つに分けられたことが、コミット履歴から分かります。
在庫を調べる関数を1つ持たせた例です。
import { type InputItem, type Tool, SpaceXAI } from "@xai-official/sdk";
const client = new SpaceXAI();
// 自分のアプリで実行する関数(在庫を調べる例)
const checkStock: Tool = {
type: "function",
name: "check_stock",
description: "商品コードから在庫数を返す",
parameters: {
type: "object",
properties: { sku: { type: "string" } },
required: ["sku"],
additionalProperties: false,
},
};
const input: Array<InputItem> = [{ role: "user", content: "A-100 と B-200 の在庫はある?" }];
const outputs: Array<InputItem> = [];
const stream = await client.responses.create({ model: "grok-4.7", input, tools: [checkStock], stream: true });
const first = await stream
// 自分で実行する必要がある呼び出しだけが届く
.on("client_tool_call", (call) => {
if (call.type !== "function_call" || call.name !== "check_stock") return;
const { sku } = JSON.parse(call.arguments) as { sku: string };
const stock = sku === "A-100" ? 12 : 0; // 本来はデータベースを引く
outputs.push({ type: "function_call_output", call_id: call.call_id, output: JSON.stringify({ sku, stock }) });
})
.done();
// モデルの出力と関数の結果を足して、もう一度呼ぶ
const answer = await client.responses.create({
model: "grok-4.7",
input: [...input, ...first.toInput(), ...outputs],
});
console.log(answer.toText());
first.toInput() は、モデルの出力(推論の暗号化された中身を含む)を次の入力にそのまま足せる形に変えるメソッドです。この SDK は 既定で会話を xAI 側に保存しない(store: false)ので、続きの会話はこの方法でつなぎます。
実際のアプリでは、モデルがツールを呼ばなくなるまでこれを繰り返す「ループ」にします。README には、回数の上限を付けたループの完成例が載っています。関数名や引数はモデルが作るものなので、許可した関数だけを呼び、引数を検査してから実行する ことが README でも強調されています。
新しくできること③:リモート MCP を、MCP クライアントなしで使える
MCP(Model Context Protocol)は、AI が外部のツールやデータにつながるための共通の約束ごとです。今回の SDK では、MCP がほかの組み込みツールと同じ並びで、最初から入っています。

図4:MCP サーバーへの接続と呼び出しは xAI が代わりに行います
ポイントは、自分のアプリに MCP クライアントを組み込まなくてよい ことです。mcp({ server_url, server_label }) を tools に1行足すと、xAI が MCP サーバーへ接続し、ツールの一覧を取り、モデルが選んだツールを呼んで、結果を回答に反映します。対象は Streaming HTTP か SSE で公開されている「リモート」の MCP サーバーです。手元の PC だけで動く MCP サーバーには、xAI からは届きません。
ツールが多い MCP サーバーでは、全ツールの説明文をプロンプトに入れると、それだけで入力が膨らみます。その対策として、defer_loading: true と toolSearch() を組み合わせると、必要になったツールの定義だけを後から読み込む ことができます。
まだ使えない設定もあります。実行前に人が承認する require_approval と、connector_id は未対応で、公式 SDK ではこれらを書くと型エラーになりました(先ほどの tsc 出力を参照)。データを書き換えるような MCP サーバーをつなぐときは、allowed_tools で使ってよいツールを絞っておくのが現実的です。
新しくできること④:文章・画像・動画・音声・Batch を1つのクライアントで
1つの SpaceXAI クライアントから、文章以外の機能も呼べます。ただし、すべてが同じ「呼んだら答えが返る」形ではありません。

図5:画像は即時、動画と Batch は受付番号を待つ形です
画像生成(images.generate())は、1回の呼び出しで最大10枚の結果が返ります。動画(videos.generate())は裏で生成される仕事なので、まず受付番号(request_id)だけが返り、videos.wait() で終わるまで待ちます。待ち合わせを SDK が用意しているので、自分で確認の繰り返し処理を書く必要がありません。
import { writeFile } from "node:fs/promises";
import { SpaceXAI } from "@xai-official/sdk";
const client = new SpaceXAI();
// 1. 生成を依頼する(すぐに受付番号だけが返る)
const { request_id } = await client.videos.generate({
model: "grok-imagine-video-1.5",
prompt: "雨上がりの路地を、紙の船がゆっくり流れていく",
duration: 8,
aspect_ratio: "16:9",
resolution: "720p",
});
// 2. 終わるまで待つ(既定は5秒ごとに確認、最長10分)
const result = await client.videos.wait(request_id);
if (result.status === "done" && result.video?.url) {
// URL は一時的なので、すぐ保存する
const mp4 = await fetch(result.video.url);
await writeFile("boat.mp4", Buffer.from(await mp4.arrayBuffer()));
console.log("費用(USD):", result.usage?.cost_usd);
} else {
console.error(result.status, result.error?.code, result.error?.message);
}
ここで気を付けたいのは、wait() をやめても動画の生成は止まらない ことです。README によると、待ち時間の上限に達して TimeoutError になっても生成は続き、終われば課金されます。動画を取り消す API はまだありません。時間切れになったら、同じ request_id でもう一度 wait() を呼べば続きから待てます。
画像や動画の元素材には、URL・base64・Files API の file_id・Blob を渡せます。ローカルのファイルは openAsBlob() で読み込めば、SDK が送れる形に変換してくれます。大きな動画は、先に client.files.upload()(50MB まで)で上げて file_id で渡すほうが効率的です。
大量の分類や要約には Batch API が向いています。依頼ごとに ID を付けて追加し、通常より割安な料金で、多くは24時間以内に処理されます。結果は順不同で届き、終わったものから読めます。
新しくできること⑤:読み上げの「演出タグ」の間違いを、送る前に見つける
xAI の読み上げ(voice.speak())では、文章の中に [pause](間を空ける)や [laugh](笑う)、<whisper>…</whisper>(ささやく)といった 演出タグ を書けます。困るのは、API がタグの間違いを報告せず、間違ったタグをそのまま読み上げてしまう ことです。
![上段は自分でコードに書いた文章の [laff] や閉じ忘れを TypeScript が指摘する流れ、下段はモデルが書いた文章を checkSpeechText で検査し stripInvalidSpeechTags で不明なタグだけ消してから voice.speak で読み上げる流れ](https://takuya-genai.com/wp-content/uploads/2026/10/xsdk-fig06-speech-tags.gif)
図6:自分で書く文章は型で、モデルが書く文章は実行時に検査します
0.2.1 では、この問題に2段構えの対策が入りました。1つ目は、コードに直接書いた文字列を TypeScript の型で検査する 仕組みです。筆者が "それは面白いですね [laff]" と書いたところ、tsc は「[laff] は不明なタグです。[laugh] のことですか?」と指摘しました。<whisper> の閉じ忘れも検出されています。
文字列の中身まで型で検査するのは、TypeScript の「テンプレートリテラル型」という仕組みを使ったものです。SDK より新しいタグを意図して使うときは、as UnsafeSpeechText を付けると検査を外せます。
2つ目は、モデルが書いた文章のように 実行時にしか中身が分からない文字列 への対策です。こちらは型では検査できないので、関数で検査します。
import { writeFile } from "node:fs/promises";
import { INLINE_SPEECH_TAGS, SpaceXAI, checkSpeechText, stripInvalidSpeechTags } from "@xai-official/sdk";
const client = new SpaceXAI();
// モデルに台本を書かせる。使ってよいタグを SDK の一覧から渡す
const tags = INLINE_SPEECH_TAGS.map((tag) => `[${tag}]`).join(", ");
const draft = await client.responses.create({
model: "grok-4.7",
input: `火山についての番組の最初の一言を英語で書いて。使えるタグ: ${tags}`,
});
const line = draft.toText();
// モデルが作ったタグ(例: [laff])を検査してログに残す
const problems = checkSpeechText(line);
if (problems.length > 0) console.warn(problems);
// 不明なタグだけ取り除いてから読み上げる
const speech = await client.voice.speak({ text: stripInvalidSpeechTags(line), language: "en", voice_id: "eve" });
await writeFile("opening.mp3", await speech.bytes());
検査用の関数は API キーなしで動くので、筆者の Mac で実際に試しました。ようこそ [laff] 今日は火山の話です。<whisper>実は内緒の話も</whisper> [long-pause] では始めましょう を渡すと、checkSpeechText() は Unknown speech tag [laff], did you mean [laugh]? を返しました。stripInvalidSpeechTags() を通すと、[laff] だけが消え、<whisper> と [long-pause] は残りました。
0.2.1 に同梱されていたタグは、文中に置くもの(pause・laugh・sigh など)が14種、文を囲むもの(whisper・slow・singing など)が12種でした。音声 API 自体には、文字起こし(voice.transcribe())や、120秒までの音声から声を作るカスタムボイス(Enterprise プラン向け)も含まれます。
新しくできること⑥:生成途中の JSON を、閉じる前から画面に出す
AI に JSON(決まった形のデータ)で答えさせると、後の処理は楽になります。ただ、JSON は最後の } が届くまで JSON.parse できないため、生成が終わるまで画面に何も出せない という弱点がありました。0.2.1 で追加された "json" イベントと parsePartialJson() は、この弱点を埋めるものです。

図7:筆者の Mac で parsePartialJson を実行した実際の結果。数値は書き終わるまで出てきません
parsePartialJson() は、途中で切れた JSON を受け取ると、開いたままの文字列・配列・オブジェクトを閉じて、その時点までの値を返します。筆者が台本の JSON を少しずつ渡して試したところ、次のように動きました。
- 文字列は途中でも返る:話者名が
"gu"まで届いた時点で{"speaker":"gu"}が返った - 数値は完成するまで返らない:
"minutes":1まで届いた時点ではminutes自体が含まれず、12が届いてから現れた - 壊れた JSON には
undefinedを返す:{"title" "oops"}はundefinedだった
数値を途中で出さないのは、12 になるはずの値を 1 と表示してしまう事故を防ぐためです。配列は「最後の要素以外は完成している」ので、1つ前の要素までを確定として表示する のがコツです。
import { z } from "zod";
import { SpaceXAI } from "@xai-official/sdk";
const client = new SpaceXAI();
// 台本の形を Zod で1回だけ定義し、JSON Schema にも変換して使う
const Script = z.object({
title: z.string(),
lines: z.array(z.object({ speaker: z.enum(["host", "guest"]), text: z.string() })),
});
type Line = z.infer<typeof Script>["lines"][number];
// 先頭の $schema 項目は API に不要なので外しておく
const { $schema, ...schema } = z.toJSONSchema(Script);
const stream = await client.responses.create({
model: "grok-4.7",
input: "空が青い理由を、2人の対話台本(4行)にして",
reasoning: { effort: "low" }, // 表示を早く始めたいので推論は軽めに
text: { format: { type: "json_schema", name: "script", schema } },
stream: true,
});
let shown = 0;
const response = await stream
.on("json", (partial) => {
// 最後の1行は書きかけの可能性があるので、その手前まで表示する
const lines = (partial as { lines?: Array<Line> }).lines ?? [];
while (shown < lines.length - 1) {
const l = lines[shown++]!;
console.log(`${l.speaker}: ${l.text}`);
}
})
.done();
// 完成したら Zod で検証し、型付きの値として受け取る
const script = response.toJson(Script);
for (const l of script.lines.slice(shown)) console.log(`${l.speaker}: ${l.text}`);
形の定義は Zod で1回だけ書き、z.toJSONSchema()(Zod 4 の機能)で API に渡す JSON Schema に変換しています。最後の response.toJson(Script) も 0.2.1 の追加機能です。Zod・Valibot・ArkType など「Standard Schema」に対応した検証ライブラリを渡すと、検証と型付けを1行で 行えます。形が合わなければ、どの項目が違うかを添えて例外を投げます。途中の値は検証されていないので、確定した値は必ずここで受け取ります。
台本の1行が完成するたびに voice.speak() へ渡せば、「モデルが次の行を書いている間に、前の行を読み上げる」こともできます。README でもこの使い方が紹介されています。
新しくできること⑦:本番運用で困る細部に、最初から答えがある
ここまでの機能に比べると地味ですが、実際にサービスで使うときに効いてくるのがこの部分です。
出力が始まる前の失敗だけ、自動でやり直す

図8:文章生成は「二重に出力しない」ことを優先して再試行が決まっています
文章生成の API で失敗を自動的にやり直すと、途中まで出ていた文章と、やり直した文章が二重になるおそれがあります。そのため、この SDK は 出力が始まった後の失敗は決してやり直しません。
0.2.1 で追加された retryBeforeOutput: true を付けると、モデルがまだ何も出力していない段階の失敗(5xx エラー、接続の切断、作成直後の 503 など)だけを自動でやり直します。やり直しのたびに新しい応答が作られ、失敗した回の入力トークンが課金される場合があるため、既定では off になっています。
import { APIError, RateLimitError, SpaceXAI } from "@xai-official/sdk";
const client = new SpaceXAI({
timeout: 60_000,
maxRetries: 2,
// 出力が始まる前の 5xx や切断なら、自動でやり直す(0.2.1 で追加。既定は off)
retryBeforeOutput: true,
});
try {
const res = await client.responses.create({ model: "grok-4.7", input: "こんにちは" });
console.log(res.toText(), res.http.requestId);
} catch (error) {
if (error instanceof RateLimitError) {
console.error("回数制限。少し待ってから再実行");
} else if (APIError.is(error)) {
// 問い合わせ時はこの2つの ID を添える
console.error(error.status, error.message, error.requestId, error.clientRequestId);
} else {
throw error;
}
}
もう1つ知っておきたいのは、stream: true を付けなくても responses.create() は裏でストリーミングしている ことです。最初に応答のヘッダーがすぐ返るので、長く考える推論モデルでも、Node.js 標準の fetch が持つ「ヘッダーを5分待つと切れる」制限に引っかかりにくくなっています。
何を送ったかを、秘密を伏せたまま確認できる
うまく動かないとき、実際に送ったリクエストを見たくなります。環境変数 XAI_DEBUG=1 を付けて実行すると、リクエストを cURL コマンドの形で表示してくれます。ブラウザで使えないようにする安全装置とあわせて、ダミーのキーで試しました。

ダミーのキーで1回だけ通信した実際の出力。本物の API キーは使っていません
表示を見ると、authorization ヘッダーは [REDACTED] と伏せ字になり、プロンプトが入る本文は「機密を含む可能性があるため省略」と出力されません。ログに残しても秘密が漏れにくい作りです。
x-client-request-id は SDK が毎回付ける ID で、やり直しの間も同じ値が使われます。API に届く前に失敗した場合でもエラーから取り出せるので、問い合わせのときに役立ちます。
この実験で1つ意外だったのは、誤った API キーのエラーが 401 ではなく 400 で返ってきた ことです。そのため SDK の AuthenticationError(認証エラー)ではなく、一般的な APIStatusError として届きました。キーの誤りを AuthenticationError だけで判定していると見逃すので、status と message もあわせて確認するのが安全です。
下段は、ブラウザ環境に見せかけてクライアントを作ろうとした結果です。SDK は既定で ブラウザや Worker での利用を拒否 します。秘密の API キーが利用者の手元に渡ってしまうのを防ぐためで、dangerouslyAllowBrowser: true を付けたときだけ作成できます。ブラウザから音声のリアルタイム接続をしたい場合は、サーバー側で短い有効期限の「client secret」を発行して渡す方法が用意されています。
送る前に数え、選ぶ前に価格を見る
トークン数を数える機能や、使えるモデルと価格の一覧、API キーの権限の確認も、同じクライアントから呼べます。
import { SpaceXAI } from "@xai-official/sdk";
const client = new SpaceXAI();
// 送る前にトークン数を数える
const { token_ids } = await client.tokenizer.encode({ model: "grok-4.7", text: "吾輩は猫である。名前はまだ無い。" });
console.log("トークン数:", token_ids.length);
// 使えるモデルと価格を一覧する(価格は 1億トークンあたりの米セント)
const models = await client.models.language.list();
for (const m of models.models) {
const perMillionUsd = (m.prompt_text_token_price ?? 0) / 10_000;
console.log(m.id, `入力 $${perMillionUsd}/100万トークン`);
}
// 今使っている API キーの権限を確認する
const key = await client.account.apiKey();
console.log(key.name, key.acls);
価格の単位が「1億トークンあたりの米セント」という独特なものなので、README の説明どおり 10,000 で割ると「100万トークンあたりのドル」になります。料金表をコードに直接書かず、API から取って計算できるのは、料金が変わったときに助かります。
実際に始める手順
ここまで読んで試したくなった方のために、最初の1回を動かすまでの流れをまとめます。

図9:サーバー側(Node.js)で使う前提の手順です
まず Node.js の版を確認し、新しいフォルダーを作って SDK を入れます。実験版で変更が続いているので、--save-exact で版を固定しておきます。
# Node.js が 22.13 以上か確認する
node -v
# 作業フォルダーを作り、ESM の設定にする
mkdir grok-ts-hello && cd grok-ts-hello
npm init -y
npm pkg set type=module
# 版を固定してインストールする(TypeScript で書く場合は tsx も入れる)
npm install --save-exact @xai-official/sdk@0.2.1
npm install --save-dev tsx typescript @types/node
# API キーを環境変数に置く(キーは https://console.x.ai で発行)
export XAI_API_KEY="<YOUR_API_KEY>"
次に hello.ts を作ります。
import { SpaceXAI } from "@xai-official/sdk";
// XAI_API_KEY 環境変数を自動で読む
const client = new SpaceXAI();
const response = await client.responses.create({
model: "grok-4.7",
input: "TypeScript の型推論を、料理にたとえて1文で説明して",
});
console.log(response.toText());
console.log("合計トークン:", response.usage.total_tokens);
# 実行する
npx tsx hello.ts
文章だけで動いたら、webSearch() → xSearch() → mcp() の順に1つずつツールを足していくと、問題が起きたときにどこが原因か切り分けやすくなります。モデル名は記事執筆時点で README が使っている grok-4.7 にしています。使えるモデルは API キーや時期で変わるので、client.models.list() で確認してください。
OpenAI・Anthropic の SDK と書き比べるときの観点
公式 SDK が出たことで、「同じ課題を各社の公式 SDK で作って比べる」ことがやりやすくなりました。xAI の SDK は、OpenAI の Responses API とよく似た形(responses.create、input、tools、function_call_output など)を採っているので、OpenAI からの移行や比較は特に楽です。
比べるときは、次の観点を表にしておくと違いが見えやすいです。
| 観点 | 見るところ |
|---|---|
| ツール定義 | 組み込みツールの種類と、オプションがどこまで型で検査されるか |
| 実行場所の区別 | サーバー側ツールと自前ツールが、ストリームでどう届くか |
| MCP | リモート MCP の設定方法、承認フローの有無、ツールの絞り方 |
| 会話の続け方 | 保存の既定値(xAI の SDK は保存しない)と、履歴の渡し方 |
| 失敗時の挙動 | 再試行の条件、二重出力を防ぐ仕組み、エラーの型 |
| 依存と動作環境 | 依存パッケージの数、Node.js の必要版、ブラウザでの扱い |
ここで挙げた xAI 側の挙動は、この記事で確認したとおりです。他社 SDK 側は版によって変わるので、比べるときはその時点の公式ドキュメントで確認してください。
トラブルシューティング
最後に、つまずきやすい点を、表示されるメッセージから引けるようにまとめます。

図10:表示されたメッセージから原因と対処を探します
| 表示されるもの | 原因 | 対処 |
|---|---|---|
@xai-official/sdk requires Node.js 22.13 or later | Node.js が古い | 22.13 以上に上げる |
does not provide an export named 'xAI' | 0.2.0 でクラス名が変わった | import { SpaceXAI } に直す。環境変数 XAI_API_KEY は変わっていない |
browser and worker use is disabled… | ブラウザや Worker で作ろうとした | サーバー側に移す。音声のリアルタイム接続は client secret を使う |
400 Incorrect API key provided. | API キーの誤り(筆者の環境では 401 ではなく 400 だった) | キーを確認する。エラー判定は status と message も見る |
TS2769 … "x_search" is not assignable | OpenAI SDK の型で xAI 独自ツールを書いた | 公式 SDK の xSearch() に置き換える |
TimeoutError(videos.wait) | 10分待って打ち切っただけ | 生成は続いている。同じ request_id で wait() を呼び直す |
それでも解決しないときは、XAI_DEBUG=1 で送った内容を確認し、エラーの requestId と clientRequestId を控えてから、GitHub の Issues で報告するのが近道です。現在、外部からのプルリクエストは受け付けていません。
まとめ
xAI の公式 TypeScript SDK で新しくできるようになったことを振り返ります。
- 独自ツールを型に守られて書ける:X 検索やリモート MCP を、OpenAI SDK の型エラーを回避せずに書け、モデル名や設定の誤りも書いている途中に分かる
- ツールの実行場所を分けて受け取れる:
server_tool_callとclient_tool_callで、自分が実行すべき呼び出しだけを処理できる - MCP クライアントなしでリモート MCP を使える:URL を1行書けば xAI が代わりに接続する
- 文章以外も1つのクライアントで扱える:画像・動画・音声・ファイル・Batch・トークン数え・価格一覧まで、依存パッケージなしでそろう
- 自作すると面倒な部分が最初から入っている:読み上げタグの検査、生成途中の JSON の表示、出力前だけの再試行、秘密を伏せたデバッグ表示
一方で、まだ 実験版 です。公開当日にクラス名が変わったように、1.0 までは変更が続く前提で、版を固定し、更新のたびに CHANGELOG を読むのがよいでしょう。Vercel AI SDK のように複数社を同じ書き方で扱いたい場合は、引き続きそちらが向いています。xAI の機能を余すところなく、xAI の API の形のまま使いたいときに、この公式 SDK が新しい選択肢になります。
次の一歩としては、手元のエージェントで使っているツールを1つ選び、OpenAI の Responses API と xAI の公式 SDK で同じ処理を書き比べてみるのがおすすめです。ツール定義の書きやすさと、型がどこまで間違いを拾ってくれるかの違いが、すぐに実感できるはずです。
参考リソース
- xai-org/xai-sdk-ts(GitHub):README、CHANGELOG、ソースコード
- @xai-official/sdk(npm)
- Rename the client to SpaceXAI and release 0.2.0(xai-org/xai-sdk-ts#6)
- docs.x.ai クイックスタート:これまでの JavaScript の案内(OpenAI SDK・AI SDK)
- docs.x.ai X Search
- xai-org/xai-sdk-python(GitHub):公式 Python SDK(gRPC ベース)
- AI SDK の xAI プロバイダー
- Standard Schema:toJson(schema) が対応する検証ライブラリの共通仕様

