Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ repositories {
}

dependencies {
implementation("io.modelcontextprotocol:kotlin-sdk:0.5.0")
implementation("io.modelcontextprotocol:kotlin-sdk:0.6.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
implementation("org.slf4j:slf4j-simple:2.0.17")

Expand Down
12 changes: 11 additions & 1 deletion src/main/kotlin/net/portswigger/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,19 @@ fun sseToStdio(args: SseToStdioArgs) = runBlocking {
}

val sseClient = sseClientManager.getClient()
// Ensure capabilities always include tools since Burp MCP Server supports them
// If serverCapabilities is null (disconnected state), create default caps with tools enabled
val serverCaps = sseClient.serverCapabilities ?: ServerCapabilities()
val capsWithTools = ServerCapabilities(
tools = serverCaps.tools ?: ServerCapabilities.Tools(listChanged = true),
resources = serverCaps.resources,
prompts = serverCaps.prompts,
logging = serverCaps.logging,
experimental = serverCaps.experimental
)
val stdioServer = Server(
serverInfo = sseClient.serverVersion ?: Implementation("burp-suite", "1.0"), options = ServerOptions(
capabilities = sseClient.serverCapabilities ?: ServerCapabilities()
capabilities = capsWithTools
)
)

Expand Down
17 changes: 16 additions & 1 deletion src/main/kotlin/net/portswigger/SseClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package net.portswigger
import io.ktor.client.*
import io.ktor.client.plugins.*
import io.ktor.client.plugins.sse.*
import io.ktor.client.request.*
import io.modelcontextprotocol.kotlin.sdk.Implementation
import io.modelcontextprotocol.kotlin.sdk.ResourceListChangedNotification
import io.modelcontextprotocol.kotlin.sdk.ToolListChangedNotification
Expand All @@ -24,12 +25,18 @@ class SseClient(
override val coroutineContext = SupervisorJob() + Dispatchers.IO

private val client = Client(clientInfo = clientInfo)
private val baseUrl = sseUrl.removeSuffix("/sse").removeSuffix("/")
private val httpClient = HttpClient {
install(SSE)
install(HttpTimeout) {
requestTimeoutMillis = 30000
connectTimeoutMillis = 15000
}
defaultRequest {
header("Origin", baseUrl)
header("Referer", baseUrl)
header("User-Agent", "MCP-Proxy")
}
Comment on lines +35 to +39
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The headers set in the defaultRequest block are duplicated in the requestBuilder at lines 54-56. This duplication is unnecessary since defaultRequest already applies these headers to all HTTP requests made by this client. Consider removing the headers from either location to avoid redundancy.

Copilot uses AI. Check for mistakes.
}

val connectionStateMutableStateFlow = MutableStateFlow(ConnectionState.DISCONNECTED)
Expand All @@ -40,7 +47,15 @@ class SseClient(
private var monitorJob: Job? = null

private fun createTransport(): SseClientTransport {
return SseClientTransport(httpClient, sseUrl)
return SseClientTransport(
client = httpClient,
urlString = sseUrl,
requestBuilder = {
header("Origin", baseUrl)
header("Referer", baseUrl)
header("User-Agent", "MCP-Proxy")
}
)
}

private fun startMonitoring() {
Expand Down