SaaS公式MCPサーバーをリリースして得た学び

383 Views

May 14, 25

スライド概要

AI Developer Meetup in Tokyo 2025/05/14

profile-image

Frontend engineer @lapras_inc / TypeScript / Vue.js / Firebase / 元消防士

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

ダウンロード

関連スライド

各ページのテキスト
1.

SaaS公式MCPサーバーをリリースして得た学び 2025/05/14 AI Developer Meetup @KawamataRyo

2.

@KawamataRyo LAPRAS株式会社 元消防士 2児の父 懸垂, 個人開発

3.

会社で開発・公開したMCP Serverの紹介と、 その開発時に意識していたこと、 リリース後の失敗と学びをお話しします

4.

何を開発した?

5.

エンジニア向けポートフォリオ・転職サービス LAPRAS公式のMCPサーバー https://github.com/lapras-inc/lapras-mcp-server lapras-inc/lapras-mcp-server lapras.com 公式MCP Server 3 Contributors 0 Issues 63 Stars 0 Forks github.com GitHub - lapras-inc/lapras-mcp-server: lapras.com 公式MCP Server

6.

エンジニア向けポートフォリオ・転職サービス LAPRAS公式のMCPサーバー https://github.com/lapras-inc/lapras-mcp-server lapras-inc/lapras-mcp-server lapras.com 公式MCP Server 3 Contributors 0 Issues 63 Stars 0 Forks github.com GitHub - lapras-inc/lapras-mcp-server: lapras.com 公式MCP Server LAPRASとは?

7.

L A P R A S Portfolio Analytics My Activity Career 求人 Rewards 設定 35 75 49 興味通知 メッセージ ヘルプ お知らせ Kawamata Ryo 茨城県ひたちなか市 ソフトウェアエンジニア Frontend engineer @lapras_inc / TypeScript / Vue.js / Firebase / 元消防士 / 父 / GitHub: https://t.co/kV9MAb3Pz Zenn: https://t.co/5gKwcJkWA4 OSSゴールドコントリビューター </> 技術力スコア v2.2.2 4.64 (TOP 0.06%) 市場価値スコア GenAI NEW 4.15 (TOP 1.04%) 2025/05/09 14:35 更新 ビジネス力は3.48、影響力は4.09です。 この表示はまもなく削除されます。今後は市場価値スコアをご利用ください。 スコアは中央値が3.0、上位約15.9%が3.5、約2.3%が4.0です。詳しくは こちら Score Detail </> 技術力スコア 4.64 エンジニアの上位 0.06% I 内訳 GitHub 4.12 日々の活動量とリポジトリの内容 技術記事 4.66 Zenn、Qiitaの記事の内容と反響、AIによる記事内容の評価 技術イベント 4.30 技術系イベントの登壇と参加 タグ個数 4.16 Lv6以上のスキルタグ数 follow 87 / follower 475 / repo 268 follow 1k / follower 5.3k follower 683 / likes 8.8k SNSアカウントの連携と編集 ○ 情報源を表示

8.

L A P R A S Portfolio Analytics My Activity Career 求人 Rewards 設定 35 75 49 興味通知 メッセージ ヘルプ お知らせ Q 求人を検索する 検索 フルリモート リモート可 年収600万円~ フロントエンド インフラ・SRE 副業OK TypeScript 詳しい条件で検索 > あなたにおすすめの求人 フロントエンドエンジニア 【フルリモート/フレックス】労働に まつわる社会課題をなくし、誰もが… 株式会社SmartHR 年収400万円~ 1080万円 フルリモート 東京都 フロントエンドエンジニア Frontend Engineer 株式会社ROUTE06 年収600万円~ 1000万円 フルリモート Webアプリケーションエンジニア TypeScript/Node.js | フルスタック エンジニア | フルリモート | 正社員 ファンタクティブ株式会社 年収500万円~ 800万円 フルリモート 東京都 フロントエンドエンジ: ユーザーに寄り いフロントエン フラー株式会社 年収550万円~ 900 千葉県 他 ○ 6日前更新 (毎週月曜更新) 特集 “体験”を テクノロジーで形にし 届けるプロダクトを 運送業、ドライバーの 支援を行うプロダクト 製造業を支援する プロダクトを 動画配信 携わるフ

9.

できること 1. 求人一覧・詳細の取得 … Rustをプロダクトで使っている会社でフルリモート可の求人を探して 2. 職歴、職務要約、やりたいことの取得・更新 … 私のキャリア情報を取得して、キャリアコーチングしてください … このPDFの情報をもとにLAPRASのキャリア情報を更新してください ※ 2025/05/11 時点

10.
[beta]
設定方法
node版
{
"mcpServers": {
"lapras": {
"command": "npx",
"args": ["-y", "@lapras-inc/lapras-mcp-server"],
"env": { "LAPRAS_API_KEY": "<YOUR_LAPRAS_API_KEY>" }
}
}
}
Docker版
{
"mcpServers": {
"lapras": {
"command": "docker",
"args": [
"run","-i","--rm","-e","LAPRAS_API_KEY",
"laprascom/lapras-mcp-server:v0.4.0"
],
"env": { "LAPRAS_API_KEY": "<YOUR_LAPRAS_API_KEY>" }
}
}
}
※ LAPRAS_API_KEYは職歴情報を扱う場合のみ必要
11.

DEMO 求人検索 → 職歴更新の流れを Raycast MCP 経由で実演します

12.

公式SDKのみ採用 MCP公式のtypescript-sdkのみ使用。 仕様変更への追従性を優先。 modelcontextprotocol/ typescript-sdk The official Typescript SDK for Model Context Protocol servers and clients 54 155 6k 745 Contributors Issues Stars Forks github.com GitHub - modelcontextprotocol/typescript-sdk: The official Typescript SDK for Model Context Protocol servers and clients

13.

toolごとにClassに分割 toolごとにスキーマと関数をまとめて管 理できるようにclassで管理。 それぞれUnitTestも追加。 lapras-mcp-server/src/ tools/ createExperience.ts updateExperience.ts searchJobs.ts ... index.ts

14.
[beta]
export class GetJobDetailTool implements IMCPTool {
readonly name = "get_job_detail";
readonly description = "Get detailed information about a specific job posting. ....";
readonly parameters = {
jobId: z.string().describe("The unique identifier of the job posting"),
} as const;
async execute(args: InferZodParams<typeof this.parameters>): Promise<{
content: TextContent[];
isError?: boolean;
}> {
const { jobId } = args;
// ...
const url = `${BASE_URL}/job_descriptions/${jobId}`;
try {
const response = await fetch(url);
// ...
}
}
}
15.
[beta]
export class GetJobDetailTool implements IMCPTool {
readonly name = "get_job_detail";
readonly description = "Get detailed information about a specific job posting. ....";
readonly parameters = {
jobId: z.string().describe("The unique identifier of the job posting"),
} as const;
async execute(args: InferZodParams<typeof this.parameters>): Promise<{
content: TextContent[];
isError?: boolean;
}> {
const { jobId } = args;
// ...
const url = `${BASE_URL}/job_descriptions/${jobId}`;
try {
const response = await fetch(url);
// ...
}
}
}
tool名、概要、パラメーター
16.
[beta]
export class GetJobDetailTool implements IMCPTool {
readonly name = "get_job_detail";
readonly description = "Get detailed information about a specific job posting. ....";
readonly parameters = {
jobId: z.string().describe("The unique identifier of the job posting"),
} as const;
async execute(args: InferZodParams<typeof this.parameters>): Promise<{
content: TextContent[];
isError?: boolean;
}> {
const { jobId } = args;
// ...
const url = `${BASE_URL}/job_descriptions/${jobId}`;
try {
const response = await fetch(url);
// ...
}
}
}
実行する処理
17.
[beta]
const server = new McpServer(
{
name: "LAPRAS",
version: "0.1.0",
},
// ...
);
const ALL_TOOLS: IMCPTool[] = [
new SearchJobsTool(), // 求人検索ツール
new GetJobDetailTool(), // 求人詳細取得ツール
// ...
]
for (const tool of ALL_TOOLS) {
server.tool(tool.name, tool.description, tool.parameters, tool.execute.bind(tool));
}
18.
[beta]
const server = new McpServer(
{
name: "LAPRAS",
version: "0.1.0",
},
// ...
);
const ALL_TOOLS: IMCPTool[] = [
new SearchJobsTool(), // 求人検索ツール
new GetJobDetailTool(), // 求人詳細取得ツール
// ...
]
for (const tool of ALL_TOOLS) {
server.tool(tool.name, tool.description, tool.parameters, tool.execute.bind(tool));
}
それぞれ初期化
19.
[beta]
const server = new McpServer(
{
name: "LAPRAS",
version: "0.1.0",
},
// ...
);
const ALL_TOOLS: IMCPTool[] = [
new SearchJobsTool(), // 求人検索ツール
new GetJobDetailTool(), // 求人詳細取得ツール
// ...
]
for (const tool of ALL_TOOLS) {
server.tool(tool.name, tool.description, tool.parameters, tool.execute.bind(tool));
}
serverにtoolを登録
20.

監視体制を整える - AWS WAFでMCPから呼ばれるAPIのレートリミット設定 - DatadogでMCP用APIのリクエスト監視・アラート設定

21.

★ MCP 稼働状況 Share Show Overlays Configure + Add Widgets UTC+09:00 1mo Past 1 Month 求人検索 (api/mcp/job_descriptions/search) 利用ユーザー数 (ELB IP基準) 1234 アクセス数 (ELB) Error Warn数 (ELB) アクセス一覧 (APM) Latency 求人詳細 (api/mcp/job_descriptions/<id>) 利用ユーザー数 (ELB IP基準) 234 アクセス数 (ELB) Error Warn数 (ELB) アクセス一覧 (APM) Latency 職歴取得 (api/mcp/experience) 利用ユーザー数 (ELB IP基準) 456 アクセス数 (ELB) Error Warn数 (ELB) アクセス一覧 (APM) Latency 職歴作成 (api/mcp/experience) 職歴更新 (api/mcp/experience/<id>) 職歴削除 (api/mcp/experience/<id>)

22.

失敗と学び

23.

① 特定デバイスでのみ発生するエラー 予期せぬ容量制限により、Claudeは現在応答できません。しばらくしてから再度お試しください。

24.

問題・原因 「無料版claude」限定で、求人検索Toolのエラーが発生。条件によって大量の求人がヒット。Toolの返すデータ量がコンテキスト長を超えていた。

25.

対応&学び ページ単位を縮小 & LLMでの応答に不要なパラメータを除外することで回避。デバイス・モデルによって動作条件は異なるので、WEBアプリ同様最適化は必要。

26.

② LLMのToolの誤操作でデータが飛ぶ 同僚の職歴は2回吹き飛びました😇

27.

問題・原因 当初の職歴更新Toolは「職歴全体の一括更新」。職歴レコードの追加・更新・削除を全て担う一つのAPIを呼んでいた。にも関わらず、LLMが「部分更新」を試みて一部の職歴が消失する事故が発生

28.

ダメだった解決策 現在の職歴とパラメータの職歴をTool側で検証し、削除が発生する場合はエラーを返す実装に変更。引数がforce=true時のみ削除を含む更新が可能。Gitのforce push的なイメージした作り。

29.

ダメだった解決策 現在の職歴とパラメータの職歴をTool側で検証し、削除が発生する場合はエラーを返す実装に変更。引数がforce=true時のみ削除を含む更新が可能。Gitのforce push的なイメージした作り。 エラー発生時、LLMが勝手にforce=trueにして再送で事故再発😇

30.

KawamataRyo 9:03 AM 皿を洗ってたら職歴更新MCPツールの良さそうな実装案を閃いたのでメモ 前提 - ツール名はsync_work_experiencesに変更して、更新APIは既存bulkのAPIを使う - ツール内の更新APIリクエストはすべてIDを除く(つまりMCP経由はすべてcreate) - ツールのparameterにforceオプションを追加。optionalでデフォルトはfalse。このオプションを使う前に必ずユーザーに確認することを追記。 動き - 職歴更新ツールが実行されたら職歴更新ツール内の処理で一覧取得API (GET) を呼ぶ - もし現在の一覧の件数よりパラメータの一覧の件数が少ない場合 or 消える項目がある場合(会社名で判定とか?)は、forceを使わないと更新できない旨のエラーを投げる - LLMがユーザーにforceをつけるか確認する - ユーザーがforceをつけると答える - 更新する メリット - forceを使わない限り職歴は消されない (これはLLMに頼らずツール側でハンドリングできる) - 職歴なしユーザーは一括で追加できる - ツールを増やさなくて良い - API側の変更も不要 リスク - MCPクライアント側がいきなりforceをつけてくる or エラーの際にユーザーに確認せずにforceをつける -> ちゃんとエラー文やforceパラメーターの説明をかけばかなりの確率で回避できそう gitのコンフリクト時のpush --forceのイメージ。コンフリクト(削除)がなければ普通にgit使ったことあるエンジニアなら動きイメージできるから良い気がするんだけどどうだ 当時圧倒的閃きだと思ってSlackに書いたメモ

31.

対応&学び Toolを「個別職歴の追加・更新・削除」の3つに分割し解消。ひとつの複雑なツールより、シンプルな動作のツールを複数作る方が動作が安定する (unixコマンド的な思想)。

32.

③ LLMの予期せぬ出力で表示が崩れる MCPサーバー経由でCareerを更新すると改行 \nの文字列として表示されてしまう #5 Closed ekusiadadus opened last month 代表取締役CEO\nBreakAI株式会社 | 2023年10月 - 現在\n\n「Almightyターミナル」を中心としたAI駆動型開発者ツールの開発と戦略的成長をリードし、製品ビジョン、技術アーキテクチャ、事業開発の主要責任を担当。一人での運営から開始し、月間売上200万円規模に成長させた実績あり。\n\n【製品開発-Almightyターミナル】\n- LLMを活用した効率的なデバッグ提案・実行機能の実装\n- OS近接のターミナルエミュレーターによりネットワーク・システム設定へのアクセスを実現\n- ISUCON13でのTVer Award受賞経験を活かし、3人チームでのGo並列化、DB分割、オンメモリキャッシュなどの技術を応用\n- Almightyターミナルのレスポンス時間を業界標準の30%以下に最適化\n- コーディング不要でソフトウェア開発可能なAIエージェント機能の実装\n\n【製品開発

33.

問題・原因 LLMがパラメータ内に \\n (改行エスケープ) を混入し、プロダクト側で画面表示が崩れる問題が発生。

34.

対応&学び MCPサーバー側でクレンジング処理 (\\nの除去) を追加し、表示崩れを回避。LLMの出力が人間ではありえない予期せぬ形式になることがあるため、MCPサーバー側で内容の正規化が必要。

35.

継続検討

36.

どこまで品質を担保するか問題 リリース時、どこまでの品質を担保すべきか意見が分かれた。特に更新系ツール。 MCPサーバー側でtoolやパラメーターの説明を詳細に書いても、使われるLLMのモデルによっては予期せぬデータ更新になる可能性がある。仕様的な値のバリデーションはAPI側で行うが、内容自体の正当性は担保することはできない。やり直しの効かない操作での利用はまだ難しい (例えば転職サービスなら応募やメッセージの送信など)。

37.

利用にリテラシーが求められる MCPは使ってみれば簡単だが、まだエンジニア以外での初期設定は難しい。MCPへの投資が直接的なリターンにつながることは難しい。また、LLMに知見がない人が使うと結果に対しての考え方も違う。LLMの出力が予期しないものの場合、それがLLMの問題でもMCPを提供したサービスへのヘイトに繋がってしまう恐れがある。業態によっては提供は慎重になる必要がある。

38.

利用者の声

39.

嬉しい声が多数 You reposted シライシ @punksky2 · Apr 16 いやこれ冷静に考えるとエンジニア向けでなく、あらゆるプロダクトに 応用できるUXだ。 ChatとMCP部分でサーバー側に隠してしまえば、音声や紙の情報から良 い感じのデータ作れちゃう。 Kawamata Ryo @KawamataRyo · Apr 16 LAPRASのMCPサーバーv0.3.0で職歴を追加・編集できるツールをリ ースしました! 転職時に一番面倒な職歴入力もLLMと対話しながら簡単にできます! Claudeとコーヒーブレイクはいかがで すか? You reposted にゃふんた @nyafunta9858 · Apr 16 ちょうど手帳を感じてたところだったから今度試してみよ・・ 職歴とプロフィール入力はもっと低コストでいきたいなーと最近よく思う Kawamata Ryo @KawamataRyo · Apr 16 LAPRASのMCPサーバーv0.3.0で職歴を追加・編集できるツールをリ ースしました! 転職時に一番面倒な職歴入力もLLMと対話しながら簡単にできます! Claudeとコーヒーブレイクはいかがで すか? You reposted こん @k8n_karin · Apr 16 転職する気無いけど使ってみたい派なんですよ 文章要約して決まったテンプレートに当てて入稿できる口を作るのはい ろんなとこで使えそうだなー Kawamata Ryo @KawamataRyo · Apr 16 LAPRASのMCPサーバーv0.3.0で職歴を追加・編集できるツールをリ ースしました! 転職時に一番面倒な職歴入力もLLMと対話しながら簡単にできます! Claudeとコーヒーブレイクはいかがで すか? You reposted 絶望ドメン(貧血症) @ekusiadadus · Apr 17 Lapras MCP サーバーを公開してたので、職歴を更新しておいた。 Claudeがほぼ全てを書いてくれた。 キャリア情報を解析した結果、市場価値スコアは4.50でした! 全体の上位0.1%です #LAPRAS市場価値スコア #LAPRAS6周年 LAPRAS 市場価値スコア 4.50 Top 0.14% 内訳 テクノロジー 3.31 リード 3.89 プロダクト 3.17 マネジメント 3.95 市場価値スコア LAPRAS From lapras.com 1 5 12 1k You reposted しゃば @shaba_dev · Apr 21 これ良いな。経歴の書き方とかいい感じに壁打ちできそうな予感 lapras-inc/lapras-mcp- server lapras.com 公式MCP Server 3 0 63 0 Contributors Issues Stars Forks GitHub - lapras-inc/lapras-mcp-server: lapras.com 公式MCP Server From github.com 4 10 781 You reposted 安東竜平 @airunner_linkai · Apr 9 今後の転職活動はこうなるのかな ChatGPTとかと壁打ちしてると、どんどん自分のこと理解してくれて、その まま自分に合った職場を提案してくれたり、自己分析が実行してそのまま 情報を探してくれたり。そのままWEB検索で求人情報のファクトチェック入 れてミススマッチも防げそう You reposted だいすけ @daisuke · Apr 9 MCPが何だかを理解するのが難しくなっているいま、新規の入門者にとって 目的がはっきりしてかつシンプルなサーバーであることは入門に最適なんじ ゃないかな。 VS Code + Copilot環境からここを読んで導入。 code.visualstudio.com/docs/copilot/c... Kawamata Ryo @KawamataRyo · Apr 9 MCPサーバー経由でLAPRAS上の職歴を更新する機能を開発中。 自分の公開レジュメが勝手に更新してくれる。フォームに入力する手 間ないのでめっちゃ楽・・!

40.

最後に

41.

LAPRAS MCP Server ぜひぜひ使ってみてください!! 職歴入れると市場価値スコア も上がります!💪

42.

End 👋