Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
73 changes: 73 additions & 0 deletions examples/parallel_search.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/// 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 <algorithm>
#include <chrono>
#include <iostream>
#include <string>
#include <unordered_map>

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<fastmcpp::client::CurlTransport>(
"https://search.parallel.ai/mcp",
std::unordered_map<std::string, std::string>{
{"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())
{
std::cerr << "Parallel Search MCP: one or more URLs could not be fetched\n";
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;
}
}