Skip to content

Commit cfa1848

Browse files
committed
Implement Tool.Annotations
1 parent 402904e commit cfa1848

File tree

1 file changed

+79
-2
lines changed

1 file changed

+79
-2
lines changed

Sources/MCP/Server/Tools.swift

+79-2
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,81 @@ public struct Tool: Hashable, Codable, Sendable {
1616
/// The tool input schema
1717
public let inputSchema: Value?
1818

19-
public init(name: String, description: String, inputSchema: Value? = nil) {
19+
/// Annotations that provide display-facing and operational information for a Tool.
20+
///
21+
/// - Note: All properties in `ToolAnnotations` are **hints**.
22+
/// They are not guaranteed to provide a faithful description of
23+
/// tool behavior (including descriptive properties like `title`).
24+
///
25+
/// Clients should never make tool use decisions based on `ToolAnnotations`
26+
/// received from untrusted servers.
27+
public struct Annotations: Hashable, Codable, Sendable, ExpressibleByNilLiteral {
28+
/// A human-readable title for the tool
29+
public var title: String?
30+
31+
/// If true, the tool may perform destructive updates to its environment.
32+
/// If false, the tool performs only additive updates.
33+
/// (This property is meaningful only when `readOnlyHint == false`)
34+
///
35+
/// When unspecified, the implicit default is `true`.
36+
public var destructiveHint: Bool?
37+
38+
/// If true, calling the tool repeatedly with the same arguments
39+
/// will have no additional effect on its environment.
40+
/// (This property is meaningful only when `readOnlyHint == false`)
41+
///
42+
/// When unspecified, the implicit default is `false`.
43+
public var idempotentHint: Bool?
44+
45+
/// If true, this tool may interact with an "open world" of external
46+
/// entities. If false, the tool's domain of interaction is closed.
47+
/// For example, the world of a web search tool is open, whereas that
48+
/// of a memory tool is not.
49+
///
50+
/// When unspecified, the implicit default is `true`.
51+
public var openWorldHint: Bool?
52+
53+
/// If true, the tool does not modify its environment.
54+
///
55+
/// When unspecified, the implicit default is `false`.
56+
public var readOnlyHint: Bool?
57+
58+
/// Returns true if all properties are nil
59+
public var isEmpty: Bool {
60+
title == nil && readOnlyHint == nil && destructiveHint == nil && idempotentHint == nil
61+
&& openWorldHint == nil
62+
}
63+
64+
public init(
65+
title: String? = nil,
66+
readOnlyHint: Bool? = nil,
67+
destructiveHint: Bool? = nil,
68+
idempotentHint: Bool? = nil,
69+
openWorldHint: Bool? = nil
70+
) {
71+
self.title = title
72+
self.readOnlyHint = readOnlyHint
73+
self.destructiveHint = destructiveHint
74+
self.idempotentHint = idempotentHint
75+
self.openWorldHint = openWorldHint
76+
}
77+
78+
public init(nilLiteral: ()) {}
79+
}
80+
81+
/// Optional annotations that provide display-facing and operational information
82+
public let annotations: Annotations
83+
84+
public init(
85+
name: String,
86+
description: String,
87+
inputSchema: Value? = nil,
88+
annotations: Annotations = nil
89+
) {
2090
self.name = name
2191
self.description = description
2292
self.inputSchema = inputSchema
93+
self.annotations = annotations
2394
}
2495

2596
/// Content types that can be returned by a tool
@@ -92,13 +163,16 @@ public struct Tool: Hashable, Codable, Sendable {
92163
case name
93164
case description
94165
case inputSchema
166+
case annotations
95167
}
96168

97169
public init(from decoder: Decoder) throws {
98170
let container = try decoder.container(keyedBy: CodingKeys.self)
99171
name = try container.decode(String.self, forKey: .name)
100172
description = try container.decode(String.self, forKey: .description)
101173
inputSchema = try container.decodeIfPresent(Value.self, forKey: .inputSchema)
174+
annotations =
175+
try container.decodeIfPresent(Tool.Annotations.self, forKey: .annotations) ?? .init()
102176
}
103177

104178
public func encode(to encoder: Encoder) throws {
@@ -108,6 +182,9 @@ public struct Tool: Hashable, Codable, Sendable {
108182
if let schema = inputSchema {
109183
try container.encode(schema, forKey: .inputSchema)
110184
}
185+
if !annotations.isEmpty {
186+
try container.encode(annotations, forKey: .annotations)
187+
}
111188
}
112189
}
113190

@@ -120,7 +197,7 @@ public enum ListTools: Method {
120197

121198
public struct Parameters: NotRequired, Hashable, Codable, Sendable {
122199
public let cursor: String?
123-
200+
124201
public init() {
125202
self.cursor = nil
126203
}

0 commit comments

Comments
 (0)