【UiPath】MCP x RPA連携手順 (Attendedロボット基本編)

# はじめに
- 本記事ではMCP(Model Context Protocol)クライアントからRPA自動化処理を実行する方法について説明します。
- プロンプトからの自動化ワークフロー実行は既に [UiPath Autopilot for Everyone](https://docs.uipath.com/ja/autopilot/other/latest/overview/about-autopilot-for-everyone) でも実現可能ですが、呼び出し元クライアントはUiPath Assistantが前提になっています。今回解説する連携手順では任意のMCPクライアントからの呼び出しが可能となります。

実際のデモ動画です。

<iframe width="600" height="440" src="https://www.youtube.com/embed/TC_3nrFVhJc?si=j3H9OeWb8hNf41HT" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

- UiPathが提供するRPA実行環境には [Attendedロボット(有人オートメーション)](https://docs.uipath.com/ja/robot/standalone/2024.10/admin-guide/attended-automations) と[Unattendedロボット(無人オートメーション)](https://docs.uipath.com/ja/robot/standalone/2024.10/admin-guide/unattended-automations) の2種類がありますが、今回はAttendedロボット実行の基本部分について解説したいと思います。

:::note warn
2025年4月現在 UiPath社からは公式にMCP連携するためのソリューションは提供されておりません。本記事ではカスタムのPythonスクリプトで連携を実装しております。もし将来公式なソリューションが提供されましたら、そちらに切り替えを行ってください。
:::

# MCP x RPA概要

## MCPとは
MCPとは大規模言語モデル(LLM)と外部ツール間のシームレスな連携を可能にする標準化されたインターフェースです。詳細については先駆者の皆さんの分かりやすい記事がいくつかありますのでそちらをご参照いただければと思います。

https://speakerdeck.com/minorun365/yasasiimcpru-men

https://zenn.dev/kazuwombat/articles/d8789724f10092

既にさまざまな製品の連携がMCPによって実装されています。まとめサイトの一例を紹介します。

https://github.com/punkpeye/awesome-mcp-servers/blob/main/README.md

## MCPにおける現状の課題
現在のMCPにはどのような課題があるでしょうか。筆者の個人的な見解を述べさせていただきます。
- 連携するMCPサーバー不足と仕様変更への懸念
    - 主要なサービスは既にMCPサーバーが提供されていますが、必ずしもすべて外部システムとMCPで連携できるわけではありません。
    - またMCPはまだ発展途上のプロトコルであればため今後企業で安定的に利用できるか、仕様変更によって互換性が維持されるか、については未知数です。
- セキュリティ・ガバナンス機能の欠如
    - 外部ツールと連携するためにはアクセス権付与やAPIキーなどの受け渡しが必要となります。更に企業で利用するためには「いつ・誰が・どのようなデータを・どのシステムに連携したか」トレースするためのガバナンス機能も不可欠です。
    - 現状のMCPでは開発者がセキュリティキーの安全な保存や監査の機能を考慮して個別に実装する必要があると考えられます。

## UiPath連携のメリット
UiPathが提供するオート―メーション基盤では多くのエンタープライズ企業で導入実績があります。
- アクティビティと呼ばれるシステム連携するための部品が数多く提供されております。[Integration Service](https://docs.uipath.com/ja/integration-service/automation-cloud/latest/user-guide/introduction) によってAPI連携するための認証を一元管理するためのサービスが利用可能です。更にAPIを持たないWebシステムやWindowsアプリケーションのGUI操作も可能であるため、自動化できないシステムはほとんどありません。これによってMCPサーバーが提供されていない外部システムとの連携も可能になりますし、複数のシステムを跨ったデータの受け渡しもローコードで容易に実装できます。
- UiPathにはガバナンスのための [監査機能](https://docs.uipath.com/ja/orchestrator/automation-cloud/latest/user-guide/about-monitoring) も標準装備しています。このためMCPクライアントからRPAがコールされた場合にも、実行履歴がすべて保存されますので管理者が利用状況を一元的に把握することが可能になります。

# RPA連携実装方法

次にMCPとRPA連携の実装方法について4つのステップで解説してゆきます。
1. [Hello World連携](#%E3%82%B9%E3%83%86%E3%83%83%E3%83%971--hello-world%E9%80%A3%E6%90%BA)
2. [入力引数の受け渡し](#%E3%82%B9%E3%83%86%E3%83%83%E3%83%972--%E5%85%A5%E5%8A%9B%E5%BC%95%E6%95%B0%E3%81%AE%E5%8F%97%E3%81%91%E6%B8%A1%E3%81%97)
3. [複数プロセス登録と出力引数](#%E3%82%B9%E3%83%86%E3%83%83%E3%83%973--%E8%A4%87%E6%95%B0%E3%83%97%E3%83%AD%E3%82%BB%E3%82%B9%E7%99%BB%E9%8C%B2%E3%81%A8%E5%87%BA%E5%8A%9B%E5%BC%95%E6%95%B0)
4. [コードの共通化と一般化](#%E3%82%B9%E3%83%86%E3%83%83%E3%83%974--%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AE%E5%85%B1%E9%80%9A%E5%8C%96%E3%81%A8%E4%B8%80%E8%88%AC%E5%8C%96)

## 実行環境
- Windows 10/11
- UiPath Studio 2024.10以降
- Python 3.11以降
- Claude Desktop
    - 開発者モード有効化: Claude for Desktopを起動し、画面左上の「≡」 >「ヘルプ」>「開発者モードを有効」を選択

## 概要
まず連携方法の概要を説明します。次の3つのパートでそれぞれ実装します。

![mcp_uipath.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/80e24063-6403-4993-b4f1-6e16f77530e6.png)

|パート|実装作業|
|---|---
|外部ツール|UiPath StudioにてRPAワークフローで自動化処理を実装し、UiPath Robotにデプロイ|
|MCPサーバー|1. RPA自動化処理(プロセス)ごとに `@mcp.tool()` としてPythonの関数を定義<br>2. Python関数内ではプロンプトから自動抽出された引数を受け取り、UiPath Robotをプロセス名と引数を渡して実行できるように実装<br>3. プロンプト実行時にLLMが適切なPython関数を選択して実行し、結果を表示|
|MCPクライアント|1. Claude DesktopのconfigファイルにてMCPサーバーを設定<br>2. プロンプト実行により適切なツール(Python関数)がコールされ、RPA実行できることを確認する|

## ステップ1 ~ Hello World連携

まずは最も簡単なケースから始めてみましょう。

### RPAワークフロー作成
- RPA連携するための簡単なワークフローを作成します。UiPath StudioにてWindowsプロジェクトにて、"Hello UiPath!"というメッセージボックスを表示するだけのワークフロー(`hello_uipath`)を作成し、パブリッシュします。

    ![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/c97cd15e-5e52-4781-86cc-5b21e6165ce9.png)

    - Orchestratorに接続している場合はプロセスとしてデプロイします。
    - Orchestratorに接続していない場合は `C:\ProgramData\UiPath\Packages` にパッケージを配置します。
- UiPath Assistantから `hello_uipath` が実行できることを確認します。
- [コマンドラインでプロセス実行する](https://docs.uipath.com/ja/robot/standalone/2024.10/admin-guide/command-line-interface#execute) には `UiRobot.exe execute` を使用します。
- コマンドプロンプトにて次のコマンドで hello_uipath が実行できることを確認します。
    ```powershell
    "C:\Program Files\UiPath\Studio\UiRobot.exe" execute --process hello_uipath
    ```

### MCPサーバーの準備
次にMCPサーバー(stdio)の実行環境を準備します。[MCPクイックスタート](https://modelcontextprotocol.io/quickstart/server)を参考にします。
- `uv` をインストールします。
    ```powershell
    powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
    ```

- 新しいPowerShellコンソールにて作業ディレクトリ(今回は `C:\mcp`) を作成・移動し、uvプロジェクトを作成します。
    ```powershell
    mkdir C:\mcp
    cd C:\mcp
    uv init uipath_attended
    cd uipath_attended
    uv venv
    .venv\Scripts\activate
    uv add mcp[cli]
    New-Item exec_uipath_attended.py
    ```

- `exec_uipath_attended.py` には次の内容を記述します。
    ```python
    import asyncio
    from mcp.server.fastmcp import FastMCP
    
    # FastMCP初期化
    mcp = FastMCP("execute_hello_uipath")
    
    @mcp.tool()
    async def execute_hello_uipath() -> str:
        """
        hello_uipathプロセスを実行する関数
        """
        # UiPath Robot実行パス定義
        UIPATH_EXECUTABLE = r"C:\Program Files\UiPath\Studio\UiRobot.exe"
        
        try:
            # Windowsサブプロセス非同期起動
            process = await asyncio.create_subprocess_exec(
                UIPATH_EXECUTABLE,
                "execute",
                "--process",
                "hello_uipath"
            )
    
            # リターンコード取得
            if process.returncode == 0:
                print("Process completed successfully.")
            else:
                print(f"Error occurred (code {process.returncode})")
        except Exception as e:
            # 例外出力
            print(f"Failed to execute UiPath process: {type(e).__name__} - {str(e)}")
    
    if __name__ == "__main__":
        # FastMCP実行
        mcp.run(transport='stdio')
    ```

### MCPクライアント設定
- MCPクライアントの設定を行います。Claude Desktopの設定ファイル(`%AppData%\Claude\claude_desktop_config.json`)を開きます。存在しない場合は新規作成します。次のように設定します。
    ```json
    {
      "mcpServers": {
        "uipath_attended": {
          "command": "uv",
          "args": [
            "--directory",
            "C:\\mcp\\uipath_attended",
            "run",
            "exec_uipath_attended.py"
          ]
        }
      }
    }
    ```

### MCP x RPA連携実行
- Claude Desktopを起動し、トンカチアイコンに `execute_hello_uipath` が表示されることを確認します。表示されない場合やエラーメッセージが表示される場合には `%AppData%\Claude\logs` 配下のログファイルを確認します。
    ![05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/869c8be7-9112-4b4d-b300-124e7c1af7d0.png)

    ![06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/c99deda0-8888-46e0-8a9a-c7c71e4e7457.png)

- **hello_uipathを実行して** とプロンプト入力して `hello_uipath` プロセスを実行してみましょう。途中ツール実行の許可を求めるダイアログが表示されますので「このチャットで許可をする」をクリックします。
    ![07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/ea28985b-c88c-4d5e-89d5-6591ae7fa099.png)

- ポップアップメッセージが表示されれば成功です！
    ![キャプチャ.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/eef03c9f-75f6-4b8f-b81d-640efa2ba362.jpeg)

## ステップ2 ~ 入力引数の受け渡し
次に入力引数を受け渡ししてみましょう。

### RPAワークフローの修正
- Studioにてhello_uipathのワークフローを次のように改修します。
    - String型入力引数 `input` を受け取ります。既定値は"UiPath"としておきます。
    - メッセージボックスのテキストに `"Hello " + input + "!"` とセットします。
        ![09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/78afddf6-4fcc-46b0-92a4-ff1e314e3dcd.png)

    - パブリッシュしてAssistantで実行できることを確認します。

### MCPサーバーの修正
- MCPサーバーのスクリプト `exec_uipath_attended.py` を下記のように修正します。

    ```python
    import asyncio
    import json
    from mcp.server.fastmcp import FastMCP
    
    # FastMCP初期化
    mcp = FastMCP("execute_hello_uipath")
    
    @mcp.tool()
    async def execute_hello_uipath(input: str) -> str:
        """
        hello_uipathプロセスを実行する関数
        Args:
            input: UiPathプロセスに渡す入力文字列
        Returns:
            プロセスの実行結果
        """
        # UiPath Robot実行パス定義
        UIPATH_EXECUTABLE = r"C:\Program Files\UiPath\Studio\UiRobot.exe"
        
        try:
            # 入力パラメータをJSON形式で構築
            input_json = json.dumps({"input": input})
            
            # Windowsサブプロセス非同期起動
            process = await asyncio.create_subprocess_exec(
                UIPATH_EXECUTABLE,
                "execute",
                "--process",
                "hello_uipath",
                "--input",
                input_json
            )
    
            # リターンコード取得
            if process.returncode == 0:
                print("Process completed successfully.")
            else:
                print(f"Error occurred (code {process.returncode})")
        except Exception as e:
            # 例外出力
            print(f"Failed to execute UiPath process: {type(e).__name__} - {str(e)}")
    
    if __name__ == "__main__":
        # FastMCP実行
        mcp.run(transport='stdio')
    ```

### MCPクライアントからの実行
次に入力引数が正常に渡されるか確認してみましょう。
- 更新を反映させるためにClaude Desktopを再起動します。
- **hello_uipathに"UiPath Robot"を渡して実行して** とプロンプト入力します。
- "Hello UiPath Robot!" とポップアップメッセージが表示されれば成功です！
    ![キャプチャ2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/b3f83e2f-4ccf-443c-8057-7df827cea5b9.jpeg)

    - このケースではLLMが"UiPath Robot"を `input` 入力引数として抽出し、Pythonの `execute_hello_uipath` 関数に渡しています。
    - 更にUiRobot.exeコマンドライン実行時に --input パラメーターにJSON形式で `{"input":"UiPath Robot"}` を渡して実行することによって入力引数の受け渡しを実現しています。

## ステップ3 ~ 複数プロセス登録と出力引数
次のステップとして複数のプロセスを実行できるようにします。また出力引数の受け取りも実装してみましょう。

### RPAワークフローの作成
- [RPAチャレンジ](https://www.rpachallenge.com/?lang=ja) を実行するためのワークフローを作成します。
    - 入力引数 `browser` を渡し、指定ブラウザーにてRPAチャレンジを実行するように実装します。
    - 実行結果を出力引数として受け取ります。

### MCPサーバーの修正
- Pythonコードに次のように変更し関数 `execute_rpa_challenge` を追加します。UiPathプロセス名は `rpa_challenge` とします。

    ```python
    import asyncio
    import json
    from mcp.server.fastmcp import FastMCP
    
    # FastMCP初期化
    mcp = FastMCP("execute_hello_uipath")
    
    @mcp.tool()
    async def execute_hello_uipath(input: str) -> str:
        """
        hello_uipathプロセスを実行する関数
        Args:
            input: UiPathプロセスに渡す入力文字列
        Returns:
            プロセスの実行結果
        """
        # UiPath Robot実行パス定義
        UIPATH_EXECUTABLE = r"C:\Program Files\UiPath\Studio\UiRobot.exe"
        
        try:
            # 入力パラメータをJSON形式で構築
            input_json = json.dumps({"input": input})
            
            # Windowsサブプロセス非同期起動
            process = await asyncio.create_subprocess_exec(
                UIPATH_EXECUTABLE,
                "execute",
                "--process",
                "hello_uipath",
                "--input",
                input_json
            )
    
            # リターンコード取得
            if process.returncode == 0:
                print("Process completed successfully.")
            else:
                print(f"Error occurred (code {process.returncode})")
        except Exception as e:
            # 例外出力
            print(f"Failed to execute UiPath process: {type(e).__name__} - {str(e)}")
    
    @mcp.tool()
    async def execute_rpa_challenge(browser: str) -> str:
        """
        RPAチャレンジを実行する関数
        
        Args:
            input: ブラウザーの種類
        
        Returns:
            プロセスの実行結果
        """
        # UiPath Robot実行パス定義
        UIPATH_EXECUTABLE = r"C:\Program Files\UiPath\Studio\UiRobot.exe"
        
        try:
            # 入力パラメータをJSON形式で構築
            input_json = json.dumps({"browser": browser})
            
            # Windowsサブプロセス非同期起動
            process = await asyncio.create_subprocess_exec(
                UIPATH_EXECUTABLE,
                "execute",
                "--process",
                "rpa_challenge",
                "--input",
                input_json,
                stdout=asyncio.subprocess.PIPE,
                stderr=asyncio.subprocess.PIPE,
            )
    
            # 標準出力と標準エラーを取得
            stdout, stderr = await process.communicate()
    
            # 日本語Windows環境で一般的なエンコーディング
            encoding = 'cp932'
            
            if process.returncode == 0:
                output = stdout.decode(encoding).strip()
                return output or "Process completed successfully."
            else:
                error_msg = stderr.decode(encoding).strip()
                return f"Error occurred (code {process.returncode}): {error_msg}"
        except Exception as e:
            # 例外出力
            return f"Failed to execute UiPath process: {type(e).__name__} - {str(e)}"
    
    if __name__ == "__main__":
        # FastMCP実行
        mcp.run(transport='stdio')
    ```

- Claude Desktopを再起動するとツールが1つ増えていることが確認できます。
    ![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/54cced8c-fb6b-45a2-820a-b80a9094798f.png)

- **EdgeでRPAチャレンジを実行して結果を表示して** とプロンプト入力すると `execute_rpa_challenge` 関数→ `rpa_challenge` プロセスが実行され、結果が表示されることを確認します。

    ![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/300ec5db-39fe-4b8b-918e-e636f40d6d7a.png)    

## ステップ4 ~ コードの共通化と一般化
- このままでも動作はしますが、Pythonコード全体を眺めてみると重複したコードが存在することに気が付くと思います。これらを共通化したコードを最後に掲載しておきますので、動作確認をしてみてください。
- また日本語エンコーディング `cp932` もOSより取得し、日本語以外の環境でも動作するように修正しています。

    ```python
    import asyncio
    import json
    from typing import Dict, Any, Optional
    from mcp.server.fastmcp import FastMCP
    import locale
    
    # UiPath Robot実行パス定義
    UIPATH_EXECUTABLE = r"C:\Program Files\UiPath\Studio\UiRobot.exe"
    
    # Systemよりエンコーディング取得
    SYSTEM_ENCODING = locale.getpreferredencoding()
    
    # FastMCP初期化
    mcp = FastMCP("execute_hello_uipath")
    
    async def execute_uipath_process(process_name: str, arg_name: str, input_data: str) -> str:
        """
        UiPathプロセスを実行する共通関数
        
        Args:
            process_name: 実行するUiPathプロセス名
            input_data: UiPathプロセスに渡す入力文字列
        
        Returns:
            プロセスの実行結果
        """
        try:
            # 入力パラメータをJSON形式で構築
            input_json = json.dumps({arg_name: input_data})
            
            # Windowsサブプロセス非同期起動
            process = await asyncio.create_subprocess_exec(
                UIPATH_EXECUTABLE,
                "execute",
                "--process",
                process_name,
                "--input",
                input_json,
                stdout=asyncio.subprocess.PIPE,
                stderr=asyncio.subprocess.PIPE,
            )
    
            # 標準出力と標準エラーを取得
            stdout, stderr = await process.communicate()
            
            if process.returncode == 0:
                output = stdout.decode(SYSTEM_ENCODING).strip()
                return output or "Process completed successfully."
            else:
                error_msg = stderr.decode(SYSTEM_ENCODING).strip()
                return f"Error occurred (code {process.returncode}): {error_msg}"
        except Exception as e:
            # 例外出力
            return f"Failed to execute UiPath process: {type(e).__name__} - {str(e)}"
    
    @mcp.tool()
    async def execute_hello_uipath(input: str) -> str:
        """
        hello_uipathプロセスを実行する関数
        
        Args:
            input: UiPathプロセスに渡す入力文字列
        
        Returns:
            プロセスの実行結果
        """
        return await execute_uipath_process("hello_uipath", "input", input)
    
    @mcp.tool()
    async def execute_rpa_challenge(browser: str) -> str:
        """
        RPAチャレンジを実行する関数
        
        Args:
            browser: ブラウザーの種類
        
        Returns:
            プロセスの実行結果
        """
        return await execute_uipath_process("rpa_challenge", "browser", browser)
    
    if __name__ == "__main__":
        # FastMCP実行
        mcp.run(transport='stdio')
    ```

- プロセス実行のロジックを共通化することによって、比較的容易にプロセスを追加できるようになりました。余力がありましたら、皆さんが普段お使いのプロセスを関数として追加登録してみてください。

# 今後に向けて
ここまででUiPathプロセスをMCPクライアントから実行でき、入力引数(1つ)・出力引数の受け渡しが可能であることが分かりました。プロセスも複数登録できることが分かりました。しかしながら次の課題があります。
- 2つ以上の引数をどのように受け渡しすれば良いか？
- プロセスを自動的に登録できないか？UiPath Orchestratorではプロセスの一元管理ができるので、そこから一覧を出力できないか？

長くなりましたのでこれらについては別記事にて解説したいと思います。
最後までお読みいただき有難うございました！

【追記】
続きはこちらです。

https://qiita.com/hidecha/items/ae96131d19da6e53f2d7