Skip to content

Commit 4837e8e

Browse files
authored
Merge pull request #34 from 0xeb/feature/instructions
Restore constructor backward compatibility for instructions
2 parents 97d9bc1 + 6054c94 commit 4837e8e

14 files changed

Lines changed: 740 additions & 55 deletions

File tree

CMakeLists.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,10 @@ if(FASTMCPP_BUILD_TESTS)
268268
target_link_libraries(fastmcpp_mcp_handler PRIVATE fastmcpp_core)
269269
add_test(NAME fastmcpp_mcp_handler COMMAND fastmcpp_mcp_handler)
270270

271+
add_executable(fastmcpp_mcp_instructions tests/mcp/test_instructions.cpp)
272+
target_link_libraries(fastmcpp_mcp_instructions PRIVATE fastmcpp_core)
273+
add_test(NAME fastmcpp_mcp_instructions COMMAND fastmcpp_mcp_instructions)
274+
271275
add_executable(fastmcpp_mcp_server_handler tests/mcp/server_handler.cpp)
272276
target_link_libraries(fastmcpp_mcp_server_handler PRIVATE fastmcpp_core)
273277
add_test(NAME fastmcpp_mcp_server_handler COMMAND fastmcpp_mcp_server_handler)
@@ -446,6 +450,13 @@ if(FASTMCPP_BUILD_TESTS)
446450
target_link_libraries(fastmcpp_stdio_client PRIVATE fastmcpp_core)
447451
add_test(NAME fastmcpp_stdio_client COMMAND fastmcpp_stdio_client)
448452

453+
add_executable(fastmcpp_stdio_instructions_e2e tests/transports/stdio_instructions_e2e.cpp)
454+
target_link_libraries(fastmcpp_stdio_instructions_e2e PRIVATE fastmcpp_core)
455+
add_test(NAME fastmcpp_stdio_instructions_e2e COMMAND fastmcpp_stdio_instructions_e2e)
456+
set_tests_properties(fastmcpp_stdio_instructions_e2e PROPERTIES
457+
WORKING_DIRECTORY "$<TARGET_FILE_DIR:fastmcpp_stdio_instructions_e2e>"
458+
)
459+
449460
add_executable(fastmcpp_stdio_failure tests/transports/stdio_failure.cpp)
450461
target_link_libraries(fastmcpp_stdio_failure PRIVATE fastmcpp_core)
451462
add_test(NAME fastmcpp_stdio_failure COMMAND fastmcpp_stdio_failure)
@@ -567,6 +578,12 @@ if(FASTMCPP_BUILD_TESTS)
567578
target_link_libraries(fastmcpp_example_stdio_mcp_server PRIVATE fastmcpp_core)
568579
endif()
569580

581+
# Stdio server with instructions for E2E test
582+
if(NOT TARGET fastmcpp_example_stdio_instructions_server)
583+
add_executable(fastmcpp_example_stdio_instructions_server examples/stdio_instructions_server.cpp)
584+
target_link_libraries(fastmcpp_example_stdio_instructions_server PRIVATE fastmcpp_core)
585+
endif()
586+
570587
option(FASTMCPP_ENABLE_STREAMING_TESTS "Enable streaming SSE tests (experimental)" OFF)
571588
if(FASTMCPP_ENABLE_STREAMING_TESTS)
572589
add_executable(fastmcpp_streaming_sse tests/server/streaming_sse.cpp)
@@ -624,6 +641,10 @@ if(FASTMCPP_BUILD_EXAMPLES)
624641
add_executable(fastmcpp_example_mcp_apps examples/mcp_apps.cpp)
625642
target_link_libraries(fastmcpp_example_mcp_apps PRIVATE fastmcpp_core)
626643

644+
# Instructions HTTP server (E2E test target)
645+
add_executable(fastmcpp_example_instructions_server examples/instructions_server.cpp)
646+
target_link_libraries(fastmcpp_example_instructions_server PRIVATE fastmcpp_core)
647+
627648
# Skills provider example
628649
add_executable(fastmcpp_example_skills_provider examples/skills_provider.cpp)
629650
target_link_libraries(fastmcpp_example_skills_provider PRIVATE fastmcpp_core)

examples/instructions_server.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// instructions_server.cpp
2+
// Minimal FastMCP HTTP/SSE server with instructions field set.
3+
// Used by test_mcp_instructions_e2e.py for cross-implementation E2E validation.
4+
5+
#include "fastmcpp/app.hpp"
6+
#include "fastmcpp/mcp/handler.hpp"
7+
#include "fastmcpp/server/sse_server.hpp"
8+
9+
#include <atomic>
10+
#include <chrono>
11+
#include <csignal>
12+
#include <iostream>
13+
#include <string>
14+
#include <thread>
15+
16+
using fastmcpp::FastMCP;
17+
using fastmcpp::Json;
18+
using fastmcpp::server::SseServerWrapper;
19+
20+
static std::atomic<bool> g_running{true};
21+
22+
static void signal_handler(int)
23+
{
24+
g_running = false;
25+
}
26+
27+
int main(int argc, char* argv[])
28+
{
29+
std::signal(SIGINT, signal_handler);
30+
std::signal(SIGTERM, signal_handler);
31+
32+
int port = 8082;
33+
for (int i = 1; i < argc; ++i)
34+
{
35+
std::string arg = argv[i];
36+
if ((arg == "--port" || arg == "-p") && i + 1 < argc)
37+
port = std::stoi(argv[++i]);
38+
}
39+
40+
FastMCP app("instructions_http_server", "2.0.0",
41+
/*website_url=*/std::nullopt,
42+
/*icons=*/std::nullopt,
43+
/*instructions=*/std::string(
44+
"This server provides echo and add tools. "
45+
"Use 'echo' to repeat a message, and 'add' to sum two numbers."));
46+
47+
app.tool(
48+
"echo",
49+
Json{{"type", "object"},
50+
{"properties", Json{{"message", Json{{"type", "string"}}}}},
51+
{"required", Json::array({"message"})}},
52+
[](const Json& args) { return args.at("message"); });
53+
54+
app.tool(
55+
"add",
56+
Json{{"type", "object"},
57+
{"properties", Json{{"a", Json{{"type", "number"}}}, {"b", Json{{"type", "number"}}}}},
58+
{"required", Json::array({"a", "b"})}},
59+
[](const Json& args)
60+
{ return args.at("a").get<double>() + args.at("b").get<double>(); });
61+
62+
auto handler = fastmcpp::mcp::make_mcp_handler(app);
63+
64+
SseServerWrapper server(handler, "127.0.0.1", port);
65+
server.start();
66+
67+
std::cout << "[READY] Instructions HTTP server listening on port " << port << std::endl;
68+
69+
while (g_running)
70+
std::this_thread::sleep_for(std::chrono::milliseconds(100));
71+
72+
server.stop();
73+
return 0;
74+
}

examples/server_metadata.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,9 @@ int main()
4747

4848
std::cout << "2. Creating server with full metadata...\n";
4949

50-
// Server constructor signature (v2.13.0+):
51-
// Server(name, version, website_url, icons, strict_input_validation)
50+
// Server constructor signatures:
51+
// Preferred: Server(name, version, website_url, icons, instructions, strict_input_validation)
52+
// Legacy-compatible: Server(name, version, website_url, icons, strict_input_validation)
5253
auto server = std::make_shared<server::Server>("example_server", // name (required)
5354
"1.2.3", // version (required)
5455
"https://example.com", // website_url (optional)
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/// @file stdio_instructions_server.cpp
2+
/// @brief Minimal stdio MCP server with instructions field set.
3+
/// Used as E2E test target for instructions wire-format validation.
4+
5+
#include "fastmcpp/app.hpp"
6+
#include "fastmcpp/mcp/handler.hpp"
7+
#include "fastmcpp/server/stdio_server.hpp"
8+
9+
int main()
10+
{
11+
using Json = fastmcpp::Json;
12+
13+
fastmcpp::FastMCP app("instructions_e2e_server", "1.0.0",
14+
/*website_url=*/std::nullopt,
15+
/*icons=*/std::nullopt,
16+
/*instructions=*/std::string("This server provides echo and math tools. "
17+
"Use 'echo' to repeat input and 'add' to sum two numbers."));
18+
19+
app.tool(
20+
"echo",
21+
Json{{"type", "object"},
22+
{"properties", Json{{"message", Json{{"type", "string"}}}}},
23+
{"required", Json::array({"message"})}},
24+
[](const Json& args) { return args.at("message"); });
25+
26+
app.tool(
27+
"add",
28+
Json{{"type", "object"},
29+
{"properties", Json{{"a", Json{{"type", "number"}}}, {"b", Json{{"type", "number"}}}}},
30+
{"required", Json::array({"a", "b"})}},
31+
[](const Json& args)
32+
{ return args.at("a").get<double>() + args.at("b").get<double>(); });
33+
34+
auto handler = fastmcpp::mcp::make_mcp_handler(app);
35+
fastmcpp::server::StdioServerWrapper server(handler);
36+
server.run();
37+
return 0;
38+
}

include/fastmcpp/app.hpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "fastmcpp/tools/manager.hpp"
99

1010
#include <chrono>
11+
#include <initializer_list>
1112
#include <memory>
1213
#include <optional>
1314
#include <string>
@@ -114,8 +115,19 @@ class FastMCP
114115
explicit FastMCP(std::string name = "fastmcpp_app", std::string version = "1.0.0",
115116
std::optional<std::string> website_url = std::nullopt,
116117
std::optional<std::vector<Icon>> icons = std::nullopt,
118+
std::optional<std::string> instructions = std::nullopt,
117119
std::vector<std::shared_ptr<providers::Provider>> providers = {},
118120
int list_page_size = 0, bool dereference_schemas = true);
121+
/// Backward-compatible constructor overload (legacy parameter order).
122+
FastMCP(std::string name, std::string version, std::optional<std::string> website_url,
123+
std::optional<std::vector<Icon>> icons,
124+
std::vector<std::shared_ptr<providers::Provider>> providers,
125+
int list_page_size = 0, bool dereference_schemas = true);
126+
/// Backward-compatible constructor overload for `{}` provider arguments.
127+
FastMCP(std::string name, std::string version, std::optional<std::string> website_url,
128+
std::optional<std::vector<Icon>> icons,
129+
std::initializer_list<std::shared_ptr<providers::Provider>> providers,
130+
int list_page_size = 0, bool dereference_schemas = true);
119131

120132
// Metadata accessors
121133
const std::string& name() const
@@ -134,6 +146,14 @@ class FastMCP
134146
{
135147
return server_.icons();
136148
}
149+
const std::optional<std::string>& instructions() const
150+
{
151+
return server_.instructions();
152+
}
153+
void set_instructions(std::optional<std::string> val)
154+
{
155+
server_.set_instructions(std::move(val));
156+
}
137157
int list_page_size() const
138158
{
139159
return list_page_size_;

include/fastmcpp/mcp/handler.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ using SessionAccessor = std::function<std::shared_ptr<server::ServerSession>(con
3838
std::function<fastmcpp::Json(const fastmcpp::Json&)> make_mcp_handler(
3939
const std::string& server_name, const std::string& version, const tools::ToolManager& tools,
4040
const std::unordered_map<std::string, std::string>& descriptions = {},
41-
const std::unordered_map<std::string, fastmcpp::Json>& input_schemas_override = {});
41+
const std::unordered_map<std::string, fastmcpp::Json>& input_schemas_override = {},
42+
const std::optional<std::string>& instructions = std::nullopt);
4243

4344
// Overload: build a handler from a generic Server plus explicit tool metadata.
4445
// tools_meta: vector of (tool_name, description, inputSchema)

include/fastmcpp/proxy.hpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ class ProxyApp
4444

4545
/// Construct proxy with client factory
4646
explicit ProxyApp(ClientFactory client_factory, std::string name = "proxy_app",
47-
std::string version = "1.0.0");
47+
std::string version = "1.0.0",
48+
std::optional<std::string> instructions = std::nullopt);
4849

4950
// Metadata accessors
5051
const std::string& name() const
@@ -55,6 +56,14 @@ class ProxyApp
5556
{
5657
return version_;
5758
}
59+
const std::optional<std::string>& instructions() const
60+
{
61+
return instructions_;
62+
}
63+
void set_instructions(std::optional<std::string> val)
64+
{
65+
instructions_ = std::move(val);
66+
}
5867

5968
// Local manager accessors (for adding local-only items)
6069
tools::ToolManager& local_tools()
@@ -132,6 +141,7 @@ class ProxyApp
132141
ClientFactory client_factory_;
133142
std::string name_;
134143
std::string version_;
144+
std::optional<std::string> instructions_;
135145
tools::ToolManager local_tools_;
136146
resources::ResourceManager local_resources_;
137147
prompts::PromptManager local_prompts_;

include/fastmcpp/server/server.hpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ namespace fastmcpp::server
1818
/// - version: Server version (optional)
1919
/// - website_url: Optional URL to server website
2020
/// - icons: Optional list of icons for UI display
21+
/// - instructions: Optional instructions shown during initialize
2122
/// - strict_input_validation: Flag for input validation behavior (optional)
2223
class Server
2324
{
@@ -28,12 +29,21 @@ class Server
2829
explicit Server(std::string name = "fastmcpp_server", std::string version = "1.0.0",
2930
std::optional<std::string> website_url = std::nullopt,
3031
std::optional<std::vector<fastmcpp::Icon>> icons = std::nullopt,
32+
std::optional<std::string> instructions = std::nullopt,
3133
std::optional<bool> strict_input_validation = std::nullopt)
3234
: name_(std::move(name)), version_(std::move(version)),
3335
website_url_(std::move(website_url)), icons_(std::move(icons)),
36+
instructions_(std::move(instructions)),
3437
strict_input_validation_(std::move(strict_input_validation))
3538
{
3639
}
40+
/// Backward-compatible constructor overload (legacy parameter order).
41+
Server(std::string name, std::string version, std::optional<std::string> website_url,
42+
std::optional<std::vector<fastmcpp::Icon>> icons, bool strict_input_validation)
43+
: Server(std::move(name), std::move(version), std::move(website_url), std::move(icons),
44+
std::nullopt, strict_input_validation)
45+
{
46+
}
3747

3848
// Route registration
3949
void route(const std::string& name, Handler h)
@@ -69,6 +79,14 @@ class Server
6979
{
7080
return icons_;
7181
}
82+
const std::optional<std::string>& instructions() const
83+
{
84+
return instructions_;
85+
}
86+
void set_instructions(std::optional<std::string> val)
87+
{
88+
instructions_ = std::move(val);
89+
}
7290
const std::optional<bool>& strict_input_validation() const
7391
{
7492
return strict_input_validation_;
@@ -80,6 +98,7 @@ class Server
8098
std::string version_;
8199
std::optional<std::string> website_url_;
82100
std::optional<std::vector<fastmcpp::Icon>> icons_;
101+
std::optional<std::string> instructions_;
83102
std::optional<bool> strict_input_validation_;
84103

85104
// Routing and middleware

src/app.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@ namespace fastmcpp
1717

1818
FastMCP::FastMCP(std::string name, std::string version, std::optional<std::string> website_url,
1919
std::optional<std::vector<Icon>> icons,
20+
std::optional<std::string> instructions,
2021
std::vector<std::shared_ptr<providers::Provider>> providers, int list_page_size,
2122
bool dereference_schemas)
22-
: server_(std::move(name), std::move(version), std::move(website_url), std::move(icons)),
23+
: server_(std::move(name), std::move(version), std::move(website_url), std::move(icons),
24+
std::move(instructions)),
2325
providers_(std::move(providers)), list_page_size_(list_page_size),
2426
dereference_schemas_(dereference_schemas)
2527
{
@@ -30,6 +32,25 @@ FastMCP::FastMCP(std::string name, std::string version, std::optional<std::strin
3032
throw ValidationError("provider cannot be null");
3133
}
3234

35+
FastMCP::FastMCP(std::string name, std::string version, std::optional<std::string> website_url,
36+
std::optional<std::vector<Icon>> icons,
37+
std::vector<std::shared_ptr<providers::Provider>> providers, int list_page_size,
38+
bool dereference_schemas)
39+
: FastMCP(std::move(name), std::move(version), std::move(website_url), std::move(icons),
40+
std::nullopt, std::move(providers), list_page_size, dereference_schemas)
41+
{
42+
}
43+
44+
FastMCP::FastMCP(std::string name, std::string version, std::optional<std::string> website_url,
45+
std::optional<std::vector<Icon>> icons,
46+
std::initializer_list<std::shared_ptr<providers::Provider>> providers,
47+
int list_page_size, bool dereference_schemas)
48+
: FastMCP(std::move(name), std::move(version), std::move(website_url), std::move(icons),
49+
std::nullopt, std::vector<std::shared_ptr<providers::Provider>>(providers),
50+
list_page_size, dereference_schemas)
51+
{
52+
}
53+
3354
namespace
3455
{
3556
fastmcpp::Json schema_from_schema_or_simple(const fastmcpp::Json& schema_or_simple)

0 commit comments

Comments
 (0)