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
101 changes: 72 additions & 29 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ option(FASTMCPP_BUILD_EXAMPLES "Build examples" ON)
option(FASTMCPP_ENABLE_POST_STREAMING "Enable POST streaming via libcurl (optional)" OFF)
option(FASTMCPP_FETCH_CURL "Fetch and build libcurl statically for POST streaming" ON)
option(FASTMCPP_ENABLE_SAMPLING_HTTP_HANDLERS "Enable built-in OpenAI/Anthropic sampling handlers (requires libcurl)" OFF)
option(FASTMCPP_ENABLE_WS_STREAMING_TESTS "Enable WebSocket streaming tests (requires external server)" OFF)
option(FASTMCPP_ENABLE_LOCAL_WS_TEST "Enable local WebSocket server test (depends on httplib ws server support)" OFF)

add_library(fastmcpp_core STATIC
src/types.cpp
Expand All @@ -24,7 +22,12 @@ add_library(fastmcpp_core STATIC
src/providers/transforms/namespace.cpp
src/providers/transforms/tool_transform.cpp
src/providers/transforms/visibility.cpp
src/providers/transforms/version_filter.cpp
src/providers/transforms/prompts_as_tools.cpp
src/providers/transforms/resources_as_tools.cpp
src/providers/filesystem_provider.cpp
src/providers/skills_provider.cpp
src/providers/openapi_provider.cpp
src/proxy.cpp
src/mcp/handler.cpp
src/mcp/tasks.cpp
Expand All @@ -40,6 +43,8 @@ add_library(fastmcpp_core STATIC
src/server/context.cpp
src/server/middleware.cpp
src/server/security_middleware.cpp
src/server/response_limiting_middleware.cpp
src/server/ping_middleware.cpp
src/server/sampling.cpp
src/server/http_server.cpp
src/server/stdio_server.cpp
Expand Down Expand Up @@ -92,19 +97,6 @@ if(NOT cpp_httplib_POPULATED)
endif()
target_include_directories(fastmcpp_core PUBLIC ${cpp_httplib_SOURCE_DIR})

# Header-only WebSocket client (easywsclient)
FetchContent_Declare(
easywsclient
GIT_REPOSITORY https://github.com/dhbaird/easywsclient.git
GIT_TAG master
)
FetchContent_GetProperties(easywsclient)
if(NOT easywsclient_POPULATED)
FetchContent_Populate(easywsclient)
endif()
target_include_directories(fastmcpp_core PUBLIC ${easywsclient_SOURCE_DIR})
target_sources(fastmcpp_core PRIVATE ${easywsclient_SOURCE_DIR}/easywsclient.cpp)

# Optional: libcurl for POST streaming and sampling handlers (modular)
if(FASTMCPP_ENABLE_POST_STREAMING OR FASTMCPP_ENABLE_SAMPLING_HTTP_HANDLERS)
if(FASTMCPP_FETCH_CURL)
Expand Down Expand Up @@ -248,6 +240,9 @@ if(FASTMCPP_BUILD_TESTS)
add_test(NAME fastmcpp_cli_tasks_demo COMMAND fastmcpp tasks demo)
add_executable(fastmcpp_cli_tasks_ux tests/cli/tasks_cli.cpp)
add_test(NAME fastmcpp_cli_tasks_ux COMMAND fastmcpp_cli_tasks_ux)
add_executable(fastmcpp_cli_generated_e2e tests/cli/generated_cli_e2e.cpp)
target_link_libraries(fastmcpp_cli_generated_e2e PRIVATE fastmcpp_core)
add_test(NAME fastmcpp_cli_generated_e2e COMMAND fastmcpp_cli_generated_e2e)

add_executable(fastmcpp_http_integration tests/server/http_integration.cpp)
target_link_libraries(fastmcpp_http_integration PRIVATE fastmcpp_core)
Expand All @@ -273,6 +268,10 @@ if(FASTMCPP_BUILD_TESTS)
target_link_libraries(fastmcpp_schema_build PRIVATE fastmcpp_core)
add_test(NAME fastmcpp_schema_build COMMAND fastmcpp_schema_build)

add_executable(fastmcpp_schema_dereference_toggle tests/schema/dereference_toggle.cpp)
target_link_libraries(fastmcpp_schema_dereference_toggle PRIVATE fastmcpp_core)
add_test(NAME fastmcpp_schema_dereference_toggle COMMAND fastmcpp_schema_dereference_toggle)

add_executable(fastmcpp_content tests/content.cpp)
target_link_libraries(fastmcpp_content PRIVATE fastmcpp_core)
add_test(NAME fastmcpp_content COMMAND fastmcpp_content)
Expand Down Expand Up @@ -477,6 +476,11 @@ if(FASTMCPP_BUILD_TESTS)
target_link_libraries(fastmcpp_app_ergonomics PRIVATE fastmcpp_core)
add_test(NAME fastmcpp_app_ergonomics COMMAND fastmcpp_app_ergonomics)

# MCP Apps metadata parity tests
add_executable(fastmcpp_app_mcp_apps tests/app/mcp_apps.cpp)
target_link_libraries(fastmcpp_app_mcp_apps PRIVATE fastmcpp_core)
add_test(NAME fastmcpp_app_mcp_apps COMMAND fastmcpp_app_mcp_apps)

# Filesystem provider tests
add_library(fastmcpp_fs_test_plugin SHARED tests/fs/test_plugin.cpp)
target_compile_definitions(fastmcpp_fs_test_plugin PRIVATE FASTMCPP_PROVIDER_EXPORTS)
Expand All @@ -497,6 +501,18 @@ if(FASTMCPP_BUILD_TESTS)
target_link_libraries(fastmcpp_provider_transforms PRIVATE fastmcpp_core)
add_test(NAME fastmcpp_provider_transforms COMMAND fastmcpp_provider_transforms)

add_executable(fastmcpp_provider_version_filter tests/providers/version_filter.cpp)
target_link_libraries(fastmcpp_provider_version_filter PRIVATE fastmcpp_core)
add_test(NAME fastmcpp_provider_version_filter COMMAND fastmcpp_provider_version_filter)

add_executable(fastmcpp_provider_skills tests/providers/skills_provider.cpp)
target_link_libraries(fastmcpp_provider_skills PRIVATE fastmcpp_core)
add_test(NAME fastmcpp_provider_skills COMMAND fastmcpp_provider_skills)

add_executable(fastmcpp_provider_openapi tests/providers/openapi_provider.cpp)
target_link_libraries(fastmcpp_provider_openapi PRIVATE fastmcpp_core)
add_test(NAME fastmcpp_provider_openapi COMMAND fastmcpp_provider_openapi)

# Proxy tests
add_executable(fastmcpp_proxy_basic tests/proxy/basic.cpp)
target_link_libraries(fastmcpp_proxy_basic PRIVATE fastmcpp_core)
Expand All @@ -507,6 +523,31 @@ if(FASTMCPP_BUILD_TESTS)
target_link_libraries(fastmcpp_main_header PRIVATE fastmcpp_core)
add_test(NAME fastmcpp_main_header COMMAND fastmcpp_main_header)

# Sync-cycle tests (Phase 11)
add_executable(fastmcpp_mcp_error_codes tests/mcp/test_error_codes.cpp)
target_link_libraries(fastmcpp_mcp_error_codes PRIVATE fastmcpp_core)
add_test(NAME fastmcpp_mcp_error_codes COMMAND fastmcpp_mcp_error_codes)

add_executable(fastmcpp_pagination tests/mcp/test_pagination.cpp)
target_link_libraries(fastmcpp_pagination PRIVATE fastmcpp_core)
add_test(NAME fastmcpp_pagination COMMAND fastmcpp_pagination)

add_executable(fastmcpp_tools_transform_enabled tests/tools/test_tool_transform_enabled.cpp)
target_link_libraries(fastmcpp_tools_transform_enabled PRIVATE fastmcpp_core)
add_test(NAME fastmcpp_tools_transform_enabled COMMAND fastmcpp_tools_transform_enabled)

add_executable(fastmcpp_tools_sequential tests/tools/test_tool_sequential.cpp)
target_link_libraries(fastmcpp_tools_sequential PRIVATE fastmcpp_core)
add_test(NAME fastmcpp_tools_sequential COMMAND fastmcpp_tools_sequential)

add_executable(fastmcpp_session_state tests/server/test_session_state.cpp)
target_link_libraries(fastmcpp_session_state PRIVATE fastmcpp_core)
add_test(NAME fastmcpp_session_state COMMAND fastmcpp_session_state)

add_executable(fastmcpp_response_limiting tests/server/test_response_limiting.cpp)
target_link_libraries(fastmcpp_response_limiting PRIVATE fastmcpp_core)
add_test(NAME fastmcpp_response_limiting COMMAND fastmcpp_response_limiting)

set_tests_properties(fastmcpp_stdio_client PROPERTIES
LABELS "conformance"
WORKING_DIRECTORY "$<TARGET_FILE_DIR:fastmcpp_stdio_client>"
Expand All @@ -527,20 +568,6 @@ if(FASTMCPP_BUILD_TESTS)
set_tests_properties(fastmcpp_streaming_sse PROPERTIES RUN_SERIAL TRUE)
endif()

if(FASTMCPP_ENABLE_WS_STREAMING_TESTS)
add_executable(fastmcpp_ws_streaming tests/transports/ws_streaming.cpp)
target_link_libraries(fastmcpp_ws_streaming PRIVATE fastmcpp_core)
add_test(NAME fastmcpp_ws_streaming COMMAND fastmcpp_ws_streaming)
# Test auto-skips if FASTMCPP_WS_URL is not set

if(FASTMCPP_ENABLE_LOCAL_WS_TEST)
add_executable(fastmcpp_ws_streaming_local tests/transports/ws_streaming_local.cpp)
target_link_libraries(fastmcpp_ws_streaming_local PRIVATE fastmcpp_core)
add_test(NAME fastmcpp_ws_streaming_local COMMAND fastmcpp_ws_streaming_local)
set_tests_properties(fastmcpp_ws_streaming_local PROPERTIES RUN_SERIAL TRUE)
endif()
endif()

# POST streaming transport test (requires libcurl)
if(FASTMCPP_ENABLE_POST_STREAMING AND TARGET CURL::libcurl)
add_executable(fastmcpp_post_streaming tests/transports/post_streaming.cpp)
Expand Down Expand Up @@ -585,6 +612,18 @@ if(FASTMCPP_BUILD_EXAMPLES)
add_executable(fastmcpp_example_tags_example examples/tags_example.cpp)
target_link_libraries(fastmcpp_example_tags_example PRIVATE fastmcpp_core)

# MCP Apps example (FastMCP 3.x surface)
add_executable(fastmcpp_example_mcp_apps examples/mcp_apps.cpp)
target_link_libraries(fastmcpp_example_mcp_apps PRIVATE fastmcpp_core)

# Skills provider example
add_executable(fastmcpp_example_skills_provider examples/skills_provider.cpp)
target_link_libraries(fastmcpp_example_skills_provider PRIVATE fastmcpp_core)

# OpenAPI provider example
add_executable(fastmcpp_example_openapi_provider examples/openapi_provider.cpp)
target_link_libraries(fastmcpp_example_openapi_provider PRIVATE fastmcpp_core)

# Context API example (v2.13.0+)
add_executable(fastmcpp_example_context_introspection examples/context_introspection.cpp)
target_link_libraries(fastmcpp_example_context_introspection PRIVATE fastmcpp_core)
Expand All @@ -605,6 +644,10 @@ if(FASTMCPP_BUILD_EXAMPLES)
add_executable(fastmcpp_example_tool_injection_middleware examples/tool_injection_middleware.cpp)
target_link_libraries(fastmcpp_example_tool_injection_middleware PRIVATE fastmcpp_core)

# Response Limiting Middleware example
add_executable(fastmcpp_example_response_limiting_middleware examples/response_limiting_middleware.cpp)
target_link_libraries(fastmcpp_example_response_limiting_middleware PRIVATE fastmcpp_core)

# Server Metadata example (v2.13.0+)
add_executable(fastmcpp_example_server_metadata examples/server_metadata.cpp)
target_link_libraries(fastmcpp_example_server_metadata PRIVATE fastmcpp_core)
Expand Down
10 changes: 3 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

---

fastmcpp is a C++ port of the Python [fastmcp](https://github.com/jlowin/fastmcp) library, providing native performance for MCP servers and clients with support for tools, resources, prompts, and multiple transport layers (STDIO, HTTP/SSE, WebSocket).
fastmcpp is a C++ port of the Python [fastmcp](https://github.com/jlowin/fastmcp) library, providing native performance for MCP servers and clients with support for tools, resources, prompts, and MCP-standard transport layers (STDIO, HTTP/SSE, Streamable HTTP).

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

Expand All @@ -20,7 +20,7 @@ fastmcpp is a C++ port of the Python [fastmcp](https://github.com/jlowin/fastmcp
## Features

- Core MCP protocol implementation (JSON‑RPC).
- Multiple transports: STDIO, HTTP (SSE), Streamable HTTP, WebSocket.
- Multiple transports: STDIO, HTTP (SSE), Streamable HTTP.
- Streamable HTTP transport (MCP spec 2025-03-26) with session management.
- Tool management and invocation.
- Resources and prompts support.
Expand All @@ -47,7 +47,6 @@ Optional:

- libcurl (for HTTP POST streaming; can be fetched when `FASTMCPP_FETCH_CURL=ON`).
- cpp‑httplib (HTTP server, fetched automatically).
- easywsclient (WebSocket client, fetched automatically).

## Building

Expand All @@ -68,8 +67,7 @@ cmake -B build -S . \
-DCMAKE_BUILD_TYPE=Release \
-DFASTMCPP_ENABLE_POST_STREAMING=ON \
-DFASTMCPP_FETCH_CURL=ON \
-DFASTMCPP_ENABLE_STREAMING_TESTS=ON \
-DFASTMCPP_ENABLE_WS_STREAMING_TESTS=ON
-DFASTMCPP_ENABLE_STREAMING_TESTS=ON
```

Key options:
Expand All @@ -80,7 +78,6 @@ Key options:
| `FASTMCPP_ENABLE_POST_STREAMING` | OFF | Enable HTTP POST streaming (requires libcurl) |
| `FASTMCPP_FETCH_CURL` | OFF | Fetch and build curl (via FetchContent) if not found |
| `FASTMCPP_ENABLE_STREAMING_TESTS` | OFF | Enable SSE streaming tests |
| `FASTMCPP_ENABLE_WS_STREAMING_TESTS` | OFF | Enable WebSocket streaming tests |

### Platform notes

Expand Down Expand Up @@ -276,7 +273,6 @@ int main() {

The `create_proxy()` factory function automatically detects the transport type from the URL:
- `http://` or `https://` URLs use HTTP transport
- `ws://` or `wss://` URLs use WebSocket transport

Local tools, resources, and prompts take precedence over remote ones with the same name.

Expand Down
62 changes: 62 additions & 0 deletions examples/mcp_apps.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#include "fastmcpp/app.hpp"
#include "fastmcpp/mcp/handler.hpp"
#include "fastmcpp/server/stdio_server.hpp"

#include <string>

int main()
{
using fastmcpp::AppConfig;
using fastmcpp::FastMCP;
using fastmcpp::Json;

FastMCP app("mcp_apps_example", "1.0.0");

// Tool with MCP Apps metadata under _meta.ui (resourceUri + visibility)
FastMCP::ToolOptions tool_opts;
AppConfig tool_ui;
tool_ui.resource_uri = "ui://widgets/echo.html";
tool_ui.visibility = std::vector<std::string>{"tool_result"};
tool_opts.app = tool_ui;

app.tool("echo_ui", [](const Json& in) { return in; }, tool_opts);

// UI resource: mime_type defaults to text/html;profile=mcp-app for ui:// URIs
FastMCP::ResourceOptions resource_opts;
AppConfig resource_ui;
resource_ui.domain = "https://example.local";
resource_ui.prefers_border = true;
resource_opts.app = resource_ui;

app.resource(
"ui://widgets/home.html", "Home Widget",
[](const Json&)
{
return fastmcpp::resources::ResourceContent{
"ui://widgets/home.html", std::nullopt,
std::string{"<html><body><h1>Home</h1></body></html>"}};
},
resource_opts);

// UI resource template with per-template metadata
FastMCP::ResourceTemplateOptions templ_opts;
AppConfig templ_ui;
templ_ui.csp = Json{{"connectDomains", Json::array({"https://api.example.test"})}};
templ_opts.app = templ_ui;

app.resource_template(
"ui://widgets/{name}.html", "Named Widget",
[](const Json& params)
{
const std::string name = params.value("name", "unknown");
return fastmcpp::resources::ResourceContent{
"ui://widgets/" + name + ".html", std::nullopt,
std::string{"<html><body><h1>" + name + "</h1></body></html>"}};
},
Json::object(), templ_opts);

auto handler = fastmcpp::mcp::make_mcp_handler(app);
fastmcpp::server::StdioServerWrapper server(handler);
server.run();
return 0;
}
38 changes: 38 additions & 0 deletions examples/openapi_provider.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include "fastmcpp/providers/openapi_provider.hpp"

#include "fastmcpp/app.hpp"

#include <iostream>
#include <memory>

int main()
{
using namespace fastmcpp;

Json spec = Json::object();
spec["openapi"] = "3.0.3";
spec["info"] = Json{{"title", "Example API"}, {"version", "1.0.0"}};
spec["servers"] = Json::array({Json{{"url", "http://127.0.0.1:8080"}}});
spec["paths"] = Json::object();
spec["paths"]["/status"]["get"] = Json{
{"operationId", "getStatus"},
{"responses",
Json{{"200", Json{{"description", "ok"},
{"content",
Json{{"application/json",
Json{{"schema",
Json{{"type", "object"},
{"properties",
Json{{"status", Json{{"type", "string"}}}}}}}}}}}}}}},
};

FastMCP app("openapi-provider-example", "1.0.0");
auto provider = std::make_shared<providers::OpenAPIProvider>(spec);
app.add_provider(provider);

std::cout << "OpenAPI tools discovered:\n";
for (const auto& tool : app.list_all_tools_info())
std::cout << " - " << tool.name << "\n";
std::cout << "Run a compatible HTTP server at http://127.0.0.1:8080 to invoke these tools.\n";
return 0;
}
36 changes: 36 additions & 0 deletions examples/response_limiting_middleware.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include "fastmcpp/server/response_limiting_middleware.hpp"

#include "fastmcpp/server/server.hpp"

#include <iostream>
#include <string>

using namespace fastmcpp;

int main()
{
server::Server srv("response_limiting_demo", "1.0.0");

srv.route("tools/call",
[](const Json& payload)
{
Json args = payload.value("arguments", Json::object());
std::string text = args.value("text", std::string(120, 'A'));
return Json{
{"content", Json::array({{{"type", "text"}, {"text", text}}})},
};
});

server::ResponseLimitingMiddleware limiter(48, "... [truncated]");
srv.add_after(limiter.make_hook());

Json req = {
{"name", "echo_large"},
{"arguments",
{{"text",
"This response is intentionally long so middleware truncation is easy to see."}}}};

auto resp = srv.handle("tools/call", req);
std::cout << resp.dump(2) << "\n";
return 0;
}
Loading
Loading