Skip to content

Commit 33ac9f3

Browse files
committed
Implement DSL APIs builders for request types
1 parent d2f0893 commit 33ac9f3

File tree

15 files changed

+2787
-0
lines changed

15 files changed

+2787
-0
lines changed

kotlin-sdk-core/api/kotlin-sdk-core.api

Lines changed: 293 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package io.modelcontextprotocol.kotlin.sdk.types
2+
3+
/**
4+
* DSL marker annotation for MCP builder classes.
5+
*
6+
* This annotation is used to prevent accidental access to outer DSL scopes
7+
* within nested DSL blocks, ensuring type-safe and unambiguous builder usage.
8+
*
9+
* @see DslMarker
10+
*/
11+
@DslMarker
12+
public annotation class McpDsl
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
package io.modelcontextprotocol.kotlin.sdk.types
2+
3+
import kotlinx.serialization.json.JsonObject
4+
import kotlinx.serialization.json.JsonObjectBuilder
5+
import kotlinx.serialization.json.buildJsonObject
6+
7+
/**
8+
* DSL builder for constructing [ClientCapabilities] instances.
9+
*
10+
* This builder is used within [InitializeRequestBuilder] to configure client capabilities.
11+
* All capabilities are optional - the presence of a capability indicates support for that feature.
12+
*
13+
* ## Available Functions (all optional)
14+
* - [sampling] - Indicates support for sampling from an LLM
15+
* - [roots] - Indicates support for listing roots
16+
* - [elicitation] - Indicates support for elicitation from the server
17+
* - [experimental] - Defines experimental, non-standard capabilities
18+
*
19+
* Example usage within [initializeRequest][io.modelcontextprotocol.kotlin.sdk.types.initializeRequest]:
20+
* ```kotlin
21+
* val request = initializeRequest {
22+
* protocolVersion = "1.0"
23+
* capabilities {
24+
* sampling(ClientCapabilities.sampling)
25+
* roots(listChanged = true)
26+
* experimental {
27+
* put("customFeature", JsonPrimitive(true))
28+
* }
29+
* }
30+
* info("MyClient", "1.0.0")
31+
* }
32+
* ```
33+
*
34+
* @see ClientCapabilities
35+
* @see InitializeRequestBuilder.capabilities
36+
*/
37+
@McpDsl
38+
public class ClientCapabilitiesBuilder @PublishedApi internal constructor() {
39+
private var sampling: JsonObject? = null
40+
private var roots: ClientCapabilities.Roots? = null
41+
private var elicitation: JsonObject? = null
42+
private var experimental: JsonObject? = null
43+
44+
/**
45+
* Indicates that the client supports sampling from an LLM.
46+
*
47+
* Use [ClientCapabilities.sampling] for default empty configuration.
48+
*
49+
* Example:
50+
* ```kotlin
51+
* capabilities {
52+
* sampling(ClientCapabilities.sampling)
53+
* }
54+
* ```
55+
*
56+
* @param value The sampling capability configuration
57+
*/
58+
public fun sampling(value: JsonObject) {
59+
this.sampling = value
60+
}
61+
62+
/**
63+
* Indicates that the client supports sampling from an LLM with custom configuration.
64+
*
65+
* Example:
66+
* ```kotlin
67+
* capabilities {
68+
* sampling {
69+
* put("temperature", JsonPrimitive(0.7))
70+
* }
71+
* }
72+
* ```
73+
*
74+
* @param block Lambda for building the sampling configuration
75+
*/
76+
public fun sampling(block: JsonObjectBuilder.() -> Unit): Unit = sampling(buildJsonObject(block))
77+
78+
/**
79+
* Indicates that the client supports listing roots.
80+
*
81+
* Example with listChanged notification:
82+
* ```kotlin
83+
* capabilities {
84+
* roots(listChanged = true)
85+
* }
86+
* ```
87+
*
88+
* Example without listChanged:
89+
* ```kotlin
90+
* capabilities {
91+
* roots()
92+
* }
93+
* ```
94+
*
95+
* @param listChanged Whether the client will emit notifications when the list of roots changes
96+
*/
97+
public fun roots(listChanged: Boolean? = null) {
98+
this.roots = ClientCapabilities.Roots(listChanged)
99+
}
100+
101+
/**
102+
* Indicates that the client supports elicitation from the server.
103+
*
104+
* Use [ClientCapabilities.elicitation] for default empty configuration.
105+
*
106+
* Example:
107+
* ```kotlin
108+
* capabilities {
109+
* elicitation(ClientCapabilities.elicitation)
110+
* }
111+
* ```
112+
*
113+
* @param value The elicitation capability configuration
114+
*/
115+
public fun elicitation(value: JsonObject) {
116+
this.elicitation = value
117+
}
118+
119+
/**
120+
* Indicates that the client supports elicitation from the server with custom configuration.
121+
*
122+
* Example:
123+
* ```kotlin
124+
* capabilities {
125+
* elicitation {
126+
* put("mode", JsonPrimitive("interactive"))
127+
* }
128+
* }
129+
* ```
130+
*
131+
* @param block Lambda for building the elicitation configuration
132+
*/
133+
public fun elicitation(block: JsonObjectBuilder.() -> Unit): Unit = elicitation(buildJsonObject(block))
134+
135+
/**
136+
* Defines experimental, non-standard capabilities that the client supports.
137+
*
138+
* Example:
139+
* ```kotlin
140+
* capabilities {
141+
* experimental(buildJsonObject {
142+
* put("customFeature", JsonPrimitive(true))
143+
* put("version", JsonPrimitive("1.0"))
144+
* })
145+
* }
146+
* ```
147+
*
148+
* @param value The experimental capabilities configuration
149+
*/
150+
public fun experimental(value: JsonObject) {
151+
this.experimental = value
152+
}
153+
154+
/**
155+
* Defines experimental, non-standard capabilities that the client supports using a DSL builder.
156+
*
157+
* Example:
158+
* ```kotlin
159+
* capabilities {
160+
* experimental {
161+
* put("customFeature", JsonPrimitive(true))
162+
* put("beta", JsonObject(mapOf(
163+
* "enabled" to JsonPrimitive(true),
164+
* "version" to JsonPrimitive("2.0")
165+
* )))
166+
* }
167+
* }
168+
* ```
169+
*
170+
* @param block Lambda for building the experimental capabilities configuration
171+
*/
172+
public fun experimental(block: JsonObjectBuilder.() -> Unit): Unit = experimental(buildJsonObject(block))
173+
174+
@PublishedApi
175+
internal fun build(): ClientCapabilities = ClientCapabilities(
176+
sampling = sampling,
177+
roots = roots,
178+
elicitation = elicitation,
179+
experimental = experimental,
180+
)
181+
}
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
package io.modelcontextprotocol.kotlin.sdk.types
2+
3+
import io.modelcontextprotocol.kotlin.sdk.ExperimentalMcpApi
4+
import kotlin.contracts.ExperimentalContracts
5+
import kotlin.contracts.InvocationKind
6+
import kotlin.contracts.contract
7+
8+
/**
9+
* Creates a [CompleteRequest] using a type-safe DSL builder.
10+
*
11+
* ## Required
12+
* - [argument][CompleteRequestBuilder.argument] - Sets the argument name and value to complete
13+
* - [ref][CompleteRequestBuilder.ref] - Sets the reference to a prompt or resource template
14+
*
15+
* ## Optional
16+
* - [context][CompleteRequestBuilder.context] - Adds additional context for the completion
17+
* - [meta][CompleteRequestBuilder.meta] - Adds metadata to the request
18+
*
19+
* Example with [PromptReference]:
20+
* ```kotlin
21+
* val request = completeRequest {
22+
* argument("query", "user input")
23+
* ref(PromptReference("searchPrompt"))
24+
* }
25+
* ```
26+
*
27+
* Example with [ResourceTemplateReference]:
28+
* ```kotlin
29+
* val request = completeRequest {
30+
* argument("path", "/users/123")
31+
* ref(ResourceTemplateReference("file:///{path}"))
32+
* context {
33+
* put("userId", "123")
34+
* put("role", "admin")
35+
* }
36+
* }
37+
* ```
38+
*
39+
* @param block Configuration lambda for setting up the completion request
40+
* @return A configured [CompleteRequest] instance
41+
* @see CompleteRequestBuilder
42+
*/
43+
@OptIn(ExperimentalContracts::class)
44+
@ExperimentalMcpApi
45+
public inline fun completeRequest(block: CompleteRequestBuilder.() -> Unit): CompleteRequest {
46+
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
47+
return CompleteRequestBuilder().apply(block).build()
48+
}
49+
50+
/**
51+
* DSL builder for constructing [CompleteRequest] instances.
52+
*
53+
* This builder provides methods to configure completion requests for prompts or resource templates.
54+
* Both [argument] and [ref] are required; [context] is optional.
55+
*
56+
* @see completeRequest
57+
*/
58+
@McpDsl
59+
public class CompleteRequestBuilder @PublishedApi internal constructor() : RequestBuilder() {
60+
private var arg: CompleteRequestParams.Argument? = null
61+
private var ref: Reference? = null
62+
private var ctx: CompleteRequestParams.Context? = null
63+
64+
/**
65+
* Sets the argument for completion.
66+
*
67+
* This method specifies the name and value of the argument to be completed.
68+
* This is a required field and must be called exactly once.
69+
*
70+
* Example:
71+
* ```kotlin
72+
* completeRequest {
73+
* argument("query", "SELECT * FROM use")
74+
* // ... other configuration
75+
* }
76+
* ```
77+
*
78+
* @param name The name of the argument to complete
79+
* @param value The partial or full value of the argument
80+
*/
81+
public fun argument(name: String, value: String) {
82+
arg = CompleteRequestParams.Argument(name, value)
83+
}
84+
85+
/**
86+
* Sets the reference to a prompt or resource template.
87+
*
88+
* This method specifies which prompt or resource template the completion request refers to.
89+
*
90+
* Example with prompt:
91+
* ```kotlin
92+
* completeRequest {
93+
* ref(PromptReference("sqlQuery", "SQL Query Builder"))
94+
* // ... other configuration
95+
* }
96+
* ```
97+
*
98+
* Example with resource template:
99+
* ```kotlin
100+
* completeRequest {
101+
* ref(ResourceTemplateReference("file:///{path}"))
102+
* // ... other configuration
103+
* }
104+
* ```
105+
*
106+
* @param value The [Reference] (either [PromptReference] or [ResourceTemplateReference])
107+
*/
108+
public fun ref(value: Reference) {
109+
ref = value
110+
}
111+
112+
/**
113+
* Sets additional context for the completion request using a Map.
114+
*
115+
* This method allows providing additional key-value pairs that may be relevant
116+
* for generating completions. This is an optional field.
117+
*
118+
* Example:
119+
* ```kotlin
120+
* completeRequest {
121+
* context(mapOf("userId" to "123", "role" to "admin"))
122+
* // ... other configuration
123+
* }
124+
* ```
125+
*
126+
* @param arguments A map of context key-value pairs
127+
*/
128+
public fun context(arguments: Map<String, String>) {
129+
ctx = CompleteRequestParams.Context(arguments)
130+
}
131+
132+
/**
133+
* Sets additional context for the completion request using a DSL builder.
134+
*
135+
* This method allows providing additional key-value pairs that may be relevant
136+
* for generating completions using a type-safe builder syntax. This is an optional field.
137+
*
138+
* Example:
139+
* ```kotlin
140+
* completeRequest {
141+
* context {
142+
* put("userId", "123")
143+
* put("role", "admin")
144+
* put("environment", "production")
145+
* }
146+
* // ... other configuration
147+
* }
148+
* ```
149+
*
150+
* @param block Lambda with receiver for building the context map
151+
*/
152+
public fun context(block: MutableMap<String, String>.() -> Unit) {
153+
ctx = CompleteRequestParams.Context(buildMap(block))
154+
}
155+
156+
@PublishedApi
157+
override fun build(): CompleteRequest {
158+
val argument = requireNotNull(arg) {
159+
"Missing required field 'argument(name, value)'. Example: argument(\"query\", \"user input\")"
160+
}
161+
162+
val reference = requireNotNull(ref) {
163+
"Missing required field 'ref(Reference)'. Use ref(PromptReference(\"name\")) or ref(ResourceTemplateReference(\"uri\"))"
164+
}
165+
166+
val params = CompleteRequestParams(argument = argument, ref = reference, context = ctx, meta = meta)
167+
return CompleteRequest(params)
168+
}
169+
}

0 commit comments

Comments
 (0)