Skip to content

Commit 1cd32e0

Browse files
authored
Merge pull request #45 from 0xeb/fix/ci-format-and-macos-timeout
fix(ci): clang-format sweep + widen tool-timeout test margins
2 parents c54d21e + 5e824ca commit 1cd32e0

9 files changed

Lines changed: 33 additions & 18 deletions

File tree

include/fastmcpp/client/client.hpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,10 +1050,8 @@ class Client
10501050
// ValidationError so older servers / partial responses do not crash the client.
10511051
// - "content" present but not an array is treated as empty (do not crash).
10521052
if (body.contains("content") && body["content"].is_array())
1053-
{
10541053
for (const auto& c : body["content"])
10551054
result.content.push_back(parse_content_block(c));
1056-
}
10571055
// else: leave result.content empty
10581056

10591057
if (body.contains("structuredContent"))

include/fastmcpp/client/types.hpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -401,9 +401,8 @@ inline void from_json(const fastmcpp::Json& j, ToolInfo& t)
401401
// Python fastmcp >= 2.x exposes per-tool version via _meta.fastmcp.version (see
402402
// fastmcp_slim/fastmcp/utilities/components.py:get_meta). Surface it as ToolInfo.version
403403
// if no top-level "version" was provided so the proxy passthrough preserves the field.
404-
if (!t.version && j["_meta"].is_object() && j["_meta"].contains("fastmcp")
405-
&& j["_meta"]["fastmcp"].is_object()
406-
&& j["_meta"]["fastmcp"].contains("version"))
404+
if (!t.version && j["_meta"].is_object() && j["_meta"].contains("fastmcp") &&
405+
j["_meta"]["fastmcp"].is_object() && j["_meta"]["fastmcp"].contains("version"))
407406
{
408407
const auto& v = j["_meta"]["fastmcp"]["version"];
409408
if (v.is_string())

include/fastmcpp/tools/tool_transform.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ build_transformed_schema(const Json& parent_schema,
157157
if (k == "$defs" && v.is_object())
158158
{
159159
hoisted_defs = v;
160-
continue; // do not also write it under the property
160+
continue; // do not also write it under the property
161161
}
162162
new_prop[k] = v;
163163
}

src/client/sampling_handlers.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,8 +261,7 @@ static fastmcpp::Json build_openai_messages(const fastmcpp::Json& params)
261261
"' content not yet supported (F16 / fastmcp #3550); cannot dispatch sampling "
262262
"request");
263263
// Unknown type — surface clearly so callers don't get silent data loss.
264-
throw std::runtime_error(
265-
"OpenAI sampling handler: unhandled content type '" + t + "'");
264+
throw std::runtime_error("OpenAI sampling handler: unhandled content type '" + t + "'");
266265
}
267266

268267
std::string text = join_text_blocks(content);

src/providers/openapi_provider.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,8 @@ Json OpenAPIProvider::invoke_route(const RouteDefinition& route, const Json& arg
444444

445445
std::ostringstream query;
446446
bool first = true;
447-
auto append_pair = [&](const std::string& key, const std::string& val) {
447+
auto append_pair = [&](const std::string& key, const std::string& val)
448+
{
448449
query << (first ? "?" : "&");
449450
first = false;
450451
query << url_encode_component(key) << "=" << url_encode_component(val);

src/server/response_limiting_middleware.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ AfterHook ResponseLimitingMiddleware::make_hook() const
7171
// outputSchema after truncation) and signal bypass via `_meta = {}` so MCP SDK
7272
// clients accept the response as a vanilla CallToolResult instead of failing
7373
// outputSchema validation. Apply at both shapes (route payload + JSON-RPC envelope).
74-
auto bypass_output_schema = [](fastmcpp::Json& obj) {
74+
auto bypass_output_schema = [](fastmcpp::Json& obj)
75+
{
7576
if (obj.contains("structuredContent"))
7677
obj.erase("structuredContent");
7778
if (!obj.contains("_meta") || !obj["_meta"].is_object())

src/util/json_schema_type.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ const std::regex& cached_regex_required(const std::string& key, const std::strin
5858
{
5959
auto* p = cached_regex(key, pattern);
6060
if (!p)
61-
throw fastmcpp::ValidationError("internal regex compile failure for built-in pattern: " + key);
61+
throw fastmcpp::ValidationError("internal regex compile failure for built-in pattern: " +
62+
key);
6263
return *p;
6364
}
6465

@@ -152,8 +153,8 @@ SchemaValue handle_string(const fastmcpp::Json& schema, const fastmcpp::Json& in
152153
}
153154
else if (fmt == "date-time")
154155
{
155-
const auto& dt_re =
156-
cached_regex_required("date-time", R"(^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$)");
156+
const auto& dt_re = cached_regex_required(
157+
"date-time", R"(^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$)");
157158
if (!std::regex_match(value, dt_re))
158159
throw fastmcpp::ValidationError("Invalid date-time format at " + path);
159160
}
@@ -363,7 +364,7 @@ SchemaValue convert(const fastmcpp::Json& schema, const fastmcpp::Json& instance
363364
if (schema.is_boolean())
364365
{
365366
if (schema.get<bool>())
366-
return instance; // true: accept-any, pass through
367+
return instance; // true: accept-any, pass through
367368
throw fastmcpp::ValidationError("schema=false rejects all values at " + path);
368369
}
369370

tests/tools/test_tool_timeout.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,14 @@ void test_tool_timeout_triggers()
2727
Tool slow_tool("slow", Json::object(), Json::object(),
2828
[](const Json&) -> Json
2929
{
30-
sleep_for_at_least(50ms);
30+
// Large sleep margin so the timeout fires reliably even on
31+
// slow CI runners (macOS Debug) where scheduling jitter can
32+
// delay future::wait_for() past the worker's sleep duration.
33+
sleep_for_at_least(5s);
3134
return Json{{"ok", true}};
3235
});
3336

34-
slow_tool.set_timeout(10ms);
37+
slow_tool.set_timeout(50ms);
3538

3639
bool threw = false;
3740
try
@@ -72,11 +75,11 @@ void test_manager_timeout_toggle()
7275
Tool slow_tool("slow_manager", Json::object(), Json::object(),
7376
[](const Json&) -> Json
7477
{
75-
sleep_for_at_least(40ms);
78+
sleep_for_at_least(5s);
7679
return Json{{"ok", true}};
7780
});
7881

79-
slow_tool.set_timeout(10ms);
82+
slow_tool.set_timeout(50ms);
8083

8184
ToolManager tm;
8285
tm.register_tool(slow_tool);

tests/transports/stdio_lifecycle.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
#include <string>
88
#include <vector>
99

10+
#ifndef _WIN32
11+
#include <csignal>
12+
#endif
13+
1014
static std::string find_stdio_server_binary()
1115
{
1216
namespace fs = std::filesystem;
@@ -30,6 +34,15 @@ int main()
3034
using fastmcpp::Json;
3135
using fastmcpp::client::StdioTransport;
3236

37+
#ifndef _WIN32
38+
// Ignore SIGPIPE: writing to a closed subprocess stdin (e.g. when the child
39+
// has already exited, as in Test 1's `sh -c "exit 42"`) must produce
40+
// EPIPE/return -1, not kill this test binary. macOS Debug runners under
41+
// CI load can race the child's exit ahead of our first write, surfacing
42+
// this as a SIGPIPE-induced test failure.
43+
signal(SIGPIPE, SIG_IGN);
44+
#endif
45+
3346
// Test 1: Server process crash surfaces TransportError with context
3447
std::cout << "Test: server crash surfaces TransportError...\n";
3548
{

0 commit comments

Comments
 (0)