🧠

AgentのToolを公開MCPから自作に変えたらToken使用料が9割削減した

に公開

AgentのToolを公開されているMCPから自作に変更したら、Token使用料が9割削減できた話を書きます。

背景

現在ko☆shi(こーし)という引き継ぎを自動化するAgentを社内プロダクトとして開発しています。

構成図

このAgentはGitHub、Asana、Slackをデータソースとして使用しており、Strands AgentsのMCP機能を使用して、各サービスの情報を取得しています。ユースケースとしては、最新のPRやIssueがどのタスクと紐づいているかを確認したり、そのタスクについてどういう意思決定がされているかを把握したりしています。

いつも通りko☆shiを使用していると、Token使用料がかなり高額になっていることに気づきました。調査したところ、GitHubの最新のPRを確認しているだけにも関わらず、かなりのTokenが消費されていました。

お金溶けた

画像のように、1回の呼び出しで約34万Tokenも使用しています。さすがに何かがおかしいと感じ、詳しく調査しました。

原因

原因を調査するために、トレースログを確認しました。

トレース

最新のPRを取得するために、github_assistantというAgentが呼び出されています。このAgentはGitHub MCPを使用しており、PRを取得するためにlist_pull_requestsというToolを呼び出しています。Toolの呼び出しとしては全く問題ないのですが、LLMに渡しているToken数が非常に多いです。

トレースその2

LLMのinputを確認すると、以下のようなjsonデータが渡されていました。

{
    "id": 1234567890,
    "number": 520,
    "state": "closed",
    "locked": false,
    "title": "fix: サンプルのPRタイトル",
    "body": "PRの説明文がここに入ります",
    "created_at": "2025-11-28T08:51:05Z",
    "updated_at": "2025-11-28T08:52:06Z",
    "closed_at": "2025-11-28T08:52:04Z",
    "merged_at": "2025-11-28T08:52:04Z",
    "user": {
        "login": "username",
        "id": 12345678,
        "node_id": "XXXXXXXX",
        "avatar_url": "https://avatars.githubusercontent.com/u/12345678?v=4",
        "gravatar_id": "",
        "url": "https://api.github.com/users/username",
        "html_url": "https://github.com/username",
        "followers_url": "https://api.github.com/users/username/followers",
        "following_url": "https://api.github.com/users/username/following{/other_user}",
        "gists_url": "https://api.github.com/users/username/gists{/gist_id}",
        "starred_url": "https://api.github.com/users/username/starred{/owner}{/repo}",
        "subscriptions_url": "https://api.github.com/users/username/subscriptions",
        "organizations_url": "https://api.github.com/users/username/orgs",
        "repos_url": "https://api.github.com/users/username/repos",
        "events_url": "https://api.github.com/users/username/events{/privacy}",
        "received_events_url": "https://api.github.com/users/username/received_events",
        "type": "User",
        "site_admin": false
    },
    "draft": false,
    "url": "https://api.github.com/repos/org-name/repo-name/pulls/520",
    "html_url": "https://github.com/org-name/repo-name/pull/520",
    "issue_url": "https://api.github.com/repos/org-name/repo-name/issues/520",
    "statuses_url": "https://api.github.com/repos/org-name/repo-name/statuses/abc123def456",
    "diff_url": "https://github.com/org-name/repo-name/pull/520.diff",
    "patch_url": "https://github.com/org-name/repo-name/pull/520.patch",
    "commits_url": "https://api.github.com/repos/org-name/repo-name/pulls/520/commits",
    "comments_url": "https://api.github.com/repos/org-name/repo-name/issues/520/comments",
    "review_comments_url": "https://api.github.com/repos/org-name/repo-name/pulls/520/comments",
    "review_comment_url": "https://api.github.com/repos/org-name/repo-name/pulls/comments{/number}",
    "assignee": {
        "login": "username",
        "id": 12345678,
        "node_id": "XXXXXXXX",
        "avatar_url": "https://avatars.githubusercontent.com/u/12345678?v=4",
        "url": "https://api.github.com/users/username",
        "html_url": "https://github.com/username",
        "type": "User",
        "site_admin": false
    },
    "assignees": [],
    "requested_reviewers": [],
    "author_association": "MEMBER",
    "node_id": "PR_XXXXXXXXXXXX",
    "merge_commit_sha": "abc123def456789",
    "_links": {
        "self": { "href": "https://api.github.com/repos/org-name/repo-name/pulls/520" },
        "html": { "href": "https://github.com/org-name/repo-name/pull/520" },
        "issue": { "href": "https://api.github.com/repos/org-name/repo-name/issues/520" },
        "comments": { "href": "https://api.github.com/repos/org-name/repo-name/issues/520/comments" },
        "review_comments": { "href": "https://api.github.com/repos/org-name/repo-name/pulls/520/comments" },
        "review_comment": { "href": "https://api.github.com/repos/org-name/repo-name/pulls/comments{/number}" },
        "commits": { "href": "https://api.github.com/repos/org-name/repo-name/pulls/520/commits" },
        "statuses": { "href": "https://api.github.com/repos/org-name/repo-name/statuses/abc123def456" }
    },
    "head": {
        "label": "org-name:feature-branch",
        "ref": "feature-branch",
        "sha": "abc123def456789"
    },
    "base": {
        "label": "org-name:main",
        "ref": "main",
        "sha": "def456abc789123"
    }
}

今回の場合、2つのリポジトリから10件ずつPRを取得しているため、上記のjsonデータが20件分LLMに渡されていました。これにより、LLMのinputが非常に大きくなり、Token使用料が高額になっていたのです。

対応

GitHub MCPは本来必要ない情報まで取得してしまうため、MCPのToolを使用することをやめて、自作で必要な情報だけを取得する関数を作成しました。今回必要なデータは以下のとおりです。

フィールド 説明
number PR番号(#64 など)
title PRのタイトル
state PRの状態(open, closed, merged)
user PR作成者のGitHubユーザー名
head_ref マージ元ブランチ名(例: feature/add-login)
base_ref マージ先ブランチ名(例: main, develop)
created_at PR作成日時(ISO 8601形式)
updated_at PR最終更新日時(ISO 8601形式)
html_url PRのWebページURL(ブラウザで開けるリンク)

Strands Agentsでは、外部関数をToolとして登録できるため、以下のように実装しました。

def list_pull_requests(
    self,
    owner: str,
    repo: str,
    state: str = "open",
    limit: int = 30,
) -> list[dict[str, Any]]:
    """PR一覧を取得(必要フィールドのみ)"""
    data = self._get(
        f"/repos/{owner}/{repo}/pulls",
        params={"state": state, "per_page": min(limit, 100)},
    )
    return [
        {
            "number": pr["number"],
            "title": pr["title"],
            "state": pr["state"],
            "user": pr["user"]["login"],
            "head_ref": pr["head"]["ref"],
            "base_ref": pr["base"]["ref"],
            "created_at": pr["created_at"],
            "updated_at": pr["updated_at"],
            "html_url": pr["html_url"],
        }
        for pr in data
    ]

Strands Agentsにはこの関数をToolとして登録するだけで、Agentから呼び出すことができます。このように、ko☆shiで本当に必要な情報を取得することができます。

結果

画像の通り、約34万Tokenから約3万Tokenに削減することができました。このAgentはClaude 4.5 Haikuを使用しているため、0.35ドルのコスト削減になりました。

コスト削減

1回あたりのコスト削減は小さいですが、塵も積もれば山となるので、月間で見るとかなりのコスト削減になります。

考察

提供されているMCPはとても便利なので、AI駆動開発をする上では非常に有用です。しかし、今回のようにAPIを叩くようなAgentでは、必要な情報だけを取得する関数を自作した方が、Token使用料を抑えられると思います。

さらに、副次的な効果として、Agentを開発する際にToolの設計をしておくと、Agentの暴走を防ぐことができると考えています。人間が介入しにくいAgent開発だからこそ、最低限の権限や機能だけを与えることが重要だと感じました。

まとめ

自作でToolを作成すると、Token使用料を9割削減することができました。MCPは便利ではありますが、AgentにToolとして定義する場合は、必要な情報だけを取得する関数を自作した方がToken使用料を抑えられます。しかし、MCPを全く使わないわけではなく、適材適所で使用することが重要なので、今後もMCPと自作Toolを組み合わせていきたいと思います。

Discussion