1. 概要
前回はGemini APIを使ってみる内容についてでした。今回は自作MCP(Model Context Protocol)サーバーをGemini CLIと連携して使ってみる内容になります。
対象としては開発を1年程やってて自分で最初から開発してみたい方になります。そのため細かい用語などの説明はしません。

2. nodeのインストール
こちらを参考
3. プロジェクトを作成
mkdir fakestore-demo
cd fakestore-demo
npm init
npm i -D typescript @types/node ts-node axios @types/express express
npx tsc --init
4. 必要なライブラリをインストール
npm install @modelcontextprotocol/sdk zod dotenv
5. ソースコード
5-1. package.json
{
"name": "fakestore-demo",
"version": "1.0.0",
"type": "module",
"description": "",
"main": "index.js",
"scripts": {
"build": "tsc",
"prestart": "npm run build",
"start": "node dist/index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/express": "^5.0.3",
"@types/node": "^24.2.0",
"axios": "^1.11.0",
"express": "^5.1.0",
"ts-node": "^10.9.2",
"typescript": "^5.9.2"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.17.2",
"dotenv": "^17.2.1"
}
}5-2. tsconfig.json
{
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"target": "ES2022",
"sourceMap": true,
"outDir": "dist",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"types": ["node"]
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}5-3. src/env.ts
export const SERVER_NAME = process.env.SERVER_NAME || "fakestore-mcp-server";
export const DEBUG = process.env.DEBUG;
export const LOG_FILE = process.env.LOG_FILE || "/tmp/fakestore-mcp.log";
export const FAKE_STORE_API =
process.env.FAKE_STORE_API || "https://fakestoreapi.com/products";5-4. src/utils.ts
import fs from "node:fs";
import { DEBUG, LOG_FILE } from "./env.js";
export const debug = (...args: unknown[]) => {
if (DEBUG !== "1") return;
const line = args
.map((a) => {
try {
return typeof a === "string" ? a : JSON.stringify(a);
} catch {
return String(a);
}
})
.join(" ");
const timestamp = new Date().toLocaleString("ja-JP", {
timeZone: "Asia/Tokyo",
});
fs.appendFileSync(LOG_FILE, `[${timestamp}] ${line}\n`);
};5-5. src/index.ts
import * as mcp from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import axios from "axios";
import { z } from "zod";
import { DEBUG, FAKE_STORE_API, SERVER_NAME } from "./env.js";
import { debug } from "./utils.js";
interface Product {
id: number;
title: string;
price: number;
description: string;
category: string;
image: string;
rating: {
rate: number;
count: number;
};
}
const mcpServer = new mcp.McpServer({
name: SERVER_NAME,
version: "1.0.0",
});
const fetchProducts = async (minPrice: number): Promise<Product[]> => {
const response = await axios.get<Product[]>(FAKE_STORE_API);
const products = response.data;
return products.filter((product) => product.price >= minPrice);
};
const GetProductsByMinPriceInput = z.object({
minPrice: z
.number()
.default(1000)
.describe("The minimum price of the products to retrieve."),
});
mcpServer.tool(
"get_products_by_min_price",
"Get a list of products from FakeStoreAPI, filtered by a minimum price.",
GetProductsByMinPriceInput.shape,
async (args, _extra) => {
debug(`[get_products_by_min_price] args:`, args);
const { minPrice } = GetProductsByMinPriceInput.parse(args);
try {
const products = await fetchProducts(minPrice);
let filteredProducts = products.filter(
(product) => product.price >= minPrice
);
if (filteredProducts.length === 0) {
return { content: [{ type: "text", text: "No products found." }] };
}
return {
content: [{ type: "text", text: JSON.stringify(filteredProducts) }],
};
} catch (error) {
const message =
error instanceof Error ? error.message : "An unknown error occurred";
return {
content: [
{
type: "text",
text: `Error fetching products: ${message}`,
},
],
};
}
}
);
async function main() {
const transport = new StdioServerTransport();
await mcpServer.connect(transport);
if (DEBUG === "1") {
debug(`MCP server is running...`);
} else {
console.error("MCP server is running...");
}
}
main().catch((error) => {
if (DEBUG === "1") {
debug("Server error:", error);
} else {
console.error("Server error:", error);
}
process.exit(1);
});5-6. .env
GOOGLE_CLOUD_PROJECT="作成済みのプロジェクト"5-7. .gemini/settings.json
{
"mcpServers": {
"fakestore-mcp-server": {
"command": "node",
"args": ["/home/sondon/dev/ai/mcp-server/fakestore-demo/dist/index.js"],
"env": {
"SERVER_NAME": "fakestore-mcp-server",
"DEBUG": "1",
"LOG_FILE": "/tmp/fakestore-mcp.log",
"FAKE_STORE_API": "https://fakestoreapi.com/products"
}
}
}
}5-8. GEMINI.md
出力条件
・表形式
・表のヘッダーは№(連番)、価格、カテゴリ、タイトル6. 実行
6-1. ビルド
npm run build
6-2. 実行
gemini -p "100ドル以上の商品を取得して高い順から並べて5件だけを出力してください"
| № | 価格 | カテゴリ | タイトル |
|---|---|---|---|
| 1 | 999.99 | electronics | Samsung 49-Inch CHG90 144Hz Curved Gaming Monitor (LC49HG90DMNXZA) – Super Ultrawide Screen QLED |
| 2 | 695 | jewelery | John Hardy Women's Legends Naga Gold & Silver Dragon Station Chain Bracelet |
| 3 | 599 | electronics | Acer SB220Q bi 21.5 inches Full HD (1920 x 1080) IPS Ultra-Thin |
| 4 | 168 | jewelery | Solid Gold Petite Micropave |
| 5 | 114 | electronics | WD 4TB Gaming Drive Works with Playstation 4 Portable External Hard Drive |7. ディレクトリの構造
.
├── .env
├── .gemini
│ └── settings.json
├── GEMINI.md
├── dist
│ ├── env.js
│ ├── env.js.map
│ ├── index.js
│ ├── index.js.map
│ ├── utils.js
│ └── utils.js.map
├── package-lock.json
├── package.json
├── src
│ ├── env.ts
│ ├── index.ts
│ └── utils.ts
└── tsconfig.json
3 directories, 15 files8. 備考
今回は自作MCP(Model Context Protocol)サーバーをGemini CLIと連携して使ってみる内容でした。
9. 参考
- Node.js — どこでもJavaScriptを使おう
- TypeScript: JavaScript With Syntax For Types.
- Model Context Protocol · GitHub
- Introduction – Model Context Protocol
投稿者プロフィール
-
開発好きなシステムエンジニアです。
卓球にハマってます。
























