1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MCPサーバーでデータベースとClaude Codeを直接つなぐ — SQLite連携ハンズオン

1
Posted at

「DBからデータ取って」が一言で済む世界

> 今月の新規ユーザー数を教えて
→ 今月の新規ユーザーは 47名です(前月比 +12%)

Claude Codeにこう聞くだけで、データベースに直接クエリを実行して結果を返す。MCPサーバーを作れば、これが実現できる。

完成形の構成

Claude Code ←→ MCP Server ←→ SQLite Database
                (TypeScript)

MCPサーバーが中間に入り、Claude Codeからのリクエストを受けてDBにクエリを実行する。

Step 1: プロジェクト作成

mkdir mcp-db-server && cd mcp-db-server
npm init -y
npm install @modelcontextprotocol/sdk better-sqlite3 zod
npm install -D typescript @types/node @types/better-sqlite3
npx tsc --init

Step 2: データベース初期化

// src/db.ts
import Database from "better-sqlite3";

const db = new Database("./data.db");

// テーブル作成
db.exec(`
  CREATE TABLE IF NOT EXISTS users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    email TEXT NOT NULL,
    created_at TEXT DEFAULT (datetime('now'))
  )
`);

export default db;

Step 3: MCPサーバー実装

// src/index.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import db from "./db.js";

const server = new McpServer({
  name: "db-server",
  version: "1.0.0",
});

// Tool: ユーザー検索
server.tool(
  "search-users",
  "ユーザーをキーワードで検索する",
  {
    query: z.string().describe("検索キーワード(名前またはメール)"),
    limit: z.number().default(10).describe("取得件数"),
  },
  async ({ query, limit }) => {
    const rows = db.prepare(
      "SELECT * FROM users WHERE name LIKE ? OR email LIKE ? LIMIT ?"
    ).all(`%${query}%`, `%${query}%`, limit);
    return {
      content: [{ type: "text", text: JSON.stringify(rows, null, 2) }],
    };
  }
);

// Tool: ユーザー追加
server.tool(
  "add-user",
  "新しいユーザーを追加する",
  {
    name: z.string().describe("ユーザー名"),
    email: z.string().email().describe("メールアドレス"),
  },
  async ({ name, email }) => {
    const result = db.prepare(
      "INSERT INTO users (name, email) VALUES (?, ?)"
    ).run(name, email);
    return {
      content: [{ type: "text", text: `ユーザーを追加しました(ID: ${result.lastInsertRowid})` }],
    };
  }
);

// Tool: 統計情報
server.tool(
  "get-stats",
  "ユーザーの統計情報を取得する",
  {},
  async () => {
    const total = db.prepare("SELECT COUNT(*) as count FROM users").get() as { count: number };
    const thisMonth = db.prepare(
      "SELECT COUNT(*) as count FROM users WHERE created_at >= date('now', 'start of month')"
    ).get() as { count: number };
    return {
      content: [{
        type: "text",
        text: `総ユーザー数: ${total.count}\n今月の新規: ${thisMonth.count}`,
      }],
    };
  }
);

// Resource: スキーマ情報
server.resource(
  "schema://tables",
  "データベースのテーブル構造",
  "application/json",
  async () => {
    const tables = db.prepare(
      "SELECT sql FROM sqlite_master WHERE type='table'"
    ).all();
    return {
      contents: [{ text: JSON.stringify(tables, null, 2) }],
    };
  }
);

const transport = new StdioServerTransport();
await server.connect(transport);

Step 4: Claude Codeに登録

.claude/settings.json:

{
  "mcpServers": {
    "db-server": {
      "command": "node",
      "args": ["./dist/index.js"]
    }
  }
}

動作確認

Claude Codeを起動して話しかけるだけ。

> ユーザー「田中」を検索して
→ search-usersツールを実行... 3件のユーザーが見つかりました

> 今月の統計を教えて
→ get-statsツールを実行... 総ユーザー数: 156、今月の新規: 23

> このDBのテーブル構造を見せて
→ schema://tablesリソースを参照... CREATE TABLE users (...)

セキュリティの注意点

  • 読み取り専用にしたい場合: better-sqlite3readonly: true オプションを使う
  • SQLインジェクション対策: プリペアドステートメントを必ず使う(上記コードは対策済み)
  • 本番DBに接続する場合: 読み取り専用のレプリカを使うこと

さらに詳しく

📘 Claude Code × MCP サーバー開発入門 — 外部ツール連携で生産性を10倍にする実践ガイド
第5章でデータベース連携MCPサーバーの詳細な実装パターンを解説。認証・スキーマ公開・バッチ処理まで。

📘 CLAUDE.md設計パターン
MCPサーバーをCLAUDE.mdから効果的に活用する設定パターン。

📕 全書籍一覧は こちら

1
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?