プレイドインターン体験記:Agent Studio開発を通じて、実装とプロダクトに向き合った日々

こんにちは。9月から10月末までの2ヶ月間、KARTE AI Teamでエンジニアとしてインターンに参加した沢辺空です。

参加のきっかけは、サポーターズを通じて知り合った先輩から「プレイドのインターンが一番印象に残っている」という話を聞いたことでした。そこからプレイドについて調べていくうちにプロダクトや開発環境に興味を惹かれ、インターンへの参加を決めました。

インターンでは、プレイドが開発を進めるAIエージェント構築基盤「Agent Studio」のUI改善から、会話履歴の取得処理、AI SDKの更新、MCP Apps対応に向けた開発まで、さまざまなタスクに取り組みました。また、単なる機能実装にとどまらず、社内でのユースケース検討やビジネスサイドのメンバーとの対話、Vue Fes Japan 2026への参加など、プロダクトの価値や技術への理解を多角的に深める機会にも恵まれました。
この記事では、担当した開発と、その中で考えたことを振り返ります!!

開発に参加したAgent Studioについて

Agent Studioは、プレイドグループ全体のデータや機能、外部サービスと連携し、業務に合わせたAIエージェントを作成・活用するための基盤です。ユーザーは、エージェントに役割や利用できるツールを設定し、チャットで動作を確かめながら、自分たちの業務に合わせて調整できます。

私が担当した範囲では、フロントエンドにReact・TypeScript、エージェントの実行にMastraやAI SDK、データの保存にMongoDBを使っていました。

主に取り組んだのは、次のような開発です。

  • 画像のドラッグ&ドロップや、ツール一覧などのUI改善
  • エージェント・MCPのバージョン状態の表示と、バージョン作成時プレビューの改善
  • 3つの会話履歴への無限スクロール統合
  • MCP Apps対応に向けた調査・実装
  • AI SDKの更新、CIや評価テストの改善

ここからは、特に印象に残っている3つの取り組みを紹介します。

バージョン作成時のエラーを作成前に伝える実装

概要

Agent Studioでは、ある時点のエージェントの設定を「バージョン」として保存します。今回は、バージョンを作成する前に、利用するMCPサーバのバージョンの状態を確認でき、作成できない場合は理由と解決方法が分かるようにする改善を担当しました。

背景

バージョン作成時には、エージェントの設定をスナップショットとして保存し、利用するMCPサーバのバージョンも記録します。そのため、MCPサーバ側にも作成済みのバージョンが必要です。なお、MCPサーバの利用権限はバージョンとは別に管理され、実行時に確認されます。 しかし、もともとの確認画面では、MCPサーバの名前は確認できても、バージョンの有無までは分かりませんでした。そのため、MCPサーバのバージョンが未作成の場合、ユーザーは作成ボタンを押した後に初めて400エラーを受け取り、原因を確認する必要がありました。

バージョン管理方法の整理

エージェントとMCPサーバでバージョンの管理方法が異なる点です。それぞれの状態が伝わるよう、表示を整理しました。

  • エージェント: 編集中の下書きと、利用する版として選ばれているバージョンを区別し、「下書き」「vN 公開中」と表示しました。
  • MCPサーバ: エージェントのように利用中のバージョンを選ぶ仕組みがないため、未作成なら「バージョンなし」、作成済みなら最新のバージョン番号を「vN」と表示しました。

また、他のエージェントからの呼び出しを受け付ける提供設定は、バージョンの有無とは別の状態です。バージョンが存在していても提供がオフになっていることがあるため、両者を別々に表示し、ユーザーが混同しないようにしました。

バージョン作成時プレビューで、依存MCPの「バージョンなし」と、作成済みの「v2」を比較する画像。

表示を支えるデータ取得と認可

Agent Studioでは、設定の所有関係やACL(アクセス制御リスト)、ワークスペースの管理権限に応じて、閲覧・編集・実行できる範囲を管理しています。権限の区分には、次のものがあります。

  • admin: 対象ワークスペースのAgent Studioを管理する利用者。
  • owner: エージェントやMCPサーバの設定の所有者。
  • editor: 共有されたエージェントを閲覧・編集・実行できる利用者。MCPサーバには付与しません。
  • invoker: 共有されたエージェントやMCPサーバを閲覧・実行できる利用者。

MCP設定の閲覧可否は、対象の設定ごとに次のように決まります。

対象のMCP設定を閲覧できる条件MCP設定の閲覧
その設定のownerである◯
その設定をinvokerとして共有されている◯
対象ワークスペースのadminである◯
上記に該当しない利用者×

今回追加したMCPサーバのバージョン情報も、この閲覧範囲に揃えて取得するようにしました。同じプロジェクトに参加していても、すべての設定やバージョン情報を閲覧できるわけではありません。

また、MCPサーバごとに最新バージョンを問い合わせると、表示件数に比例してDBアクセスが増えてしまいます。そこで、複数のMCPサーバの最新バージョンを一度のDB問い合わせでまとめて取得し、状態表示の追加による負荷の増加を抑えました。

さらにバージョン作成後の表示更新にも対応しました。作成が完了しているのに「バージョンなし」が残ると、ユーザーが作成できる状態になったか判断できません。そのため、作成後は一覧と同じプロジェクト内のエージェントの確認画面で情報を再取得し、新しい状態を確認して次の操作へ進めるようにしました。

状態を見せるところから、次の操作を案内するところへ

続く改善では、利用するMCPのバージョンがまだ作られていない場合に、バージョン作成ボタンを無効にし、先にそのバージョンを作成するよう案内をする実装をしました。状態を表示するだけでなく、公開するために何をすればよいかまで伝えるためです。 一方、MCPサーバーの情報を取得できなかったときは、「バージョンがない」と判断しないようにしました。画面を開いている利用者に、その設定を閲覧する権限がなく、情報が返ってこない場合もあるためです。取得した情報からバージョンが未作成だと確認できた場合は画面で案内し、判断に必要な情報が足りない場合は、公開操作を処理するAgent Studioのサーバー側での確認に委ねました。

タイミングによる不整合にも注意が必要です。MCP Aの状態を取得中に設定をMCP Bへ変えたとき、後から返ってきたAの結果で、Bを利用する設定の公開可否を判断してはいけません。この問題には、依存設定をTanStack QueryのQuery keyに含め、変更前後の取得結果を区別する方法で対応しました。

画面上では小さなラベルやボタンの変更でも、その背景には、データの取得方法、権限、公開条件、情報の更新タイミングが関わっています。既存の認可と公開処理を引き継ぎながら、利用者が状態を理解し、次の操作を判断できるようにする開発でした。

3つの会話履歴を無限スクロールの統合

概要

Agent Studioの会話履歴には、ユーザーとエージェントのやり取りに加え、エージェントが呼び出したツールの入力や実行結果も記録されています。この履歴は、主に次の3つの画面で確認できます。

  • 会話画面: エージェントを作成・試行する画面
  • 共有画面: 会話を他の利用者に見せる画面
  • 会話監視画面: ワークスペースの管理者が会話を確認する画面

今回は、この3画面で異なっていた履歴の取得方法を、最新の履歴から表示し、上にスクロールすると過去の履歴を追加取得する無限スクロール方式に統一しました。

背景

今までは、画面ごとに履歴の取得方法が異なっていました。 通常の会話画面では一定件数より古い履歴に遡れず、共有画面や会話監視画面では、最初に全履歴を読み込む構成になっていました。 長い会話でも必要な履歴を読み進められるようにするため、3つの画面に無限スクロールを導入しました。ただし、取得処理を揃えるには、それぞれの画面が異なる仕様になっている理由を確認する必要がありました。

既存の仕様と、監査で必要な要件を確認する大切さ

3つの画面の取得方法を揃えるにあたり、まず、以前この機能を実装したエンジニアの方へ、会話共有と会話監視で仕様が異なる理由を確認しました。

会話共有には共有できる件数に上限がある一方、会話監視には、監査のために対象の会話を最後まで確認できる必要がありました。ただし、必要なのは「全履歴を閲覧できること」であり、「一度の通信ですべて取得すること」までは要件ではないと分かりました。

続いて、取得方法を変えた際の影響を判断するため、PdMの方に監査時の具体的な使われ方を相談しました。そこで重要だと確認できたのは、調べたい発言やツール実行と、その前後のやり取りを確認すること、そして確認した内容を証跡として提出できることでした。

この相談では、エンジニア以外の方への伝え方にも難しさがありました。最初は「一括取得する必要があるか」「スクロールに応じた取得でよいか」と、実装方法を中心に質問していました。しかし、どちらの方式でも、利用者がスクロールして履歴を読む操作は変わりません。仕組みの違いだけでは、判断に必要な情報を十分に伝えられていませんでした。

そこで、「まだ読み込んでいない過去の発言も検索できるか」という、利用者への影響に置き換えて整理しました。段階的な読み込みでも全履歴へ遡れますが、ブラウザ内検索の対象になるのは読み込み済みの範囲だけです。単に全履歴を閲覧できるかだけでなく、必要な箇所をどう見つけるかも考える必要がありました。

最終的には、全履歴へ遡れることを維持しながら、必要に応じて追加取得する方式を採用しました。全履歴を対象とした検索やダウンロードは、今回の実装には含めず、別の課題として切り分けています。

この経験から、以前の実装者やPdMの方とコミュニケーションを取り、仕様の背景や利用目的を確認しながら、実装を前に進めることの大切さを学びました。特に、エンジニア以外の方への相談では、内部の仕組みだけでなく、利用者にとって何が変わり、どのような制約があるのかを具体的に伝える必要があると実感しました。

生成中の回答と、読んでいる位置を保つ実装

各画面の利用目的と制約を整理した後は、決めた取得方式を画面に組み込んでいきました。その際に注意したのが、過去の履歴を追加しても、生成中の回答や読んでいる位置を崩さないことです。

会話画面では、過去の履歴を読み込んでいる間にも、新しい回答が生成されます。取得した履歴には、保存された時点の回答途中のデータが含まれることがあります。その内容で画面を上書きすると、生成が進んでいた回答が、以前の状態に戻ったように見えてしまいます。

そこで、現在表示している内容を優先し、まだ表示していない過去のメッセージだけを先頭に追加するようにしました。

export function prependHistory<T extends { id: string }>(history: readonly T[], current: T[]): T[] {
  const ids = new Set(current.map(message => message.id));
  const older = history.filter(message => !ids.has(message.id));
  return older.length ? [...older, ...current] : current;
}

現在表示しているメッセージのIDを基準に、取得した履歴から重複を除いています。同じIDのメッセージが返ってきても、画面上の生成中の回答は置き換えません。

一方、保存済みの履歴同士をまとめる場合は、より新しい履歴を含む取得分を優先しました。同じメッセージが重複していても、生成中の回答に履歴を追加する場合と、保存済みの履歴同士をまとめる場合とで、どちらの内容を残すかを分けています。

スクロール位置の維持にも対応しました。過去のメッセージを画面の上側に追加すると、読んでいた箇所の位置がずれてしまうことがあります。そこで、追加前に見えていたメッセージと位置を記録し、追加後にスクロール位置を補正することで、同じ箇所を読み続けられるようにしました。

関心事でPRを分けると上手くいく

3つの画面の無限スクロールの統合には実装の進め方にも試行錯誤がありました。最初は、1つのIssueに対して1つのPRを作るつもりで、3つの画面の変更をまとめて進めていました。しかし、差分が大きくなるにつれて変更点の整理が難しくなり、作業が行き詰まってしまいました。

そこで、会話画面・共有画面・会話監視画面ごとにPRを分ける方針に切り替えました。まず各画面に無限スクロールを導入し、その後、共通する処理をまとめる変更を別のPRとして作成しました。

このように分けることで、一度に考える範囲が小さくなり、画面ごとの動作を確認しながら作業を進められるようになりました。レビューでも、各画面の動作変更と共通化を分けて確認できるため、変更の意図が伝わりやすいPRにできました。

この経験から、Issueの単位にそのまま合わせるのではなく、変更の目的や確認する範囲に応じてPRを分けることが、開発を進めるうえでも、レビューを受けるうえでも大切だと学びました。

MCP Apps 対応

概要

MCP Appsについて、MCPの公式ブログでは、次のように説明されています。

MCP Apps let tools return rich, interactive interfaces instead of plain text.

つまり、AIとの会話の中に、利用者が直接操作できる画面を表示する仕組みです。例えば、取得したデータをグラフで確認する、表示条件を絞り込む、フォームで設定を入力するといった操作を、チャット内で行えます。

今回は、Agent StudioがMCP Appsを扱うためのクライアント側の対応として、関連ライブラリの更新、画面情報の保持、画面用HTMLの取得処理を担当しました。

背景

MCP Appsは、クライアント側とサーバ側が連携して動作します。

  • クライアント側: HTMLを取得して画面を表示し、画面の操作とツールの実行をつなぎます。
  • サーバ側: ツールと、そのツールに対応する画面のHTMLを提供します。

Agent Studioには、MCPツールへの接続や実行、利用権限の確認といった仕組みがすでにありました。今回の対応では、その仕組みに画面情報の取得をどう組み合わせるかを考える必要がありました。

MCP Apps 対応に向けたライブラリの更新

Agent Studioでは、エージェントの実行を担うMastraと、AIの応答を会話画面で扱うAI SDKを組み合わせています。MCP Appsへの対応に向けて、AI SDKをv6からv7へ更新し、Mastraとの連携パッケージも更新しました。

当初は、Mastraとの連携パッケージだけを先に更新する予定でした。しかし、レビューを通じて、新しい連携パッケージが渡すGeminiのツール実行結果には、AI SDK v6が受け付けない追加情報が含まれることが分かりました。この組み合わせでは、ツールの実行後、画面が結果を受け取る段階でエラーになります。そこで、関連ライブラリを同時に更新し、会話データをv7の形式で受け渡すようにしました。

また、別のエージェントに処理を依頼した際の表示にも注意が必要でした。例えば、依頼先のエージェントがツールでデータを取得している間、会話画面には、そのエージェントの途中の回答や、ツールの入力・実行結果も表示されます。しかし、新しい連携パッケージでは、これらの進捗情報を送る設定が既定で無効になっていました。

そこで、実行中のデータを画面向けに変換する処理に、「依頼先の進捗情報も含める」という設定を明示しました。これにより、最終的な回答を待つ間にも、依頼先がどのような処理を進めているか確認できる表示を維持しました。

画面を表示するまでの経路を整理する

MCP Appsでは、ツールのメタデータに画面の参照先が含まれます。クライアント側は、その参照先を使ってMCPサーバへresources/readを呼び出し、画面のHTMLを取得します。

Agent Studioへ組み込むには、この情報をツールの検出・保存・API応答の経路で保持し、必要なときに取得できるようにする必要がありました。そこで、画面情報を保持する処理と、UI Resourceを取得するAPIを別のPRに分けて実装しました。

画面の取得にも、既存の認可を適用する

画面用HTMLを取得するAPIにも、既存のツール実行と同じ認可を適用しました。 クライアントからは、エージェント設定とMCPサーバーのID、ツール名を受け取ります。サーバー側でアクセス権限や公開設定、承認済みのツール定義を確認したうえで、登録済みの設定から取得先を決定します。

この構成により、取得対象を、利用者が使えるツールに紐づく画面に限定しています。 また、承認後に画面の参照先が変わったことも検知する必要があります。既存の仕組みでは、ツール定義から計算したハッシュ値を「承認署名」として使い、承認時から定義が変わっていないかを確認しています。この計算対象に、画面の参照先URIも加えました。

const payload = canonicalJsonStringify({
  description: tool.description ?? null,
  inputSchema: tool.inputSchema ?? null,
  ...(tool.ui ? { ui: { resourceUri: tool.ui.resourceUri } } : {}),
});
return createHash('sha256').update(payload, 'utf8').digest('hex');

画面情報がある場合だけURIを加えることで、画面を持たない既存ツールの署名を維持しています。参照先の追加・変更・削除があった場合には、変更後の設定を改めて承認してもらうようにしました。 画面情報について、承認署名で検知できるのは参照先URIの変更から返されるHTMLの内容を保証するものではありません。そのため、取得したHTMLを管理画面から隔離して動かすためのsandboxも、別の部品として実装しました。

実装の外でも、プロダクトへの理解を深める

インターンでは、実装だけでなく、ユースケースを考えたり、ビジネスサイドの方と話したりする機会もありました。

エージェントがどのような業務で使われ、どこで人の確認や判断が必要になるのかを知ることは、画面や機能を考えることにもつながります。会話監視の取得要件を確認したことも、実際の利用目的を理解してから実装方針を決める経験の一つでした。

また、社内のデザイン会にも参加し、Agent StudioのUI/UXについて議論したり、プロトタイプを作ってみたりしました。利用者が何を見て、どのように操作するのかを考えながら、アイデアを画面の形にする経験ができました。機能をどう実装するかに加えて、どのような体験として届けるかを考える機会にもなりました。

ユースケースについて話すことと、プロトタイプを作ることは、取り組み方こそ異なりますが、どちらも利用者の立場からプロダクトを考えることにつながっていました。実装の担当箇所から少し視野を広げて、機能の背景や使われ方を知る機会になったと思います。

さらにVue Fes Japan 2026への参加など、普段のタスクから少し離れて技術に触れる機会もありました。実務で開発しながら、社内外の人や技術に触れられたことも、今回のインターンで得られた経験です。 もともとプロダクトに興味がありましたが、こうした経験を通して、よりプロダクトに責任を持ちたいと思うようになりました。実装する機能の背景や使われ方を理解し、何をつくるか、どのように届けるかにも関われるようになりたいと考えています。

おわりに

2ヶ月間、Agent Studioの開発を通じて、技術面でもプロダクトへの向き合い方の面でも、多くの学びを得ることができました。この経験を、これからの開発にもつなげていきたいと思います。
KARTE AI Teamの皆さんをはじめ、相談やレビューに付き合ってくださった皆さん、本当にありがとうございました。

この記事が、プレイドのインターンに興味を持っている方の参考になれば嬉しいです。