MCP Server
Overview
The Model Context Protocol (MCP) is an open standard for connecting AI applications to external data sources and tools. It defines a JSON-RPC-based communication protocol between MCP clients (AI agents) and MCP servers (capability providers).
The Zephyr MCP Server library implements the server role of the MCP specification (version 2025-11-25). It allows networked Zephyr devices to expose tools that AI agents can discover and invoke over HTTP. The library builds on existing Zephyr subsystems: the HTTP server library for transport and the JSON library for serialization.
Note
This library is marked experimental. Only the tool service with text responses is implemented. Additional features, like more content types, session management, SSE streaming, authorization and other services are planned for future releases.
Architecture
- Application
Registers tools and implements their callbacks. Tool execution results are submitted back to the core via
mcp_server_submit_tool_message().- MCP Server Core
Implements the MCP protocol state machines. Manages client connections, a tool registry, execution tracking, and a configurable worker thread pool. A health monitor thread enforces timeouts and triggers cancellations.
- JSON Processing
Serializes outgoing responses and deserializes incoming JSON-RPC requests using the Zephyr JSON library.
- Transport Layer
Abstracts the network protocol. The included HTTP transport uses the Zephyr HTTP server library. A mock transport is available for unit testing.
HTTP Transport and Asynchronous Responses
Zephyr’s HTTP server runs in a single thread and processes one request at a time across all connections. A resource callback must return before the server can handle the next request from any client. Because tool execution may take longer than acceptable for blocking the entire server, the MCP HTTP transport implements a polling-to-SSE fallback:
On a POST request, the transport polls internally for a tool response up to
CONFIG_MCP_HTTP_TIMEOUT_MS(checked everyCONFIG_MCP_HTTP_POLL_INTERVAL_MS).If a response is ready within this window, it is returned directly as
application/json.If the timeout expires, the transport switches to SSE mode: it returns a
text/event-streamresponse containing only an event ID (no data). This signals the client that the response is pending and it should start polling with periodic GET requests (interval controlled byCONFIG_MCP_HTTP_SSE_RETRY_MS).The client then issues the periodic GET requests with a
Last-Event-Idheader. If a result is ready, the server sends the response and ends the SSE stream. If the result is not ready, the server sends a retry response again.
Note
This is not full SSE streaming. The mechanism only delivers deferred responses for requests that could not complete within the initial HTTP timeout. Server-initiated notifications and streaming tool output are not supported in this phase.
Configuration
Enable the library with CONFIG_MCP_SERVER. The transport is
selected through CONFIG_MCP_TRANSPORT_HTTP (default) or
CONFIG_MCP_TRANSPORT_MOCK (testing only).
Minimal prj.conf:
CONFIG_NETWORKING=y
CONFIG_NET_TCP=y
CONFIG_HTTP_SERVER=y
CONFIG_MCP_SERVER=y
CONFIG_MCP_TRANSPORT_HTTP=y
Key configuration groups:
- Scalability
CONFIG_MCP_MAX_CLIENTS,CONFIG_MCP_MAX_CLIENT_REQUESTS,CONFIG_MCP_MAX_TOOLS,CONFIG_MCP_REQUEST_WORKERS- Timeouts
CONFIG_MCP_TOOL_EXEC_TIMEOUT_MS,CONFIG_MCP_TOOL_IDLE_TIMEOUT_MS,CONFIG_MCP_TOOL_CANCEL_TIMEOUT_MS,CONFIG_MCP_CLIENT_TIMEOUT_MSCONFIG_MCP_HTTP_TIMEOUT_MS- Memory allocation
CONFIG_MCP_ALLOC_SLAB(default) provides deterministic, fragmentation-free allocation using pre-allocated slabs.CONFIG_MCP_ALLOC_HEAPusesk_malloc/k_freefor on-demand allocation at the cost of possible fragmentation.
Usage
Server Setup
#include <zephyr/net/mcp/mcp_server.h>
#include <zephyr/net/mcp/mcp_server_http.h>
static mcp_server_ctx_t server;
int main(void)
{
server = mcp_server_init();
mcp_server_http_init(server);
/* Register tools here */
mcp_server_start(server);
mcp_server_http_start(server);
return 0;
}
Tool Registration
static int my_tool_cb(enum mcp_tool_event_type event,
const char *arguments,
const char *execution_token)
{
if (event == MCP_TOOL_CANCEL_REQUEST) {
struct mcp_tool_message ack = {
.type = MCP_USR_TOOL_CANCEL_ACK,
};
mcp_server_submit_tool_message(server, &ack, execution_token);
/* Handle cancellation here */
}
struct mcp_tool_message resp = {
.type = MCP_USR_TOOL_RESPONSE,
.data = "Tool execution result",
.length = strlen("Tool execution result"),
.is_error = false,
};
return mcp_server_submit_tool_message(server, &resp, execution_token);
}
static const struct mcp_tool_record my_tool = {
.metadata = {
.name = "my_tool",
.input_schema = "{\"type\":\"object\",\"properties\":{}}",
},
.callback = my_tool_cb,
};
mcp_server_add_tool(server, &my_tool);
The .data field accepts a plain text string. The server wraps it into an
MCP-compliant "text" content item automatically. Maximum length is
CONFIG_MCP_TOOL_RESULT_MAX_LEN.
Tool Callback Patterns
- Blocking
Short-running tools execute directly in the worker thread and call
mcp_server_submit_tool_message()before returning. The worker stack size isCONFIG_MCP_REQUEST_WORKER_STACK_SIZE.- Asynchronous
Long-running tools should spawn a dedicated thread, return immediately from the callback, and submit the response later using the provided execution token. Periodic pings (
MCP_USR_TOOL_PING) prevent the health monitor from cancelling idle executions.- Cancellation
When the health monitor or a client requests cancellation, the callback is invoked with
MCP_TOOL_CANCEL_REQUEST. The tool should stop work and submitMCP_USR_TOOL_CANCEL_ACK.
Tool Removal
Tools can be removed at runtime with mcp_server_remove_tool(). The
call returns -EBUSY if the tool is currently executing; retry later.
Limitations
The following MCP features are not yet implemented:
Resources, prompts, sampling, roots, and session management
Server-initiated notifications and streaming tool output
Image and embedded-resource content types (only
"text"is supported)Full SSE transport (only deferred response delivery is supported)
Testing
Unit tests are available under tests/net/lib/mcp/. They use
the mock transport (CONFIG_MCP_TRANSPORT_MOCK) to exercise
protocol logic without a network stack.
Sample
See MCP Server Hello World for a working example that registers multiple tools including GPIO-based LED control.