adbコマンド覚えられなくても気にしない。自作MCPサーバーでAIにお任せ!

(本記事は OPTiM TECH BLOG Advent Calendar 2025 Day 23 の記事です。)

はじめに

こんにちは。サービス開発統括本部の三好です。
私はOPTiM BizというサービスのAndroidエージェント開発と問い合わせ業務を担当しています。
正直なところ、Android開発の経験はまだ浅く、adbコマンドを覚えるのに苦労していました。
そこで、近年話題のMCPサーバーを自作し、AIにadbコマンドの実行をお任せすることにしました。

この記事では、その実装手順から、Android Studioでの動作確認までを紹介します。

MCPサーバーとは

MCPサーバーは、AIに外部やローカルのデータ・機能を提供するための仕組みです。
通常、AIは学習済みの知識に依存しますが、MCPサーバーを介することで次のことが可能になります。
- 外部システムとの連携:データベースやWeb APIへのアクセス
- ローカルリソースの利用:ファイル、デバイス、アプリケーションへの操作
これにより、AIがより高度な応答やアクションを行えるようになります。

参考: https://cloud.google.com/discover/what-is-model-context-protocol

MCPサーバーを自作

使用するライブラリ

mark3labs/mcp-goを選定しました。
これはGo製MCP SDKの中で現在最もスター数が多く、活発にメンテナンスされているためです。
また、Go言語を採用した理由としては「クロスコンパイルの容易さ」にあります。
ランタイム不要でシングルバイナリを生成できるため、私のWindows環境でも同僚のMac環境でも手軽に配布・実行でき、運用コストを下げることができます。

実装

コード全体は下記の通りです。 重要な箇所をピックアップして解説します。

package main

import (
    "context"
    "fmt"
    "os/exec"

    "github.com/mark3labs/mcp-go/mcp"
    "github.com/mark3labs/mcp-go/server"
)

func main() {
    s := server.NewMCPServer(
        "adb-mcp-server",
        "0.0.1",
    )

    executeAdbTool := mcp.NewTool(
        "execute-adb",
        mcp.WithDescription("adbコマンドを実行します"),
        mcp.WithString(
            "subCommand",
            mcp.Required(),
            mcp.Description("adbコマンドで実行するサブコマンドです"),
        ),
    )

    s.AddTool(executeAdbTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
        subCommand, err := request.RequireString("subCommand")
        if err != nil {
            return mcp.NewToolResultError(err.Error()), nil
        }

 
        cmd := exec.Command("adb", subCommand)
        output, err := cmd.CombinedOutput()
        if err != nil {
            return mcp.NewToolResultError(err.Error()), nil
        }

        return mcp.NewToolResultText(string(output)), nil
    })

    if err := server.ServeStdio(s); err != nil {
        fmt.Printf("Server error: %v\n", err)
    }
}

サーバーを生成

まずは server.NewMCPServer でサーバーのインスタンスを生成します。
ここで指定した名前(adb-mcp-server)とバージョンは、クライアント側がサーバーを識別するために使われます。

ツールを定義

次に、AIに提供する機能を「ツール」として定義します。
mcp.NewTool を使い、execute-adbという ツールを定義します。
重要なのは mcp.WithString の部分です。ここでAIに対して「subCommand という名前で、実行したいadbのサブコマンド(例: devices, shellなど)を文字列で渡してください」と定義しています。

ツールを実装

s.AddTool で、定義したツールが呼ばれた際の実際の処理を記述します。
1. request.RequireString("subCommand") でAIから送られてきた引数を取得し、
2. exec.Command("adb", subCommand) でOSの adb コマンドとして実行します。
3. cmd.CombinedOutput() で実行結果(標準出力と標準エラー出力)を取得し、それをテキストとしてAIに返却しています。

サーバーを起動

最後に server.ServeStdio(s) でサーバーを起動します。
MCPサーバーは標準入出力を通じてMCPクライアントと通信を行うため、待ち受け状態で起動します。

ビルド

私のPCはWindowsですがWSLで開発しているため、GOOSを指定しビルドを実行します。

$ GOOS=windows GOARCH=amd64 go build -o adb-mcp-server.exe

Android StudioのGemini Code Assistから使ってみる

準備

生成したバイナリをAndroid StudioのGemini Code AssistのMCPサーバーとして登録します。
「Setting」→「Tools」→「Gemini」→「MCP Servers」と遷移すると、設定用のjsonが表示されるので、以下の内容で修正してください。

{
    "mcpServers": {
      "adb-mcp-server": {
        "command": "{{ ビルド済みexeファイルのパス }}",
        "args": [],
        "env": {}
      }
    }
}

これで、Gemini Code Assistがadb-mcp-serverをツールとして認識してくれます。

手始めに

まずは簡単なコマンドで接続確認をしてみましょう。以下のようなプロンプトを入力します。

私: 「現在接続しているデバイスはありますか?」

すると、Geminiはexecute-adb ツールを選択し、サブコマンドに devices を指定して実行してくれます。
返答も期待通りの内容でした。

少し難しいお願い

次はアプリに付与されているランタイムパーミッションを取得してみたいと思います。
アプリは弊社のOPTiM Bizを指定し、以下のようなプロンプトを入力します。

私:  「jp.co.optim.bizagent.biz3.storeに付与されているランタイムパーミッションを列挙してください」

今回は、サブコマンドに shell dumpsys package jp.co.optim.bizagent.biz3.store を指定して実行してくれました。
dumpsysの出力をGeminiがいい感じに解釈してくれるので、自分で確認する手間が省けました。

まとめ

mark3labs/mcp-goを使って、少ないコードで実用的なMCPサーバーを構築できました。
ただし、現状の実装ではadbコマンドの実行に制限がなく、データ消去などのリスクが存在します。
そのために、ホワイトリスト方式で実行可能なコマンドを厳しく管理するなど、制御機構の導入が必要そうです。
今後は実際の運用を通じてフィードバックを集め、より安全で便利なものに改善していきたいです。

今回はMCPサーバーでadbコマンドを自動化しましたが、この他にもOPTiMでは様々な技術領域でAIの活用を推進しています。
あなたのアイデアと技術で、開発現場や社会に大きなインパクトを与えませんか?
少しでも興味をお持ちいただけたら、ぜひ採用ページをご覧ください。

www.optim.co.jp