diff --git a/mirai-core-api/compatibility-validation/android/api/android.api b/mirai-core-api/compatibility-validation/android/api/android.api
index c3e8c889a..790e1c338 100644
--- a/mirai-core-api/compatibility-validation/android/api/android.api
+++ b/mirai-core-api/compatibility-validation/android/api/android.api
@@ -5636,6 +5636,7 @@ public final class net/mamoe/mirai/utils/BotConfiguration$MiraiProtocol : java/l
 	public static final field ANDROID_WATCH Lnet/mamoe/mirai/utils/BotConfiguration$MiraiProtocol;
 	public static final field IPAD Lnet/mamoe/mirai/utils/BotConfiguration$MiraiProtocol;
 	public static final field MACOS Lnet/mamoe/mirai/utils/BotConfiguration$MiraiProtocol;
+	public final fun isQRLoginSupported ()Z
 	public static fun valueOf (Ljava/lang/String;)Lnet/mamoe/mirai/utils/BotConfiguration$MiraiProtocol;
 	public static fun values ()[Lnet/mamoe/mirai/utils/BotConfiguration$MiraiProtocol;
 }
diff --git a/mirai-core-api/compatibility-validation/jvm/api/jvm.api b/mirai-core-api/compatibility-validation/jvm/api/jvm.api
index 202bfe61f..bcf29e720 100644
--- a/mirai-core-api/compatibility-validation/jvm/api/jvm.api
+++ b/mirai-core-api/compatibility-validation/jvm/api/jvm.api
@@ -5636,6 +5636,7 @@ public final class net/mamoe/mirai/utils/BotConfiguration$MiraiProtocol : java/l
 	public static final field ANDROID_WATCH Lnet/mamoe/mirai/utils/BotConfiguration$MiraiProtocol;
 	public static final field IPAD Lnet/mamoe/mirai/utils/BotConfiguration$MiraiProtocol;
 	public static final field MACOS Lnet/mamoe/mirai/utils/BotConfiguration$MiraiProtocol;
+	public final fun isQRLoginSupported ()Z
 	public static fun valueOf (Ljava/lang/String;)Lnet/mamoe/mirai/utils/BotConfiguration$MiraiProtocol;
 	public static fun values ()[Lnet/mamoe/mirai/utils/BotConfiguration$MiraiProtocol;
 }
diff --git a/mirai-core-api/src/commonMain/kotlin/utils/BotConfiguration.kt b/mirai-core-api/src/commonMain/kotlin/utils/BotConfiguration.kt
index 0c6462e63..eba516e1a 100644
--- a/mirai-core-api/src/commonMain/kotlin/utils/BotConfiguration.kt
+++ b/mirai-core-api/src/commonMain/kotlin/utils/BotConfiguration.kt
@@ -20,6 +20,8 @@ import kotlinx.coroutines.SupervisorJob
 import kotlinx.serialization.json.Json
 import net.mamoe.mirai.Bot
 import net.mamoe.mirai.BotFactory
+import net.mamoe.mirai.Mirai
+import net.mamoe.mirai.auth.BotAuthorization
 import net.mamoe.mirai.event.events.BotOfflineEvent
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.EmptyCoroutineContext
@@ -249,7 +251,19 @@ public open class BotConfiguration : AbstractBotConfiguration() { // open for Ja
          * @since 2.8
          */
         MACOS,
+        ;
 
+        /**
+         * 当前协议是否支持[二维码登录][BotAuthorization.byQRCode]
+         *
+         * @since 2.15.0
+         */
+        public val isQRLoginSupported: Boolean get() = data.isQRLoginSupported
+
+        private inline val data: InternalProtocolDataExchange.InternalProtocolData
+            get() = InternalProtocolDataExchange.instance.of(
+                this
+            )
     }
 
     /**
@@ -562,3 +576,30 @@ internal val deviceInfoStub: (Bot) -> DeviceInfo = {
 private val logger by lazy {
     MiraiLogger.Factory.create(BotConfiguration::class)
 }
+
+/** @since 2.15.0 */
+@MiraiInternalApi
+public interface InternalProtocolDataExchange {
+    @MiraiInternalApi
+    public interface InternalProtocolData {
+        public val isQRLoginSupported: Boolean
+        public val mainVersion: String
+        public val buildVersion: String
+        public val sdkVersion: String
+    }
+
+    @MiraiInternalApi
+    public fun of(protocol: BotConfiguration.MiraiProtocol): InternalProtocolData
+
+    @MiraiInternalApi
+    public companion object {
+        internal val instance by lazy {
+            Mirai // ensure service loaded
+
+            loadService(
+                InternalProtocolDataExchange::class,
+                "net.mamoe.mirai.internal.utils.MiraiProtocolInternal\$Exchange"
+            )
+        }
+    }
+}
diff --git a/mirai-core/src/commonMain/kotlin/utils/MiraiCoreServices.kt b/mirai-core/src/commonMain/kotlin/utils/MiraiCoreServices.kt
index e6512e296..d83a597ee 100644
--- a/mirai-core/src/commonMain/kotlin/utils/MiraiCoreServices.kt
+++ b/mirai-core/src/commonMain/kotlin/utils/MiraiCoreServices.kt
@@ -14,6 +14,7 @@ import net.mamoe.mirai.utils.Services
 
 internal object MiraiCoreServices {
 
+    @Suppress("RemoveRedundantQualifierName")
     @OptIn(InternalEventMechanism::class)
     fun registerAll() {
         Services.register(
@@ -120,5 +121,10 @@ internal object MiraiCoreServices {
             "net.mamoe.mirai.auth.DefaultBotAuthorizationFactory",
             "net.mamoe.mirai.internal.network.auth.DefaultBotAuthorizationFactoryImpl"
         ) { net.mamoe.mirai.internal.network.auth.DefaultBotAuthorizationFactoryImpl() }
+
+        Services.register(
+            "net.mamoe.mirai.utils.InternalProtocolDataExchange",
+            "net.mamoe.mirai.internal.utils.MiraiProtocolInternal\$Exchange"
+        ) { net.mamoe.mirai.internal.utils.MiraiProtocolInternal.Exchange() }
     }
 }
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/utils/MiraiProtocolInternal.kt b/mirai-core/src/commonMain/kotlin/utils/MiraiProtocolInternal.kt
index c825bf103..9f3ab0362 100644
--- a/mirai-core/src/commonMain/kotlin/utils/MiraiProtocolInternal.kt
+++ b/mirai-core/src/commonMain/kotlin/utils/MiraiProtocolInternal.kt
@@ -11,6 +11,8 @@ package net.mamoe.mirai.internal.utils
 
 import net.mamoe.mirai.utils.BotConfiguration.MiraiProtocol
 import net.mamoe.mirai.utils.EnumMap
+import net.mamoe.mirai.utils.InternalProtocolDataExchange
+import net.mamoe.mirai.utils.MiraiInternalApi
 import net.mamoe.mirai.utils.toUHexString
 
 internal class MiraiProtocolInternal(
@@ -29,7 +31,7 @@ internal class MiraiProtocolInternal(
     var supportsQRLogin: Boolean,
 
     // don't change property signatures, used externally.
-) {
+) : InternalProtocolDataExchange.InternalProtocolData {
     internal companion object {
         // don't change signature
         internal val protocols = EnumMap<MiraiProtocol, MiraiProtocolInternal>(MiraiProtocol::class)
@@ -121,4 +123,18 @@ internal class MiraiProtocolInternal(
 
         inline val MiraiProtocol.asInternal: MiraiProtocolInternal get() = get(this)
     }
+
+    @PublishedApi
+    internal class Exchange : InternalProtocolDataExchange {
+        @MiraiInternalApi
+        override fun of(protocol: MiraiProtocol): InternalProtocolDataExchange.InternalProtocolData {
+            return get(protocol)
+        }
+    }
+
+
+    override val isQRLoginSupported: Boolean get() = supportsQRLogin
+    override val mainVersion: String get() = ver
+    override val buildVersion: String get() = buildVer
+    override val sdkVersion: String get() = sdkVer
 }
diff --git a/mirai-core/src/commonMain/resources/META-INF/services/net.mamoe.mirai.utils.InternalProtocolDataExchange b/mirai-core/src/commonMain/resources/META-INF/services/net.mamoe.mirai.utils.InternalProtocolDataExchange
new file mode 100644
index 000000000..cdb374e85
--- /dev/null
+++ b/mirai-core/src/commonMain/resources/META-INF/services/net.mamoe.mirai.utils.InternalProtocolDataExchange
@@ -0,0 +1,10 @@
+#
+# Copyright 2019-2023 Mamoe Technologies and contributors.
+#
+# 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
+# Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
+#
+# https://github.com/mamoe/mirai/blob/dev/LICENSE
+#
+
+net.mamoe.mirai.internal.utils.MiraiProtocolInternal$Exchange