18#include <unordered_map>
19#include <unordered_set>
31namespace mcp::protocol {
58 return std::move(*
this);
64 static constexpr bool defined =
true;
65 static auto fields() {
68 static std::vector<std::string> known_keys() {
return {
"taskSupport"}; }
74 static void serialize(
Json& json,
const char* key,
78 static bool deserialize(
const Json& json,
const char* key,
80 if (!json.contains(key)) {
83 auto result = reflect_from_json<ToolExecution>(json.at(key));
87 target = std::move(*result);
118 block.
text = std::move(value);
125 block.
type =
"image";
126 block.
data = std::move(base64_data);
134 block.
type =
"audio";
135 block.
data = std::move(base64_data);
143 block.
type =
"resource";
151 block.
type =
"resource_link";
172 std::optional<std::string_view>
as_text() const noexcept {
176 return std::string_view(
text);
184 return std::string_view(
data.get_ref<
const std::string&>());
192 return std::string_view(
data.get_ref<
const std::string&>());
244 return TaskSupport::Forbidden;
254 definition_.
name = std::move(name);
258 definition_.
title = std::move(value);
274 return input_schema(schema_for<T>());
285 return output_schema(schema_for<T>());
294 definition_.
icons.push_back(std::move(value));
299 if (!definition_.
execution.has_value()) {
302 definition_.
execution->task_support = value;
317 definition_.
meta = std::move(value);
341 std::optional<TaskRequestParameters>
task;
354 static constexpr bool defined =
true;
355 static auto fields() {
356 return std::make_tuple(
363 {
"name",
"arguments",
"task",
"_meta",
"requestState",
366 static std::vector<std::string> known_keys() {
367 return {
"name",
"arguments",
"task",
368 "_meta",
"requestState",
"inputResponses"};
433 static_cast<int>(ErrorCode::InvalidRequest), std::move(message), {}};
439 case TaskSupport::Forbidden:
441 case TaskSupport::Optional:
443 case TaskSupport::Required:
451 std::string_view value)
noexcept {
452 if (value ==
"forbidden") {
453 return TaskSupport::Forbidden;
455 if (value ==
"optional") {
456 return TaskSupport::Optional;
458 if (value ==
"required") {
459 return TaskSupport::Required;
466 static void serialize(
Json& json,
const char* key,
TaskSupport value) {
469 static bool deserialize(
const Json& json,
const char* key,
471 if (!json.contains(key) || !json.at(key).is_string()) {
476 if (!val.has_value()) {
489 if (value.size() % 4 != 0) {
493 std::size_t padding = 0;
494 if (value.back() ==
'=') {
496 if (value.size() >= 2 && value[value.size() - 2] ==
'=') {
501 for (std::size_t i = 0; i < value.size(); ++i) {
502 const unsigned char c =
static_cast<unsigned char>(value[i]);
503 const bool is_base64_char = (c >=
'A' && c <=
'Z') ||
504 (c >=
'a' && c <=
'z') ||
505 (c >=
'0' && c <=
'9') || c ==
'+' || c ==
'/';
506 if (is_base64_char) {
509 if (c ==
'=' && i >= value.size() - padding) {
526 return reflect_from_json<ToolExecution>(json);
531 Json json = Json::object();
532 json[
"type"] = block.
type;
533 if (block.
type ==
"resource" && block.
resource.has_value()) {
537 json[
"type"] = block.
type;
539 if (!block.
text.empty() || block.
type ==
"text") {
540 json[
"text"] = block.
text;
542 if (((block.
type ==
"image" || block.
type ==
"audio") &&
543 block.
data.is_string()) ||
544 !block.
data.empty()) {
545 json[
"data"] = block.
data;
554 if (block.
meta.has_value()) {
555 json[
"_meta"] = *block.
meta;
563 const Json& json, std::string_view member, std::string message) {
564 const std::string key(member);
565 if (!json.contains(key) || !json.at(key).is_string()) {
568 return json.at(key).get<std::string>();
574 if (!json.is_object()) {
575 return mcp::core::unexpected(
580 if (!json.contains(
"type") || !json.at(
"type").is_string()) {
581 return mcp::core::unexpected(
584 block.
type = json.at(
"type").get<std::string>();
586 if (block.
type ==
"image" || block.
type ==
"audio") {
588 json,
"data", block.
type +
" content block requires string data");
590 return mcp::core::unexpected(data.error());
594 block.
type +
" content block requires string mimeType");
596 return mcp::core::unexpected(mime_type.error());
599 return mcp::core::unexpected(
604 }
else if (block.
type ==
"resource") {
605 if (!json.contains(
"resource")) {
606 return mcp::core::unexpected(
611 return mcp::core::unexpected(resource.error());
614 }
else if (block.
type ==
"resource_link") {
617 return mcp::core::unexpected(resource.error());
620 }
else if (block.
type ==
"text") {
622 json,
"text",
"text content block requires string text");
624 return mcp::core::unexpected(text.error());
628 return mcp::core::unexpected(
632 if (json.contains(
"annotations")) {
635 return mcp::core::unexpected(parsed.error());
639 if (json.contains(
"_meta")) {
640 if (!json.at(
"_meta").is_object()) {
641 return mcp::core::unexpected(
644 block.
meta = json.at(
"_meta");
648 {
"type",
"text",
"data",
"mimeType",
"resource",
"uri",
"name",
"title",
649 "description",
"mimeType",
"size",
"icons",
"annotations",
"_meta"});
656 Json json = Json::object();
657 if (!definition.
title.empty()) {
658 json[
"title"] = definition.
title;
660 json[
"name"] = definition.
name;
669 json[
"streaming"] = definition.
streaming;
671 if (!definition.
icons.empty()) {
672 json[
"icons"] = Json::array();
673 for (
const auto& icon : definition.
icons) {
681 json[
"annotations"] =
686 if (definition.
meta.has_value()) {
687 json[
"_meta"] = *definition.
meta;
697 if (!json.is_object()) {
698 return mcp::core::unexpected(
703 if (json.contains(
"title")) {
704 if (!json.at(
"title").is_string()) {
705 return mcp::core::unexpected(
708 definition.
title = json.at(
"title").get<std::string>();
710 if (!json.contains(
"name") || !json.at(
"name").is_string()) {
711 return mcp::core::unexpected(
714 definition.
name = json.at(
"name").get<std::string>();
716 if (json.contains(
"description")) {
717 if (!json.at(
"description").is_string()) {
718 return mcp::core::unexpected(
721 definition.
description = json.at(
"description").get<std::string>();
724 if (!json.contains(
"inputSchema") || !json.at(
"inputSchema").is_object()) {
725 return mcp::core::unexpected(
730 if (json.contains(
"outputSchema")) {
731 if (!json.at(
"outputSchema").is_object()) {
732 return mcp::core::unexpected(
739 if (json.contains(
"streaming")) {
740 if (!json.at(
"streaming").is_boolean()) {
741 return mcp::core::unexpected(
744 definition.
streaming = json.at(
"streaming").get<
bool>();
747 if (json.contains(
"icons")) {
748 if (!json.at(
"icons").is_array()) {
749 return mcp::core::unexpected(
752 for (
const auto& item : json.at(
"icons")) {
754 if (!icon.has_value()) {
755 return mcp::core::unexpected(
758 definition.
icons.push_back(*icon);
762 if (json.contains(
"execution")) {
765 return mcp::core::unexpected(execution.error());
770 if (json.contains(
"annotations")) {
771 if (!json.at(
"annotations").is_object()) {
772 return mcp::core::unexpected(
782 if (json.contains(
"_meta")) {
783 if (!json.at(
"_meta").is_object()) {
784 return mcp::core::unexpected(
787 definition.
meta = json.at(
"_meta");
790 json, {
"title",
"name",
"description",
"inputSchema",
"outputSchema",
791 "streaming",
"icons",
"execution",
"annotations",
"_meta"});
798 Json json = Json::object();
799 json[
"tools"] = Json::array();
800 for (
const auto& tool : result.
tools) {
806 if (result.
meta.has_value()) {
807 json[
"_meta"] = *result.
meta;
809 if (result.
ttl_ms.has_value()) {
810 json[
"ttlMs"] = *result.
ttl_ms;
823 if (!json.is_object()) {
824 return mcp::core::unexpected(
827 if (!json.contains(
"tools") || !json.at(
"tools").is_array()) {
828 return mcp::core::unexpected(
833 for (
const auto& item : json.at(
"tools")) {
836 return mcp::core::unexpected(tool.error());
838 result.
tools.push_back(*tool);
840 if (json.contains(
"nextCursor")) {
841 if (!json.at(
"nextCursor").is_string()) {
842 return mcp::core::unexpected(
845 result.
next_cursor = json.at(
"nextCursor").get<std::string>();
847 if (json.contains(
"_meta")) {
848 if (!json.at(
"_meta").is_object()) {
849 return mcp::core::unexpected(
852 result.
meta = json.at(
"_meta");
861 Json json = Json::object();
862 json[
"name"] = call.
name;
866 if (call.
task.has_value()) {
869 if (call.
meta.has_value()) {
870 json[
"_meta"] = *call.
meta;
885 if (!json.is_object()) {
886 return mcp::core::unexpected(
889 if (!json.contains(
"name") || !json.at(
"name").is_string()) {
890 return mcp::core::unexpected(
895 call.
name = json.at(
"name").get<std::string>();
896 if (json.contains(
"arguments")) {
897 if (!json.at(
"arguments").is_object()) {
898 return mcp::core::unexpected(
903 if (json.contains(
"task")) {
906 return mcp::core::unexpected(task.error());
910 if (json.contains(
"_meta")) {
911 if (!json.at(
"_meta").is_object()) {
912 return mcp::core::unexpected(
915 call.
meta = json.at(
"_meta");
917 if (json.contains(
"requestState")) {
918 call.
request_state = json.at(
"requestState").get<std::string>();
920 if (json.contains(
"inputResponses")) {
925 {
"name",
"arguments",
"task",
"_meta",
"requestState",
"inputResponses"});
931 Json json = Json::object();
936 json[
"content"] = Json::array();
937 for (
const auto& block : result.
content) {
947 if (result.
meta.has_value()) {
948 json[
"_meta"] = *result.
meta;
963 if (!json.is_object()) {
964 return mcp::core::unexpected(
967 if (!json.contains(
"content") && !json.contains(
"structuredContent") &&
968 !json.contains(
"isError") && !json.contains(
"_meta") &&
969 !json.contains(
"resultType") && !json.contains(
"inputRequests") &&
970 !json.contains(
"requestState")) {
972 "tool result requires content, structuredContent, isError, _meta, "
973 "resultType, inputRequests, or requestState"));
977 if (json.contains(
"resultType")) {
978 result.
result_type = json.at(
"resultType").get<std::string>();
980 if (json.contains(
"content")) {
981 if (!json.at(
"content").is_array()) {
982 return mcp::core::unexpected(
985 for (
const auto& item : json.at(
"content")) {
988 return mcp::core::unexpected(block.error());
990 result.
content.push_back(*block);
994 if (json.contains(
"structuredContent")) {
998 if (json.contains(
"isError")) {
999 if (!json.at(
"isError").is_boolean()) {
1000 return mcp::core::unexpected(
1003 result.
is_error = json.at(
"isError").get<
bool>();
1006 if (json.contains(
"_meta")) {
1007 if (!json.at(
"_meta").is_object()) {
1008 return mcp::core::unexpected(
1011 result.
meta = json.at(
"_meta");
1013 if (json.contains(
"inputRequests")) {
1016 if (json.contains(
"requestState")) {
1017 result.
request_state = json.at(
"requestState").get<std::string>();
1020 json, {
"content",
"structuredContent",
"isError",
"_meta",
"resultType",
1021 "inputRequests",
"requestState"});
1030 std::string header_name;
1031 std::string param_name;
1037 const Json& input_schema) {
1038 std::vector<XHeaderEntry> result;
1039 if (!input_schema.is_object() || !input_schema.contains(
"properties") ||
1040 !input_schema.at(
"properties").is_object()) {
1043 for (
auto& [key, prop] : input_schema.at(
"properties").items()) {
1044 if (!prop.is_object() || !prop.contains(
"x-mcp-header") ||
1045 !prop.at(
"x-mcp-header").is_string()) {
1048 std::string type_str;
1049 if (prop.contains(
"type") && prop.at(
"type").is_string()) {
1050 type_str = prop.at(
"type").get<std::string>();
1052 result.push_back({prop.at(
"x-mcp-header").get<std::string>(),
1053 std::string(key), std::move(type_str)});
1061 std::unordered_set<std::string> seen_lower;
1062 for (
const auto& entry : entries) {
1063 if (entry.header_name.empty())
return false;
1064 for (
char ch : entry.header_name) {
1065 const auto c =
static_cast<unsigned char>(ch);
1066 if (c < 0x21 || c > 0x7E || c ==
':')
return false;
1068 if (entry.type ==
"object" || entry.type ==
"array" ||
1069 entry.type ==
"null" || entry.type.empty()) {
1072 std::string lower = entry.header_name;
1074 lower.begin(), lower.end(), lower.begin(),
1075 [](
unsigned char c) { return static_cast<char>(std::tolower(c)); });
1076 if (!seen_lower.insert(lower).second)
return false;
1083 for (
const char ch : value) {
1084 const auto c =
static_cast<unsigned char>(ch);
1085 if (c < 0x20 || c > 0x7E)
return true;
1087 if (!value.empty() && (value.front() ==
' ' || value.back() ==
' ')) {
1095 static constexpr char kAlphabet[] =
1096 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1098 result.reserve(((input.size() + 2) / 3) * 4);
1099 const auto* data =
reinterpret_cast<const unsigned char*
>(input.data());
1101 while (i + 2 < input.size()) {
1102 result.push_back(kAlphabet[(data[i] >> 2) & 0x3F]);
1104 kAlphabet[((data[i] & 0x03) << 4) | ((data[i + 1] >> 4) & 0x0F)]);
1106 kAlphabet[((data[i + 1] & 0x0F) << 2) | ((data[i + 2] >> 6) & 0x03)]);
1107 result.push_back(kAlphabet[data[i + 2] & 0x3F]);
1110 if (i + 1 == input.size()) {
1111 result.push_back(kAlphabet[(data[i] >> 2) & 0x3F]);
1112 result.push_back(kAlphabet[(data[i] & 0x03) << 4]);
1113 result.push_back(
'=');
1114 result.push_back(
'=');
1115 }
else if (i + 2 == input.size()) {
1116 result.push_back(kAlphabet[(data[i] >> 2) & 0x3F]);
1118 kAlphabet[((data[i] & 0x03) << 4) | ((data[i + 1] >> 4) & 0x0F)]);
1119 result.push_back(kAlphabet[(data[i + 1] & 0x0F) << 2]);
1120 result.push_back(
'=');
1130 return std::string(value);
1135 if (value.is_number_integer()) {
1136 return std::to_string(value.get<std::int64_t>());
1138 return value.dump();
1146 const Json& arguments,
const std::vector<XHeaderEntry>& entries) {
1147 std::unordered_map<std::string, std::string> headers;
1148 for (
const auto& entry : entries) {
1149 if (!arguments.contains(entry.param_name))
continue;
1150 const auto& value = arguments.at(entry.param_name);
1151 if (value.is_null())
continue;
1152 std::string header_key =
"Mcp-Param-" + entry.header_name;
1153 if (value.is_boolean()) {
1154 headers[header_key] = value.get<
bool>() ?
"true" :
"false";
1155 }
else if (value.is_number()) {
1157 }
else if (value.is_string()) {
Typed annotations for content blocks, tool definitions, and other protocol objects.
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.
void append_json_extensions(Json &json, const Json &extensions)
Flattens extension members into a JSON object without overwriting typed fields.
Definition types.hpp:358
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
#define CXXMCP_REFLECT_CHECK(Struct, expected_count)
Validates that a Reflect<> specialization covers the expected number of fields.
Definition reflect.hpp:1008
constexpr FieldDescriptor< Struct, Field > field(const char *wire_name, Field Struct::*pointer)
Creates a FieldDescriptor with wire name and pointer-to-member.
Definition reflect.hpp:75
ExtensionsField< Struct > extensions_field(Json Struct::*pointer, std::vector< std::string > own_keys)
Creates an ExtensionsField descriptor.
Definition reflect.hpp:90
Json reflect_to_json(const T &obj)
Serializes a DTO to JSON using its Reflect<T> trait.
Definition reflect.hpp:701
Resource listing, template, subscription, and read payloads.
Json resource_contents_to_json(const ResourceContents &contents)
Serializes resource contents.
Definition resource.hpp:788
core::Result< ResourceContents > resource_contents_from_json(const Json &json)
Parses resource contents.
Definition resource.hpp:795
core::Result< Resource > resource_from_json(const Json &json)
Parses a resource descriptor.
Definition resource.hpp:374
Json resource_to_json(const Resource &resource)
Serializes a resource descriptor.
Definition resource.hpp:340
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
@ Required
Require at least one tool use.
Small JSON Schema builders for MCP tool and elicitation metadata.
Structured error returned by fallible SDK operations.
Definition result.hpp:35
A single content item returned by a tool or embedded in prompts.
Definition tool.hpp:93
const ResourceContents * as_embedded_resource() const noexcept
Returns embedded resource contents when present.
Definition tool.hpp:196
std::optional< Json > meta
Optional _meta extension object preserved on the wire.
Definition tool.hpp:110
std::string type
Content kind.
Definition tool.hpp:96
Json data
Base64 payload for image/audio blocks or extension data for custom blocks.
Definition tool.hpp:100
bool is_text() const noexcept
Returns true when this block is a text block.
Definition tool.hpp:157
static ContentBlock embedded_resource(ResourceContents value)
Creates an embedded resource content block.
Definition tool.hpp:141
std::optional< std::string_view > as_text() const noexcept
Returns the text payload when this is a text block.
Definition tool.hpp:172
std::optional< Resource > resource_link
Resource descriptor for resource_link blocks.
Definition tool.hpp:106
std::optional< std::string_view > as_image_data() const
Returns base64 image data when this is an image block.
Definition tool.hpp:180
static ContentBlock audio(std::string base64_data, std::string mime_type)
Creates an audio content block with base64 data.
Definition tool.hpp:132
Json extensions
Unknown JSON members preserved for forward-compatible round trips.
Definition tool.hpp:112
std::optional< std::string_view > as_audio_data() const
Returns base64 audio data when this is an audio block.
Definition tool.hpp:188
static ContentBlock image(std::string base64_data, std::string mime_type)
Creates an image content block with base64 data.
Definition tool.hpp:123
static ContentBlock resource_link_content(Resource value)
Creates a resource link content block.
Definition tool.hpp:149
bool is_image() const noexcept
Returns true when this block is an image block.
Definition tool.hpp:160
const Resource * as_resource_link() const noexcept
Returns the linked resource descriptor when present.
Definition tool.hpp:204
std::string text
Text payload for text blocks.
Definition tool.hpp:98
bool is_embedded_resource() const noexcept
Returns true when this block embeds resource contents.
Definition tool.hpp:166
bool is_resource_link() const noexcept
Returns true when this block links to a resource descriptor.
Definition tool.hpp:169
bool is_audio() const noexcept
Returns true when this block is an audio block.
Definition tool.hpp:163
static ContentBlock text_content(std::string value)
Creates a text content block.
Definition tool.hpp:115
Json annotations
Optional annotations for model or client presentation.
Definition tool.hpp:108
std::optional< ResourceContents > resource
Embedded resource contents for resource blocks.
Definition tool.hpp:104
std::string mime_type
MIME type for image/audio blocks.
Definition tool.hpp:102
Icon descriptor used by tools, resources, resource templates, and prompts.
Definition types.hpp:160
Type-specific serialization and deserialization logic.
Definition reflect.hpp:246
Primary template.
Definition reflect.hpp:199
One content part returned by resources/read.
Definition resource.hpp:250
Concrete resource advertised by resources/list.
Definition resource.hpp:28
Asynchronous task status and task-management payloads.
Json task_request_parameters_to_json(const TaskRequestParameters ¶meters)
Serializes optional task request parameters.
Definition task.hpp:319
core::Result< TaskRequestParameters > task_request_parameters_from_json(const Json &json)
Parses optional task request parameters.
Definition task.hpp:326
Reflection specializations for DTOs defined in types.hpp.
Json icon_to_json(const Icon &icon)
Serializes a shared icon descriptor.
Definition types_reflect.hpp:109
std::optional< Icon > icon_from_json(const Json &json)
Parses a shared icon descriptor.
Definition types_reflect.hpp:114