Skip to content

Commit c920f1f

Browse files
committed
release: sync fastmcpp v3.4.7.1 from private
HTTPS without OpenSSL, an OAuth-capable client, and a handshake that negotiates. Reaching https:// no longer needs OpenSSL. With FASTMCPP_ENABLE_OPENSSL off -- the default -- an https:// URL parsed fine and then failed at the TLS handshake. Two new transports close that, both implementing ITransport so they drop into Client in place of StreamableHttpTransport: * WinHttpTransport (Windows, on by default) -- TLS via Schannel, with the system certificate store and enterprise proxy configuration. No third-party dependency. * CurlTransport (all platforms, FASTMCPP_ENABLE_CURL_TRANSPORT=ON) -- binds to whichever TLS backend the platform provides. Client: * HeaderProvider computes headers per request, so a rotating bearer token is refreshed without rebuilding the transport and losing its session. It is given the outgoing request, so request-signing schemes work too. * TransportHttpError retains the status, headers and body of a failure. The WWW-Authenticate challenge -- the client's discovery mechanism under the MCP authorization spec -- was previously discarded. It derives from TransportError, so existing catch sites are unaffected. * Credentials are dropped across a cross-origin redirect. * Client::set_protocol_version(), and cancel() now accepts a JSON-RPC id. Protocol: * ITransport::notify() frames notifications without an id. notifications/initialized was never sent, leaving the handshake incomplete; cancel/progress/roots-list-changed went out as requests carrying ids. * Servers negotiate: initialize echoes back a recognised requested version rather than announcing a constant, so older clients keep working. The default advances to 2025-11-25. * A timed-out tools/call sends notifications/cancelled naming that request. Fixes: * The Host/Origin guard could reset the connection instead of delivering its 421/403. httplib runs pre-routing before the body is read, so rejecting a POST left it unread and the close sent RST -- and every MCP request is a POST with a body. * call_tool_mcp's timeout could not fire early: an std::async future's destructor blocks, so the exception arrived only after the call finished. * clientInfo.version was hardcoded at 2.14.0; it is now the build version. Versioning: major.minor.patch tracks the upstream fastmcp release this port is at parity with; the fourth component is a local revision for C++-side work with no upstream delta, and resets when the parity level moves.
1 parent 3f4f9b5 commit c920f1f

45 files changed

Lines changed: 5035 additions & 753 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/ci.yml‎

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,46 @@ jobs:
6464
run: cmake --build build --config ${{ matrix.build_type }} -- /m:1 /p:CL_MPCount=1
6565

6666
- name: Test
67-
run: ctest --test-dir build -C ${{ matrix.build_type }} --output-on-failure
67+
# --timeout so a hung test fails its own case instead of running to the
68+
# job limit. There is no per-test TIMEOUT property on most of these, and a
69+
# blocked process would otherwise hold the whole leg for hours.
70+
run: ctest --test-dir build -C ${{ matrix.build_type }} --output-on-failure --timeout 300
71+
72+
# The default matrix never sets FASTMCPP_ENABLE_CURL_TRANSPORT, so without this
73+
# job the libcurl transport is the one piece of the client that CI never
74+
# compiles -- on exactly the platforms it exists for. WinHTTP needs no
75+
# equivalent: it is on by default and windows-latest is already in the matrix.
76+
curl-transport:
77+
strategy:
78+
fail-fast: false
79+
matrix:
80+
os: [ubuntu-latest, macos-latest]
81+
82+
runs-on: ${{ matrix.os }}
83+
name: ${{ matrix.os }} (curl transport)
84+
85+
steps:
86+
- name: Checkout
87+
uses: actions/checkout@v4
88+
89+
- name: Cache FetchContent dependencies
90+
uses: actions/cache@v4
91+
with:
92+
path: build-curl/_deps
93+
key: curl-deps-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('CMakeLists.txt') }}
94+
restore-keys: |
95+
curl-deps-${{ runner.os }}-${{ runner.arch }}-
96+
97+
- name: Configure
98+
run: >
99+
cmake -B build-curl -S .
100+
-DCMAKE_BUILD_TYPE=Release
101+
-DFASTMCPP_BUILD_TESTS=ON
102+
-DFASTMCPP_BUILD_EXAMPLES=OFF
103+
-DFASTMCPP_ENABLE_CURL_TRANSPORT=ON
104+
105+
- name: Build
106+
run: cmake --build build-curl --config Release --parallel 2
107+
108+
- name: Test
109+
run: ctest --test-dir build-curl -C Release --output-on-failure --timeout 300

‎CMakeLists.txt‎

Lines changed: 99 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,14 @@ cmake_minimum_required(VERSION 3.16)
33
if(POLICY CMP0169)
44
cmake_policy(SET CMP0169 OLD)
55
endif()
6-
project(fastmcpp VERSION 3.4.7 LANGUAGES CXX)
6+
# major.minor.patch = upstream fastmcp parity level; optional 4th = local revision.
7+
project(fastmcpp VERSION 3.4.7.1 LANGUAGES CXX)
8+
9+
# Empty for a 3-component version, which would expand to FASTMCPP_VERSION_TWEAK=.
10+
if(NOT DEFINED PROJECT_VERSION_TWEAK OR PROJECT_VERSION_TWEAK STREQUAL "")
11+
set(PROJECT_VERSION_TWEAK 0)
12+
endif()
13+
set(FASTMCPP_VERSION_STRING "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}.${PROJECT_VERSION_TWEAK}")
714

815
set(CMAKE_CXX_STANDARD 17)
916
set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -16,6 +23,17 @@ option(FASTMCPP_FETCH_CURL "Fetch and build libcurl statically for POST streamin
1623
option(FASTMCPP_ENABLE_SAMPLING_HTTP_HANDLERS "Enable built-in OpenAI/Anthropic sampling handlers (requires libcurl)" OFF)
1724
option(FASTMCPP_ENABLE_OPENSSL "Enable HTTPS/SSL support via OpenSSL (for cpp-httplib)" OFF)
1825

26+
# TLS via Schannel, no extra dependency. Reaches https:// without OpenSSL.
27+
if(WIN32)
28+
option(FASTMCPP_ENABLE_WINHTTP "Build the WinHTTP client transport (Windows only)" ON)
29+
else()
30+
set(FASTMCPP_ENABLE_WINHTTP OFF)
31+
endif()
32+
33+
# Portable https://, using whichever TLS backend libcurl binds to. Off by default
34+
# because it pulls libcurl into the build.
35+
option(FASTMCPP_ENABLE_CURL_TRANSPORT "Build the libcurl client transport (all platforms)" OFF)
36+
1937
add_library(fastmcpp_core STATIC
2038
src/types.cpp
2139
src/util/schema_build.cpp
@@ -55,18 +73,33 @@ add_library(fastmcpp_core STATIC
5573
src/client/client.cpp
5674
src/client/sampling_handlers.cpp
5775
src/client/transports.cpp
76+
src/client/http_detail.cpp
77+
src/client/sse_stream.cpp
78+
src/client/http_rpc_transport.cpp
5879
src/telemetry.cpp
5980
src/util/json_schema.cpp
6081
src/util/json_schema_type.cpp
6182
src/settings.cpp
6283
)
84+
if(WIN32 AND FASTMCPP_ENABLE_WINHTTP)
85+
target_sources(fastmcpp_core PRIVATE src/client/winhttp_transport.cpp)
86+
target_link_libraries(fastmcpp_core PUBLIC winhttp)
87+
target_compile_definitions(fastmcpp_core PUBLIC FASTMCPP_HAS_WINHTTP)
88+
message(STATUS "FASTMCPP_ENABLE_WINHTTP=ON: WinHTTP client transport enabled")
89+
endif()
90+
6391
target_include_directories(fastmcpp_core PUBLIC
6492
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
6593
$<INSTALL_INTERFACE:include>
6694
)
6795

6896
# Version header is public
69-
target_compile_definitions(fastmcpp_core PUBLIC FASTMCPP_VERSION_MAJOR=${PROJECT_VERSION_MAJOR} FASTMCPP_VERSION_MINOR=${PROJECT_VERSION_MINOR} FASTMCPP_VERSION_PATCH=${PROJECT_VERSION_PATCH})
97+
target_compile_definitions(fastmcpp_core PUBLIC
98+
FASTMCPP_VERSION_MAJOR=${PROJECT_VERSION_MAJOR}
99+
FASTMCPP_VERSION_MINOR=${PROJECT_VERSION_MINOR}
100+
FASTMCPP_VERSION_PATCH=${PROJECT_VERSION_PATCH}
101+
FASTMCPP_VERSION_TWEAK=${PROJECT_VERSION_TWEAK}
102+
FASTMCPP_VERSION_STRING="${FASTMCPP_VERSION_STRING}")
70103

71104
# MSVC: avoid parallel compilation PDB contention (C1041) in large targets
72105
if(MSVC)
@@ -135,7 +168,7 @@ else()
135168
endif()
136169

137170
# Optional: libcurl for POST streaming and sampling handlers (modular)
138-
if(FASTMCPP_ENABLE_POST_STREAMING OR FASTMCPP_ENABLE_SAMPLING_HTTP_HANDLERS)
171+
if(FASTMCPP_ENABLE_POST_STREAMING OR FASTMCPP_ENABLE_SAMPLING_HTTP_HANDLERS OR FASTMCPP_ENABLE_CURL_TRANSPORT)
139172
if(FASTMCPP_FETCH_CURL)
140173
message(STATUS "FASTMCPP_FETCH_CURL=ON: fetching curl via FetchContent (static-only)")
141174
include(FetchContent)
@@ -196,13 +229,22 @@ if(FASTMCPP_ENABLE_POST_STREAMING OR FASTMCPP_ENABLE_SAMPLING_HTTP_HANDLERS)
196229
if(FASTMCPP_ENABLE_POST_STREAMING)
197230
target_compile_definitions(fastmcpp_core PRIVATE FASTMCPP_POST_STREAMING)
198231
endif()
232+
if(FASTMCPP_ENABLE_CURL_TRANSPORT)
233+
target_sources(fastmcpp_core PRIVATE src/client/curl_transport.cpp)
234+
# PUBLIC: the header is installed, so consumers need the same guard.
235+
target_compile_definitions(fastmcpp_core PUBLIC FASTMCPP_HAS_CURL_TRANSPORT)
236+
message(STATUS "FASTMCPP_ENABLE_CURL_TRANSPORT=ON: libcurl client transport enabled")
237+
endif()
199238
else()
200239
if(FASTMCPP_ENABLE_POST_STREAMING)
201240
message(STATUS "libcurl not found; POST streaming will be disabled at runtime (request_stream_post throws)")
202241
endif()
203242
if(FASTMCPP_ENABLE_SAMPLING_HTTP_HANDLERS)
204243
message(STATUS "libcurl not found; built-in sampling handlers will be unavailable")
205244
endif()
245+
if(FASTMCPP_ENABLE_CURL_TRANSPORT)
246+
message(FATAL_ERROR "FASTMCPP_ENABLE_CURL_TRANSPORT=ON but libcurl was not found; set FASTMCPP_FETCH_CURL=ON or provide a curl toolchain")
247+
endif()
206248
endif()
207249
endif()
208250

@@ -222,6 +264,13 @@ endif()
222264

223265
if(FASTMCPP_BUILD_TESTS)
224266
enable_testing()
267+
268+
# Routes a failed Debug assert to stderr instead of a modal dialog.
269+
# OBJECT, not STATIC: nothing references it, so STATIC would link but not run.
270+
# link_libraries() covers every test target created below.
271+
add_library(fastmcpp_test_crt OBJECT tests/support/crt_bootstrap.cpp)
272+
link_libraries(fastmcpp_test_crt)
273+
225274
add_executable(fastmcp_smoke tests/smoke.cpp)
226275
target_link_libraries(fastmcp_smoke PRIVATE fastmcpp_core)
227276
add_test(NAME fastmcp_smoke COMMAND fastmcp_smoke)
@@ -507,6 +556,50 @@ if(FASTMCPP_BUILD_TESTS)
507556
target_link_libraries(fastmcpp_client_transports PRIVATE fastmcpp_core)
508557
add_test(NAME fastmcpp_client_transports COMMAND fastmcpp_client_transports)
509558

559+
add_executable(fastmcpp_client_header_provider tests/client/header_provider.cpp)
560+
target_link_libraries(fastmcpp_client_header_provider PRIVATE fastmcpp_core)
561+
add_test(NAME fastmcpp_client_header_provider COMMAND fastmcpp_client_header_provider)
562+
563+
add_executable(fastmcpp_client_http_rpc_transport tests/client/http_rpc_transport.cpp)
564+
target_link_libraries(fastmcpp_client_http_rpc_transport PRIVATE fastmcpp_core)
565+
add_test(NAME fastmcpp_client_http_rpc_transport COMMAND fastmcpp_client_http_rpc_transport)
566+
567+
add_executable(fastmcpp_client_sse_stream tests/client/sse_stream.cpp)
568+
target_link_libraries(fastmcpp_client_sse_stream PRIVATE fastmcpp_core)
569+
add_test(NAME fastmcpp_client_sse_stream COMMAND fastmcpp_client_sse_stream)
570+
571+
add_executable(fastmcpp_client_version_reporting tests/client/version_reporting.cpp)
572+
target_link_libraries(fastmcpp_client_version_reporting PRIVATE fastmcpp_core)
573+
add_test(NAME fastmcpp_client_version_reporting COMMAND fastmcpp_client_version_reporting)
574+
575+
add_executable(fastmcpp_client_cancel_on_timeout tests/client/cancel_on_timeout.cpp)
576+
target_link_libraries(fastmcpp_client_cancel_on_timeout PRIVATE fastmcpp_core)
577+
add_test(NAME fastmcpp_client_cancel_on_timeout COMMAND fastmcpp_client_cancel_on_timeout)
578+
579+
if(WIN32 AND FASTMCPP_ENABLE_WINHTTP)
580+
add_executable(fastmcpp_client_winhttp_transport tests/client/winhttp_transport.cpp)
581+
target_link_libraries(fastmcpp_client_winhttp_transport PRIVATE fastmcpp_core)
582+
add_test(NAME fastmcpp_client_winhttp_transport COMMAND fastmcpp_client_winhttp_transport)
583+
endif()
584+
585+
if(FASTMCPP_ENABLE_CURL_TRANSPORT)
586+
add_executable(fastmcpp_client_curl_transport tests/client/curl_transport.cpp)
587+
target_link_libraries(fastmcpp_client_curl_transport PRIVATE fastmcpp_core)
588+
add_test(NAME fastmcpp_client_curl_transport COMMAND fastmcpp_client_curl_transport)
589+
endif()
590+
591+
add_executable(fastmcpp_client_http_error_detail tests/client/http_error_detail.cpp)
592+
target_link_libraries(fastmcpp_client_http_error_detail PRIVATE fastmcpp_core)
593+
add_test(NAME fastmcpp_client_http_error_detail COMMAND fastmcpp_client_http_error_detail)
594+
595+
add_executable(fastmcpp_client_notifications tests/client/notifications.cpp)
596+
target_link_libraries(fastmcpp_client_notifications PRIVATE fastmcpp_core)
597+
add_test(NAME fastmcpp_client_notifications COMMAND fastmcpp_client_notifications)
598+
599+
add_executable(fastmcpp_client_protocol_version tests/client/protocol_version.cpp)
600+
target_link_libraries(fastmcpp_client_protocol_version PRIVATE fastmcpp_core)
601+
add_test(NAME fastmcpp_client_protocol_version COMMAND fastmcpp_client_protocol_version)
602+
510603
add_executable(fastmcpp_client_http_client_security tests/client/http_client_security.cpp)
511604
target_link_libraries(fastmcpp_client_http_client_security PRIVATE fastmcpp_core)
512605
add_test(NAME fastmcpp_client_http_client_security COMMAND fastmcpp_client_http_client_security)
@@ -738,6 +831,9 @@ if(FASTMCPP_BUILD_TESTS)
738831
endif()
739832
endif()
740833

834+
# End of test scope: examples must not inherit the Debug CRT bootstrap.
835+
set_property(DIRECTORY PROPERTY LINK_LIBRARIES "")
836+
741837
if(FASTMCPP_BUILD_EXAMPLES)
742838
add_executable(fastmcpp_example_server examples/server_quick_start.cpp)
743839
target_link_libraries(fastmcpp_example_server PRIVATE fastmcpp_core)

‎README.md‎

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,27 @@ fastmcpp is a C++ port of the Python [fastmcp](https://github.com/jlowin/fastmcp
1515

1616
**Status:** Beta – core MCP features track the Python `fastmcp` reference.
1717

18-
**Current version:** 3.4.7
18+
**Current version:** 3.4.7.1
1919

2020
## Features
2121

2222
- Core MCP protocol implementation (JSON‑RPC).
2323
- Multiple transports: STDIO, HTTP (SSE), Streamable HTTP.
2424
- Streamable HTTP transport (MCP spec 2025-03-26) with session management.
25+
- WinHTTP client transport on Windows: HTTPS through Schannel, with the system
26+
certificate store and enterprise proxy configuration, and no OpenSSL.
27+
- libcurl client transport on every platform, binding to whichever TLS backend
28+
the platform provides (`FASTMCPP_ENABLE_CURL_TRANSPORT=ON`).
29+
- Per-request header provider for rotating credentials such as OAuth tokens.
30+
- HTTP failures reported as `TransportHttpError`, retaining the status code,
31+
response headers (including `WWW-Authenticate`) and body.
2532
- Tool management and invocation.
2633
- Resources and prompts support.
2734
- Resource templates with URI pattern matching.
2835
- Resource annotations (MCP 2025-11-25): audience targeting, priority hints, icons.
36+
- MCP protocol version `2025-11-25` by default, with negotiation: a server echoes
37+
back the revision a client asked for when it recognises it, so an older client
38+
keeps working. Override per client with `Client::set_protocol_version()`.
2939
- JSON Schema validation.
3040
- FastMCP high-level application class.
3141
- ProxyApp for backend server proxying.
@@ -40,13 +50,47 @@ fastmcpp is a C++ port of the Python [fastmcp](https://github.com/jlowin/fastmcp
4050
## Requirements
4151

4252
- C++17 or later compiler.
43-
- CMake 3.20 or higher.
53+
- CMake 3.16 or higher.
4454
- `nlohmann/json` (fetched automatically).
4555

4656
Optional:
4757

48-
- libcurl (for HTTP POST streaming; can be fetched when `FASTMCPP_FETCH_CURL=ON`).
58+
- libcurl (for HTTP POST streaming and the libcurl client transport; fetched
59+
when `FASTMCPP_FETCH_CURL=ON`, which is the default).
4960
- cpp‑httplib (HTTP server, fetched automatically).
61+
- OpenSSL, only if you want HTTPS through the cpp‑httplib transports
62+
(`FASTMCPP_ENABLE_OPENSSL=ON`). See "Reaching `https://` servers" below for
63+
the alternatives that need no extra dependency.
64+
65+
### Reaching `https://` servers
66+
67+
The cpp‑httplib transports negotiate TLS only when cpp‑httplib itself is
68+
compiled with `CPPHTTPLIB_OPENSSL_SUPPORT`, so with the default
69+
`FASTMCPP_ENABLE_OPENSSL=OFF` an `https://` URL fails at runtime with
70+
`'https' scheme is not supported`. The URL parser accepts the scheme and
71+
defaults to port 443; only the TLS handshake is missing.
72+
73+
Three ways to fix that:
74+
75+
- **Windows** — use `fastmcpp::client::WinHttpTransport`, built by default
76+
(`FASTMCPP_ENABLE_WINHTTP=ON`). TLS comes from Schannel, and the system
77+
certificate store, enterprise proxy settings and CA policy come with it. No
78+
extra dependency.
79+
- **Any platform** — use `fastmcpp::client::CurlTransport`, built with
80+
`FASTMCPP_ENABLE_CURL_TRANSPORT=ON`. libcurl binds to whichever TLS backend
81+
the platform provides, so this reaches `https://` on Linux and macOS as well
82+
as Windows.
83+
- **OpenSSL** — set `FASTMCPP_ENABLE_OPENSSL=ON`, with one caveat worth knowing:
84+
it is necessary but not sufficient when your project supplies its own
85+
`httplib::httplib` target. fastmcpp then leaves `CPPHTTPLIB_OPENSSL_SUPPORT`
86+
to that target to avoid an ODR mismatch, so cpp‑httplib must itself be built
87+
with its `openssl` feature or the build will succeed and then fail at runtime.
88+
89+
All three implement `ITransport`, so they drop into `Client` in place of
90+
`StreamableHttpTransport`. The two above share `HttpRpcTransport`, which owns
91+
the JSON-RPC framing, the redirect policy and session handling, so they also
92+
support the per-request header provider and report failures as
93+
`TransportHttpError`.
5094

5195
## Building
5296

@@ -75,8 +119,15 @@ Key options:
75119
| Option | Default | Description |
76120
|----------------------------------|---------|--------------------------------------------------|
77121
| `CMAKE_BUILD_TYPE` | Debug | Build configuration (Debug/Release/RelWithDebInfo) |
122+
| `FASTMCPP_BUILD_TESTS` | ON | Build the test suite |
123+
| `FASTMCPP_BUILD_EXAMPLES` | ON | Build the example programs |
124+
| `FASTMCPP_BUILD_CLI` | ON | Build the `fastmcpp` command-line executable |
78125
| `FASTMCPP_ENABLE_POST_STREAMING` | OFF | Enable HTTP POST streaming (requires libcurl) |
79-
| `FASTMCPP_FETCH_CURL` | OFF | Fetch and build curl (via FetchContent) if not found |
126+
| `FASTMCPP_FETCH_CURL` | ON | Fetch and build curl (via FetchContent) if not found |
127+
| `FASTMCPP_ENABLE_OPENSSL` | OFF | HTTPS for the cpp-httplib transports, via OpenSSL |
128+
| `FASTMCPP_ENABLE_WINHTTP` | ON (Windows) | Build the WinHTTP client transport |
129+
| `FASTMCPP_ENABLE_CURL_TRANSPORT` | OFF | Build the libcurl client transport (all platforms) |
130+
| `FASTMCPP_ENABLE_SAMPLING_HTTP_HANDLERS` | OFF | Built-in OpenAI/Anthropic sampling handlers |
80131
| `FASTMCPP_ENABLE_STREAMING_TESTS` | OFF | Enable SSE streaming tests |
81132

82133
### Platform notes
@@ -355,7 +406,7 @@ See the `examples/` directory for complete programs, including:
355406
fastmcpp/
356407
include/fastmcpp/ # Public headers (client, server, tools, etc.)
357408
src/ # Implementation
358-
tests/ # Test suite (GoogleTest)
409+
tests/ # Test suite (standalone executables, no framework)
359410
examples/ # Example programs
360411
CMakeLists.txt # Build configuration
361412
LICENSE # Apache 2.0 license

‎include/fastmcpp.hpp‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,11 @@
3232

3333
// Client
3434
#include "fastmcpp/client/client.hpp"
35+
#include "fastmcpp/client/curl_transport.hpp"
36+
#include "fastmcpp/client/http_rpc_transport.hpp"
3537
#include "fastmcpp/client/transports.hpp"
3638
#include "fastmcpp/client/types.hpp"
39+
#include "fastmcpp/client/winhttp_transport.hpp"
3740

3841
// Server
3942
#include "fastmcpp/server/context.hpp"

0 commit comments

Comments
 (0)