Skip to content

Commit 9174b21

Browse files
committed
feat: Add taskHint and builder to schema
Signed-off-by: He-Pin <[email protected]>
1 parent e307927 commit 9174b21

File tree

2 files changed

+146
-21
lines changed

2 files changed

+146
-21
lines changed

mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java

Lines changed: 131 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
* @author Luca Chang
3434
* @author Surbhi Bansal
3535
* @author Anurag Pant
36+
* @author Pin He
3637
*/
3738
public final class McpSchema {
3839

@@ -111,6 +112,7 @@ private McpSchema() {
111112
// ---------------------------
112113
// JSON-RPC Error Codes
113114
// ---------------------------
115+
114116
/**
115117
* Standard error codes used in MCP JSON-RPC responses.
116118
*/
@@ -154,10 +156,10 @@ public static final class ErrorCodes {
154156
public interface Meta {
155157

156158
/**
159+
* @return additional metadata related to this resource.
157160
* @see <a href=
158161
* "https://modelcontextprotocol.io/specification/2025-06-18/basic/index#meta">Specification</a>
159162
* for notes on _meta usage
160-
* @return additional metadata related to this resource.
161163
*/
162164
Map<String, Object> meta();
163165

@@ -315,6 +317,7 @@ public record JSONRPCError( // @formatter:off
315317
// ---------------------------
316318
// Initialization
317319
// ---------------------------
320+
318321
/**
319322
* This request is sent from the client to the server when it first connects, asking
320323
* it to begin initialization.
@@ -626,14 +629,15 @@ public Implementation(String name, String version) {
626629
// Existing Enums and Base Types (from previous implementation)
627630
public enum Role {
628631

629-
// @formatter:off
632+
// @formatter:off
630633
@JsonProperty("user") USER,
631634
@JsonProperty("assistant") ASSISTANT
632635
} // @formatter:on
633636

634637
// ---------------------------
635638
// Resource Interfaces
636639
// ---------------------------
640+
637641
/**
638642
* Base for objects that include optional annotations for the client. The client can
639643
* use annotations to inform how objects are used or displayed
@@ -706,7 +710,7 @@ public interface Identifier {
706710
/**
707711
* Intended for UI and end-user contexts — optimized to be human-readable and
708712
* easily understood, even by those unfamiliar with domain-specific terminology.
709-
*
713+
* <p>
710714
* If not provided, the name should be used for display.
711715
*/
712716
String title();
@@ -858,8 +862,8 @@ public Resource build() {
858862
* @param mimeType The MIME type of this resource, if known.
859863
* @param annotations Optional annotations for the client. The client can use
860864
* annotations to inform how objects are used or displayed.
861-
* @see <a href="https://datatracker.ietf.org/doc/html/rfc6570">RFC 6570</a>
862865
* @param meta See specification for notes on _meta usage
866+
* @see <a href="https://datatracker.ietf.org/doc/html/rfc6570">RFC 6570</a>
863867
*
864868
*/
865869
@JsonInclude(JsonInclude.Include.NON_ABSENT)
@@ -1130,6 +1134,7 @@ public BlobResourceContents(String uri, String mimeType, String blob) {
11301134
// ---------------------------
11311135
// Prompt Interfaces
11321136
// ---------------------------
1137+
11331138
/**
11341139
* A prompt or prompt template that the server offers.
11351140
*
@@ -1180,7 +1185,7 @@ public PromptArgument(String name, String description, Boolean required) {
11801185

11811186
/**
11821187
* Describes a message returned as part of a prompt.
1183-
*
1188+
* <p>
11841189
* This is similar to `SamplingMessage`, but also supports the embedding of resources
11851190
* from the MCP server.
11861191
*
@@ -1207,7 +1212,7 @@ public record PromptMessage( // @formatter:off
12071212
public record ListPromptsResult( // @formatter:off
12081213
@JsonProperty("prompts") List<Prompt> prompts,
12091214
@JsonProperty("nextCursor") String nextCursor,
1210-
@JsonProperty("_meta") Map<String, Object> meta) implements Result { // @formatter:on
1215+
@JsonProperty("_meta") Map<String, Object> meta) implements Result { // @formatter:on
12111216

12121217
public ListPromptsResult(List<Prompt> prompts, String nextCursor) {
12131218
this(prompts, nextCursor, null);
@@ -1255,6 +1260,7 @@ public GetPromptResult(String description, List<PromptMessage> messages) {
12551260
// ---------------------------
12561261
// Tool Interfaces
12571262
// ---------------------------
1263+
12581264
/**
12591265
* The server's response to a tools/list request from the client.
12601266
*
@@ -1297,12 +1303,51 @@ public record JsonSchema( // @formatter:off
12971303
}
12981304

12991305
/**
1300-
* Additional properties describing a Tool to clients.
1306+
* The task hint.
1307+
* <p>
1308+
* If taskHint is {@code always}, clients SHOULD invoke the tool as a task. Servers
1309+
* MAY return a -32601 (Method not found) error if a client does not attempt to do so.
1310+
* <p>
1311+
* If taskHint is {@code optional}, clients MAY invoke the tool as a task or as a
1312+
* normal request.
1313+
* <p>
1314+
* If taskHint is not present or {@code never}, clients MUST NOT attempt to invoke the
1315+
* tool as a task. Servers SHOULD return a -32601 (Method not found) error if a client
1316+
* attempts to do so. This is the default behavior.
13011317
*
1318+
*/
1319+
public static enum TaskHint {
1320+
1321+
ALWAYS("always"), OPTIONAL("optional"), NEVER("never");
1322+
1323+
private final String value;
1324+
1325+
TaskHint(final String value) {
1326+
this.value = value;
1327+
}
1328+
1329+
public String getValue() {
1330+
return value;
1331+
}
1332+
1333+
public static TaskHint fromValue(String value) {
1334+
for (TaskHint hint : TaskHint.values()) {
1335+
if (hint.value.equalsIgnoreCase(value)) {
1336+
return hint;
1337+
}
1338+
}
1339+
throw new IllegalArgumentException("Unknown TaskHint value: " + value);
1340+
}
1341+
1342+
}
1343+
1344+
/**
1345+
* Additional properties describing a Tool to clients.
1346+
* <p>
13021347
* NOTE: all properties in ToolAnnotations are **hints**. They are not guaranteed to
13031348
* provide a faithful description of tool behavior (including descriptive properties
13041349
* like `title`).
1305-
*
1350+
* <p>
13061351
* Clients should never make tool use decisions based on ToolAnnotations received from
13071352
* untrusted servers.
13081353
*/
@@ -1314,7 +1359,73 @@ public record ToolAnnotations( // @formatter:off
13141359
@JsonProperty("destructiveHint") Boolean destructiveHint,
13151360
@JsonProperty("idempotentHint") Boolean idempotentHint,
13161361
@JsonProperty("openWorldHint") Boolean openWorldHint,
1317-
@JsonProperty("returnDirect") Boolean returnDirect) { // @formatter:on
1362+
@JsonProperty("returnDirect") Boolean returnDirect,
1363+
@JsonProperty("taskHint") String taskHint) { // @formatter:on
1364+
1365+
public static Builder builder() {
1366+
return new Builder();
1367+
}
1368+
1369+
public static class Builder {
1370+
1371+
private String title;
1372+
1373+
private boolean readOnlyHint;
1374+
1375+
private boolean destructiveHint;
1376+
1377+
private boolean idempotentHint;
1378+
1379+
private boolean openWorldHint;
1380+
1381+
private boolean returnDirect;
1382+
1383+
private String taskHint;
1384+
1385+
public Builder title(String title) {
1386+
this.title = title;
1387+
return this;
1388+
}
1389+
1390+
public Builder readOnlyHint(boolean readOnlyHint) {
1391+
this.readOnlyHint = readOnlyHint;
1392+
return this;
1393+
}
1394+
1395+
public Builder destructiveHint(boolean destructiveHint) {
1396+
this.destructiveHint = destructiveHint;
1397+
return this;
1398+
}
1399+
1400+
public Builder idempotentHint(boolean idempotentHint) {
1401+
this.idempotentHint = idempotentHint;
1402+
return this;
1403+
}
1404+
1405+
public Builder openWorldHint(boolean openWorldHint) {
1406+
this.openWorldHint = openWorldHint;
1407+
return this;
1408+
}
1409+
1410+
public Builder returnDirect(boolean returnDirect) {
1411+
this.returnDirect = returnDirect;
1412+
return this;
1413+
}
1414+
1415+
/**
1416+
* The task hint, {@link TaskHint}
1417+
*/
1418+
public Builder taskHint(String taskHint) {
1419+
this.taskHint = taskHint;
1420+
return this;
1421+
}
1422+
1423+
public ToolAnnotations build() {
1424+
return new ToolAnnotations(title, readOnlyHint, destructiveHint, idempotentHint, openWorldHint,
1425+
returnDirect, taskHint);
1426+
}
1427+
1428+
}
13181429
}
13191430

13201431
/**
@@ -1686,6 +1797,7 @@ public CallToolResult build() {
16861797
// ---------------------------
16871798
// Sampling Interfaces
16881799
// ---------------------------
1800+
16891801
/**
16901802
* The server's preferences for model selection, requested of the client during
16911803
* sampling.
@@ -1837,7 +1949,7 @@ public CreateMessageRequest(List<SamplingMessage> messages, ModelPreferences mod
18371949

18381950
public enum ContextInclusionStrategy {
18391951

1840-
// @formatter:off
1952+
// @formatter:off
18411953
@JsonProperty("none") NONE,
18421954
@JsonProperty("thisServer") THIS_SERVER,
18431955
@JsonProperty("allServers")ALL_SERVERS
@@ -1951,7 +2063,7 @@ public record CreateMessageResult( // @formatter:off
19512063

19522064
public enum StopReason {
19532065

1954-
// @formatter:off
2066+
// @formatter:off
19552067
@JsonProperty("endTurn") END_TURN("endTurn"),
19562068
@JsonProperty("stopSequence") STOP_SEQUENCE("stopSequence"),
19572069
@JsonProperty("maxTokens") MAX_TOKENS("maxTokens"),
@@ -2032,6 +2144,7 @@ public CreateMessageResult build() {
20322144
}
20332145

20342146
// Elicitation
2147+
20352148
/**
20362149
* A request from the server to elicit additional information from the user via the
20372150
* client.
@@ -2114,7 +2227,7 @@ public record ElicitResult( // @formatter:off
21142227

21152228
public enum Action {
21162229

2117-
// @formatter:off
2230+
// @formatter:off
21182231
@JsonProperty("accept") ACCEPT,
21192232
@JsonProperty("decline") DECLINE,
21202233
@JsonProperty("cancel") CANCEL
@@ -2162,6 +2275,7 @@ public ElicitResult build() {
21622275
// ---------------------------
21632276
// Pagination Interfaces
21642277
// ---------------------------
2278+
21652279
/**
21662280
* A request that supports pagination using cursors.
21672281
*
@@ -2202,6 +2316,7 @@ public record PaginatedResult(@JsonProperty("nextCursor") String nextCursor) {
22022316
// ---------------------------
22032317
// Progress and Logging
22042318
// ---------------------------
2319+
22052320
/**
22062321
* The Model Context Protocol (MCP) supports optional progress tracking for
22072322
* long-running operations through notification messages. Either side can send
@@ -2313,7 +2428,7 @@ public LoggingMessageNotification build() {
23132428

23142429
public enum LoggingLevel {
23152430

2316-
// @formatter:off
2431+
// @formatter:off
23172432
@JsonProperty("debug") DEBUG(0),
23182433
@JsonProperty("info") INFO(1),
23192434
@JsonProperty("notice") NOTICE(2),
@@ -2371,7 +2486,7 @@ public sealed interface CompleteReference permits PromptReference, ResourceRefer
23712486
public record PromptReference( // @formatter:off
23722487
@JsonProperty("type") String type,
23732488
@JsonProperty("name") String name,
2374-
@JsonProperty("title") String title ) implements McpSchema.CompleteReference, Identifier { // @formatter:on
2489+
@JsonProperty("title") String title) implements McpSchema.CompleteReference, Identifier { // @formatter:on
23752490

23762491
public static final String TYPE = "ref/prompt";
23772492

@@ -2667,7 +2782,7 @@ public AudioContent(Annotations annotations, String data, String mimeType) {
26672782

26682783
/**
26692784
* The contents of a resource, embedded into a prompt or tool call result.
2670-
*
2785+
* <p>
26712786
* It is up to the client how best to render embedded resources for the benefit of the
26722787
* LLM and/or the user.
26732788
*
@@ -2821,6 +2936,7 @@ public ResourceLink build() {
28212936
// ---------------------------
28222937
// Roots
28232938
// ---------------------------
2939+
28242940
/**
28252941
* Represents a root directory or file that the server can operate on.
28262942
*

mcp-core/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -879,8 +879,10 @@ void testToolWithAnnotations() throws Exception {
879879
"required": ["name"]
880880
}
881881
""";
882-
McpSchema.ToolAnnotations annotations = new McpSchema.ToolAnnotations("A test tool", false, false, false, false,
883-
false);
882+
McpSchema.ToolAnnotations annotations = McpSchema.ToolAnnotations.builder()
883+
.title("A test tool")
884+
.taskHint(McpSchema.TaskHint.OPTIONAL.getValue())
885+
.build();
884886

885887
McpSchema.Tool tool = McpSchema.Tool.builder()
886888
.name("test-tool")
@@ -911,7 +913,8 @@ void testToolWithAnnotations() throws Exception {
911913
"destructiveHint":false,
912914
"idempotentHint":false,
913915
"openWorldHint":false,
914-
"returnDirect":false
916+
"returnDirect":false,
917+
"taskHint":"optional"
915918
}
916919
}
917920
"""));
@@ -1014,8 +1017,13 @@ void testToolWithOutputSchemaAndAnnotations() throws Exception {
10141017
}
10151018
""";
10161019

1017-
McpSchema.ToolAnnotations annotations = new McpSchema.ToolAnnotations("A test tool with output", true, false,
1018-
true, false, true);
1020+
McpSchema.ToolAnnotations annotations = McpSchema.ToolAnnotations.builder()
1021+
.title("A test tool with output")
1022+
.readOnlyHint(true)
1023+
.idempotentHint(true)
1024+
.returnDirect(true)
1025+
.taskHint(McpSchema.TaskHint.OPTIONAL.getValue())
1026+
.build();
10191027

10201028
McpSchema.Tool tool = McpSchema.Tool.builder()
10211029
.name("test-tool")
@@ -1053,7 +1061,8 @@ void testToolWithOutputSchemaAndAnnotations() throws Exception {
10531061
"destructiveHint":false,
10541062
"idempotentHint":true,
10551063
"openWorldHint":false,
1056-
"returnDirect":true
1064+
"returnDirect":true,
1065+
"taskHint":"optional"
10571066
}
10581067
}"""));
10591068
}

0 commit comments

Comments
 (0)