📘

MCP Server構築 | Claude上で画像生成できるように

に公開

Claudeで画像生成ができない

Claudeは標準では画像生成機能を備えていません。本記事では、Model Context Protocol (MCP)とfal.aiのAPIを活用して、Claudeに画像生成機能を利用する方法を解説します。

本記事で解説するコードは、以下のGitHubリポジトリで公開しています:
GitHub リポジトリ: https://github.com/taimo3810/fal_api_mcp_server

fal.aiとは

fal.aiは、AIモデルをAPIとして提供しているプラットフォームです。特に画像生成や画像処理に特化したモデルを簡単に利用できるようにしています。本記事では、fal.aiが提供するFLUX.1 Proモデルを利用した、画像生成機能をClaudeに追加します。

https://fal.ai

筆者の実行環境

  • macOS
  • Python 3.10以上
  • fal.aiのAPIキー(サインアップが必要)
  • Claude Desktop

セットアップ手順

1. uvのインストール

macOSの場合:

brew install uv

Windowsの場合:

pip install uv

2. プロジェクトの作成

# プロジェクトディレクトリの作成
mkdir -p ~/Documents/fal_api_mcp_server
cd ~/Documents/fal_api_mcp_server

# 必要なファイル構造の作成
mkdir -p src/fal_api_mcp_server
touch pyproject.toml
touch src/fal_api_mcp_server/__init__.py
touch src/fal_api_mcp_server/server.py

3. 依存関係の設定

pyproject.tomlに以下の内容を記述します:

[project]
name = "fal-api-mcp-server"
version = "0.1.0"
description = "A MCP server project"
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
    "fal-client>=0.5.9",
    "httpx>=0.28.1",
    "mcp>=1.6.0",
    "python-dotenv>=1.1.0",
    "requests>=2.32.3"
]

[[project.authors]]
name = "your-name"
email = "your-email@example.com"

[build-system]
requires = [ "hatchling",]
build-backend = "hatchling.build"

[project.scripts]
fal-api-mcp-server = "fal_api_mcp_server:main"

依存関係をインストールします:

uv pip install -e .

サーバーの実装

1. init.py の作成

src/fal_api_mcp_server/init.py に以下の内容を記述します:

from . import server
import asyncio

def main():
    """パッケージのエントリポイント"""
    asyncio.run(server.main())

__all__ = ['main', 'server']

2. server.py の実装

src/fal_api_mcp_server/server.py に以下の内容を記述します:

import logging
import os
import base64
from collections.abc import Sequence
from typing import Any

from fal_client import async_client
import httpx
from dotenv import load_dotenv
from mcp.server import Server
from mcp.types import EmbeddedResource, ImageContent, TextContent, Tool
from pydantic import AnyUrl

# 環境変数の読み込み
load_dotenv()

# ログの準備
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("fal-image-server")

# fal.ai 認証情報の確認
if not os.getenv("FAL_KEY"):
    logger.warning("FAL_KEY environment variable is recommended.")

# サーバの準備
app = Server(name="fal-image-server")

# 利用可能なリソース一覧
@app.list_resources()
async def list_resources() -> list:
    return []

# 特定のリソースの取得
@app.read_resource()
async def read_resource(uri: AnyUrl) -> str:
    raise ValueError(f"This server does not provide resources like: {uri}")

# 利用可能なツール一覧
@app.list_tools()
async def list_tools() -> list[Tool]:
    return [
        Tool(
            name="generate_image",
            description="Generate an image based on a text prompt using fal.ai FLUX.1 [pro].",
            inputSchema={
                "type": "object",
                "properties": {
                    "prompt": {
                        "type": "string",
                        "description": "The text prompt to generate the image from.",
                    },
                    "image_size": {
                        "type": "string",
                        "description": "The desired image size. Defaults to 'landscape_4_3'.",
                        "enum": [
                            "square_hd", "square", "portrait_4_3",
                            "portrait_16_9", "landscape_4_3", "landscape_16_9",
                        ],
                        "default": "landscape_4_3",
                    },
                    "num_images": {
                        "type": "integer",
                        "description": "The number of images to generate.",
                        "default": 1,
                    },
                },
                "required": ["prompt"],
            },
        )
    ]

# ツールの呼び出し
@app.call_tool()
async def call_tool(name: str, arguments: Any) -> Sequence[TextContent | ImageContent | EmbeddedResource]:
    if name != "generate_image":
        raise ValueError(f"Unknown tool: {name}")

    # 引数の検証
    if not isinstance(arguments, dict) or "prompt" not in arguments:
        raise ValueError("Invalid arguments: 'prompt' is required.")

    # 引数を取得
    prompt = arguments["prompt"]
    image_size = arguments.get("image_size", "landscape_4_3")
    num_images = arguments.get("num_images", 1)

    logger.info(f"Generating image with prompt: '{prompt}', size: {image_size}")

    try:
        # fal.ai APIを呼び出して画像を生成
        result = await async_client.run(
            "fal-ai/flux-pro/v1.1",
            arguments={
                "prompt": prompt,
                "image_size": image_size,
                "num_images": num_images,
            },
        )

        # 結果の処理
        image_contents = []
        async with httpx.AsyncClient() as client:
            for image_info in result["images"]:
                if "url" in image_info and "content_type" in image_info:
                    image_url = image_info["url"]
                    content_type = image_info.get("content_type", "image/jpeg")
                    
                    # 画像データを取得
                    response = await client.get(image_url)
                    response.raise_for_status()
                    image_bytes = response.content
                    
                    # Base64エンコード
                    base64_image = base64.b64encode(image_bytes).decode("utf-8")
                    
                    # 画像コンテンツを追加
                    image_contents.append(
                        ImageContent(
                            type="image", data=base64_image, mimeType=content_type
                        )
                    )
                    logger.info(f"Downloaded image from {image_url}")

        if not image_contents:
            return [TextContent(type="text", text="No images were generated.")]

        return image_contents

    except Exception as e:
        logger.error(f"Error generating image: {e}")
        return [TextContent(type="text", text=f"Error generating image: {e}")]

# メイン
async def main():
    from mcp.server.stdio import stdio_server
    async with stdio_server() as (read_stream, write_stream):
        await app.run(read_stream, write_stream, app.create_initialization_options())

Claudeとの連携

1. Claude Desktopの設定

Claude Desktopの設定ファイルを編集します:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%/Claude/claude_desktop_config.json

以下の内容を追加します:

{
  "mcpServers": {
    "fal-api-mcp-server": {
      "command": "uv",
      "args": [
        "--directory",
        "/path/to/fal_api_mcp_server",  // プロジェクトの絶対パスに変更
        "run",
        "fal-api-mcp-server"
      ],
      "env": {
        "FAL_KEY": "your_fal_ai_api_key"  // fal.aiのAPIキーに変更
      }
    }
  }
}

2. Claude Desktopの再起動

設定を反映させるため、Claude Desktopを再起動します。

3. Claude Desktopで画像生成

まとめ

最近話題のMCPを活用することで、Claudeに画像生成機能を追加することができました。この方法は他のAPIサービスとの連携にも応用可能です。ぜひ試してみてください。

参考リンク

Discussion