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?

【技術選定編】Slack × Redmine × GitHub × LLMで開発アシスタントを作る

0
Last updated at Posted at 2026-09-08

コード基盤・技術選定編

前回の記事では、Slack・Redmine・GitHubに分散している開発情報をLLMで横断的に扱う「開発アシスタント」の構想について整理しました。

今回から、実際にOSSとして実装を始めていきます。

最初に決めるのは、AIのプロンプトやWeb画面ではありません。

その前に、どのようなコード基盤で作るのかを決めます。

今回の記事では、

  • 使用する言語
  • MCP Server
  • MCP SDK
  • DB
  • バリデーション
  • 外部サービスとの連携方法
  • 認証情報の管理
  • ディレクトリ構成

など、今後の実装の土台となる部分を決めていきます。

1. 今回作るもの

今回の開発アシスタントは、最終的には以下のような構成を目指します。

                    ┌──────────────┐
                    │     User     │
                    └──────┬───────┘
                           │
                           ▼
                    ┌──────────────┐
                    │     Agent    │
                    │     + LLM    │
                    └──────┬───────┘
                           │
                           ▼
                    ┌──────────────┐
                    │  MCP Client  │
                    └──────┬───────┘
                           │
                     tools/list
                     tools/call
                           │
                           ▼
              ┌────────────────────────┐
              │       MCP Server       │
              └───────────┬────────────┘
                          │
           ┌──────────────┼──────────────┐
           ▼              ▼              ▼
      ┌─────────┐    ┌─────────┐    ┌─────────┐
      │ Redmine │    │ GitHub  │    │  Slack  │
      │Integration│  │Integration│  │Integration│
      └────┬────┘    └────┬────┘    └────┬────┘
           │              │              │
           ▼              ▼              ▼
       Redmine         GitHub          Slack

今回の記事では、この中でも特に

MCP Server
Integration
Database
Credential

のコード基盤を作ることを目的にします。

RedmineやGitHubなどの具体的なAPI連携は次の記事以降で実装します。

2. 言語はTypeScript

まず、バックエンドの言語にはTypeScriptを採用します。

候補としては、

  • TypeScript
  • Python
  • C#

などが考えられます。

今回はTypeScriptを選択しました。

理由

一番大きいのは、今回のシステムがかなり「API・外部サービス連携寄り」だからです。

Redmine、GitHub、SlackなどのAPIを呼び出し、

API
 ↓
データ変換
 ↓
LLM
 ↓
Tool Call
 ↓
API

という処理を大量に行うことになります。

TypeScriptはこのようなWeb API中心のアプリケーションを作りやすく、さらにMCP周辺のエコシステムも利用できます。

また、将来的にWeb UIを作る場合にもTypeScriptをそのまま利用できます。

Backend
  TypeScript

Frontend
  TypeScript

とできるのもメリットです。

3. Node.js

TypeScriptの実行環境にはNode.jsを使用します。

今回の用途では、

  • HTTP Server
  • MCP Server
  • API Client
  • DB Client
  • AI SDK
  • WebSocket
  • CLI

などを扱う可能性があります。

Node.jsはこれらとの相性がよく、TypeScriptによる開発環境も整っています。

そのため、

TypeScript
    +
Node.js

を基本的な実行環境とします。

4. MCPを中心にする

今回のシステムで重要になるのが**MCP(Model Context Protocol)**です。

単純にLLMからRedmineやGitHubのAPIを直接呼び出す構成にはしません。

代わりに、

LLM / Agent
      │
      ▼
 MCP Client
      │
      ▼
 MCP Server
      │
      ├── Redmine
      ├── GitHub
      └── Slack

という構成にします。

MCPを利用することで、AIから利用できる機能を「Tool」として明確に分離できます。

例えばRedmineなら、

issue.get
issue.search
issue.create
issue.update

GitHubなら、

repository.get
issue.search
pullRequest.get
pullRequest.list

Slackなら、

channel.get
message.search
message.get

といったToolとして公開できます。

5. MCP Serverは公式SDKを利用する

MCP Serverは、独自プロトコルを作らず、MCPのTypeScript SDKを利用します。

ここは重要なポイントです。

以前は、

AIから呼び出すToolを独自のJSON形式で定義する

という方法も考えられます。

しかし今回はMCPそのものを利用します。

MCPでは、ClientがServerに対してtools/listを呼び出すことで、Serverが提供しているToolの情報を取得できます。

つまり、

MCP Server
    │
    │ tools/list
    ▼
MCP Client
    │
    ▼
Tool definitions
    │
    ▼
LLM

という流れになります。

これによって、Tool定義を別のファイルにコピーして管理する必要がありません。

6. Tool定義はMCP ServerをSource of Truthにする

ここで一つ設計上の判断をしました。

当初は、

tool.yml

のようなファイルを作り、そこからMCP Toolの定義を生成する方法も考えました。

しかし、現時点では採用しません。

理由は単純で、MCP Server自身がTool定義を公開できるからです。

例えば、

server.tool(
  "sample.add",
  "Add two numbers",
  {
    a: z.number(),
    b: z.number(),
  },
  async ({ a, b }) => {
    return {
      content: [
        {
          type: "text",
          text: String(a + b),
        },
      ],
    };
  },
);

と登録すれば、MCP Clientからtools/listでこのToolを取得できます。

そのため、

Tool registration
      ↓
MCP Server
      ↓
tools/list

をTool定義の基本的な流れとします。

余計なSSoTを増やさず、まずはMCPの標準機能に乗ることにしました。

7. Zodを使う

Toolの入力値のバリデーションにはZodを利用します。

例えば、

{
  a: z.number(),
  b: z.number(),
}

と定義できます。

ここで注意したいのは、

ZodがSSoTなのか?

という点です。

厳密には、今回の設計ではZodそのものを独立したSSoTとは考えません。

MCP Serverに登録したTool定義がMCP上のTool契約になります。

Zodは、

  • 入力値の型を定義する
  • 実行時にバリデーションする
  • ToolのInput Schemaを表現する

ために利用します。

つまり、

MCP Tool
  └── Input Schema
        └── Zod

という関係です。

8. DBにはPostgreSQL + Prisma

アプリケーションの永続データにはPostgreSQLを使用します。

そしてORMにはPrismaを採用します。

Application
     │
     ▼
   Prisma
     │
     ▼
 PostgreSQL

今回DBで管理する予定なのは、例えば以下です。

User
Workspace
Integration
Credential

将来的には、

Agent
Conversation
Task
Execution
Approval

なども追加する可能性があります。

9. Prisma SchemaをDBモデルのSSoTにする

DBについてはPrisma Schemaを中心に管理します。

例えば、

model User {
  id        String   @id @default(cuid())
  email     String   @unique
  createdAt DateTime @default(now())
}

のように定義します。

ここから、

  • DB migration
  • Prisma Client
  • TypeScriptの型

を生成できます。

そのため、

schema.prisma
      │
      ├── Migration
      └── Prisma Client

という形になります。

ToolについてはMCP ServerのTool登録を中心にし、DBについてはPrisma Schemaを中心にする。

それぞれの領域で自然なSSoTを使い分けます。

10. 外部サービスはIntegrationとして分離する

今回の設計で特に重要なのがIntegrationです。

Redmine、GitHub、SlackをMCP Serverの中に直接書いてしまうと、

MCP Server
 ├── Redmine処理
 ├── GitHub処理
 ├── Slack処理
 ├── Credential処理
 ├── Agent処理
 └── ...

となり、どんどん巨大化していきます。

そこで、外部サービスをIntegrationとして分離します。

Integrations
 ├── Redmine
 ├── GitHub
 └── Slack

それぞれが、

  • API Client
  • 認証
  • データ変換
  • MCP Tool登録

などを担当します。

11. SDKを利用する

外部サービスとの通信には、基本的に各サービスのSDKを利用します。

例えば、

Redmine
   ↓
Redmine SDK / HTTP Client

GitHub
   ↓
GitHub SDK

Slack
   ↓
Slack SDK

という形です。

OpenAPIから全API Clientを生成する方法もありますが、今回はまずSDKを優先します。

理由は、単純なHTTP Clientを生成することが目的ではなく、

  • 認証
  • Pagination
  • API固有の型
  • API固有のエラー
  • データ変換

なども扱う必要があるためです。

必要になれば、後からOpenAPIベースのClient生成を検討します。

12. CredentialStoreを抽象化する

もう一つ重要なのが認証情報です。

Redmine API KeyやGitHub Token、Slack Tokenなどをどこに保存するかという問題があります。

これをIntegration側に直接書かせないようにします。

例えば、

interface CredentialStore {
  get(
    userId: string,
    integrationId: string,
  ): Promise<Credential | null>;

  save(
    userId: string,
    integrationId: string,
    credential: Credential,
  ): Promise<void>;
}

のような抽象を用意します。

Integration側は、

「GitHub Tokenが欲しい」

と要求するだけにします。

どこに保存されているかは知りません。

13. 最初はDB実装で十分

CredentialStoreを抽象化するからといって、最初からAWS Secrets ManagerやVaultなどを導入するわけではありません。

まずはPostgreSQLに暗号化して保存します。

CredentialStore
      │
      ▼
DatabaseCredentialStore
      │
      ▼
 PostgreSQL

暗号化にはNode.jsのcryptoなどを利用します。

重要なのは、

Integration
     ↓
CredentialStore
     ↓
保存先

という依存関係にすることです。

将来的に、

DatabaseCredentialStore
AwsSecretsManagerCredentialStore
AzureKeyVaultCredentialStore
VaultCredentialStore

などに差し替えられるようにします。

14. IntegrationはCredentialの保存方法を知らない

例えばGitHub Integrationがあるとします。

悪い例は、

const token = process.env.GITHUB_TOKEN;

のようにIntegration自身が環境変数を直接読むことです。

今回作りたいのは複数ユーザーが利用するアプリケーションです。

そのため、

User A → GitHub Token A
User B → GitHub Token B

という状態を扱える必要があります。

そこで、

GitHub Integration
       │
       ▼
 CredentialStore
       │
       ▼
 User-specific credential

とします。

これによってIntegrationは、

「誰のCredentialなのか」

という情報をもとに必要な認証情報を取得できます。

15. Workspaceという単位を作る

もう一つ、今後重要になる概念がWorkspaceです。

例えば、

Workspace: Project A

に対して、

Redmine
  Project #123

GitHub
  organization/repository-a

Slack
  #project-a

を紐付けます。

イメージとしては、

Workspace
 ├── Redmine Project
 ├── GitHub Repository
 └── Slack Channel

です。

これによってAIは、

「Project Aについて調べて」

という依頼を受けたとき、

Redmine
   ↓
問い合わせ・チケット

GitHub
   ↓
コード・Issue・PR

Slack
   ↓
開発者間の会話

を横断して調査できるようになります。

Workspaceについては、この後の実装記事で詳しく扱います。

16. 現時点のディレクトリ構成

ここまでの設計を踏まえて、最初のディレクトリ構成を以下のようにします。

src/
├── agent/
│
├── mcp/
│   ├── server.ts
│   └── tools/
│
├── integrations/
│   ├── redmine/
│   │   ├── client.ts
│   │   └── tools.ts
│   │
│   ├── github/
│   │   ├── client.ts
│   │   └── tools.ts
│   │
│   └── slack/
│       ├── client.ts
│       └── tools.ts
│
├── credentials/
│   ├── credential-store.ts
│   └── database-credential-store.ts
│
├── workspace/
│
├── db/
│
├── config/
│
└── index.ts

まだ実装しないディレクトリもあります。

最初は、

mcp/
credentials/
db/

あたりから作っていきます。

17. 最初のMCP Server

まずはMCP Serverを起動できる状態にします。

最初からRedmineを実装するのではなく、単純なToolを一つ作ります。

例えば、

sample.add

です。

sample.add
    a: number
    b: number

    ↓

a + b

これだけです。

一見すると意味のないToolですが、最初の確認としては非常に重要です。

18. 動作確認

今回確認したいのは、

MCP Client
    │
    │ tools/list
    ▼
MCP Server
    │
    ▼
sample.add

そして、

MCP Client
    │
    │ tools/call
    │
    │ a = 1
    │ b = 2
    ▼
MCP Server
    │
    ▼
3

という一連の流れです。

MCP Inspectorなどを利用して確認します。

ここまで動けば、

「MCP ServerをAIから利用できる基盤ができた」

と言えます。

19. 最終的にはLLMがToolを選択する

ここでMCP ServerとAI Agentの関係も整理しておきます。

MCP Serverが自分で、

「このToolを使おう」

と判断するわけではありません。

流れは、

① MCP Client
      │
      │ tools/list
      ▼
② MCP Server
      │
      ▼
   Tool一覧
      │
      ▼
③ Agent / LLM
      │
      │ 「このToolを使おう」
      ▼
④ MCP Client
      │
      │ tools/call
      ▼
⑤ MCP Server
      │
      ▼
⑥ Tool実行

です。

つまりMCP Serverは、

AIが利用できる「手」を提供する役割

と考えると分かりやすいです。

AIが頭脳、MCP Serverが手足というイメージです。

20. 今回の技術選定まとめ

ここまでの選定をまとめます。

項目 採用
言語 TypeScript
Runtime Node.js
AI Tool Protocol MCP
MCP SDK TypeScript MCP SDK
Validation Zod
Database PostgreSQL
ORM Prisma
外部API 各サービスSDK / HTTP Client
Credential管理 CredentialStore
初期Credential保存先 PostgreSQL
AI Model 抽象化して切り替え可能にする
UI 後で実装
Agent 後で実装

特に重要なのは、

TypeScript
   │
   ├── MCP Server
   │
   ├── Integration
   │
   ├── CredentialStore
   │
   ├── Workspace
   │
   └── Agent

という責務分離です。

21. なぜ最初から全部作らないのか

今回、あえてRedmine・GitHub・Slackをまだ実装していません。

理由は、最初から全部実装すると、

MCP
Redmine
GitHub
Slack
DB
認証
Agent
LLM
UI

が同時に絡み始めるからです。

そうすると、

何が原因で動かないのか分からない

という状態になりやすいです。

まず、

MCP Server
    ↓
sample.add
    ↓
MCP Client

という最小構成を動かします。

そこから、

Redmine
    ↓
MCP Tool

を追加します。

次に、

GitHub
    ↓
MCP Tool

を追加。

その後、

Slack
    ↓
MCP Tool

を追加します。

この順番で一つずつ積み上げていきます。

22. 次回はRedmine Integration

今回でコード基盤の方向性が決まりました。

次回はいよいよ最初のIntegrationを実装します。

対象はRedmineです。

まずは読み取り専用から始めます。

例えば、

issue.get
issue.search
project.get

などのToolを作ります。

最終的には、

LLM
 ↓
MCP Client
 ↓
MCP Server
 ↓
Redmine Integration
 ↓
Redmine API

というところまで実際に動かします。

そして、

「Redmineの問い合わせをAIが取得できる」

状態を作ります。

ここから、構想していた「開発アシスタント」が少しずつ実体になっていきます。

まとめ

今回は、開発アシスタントを実装するためのコード基盤と技術選定を行いました。

ポイントは以下です。

  • バックエンドはTypeScript + Node.js
  • AIとのTool連携にはMCPを利用
  • MCP Serverは公式SDKを利用
  • Tool定義はMCP Serverの登録を中心にする
  • ZodでTool入力をバリデーションする
  • DBはPostgreSQL + Prisma
  • Redmine/GitHub/SlackはIntegrationとして分離する
  • 外部サービスとの通信にはSDKを優先する
  • CredentialはCredentialStoreとして抽象化する
  • WorkspaceでRedmine/GitHub/Slackを関連付ける
  • 最初は最小のMCP Serverから始める
  • 実装を進めながら設計を検証していく

特に今回は、最初から完璧なアーキテクチャを作るのではなく、

「実際に動かしながら、必要な抽象化だけ追加する」

という方針で進めます。

次回は、実際にRedmine Integrationを実装していきます。

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?