ZOZO Advent Calendar 2025 カレンダー Vol.12 の 20 日目の記事です。
恒例になりつつある黒トイプー琥珀の3歳の誕生日にアドベントカレンダーを書かせてもらいます。
Happy Birthday!!

アドベントカレンダーしか記事を公開しないブログとなりつつありますね、、
毎年言っていますが来年は初心に帰ってインプット(主に勉強会の参加など)を増やしてアウトプット(感想記事)を行っていければと思っています。
古着の相場ってむずかしくない?
先日、僕が入会している某アーティストのファンクラブ配信で、アーティストの私物ブランド品のフリマ販売が行われました。
サンローランのブルゾンが30,000円、GUCCIのブーツも30,000円、ルイヴィトンのジャケットが10,000円と破格の値段でしたが残念ながら抽選にはずれてしまい、購入することはできませんでした。。。
そのような超格安販売を見ていてふと、「これ、いま買ったら後で値上がりするの?それとも下がるの?」みたいな疑問が頭をよぎりました(転売は絶対しません)。
特に最近は、
- ブランドのリバイバル
- コラボによる一時的な高騰
- モデルチェンジによる相場落ち
など、アイテムごとに動き方がけっこう違います。
ただ、僕が実際にやっているのは、メルカリを数ページ検索してなんとなくの相場感をつかんで「たぶんこのへんの値段かな?」と判断するという、わりと属人的で肌感に頼る感じです。
もちろんそれでも問題ないですが、最近のAIブームの中で、ふとこんなことを考えました。
相場の「傾向」だけでもAIに補助してもらえないか?
細かい統計モデルやMLをやるほど大げさにしたいわけではなくて、もっとざっくりした判断をAIに持たせたい。
たとえば、
- COMME des GARÇONS のベーシックTは年式が多少古くても需要が落ちない
- ACG のアウターは2010年代前半の値動きが独特
- スニーカーはリリース年とカラーで上下の差が激しい
- GUCCI のバッグは年式が古いと一気に相場が落ちることがある
こういうジャンルごとの傾向はAI とデータがセットになるとかなり得意領域になります。
そこで今回試してみたのが、「古着・ブランド価格リスク判定MCP」 という仕組みです。
MCP(Model Context Protocol)を使うことで、自分で作ったミニDB(SQLite)をAIの“外部ツール”として扱い、ブランド×アイテム×年式の入力から、「いま買うと価値が上がりそうか/下がりそうか」を返せるようにしていきます。
投資ではなく、あくまで“相場の傾向をデータとして扱うための実験”という位置づけです。
これを作ってみると、思った以上に手軽に「AIの相場アシスト」が実現できたので、この記事ではその作り方を順番に紹介していきます。
まずはミニ相場DBを作る:SQLiteでブランド × アイテム × モデル名 × 色 × サイズを管理
「相場をAIに判断させる」と聞くと難しく感じますが、最初は 手元のミニDB(SQLite) だけあれば十分です。
古着やスニーカー市場は、同じブランド・同じアイテムでも “モデル名・カラー・サイズ” で価値が激変する世界です。
そのため今回のミニDBでは、以下の7つの軸で管理することにしました。
- ブランド(brand)
- アイテムタイプ(item_type)
- 商品名・モデル名(model_name)
- 色(color)
- サイズ(size)
- 年式(year)
- 平均相場(avg_price)
これだけでも、それっぽい相場感が出せます。
SQLite を作る
まずDB本体を作ります。
sqlite3 items.db
SQLite のプロンプトが開いたら、以下のテーブル作成SQLを流します。
テーブル構造(色・サイズ・モデル名まで含む)
CREATE TABLE items ( id INTEGER PRIMARY KEY AUTOINCREMENT, brand TEXT NOT NULL, item_type TEXT NOT NULL, model_name TEXT, color TEXT, size TEXT, year INTEGER, avg_price REAL, currency TEXT );
- model_name
- “Dunk Low Retro Panda” / “Box Logo Tee” / “Classic Leather” などの商品名
- color
- 黒 / 白 / Red / “Black/White” など
- size
- Tシャツ:M / L
- スニーカー:27.5 / 28
- バッグ:Small / Medium etc.
扱うジャンルによって揺れがあるので、TEXT型で柔軟にしておくのがポイント。
とりあえず数件だけでも十分
まずは、動かすためのダミーデータです。
※実在の相場ではありません(例示用)。
INSERT INTO items (brand, item_type, model_name, color, size, year, avg_price, currency) VALUES ('Supreme', 'T-shirt', 'Box Logo Tee', 'White', 'L', 2021, 21000, 'JPY'), ('Supreme', 'T-shirt', 'Box Logo Tee', 'Black', 'L', 2020, 24000, 'JPY'), ('Nike', 'Sneakers', 'Dunk Low Retro "Panda"', 'Black/White', '27.5', 2020, 28000, 'JPY'), ('Nike', 'Sneakers', 'Dunk Low Retro "Panda"', 'Black/White', '28.0', 2021, 32000, 'JPY'), ('GUCCI', 'Bag', 'GG Marmont Matelassé Small', 'Black', NULL, 2019, 120000, 'JPY'), ('GUCCI', 'Bag', 'GG Marmont Matelassé Small', 'Beige', NULL, 2018, 110000, 'JPY'), ('COMME des GARÇONS', 'T-shirt', 'Heart Logo Tee', 'White', 'M', 2022, 9000, 'JPY'), ('COMME des GARÇONS', 'T-shirt', 'Heart Logo Tee', 'Black', 'L', 2020, 12000, 'JPY');
これだけでも、「ブランドの傾向 + モデルの人気 + カラー人気 + サイズ人気」が相場にどう効いているかがなんとなく見えるデータになります。
中身を確認
SELECT brand, item_type, model_name, color, size, year, avg_price FROM items;
数行入っていればOKです。
どう使うのか?
次章で作る MCPサーバー は、ユーザーから受け取った情報(例:ブランド=Nike、アイテム=スニーカー…)を元に、
という流れで動きます。
つまりこの items.db が、AIが“相場のクセ”を学習するためのミニ辞書の役割を果たします。
"古着・ブランド価格リスク判定MCP" を最小構成で作る
ここからは、いよいよ MCPサーバー本体 を作っていきます。
やることはシンプルで、
- Node.js で MCP サーバーを立ち上げる
- さきほど作った
items.db(SQLite)を読み込む - ブランド / アイテム / 年式 などの条件で検索
- 「上がりそう/下がりそう」のざっくり評価を返すツールを作る
という流れです。
ここでは あくまで"最小構成のサンプル"に絞ります。
プロジェクトの準備
まずは適当なディレクトリを作ります。
mkdir vintage-risk-mcp cd vintage-risk-mcp
npm init して最低限の設定をします。
npm init -y
次に、必要なライブラリを入れます。
- SQLite 用:sql.js(Pure JavaScript実装でネイティブビルド不要)
- MCP 用:@modelcontextprotocol/sdk
npm install sql.js npm install @modelcontextprotocol/sdk
TypeScript にしたい場合は typescript / ts-node なども入れればよいですが、ここでは シンプルに Node.js(CommonJS or ES Modules)で動く前提のサンプルにしておきます。
Note:
better-sqlite3はネイティブモジュールのため、Node.js のバージョンによってはビルドエラーが発生することがあります。sql.jsは Pure JavaScript 実装なので、環境を選ばず動作します。
ファイル構成(最小)
とりあえずこんなイメージです。
vintage-risk-mcp/ ├─ items.db # 作ったSQLiteファイルをここにコピー ├─ package.json └─ server.mjs # MCPサーバー本体(ES Modules形式)
package.json の例
必要に応じて "type": "module" をつけておきます。
{ "name": "vintage-risk-mcp", "version": "0.1.0", "type": "module", "main": "server.mjs", "scripts": { "start": "node server.mjs" }, "dependencies": { "@modelcontextprotocol/sdk": "^1.0.0", "sql.js": "^1.10.0" } }
バージョンは実際のSDKや環境に合わせて調整してください。
リスク判定の考え方(ゆるいロジック)
ここではあくまで「相場の“傾き”をざっくり見るだけ」のロジックにします。
入力として受け取るのは、だいたいこのあたり:
- brand(ブランド名)
- item_type(T-shirt, Sneakers, Bag …)
- year(買おうとしているアイテムの年式)
- あれば:model_name, color, size
これをもとに、SQLiteから近いレコードを取ってきて、以下のような判定をします。
ざっくりアルゴリズム例:
- 同じブランド & アイテムタイプのデータを取得(必要に応じて model_name・color・size を絞る)
- year が小さい順にソートして、価格推移をざっと見る
- 直近数年の平均価格の変化率を見る
- 変化率に応じて:
- 上昇傾向 → trend: "up", risk: "low"
- 横ばい → trend: "flat", risk: "medium"
- 下降傾向 → trend: "down", risk: "high"
もちろん、本気でやるならもっと複雑にできますが、記事の段階ではこのくらいの“ゆるさ”がちょうどいいです。
server.mjs(MCPサーバー本体)
以下が実際に動作するMCPサーバーのコードです。
// server.mjs import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import initSqlJs from 'sql.js'; import { readFileSync } from 'fs'; import { fileURLToPath } from 'url'; import { dirname, join } from 'path'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); // SQLite を開く const SQL = await initSqlJs(); const dbBuffer = readFileSync(join(__dirname, 'items.db')); const db = new SQL.Database(dbBuffer); // MCPサーバーを初期化 const server = new Server( { name: 'vintage-price-risk', version: '0.1.0', }, { capabilities: { tools: {}, }, } ); // ツール一覧を返すハンドラ server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'judge_vintage_price_risk', description: 'ブランド古着の相場傾向から、価格リスク(上がりそう/下がりそう)をざっくり判定する。', inputSchema: { type: 'object', properties: { brand: { type: 'string', description: 'ブランド名' }, item_type: { type: 'string', description: 'アイテム種別(T-shirt, Sneakers, Bag など)' }, model_name: { type: 'string', description: '商品名・モデル名' }, color: { type: 'string', description: '色' }, size: { type: 'string', description: 'サイズ' }, year: { type: 'number', description: 'アイテムの年式' }, }, required: ['brand', 'item_type'], }, }, ], }; }); // ツール実行ハンドラ server.setRequestHandler(CallToolRequestSchema, async (request) => { if (request.params.name !== 'judge_vintage_price_risk') { throw new Error(`Unknown tool: ${request.params.name}`); } const { brand, item_type, model_name, color, size, year } = request.params.arguments; // 1. ベースとなるクエリを組み立てる let query = ` SELECT year, avg_price FROM items WHERE brand = ? AND item_type = ? `; const params = [brand, item_type]; // 任意項目で少しずつ絞り込む(あれば使う) if (model_name) { query += ` AND model_name = ?`; params.push(model_name); } if (color) { query += ` AND color = ?`; params.push(color); } if (size) { query += ` AND size = ?`; params.push(size); } query += ` ORDER BY year ASC`; const result = db.exec(query, params); const rows = result.length > 0 ? result[0].values.map(row => ({ year: row[0], avg_price: row[1] })) : []; // 2. データが少なすぎる場合 if (rows.length < 2) { return { content: [ { type: 'text', text: JSON.stringify({ risk: 'unknown', trend: 'unknown', score: 0.0, reason: '該当データが少なすぎるため、価格リスクを判定できませんでした。ブランドや条件を変えてみてください。', sampleYears: rows.map((r) => r.year), }), }, ], }; } // 3. 推移をざっくり計算する(最後と最初の比だけ見るシンプル版) const first = rows[0]; const last = rows[rows.length - 1]; const diff = last.avg_price - first.avg_price; const rate = diff / first.avg_price; // 変化率 let trend = 'flat'; let risk = 'medium'; let score = 0.5; if (rate > 0.15) { trend = 'up'; risk = 'low'; score = 0.8; } else if (rate < -0.15) { trend = 'down'; risk = 'high'; score = 0.2; } // 年式指定があれば、それも軽くコメントに反映する let yearComment = ''; if (year) { const closest = rows.reduce((prev, curr) => { return Math.abs(curr.year - year) < Math.abs(prev.year - year) ? curr : prev; }); yearComment = `指定された年式 ${year} 年に最も近いデータは ${closest.year} 年の平均相場 ${closest.avg_price} 円です。`; } // 4. 結果を返す return { content: [ { type: 'text', text: JSON.stringify({ risk, trend, score, rate, sampleYears: rows.map((r) => r.year), reason: `ブランド「${brand}」の「${item_type}」について、初期の平均相場 ${first.avg_price} 円から直近の平均相場 ${last.avg_price} 円へ変化しています(変化率: ${(rate * 100).toFixed(1)}%)。${yearComment}`, }), }, ], }; }); // MCPサーバーを起動(標準入出力で待ち受け) async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error('Vintage Price Risk MCP server running on stdio'); } main().catch(console.error);
ざっくり動作イメージ
この MCP サーバーが立ち上がると、Claude(MCP対応クライアント)からは、だいたいこんなイメージで呼ばれます。
Claude に:
「Nike の Dunk Low Panda で、スニーカー、サイズ 27.5、2020年あたりの相場の傾向を見て。上がりそうか、下がりそうかざっくり教えて。」
Claude は judge_vintage_price_risk ツールを呼び出し、brand="Nike", item_type="Sneakers", … を渡す
- MCPサーバーが SQLite を検索して、JSON で risk, trend, score, reason を返す
- Claude がそれを読み取り、人間向けの文章でまとめ直す
という流れです。
この章のゴール
この章のゴールは、SQLite のミニDBをMCPを通じて「古着・ブランド価格リスク判定ツール」として公開できるというところまで持っていくことでした。
ロジック自体はものすごく単純ですが、「自分で定義した相場ロジックを、AIのツールとして組み込めた」というところが一番大きなポイントです。
Claude に聞いてみる:「このアイテム、今買うとどう?」
ここまでで、
が用意できました。
この章では、いよいよ Claude 側からこのMCPを呼び出して使ってみる パートです。
ざっくり全体像
やりたいことはシンプルです。
- ユーザーが Claude に自然言語で質問する
- Claude が「この質問には
judge_vintage_price_riskツールを使うのが良さそう」と判断 - MCPサーバーに対して、必要なパラメータを投げる
- MCPサーバーが SQLite を検索し、リスク判定結果を JSON で返す
- Claude がその JSON を読み、日本語の解説に変換して返してくれる
最終的に人間から見えるのは、 「AIが過去の相場を見ながらコメントしているような回答」です。
想定プロンプト例①:シンプルな例
まずは、Supreme の Tシャツから。
「Supreme の Box Logo Tee で、白の L サイズ、2021年のモデルって、
いま買うと相場的にどう?上がりそう、それとも下がりそう?」
Claude の内部では、だいたいこんなパラメータで judge_vintage_price_risk が呼ばれているイメージです。
{ "brand": "Supreme", "item_type": "T-shirt", "model_name": "Box Logo Tee", "color": "White", "size": "L", "year": 2021 }
MCPサーバー側は、この条件をもとに items テーブルを検索します。
MCPサーバーからのレスポンス例(イメージ)
例えば、こんな JSON が返ってくるとします。
{ "risk": "low", "trend": "up", "score": 0.78, "rate": 0.21, "sampleYears": [2020, 2021], "reason": "ブランド「Supreme」の「T-shirt」について、初期の平均相場 21000 円から直近の平均相場 25500 円へ変化しています(変化率: 21.0%)。指定された年式 2021 年に最も近いデータは 2021 年の平均相場 25500 円です。" }
これをそのまま人間に見せるとちょっと無骨なので、Claude がいい感じに自然文にまとめてくれます。
Claude の回答イメージ
例えば、こんな感じになりえます:
Supreme の Box Logo Tee(White / L / 2021年)について、過去データを見ると平均相場はおよそ 21,000円 → 25,500円 と 約21% 上昇 しています。
直近までの推移だけ見ると 緩やかに値上がり傾向 にあり、いまの水準で購入するリスクはそこまで高くない、という判定になりました。
もちろん市場全体のトレンドやコンディションにもよりますが、「相場が大きく崩れているカテゴリ」ではない、という程度のニュアンスで捉えてもらえれば良いと思います。
MCPが「数字ベースの評価」を担当し、Claude が「文章化・解釈」を担当するイメージです。
想定プロンプト例②:スニーカー・サイズ込み
次に、スニーカーの例を見てみます。
「Nike の Dunk Low Retro 'Panda' で、ブラック/ホワイト、27.5cm、2020年頃の相場って今後どうなりそう?ざっくり傾向だけ教えて。」
内部的には、こういう呼び出しになるイメージです。
{ "brand": "Nike", "item_type": "Sneakers", "model_name": "Dunk Low Retro \"Panda\"", "color": "Black/White", "size": "27.5", "year": 2020 }
もし items.db に、
- 2018年:19,000円
- 2020年:28,000円
- 2021年:32,000円
みたいなデータが入っていれば、かなり強い上昇トレンドとして判定されるはずです。
Claude の回答イメージ(スニーカー)
Nike Dunk Low Retro "Panda"(Black/White / 27.5cm)について、手元の相場データでは 2018年から2021年にかけて 約19,000円 → 約32,000円 と価格が上昇しており、全体としては強めの上昇傾向になっています。
この傾向だけ見ると、短期的に「暴落リスクが高いカテゴリ」というよりは、「人気が続いているモデル」寄りの動きです。
もちろん再販やモデルチェンジで変わる可能性はありますが、過去数年のデータだけで見れば、いまの価格で買うリスクは比較的低め という判定になります。
このように、数字を“文章の温度感”に変換するところをAIに任せるのがポイントです。
条件があいまいな場合はどうなる?
例えば、以下のようにざっくりした質問をすることもできます。
「COMME des GARÇONS の Tシャツ全般で、最近の相場って上がってる印象?下がってる印象?年式まではあまり気にしてない。」
この場合、Claude が
- brand = "COMME des GARÇONS"
- item_type = "T-shirt"
だけを渡して、model_name や color は指定せずに呼び出す、という動きをしてくれるかもしれません。
MCPサーバー側では「該当件数が少ない」「バラつきが大きい」などの状況もふまえて、
- risk: "unknown"
- trend: "flat"
などを返すようにしておくと、Claude側でちゃんと
「データがまだ少なめなので断言はできませんが、現時点の傾向としては…」
のようなニュアンスをつけて説明してくれます。
実際に使ってみると見えてくること
実際にいくつかパターンを試すと、
- 「ブランド × アイテム」だけだと大雑把すぎる
- 「モデル名・カラー・サイズ」まで入れると、それっぽい話になる
- データの行数が少ないとすぐに unknown 判定になる
- 逆に、データが増えてくると“相場のクセ”がはっきり見え始める
といった感覚が分かってきます。
この「データが増えるほど相場感が育っていく」感覚は、実際に試してみるとけっこう楽しいポイントです。
この章のまとめ
この章では、実際に Claude から
「このアイテム、今買うとどう?」
と聞いてみたときの
という一連の流れを見てきました。
MCPが “数字とロジック” を、Claude が “文章と解釈” を担当することで、「自分で定義した相場判定ロジックを、会話の中で使えるようになる」というのが一番のポイントです。
使ってみて気づいたこと & 「データを自動で集めてDBを作る」ならどうする?
ここまでで、 ミニ相場DB(SQLite) → MCP → Claude という流れで、
「このアイテム、今買うとどう?」
を AI にざっくり判定させる仕組みが完成しました。
実際に動かしてみると、 いくつか気づきや改善点、そして 「これ、もっと実用的にするならどうしたらよい?」 という考えも自然と湧いてきます。
この章では、そのあたりをゆるくまとめながら、最後に「データを自動で集めてDBを作る」アイデアまで触れていきます。
まず使ってみて感じたこと
1. モデル名・カラー・サイズはやっぱり重要
古着・スニーカー市場は “個別の特徴で値動きが変わる”世界なので、この3つを入れたことで判定の説得力が一気に上がります。
2. データが少ないとすぐに unknown 判定になる
これはSQLiteの行数を増やせば解決します。
逆にいうと「自分専用の相場データが増えるほど、AIが賢くなる」感じが楽しい。
3. 判定ロジックはシンプルで十分
複雑な回帰や機械学習をする必要はなく、「過去の平均相場の推移を見るだけ」で意外と"人間っぽい"判断が返ってきます。
今後やりたい改善ポイント
ここからは、もっと現実的に使えるための改善案です。
改善1:データ件数を増やす(精度アップの王道)
- モデル名・カラーごとに最低3年ぶん
- サイズ違い(スニーカー)は3〜5サイズ
- バッグなら状態(美品 / 使用感あり)も欲しい
DBが育つほど、AIが「相場のクセ」を掴む速度が速くなるのが面白いところ。
改善2:「コンディション」を追加したくなる
古着は状態がすべてです。
例えば:
- 新品
- 未使用に近い
- 目立った傷汚れなし
- 使用感あり
これを DB に追加するだけで、より"それっぽい"相場判定になります。
改善3:年式じゃなく「リリースシーズン」に対応したい
スニーカーやデザイナーズブランドは、FW(秋冬)/ SS(春夏)で相場が違うことがあります。
例えば:
- FW のコートはリリース年よりシーズンの方が重要
- SS のTシャツは夏前に値段が上がる
- スニーカーは“初回発売年”が重要
後からカラム追加すれば大丈夫なので、SQLite の柔軟性が活きます。
「検索して対象データを集めてDBを作る」ならどうする?
この記事で一番触れたかった部分かもしれません。
将来的に、手作業でデータを追加するのではなく、検索して集めてDBを作る という方向にも広げられます。
ここでは主要な方法を3つ紹介します。
方法1:公式API(あるなら)を使う
もし利用しているサービスに公式API があれば、それが一番安全で確実です。
- StockX(非公式APIが有名だけど公式も一部あり)
- Grailed(APIあり)
- eBay(API豊富)
- 国内なら一部の小規模マーケットにAPIが存在
APIで検索 → JSONをSQLiteに入れるだけで以下が自動化できます:
- ブランドで検索
- モデルごとの相場を取得
- 最新の販売価格を更新
- データ差分だけ更新
方法2:スクレイピング(規約に注意)
メルカリやラクマなど、HTMLスクレイピングは禁止 or グレー なケースが多いです。
そのためブログで明確に推奨はできませんが、
一般論としては:
- Playwright / Puppeteer でブラウザ経由の取得
- BeautifulSoup / Cheerio でHTML解析
- “出品中価格” ではなく “売却済み価格” を対象にする
などがあります。
ただし、規約がNGな場合は絶対にやらないのが前提です。
自動データ収集 → SQLite更新 の例(擬似コード)
SQLite を自動で更新する流れの疑似コードも載せておきます。
import initSqlJs from 'sql.js'; import { readFileSync, writeFileSync } from 'fs'; async function updateDbFromApi(apiResults) { const SQL = await initSqlJs(); const dbBuffer = readFileSync('./items.db'); const db = new SQL.Database(dbBuffer); const insert = db.prepare(` INSERT INTO items (brand, item_type, model_name, color, size, year, avg_price, currency) VALUES (?, ?, ?, ?, ?, ?, ?, ?) `); for (const row of apiResults) { insert.run([ row.brand, row.item_type, row.model_name, row.color, row.size, row.year, row.avg_price, row.currency ]); } insert.free(); // 更新したDBをファイルに書き戻す const data = db.export(); writeFileSync('./items.db', Buffer.from(data)); db.close(); }
これを
と繋げると「自動でミニ相場DBが育っていく」 世界が実現できます。
この章のまとめ
- 最初はミニDBでも、データが増えるほどAIが賢くなる
- モデル名・色・サイズを入れると判定の“実在感”が増す
- データ収集の自動化は、API → DB更新が最も現実的
- 企業データを使えば一気にレベルアップも可能
- SQLite なので構造変更も簡単で、育てるのが楽しい
そして何より、
「相場感」という“肌感覚”の部分を、AIと自作DBで再現できるという面白さ
ここに尽きるかもしれません。
最後に
今回作った 古着・ブランド価格リスク判定MCP は、まだまだ原始的な仕組みですが、
- 自分の価値観
- 自分の相場観
- 自分のデータ
を AI に持たせる第一歩としてはちょうどよい題材でした。
実験素材としても遊びとしても優秀なので、ぜひ SQLite の行数を増やしながら、自分だけの「相場AI」を育ててみてください。











