Skip to content

Commit f6fb8c5

Browse files
Add remote mcp server and demo
V2.13
1 parent de5b9bd commit f6fb8c5

7 files changed

Lines changed: 1226 additions & 1 deletion

File tree

CMakeLists.txt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ if(BUILD_TESTING)
8080
tests/test_main.cpp
8181
tests/func_registry_tests.cpp
8282
tests/json_invoke_tests.cpp
83+
tests/mcp_http_gateway_tests.cpp
8384
tests/json_stateful_tests.cpp
8485
tests/mcp_stdio_tests.cpp
8586
tests/runtime_tests.cpp
@@ -99,12 +100,50 @@ if(BUILD_TESTING)
99100
endif()
100101

101102
if(LLM_INVOKE_CPP_BUILD_EXAMPLES)
103+
set(CPP_HTTPLIB_VERSION v0.18.6)
104+
set(CPP_HTTPLIB_DIR ${CMAKE_CURRENT_BINARY_DIR}/_deps/cpp_httplib)
105+
set(CPP_HTTPLIB_HEADER ${CPP_HTTPLIB_DIR}/httplib.h)
106+
107+
file(MAKE_DIRECTORY ${CPP_HTTPLIB_DIR})
108+
set(CPP_HTTPLIB_NEEDS_DOWNLOAD ON)
109+
if(EXISTS ${CPP_HTTPLIB_HEADER})
110+
file(SIZE ${CPP_HTTPLIB_HEADER} CPP_HTTPLIB_HEADER_SIZE)
111+
if(CPP_HTTPLIB_HEADER_SIZE GREATER 0)
112+
set(CPP_HTTPLIB_NEEDS_DOWNLOAD OFF)
113+
endif()
114+
endif()
115+
116+
if(CPP_HTTPLIB_NEEDS_DOWNLOAD)
117+
file(DOWNLOAD
118+
https://raw.githubusercontent.com/yhirose/cpp-httplib/${CPP_HTTPLIB_VERSION}/httplib.h
119+
${CPP_HTTPLIB_HEADER}
120+
STATUS cpp_httplib_download_status
121+
TLS_VERIFY ON
122+
)
123+
124+
list(GET cpp_httplib_download_status 0 cpp_httplib_download_code)
125+
list(GET cpp_httplib_download_status 1 cpp_httplib_download_message)
126+
if(NOT cpp_httplib_download_code EQUAL 0)
127+
message(FATAL_ERROR "Failed to download cpp-httplib header: ${cpp_httplib_download_message}")
128+
endif()
129+
endif()
130+
102131
add_executable(func_registry_demo examples/func_registry/func_registry_demo.cpp)
103132
target_link_libraries(func_registry_demo PRIVATE llm_invoke_cpp::func_registry)
104133

105134
add_executable(mcp_stdio_server_demo examples/mcp_stdio/mcp_stdio_server_demo.cpp)
106135
target_link_libraries(mcp_stdio_server_demo PRIVATE llm_invoke_cpp::mcp)
107136

137+
add_executable(mcp_http_gateway_demo examples/mcp_http/mcp_http_gateway_demo.cpp)
138+
target_include_directories(mcp_http_gateway_demo PRIVATE ${CPP_HTTPLIB_DIR})
139+
target_link_libraries(mcp_http_gateway_demo PRIVATE llm_invoke_cpp::mcp)
140+
if(MINGW)
141+
target_compile_options(mcp_http_gateway_demo PRIVATE -Wa,-mbig-obj)
142+
endif()
143+
if(WIN32)
144+
target_link_libraries(mcp_http_gateway_demo PRIVATE ws2_32)
145+
endif()
146+
108147
add_executable(json_invoke_demo examples/json_invoke/json_invoke_demo.cpp)
109148
target_link_libraries(json_invoke_demo PRIVATE llm_invoke_cpp::json_invoke)
110149

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ It is split into eight public modules:
1111
- `include/json_session_invoke`: higher-level session/runtime APIs built on top of `json_invoke` for stateful create/call/destroy flows.
1212
- `include/task_scheduler`: task classification and scheduling helpers built on top of `json_session_invoke`.
1313
- `include/runtime`: protocol-neutral runtime facade built on top of `task_scheduler` for normalized tool listing and unary invocation.
14-
- `include/mcp`: MCP-specific adapters built on top of `runtime` for protocol-facing transports such as stdio.
14+
- `include/mcp`: MCP-specific adapters built on top of `runtime` for protocol-facing transports such as stdio and a framework-agnostic HTTP gateway core.
1515

1616
Project layout
1717

@@ -35,7 +35,9 @@ Project layout
3535
- `include/task_scheduler/task_scheduler.hpp`: scheduler-side task abstractions, including a minimal `ITaskScheduler` interface and a keyed scheduler that enforces session-wide and per-object exclusivity.
3636
- `include/runtime/runtime_facade.hpp`: protocol-neutral facade that projects exported tool schemas into normalized descriptors and turns scheduler-backed JSON requests into normalized invoke results.
3737
- `include/mcp/mcp_stdio_server.hpp`: minimal MCP stdio server that handles JSON-RPC framing plus `initialize`, `ping`, `tools/list`, and `tools/call` on top of the runtime facade.
38+
- `include/mcp/mcp_http_gateway.hpp`: framework-agnostic HTTP request/session gateway core for one MCP endpoint backed by session objects.
3839
- `examples/func_registry/func_registry_demo.cpp`: core-only registry example.
40+
- `examples/mcp_http/mcp_http_gateway_demo.cpp`: cpp-httplib demo that mounts one `/mcp` endpoint and routes HTTP requests through the framework-agnostic gateway core.
3941
- `examples/mcp_stdio/mcp_stdio_server_demo.cpp`: standalone MCP stdio server process that exposes demo tools and can be launched directly by an MCP host.
4042
- `examples/json_invoke/json_invoke_demo.cpp`: JSON invocation example.
4143
- `examples/json_stateful/json_stateful_demo.cpp`: stateful object-handle example for create/call/destroy flows.
@@ -162,6 +164,7 @@ API notes
162164
- `runtime::RuntimeFacadeThreadSafe`: protocol-neutral entry point above `task_scheduler`; `listTools()` returns normalized tool descriptors, `submitInvoke(...)` schedules one unary call and returns `std::future<runtime::InvokeResult>`, and `invoke(...)` gives the same normalized result synchronously.
163165
- `runtime::InvokeResult`: normalized invoke envelope with `ok`, `value`, optional `error`, and the original `raw_response`; classification-time failures such as unknown tools are converted into the same error shape instead of leaking exceptions to protocol adapters.
164166
- `mcp::McpStdioServerThreadSafe`: thin MCP adapter over `runtime::RuntimeFacadeThreadSafe`; it reads and writes `Content-Length` framed JSON-RPC messages and `serveConcurrent(...)` keeps multiple `tools/call` requests in flight on one stdio session while `initialize`, `ping`, and `tools/list` remain ordered.
167+
- `mcp::HttpMcpGateway`: framework-agnostic POST `/mcp` core that validates basic HTTP semantics, allocates MCP sessions on `initialize`, requires `Mcp-Session-Id` afterwards, and leaves server startup/routing to whichever HTTP framework hosts it.
165168
- `mcp::McpStdioServerThreadSafe::handleMessage(...)`: useful when you want protocol handling without a real stdio loop, for example in tests or when embedding the MCP adapter into another transport shim.
166169

167170
MCP stdio demo
@@ -170,6 +173,13 @@ MCP stdio demo
170173
- The demo server registers `sum`, `slow_sum`, `echo_text`, `create_counter`, `counter_add`, `counter_value`, and `destroy_counter`.
171174
- The process writes only framed MCP responses to stdout; startup notes, `slow_sum` overlap logs, and fatal errors go to stderr.
172175
- `examples/mcp_stdio/test_mcp.ps1` now runs a fuller smoke test against the demo executable: initialize, tools/list, one stateless `sum` call, then a stateful `create_counter -> counter_add -> counter_value -> destroy_counter` flow.
176+
177+
MCP HTTP demo
178+
179+
- Build `mcp_http_gateway_demo` when you want a single `/mcp` HTTP endpoint hosted by cpp-httplib while keeping MCP/HTTP semantics inside `mcp::HttpMcpGateway`.
180+
- The demo keeps one stdio child-process MCP session per `Mcp-Session-Id`; initialize launches `mcp_stdio_server_demo`, later requests reuse that backend process, and clients must send the returned session header.
181+
- Pass `--backend-executable <path>` when the stdio backend is not discoverable next to the HTTP gateway binary or under `build/`.
182+
- On Windows, `mcp_stdio_server_demo` switches stdin/stdout to binary mode so MCP `Content-Length` framing survives stdio transport without CRLF expansion.
173183
- The fluent builder also supports `.options(...)`, so object type selection and session object options can be expressed separately: `.stateful<T>("counter").options(opts)...`.
174184
- When `.stateful<T>("counter")` uses `.create(...)` without an explicit tool name, the builder defaults to `create_counter`.
175185
- If a stateful builder creates an object but omits `.destroy()`, the adapter auto-registers the default `destroy_<object_type>` tool unless `setStatefulDefaults(...)` disables `auto_register_destroy`.

0 commit comments

Comments
 (0)