しおしお

IntelliJ IDEAのことなんかを書いてます

Jetbrains系IDE(IntelliJ IDEA)のAI ProでMCPを使ってみる

IntelliJ IDEAのAI ProでModel Context Protocol(MCP)を活用する方法

JetBrains社のIntelliJ IDEAにAI Proの新機能としてModel Context Protocol(MCP)が導入されました。この機能により、AI Assistantの能力を外部サービスと連携して拡張できるようになりました。今回はその設定方法と活用例を紹介します。

MCPの設定手順

  1. IntelliJ IDEAの設定画面を開き、「AI Assistant」セクション内に新たに追加された「Model Context Protocol(MCP)」を選択します。

  2. 画面右側の「+」アイコンをクリックして新しいMCP設定を追加します。今回はLAPRASが提供するMCPサーバーを活用します。

  3. 設定方法は「JSON直接編集」と「コマンド入力方式」の2種類がありますが、ここではシンプルな「コマンド入力方式」を選択します。

  4. コマンド欄には以下を入力します: docker run -i --rm laprascom/lapras-mcp-server

  5. 「Apply」ボタンをクリックして設定を適用します。ステータスインジケーターが緑色に変わったら、正常に接続されています。

MCPの活用例

AI Chat画面を開くと、LAPRASのツールが統合されていることが確認できます。これにより、AI Assistantから直接LAPRASの機能にアクセスできるようになりました。

実際に使ってみましょう。Chat欄に「laprasで年収1000万以上のKotlinの求人を検索して」と入力すると、AI AssistantがMCPを通じてLAPRASの求人検索機能を呼び出し、条件に合った求人情報を表示してくれます。 なお、謎ですがcodebaseをonにしないとうまく動いてくれません…

まとめ

Model Context Protocol(MCP)は、IntelliJ IDEAのAI機能を大幅に拡張する優れた機能です。外部ツールとの連携により、より強力で多機能なAIアシスタンスを実現できます。 JetBrains系IDEをお使いの方は、ぜひMCPを活用して開発体験をさらに向上させてみてください!

まだ、Junieでは使えないみたいなので早く使えるようになるといいなぁ…

Ktorを3系にしたらファイルアップロードの上限サイズが50MBに制限されたお話

Ktorを2系から3系にバージョンアップしたところ、ファイルアップロード機能で50MBを超えるファイルがアップロードできない問題に発生しました…原因を調べたうえで、対応方法をまとめました。

起きたこと

50MBを超えるサイズのファイルをアップロードすると、👇️のエラーが発生するようになってしまいました。なお、この時点ではKtorのバージョンは3.0.1を使用しています。

java.io.IOException: Limit of 52428800 bytes exceeded while scanning for "
------WebKitFormBoundaryHLSvk60VSMAK9KBD"
    at io.ktor.utils.io.ByteReadChannelOperationsKt.readUntil(ByteReadChannelOperations.kt:550)
    at io.ktor.utils.io.ByteReadChannelOperationsKt$readUntil$1.invokeSuspend(ByteReadChannelOperations.kt)

原因

Ktor 3系から、ファイルアップロード時の「各ファイルの上限サイズ」が新たに導入され、デフォルトでは50MBが上限として設定されています。そのため、上限を超えるファイルをアップロードするとエラーが発生するようになりました。

なお、公式ドキュメントにはデフォルト値が64KBと記載されていますが、実際のコードでは50MBが適用されています。

該当するコード

以下、3.0.1時点のKtorの該当コードです。これを見ると、デフォルト値および設定変更の仕組みが確認できます。

@PublishedApi
internal const val DEFAULT_FORM_FIELD_MAX_SIZE: Long = 50 * 1024 * 1024

/**
 * Represents the limit for form field size in bytes for an [ApplicationCall].
 * This limit determines the maximum size allowed for form field data in a request.
 *
 * The default value is 65536 bytes (64 KB).
 *
 * To get the value of the formFieldLimit, use the getter:
 * ```
 * val limit = call.formFieldLimit
 * ```
 *
 * To set the value of the formFieldLimit, use the setter:
 * ```
 * call.formFieldLimit = limit
 * ```
 */
public var ApplicationCall.formFieldLimit: Long
    get() {
        return attributes.getOrNull(FORM_FIELD_LIMIT) ?: DEFAULT_FORM_FIELD_MAX_SIZE
    }
    set(value) {
        attributes.put(FORM_FIELD_LIMIT, value)
    }

上記から、call.formFieldLimitの値を変更することで、この上限を変更できる仕組みが導入されていることがわかります。


対応方法

リクエストパイプラインのinterceptformFieldLimitを設定することで、アップロードできるファイルサイズの上限を変更できます。たとえば、下記コードのように設定を行い、100MBに変更することができます。

【例】100MBに変更する

intercept(ApplicationCallPipeline.Setup) {
    call.formFieldLimit = 100 * 1024 * 1024
}

注意事項:
3.0.3より前のバージョンでは、interceptで設定したformFieldLimitが無視されます。具体的な原因として、MultiPartDataの読取関数の内部でformFieldLimitの値を初期値に戻してしまう実装が含まれているためです。


修正が必要なKtorバージョン

以下のコードが問題に該当します(3.0.1時点の実装です)。

/**
 * Receives multipart data for this call.
 * @return instance of [MultiPartData].
 * @throws ContentTransformationException when content cannot be transformed to the [MultiPartData].
 */
public suspend inline fun ApplicationCall.receiveMultipart(
    formFieldLimit: Long = DEFAULT_FORM_FIELD_MAX_SIZE
): MultiPartData {
    this.formFieldLimit = formFieldLimit
    return receive()
}

上記コードでは、関数内でローカルなformFieldLimitを直接利用しており、interceptで設定した値が反映されない状態でした。この問題は3.0.3で修正済みであるため、対応を実施する際にはKtorのバージョンを3.0.3以降にアップグレードすることを推奨します。


最終的な修正案

  1. Ktorのバージョンを必ず3.0.3以降にアップデートします。
  2. 必要に応じてformFieldLimitの値を変更します。以下のようなコードをアプリケーションに追加してください。

完全なコード例

fun Application.module() {
    install(ContentNegotiation) {
        // 必要なコンフィグの例: JSONシリアライズ設定など
    }

    intercept(ApplicationCallPipeline.Setup) {
        // ファイルアップロード上限を 100MB に変更
        call.formFieldLimit = 100 * 1024 * 1024
    }

    routing {
        post("/upload") {
            val multipart = call.receiveMultipart()
            multipart.forEachPart { part ->
                // ここでアップロードされたファイルに対する処理をする
            }
        }
    }
}

おわり。

IntelliJ IDEAのAI AssistantにGitHubのPRのレビューをしてもらう

AI Assistantを有効にしてPRを開くと、タイムラインの上部に変更の概要が表示されるようになりました。ちょっと設定をいじれば、もっとレビューしやすくなるんじゃないかと思ったので、カスタマイズしてみました!

以下は、そのカスタマイズ方法と使い方です。


1. 設定画面を開く

まず、PRのタイムライン上部にある AI Assistant の右横にある歯車アイコンをクリック!
赤枠の部分です👇

すると、こんな感じの設定画面がポップアップで表示されます👇


2. Promptのカスタマイズ

レビュー用の指示をカスタマイズします。

僕は、とりあえずこんな感じのテンプレを設定してみました:

Write the summary in the following format:

## 概要
このPRの変更の概要を

## 主な変更点
各ファイルの主な変更点

## 良い点
変更の良い点を

## 確実に修正したほうが良いポイント
明らかにバグである。セキュリティ上の問題がありそう、Typoがあるなどがあればこちらに

## 改善ポイント
変更内容に改善したほうが良いポイントがあれば

3. いざレビュー開始!

カスタマイズが終わったらGitHubのPRを開きます。すると、タイムライン上部にこんな感じで表示されるようになります👇

便利すぎてレビューが捗りそうですね!


これでおしまい!ぜひ試してみてください! 🎉

IntelliJ IDEAを2024.2のEAPにしたら日本語化されてしまった件

IntelliJ IDEAの2024.2 EAP(242.14146.16)にあげてみると、全てが日本語化されてしまいました。 設定画面などは、下の画像のように全て日本語で表示されるようになっています。

今まではプラグインでの日本語化だったと思うのですが、プラグインがなくても日本語化されるようになってしまったようです。 言語設定に依存してそうという推測をもとに、user.languageを変更して英語に戻せるか試してみます。

Toolboxからの設定方法

  1. 2024.2 EAP三点リーダからSettingsへ移動します。

  2. Edit JVM options...からoptionを編集するエディタを開き、最終行に -Duser.language=en を追加してあげます。

  3. IntelliJを再起動すると…無事に英語に戻りました!

IntelliJ からの設定方法

command + shift + AのFind ActionsからJVMオプションを変更してあげます。 Edit Custom VM Options...を選択するとエディタが開くので、最終行に-Duser.language=enを追加してあげます。

おわり。


今後のリリースでは設定画面から変更できるようになるとの情報いただきました。

IntelliJ IDEA 2024.1から消えてしまったSearch Everywhereのdatabase検索タブを復活する方法

IntelliJ IDEA 2024.1 の EAPで消えた「Search Everywhere」のデータベース検索タブを復活する方法

IntelliJ IDEA 2024.1 の EAP版にアップデートしてから、「Search Everywhere」のデータベース検索タブが消えてしまって困っている方はいませんか?(下の画像の赤枠部分です)

この便利だった機能を復活させるには、以下の手順を実行してください。

  1. 設定画面のAdvanced Settingsに移動
  2. 設定項目でShow database tab in Search Everywhereを探す
  3. 該当のチェックボックスをオンにする👇

これだけで完了です! 設定後は、「Search Everywhere」画面にデータベース検索タブが復活し、以前と同じように使えるようになります。

IntelliJ IDEA2024.1 EAPで変わってしまったTerminalをもとに戻す方法

IntelliJ IDEA2024.1 EAPですっかり変わってしまったTerminal機能を従来の普通のTerminalに戻す方法になります。

戻す方法

設定画面のTools->Terminalにある、Shell integrationのチェックを外すことで、今まで通りの普通のTerminalに戻ってくれます

おわり。

LogbackのLogstashEncoderを使用しつつメッセージをマスクしてみたお話

LogbackでログメッセージをJsonで出力(logstash-logback-encoderを使用)する際に、メッセージの内容を一部マスクしてみました。

マスクの方法は、logback.xmlの指定だけで特定フィールドの内容をまるっと置き換えたり、正規表現を使用して柔軟に置き換えたりできるようです。 また、カスタムな実装を用意することで設定ファイルだけでは実現できないような柔軟な置き換えもできるみたいなので色々と試してみます。*1

フィールド名を指定してのマスキング

デフォルトのマスク定義でのマスキング

これは、特定のフィールドの内容をデフォルト定義に従い置き換える方法になります。

logback.xmlの内容

LogstashEncoderMaskingJsonGeneratorDecoratorを指定することで、マスキングの指定ができるようです。 この例ではmessageフィールドはすべて「これに置き換わるよ」と出力されます。

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="net.logstash.logback.encoder.LogstashEncoder">
            <jsonGeneratorDecorator class="net.logstash.logback.mask.MaskingJsonGeneratorDecorator">
                <defaultMask>これに置き換わるよ</defaultMask>
                <path>message</path>
            </jsonGeneratorDecorator>
        </encoder>
    </appender>

実行結果

実行してみると、testとログメッセージを出力したのにlogback.xmlに従い「これに置き換わるよ」と出力されていることが確認できます。

フィールドごとにマスク定義を変えてのマスキング

これは、フィールドごとにマスク内容を定義して置き換える方法になります。

logback.xmlの内容

この例ではmessageフィールドはdefaultMaskの定義が適用されますが、pathMask配下で定義されているcustomフィールドは「customはこれにかわるよ」に置き換えられ出力されます。

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="net.logstash.logback.encoder.LogstashEncoder">
            <jsonGeneratorDecorator class="net.logstash.logback.mask.MaskingJsonGeneratorDecorator">
                <defaultMask>これに置き換わるよ</defaultMask>
                <path>message</path>
                <pathMask>
                    <path>custom</path>
                    <mask>customはこれにかわるよ</mask>
                </pathMask>
            </jsonGeneratorDecorator>
        </encoder>
    </appender>

実行結果

実行してみると、messagecustomではlogback.xmlの定義に従い異なる置き換えが行われているのが確認できます。

出力される値をハンドリングしてのマスキング

値をベースのマスキングはすべてのフィールドに対して行われていくので、フィールド名を指定してのマスキングよりもパフォーマンスに与える影響は大ききなってしまいます。

正規表現を使ってのマスキング

これは、出力されるログメッセージの中で指定した正規表現にマッチした部分のみマスキングする方法になります。

loback.xmlの内容

defaultMaskはフィールド指定と同じになっていて、 valueに対して正規表現などを指定してマッチした部分を置き換えることができます。 なお、valueMaskを使うとフィールド名指定のpathMaskと同じようにマスク定義をそれぞれ定義できるようになります。

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="net.logstash.logback.encoder.LogstashEncoder">
            <jsonGeneratorDecorator class="net.logstash.logback.mask.MaskingJsonGeneratorDecorator">
                <defaultMask>*****</defaultMask>
                <value>(sio){2}</value>
            </jsonGeneratorDecorator>
        </encoder>
    </appender>

実行結果

実行してみるといい感じに正規表現にマッチしたいる部分がマスキングできているのがわかりますね。 複数箇所マッチすれば全て置き換えられているのも確認できます。

カスタム実装を使用してマスキングしてみる

カスタム実装を使用すると、特定フィールドの値が特定の正規表現にマッチしたら置き換えなんてことが柔軟にできるようになります。 例えば、messageフィールド内に出力される可能性のあるメールアドレスなどをマスキングするなんてこともできたりします。

カスタム実装

この例では、出力される値のマスキングを行うインタフェースのValueMaskerを使ってログに出力される値のマスキングをしています。 また、余計なフィールドに対しては何も行わないようmessageフィールドのみを対象にしています。

class ValueMaskerExample: ValueMasker {
    override fun mask(context: JsonStreamContext, value: Any?): Any? {
        return if (value != null && context.currentName == "message") {
            value.toString().replace("siosio", "***")
        } else {
            value
        }
    }
}

logback.xmlの内容

valueMaskerに、カスタム実装のクラスの完全修飾名を指定してあげます。 これで、すべてのフィールドに対してカスタム実装のマスク処理が呼び出されるようになります。

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="net.logstash.logback.encoder.LogstashEncoder">
            <jsonGeneratorDecorator class="net.logstash.logback.mask.MaskingJsonGeneratorDecorator">
                <valueMasker class="ValueMaskerExample" />
            </jsonGeneratorDecorator>
        </encoder>
    </appender>

実行結果

実行してみるとmessageフィールドのみマスキングが行われていることが確認できます。 マスキング対象外のcustomフィールドはマスキングが行われていないことが確認できます。