back
Next.js + Vercel AI SDK + Gemini , MCP作成
id: 196, 2025-05-23
### 音声概要
・AIで生成された 音声概要になります。
### 概要
- Next.js + Vercel AI SDK , MCP Host的なメモになります。
- LLMは、Gemini
[ 公開 2025/05/08 ]
### 構成
- Next 15
- LLM: gemini
- node20
### 関連
### 書いたコード
- next.jsに、AI関係ライブラリ追加
npm i ai @ai-sdk/react @ai-sdk/google zod
- .env
- gemini API 設定
GOOGLE_GENERATIVE_AI_API_KEY="key"
- package.json
{
"name": "mcp-nextjs",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@ai-sdk/google": "^1.2.11",
"@ai-sdk/react": "^1.2.9",
"ai": "^4.3.9",
"next": "15.3.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"zod": "^3.24.3"
},
"devDependencies": {
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"tailwindcss": "^4",
"typescript": "^5"
}
}
- tree
$ tree .
.
├── app
│ ├── api
│ │ └── chat
│ │ └── route.ts
│ ├── favicon.ico
│ ├── globals.css
│ ├── layout.tsx
│ ├── lib
│ ├── page.tsx
│ └── tools
│ └── getNumber.ts
├── next-env.d.ts
├── next.config.ts
├── package.json
├── postcss.config.mjs
├── public
└── tsconfig.json
- api
- app/api/chat/route.ts
- generateText 使います。
import { NextResponse } from "next/server";
import { google } from "@ai-sdk/google";
import { streamText, tool } from "ai";
import { generateText } from "ai";
import { z } from "zod";
import { getNumber } from "../../tools/getNumber";
const MODEL_NAME = "gemini-2.0-flash";
export async function POST(req: Request) {
const { messages } = await req.json();
//console.log("msg=", messages);
const result = await generateText({
model: google(MODEL_NAME),
tools: {
getNumber ,
},
maxSteps: 5,
messages: [{ role: "user", content: messages }],
});
console.log("artifact:");
console.log(result.text);
return NextResponse.json({ret: 200, text: result.text});
}
- tool: サイコロ実行の例
- app/tools/getNumber.ts
import { generateText, tool } from "ai";
import { z } from "zod";
// サイコロを振ってください。1から6までの整数を返してください。
export const getNumber = tool({
description: "入力された面数のサイコロを振ります。",
parameters: z.object({
dice: z.number().min(1).describe("サイコロの面数").optional().default(6),
}),
execute: async ({ dice }) => {
return Math.floor(Math.random() * dice) + 1;
},
});
- mcp-nextjs/app/page.tsx
"use client";
import {useState} from "react";
export default function Chat() {
const [text, setText] = useState<string>("");
const chatStart = async function(){
try{
const elem = document.getElementById("input_text");
let inText = "";
if(elem){
inText = elem.value;
};
console.log("inText=", inText);
if(!inText){ return; }
const item = {messages: inText};
const body: any = JSON.stringify(item);
const res = await fetch("/api/chat", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: body
});
const json = await res.json();
console.log(json);
setText(json.text);
} catch(e){
console.error(e);
}
}
return (
<div className="flex flex-col w-full max-w-2xl py-24 mx-auto gap-4">
<h1 className="text-2xl font-bold">MCP-Chat</h1>
<div className="flex flex-col gap-2">
<input
id="input_text"
type="text"
className="w-full p-2 border border-gray-300 rounded dark:disabled:bg-gray-700"
placeholder="Type your message..."
/>
<button
type="button"
className="px-4 py-2 text-white bg-blue-500 rounded hover:bg-blue-600 disabled:bg-gray-700"
onClick={()=>{chatStart()}}
> GO
</button>
<div className="my-2 p-2 bg-gray-100">
{text}
</div>
</div>
</div>
);
}
- プロンプト参考
サイコロを振ってください。1から6までの整数を返してください。