AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
MCP verified Apache-2.0 Self-run

Mcp4k

mcp-ondrsh-mcp4k · by ondrsh

Compiler-driven MCP framework for Kotlin Multiplatform

No reviews yet
0 installs
20 views
0.0% view→install

Install

$ agentstack add mcp-ondrsh-mcp4k

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/mcp-ondrsh-mcp4k)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
3mo ago

Declared compatibility

Claude CodeClaude DesktopCursorWindsurf

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Mcp4k? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

mcp4k is a compiler-driven framework for building both clients and servers using the Model Context Protocol (MCP) in Kotlin. It implements the vast majority of the MCP specification, including resources, prompts, tools, sampling, and more.

mcp4k automatically generates JSON-RPC handlers, schema metadata, and manages the complete lifecycle for you.


Overview

  • Client: Connects to any MCP server to request prompts, read resources, or invoke tools.
  • Server: Exposes resources, prompts, and tools to MCP-compatible clients, handling standard JSON-RPC messages and protocol events.
  • Transports: Supports stdio, with HTTP-Streaming and other transports on the roadmap.
  • Lifecycle: Manages initialization, cancellation, sampling, progress tracking, and more.

mcp4k also enforces correct parameter typing at compile time. If you describe a tool parameter incorrectly, you get a compile-time error instead of a runtime mismatch.


Installation

Add mcp4k to your build:

plugins {
  kotlin("multiplatform") version "2.4.0" // or kotlin("jvm")
  kotlin("plugin.serialization") version "2.4.0"

  id("sh.ondr.mcp4k") version "0.4.11" // ()
println(result) // --> "desrever tnaw ew gnirts emoS"

If you want to get notified when the server changes its tools, you can provide a callback:

val client = Client.Builder()
  // ...
  .withOnToolsChanged { updatedTools: List ->
    println("Updated tools: $updatedTools")
  }
  .build()

Transport Logging

You can observe raw incoming/outgoing messages by providing ``withTransportLogger`` lambdas:

val server = Server.Builder()
  .withTransport(StdioTransport())
  .withTransportLogger(
    logIncoming = { msg -> println("SERVER INCOMING: $msg") },
    logOutgoing = { msg -> println("SERVER OUTGOING: $msg") },
  )
  .build()

Both ``Server` and `Client`` accept this configuration. Super useful for debugging and tests.


Tools

Let's look at a more advanced tool example:

@JsonSchema @Serializable
enum class Priority {
  LOW, NORMAL, HIGH
}

/**
 * @property title The email's title
 * @property body The email's body
 * @property priority The email's priority
 */
@JsonSchema @Serializable
data class Email(
  val title: String,
  val body: String?,
  val priority: Priority = Priority.NORMAL,
)

/**
 * Sends an email
 * @param recipients The email addresses of the recipients
 * @param email The email to send
 */
@McpTool
fun sendEmail(
  recipients: List,
  email: Email,
) = buildString {
  append("Email sent to ${recipients.joinToString()} with ")
  append("title '${email.title}' and ")
  append("body '${email.body}' and ")
  append("priority ${email.priority}")
}.toTextContent()

When clients call tools/list, they see a JSON schema describing the tool's input:

{
  "type": "object",
  "description": "Sends an email",
  "properties": {
    "recipients": {
      "type": "array",
      "description": "The email addresses of the recipients",
      "items": {
        "type": "string"
      }
    },
    "email": {
      "type": "object",
      "description": "The email to send",
      "properties": {
        "title": {
          "type": "string",
          "description": "The email's title"
        },
        "body": {
          "type": "string",
          "description": "The email's body"
        },
        "priority": {
          "type": "string",
          "description": "The email's priority",
          "enum": [
            "LOW",
            "NORMAL",
            "HIGH"
          ]
        }
      },
      "required": [
        "title"
      ]
    }
  },
  "required": [
    "recipients",
    "email"
  ]
}

KDoc parameter descriptions are type-safe and will throw a compile-time error if you specify a non-existing property. Tool call invocation and type-safe deserialization will be handled by mcp4k.

Server can also add or remove tools at runtime:

server.addTool(::sendEmail)
// ...
server.removeTool(::sendEmail)

Both calls will automatically send ToolListChanged notifications to the client.

Tools can also be added or removed from inside tool functions if they are implemented as Server extension functions:

@McpTool
fun Server.toolThatAddsSecondTool(): ToolContent {
  addTool(::secondTool)
  return "Second tool added!".toTextContent()
}

Prompts

Annotate functions with ``@McpPrompt`` to define parameterized conversation templates:

@McpPrompt
fun codeReviewPrompt(code: String) = buildPrompt {
  user("Please review the following code:")
  user("'''\n$code\n'''")
}

Clients can call ``prompts/get`` to retrieve the underlying messages.


Server Context

In some cases, you want multiple tools or prompts to share state. mcp4k allows you to attach a custom context object that tools and prompts can reference.

1) Create a ServerContext object 2) Pass it in with ``.withContext(...)` 3) Each tool or prompt can access it by calling `getContextAs()``

For example:

// 1) Create your context
class MyServerContext : ServerContext {
  var userName: String = ""
}

// 2) A tool function that writes into the context
@McpTool
fun Server.setUserName(name: String): ToolContent {
  getContextAs().userName = name
  return "Username set to: $name".toTextContent()
}

// 3) Another tool that reads from the context
@McpTool
fun Server.greetUser(): ToolContent {
  val name = getContextAs().userName
  if (name.isEmpty()) return "No user set yet!".toTextContent()
  return "Hello, $name!".toTextContent()
}

fun main() = runBlocking {
  val context = MyServerContext()
  val server = Server.Builder()
    .withContext(context) // ().userName = name
  addTool(Server::greetUser) // Now, add greetUser
  return "Username set to: $name".toTextContent()
}

Resources

Resources in MCP allow servers to expose data that clients can read. The ResourceProvider interface is the core abstraction for implementing resource support:

interface ResourceProvider {
  suspend fun listResources(): List
  suspend fun readResource(uri: String): ResourceContents
  suspend fun listResourceTemplates(): List
  suspend fun subscribe(uri: String)
  suspend fun unsubscribe(uri: String)
  fun onResourceListChanged(callback: () -> Unit)
  fun onResourceUpdated(callback: (uri: String) -> Unit)
}

You can implement this interface to expose any type of data as resources - databases, APIs, files, or any other data source.

File-Based Resource Providers

For common file-based use cases, mcp4k provides ready-to-use implementations in the optional mcp4k-file-provider module. See the [file provider documentation](mcp4k-file-provider/README.md) for details on:

  • DiscreteFileProvider - Expose specific files with discrete URIs
  • TemplateFileProvider - Expose entire directories with URI templates

Creating Custom Resource Providers

Here's a simple example of a custom resource provider:

class DatabaseResourceProvider : ResourceProvider {
  override suspend fun listResources(): List {
    return listOf(
      Resource(
        uri = "db://users",
        name = "Users Table",
        description = "Access to user data",
        mimeType = "application/json"
      )
    )
  }
  
  override suspend fun readResource(uri: String): ResourceContents {
    return when (uri) {
      "db://users" -> ResourceContents(
        uri = uri,
        mimeType = "application/json",
        text = fetchUsersAsJson()
      )
      else -> throw ResourceNotFoundException(uri)
    }
  }
  
  // Implement other methods as needed...
}

Then add it to your server:

val server = Server.Builder()
  .withResourceProvider(DatabaseResourceProvider())
  .withTransport(StdioTransport())
  .build()

Sampling

Clients can fulfill server-initiated LLM requests by providing a SamplingProvider.

In a real application, you would call your favorite LLM API (e.g., OpenAI, Anthropic) inside the provider. Here’s a simplified example that always returns a dummy completion:

// 1) Define a sampling provider
val samplingProvider = SamplingProvider { params: CreateMessageParams ->
  CreateMessageResult(
    model = "dummy-model",
    role = Role.ASSISTANT,
    content = TextContent("Dummy completion result"),
    stopReason = "endTurn",
  )
}

// 2) Build the client with sampling support
val client = Client.Builder()
  .withTransport(StdioTransport())
  .withPermissionCallback { userApprovable -> 
    // Prompt the user for confirmation here
    true 
  }
  .withSamplingProvider(samplingProvider) // Register the provider
  .build()

runBlocking {
  client.start()
  client.initialize()

  // Now, if a server sends a "sampling/createMessage" request, 
  // the samplingProvider will be invoked to generate a response.
}

Request Cancellations

mcp4k uses Kotlin coroutines for cooperative cancellation. For example, a long-running server tool:

@McpTool
suspend fun slowToolOperation(iterations: Int = 10): ToolContent {
  for (i in 1..iterations) {
    delay(1000)
  }
  return "Operation completed after $iterations".toTextContent()
}

The client can cancel mid-operation:

val requestJob = launch {
  client.sendRequest { id ->
    CallToolRequest(
      id = id,
      params = CallToolRequest.CallToolParams(
        name = "slowToolOperation",
        arguments = mapOf("iterations" to 20),
      ),
    )
  }
}
delay(600)
requestJob.cancel("User doesn't want to wait anymore")

Under the hood, mcp4k sends a notification to the server:

{
  "method": "notifications/cancelled",
  "jsonrpc": "2.0",
  "params": {
    "requestId": "2",
    "reason": "Client doesn't want to wait anymore"
  }
}

and the server will abort the suspended tool operation.


Roadmap

✅ Add resource capability
✅ @McpTool and @McpPrompt functions
✅ Request cancellations
✅ Pagination
✅ Sampling (client-side)
✅ Roots
✅ Transport logging
✅ onToolsChanged callback in Client
⬜ Support other Kotlin versions
⬜ Completions
⬜ Support logging levels
⬜ Proper version negotiation
⬜ Emit progress notifications from @McpTool functions
⬜ Proper MIME detection
⬜ Add FileWatcher to automate resources/updated notifications
⬜ HTTP-Streaming transport
⬜ Add references, property descriptions and validation keywords to the JSON schemas

How mcp4k Works

  • Annotated ``@McpTool` and `@McpPrompt`` functions are processed at compile time.
  • mcp4k generates JSON schemas, request handlers, and registration code automatically.
  • Generated code is injected during Kotlin's IR compilation phase, guaranteeing type-safe usage.
  • If your KDoc references unknown parameters, the build fails, forcing you to keep docs in sync with code.

Contributing

Issues and pull requests are welcome! Feel free to open a discussion or contribute improvements.

License: mcp4k is available under the [Apache License 2.0](./LICENSE).

Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.