diff --git a/build.gradle.kts b/build.gradle.kts index f4c23bf..994341e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -18,12 +18,14 @@ plugins { scmVersion { unshallowRepoOnCI.set(true) tag { prefix.set("v") } + versionCreator("versionWithBranch") + branchVersionCreator.set(mapOf("main" to "simple")) + versionIncrementer("incrementMinor") + branchVersionIncrementer.set(mapOf("feature/.*" to "incrementMinor", "bugfix/.*" to "incrementPatch")) } group = "io.github.eschizoid" - version = rootProject.scmVersion.version - description = "MCP Server for GitHub Code Repositories Analysis" dependencies { @@ -78,15 +80,15 @@ application { mainClass.set("MainKt") } tasks.jar { manifest { attributes["Main-Class"] = "MainKt" } - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - exclude("META-INF/*.SF", "META-INF/*.DSA", "META-INF/*.RSA") - from(configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) }) } -tasks.test { useJUnitPlatform() } +tasks.test { + useJUnitPlatform() + jvmArgs("-Dnet.bytebuddy.experimental=true") +} tasks.jacocoTestReport { reports { @@ -99,7 +101,23 @@ tasks.jacocoTestReport { java { withSourcesJar() withJavadocJar() - toolchain { languageVersion = JavaLanguageVersion.of(23) } + toolchain { languageVersion = JavaLanguageVersion.of(24) } +} + +repositories { + mavenCentral() + maven("https://maven.pkg.jetbrains.space/public/p/kotlin-mcp-sdk/sdk") + maven { + credentials { + username = + System.getenv("JRELEASER_MAVENCENTRAL_SONATYPE_USERNAME") + ?: project.properties["mavencentralSonatypeUsername"]?.toString() + password = + System.getenv("JRELEASER_MAVENCENTRAL_SONATYPE_PASSWORD") + ?: project.properties["mavencentralSonatypePassword"]?.toString() + } + url = uri("https://central.sonatype.com/") + } } spotless { @@ -196,6 +214,7 @@ jreleaser { stagingRepository("build/staging-deploy") enabled.set(true) sign.set(false) + maxRetries.set(180) } } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 69ba540..edc24fc 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,13 +1,3 @@ -import org.gradle.kotlin.dsl.maven - plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" } rootProject.name = "mcp-github-code-analyzer" - -dependencyResolutionManagement { - repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) - repositories { - mavenCentral() - maven("https://maven.pkg.jetbrains.space/public/p/kotlin-mcp-sdk/sdk") - } -} diff --git a/src/main/kotlin/mcp/code/analysis/server/Mcp.kt b/src/main/kotlin/mcp/code/analysis/server/Mcp.kt index fc3f8ca..778c4b9 100644 --- a/src/main/kotlin/mcp/code/analysis/server/Mcp.kt +++ b/src/main/kotlin/mcp/code/analysis/server/Mcp.kt @@ -16,6 +16,7 @@ import io.modelcontextprotocol.kotlin.sdk.server.ServerOptions import io.modelcontextprotocol.kotlin.sdk.server.SseServerTransport import io.modelcontextprotocol.kotlin.sdk.server.StdioServerTransport import io.modelcontextprotocol.kotlin.sdk.server.mcp +import java.util.Locale.getDefault import kotlinx.coroutines.* import kotlinx.io.asSink import kotlinx.io.asSource @@ -240,7 +241,134 @@ class Mcp( } } - logger.info("MCP server configured successfully with 1 tool") + server.addPrompt( + name = "analyze-codebase", + description = "Generate a comprehensive analysis prompt for a codebase", + arguments = + listOf( + PromptArgument( + name = "focus", + description = "What aspect to focus on (architecture, security, performance, etc.)", + required = false, + ), + PromptArgument( + name = "language", + description = "Primary programming language of the codebase", + required = false, + ), + ), + ) { request -> + val focus = request.arguments?.get("focus") ?: "general architecture" + val language = request.arguments?.get("language") ?: "any language" + + val promptText = + """ + Please analyze this codebase with a focus on ${focus}. + + Primary language: $language + + Please provide: + 1. Overall architecture and design patterns + 2. Code quality and maintainability assessment + 3. Potential security concerns + 4. Performance considerations + 5. Recommendations for improvements + + Focus particularly on $focus aspects of the code. + """ + .trimIndent() + + GetPromptResult( + description = "Codebase analysis prompt focusing on $focus", + messages = listOf(PromptMessage(role = Role.user, content = TextContent(promptText))), + ) + } + + server.addPrompt( + name = "code-review", + description = "Generate a code review prompt template", + arguments = + listOf( + PromptArgument( + name = "type", + description = "Type of review (security, performance, style, etc.)", + required = false, + ) + ), + ) { request -> + val reviewType = request.arguments?.get("type") ?: "comprehensive" + + val promptText = + """ + Please perform a $reviewType code review of the following code. + + Review criteria: + - Code clarity and readability + - Best practices adherence + - Potential bugs or issues + - Performance implications + - Security considerations + - Maintainability + + Please provide specific feedback with examples and suggestions for improvement. + """ + .trimIndent() + + GetPromptResult( + description = + "${reviewType.replaceFirstChar { if (it.isLowerCase()) it.titlecase(getDefault()) else it.toString() }} code review template", + messages = listOf(PromptMessage(role = Role.user, content = TextContent(promptText))), + ) + } + + server.addResource( + uri = "repo://analysis-results", + name = "Repository Analysis Results", + description = "Latest repository analysis results", + mimeType = "application/json", + ) { + // Return cached analysis results or a placeholder + ReadResourceResult( + contents = + listOf( + TextResourceContents( + uri = "repo://analysis-results", + mimeType = "application/json", + text = """{"message": "No analysis results available yet. Run analyze-repository tool first."}""", + ) + ) + ) + } + + server.addResource( + uri = "repo://metrics", + name = "Repository Metrics", + description = "Code metrics and statistics", + mimeType = "application/json", + ) { + ReadResourceResult( + contents = + listOf( + TextResourceContents( + uri = "repo://metrics", + mimeType = "application/json", + text = + """ + { + "totalFiles": 0, + "linesOfCode": 0, + "languages": [], + "lastAnalyzed": null, + "complexity": "unknown" + } + """ + .trimIndent(), + ) + ) + ) + } + + logger.info("MCP server configured successfully with 1 tool, 2 prompts, and 2 resources") return server } } diff --git a/src/test/kotlin/mcp/code/analysis/server/McpTest.kt b/src/test/kotlin/mcp/code/analysis/server/McpTest.kt index c1b09d9..4eb54ba 100644 --- a/src/test/kotlin/mcp/code/analysis/server/McpTest.kt +++ b/src/test/kotlin/mcp/code/analysis/server/McpTest.kt @@ -19,6 +19,7 @@ class McpTest { private lateinit var serverUnderTest: Mcp private val toolHandlerSlot = slot CallToolResult>() + private val resourceHandlerSlot = slot String>() @BeforeEach fun setUp() { @@ -31,6 +32,16 @@ class McpTest { anyConstructed() .addTool(name = any(), description = any(), inputSchema = any(), handler = capture(toolHandlerSlot)) } returns Unit + + every { + anyConstructed() + .addPrompt(name = any(), description = any(), arguments = any(), promptProvider = any()) + } returns Unit + + every { + anyConstructed() + .addResource(name = any(), description = any(), uri = any(), mimeType = any(), readHandler = any()) + } returns Unit } @AfterEach