こんにちは、ふくちと申します。
AWS re:Invent 2025にて、AWSが公開しているLLMアプリ開発フレームワーク「Strands Agents SDK」のTypeScript版対応開始が発表されました。
これまではPython版しか無かったのですが、ここに来てTypeScript(以降TSと記載)にも対応。私はDr.SwamiのKeynoteで聞いた瞬間にガッツポーズしてました。

TS対応の何が嬉しいかと言うと、基本TSだけでフロント・バック・インフラすべて書けるようになることかなと思っています。
フロントはReact/Next.js、バックはWebフレームワーク+Strands、インフラはAWS CDK、など。
単純に選択肢が増えるという意味で個人的にはとても嬉しいアップデートでした。
TS版のStrands Agentsとその対応状況
re:Invent中にStrands Agentsのセッションへ参加してきました。
そこで伺ったのは、TS版はPython版と比べてまだまだ制限があるということ。
具体的には以下のような内容でした。
現状の制限ポイント
- GitHub/npm上の preview release(v0.1)という位置づけ
- 今後のゴールはPython版と同等レベル(v1.0)
- 現時点では単一エージェントのみ対応
- Python 版にあるようなマルチエージェントパターン(Swarm/Graph/Agent as toolsなど)はまだ未対応
- Python版に比べて機能不足
- 組み込みツールの数が少ない(TypeScriptには少しだけある、今後拡充予定)
- Pythonで提供されている一部拡張機構や周辺機能がまだ未移植
- OpenTelemetry連携は優先対応予定
- 現状のTS版ではトレースやメトリクスをOTel経由で飛ばす統合はまだ入っていない
- モデルプロバイダ周りの差分
- Python版
- Bedrock / OpenAI / そのほか各種reasoning model
- LightLLMやカスタムゲートウェイ向けのmodel provider実装が既に存在
- TypeScript版
- Bedrock / OpenAI など代表的なモデルは使える前提
- LightLLM連携や一部カスタムモデルプロバイダの仕組みは少し時間がかかる予定
- Python版
まだまだ対応状況としては発展途上ですね。とはいえ対応してくれるだけありがたいので、私も積極的にコントリビュートしていこうと思います。
Strands Agents Quick Start
最小コードで書こうと思うと、4行です(多分)。相変わらず少ない行数で書けますね。
import { Agent } from '@strands-agents/sdk'
const agent = new Agent()
const result = await agent.invoke("What is AWS?")
console.log(result.lastMessage)
ただし実際の公式Quick Startはもう少し行数多いものが用意されています。
とはいえ10分もあれば終わってしまうようなものなので、まずはぜひこの辺りから触ってみてはいかがでしょうか。
AgentCore Runtimeへのデプロイも可能
Strands AgentsのドキュメントとしてAgentCore Runtimeへのデプロイ方法も記載があります。
これもそんなに難しいことはなく、1時間もあれば終わる内容だとは思うのですが、手動構築がちょっと多いです。
初めてやる方はこれで始めていただくのが良いと思いますが、実務でなかなか活かしづらいところもあると思います。
そこでここではCDKを用いて、TS版Strands AgentをAgentCore Runtimeへデプロイまで持って行く流れとコードをご紹介します。
前提:AgentCoreへのデプロイにはAgentCore SDKが必要
Strands AgentをAgentCore Runtimeへデプロイする際、そのままの形ではデプロイできません。
本当に簡単なラッパー関数を追加してあげる必要があります。
例えば、Pythonでは以下のようにBedrockAgentCoreAppや@app.entrypointのようなものを追加してあげる必要があります。
これはAgentCore SDKの機能の一部であり、AgentCore Runtime上でサーバーを動かすため必要なものになっています。
from bedrock_agentcore.runtime import BedrockAgentCoreApp
from strands import Agent
app = BedrockAgentCoreApp()
agent = Agent()
@app.entrypoint
def invoke(payload):
"""Process user input and return a response"""
user_message = payload.get("prompt", "Hello")
result = agent(user_message)
return {"result": result.message}
if __name__ == "__main__":
app.run()
しかしこれはStrands Agents SDKとAgentCore SDK、両方でPython版を使用しています。
当たり前ですが、Strands Agents SDK側はTS、AgentCore SDKはPython、のようなことはできません。
したがって、Strands Agents SDKとAgentCore SDKそれぞれでTS版を使う必要があります。
ところが、TS版AgentCore SDKにはまだRuntime統合の機能が存在していません。
This SDK currently provides Code Interpreter and Browser tools. Additional service integrations are on the roadmap.
このSDKは現在、コードインタープリターとブラウザツールを提供しています。今後、追加のサービス統合も予定されています。
ということで、代替手段を使う必要があるそうです。
ここはそのうちアップデートが入るかもしれませんが、ひとまず12/5時点での話ということで。
Express.jsを用いる
Strands Agentsの公式ドキュメント上には、Express.jsを用いる際のコードが用意されていました。
import { z } from 'zod'
import * as strands from '@strands-agents/sdk'
import express, { type Request, type Response } from 'express'
const PORT = process.env.PORT || 8080
// Define a custom tool(省略)
// Configure the agent with Amazon Bedrock
const agent = new strands.Agent({
model: new strands.BedrockModel({
region: 'ap-southeast-2', // Change to your preferred region
}),
tools: [calculatorTool],
})
const app = express()
// Health check endpoint (REQUIRED)
app.get('/ping', (_, res) =>
res.json({
status: 'Healthy',
time_of_last_update: Math.floor(Date.now() / 1000),
})
)
// Agent invocation endpoint (REQUIRED)
// AWS sends binary payload, so we use express.raw middleware
app.post('/invocations', express.raw({ type: '*/*' }), async (req, res) => {
try {
// Decode binary payload from AWS SDK
const prompt = new TextDecoder().decode(req.body)
// Invoke the agent
const response = await agent.invoke(prompt)
// Return response
return res.json({ response })
} catch (err) {
console.error('Error processing request:', err)
return res.status(500).json({ error: 'Internal server error' })
}
})
// Start server
app.listen(PORT, () => {
console.log(`🚀 AgentCore Runtime server listening on port ${PORT}`)
console.log(`📍 Endpoints:`)
console.log(` POST http://0.0.0.0:${PORT}/invocations`)
console.log(` GET http://0.0.0.0:${PORT}/ping`)
})
私自身Express.jsを使ったことがないので軽く調べていたところ、クラメソさんがわかりやすくまとめてくださっていました。
Node.js用の高速で最小限の機能を持つWebアプリケーションフレームワークです。
Webアプリケーションやモバイルアプリケーションのバックエンドを構築する際によく使用されます。
Express.jsは、ルーティング、ミドルウェア、テンプレートエンジンなどの機能を提供し、開発者がより簡単にWebアプリケーションを構築できるようサポートしてくれます。
そんなアプリケーションフレームワークがなぜここで必要なのでしょうか。
それを理解するためには、AgentCore RuntimeとStrands Agentsの関係性について理解する必要があります。
AgentCore Runtimeとしての要件
まずはエージェントが動くインフラ部分、AgentCore Runtimeが何者なのかを理解しましょう。
AgentCore Runtimeとは、エージェント(およびプログラム)を動かすための単なるコンテナです。ただしいくつか制約があります。
- コンテナ要件:0.0.0.0:8080 で待ち受けるARM64コンテナであること
- パス要件:最低でも次の2つのHTTPエンドポイント実装
- GET
/ping- ランタイムのヘルスチェック用
- {"status": "Healthy", "time_of_last_update": ...} などのJSONを返す
- POST
/invocations- エージェントへの本番リクエスト入口
- JSONやSSE(ストリーミング)でリクエスト/レスポンスをやり取り
- GET
つまり、「/invocationsにHTTPリクエスト投げたらエージェントが動き、/pingが200を返せば元気」ということだけが決まっており、裏側では何が動いていてもRuntimeとしては気にしないのです。
だからStrands Agentsでも、LangGraphでも、Mastraでも、AgentCore Runtimeとしてデプロイできる・動かせるのです。
これを裏付けているのが、先程のExpress.jsを用いた実装です。
// Health check endpoint (REQUIRED)
app.get('/ping', (_, res) =>
(中略)
)
// Agent invocation endpoint (REQUIRED)
app.post('/invocations', express.raw({ type: '*/*' }), async (req, res) => {
(中略)
})
というように、2つのエンドポイントを設定していました。これによって、Runtime上で動くための要件を満たせるようになるのです。
Strands Agents SDKなどのLLMアプリ開発フレームワーク
上記のRuntime上で動くエージェントはフレームワークを用いて実装されることが多いです。
ここではStrands Agentsとしていますが、お好きなフレームワークに置き換えてください。
これらのフレームワークは、一般的に以下の機能を提供しています。
- LLM呼び出し
- tool呼び出し
- Agent のループ・状態管理
つまり、エージェント本体を提供するものです。
その一方で、Strands Agents SDKだけでは先程のRuntime上で動くために必要な要件を満たすことができません。
HTTPサーバーを立てる、/invocationsに来たHTTPリクエストをagent.invoke(prompt)に橋渡しす、/pingを返す、といったサーバーとしての入り口は、Strands Agents SDKの責務ではないのです。
そこでよく用いられるのが、AgentCore SDKでした。
これの@app.entrypointというデコレーターは、裏側で上記のような要件を満たすための動きをしてくれます。
自身のStrands Agentにデコレーターを追加するだけで、ユーザーはAgentCore Runtimeの要件を特別意識しなくともデプロイまでできるようになっていたんです。
ところが前述のとおり、現状TS版AgentCore SDKではRuntimeとの統合処理がまだ実装されていません。
なので代替手段として、AgentCoreのHTTPエンドポイント制約 ↔ Strands Agentsの関数(エージェント)呼び出しをつなぐアダプタ的な存在が別途必要です。
そこでこの要件を満たすことができるExpress.jsが用いられている、ということです。
別にExpress.jsでなくても良い?
しかしここで2つの疑問が生まれるのでは無いでしょうか。
- わざわざExpress.jsを使うことなく、自前実装でHTTPサーバーを立てたりルーティングしたりしても良いのではないか?
- Express.jsに拘る必要はないのではないか?
1つ目は基本的にNo、2つ目はYesだと考えています。
1つ目は純粋に手間かつバグになる可能性があり、何よりそこをやりたいわけではないからです。
そもそもこのAgentCoreというサービスは、エージェントの本番導入における差別化されない重労働を無くすというのがコンセプトです。詳しくはAWS Summit NewYork 2025 Dr.SwamiのKeynoteをどうぞ。

そしてAgentCore SDKはHTTPサーバー実装などという手間から解放するためのものです。
それをわざわざ自前でやってしまうというのは、コンセプトにかなり反しています。
できるだけ楽をし、価値のあるエージェントの実装に注力していくべきだと私は考えています。
また、2つ目の質問はYesで、Express.jsに拘る必要はどこにもありません。
同じことができれば何でも良いです。
Honoを使ってみる
ということで今回はHonoを使ってみたいと思います。
Honoとは、開発者のブログ曰く
「Webアプリを作るためのフレームワーク」
- ルーター
- Request/Responseを扱うためのContext
- ミドルウェア
というちょっとした構成になっています。
「Ultrafast web framework」を謳っています。アイデンティティisそれです。
ということで、めちゃくちゃ高速なWebアプリ用フレームワークだそうです。
私は名前だけ知っていて、使ったことはありませんでした。
なので今回はこれでできるか試してみたいと思います。
ということでできたものがこちらになります。
import { Hono } from 'hono'
import { serve } from '@hono/node-server'
import * as strands from '@strands-agents/sdk'
import { httpRequest } from '@strands-agents/sdk/vended_tools/http_request'
const PORT = Number(process.env.PORT) || 8080
const MODEL_ID = process.env.MODEL_ID || 'jp.anthropic.claude-haiku-4-5-20251001-v1:0'
const bedrock = new strands.BedrockModel({ modelId: MODEL_ID })
const agent = new strands.Agent({
model: bedrock,
systemPrompt: "あなたはユーザーの質問に対してWeb検索を行うAIエージェントです。どんなURLを使ったのかも含めてレスポンスしてください。また、語尾に「Hono!」と付けてください。例えば「了解しましたHono!」のような形です。",
tools: [httpRequest]
})
const app = new Hono()
// ヘルスチェック
app.get('/ping', (c) =>
c.json({
status: 'Healthy',
time_of_last_update: Math.floor(Date.now() / 1000),
})
)
// エージェント呼び出し
app.post('/invocations', async (c) => {
try {
const body = await c.req.arrayBuffer()
const prompt = new TextDecoder().decode(body)
const response = await agent.invoke(prompt)
return c.json({ response })
} catch (err) {
console.error('Error processing request:', err)
return c.json({ error: 'Internal server error' }, 500)
}
})
// サーバー起動
serve({ fetch: app.fetch, port: PORT }, () => {
console.log(`🚀 AgentCore Runtime server listening on port ${PORT}`)
})
すごい!簡単!(ほぼExpress.jsのサンプルを置き換えただけ)
あとは以下GitHubリポジトリを用いて、AWS CDKでデプロイしてみました。
以下、CDKの実装です。興味ある方はどうぞ!
必要なモジュールをpackage.jsonに記載しています。
Stackファイルはこちら。Runtime L2 Constructと、deploy-time-build L3 Constructを用いています。
無事デプロイできたら、invokeしてみます。
$ npx ts-node script/invoke.ts "Honoって何?"
Response: {"response":{"type":"agentResult","stopReason":"endTurn","lastMessage":{"type":"message","role":"assistant","content":[{
"type":"textBlock",
"text":"了解しましたHono!\n\n
Honoについて調べたので、結果をお知らせします。使用したURLは以下の通りです:\n\n
**使用したURL:**\n- https://hono.dev\n\n
**Honoとは:**\n\n
Honoは、**Web Standards(ウェブ標準)に基づいた軽量で高速なWebアプリケーションフレームワーク**です。\n\n
**主な特徴:**\n\n
1. **🚀 超高速&軽量**\n
- RegExpRouterという高速ルーターを搭載\n
- hono/tinyプリセットは14kB以下\n
- Web標準APIのみを使用\n\n
2. **🌍 マルチランタイム対応**\n
- Cloudflare Workers、Fastly Compute、Deno、Bun、AWS Lambda、Node.jsなど多数のプラットフォームで動作\n
- 同じコードがすべてのプラットフォームで実行可能\n\n
3. **🔋 充実の機能**\n
- ビルトインミドルウェア\n
- カスタムミドルウェア対応\n
- サードパーティミドルウェア\n
- 各種ヘルパーツール\n\n
4. **😃 優れた開発体験**\n
- シンプルなAPI設計\n
- TypeScriptの完全サポート\n
- 直感的に使える\n\n
Honoは、エッジコンピューティングからトラディショナルなサーバーまで、
様々な環境で動作する柔軟なフレームワークとして設計されていますHono!"}]}}}
きちんと動いてくれましたね!
余談: Hono特有の書き方
ドキュメントに記載されていたExpress.jsと比較して、Honoで変更が必要だった箇所を以下にまとめてあります。ご興味ある方はどうぞ!
Contextオブジェクト c
Expressでは req(リクエスト) と res(レスポンス) の2つを使いましたが、Honoでは c (Context) に統合されているようです。
app.get('/example', (c) => {
// リクエスト情報の取得
const query = c.req.query('name') // クエリパラメータ
const param = c.req.param('id') // パスパラメータ
const body = await c.req.json() // JSON ボディ
// レスポンスの返却
return c.json({ message: 'Hello' }) // 200 OK
return c.json({ error: 'Not found' }, 404) // 404 エラー
})
returnが必須
Express.jsでは res.json() を呼ぶだけで良かったですが、Honoでは return が必須 だそうです。
// Express - return 不要
app.get('/ping', (req, res) => {
res.json({ status: 'ok' })
})
// Hono - return 必須
app.get('/ping', (c) => {
return c.json({ status: 'ok' })
})
Node.jsで動かすにはアダプターが必要
Hono本体はランタイム非依存のため、Node.jsで動かすには @hono/node-server というアダプターが必要です。
import { Hono } from 'hono'
import { serve } from '@hono/node-server'
const app = new Hono()
// ルート定義...
// Node.js 用のサーバー起動
serve({ fetch: app.fetch, port: 8080 })
ちなみに Bun なら、アダプター不要で動くそうです。
// Bun の場合 - これだけでOK!
export default app
もっと高いレベルのものを見たい場合は、以下を御覧ください。bunも使われているようです(私も勉強させていただきます)。
まとめ
多分そのうちTS版AgentCore SDKも充実してくると思うので、そうなったら不要になる実装例だとは思います。
とはいえこんな手段もあるんだな〜ということと、AgentCore Runtimeの裏側も少し把握しておくと良いのでは、と思っての記事でした!
また注意するべき点が1点。Strands Agents SDKもAgentCore SDKもそうですが、優先はPythonです。
TypeScript版の開発が続く保証も、機能がPython版と同等になる確証も、ありません。
なので引き続き私はTS版の情報発信も続けていきますし、可能であればコントリビュートもしていきたいと思います。
TS派の方も良ければ一緒に盛り上げていきましょう!🔥