cxxmcp 1.1.6
C++ MCP SDK
Loading...
Searching...
No Matches
router.hpp
Go to the documentation of this file.
1// Copyright (c) 2025 [caomengxuan666]
2
3#pragma once
4
7
8#include <algorithm>
9#include <functional>
10#include <stdexcept>
11#include <string>
12#include <string_view>
13#include <utility>
14#include <vector>
15
22
23namespace mcp::server {
24
26using RouterChangedHandler = std::function<void()>;
27
30 public:
31 struct Route {
32 protocol::ToolDefinition definition;
33 ToolHandler handler;
34 bool enabled = true;
35 };
36
37 ToolRouter& add(protocol::ToolDefinition definition, ToolHandler handler) {
38 routes_.push_back(Route{std::move(definition), std::move(handler), true});
39 notify_changed();
40 return *this;
41 }
42
43 ToolRouter& tool(protocol::ToolDefinition definition, ToolHandler handler) {
44 return add(std::move(definition), std::move(handler));
45 }
46
47 template <class Args, class Result, class Handler>
48 ToolRouter& tool(TypedToolRegistration<Args, Result, Handler> registration) {
49 return add(
50 std::move(registration.definition),
51 [handler = std::move(registration.handler)](
52 const ToolContext& context) -> core::Result<protocol::ToolResult> {
53 try {
54 auto args = detail::argument_from_json<Args>(context.arguments);
55 auto handled =
56 detail::invoke_tool_handler(handler, std::move(args), context);
57 if constexpr (detail::is_result<decltype(handled)>::value) {
58 if (!handled) {
59 return mcp::core::unexpected(handled.error());
60 }
61 return detail::value_to_tool_result(*handled);
62 } else {
63 return detail::value_to_tool_result(std::move(handled));
64 }
65 } catch (const std::exception& exception) {
66 return mcp::core::unexpected(core::Error{
67 static_cast<int>(protocol::ErrorCode::InvalidParams),
68 "failed to decode tool arguments",
69 exception.what(),
70 });
71 }
72 });
73 }
74
75 ToolRouter& merge(const ToolRouter& other) {
76 for (const auto& route : other.routes_) {
77 routes_.push_back(route);
78 }
79 notify_changed();
80 return *this;
81 }
82
83 ToolRouter& remove(std::string_view name) {
84 const auto original_size = routes_.size();
85 routes_.erase(std::remove_if(routes_.begin(), routes_.end(),
86 [name](const Route& route) {
87 return route.definition.name == name;
88 }),
89 routes_.end());
90 if (routes_.size() != original_size) {
91 notify_changed();
92 }
93 return *this;
94 }
95
96 ToolRouter& clear() {
97 if (!routes_.empty()) {
98 routes_.clear();
99 notify_changed();
100 }
101 return *this;
102 }
103
104 ToolRouter& enable(std::string_view name, bool enabled = true) {
105 for (auto& route : routes_) {
106 if (route.definition.name == name) {
107 route.enabled = enabled;
108 }
109 }
110 notify_changed();
111 return *this;
112 }
113
114 ToolRouter& disable(std::string_view name) { return enable(name, false); }
115
116 ToolRouter& on_changed(RouterChangedHandler handler) {
117 on_changed_ = std::move(handler);
118 return *this;
119 }
120
121 template <class Notifier>
122 ToolRouter& bind(Notifier& notifier) {
123 auto previous = std::move(on_changed_);
124 on_changed_ = [previous = std::move(previous), &notifier] {
125 if (previous) {
126 previous();
127 }
128 (void)notifier.notify_tool_list_changed();
129 };
130 return *this;
131 }
132
133 const std::vector<Route>& routes() const noexcept { return routes_; }
134
135 ServerBuilder& apply_to(ServerBuilder& builder) const {
136 for (const auto& route : routes_) {
137 if (route.enabled) {
138 builder.add_tool(route.definition, route.handler);
139 }
140 }
141 return builder;
142 }
143
144 private:
145 void notify_changed() const {
146 if (on_changed_) {
147 on_changed_();
148 }
149 }
150
151 std::vector<Route> routes_;
152 RouterChangedHandler on_changed_;
153};
154
157 public:
158 struct Route {
159 protocol::Prompt prompt;
160 PromptHandler handler;
161 bool enabled = true;
162 };
163
164 PromptRouter& add(protocol::Prompt prompt, PromptHandler handler) {
165 routes_.push_back(Route{std::move(prompt), std::move(handler), true});
166 notify_changed();
167 return *this;
168 }
169
170 PromptRouter& prompt(protocol::Prompt prompt, PromptHandler handler) {
171 return add(std::move(prompt), std::move(handler));
172 }
173
174 PromptRouter& merge(const PromptRouter& other) {
175 for (const auto& route : other.routes_) {
176 routes_.push_back(route);
177 }
178 notify_changed();
179 return *this;
180 }
181
182 PromptRouter& remove(std::string_view name) {
183 const auto original_size = routes_.size();
184 routes_.erase(std::remove_if(routes_.begin(), routes_.end(),
185 [name](const Route& route) {
186 return route.prompt.name == name;
187 }),
188 routes_.end());
189 if (routes_.size() != original_size) {
190 notify_changed();
191 }
192 return *this;
193 }
194
195 PromptRouter& clear() {
196 if (!routes_.empty()) {
197 routes_.clear();
198 notify_changed();
199 }
200 return *this;
201 }
202
203 PromptRouter& enable(std::string_view name, bool enabled = true) {
204 for (auto& route : routes_) {
205 if (route.prompt.name == name) {
206 route.enabled = enabled;
207 }
208 }
209 notify_changed();
210 return *this;
211 }
212
213 PromptRouter& disable(std::string_view name) { return enable(name, false); }
214
215 PromptRouter& on_changed(RouterChangedHandler handler) {
216 on_changed_ = std::move(handler);
217 return *this;
218 }
219
220 template <class Notifier>
221 PromptRouter& bind(Notifier& notifier) {
222 auto previous = std::move(on_changed_);
223 on_changed_ = [previous = std::move(previous), &notifier] {
224 if (previous) {
225 previous();
226 }
227 (void)notifier.notify_prompt_list_changed();
228 };
229 return *this;
230 }
231
232 const std::vector<Route>& routes() const noexcept { return routes_; }
233
234 ServerBuilder& apply_to(ServerBuilder& builder) const {
235 for (const auto& route : routes_) {
236 if (route.enabled) {
237 builder.add_prompt(route.prompt, route.handler);
238 }
239 }
240 return builder;
241 }
242
243 private:
244 void notify_changed() const {
245 if (on_changed_) {
246 on_changed_();
247 }
248 }
249
250 std::vector<Route> routes_;
251 RouterChangedHandler on_changed_;
252};
253
256 public:
258 protocol::Resource resource;
259 ResourceReadHandler handler;
260 bool enabled = true;
261 };
262
264 protocol::ResourceTemplate resource_template;
265 bool enabled = true;
266 };
267
269 ResourceReadHandler handler) {
270 resources_.push_back(
271 ResourceRoute{std::move(resource), std::move(handler), true});
272 notify_changed();
273 return *this;
274 }
275
276 ResourceRouter& resource(protocol::Resource resource,
277 ResourceReadHandler handler) {
278 return add(std::move(resource), std::move(handler));
279 }
280
281 ResourceRouter& add_template(protocol::ResourceTemplate resource_template) {
282 templates_.push_back(TemplateRoute{std::move(resource_template), true});
283 notify_changed();
284 return *this;
285 }
286
287 ResourceRouter& resource_template(
288 protocol::ResourceTemplate resource_template) {
289 return add_template(std::move(resource_template));
290 }
291
292 ResourceRouter& merge(const ResourceRouter& other) {
293 for (const auto& route : other.resources_) {
294 resources_.push_back(route);
295 }
296 for (const auto& route : other.templates_) {
297 templates_.push_back(route);
298 }
299 notify_changed();
300 return *this;
301 }
302
303 ResourceRouter& remove_resource(std::string_view uri) {
304 const auto original_size = resources_.size();
305 resources_.erase(std::remove_if(resources_.begin(), resources_.end(),
306 [uri](const ResourceRoute& route) {
307 return route.resource.uri == uri;
308 }),
309 resources_.end());
310 if (resources_.size() != original_size) {
311 notify_changed();
312 }
313 return *this;
314 }
315
316 ResourceRouter& remove_template(std::string_view uri_template) {
317 const auto original_size = templates_.size();
318 templates_.erase(
319 std::remove_if(templates_.begin(), templates_.end(),
320 [uri_template](const TemplateRoute& route) {
321 return route.resource_template.uri_template ==
322 uri_template;
323 }),
324 templates_.end());
325 if (templates_.size() != original_size) {
326 notify_changed();
327 }
328 return *this;
329 }
330
331 ResourceRouter& clear_resources() {
332 if (!resources_.empty()) {
333 resources_.clear();
334 notify_changed();
335 }
336 return *this;
337 }
338
339 ResourceRouter& clear_templates() {
340 if (!templates_.empty()) {
341 templates_.clear();
342 notify_changed();
343 }
344 return *this;
345 }
346
347 ResourceRouter& clear() {
348 if (!resources_.empty() || !templates_.empty()) {
349 resources_.clear();
350 templates_.clear();
351 notify_changed();
352 }
353 return *this;
354 }
355
356 ResourceRouter& enable_resource(std::string_view uri, bool enabled = true) {
357 for (auto& route : resources_) {
358 if (route.resource.uri == uri) {
359 route.enabled = enabled;
360 }
361 }
362 notify_changed();
363 return *this;
364 }
365
366 ResourceRouter& disable_resource(std::string_view uri) {
367 return enable_resource(uri, false);
368 }
369
370 ResourceRouter& enable_template(std::string_view uri_template,
371 bool enabled = true) {
372 for (auto& route : templates_) {
373 if (route.resource_template.uri_template == uri_template) {
374 route.enabled = enabled;
375 }
376 }
377 notify_changed();
378 return *this;
379 }
380
381 ResourceRouter& disable_template(std::string_view uri_template) {
382 return enable_template(uri_template, false);
383 }
384
385 ResourceRouter& on_changed(RouterChangedHandler handler) {
386 on_changed_ = std::move(handler);
387 return *this;
388 }
389
390 template <class Notifier>
391 ResourceRouter& bind(Notifier& notifier) {
392 auto previous = std::move(on_changed_);
393 on_changed_ = [previous = std::move(previous), &notifier] {
394 if (previous) {
395 previous();
396 }
397 (void)notifier.notify_resource_list_changed();
398 };
399 return *this;
400 }
401
402 const std::vector<ResourceRoute>& resources() const noexcept {
403 return resources_;
404 }
405
406 const std::vector<TemplateRoute>& templates() const noexcept {
407 return templates_;
408 }
409
410 ServerBuilder& apply_to(ServerBuilder& builder) const {
411 for (const auto& route : resources_) {
412 if (route.enabled) {
413 builder.add_resource(route.resource, route.handler);
414 }
415 }
416 for (const auto& route : templates_) {
417 if (route.enabled) {
418 builder.add_resource_template(route.resource_template);
419 }
420 }
421 return builder;
422 }
423
424 private:
425 void notify_changed() const {
426 if (on_changed_) {
427 on_changed_();
428 }
429 }
430
431 std::vector<ResourceRoute> resources_;
432 std::vector<TemplateRoute> templates_;
433 RouterChangedHandler on_changed_;
434};
435
436} // namespace mcp::server
High-level server authoring helpers and typed App builder adapters.
Composable set of prompt routes.
Definition router.hpp:156
Composable set of resource routes and resource templates.
Definition router.hpp:255
Composable set of tool routes.
Definition router.hpp:29
std::function< core::Result< protocol::ToolResult >(const ToolContext &)> ToolHandler
Application callback that executes a tool.
Definition handler_types.hpp:21
std::function< core::Result< protocol::ResourcesReadResult >(const ResourceContext &)> ResourceReadHandler
Application callback that reads a resource.
Definition handler_types.hpp:32
std::function< core::Result< protocol::PromptsGetResult >(const PromptContext &)> PromptHandler
Application callback that renders a prompt.
Definition handler_types.hpp:26
Prompt discovery and rendering payloads.
In-memory registries and handler contracts for server capabilities.
Resource listing, template, subscription, and read payloads.
Shared result and error primitives used by the public cxxmcp SDK.
std::function< void()> RouterChangedHandler
Change callback fired when a router's exposed route set changes.
Definition router.hpp:26
Prompt descriptor returned by prompts/list.
Definition prompt.hpp:83
URI template advertised by resources/templates/list.
Definition resource.hpp:127
Concrete resource advertised by resources/list.
Definition resource.hpp:28
Metadata describing a callable MCP tool.
Definition tool.hpp:213
Definition router.hpp:158
Definition router.hpp:31
Tool definition, call, and result payloads.