cxxmcp 1.1.6
C++ MCP SDK
Loading...
Searching...
No Matches
annotations.hpp
Go to the documentation of this file.
1// Copyright (c) 2025 [caomengxuan666]
2
3#pragma once
4
18
19#include <cmath>
20#include <optional>
21#include <string>
22#include <vector>
23
26
27namespace mcp::protocol {
28
36 std::optional<std::vector<std::string>> audience;
37
39 std::optional<float> priority;
40
42 std::optional<std::string> last_modified;
43
45 Json raw = Json::object();
46};
47
55 std::optional<std::string> title;
57 std::optional<std::string> description_hint;
59 std::optional<bool> destructive_hint;
61 std::optional<bool> read_only_hint;
63 std::optional<bool> open_world_hint;
65 std::optional<bool> idempotent_hint;
67 Json raw = Json::object();
68};
69
71inline Json tool_annotations_to_json(const ToolAnnotations& annotations) {
72 Json json = Json::object();
73 if (annotations.title.has_value()) {
74 json["title"] = *annotations.title;
75 }
76 if (annotations.description_hint.has_value()) {
77 json["descriptionHint"] = *annotations.description_hint;
78 }
79 if (annotations.destructive_hint.has_value()) {
80 json["destructiveHint"] = *annotations.destructive_hint;
81 }
82 if (annotations.read_only_hint.has_value()) {
83 json["readOnlyHint"] = *annotations.read_only_hint;
84 }
85 if (annotations.open_world_hint.has_value()) {
86 json["openWorldHint"] = *annotations.open_world_hint;
87 }
88 if (annotations.idempotent_hint.has_value()) {
89 json["idempotentHint"] = *annotations.idempotent_hint;
90 }
91 if (annotations.raw.is_object()) {
92 for (const auto& item : annotations.raw.items()) {
93 if (!json.contains(item.key())) {
94 json[item.key()] = item.value();
95 }
96 }
97 }
98 return json;
99}
100
103 const Json& json) {
104 if (!json.is_object()) {
105 return mcp::core::unexpected(
106 core::Error{static_cast<int>(ErrorCode::InvalidRequest),
107 "tool annotations must be an object",
108 {}});
109 }
110
111 ToolAnnotations annotations;
112
113 if (json.contains("title")) {
114 if (!json.at("title").is_string()) {
115 return mcp::core::unexpected(
116 core::Error{static_cast<int>(ErrorCode::InvalidRequest),
117 "tool annotations title must be a string",
118 {}});
119 }
120 annotations.title = json.at("title").get<std::string>();
121 }
122 if (json.contains("descriptionHint")) {
123 if (!json.at("descriptionHint").is_string()) {
124 return mcp::core::unexpected(
125 core::Error{static_cast<int>(ErrorCode::InvalidRequest),
126 "tool annotations descriptionHint must be a string",
127 {}});
128 }
129 annotations.description_hint =
130 json.at("descriptionHint").get<std::string>();
131 }
132 if (json.contains("destructiveHint")) {
133 if (!json.at("destructiveHint").is_boolean()) {
134 return mcp::core::unexpected(
135 core::Error{static_cast<int>(ErrorCode::InvalidRequest),
136 "tool annotations destructiveHint must be a boolean",
137 {}});
138 }
139 annotations.destructive_hint = json.at("destructiveHint").get<bool>();
140 }
141 if (json.contains("readOnlyHint")) {
142 if (!json.at("readOnlyHint").is_boolean()) {
143 return mcp::core::unexpected(
144 core::Error{static_cast<int>(ErrorCode::InvalidRequest),
145 "tool annotations readOnlyHint must be a boolean",
146 {}});
147 }
148 annotations.read_only_hint = json.at("readOnlyHint").get<bool>();
149 }
150 if (json.contains("openWorldHint")) {
151 if (!json.at("openWorldHint").is_boolean()) {
152 return mcp::core::unexpected(
153 core::Error{static_cast<int>(ErrorCode::InvalidRequest),
154 "tool annotations openWorldHint must be a boolean",
155 {}});
156 }
157 annotations.open_world_hint = json.at("openWorldHint").get<bool>();
158 }
159 if (json.contains("idempotentHint")) {
160 if (!json.at("idempotentHint").is_boolean()) {
161 return mcp::core::unexpected(
162 core::Error{static_cast<int>(ErrorCode::InvalidRequest),
163 "tool annotations idempotentHint must be a boolean",
164 {}});
165 }
166 annotations.idempotent_hint = json.at("idempotentHint").get<bool>();
167 }
168
169 annotations.raw = collect_json_extensions(
170 json, {"title", "descriptionHint", "destructiveHint", "readOnlyHint",
171 "openWorldHint", "idempotentHint"});
172
173 return annotations;
174}
175
181inline Json annotations_to_json(const Annotations& annotations) {
182 Json json = Json::object();
183
184 if (annotations.audience.has_value()) {
185 json["audience"] = *annotations.audience;
186 }
187 if (annotations.priority.has_value()) {
188 json["priority"] = *annotations.priority;
189 }
190 if (annotations.last_modified.has_value()) {
191 json["lastModified"] = *annotations.last_modified;
192 }
193
194 // Merge unknown fields preserved from a prior deserialization.
195 if (annotations.raw.is_object()) {
196 for (const auto& item : annotations.raw.items()) {
197 if (!json.contains(item.key())) {
198 json[item.key()] = item.value();
199 }
200 }
201 }
202
203 return json;
204}
205
214 if (!json.is_object()) {
215 return mcp::core::unexpected(
216 core::Error{static_cast<int>(ErrorCode::InvalidRequest),
217 "annotations must be an object",
218 {}});
219 }
220
221 Annotations annotations;
222
223 if (json.contains("audience")) {
224 const auto& audience_val = json.at("audience");
225 if (!audience_val.is_array()) {
226 return mcp::core::unexpected(
227 core::Error{static_cast<int>(ErrorCode::InvalidRequest),
228 "annotations audience must be an array",
229 {}});
230 }
231 std::vector<std::string> audience;
232 audience.reserve(audience_val.size());
233 for (const auto& item : audience_val) {
234 if (!item.is_string()) {
235 return mcp::core::unexpected(
236 core::Error{static_cast<int>(ErrorCode::InvalidRequest),
237 "annotations audience entries must be strings",
238 {}});
239 }
240 audience.push_back(item.get<std::string>());
241 }
242 annotations.audience = std::move(audience);
243 }
244
245 if (json.contains("priority")) {
246 const auto& priority_val = json.at("priority");
247 if (!priority_val.is_number()) {
248 return mcp::core::unexpected(
249 core::Error{static_cast<int>(ErrorCode::InvalidRequest),
250 "annotations priority must be a number",
251 {}});
252 }
253 const float priority = priority_val.get<float>();
254 if (!std::isfinite(priority) || priority < 0.0f || priority > 1.0f) {
255 return mcp::core::unexpected(
256 core::Error{static_cast<int>(ErrorCode::InvalidRequest),
257 "annotations priority must be between 0.0 and 1.0",
258 {}});
259 }
260 annotations.priority = priority;
261 }
262
263 if (json.contains("lastModified")) {
264 const auto& ts_val = json.at("lastModified");
265 if (!ts_val.is_string()) {
266 return mcp::core::unexpected(
267 core::Error{static_cast<int>(ErrorCode::InvalidRequest),
268 "annotations lastModified must be a string",
269 {}});
270 }
271 annotations.last_modified = ts_val.get<std::string>();
272 }
273
274 // Preserve unknown fields for forward-compatible round trips.
275 annotations.raw =
276 collect_json_extensions(json, {"audience", "priority", "lastModified"});
277
278 return annotations;
279}
280
281} // namespace mcp::protocol
Json tool_annotations_to_json(const ToolAnnotations &annotations)
Serializes a ToolAnnotations struct to JSON.
Definition annotations.hpp:71
core::Result< Annotations > annotations_from_json(const Json &json)
Parses an Annotations struct from JSON.
Definition annotations.hpp:213
core::Result< ToolAnnotations > tool_annotations_from_json(const Json &json)
Parses a ToolAnnotations struct from JSON.
Definition annotations.hpp:102
Json annotations_to_json(const Annotations &annotations)
Serializes an Annotations struct to JSON.
Definition annotations.hpp:181
Shared JSON, JSON-RPC, error, cancellation, and progress model types.
nlohmann::json Json
JSON value type used by all protocol DTOs.
Definition types.hpp:28
Json collect_json_extensions(const Json &json, std::initializer_list< std::string_view > known_keys)
Collects unknown object members so typed DTOs can preserve future protocol fields and vendor extensio...
Definition types.hpp:320
Shared result and error primitives used by the public cxxmcp SDK.
tl::expected< T, Error > Result
Alias for the SDK result type.
Definition result.hpp:64
Structured error returned by fallible SDK operations.
Definition result.hpp:35
Typed representation of MCP annotations.
Definition annotations.hpp:34
std::optional< std::vector< std::string > > audience
Target audience roles. Valid wire values are "user" and "assistant".
Definition annotations.hpp:36
std::optional< float > priority
Presentation priority in the closed interval [0.0, 1.0].
Definition annotations.hpp:39
std::optional< std::string > last_modified
ISO 8601 timestamp indicating when the annotated object was last modified.
Definition annotations.hpp:42
Json raw
Unknown JSON members preserved for forward-compatible round trips.
Definition annotations.hpp:45
Typed representation of MCP tool annotations.
Definition annotations.hpp:53
Json raw
Unknown JSON members preserved for forward-compatible round trips.
Definition annotations.hpp:67
std::optional< bool > open_world_hint
Whether the tool may interact with an open world of entities.
Definition annotations.hpp:63
std::optional< bool > idempotent_hint
Whether the tool is idempotent (safe to retry).
Definition annotations.hpp:65
std::optional< std::string > title
Optional human-readable display title for the tool.
Definition annotations.hpp:55
std::optional< bool > destructive_hint
Whether the tool may perform destructive actions.
Definition annotations.hpp:59
std::optional< bool > read_only_hint
Whether the tool only reads data without side effects.
Definition annotations.hpp:61
std::optional< std::string > description_hint
Optional hint describing what the tool does.
Definition annotations.hpp:57