【Node.js】 OpenAI Agents SDK の TypeScript版で MCPサーバーのツールを使う

## はじめに

今回の内容は、以下のお試しの続きになります。

●【Node.js】 OpenAI Agents SDK の TypeScript版で Hello World とサンプルを軽く試す Qiita
　https://qiita.com/youtoy/items/0972e1f4ba5213f76cb1

内容は、OpenAI が新しく出した「TypeScript版の OpenAI Agents SDK」のお試しの続きで、MCPサーバーとの組み合わせを軽く試します。

### 従来の SDK と MCPサーバーとの組み合わせの話

ちなみに、従来からあった Python版の OpenAI Agents SDK に関しては、以下の記事に書いた内容などを試したことがありました。

●MCP を OpenAI Agents SDK で軽く試してみた時のメモ： OpenAI の SDK で エージェント ＋ MCP - Qiita
　https://qiita.com/youtoy/items/13f5038ef45ce6763926
●AIエージェント で MCP： OpenAI Agents SDK を使ったお試し【その2】 - Qiita
　https://qiita.com/youtoy/items/5f4e477500c23c7dfd56


## 実際に試してみる

### 参考にする内容

今回の内容を、実際に試していきます。その際に参照する情報としては、以下を用います。

●openai-agents-js/examples/mcp at main · openai/openai-agents-js
　https://github.com/openai/openai-agents-js/tree/main/examples/mcp

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/bf6684a7-5f33-4ba1-9f44-872a38032dc9.png)

こちらの [filesystem-example.ts](https://github.com/openai/openai-agents-js/blob/main/examples/mcp/filesystem-example.ts) の内容は、こんな感じです。

```typescript
import { Agent, run, MCPServerStdio, withTrace } from '@openai/agents';
import * as path from 'node:path';

async function main() {
  const samplesDir = path.join(__dirname, 'sample_files');
  const mcpServer = new MCPServerStdio({
    name: 'Filesystem Server, via npx',
    fullCommand: `npx -y @modelcontextprotocol/server-filesystem ${samplesDir}`,
    // Or passing command and args
    // command: 'npx',
    // args: ['-y', '@modelcontextprotocol/server-filesystem', samplesDir],
  });

  await mcpServer.connect();

  try {
    await withTrace('MCP Filesystem Example', async () => {
      const agent = new Agent({
        name: 'Assistant',
        instructions:
          'Use the tools to read the filesystem and answer questions based on those files. If you are unable to find any files, you can say so instead of assuming they exist.',
        mcpServers: [mcpServer],
      });
      // List the files it can read
      let message = 'Read the files and list them.';
      console.log(`Running: ${message}`);
      let result = await run(agent, message);
      console.log(result.finalOutput ?? result.output ?? result);

      // Ask about books
      message = 'What is my #1 favorite book?';
      console.log(`\nRunning: ${message}\n`);
      result = await run(agent, message);
      console.log(result.finalOutput ?? result.output ?? result);

      // Ask a question that reads then reasons
      message =
        'Look at my favorite songs. Suggest one new song that I might like.';
      console.log(`\nRunning: ${message}\n`);
      result = await run(agent, message);
      console.log(result.finalOutput ?? result.output ?? result);
    });
  } finally {
    await mcpServer.close();
  }
}

main().catch((err) => {
  console.error(err);
  process.exit(1);
});
```


### 下準備

まずは、下準備をします。

#### パッケージのインストールと環境変数の設定

以下は、パッケージのインストールです。

```zsh
npm i @openai/agents
```

また、OPENAI_API_KEY という名前の環境変数に、OpenAI の APIキーをセットしておきます。


#### 処理対象のフォルダを作成

それと「sample_files」いう名前のフォルダを作成します。これは、今回のお試しで使う MCPサーバーの「Filesystem MCP Server」で、処理対象で指定するフォルダとなります。

●servers/src/filesystem at main · modelcontextprotocol/servers
　https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem


### コードの内容と実行結果

今回のお試し用のコードは、以下のようにしました。

```javascript
import { Agent, run, MCPServerStdio, withTrace } from "@openai/agents";
import * as path from "node:path";

async function main() {
  // カレントフォルダ直下の sample_files フォルダを処理対象にする
  const samplesDir = path.join(process.cwd(), "sample_files");
  const mcpServer = new MCPServerStdio({
    name: "Filesystem Server, via npx",
    fullCommand: `npx -y @modelcontextprotocol/server-filesystem ${samplesDir}`,
    // Or passing command and args
    // command: 'npx',
    // args: ['-y', '@modelcontextprotocol/server-filesystem', samplesDir],
  });

  await mcpServer.connect();

  try {
    await withTrace("MCP Filesystem Example", async () => {
      const agent = new Agent({
        name: "Assistant",
        instructions:
          "ツールを使ってファイルシステムを読み込み、ファイルに基づいて質問に回答してください。ファイルが見つからない場合は、存在すると仮定するのではなく、その旨を伝えてください。",
        mcpServers: [mcpServer],
      });
      let message = "ファイルを読み込んで一覧表示してください。";
      console.log(`実行中: ${message}`);
      let result = await run(agent, message);
      console.log(result.finalOutput ?? result.output ?? result);

      message = "私の一番好きな本は何ですか？";
      console.log(`\n実行中: ${message}\n`);
      result = await run(agent, message);
      console.log(result.finalOutput ?? result.output ?? result);

      message =
        "お気に入りの曲を確認し、私が好きそうな新しい曲を一つ提案してください。";
      console.log(`\n実行中: ${message}\n`);
      result = await run(agent, message);
      console.log(result.finalOutput ?? result.output ?? result);
    });
  } finally {
    await mcpServer.close();
  }
}

main().catch((err) => {
  console.error(err);
  process.exit(1);
});
```

#### 実行結果など

そして、「sample_files」フォルダを空のままで実行してみます。そうすると、「ファイルが見つからない場合は、存在すると仮定するのではなく、その旨を伝えてください。」という指定に従ったと思われる返答が返ってきました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/d223ae23-fc84-467d-af20-ea8add153bd9.png)

ちなみにフォルダ指定の部分は、[ちょっと前にやったお試しと同じような実装にしています（カレントフォルダを基点にしたサブフォルダの指定）](https://qiita.com/youtoy/items/1e11af2e910e41890274)。

公式サンプルだと、「sample_files」フォルダ以下には 2つのテキストファイルがあり、それを使った回答が返されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/8d5f675f-1ea6-40f0-adac-fdb1ba79b723.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/754e02c2-8f88-43e3-9271-0757e48ff9be.png)



### コードを書きかえてさらに試す

先ほどの内容を書きかえて、さらに試してみます。内容は以下としました。

```javascript
import { Agent, run, MCPServerStdio, withTrace } from "@openai/agents";
import * as path from "node:path";

async function main() {
  // カレントフォルダ直下の sample_files フォルダを処理対象にする
  const samplesDir = path.join(process.cwd(), "sample_files");
  const mcpServer = new MCPServerStdio({
    name: "Filesystem Server, via npx",
    fullCommand: `npx -y @modelcontextprotocol/server-filesystem ${samplesDir}`,
  });

  await mcpServer.connect();

  try {
    await withTrace("MCP Filesystem Example", async () => {
      const agent = new Agent({
        name: "Assistant",
        instructions:
          "ツールを使ってファイルシステムを読み込み、指示に従って処理をしてください。",
        mcpServers: [mcpServer],
      });
      let message =
        "ai.txtというファイルを作成し、その中にOpenAIのモデルのリストを書いてください。";
      console.log(`実行中: ${message}`);
      let result = await run(agent, message);
      console.log(result.finalOutput ?? result.output ?? result);

      message =
        "処理対象のフォルダの中にあるファイルをあらためて確認してください。その中にai.txtというファイルの内容があったら、その中にあるリストの1つの項目を選んでください。ai_summary.txtというファイルを新規に作って、そこに先ほどリストから選んだ項目についての説明の概要を書いてください。";
      console.log(`\n実行中: ${message}\n`);
      result = await run(agent, message);
      console.log(result.finalOutput ?? result.output ?? result);

      message =
        "処理対象のフォルダの中にあるファイルをあらためて確認してください。そこにai_summary.txtというファイルがあったら、ファイルに書かれた内容を8歳に分かる説明に変えて出力してください。";
      console.log(`\n実行中: ${message}\n`);
      result = await run(agent, message);
      console.log(result.finalOutput ?? result.output ?? result);
    });
  } finally {
    await mcpServer.close();
  }
}

main().catch((err) => {
  console.error(err);
  process.exit(1);
});
```

#### 実行結果

実行結果は、以下のとおりです。内容を見ると、想定どおりの処理をしてくれていそうです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/0fad655a-ac46-44bc-93a1-3e496dfcaf72.png)

実際に、指定したフォルダの中を見て確認します。まずは、以下のようにテキストファイルは作成されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/b810d8ad-f25c-4a19-aa35-8d1e16e6d977.png)

さらにテキストファイルの中身を見てみると、先ほど処理を実行した際に、返答の中で書かれていた内容と同じものが書かれていることが分かります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/9c05eb92-7f0e-4264-a4e1-a185cb29ff4a.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/37c2a65f-979c-45f5-851e-6181c6df1670.png)


#### 補足

先ほどのコード内のプロンプトで「処理対象のフォルダの中にあるファイルをあらためて確認してください。そこに・・・というファイルがあったら、・・・」という書き方をしていた理由を補足します。

当初このような書き方をせず、ファイル名を単純に指定して作業するよう指示を書いていましたが、それだと以下のようにファイルを見つけられないという返答が返ってきていました（※ フォルダ内を確認すると、指定したファイルが生成されていた状況でした）。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/5c593a9f-a839-4e97-a3a3-c9a0ee44b536.png)

それで試しに、都度フォルダ内を確認するような指示にしてみたところ、うまく動作したのでこのプロンプトを使う形にしました。ただ試行回数が少ないので、偶然こうなっていたのか、明確にフォルダ内を読み直す指示を冒頭なり途中のプロンプトなりに含める必要があるのかは、切り分けられてはいません。


## おわりに

OpenAI が新しく出した「TypeScript版の OpenAI Agents SDK」のお試しの続きとして、MCPサーバーとの組み合わせを試してみました。

また別途、試す内容を見つけて、そして試してみた結果を記事に記録として残す、といったことをやれればと思います。


### 余談

最近の話題で、OpenAI・ChatGPT関連では、このあたりも試したいところです！

#### ChatGPT の中での Codex（Proプランだけでなく Plusプランでも使えるようになったもの）

https://x.com/youtoy/status/1930281267582644432

#### オーディオ系のモデルの最新版

https://x.com/youtoy/status/1930623995189301525

●Model： GPT-4o Audio の gpt-4o-audio-preview-2025-06-03
　https://platform.openai.com/docs/models/gpt-4o-audio-preview

●Model：GPT-4o Realtime の gpt-4o-realtime-preview-2025-06-03
　https://platform.openai.com/docs/models/gpt-4o-realtime-preview

●Updates to building agents: Typescript Agents SDK, a new RealtimeAgent feature for voice agents, Traces for Realtime, and speech-to-speech improvements - Announcements - OpenAI Developer Community
　https://community.openai.com/t/updates-to-building-agents-typescript-agents-sdk-a-new-realtimeagent-feature-for-voice-agents-traces-for-realtime-and-speech-to-speech-improvements/1277152