feat(server): add support for tool structured content and output schema (#146)
Fixes #139
This commit is contained in:
parent
675cc73c6d
commit
12f686fa4b
4 changed files with 123 additions and 20 deletions
|
|
@ -186,12 +186,30 @@ public open class Server(
|
|||
_onClose()
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a single tool. The client can then call this tool.
|
||||
*
|
||||
* @param tool A [Tool] object describing the tool.
|
||||
* @param handler A suspend function that handles executing the tool when called by the client.
|
||||
* @throws IllegalStateException If the server does not support tools.
|
||||
*/
|
||||
public fun addTool(tool: Tool, handler: suspend (CallToolRequest) -> CallToolResult) {
|
||||
if (capabilities.tools == null) {
|
||||
logger.error { "Failed to add tool '${tool.name}': Server does not support tools capability" }
|
||||
throw IllegalStateException("Server does not support tools capability. Enable it in ServerOptions.")
|
||||
}
|
||||
logger.info { "Registering tool: ${tool.name}" }
|
||||
_tools.update { current -> current.put(tool.name, RegisteredTool(tool, handler)) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a single tool. The client can then call this tool.
|
||||
*
|
||||
* @param name The name of the tool.
|
||||
* @param description A human-readable description of what the tool does.
|
||||
* @param inputSchema The expected input schema for the tool.
|
||||
* @param outputSchema The optional expected output schema for the tool.
|
||||
* @param toolAnnotations Optional additional tool information.
|
||||
* @param handler A suspend function that handles executing the tool when called by the client.
|
||||
* @throws IllegalStateException If the server does not support tools.
|
||||
*/
|
||||
|
|
@ -199,17 +217,12 @@ public open class Server(
|
|||
name: String,
|
||||
description: String,
|
||||
inputSchema: Tool.Input = Tool.Input(),
|
||||
outputSchema: Tool.Output? = null,
|
||||
toolAnnotations: ToolAnnotations? = null,
|
||||
handler: suspend (CallToolRequest) -> CallToolResult
|
||||
) {
|
||||
if (capabilities.tools == null) {
|
||||
logger.error { "Failed to add tool '$name': Server does not support tools capability" }
|
||||
throw IllegalStateException("Server does not support tools capability. Enable it in ServerOptions.")
|
||||
}
|
||||
logger.info { "Registering tool: $name" }
|
||||
_tools.update { current ->
|
||||
current.put(name, RegisteredTool(Tool(name, description, inputSchema, toolAnnotations), handler))
|
||||
}
|
||||
val tool = Tool(name, description, inputSchema, outputSchema, toolAnnotations)
|
||||
addTool(tool, handler)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1105,7 +1105,10 @@ public data class Tool(
|
|||
* A JSON object defining the expected parameters for the tool.
|
||||
*/
|
||||
val inputSchema: Input,
|
||||
|
||||
/**
|
||||
* An optional JSON object defining the expected output schema for the tool.
|
||||
*/
|
||||
val outputSchema: Output?,
|
||||
/**
|
||||
* Optional additional tool information.
|
||||
*/
|
||||
|
|
@ -1120,6 +1123,16 @@ public data class Tool(
|
|||
@EncodeDefault
|
||||
val type: String = "object"
|
||||
}
|
||||
|
||||
@Serializable
|
||||
public data class Output(
|
||||
val properties: JsonObject = EmptyJsonObject,
|
||||
val required: List<String>? = null,
|
||||
) {
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@EncodeDefault
|
||||
val type: String = "object"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1149,6 +1162,7 @@ public class ListToolsResult(
|
|||
@Serializable
|
||||
public sealed interface CallToolResultBase : ServerResult {
|
||||
public val content: List<PromptMessageContent>
|
||||
public val structuredContent: JsonObject?
|
||||
public val isError: Boolean? get() = false
|
||||
}
|
||||
|
||||
|
|
@ -1158,6 +1172,7 @@ public sealed interface CallToolResultBase : ServerResult {
|
|||
@Serializable
|
||||
public class CallToolResult(
|
||||
override val content: List<PromptMessageContent>,
|
||||
override val structuredContent: JsonObject? = null,
|
||||
override val isError: Boolean? = false,
|
||||
override val _meta: JsonObject = EmptyJsonObject,
|
||||
) : CallToolResultBase
|
||||
|
|
@ -1168,6 +1183,7 @@ public class CallToolResult(
|
|||
@Serializable
|
||||
public class CompatibilityCallToolResult(
|
||||
override val content: List<PromptMessageContent>,
|
||||
override val structuredContent: JsonObject? = null,
|
||||
override val isError: Boolean? = false,
|
||||
override val _meta: JsonObject = EmptyJsonObject,
|
||||
public val toolResult: JsonObject = EmptyJsonObject,
|
||||
|
|
|
|||
|
|
@ -25,6 +25,24 @@ class ToolSerializationTest {
|
|||
}
|
||||
},
|
||||
"required": ["location"]
|
||||
},
|
||||
"outputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"temperature": {
|
||||
"type": "number",
|
||||
"description": "Temperature in celsius"
|
||||
},
|
||||
"conditions": {
|
||||
"type": "string",
|
||||
"description": "Weather conditions description"
|
||||
},
|
||||
"humidity": {
|
||||
"type": "number",
|
||||
"description": "Humidity percentage"
|
||||
}
|
||||
},
|
||||
"required": ["temperature", "conditions", "humidity"]
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
|
|
@ -41,6 +59,23 @@ class ToolSerializationTest {
|
|||
})
|
||||
},
|
||||
required = listOf("location")
|
||||
),
|
||||
outputSchema = Tool.Output(
|
||||
properties = buildJsonObject {
|
||||
put("temperature", buildJsonObject {
|
||||
put("type", JsonPrimitive("number"))
|
||||
put("description", JsonPrimitive("Temperature in celsius"))
|
||||
})
|
||||
put("conditions", buildJsonObject {
|
||||
put("type", JsonPrimitive("string"))
|
||||
put("description", JsonPrimitive("Weather conditions description"))
|
||||
})
|
||||
put("humidity", buildJsonObject {
|
||||
put("type", JsonPrimitive("number"))
|
||||
put("description", JsonPrimitive("Humidity percentage"))
|
||||
})
|
||||
},
|
||||
required = listOf("temperature", "conditions", "humidity")
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -587,7 +587,8 @@ class ClientTest {
|
|||
name = "testTool",
|
||||
description = "testTool description",
|
||||
annotations = null,
|
||||
inputSchema = Tool.Input()
|
||||
inputSchema = Tool.Input(),
|
||||
outputSchema = null
|
||||
)
|
||||
), nextCursor = null
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue