Server Context
The server_context is a user-defined hash that is passed into the server instance and made available to tool and prompt calls. It can be used to provide contextual information such as authentication state, user IDs, or request-specific data.
Type:
server_context: { [String, Symbol] => Any }
Example:
server = MCP::Server.new(
name: "my_server",
server_context: { user_id: current_user.id, request_id: request.uuid }
)
This hash is then passed as the server_context keyword argument to tool and prompt calls. Note that the exception reporter does not receive this user-defined hash, and instrumentation callbacks omit it unless you opt in with instrument_server_context. See the Configuration page for the arguments they receive.
Request-specific _meta Parameter
The MCP protocol supports a special _meta parameter in requests that allows clients to pass request-specific metadata. The server automatically extracts this parameter and makes it available to tools and prompts as a nested field within the server_context.
_metais only merged whenserver_contextis aHash(ornil, in which case a new{ _meta: ... }hash is synthesized). If you assign a non-Hashvalue toserver_context,_metais not merged and tools will not see it underserver_context[:_meta]. Keepserver_contextas aHashif your tools need access to_meta.
Access Pattern:
When a client includes _meta in the request params, it becomes available as server_context[:_meta]:
class MyTool < MCP::Tool
def self.call(message:, server_context:)
# Access provider-specific metadata
session_id = server_context.dig(:_meta, :session_id)
request_id = server_context.dig(:_meta, :request_id)
# Access server's original context
user_id = server_context.dig(:user_id)
MCP::Tool::Response.new([{
type: "text",
text: "Processing for user #{user_id} in session #{session_id}"
}])
end
end
Client Request Example:
A tools/call request carrying _meta, as read by the tool above:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "my_tool",
"arguments": { "message": "Hello" },
"_meta": {
"session_id": "abc123",
"request_id": "req_456"
}
}
}
Distributed Tracing
Per SEP-414, the keys traceparent, tracestate, and baggage are reserved un-prefixed _meta keys for propagating W3C Trace Context across MCP requests. The SDK guarantees these keys pass through incoming request _meta untouched, and exposes their names as constants on MCP::TraceContext (TRACEPARENT_META_KEY, TRACESTATE_META_KEY, BAGGAGE_META_KEY, and META_KEYS). The SDK does not depend on OpenTelemetry; bridge the values to your tracing system yourself:
class TracedTool < MCP::Tool
def self.call(message:, server_context:)
traceparent = server_context.dig(:_meta, :traceparent)
# Hand traceparent/tracestate/baggage to your tracing library
# (e.g. the opentelemetry-ruby gems) to continue the caller's trace.
MCP::Tool::Response.new([{ type: "text", text: "ok" }])
end
end
On the client side, every request method (call_tool, read_resource, get_prompt, complete, ping, and the list_* methods) accepts a meta: keyword to inject these keys into the outgoing request, so trace context can flow on every request:
meta = { MCP::TraceContext::TRACEPARENT_META_KEY => "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01" }
client.call_tool(tool: tool, arguments: { message: "Hello" }, meta: meta)
client.read_resource(uri: "file:///report.txt", meta: meta)