From b4a0cb7c766641da922a71bdcca1d7ee79d3e48f Mon Sep 17 00:00:00 2001 From: George at Parallel Date: Fri, 9 Oct 2026 03:52:50 +0000 Subject: [PATCH 1/2] Add a keyless Parallel Search MCP client example --- CMakeLists.txt | 5 +++ README.md | 30 ++++++++++++++++ examples/parallel_search.cpp | 70 ++++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+) create mode 100644 examples/parallel_search.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index fab758e..0c2b4fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -844,6 +844,11 @@ if(FASTMCPP_BUILD_EXAMPLES) add_executable(fastmcpp_example_client_api examples/client_api.cpp) target_link_libraries(fastmcpp_example_client_api PRIVATE fastmcpp_core) + if(FASTMCPP_ENABLE_CURL_TRANSPORT) + add_executable(fastmcpp_example_parallel_search examples/parallel_search.cpp) + target_link_libraries(fastmcpp_example_parallel_search PRIVATE fastmcpp_core) + endif() + # Removed outdated server_toolmanager_handler example target (file no longer exists) add_executable(fastmcpp_example_stdio examples/simple_echo.cpp) diff --git a/README.md b/README.md index 90e66e7..d2e2c19 100644 --- a/README.md +++ b/README.md @@ -236,6 +236,36 @@ int main() { } ``` +### Keyless web search and fetch with Parallel + +The [Parallel Search MCP](https://docs.parallel.ai/integrations/mcp/search-mcp) +example connects to `https://search.parallel.ai/mcp` using `CurlTransport` and +Streamable HTTP. It initializes the client, discovers the available tools, then +calls `web_search` or `web_fetch` and prints the returned URLs and excerpts. +No API key or model is needed. Anonymous access is free for light use and subject +to rate limits. + +```bash +cmake -B build-parallel -S . -DCMAKE_BUILD_TYPE=Release \ + -DFASTMCPP_BUILD_TESTS=OFF -DFASTMCPP_BUILD_EXAMPLES=ON \ + -DFASTMCPP_ENABLE_CURL_TRANSPORT=ON +cmake --build build-parallel --config Release --target fastmcpp_example_parallel_search -j2 + +./build-parallel/fastmcpp_example_parallel_search search "C++ MCP client examples" +./build-parallel/fastmcpp_example_parallel_search fetch https://modelcontextprotocol.io/docs/getting-started/intro +``` + +On Windows, the executable is under `build-parallel/Release/` for multi-config +generators. The build needs libcurl with a TLS backend and a trusted CA store; +the existing `FASTMCPP_FETCH_CURL` option fetches libcurl by default. If your +toolchain supplies libcurl, add `-DFASTMCPP_FETCH_CURL=OFF` when configuring. + +Each invocation makes one tool call with certificate verification enabled and +a 120-second transport timeout. Errors go to stderr with a nonzero exit status. +For a multi-call application, reuse the returned `session_id` in later search +and fetch arguments. This example executes tools directly; it does not run an +LLM agent loop. Existing client and build defaults are unchanged. + ### Streamable HTTP server (MCP spec 2025-03-26) ```cpp diff --git a/examples/parallel_search.cpp b/examples/parallel_search.cpp new file mode 100644 index 0000000..893c6c5 --- /dev/null +++ b/examples/parallel_search.cpp @@ -0,0 +1,70 @@ +/// A single-call, anonymous Parallel Search MCP client using Streamable HTTP. +#include "fastmcpp/client/client.hpp" +#include "fastmcpp/client/curl_transport.hpp" +#include "fastmcpp/version.hpp" + +#include +#include +#include +#include +#include + +int main(int argc, char* argv[]) +{ + if (argc != 3 || (std::string(argv[1]) != "search" && std::string(argv[1]) != "fetch") || + std::string(argv[2]).empty()) + { + std::cerr << "Usage: " << argv[0] << " search \"keyword query\"\n" + << " " << argv[0] << " fetch https://example.com/page\n"; + return 2; + } + + try + { + const std::string action = argv[1]; + const std::string input = argv[2]; + const std::string tool_name = action == "search" ? "web_search" : "web_fetch"; + auto transport = std::make_unique( + "https://search.parallel.ai/mcp", + std::unordered_map{ + {"User-Agent", + std::string("fastmcpp-parallel-search-example/") + fastmcpp::VERSION_STRING}}); + transport->set_timeouts(std::chrono::seconds{30}, std::chrono::seconds{120}); + fastmcpp::client::Client client(std::move(transport)); + const auto init = client.initialize(); + std::cout << "Connected to: " << init.serverInfo.name << '\n'; + + const auto tools = client.list_tools(); + if (std::none_of(tools.begin(), tools.end(), + [&](const auto& tool) { return tool.name == tool_name; })) + { + std::cerr << "Server does not advertise " << tool_name << '\n'; + return 1; + } + + // Each invocation makes one tool call. For a multi-call conversation, + // reuse the returned session_id in subsequent search and fetch arguments. + const fastmcpp::Json arguments = + action == "search" ? fastmcpp::Json{{"objective", input}, + {"search_queries", fastmcpp::Json::array({input})}} + : fastmcpp::Json{{"urls", fastmcpp::Json::array({input})}}; + const auto result = client.call_tool(tool_name, arguments); + if (result.structuredContent) + { + std::cout << result.structuredContent->dump(2) << '\n'; + if (result.structuredContent->contains("errors") && + !result.structuredContent->at("errors").empty()) + return 1; + } + else + { + std::cout << result.text() << '\n'; + } + return 0; + } + catch (const std::exception& error) + { + std::cerr << "Parallel Search MCP: " << error.what() << '\n'; + return 1; + } +} From 825f87c52371d2daee06e5e9978ed04d5024b63f Mon Sep 17 00:00:00 2001 From: George at Parallel Date: Fri, 9 Oct 2026 03:53:36 +0000 Subject: [PATCH 2/2] Report fetch failures on stderr --- examples/parallel_search.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/parallel_search.cpp b/examples/parallel_search.cpp index 893c6c5..05c5ef2 100644 --- a/examples/parallel_search.cpp +++ b/examples/parallel_search.cpp @@ -54,7 +54,10 @@ int main(int argc, char* argv[]) std::cout << result.structuredContent->dump(2) << '\n'; if (result.structuredContent->contains("errors") && !result.structuredContent->at("errors").empty()) + { + std::cerr << "Parallel Search MCP: one or more URLs could not be fetched\n"; return 1; + } } else {