diff --git a/README.md b/README.md index 804063a..10402c0 100644 --- a/README.md +++ b/README.md @@ -17,11 +17,11 @@ The Model Context Protocol allows applications to provide context for LLMs in a ## Installation -Add the JitPack repository to your build file: +Add the new repository to your build file: ```kotlin repositories { - maven { url = uri("https://jitpack.io") } + maven(url = "https://maven.pkg.jetbrains.space/public/p/kotlin-mcp-sdk/sdk") } ``` @@ -29,7 +29,7 @@ Add the dependency: ```kotlin dependencies { - implementation("com.github.JetBrains:mcp-kotlin-sdk:main-SNAPSHOT") + implementation("org.jetbrains.kotlinx:kotlinx-mcp-sdk:0.1.0") } ``` diff --git a/build.gradle.kts b/build.gradle.kts index 4ba3f3b..be7f74c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,8 +7,16 @@ plugins { `maven-publish` } -group = "org.jetbrains.kotlinx.mcp" -version = "1.0-SNAPSHOT" +if (project.getSensitiveProperty("libs.sign.key.private") != null) { + apply(plugin = "signing") +} + +tasks.withType().configureEach { + dependsOn(tasks.withType()) +} + +group = "org.jetbrains.kotlinx" +version = "0.1.0" repositories { mavenCentral() @@ -30,7 +38,29 @@ dependencies { testImplementation(libs.kotlinx.coroutines.debug) } +val spaceUsername = System.getenv("SPACE_PACKAGES_USERNAME") + ?: project.findProperty("kotlin.mcp.sdk.packages.username") as String? + +val spacePassword = System.getenv("SPACE_PACKAGES_PASSWORD") + ?: project.findProperty("kotlin.mcp.sdk.packages.password") as String? + +val sources = tasks.create("sourcesJar") { + from(sourceSets["main"].allSource) + archiveClassifier.set("sources") +} + publishing { + repositories { + maven { + url = uri("https://maven.pkg.jetbrains.space/public/p/kotlin-mcp-sdk/sdk") + name = "Space" + credentials { + username = spaceUsername + password = spacePassword + } + } + } + publications { create("maven") { groupId = project.group.toString() @@ -40,11 +70,91 @@ publishing { from(components["java"]) } } + + val javadocJar = configureEmptyJavadocArtifact() + + publications.withType(MavenPublication::class).all { + pom.configureMavenCentralMetadata() + signPublicationIfKeyPresent() + artifact(javadocJar) + artifact(sources) + } } -tasks.create("sourcesJar") { - from(sourceSets["main"].allSource) - archiveClassifier.set("sources") +fun MavenPom.configureMavenCentralMetadata() { + name by project.name + description by "Kotlin implementation of the Model Context Protocol (MCP)" + url by "https://github.com/JetBrains/mcp-kotlin-sdk" + + licenses { + license { + name by "The Apache Software License, Version 2.0" + url by "https://www.apache.org/licenses/LICENSE-2.0.txt" + distribution by "repo" + } + } + + developers { + developer { + id by "JetBrains" + name by "JetBrains Team" + organization by "JetBrains" + organizationUrl by "https://www.jetbrains.com" + } + } + + scm { + url by "https://github.com/JetBrains/mcp-kotlin-sdk" + connection by "scm:git:git://github.com/JetBrains/mcp-kotlin-sdk.git" + developerConnection by "scm:git:git@github.com:JetBrains/mcp-kotlin-sdk.git" + } +} + +fun configureEmptyJavadocArtifact(): org.gradle.jvm.tasks.Jar { + val javadocJar by project.tasks.creating(Jar::class) { + archiveClassifier.set("javadoc") + // contents are deliberately left empty + // https://central.sonatype.org/publish/requirements/#supply-javadoc-and-sources + } + return javadocJar +} + +fun MavenPublication.signPublicationIfKeyPresent() { + val keyId = project.getSensitiveProperty("libs.sign.key.id") + val signingKey = project.getSensitiveProperty("libs.sign.key.private") + val signingKeyPassphrase = project.getSensitiveProperty("libs.sign.passphrase") + + if (!signingKey.isNullOrBlank()) { + the().apply { + useInMemoryPgpKeys(keyId, signingKey, signingKeyPassphrase) + + sign(this@signPublicationIfKeyPresent) + } + } +} + +fun Project.getSensitiveProperty(name: String?): String? { + if (name == null) { + error("Expected not null property '$name' for publication repository config") + } + + return project.findProperty(name) as? String + ?: System.getenv(name) + ?: System.getProperty(name) +} + +infix fun Property.by(value: T) { + set(value) +} + +tasks.create("localJar") { + dependsOn(tasks.jar) + + archiveFileName = "kotlinx-mcp-sdk.jar" + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + from(configurations.runtimeClasspath.map { + it.map { if (it.isDirectory) it else zipTree(it) } + }) } tasks.test { @@ -53,15 +163,20 @@ tasks.test { abstract class GenerateLibVersionTask @Inject constructor( @get:Input val libVersion: String, - @get:OutputDirectory val sourcesDir: File + @get:OutputDirectory val sourcesDir: File, ) : DefaultTask() { @TaskAction fun generate() { - val sourceFile = File(sourcesDir, "LibVersion.kt") + val sourceFile = File(sourcesDir.resolve("org/jetbrains/kotlinx/mcp"), "LibVersion.kt") + + if (!sourceFile.exists()) { + sourceFile.parentFile.mkdirs() + sourceFile.createNewFile() + } sourceFile.writeText( """ - package shared + package org.jetbrains.kotlinx.mcp const val LIB_VERSION = "$libVersion" @@ -76,7 +191,7 @@ dokka { dokkaSourceSets.main { sourceLink { localDirectory.set(file("src/main/kotlin")) - remoteUrl("https://github.com/e5l/mcp-kotlin-sdk") + remoteUrl("https://github.com/JetBrains/mcp-kotlin-sdk") remoteLineSuffix.set("#L") documentedVisibilities(VisibilityModifier.Public) } diff --git a/samples/connect-to-intellij.sh b/samples/connect-to-intellij.sh index 817b1bd..87dae68 100755 --- a/samples/connect-to-intellij.sh +++ b/samples/connect-to-intellij.sh @@ -1,3 +1,3 @@ #!/usr/bin/env bash -../gradlew clean jar; java -jar ../build/libs/mcp-kotlin-sdk-1.0-SNAPSHOT.jar --demo \ No newline at end of file +../gradlew clean localJar; java -jar ../build/libs/kotlinx-mcp-sdk.jar --demo diff --git a/samples/inspector.sh b/samples/inspector.sh index dc6de14..eed6701 100755 --- a/samples/inspector.sh +++ b/samples/inspector.sh @@ -1,3 +1,3 @@ #!/usr/bin/env bash -../gradlew clean jar && npx @modelcontextprotocol/inspector java -jar ../build/libs/mcp-kotlin-sdk-1.0-SNAPSHOT.jar --server \ No newline at end of file +../gradlew clean localJar && npx @modelcontextprotocol/inspector java -jar ../build/libs/kotlinx-mcp-sdk.jar --server diff --git a/samples/kotlin-mcp-server/build.gradle.kts b/samples/kotlin-mcp-server/build.gradle.kts index 42306b7..a9e90fd 100644 --- a/samples/kotlin-mcp-server/build.gradle.kts +++ b/samples/kotlin-mcp-server/build.gradle.kts @@ -10,10 +10,10 @@ application { group = "org.example" -version = "1.0-SNAPSHOT" +version = "0.1.0" dependencies { - implementation("com.github.JetBrains:mcp-kotlin-sdk:b1b0238") + implementation("org.jetbrains.kotlinx:kotlinx-mcp-sdk:0.1.0") implementation("org.slf4j:slf4j-nop:2.0.9") testImplementation(kotlin("test")) @@ -24,4 +24,4 @@ tasks.test { } kotlin { jvmToolchain(21) -} \ No newline at end of file +} diff --git a/samples/kotlin-mcp-server/settings.gradle.kts b/samples/kotlin-mcp-server/settings.gradle.kts index 226d5f7..19d82e0 100644 --- a/samples/kotlin-mcp-server/settings.gradle.kts +++ b/samples/kotlin-mcp-server/settings.gradle.kts @@ -7,6 +7,6 @@ dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { mavenCentral() - maven("https://jitpack.io") + maven("https://maven.pkg.jetbrains.space/public/p/kotlin-mcp-sdk/sdk") } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 9a96391..011a448 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,5 +1,5 @@ plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" } -rootProject.name = "mcp-kotlin-sdk" +rootProject.name = "kotlinx-mcp-sdk" diff --git a/src/main/kotlin/org/jetbrains/kotlinx/mcp/client/WebSocketMcpKtorClientExtensions.kt b/src/main/kotlin/org/jetbrains/kotlinx/mcp/client/WebSocketMcpKtorClientExtensions.kt index 0b0a7db..33150f6 100644 --- a/src/main/kotlin/org/jetbrains/kotlinx/mcp/client/WebSocketMcpKtorClientExtensions.kt +++ b/src/main/kotlin/org/jetbrains/kotlinx/mcp/client/WebSocketMcpKtorClientExtensions.kt @@ -3,8 +3,8 @@ package org.jetbrains.kotlinx.mcp.client import org.jetbrains.kotlinx.mcp.Implementation import io.ktor.client.HttpClient import io.ktor.client.request.HttpRequestBuilder +import org.jetbrains.kotlinx.mcp.LIB_VERSION import org.jetbrains.kotlinx.mcp.shared.IMPLEMENTATION_NAME -import shared.LIB_VERSION /** * Returns a new WebSocket transport for the Model Context Protocol using the provided HttpClient. diff --git a/src/main/kotlin/org/jetbrains/kotlinx/mcp/client/sse.ktor.kt b/src/main/kotlin/org/jetbrains/kotlinx/mcp/client/sse.ktor.kt index f17af26..54ff9ce 100644 --- a/src/main/kotlin/org/jetbrains/kotlinx/mcp/client/sse.ktor.kt +++ b/src/main/kotlin/org/jetbrains/kotlinx/mcp/client/sse.ktor.kt @@ -3,8 +3,8 @@ package org.jetbrains.kotlinx.mcp.client import org.jetbrains.kotlinx.mcp.Implementation import io.ktor.client.HttpClient import io.ktor.client.request.HttpRequestBuilder +import org.jetbrains.kotlinx.mcp.LIB_VERSION import org.jetbrains.kotlinx.mcp.shared.IMPLEMENTATION_NAME -import shared.LIB_VERSION import kotlin.time.Duration /** diff --git a/src/main/kotlin/org/jetbrains/kotlinx/mcp/server/WebSocketMcpKtorServerExtensions.kt b/src/main/kotlin/org/jetbrains/kotlinx/mcp/server/WebSocketMcpKtorServerExtensions.kt index 0188a80..6eb31ae 100644 --- a/src/main/kotlin/org/jetbrains/kotlinx/mcp/server/WebSocketMcpKtorServerExtensions.kt +++ b/src/main/kotlin/org/jetbrains/kotlinx/mcp/server/WebSocketMcpKtorServerExtensions.kt @@ -4,8 +4,8 @@ import org.jetbrains.kotlinx.mcp.Implementation import org.jetbrains.kotlinx.mcp.ServerCapabilities import io.ktor.server.routing.* import io.ktor.server.websocket.* +import org.jetbrains.kotlinx.mcp.LIB_VERSION import org.jetbrains.kotlinx.mcp.shared.IMPLEMENTATION_NAME -import shared.LIB_VERSION /** * Registers a WebSocket route that establishes an MCP (Model Context Protocol) server session.