0
0

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サーバーを自作してNotion・Slack・社内DBを繋いだら、Claude Codeが『社内事情を知るエージェント』になった話

0
Posted at

社外のコードは書けるのに社内のコンテキストを何も知らない──Claude Codeの最大の弱点を、MCPサーバー3つで埋めた1ヶ月の記録。

結論:MCPサーバー3つで「前提説明プロンプト」が72%減った

Claude Codeは優秀です。コードの生成・リファクタリング・テスト作成、どれも高い精度で応えてくれます。

しかし、社内のことを何も知りません。

  • 「うちのユーザーテーブルのカラム定義はこうで…」
  • 「この設計判断はADRの#023に書いてあるんだけど…」
  • 「先月Slackで議論した結果、この方針になったんだけど…」

毎回これを説明するプロンプトを書いていました。チームメンバー5人が、それぞれ独自の「前提説明テンプレ」を持っている状態。これは明らかにおかしい。

そこで MCP(Model Context Protocol)サーバーを3つ自作 し、Claude Codeから社内のNotion・Slack・PostgreSQLに直接アクセスできるようにしました。結果、前提説明プロンプトの文字数が平均72%削減。Claude Codeが「社内事情を知るエージェント」に変わりました。

環境・前提条件

項目 バージョン・サービス
Claude Code 最新版(2025年6月時点)
MCP SDK @modelcontextprotocol/sdk 1.x
Node.js v20 LTS
Notion API 2022-06-28
Slack API Web API + Events API
DB PostgreSQL 16
実行環境 macOS(ローカル開発)/ Docker(チーム共有)

MCPの基本概念についてはAnthropicの公式ドキュメント(後述の参考リンク)を前提とします。

全体アーキテクチャ

まず完成形の構成を示します。

ポイントは3つです。

  1. 各MCPサーバーは独立したプロセスとして動作し、Claude Codeの設定ファイルで束ねる
  2. セキュリティレイヤーをMCPサーバー内部に実装する(外部APIとClaude Codeの間にフィルタを挟む)
  3. 各サーバーはstdio通信で接続し、ローカルで完結する

Step1:Notion MCPサーバーの構築

目的

設計ドキュメント(ADR・API仕様・ER図の説明文)をClaude Codeから参照可能にします。

実装のポイント

Notion MCPサーバーでは、以下の3つのツールを公開しました。

// tools の定義(抜粋)
const tools = [
  {
    name: "search_design_docs",
    description: "設計ドキュメントをキーワード検索する",
    inputSchema: {
      type: "object",
      properties: {
        query: { type: "string", description: "検索キーワード" },
        doc_type: { 
          type: "string", 
          enum: ["adr", "api_spec", "architecture"],
          description: "ドキュメントの種別"
        }
      },
      required: ["query"]
    }
  },
  {
    name: "get_page_content",
    description: "指定IDのNotionページの内容を取得する",
    inputSchema: {
      type: "object",
      properties: {
        page_id: { type: "string" }
      },
      required: ["page_id"]
    }
  },
  {
    name: "list_recent_adrs",
    description: "直近N件のADR(Architecture Decision Record)一覧を返す",
    inputSchema: {
      type: "object",
      properties: {
        limit: { type: "number", default: 10 }
      }
    }
  }
];

claude_desktop_config.json への登録

{
  "mcpServers": {
    "notion-docs": {
      "command": "node",
      "args": ["./mcp-servers/notion/dist/index.js"],
      "env": {
        "NOTION_API_KEY": "secret_xxx",
        "NOTION_ROOT_PAGE_ID": "xxx"
      }
    }
  }
}

これだけでClaude Codeに /search_design_docs と打つ必要すらなく、「うちのユーザー認証の設計方針を教えて」と聞くだけで、ADRを検索して回答してくれるようになりました。

Step2:Slack MCPサーバーの構築

目的

過去の技術議論スレッドを検索し、「なぜこの実装になったのか」という経緯をClaude Codeに注入します。

実装のポイント

const tools = [
  {
    name: "search_tech_threads",
    description: "技術チャンネルの過去スレッドを検索する",
    inputSchema: {
      type: "object",
      properties: {
        query: { type: "string" },
        channel_names: {
          type: "array",
          items: { type: "string" },
          description: "検索対象チャンネル名(例: ['dev-backend', 'dev-infra'])"
        },
        days_back: { type: "number", default: 90 }
      },
      required: ["query"]
    }
  }
];

検索対象チャンネルをホワイトリスト制にしているのが重要です。#general#random、HR関連チャンネルは絶対に含めません。これについては後述の「想定外だったこと②」で詳しく書きます。

Step3:社内DB MCPサーバーの構築

目的

テーブル定義(スキーマ)とサンプルデータをClaude Codeから参照し、SQLやマイグレーションの生成精度を上げます。

権限設計が最も重要

具体的には以下のルールを設けました。

ルール 内容
接続ユーザー mcp_readonly (SELECT権限のみ)
データ取得上限 1テーブルあたり最大5行
個人情報マスキング email, phone, name カラムは *** に置換
除外テーブル audit_logs, payment_*, user_secrets
クエリ制限 任意SQLの実行は不可。事前定義したツールのみ
// サンプルデータ取得時のマスキング処理(抜粋)
const MASKED_COLUMNS = ["email", "phone", "name", "first_name", "last_name"];

function maskRow(row: Record<string, unknown>): Record<string, unknown> {
  const masked = { ...row };
  for (const col of MASKED_COLUMNS) {
    if (col in masked) {
      masked[col] = "***MASKED***";
    }
  }
  return masked;
}

任意SQLを実行させないことが最大のポイントです。MCPサーバーが提供するのは get_table_schemaget_sample_datalist_tables の3ツールだけ。Claude Codeが「このSQLを実行して」と言っても、MCPサーバー側で弾きます。

想定外だったこと①:Notionのトークン圧迫問題

何が起きたか

Notionのページにはネストされたブロックがあり、1ページ取得するだけで数千〜数万トークンになることがありました。設計ドキュメントは図やテーブルが多く、特に肥大化しがちです。

Claude Codeのコンテキストウィンドウを圧迫し、肝心のコード生成に使えるトークンが減る本末転倒な状態に。

解決策:要約レイヤーを挟む

実装としては以下のアプローチを取りました。

  1. セクション見出し抽出:H1〜H3の見出しだけを抜き出した「目次」を先に返す
  2. セクション単位取得:Claude Codeが必要なセクションだけを追加取得できるツールを用意
  3. キャッシュ:同じページへのリクエストは1時間キャッシュ
// ページ内容を構造化して返す
function structurePage(blocks: NotionBlock[]): StructuredPage {
  const sections = extractSections(blocks); // H1-H3で分割
  const toc = sections.map(s => ({
    id: s.id,
    heading: s.heading,
    level: s.level,
    estimatedTokens: estimateTokens(s.content)
  }));
  
  return {
    toc,
    totalEstimatedTokens: toc.reduce((sum, s) => sum + s.estimatedTokens, 0),
    sections // 個別取得用
  };
}

これにより、1回のリクエストで消費するトークンが平均85%削減できました。

想定外だったこと②:Slack MCPで機密情報が漏れかけた

何が起きたか

テスト中、Claude Codeに「認証周りの過去の議論を探して」と依頼したところ、Slackの検索結果に本番環境のAPIキーが平文で貼られたスレッドが含まれていました。

MCPサーバーがそのままレスポンスを返していたため、Claude CodeのコンテキストにAPIキーが入ってしまいました。

解決策:多層フィルタリング

最終的に以下の3層でフィルタリングする設計にしました。

フィルタ内容 実装場所
第1層 チャンネルホワイトリスト MCPサーバー起動時設定
第2層 正規表現による秘匿情報検出 レスポンス整形時
第3層 特定キーワードを含むメッセージの除外 レスポンス整形時
// 第2層:秘匿情報の検出と置換
const SECRET_PATTERNS = [
  /(?:api[_-]?key|secret|token|password)\s*[:=]\s*\S+/gi,
  /(?:sk-|pk_|rk_)[a-zA-Z0-9]{20,}/g,           // APIキー形式
  /(?:ghp_|github_pat_)[a-zA-Z0-9]{36,}/g,        // GitHubトークン
  /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g, // メールアドレス
  /xox[baprs]-[a-zA-Z0-9-]+/g,                    // Slackトークン
];

function sanitizeMessage(text: string): string {
  let sanitized = text;
  for (const pattern of SECRET_PATTERNS) {
    sanitized = sanitized.replace(pattern, "[REDACTED]");
  }
  return sanitized;
}

このフィルタリング設計はNotion MCPサーバーにも横展開しました。Notionにもたまに秘匿情報が書かれていることがあるためです。

効果測定:導入前後の比較

導入から3週間後、チームメンバー5人の利用データを集計しました。

指標 導入前 導入後 変化
前提説明プロンプトの平均文字数 1,847文字 512文字 -72.3%
Claude Codeへの1タスクあたり平均ターン数 6.2回 3.8回 -38.7%
「スキーマ教えて」系の質問回数/日 12.4回 1.1回 -91.1%
MCPツール呼び出し回数/日(チーム計) - 47.3回 -

特に「スキーマ教えて」系の質問がほぼゼロになったのは大きいです。Claude Codeが自分でDB MCPサーバーに問い合わせて、テーブル定義を取得してからコードを書いてくれます。

体感としても「Claude Codeが同じチームのメンバーになった」感覚です。「うちの orders テーブルに status カラムを追加するマイグレーション書いて」と言えば、既存のスキーマを確認し、ADRの命名規則を参照し、適切なマイグレーションファイルを生成してくれます。

MCPは「Claude Codeに記憶を与える」インフラである

MCPサーバーの本質は、APIのラッパーではありません。

組織の暗黙知を、AIが参照可能な形に構造化するインフラです。

これまでClaude Codeは「優秀だけど新入社員」でした。何を聞いてもそれなりの回答をくれるけど、社内の文脈を知らない。毎回オンボーディングが必要。

MCPサーバーを3つ立てたことで、Claude Codeは「社内事情を知っているシニアエンジニア」に近づきました。完璧ではありませんが、少なくとも「うちのDBのカラム名」や「なぜこの設計にしたか」を自分で調べてくれます。

まとめ

  • MCPサーバーは「AIに組織の記憶を与える」インフラであり、Notion・Slack・DBの3つを繋ぐだけで前提説明プロンプトが72%減った
  • セキュリティ設計は必須。特にSlack連携では秘匿情報フィルタリング、DB連携ではREAD ONLY権限+データマスキングを多層で実装すべき
  • トークン圧迫への対策として、Notionのようなリッチコンテンツは「目次→セクション単位取得」の2段階アーキテクチャが有効

参考リンク

0
0
0

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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?