Skip to content

Commit de5b9bd

Browse files
mcp stdio support concurrent
V2.12
1 parent 0aa67bb commit de5b9bd

4 files changed

Lines changed: 361 additions & 42 deletions

File tree

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,14 +161,14 @@ API notes
161161
- `task_scheduler::KeyedTaskScheduler`: fixed-worker scheduler implementation that admits the next runnable task from an internal queue, allows `FreeReadOnly` work to run concurrently, serializes `ObjectExclusive` by `object_id`, serializes `FactoryLane` by `object_type`, serializes `ToolExclusive` by `tool_name`, and treats `SessionBarrier` as a stop-the-world session-wide exclusive operation.
162162
- `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.
163163
- `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.
164-
- `mcp::McpStdioServerThreadSafe`: thin MCP adapter over `runtime::RuntimeFacadeThreadSafe`; it reads and writes `Content-Length` framed JSON-RPC messages and serves `initialize`, `ping`, `tools/list`, and `tools/call`.
164+
- `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.
165165
- `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.
166166

167167
MCP stdio demo
168168

169169
- Build `mcp_stdio_server_demo` when you want a real process that an MCP host can launch over stdio.
170-
- The demo server registers `sum`, `echo_text`, `create_counter`, `counter_add`, `counter_value`, and `destroy_counter`.
171-
- The process writes only framed MCP responses to stdout; fatal startup errors go to stderr.
170+
- The demo server registers `sum`, `slow_sum`, `echo_text`, `create_counter`, `counter_add`, `counter_value`, and `destroy_counter`.
171+
- The process writes only framed MCP responses to stdout; startup notes, `slow_sum` overlap logs, and fatal errors go to stderr.
172172
- `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.
173173
- The fluent builder also supports `.options(...)`, so object type selection and session object options can be expressed separately: `.stateful<T>("counter").options(opts)...`.
174174
- When `.stateful<T>("counter")` uses `.create(...)` without an explicit tool name, the builder defaults to `create_counter`.

examples/mcp_stdio/mcp_stdio_server_demo.cpp

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
1+
#include <atomic>
2+
#include <chrono>
13
#include <exception>
24
#include <iostream>
35
#include <memory>
46
#include <string>
7+
#include <thread>
58

69
#include <json_session_invoke/json_session_invoke.hpp>
710
#include <mcp/mcp_stdio_server.hpp>
811

912
namespace {
1013

14+
using namespace std::chrono_literals;
15+
1116
struct Counter {
1217
int value{0};
1318

@@ -27,13 +32,28 @@ struct Counter {
2732
}
2833
};
2934

30-
void registerDemoTools(json_session_invoke::JsonSessionInvokeAdapterThreadSafe& adapter)
35+
void registerDemoTools(
36+
json_session_invoke::JsonSessionInvokeAdapterThreadSafe& adapter,
37+
std::atomic<int>& active_slow_jobs)
3138
{
3239
adapter.registerFunction(
3340
"sum",
3441
json_invoke::readOnly([](int left, int right) { return left + right; }),
3542
json_invoke::FunctionMetadata{{"left", "right"}, "Add two integers without session state."});
3643

44+
adapter.registerFunction(
45+
"slow_sum",
46+
json_invoke::readOnly([&active_slow_jobs](int left, int right, int delay_ms) {
47+
const int active_now = active_slow_jobs.fetch_add(1) + 1;
48+
std::cerr << "slow_sum start delay_ms=" << delay_ms << " active_jobs=" << active_now << std::endl;
49+
std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms));
50+
const int remaining = active_slow_jobs.fetch_sub(1) - 1;
51+
std::cerr << "slow_sum finish delay_ms=" << delay_ms << " active_jobs=" << remaining << std::endl;
52+
return left + right;
53+
}),
54+
json_invoke::FunctionMetadata{{"left", "right", "delay_ms"},
55+
"Add two integers after delay_ms; overlapping slow_sum calls log concurrent activity to stderr."});
56+
3757
adapter.registerFunction(
3858
"echo_text",
3959
json_invoke::readOnly([](std::string text) { return text; }),
@@ -68,7 +88,8 @@ int main()
6888
try
6989
{
7090
json_session_invoke::JsonSessionInvokeAdapterThreadSafe adapter;
71-
registerDemoTools(adapter);
91+
std::atomic<int> active_slow_jobs{0};
92+
registerDemoTools(adapter, active_slow_jobs);
7293

7394
runtime::RuntimeFacadeThreadSafe runtime(adapter, 4);
7495
mcp::McpStdioServerThreadSafe server(
@@ -77,11 +98,12 @@ int main()
7798
"llm_invoke_cpp_demo",
7899
"0.1.0",
79100
"2025-03-26",
80-
"Demo MCP stdio server exposing sum, echo_text, and counter create/call/destroy tools.",
101+
"Demo MCP stdio server exposing sum, slow_sum, echo_text, and counter create/call/destroy tools. slow_sum logs concurrent overlap to stderr.",
81102
false,
82103
});
83104

84-
server.serve(std::cin, std::cout);
105+
std::cerr << "mcp_stdio_server_demo ready; send overlapping slow_sum calls to observe concurrent jobs on stderr." << std::endl;
106+
server.serveConcurrent(std::cin, std::cout);
85107
return 0;
86108
}
87109
catch (const std::exception& e)

0 commit comments

Comments
 (0)