cxxmcp 1.1.6
C++ MCP SDK
Loading...
Searching...
No Matches
http_metadata_endpoint.hpp
Go to the documentation of this file.
1// Copyright (c) 2025 [caomengxuan666]
2
3#pragma once
4
5#include <functional>
6#include <nlohmann/json.hpp>
7#include <optional>
8#include <string>
9#include <utility>
10
12
15
16namespace mcp::auth {
17
20 int status_code = 0;
21 HeaderMap headers;
22 std::string body;
23};
24
27 std::function<core::Result<OAuthHttpResponse>(const MetadataFetchRequest&)>;
28
35 public:
36 explicit HttpOAuthMetadataEndpoint(OAuthHttpGet get) : get_(std::move(get)) {}
37
38 core::Result<ProtectedResourceMetadata> fetch_protected_resource_metadata(
39 const MetadataFetchRequest& request) override {
40 auto response = fetch_json(request);
41 if (!response.has_value()) {
42 return mcp::core::unexpected(response.error());
43 }
44 return parse_protected_resource_metadata(*response);
45 }
46
47 core::Result<AuthorizationServerMetadata> fetch_authorization_server_metadata(
48 const MetadataFetchRequest& request) override {
49 auto response = fetch_json(request);
50 if (!response.has_value()) {
51 return mcp::core::unexpected(response.error());
52 }
53 return parse_authorization_server_metadata(*response);
54 }
55
56 private:
57 using Json = nlohmann::json;
58
59 core::Result<Json> fetch_json(const MetadataFetchRequest& request) {
60 if (!get_) {
61 return mcp::core::unexpected(make_oauth_error(
62 OAuthErrorCode::kMetadataDiscoveryFailed,
63 "OAuth metadata HTTP GET endpoint is not configured"));
64 }
65
66 auto response = get_(request);
67 if (!response.has_value()) {
68 return mcp::core::unexpected(response.error());
69 }
70 if (response->status_code < 200 || response->status_code >= 300) {
71 return mcp::core::unexpected(make_oauth_error(
72 OAuthErrorCode::kMetadataDiscoveryFailed,
73 "OAuth metadata endpoint returned non-success HTTP status",
74 std::to_string(response->status_code)));
75 }
76
77 try {
78 return Json::parse(response->body);
79 } catch (const Json::exception& error) {
80 return mcp::core::unexpected(make_oauth_error(
81 OAuthErrorCode::kMetadataDiscoveryFailed,
82 "OAuth metadata endpoint returned invalid JSON", error.what()));
83 }
84 }
85
86 static std::optional<std::string> optional_string(const Json& value,
87 const char* key) {
88 const auto iter = value.find(key);
89 if (iter == value.end() || iter->is_null()) {
90 return std::nullopt;
91 }
92 if (iter->is_string()) {
93 return iter->get<std::string>();
94 }
95 return std::nullopt;
96 }
97
98 static std::string string_or_empty(const Json& value, const char* key) {
99 auto result = optional_string(value, key);
100 return result.has_value() ? *result : std::string{};
101 }
102
103 static StringList string_list_or_empty(const Json& value, const char* key) {
104 StringList result;
105 const auto iter = value.find(key);
106 if (iter == value.end() || !iter->is_array()) {
107 return result;
108 }
109 for (const auto& entry : *iter) {
110 if (entry.is_string()) {
111 result.push_back(entry.get<std::string>());
112 }
113 }
114 return result;
115 }
116
117 static MetadataMap extension_metadata(const Json& value) {
118 MetadataMap result;
119 for (auto iter = value.begin(); iter != value.end(); ++iter) {
120 if (iter->is_string()) {
121 result.emplace(iter.key(), iter->get<std::string>());
122 } else if (iter->is_boolean()) {
123 result.emplace(iter.key(), iter->get<bool>() ? "true" : "false");
124 } else if (iter->is_number() || iter->is_object() || iter->is_array()) {
125 result.emplace(iter.key(), iter->dump());
126 }
127 }
128 return result;
129 }
130
132 parse_protected_resource_metadata(const Json& value) {
133 if (!value.is_object()) {
134 return mcp::core::unexpected(make_oauth_error(
135 OAuthErrorCode::kMetadataDiscoveryFailed,
136 "protected resource metadata JSON must be an object"));
137 }
138
140 metadata.resource = string_or_empty(value, "resource");
141 metadata.authorization_servers =
142 string_list_or_empty(value, "authorization_servers");
143 metadata.bearer_methods_supported =
144 string_list_or_empty(value, "bearer_methods_supported");
145 metadata.resource_signing_alg_values_supported =
146 string_list_or_empty(value, "resource_signing_alg_values_supported");
147 metadata.scopes_supported = string_list_or_empty(value, "scopes_supported");
148 metadata.resource_name = optional_string(value, "resource_name");
149 metadata.resource_documentation =
150 optional_string(value, "resource_documentation");
151 metadata.metadata = extension_metadata(value);
152 return metadata;
153 }
154
156 parse_authorization_server_metadata(const Json& value) {
157 if (!value.is_object()) {
158 return mcp::core::unexpected(make_oauth_error(
159 OAuthErrorCode::kMetadataDiscoveryFailed,
160 "authorization server metadata JSON must be an object"));
161 }
162
164 metadata.issuer = string_or_empty(value, "issuer");
165 metadata.authorization_endpoint =
166 string_or_empty(value, "authorization_endpoint");
167 metadata.token_endpoint = string_or_empty(value, "token_endpoint");
168 metadata.registration_endpoint =
169 optional_string(value, "registration_endpoint");
170 metadata.jwks_uri = optional_string(value, "jwks_uri");
171 metadata.response_types_supported =
172 string_list_or_empty(value, "response_types_supported");
173 metadata.grant_types_supported =
174 string_list_or_empty(value, "grant_types_supported");
175 metadata.code_challenge_methods_supported =
176 string_list_or_empty(value, "code_challenge_methods_supported");
177 metadata.token_endpoint_auth_methods_supported =
178 string_list_or_empty(value, "token_endpoint_auth_methods_supported");
179 metadata.dpop_signing_alg_values_supported =
180 string_list_or_empty(value, "dpop_signing_alg_values_supported");
181 metadata.scopes_supported = string_list_or_empty(value, "scopes_supported");
182 metadata.metadata = extension_metadata(value);
183 return metadata;
184 }
185
186 OAuthHttpGet get_;
187};
188
189} // namespace mcp::auth
Default metadata endpoint implementation over an injected HTTP GET.
Definition http_metadata_endpoint.hpp:34
OAuth metadata network boundary.
Definition lifecycle.hpp:351
std::function< core::Result< OAuthHttpResponse >(const MetadataFetchRequest &)> OAuthHttpGet
Application or SDK transport adapter used for metadata GET calls.
Definition http_metadata_endpoint.hpp:27
OAuth authorization lifecycle contracts and lightweight state logic.
core::Error make_oauth_error(OAuthErrorCode code, std::string message, std::string detail={})
Build an auth-category lifecycle error.
Definition lifecycle.hpp:50
tl::expected< T, Error > Result
Alias for the SDK result type.
Definition result.hpp:64
RFC 8414 OAuth authorization server metadata.
Definition metadata.hpp:28
Metadata fetch request routed through application/transport code.
Definition lifecycle.hpp:342
Transport-neutral HTTP response used by the auth metadata endpoint.
Definition http_metadata_endpoint.hpp:19
RFC 9728 OAuth protected resource metadata.
Definition metadata.hpp:16