mcp #
mcp
Native [Model Context Protocol][spec] implementation for V — both client and server, covering two revisions of the spec:
- 2025-11-25 — the stateful revision, and still the default: the
initializehandshake plusMCP-Session-Idsessions. - 2026-07-28 — the sessionless revision: no handshake, no session id, and every request declares its own revision, client info and capabilities.
Both are supported at once. A server speaks 2025-11-25 by default and switches per request based on the version the client declares; a client picks the mode explicitly. The wire shapes never mix: a request answered under 2025-11-25 carries no 2026-only fields, and vice versa.
[spec-2025]: https://modelcontextprotocol.io/specification/2025-11-25 [spec-2026]: https://modelcontextprotocol.io/specification/2026-07-28 [spec]: https://modelcontextprotocol.io/specification/2026-07-28
Capabilities
| Feature | Status |
|---|---|
| JSON-RPC 2.0 base protocol | ✅ |
| stdio transport (newline-delimited) | ✅ |
| Streamable HTTP transport (POST + GET, SSE, sessions) | ✅ |
Origin header validation (DNS rebinding protection) |
✅ |
MCP-Session-Id and MCP-Protocol-Version headers |
✅ 2025-11-25 |
Last-Event-ID resumption |
✅ 2025-11-25 |
Tools (with annotations) |
✅ |
| Resources, resource templates | ✅ |
resources/subscribe / unsubscribe / resources/updated |
✅ 2025-11-25 |
| Prompts | ✅ |
completion/complete |
✅ |
logging/setLevel + notifications/message |
✅ 2025-11-25 |
notifications/progress + cooperative cancellation |
✅ |
*/list_changed notifications (auto on add_*) |
✅ |
Server-initiated roots/list, sampling/createMessage, elicitation/create |
✅ 2025-11-25 |
Icon, BaseMetadata (title), Annotations |
✅ |
Tool.execution.taskSupport advertisement |
✅ |
Content helpers (text, image, audio, embedded, link) |
✅ |
server/discover |
✅ 2026-07-28 |
Stateless operation (no handshake, per-request _meta) |
✅ 2026-07-28 |
subscriptions/listen |
✅ 2026-07-28 |
Multi Round-Trip Requests (MRTR, resultType: input_required) |
✅ 2026-07-28 |
resultType on every result |
✅ 2026-07-28 |
CacheableResult ttlMs / cacheScope |
✅ 2026-07-28 |
Mcp-Method / Mcp-Name request headers |
✅ 2026-07-28 |
Tasks utility (tasks/*) |
⏳ deferred (experimental) |
| OAuth Authorization | ⏳ deferred (SHOULD) |
A comprehensive demo server lives at examples/mcp/server.v.
Quick start — client
import mcp
fn main() {
mut client := mcp.connect('http://localhost:8000/mcp')!
init := client.initialize()!
println(init.server_info.name)
client.close()
}
Quick start — server
import mcp
fn main() {
mut server := mcp.new_server(
name: 'my-v-mcp-server'
version: '1.0.0'
enable_logging: true
)
server.add_tool(mcp.Tool{
name: 'say_hello'
description: 'Greets the caller'
annotations: mcp.ToolAnnotations{
read_only_hint: true
}
}, fn (_ mcp.Context, _ string) !mcp.ToolResult {
return mcp.tool_text_result('Hello, user!')
})!
server.serve_stdio()!
}
Cancellation and progress
Tool/resource/prompt handlers receive a Context. When the client supplies a _meta.progressToken, the handler can call ctx.notify_progress(progress, total, message). For long-running work, poll ctx.is_cancelled() regularly — when the client sends notifications/cancelled, the flag flips to true until the request completes.
Server-initiated requests
import mcp
import time
mut server := mcp.new_server(name: 'demo', version: '0')
session_id := 'session'
roots := server.list_roots(session_id, 5 * time.second)!
sampled := server.sample(session_id, mcp.CreateMessageParams{}, 30 * time.second)!
elicited := server.elicit(session_id, mcp.ElicitParams{}, 60 * time.second)!
These block until the client returns the matching JSON-RPC response (or until the timeout fires). They are a 2025-11-25 facility; under 2026-07-28 a server cannot originate requests at all and uses MRTR instead — see below.
Content blocks
Tool, prompt and resource handlers return arrays of MCP content blocks. The module ships ready-made helpers — pass the result through tool_text_result or compose them by hand:
import mcp
text := mcp.text_content('done')
img := mcp.image_content('AAA=', 'image/png')
audio := mcp.audio_content('BBB=', 'audio/wav')
embedded_text := mcp.embedded_text_resource('res://config', 'application/json', '{}')
embedded_blob := mcp.embedded_blob_resource('res://blob', 'image/png', 'AAA=')
resource_link := mcp.resource_link_content(mcp.Resource{
uri: 'res://docs'
name: 'docs'
})
Each helper returns a JSON string conforming to the spec's ContentBlock union (type: "text" | "image" | "audio" | "resource" | "resource_link").
2026-07-28 stateless mode
Under 2026-07-28 there is no session and no handshake. Every request carries its own protocol state in params._meta. The first two are required by the schema — a request missing either is rejected as a malformed request (-32600):
io.modelcontextprotocol/protocolVersion— the revision, requiredio.modelcontextprotocol/clientCapabilities— what the client can do, requiredio.modelcontextprotocol/clientInfo— who is callingio.modelcontextprotocol/logLevel— opts the request into log notifications
Every 2026-07-28 result also identifies its server in _meta["io.modelcontextprotocol/serverInfo"]. With the handshake gone, that _meta is the only place a client can learn who answered — which is why server/discover needs no separate identity field.
Server
ServerConfig.supported_versions lists the revisions a server speaks (default: both). The preferred protocol_version must be in that list. A request that names a supported revision in _meta is dispatched sessionlessly, and the revision it named decides its wire shape — so one server can serve both kinds of client at once.
import mcp
mut server := mcp.new_server(
name: 'dual-stack'
version: '1.0.0'
supported_versions: ['2025-11-25', '2026-07-28']
cache_ttl_ms: 300_000
cache_scope: 'private'
)
server/discover tells a client what the server speaks, and is callable before anything else:
{"supportedVersions":["2025-11-25","2026-07-28"],"capabilities":{...}}
Over HTTP, a 2026-07-28 POST must also carry MCP-Protocol-Version (equal to the _meta value) plus Mcp-Method, and Mcp-Name for the methods that address a named tool, resource or prompt. A mismatch is a HeaderMismatchError (-32020); an unknown revision is an UnsupportedProtocolVersionError (-32022) whose data names the versions the server does speak. No MCP-Session-Id is ever issued or expected.
Client
mcp.connect_2026 builds a client that skips the handshake entirely. Every request it sends carries the _meta block and, over HTTP, the matching headers:
import mcp
mut client := mcp.connect_2026('http://localhost:8000/mcp', mcp.ClientConfig{
elicitation_handler: fn (_ string) string {
return '{"action":"accept","content":{"ok":true}}'
}
})!
tools := client.request[mcp.ListToolsResult, mcp.ListToolsResult]('tools/list', mcp.empty)
client.close()
If the server rejects the revision with -32022, the client reads data.supported, switches to the first revision listed and retries once — the 2025-11-25 handshake runs normally from there.
Listening for notifications
client.listen opens a subscription and returns the subset the server agreed to honor; the notifications themselves arrive in client.take_notifications(), each tagged with _meta["io.modelcontextprotocol/subscriptionId"]. Subscription metadata preserves the originating request ID's JSON type and value: numeric 7 and string "7" identify different streams. Acknowledgment matching keeps that distinction. subscription_id_of returns decoded string IDs and numeric IDs as text for display. The call returns when its matching acknowledgment arrives, without waiting for the live stdio subscription to close. Notifications read while awaiting later responses remain available through take_notifications. HTTP listen requests use Accept: text/event-stream to agree with the server's subscription transport.
import mcp
filter := client.listen(mcp.SubscriptionListenParams{
notifications: mcp.SubscriptionFilter{
tools_list_changed: true
resource_uris: ['res://config']
}
})!
if filter.tools_list_changed {
for notification in client.take_notifications() {
if mcp.subscription_id_of(notification) or { '' } == '7' {
println(notification.method)
}
}
}
Transport caveat. net.http is strictly request→response, so a subscriptions/listen over HTTP is a finite SSE stream: it carries the acknowledged notification followed by a SubscriptionsListenResult, and the subscription does not outlive the response — clients re-listen when they need live updates. On stdio the subscription is genuinely long-lived: it shares the single stdout channel, notifications are pushed as they happen, and the server terminates the stream with notifications/cancelled when the transport closes.
Multi Round-Trip Requests (MRTR)
2026-07-28 removes server-initiated requests. Instead of calling the client mid-flight, a handler returns an intermediate result asking for input, and the client retries the original request with the answers attached. Handlers use a take-or-require pattern: try to read the answer, and if it is not there yet, ask for it.
import mcp
server.add_tool(mcp.Tool{ name: 'delete_project' }, fn (ctx mcp.Context, _ string) !mcp.ToolResult {
// First pass: no answer yet, so ask.
if answer := ctx.take_elicit_result('name') {
return mcp.tool_text_result('deleted ${answer.content}')
}
// `content` is the raw JSON the client returned.
return ctx.require_elicit('name', mcp.ElicitParams{
message: 'Which project should I delete?'
requested_schema: mcp.ElicitSchema{
properties: '{"name":{"type":"string"}}'
required: ['name']
}
})
})!
The first call answers with resultType: "input_required" and an inputRequests map naming what it needs. The client fills in params.inputResponses and resends, and the handler is invoked again from the top — so the take_* branch now wins and the tool finishes with resultType: "complete".
The typed readers are take_elicit_result, take_roots_result and take_sampling_result; the asking counterparts are require_elicit, require_roots and require_sampling. ctx.request_state carries the opaque requestState token the server sent, and a require_* helper echoes it back on the next round.
On the client, register roots_handler, sampling_handler and elicitation_handler in ClientConfig; the client answers the embedded requests and retries automatically (up to 8 rounds). The client reads the top-level resultType regardless of JSON member order or formatting; a result without that member is treated as complete.
Streamable HTTP details
2025-11-25 transport behaviour:
- POST: returns JSON by default. Returns SSE if the client sends
Accept: text/event-streamonly. - GET: opens an SSE stream of queued notifications. Resume with
Last-Event-ID. - DELETE: terminates the session (
MCP-Session-Idrequired). - 403 on disallowed
Origin, 400 on unsupportedMCP-Protocol-Version, 406 whenAcceptlists neitherapplication/jsonnortext/event-stream.
2026-07-28 stateless requests additionally require Mcp-Method (and Mcp-Name where it applies) on every POST, and never receive an MCP-Session-Id.
Tests
v test vlib/mcp
spec_compliance_test.v and spec_compliance_2026_test.v cross-check wire shapes against the [official 2025-11-25 schema][schema-2025] and [2026-07-28 schema][schema-2026]. Add a case there whenever a payload field changes.
[schema-2025]: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/2025-11-25/schema.json [schema-2026]: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/2026-07-28/schema.json
Constants #
const jsonrpc_version = '2.0'
const protocol_version = '2025-11-25'
protocol_version is the revision a server speaks by default. It stays on 2025-11-25 so clients that never negotiate keep the current wire behaviour.
const protocol_version_2025_11_25 = protocol_version
protocol_version_2025_11_25 and protocol_version_2026_07_28 name the revisions a server can speak; latest_protocol_version is the newest one. The 2025-11-25 constant aliases protocol_version so the default and the named revision can never drift apart.
const protocol_version_2026_07_28 = '2026-07-28'
const latest_protocol_version = protocol_version_2026_07_28
const parse_error = ResponseError{
code: -32700
message: 'Invalid JSON.'
}
const invalid_request = ResponseError{
code: -32600
message: 'Invalid request.'
}
const method_not_found = ResponseError{
code: -32601
message: 'Method not found.'
}
const invalid_params = ResponseError{
code: -32602
message: 'Invalid params.'
}
const internal_error = ResponseError{
code: -32603
message: 'Internal error.'
}
const server_not_initialized = ResponseError{
code: -32002
message: 'Server not initialized.'
}
const resource_not_found = ResponseError{
code: -32002
message: 'Resource not found.'
}
const url_elicitation_required = ResponseError{
code: -32042
message: 'URL mode elicitation required.'
}
const header_mismatch = ResponseError{
code: -32020
message: 'Header mismatch.'
}
const missing_required_client_capability = ResponseError{
code: -32021
message: 'Missing required client capability.'
}
const unsupported_protocol_version = ResponseError{
code: -32022
message: 'Unsupported protocol version.'
}
const null = Null{}
const empty = Empty{}
const empty_object = EmptyObject{}
fn audio_content #
fn audio_content(data string, mime_type string) string
audio_content creates a raw MCP audio content block. data must be the base64-encoded payload and mime_type the IANA media type (e.g. audio/wav).
fn audio_content_with_annotations #
fn audio_content_with_annotations(data string, mime_type string, annotations Annotations) string
audio_content_with_annotations behaves like audio_content but attaches the provided annotations to the block.
fn connect #
fn connect(url string) !Client
connect creates an MCP client for a streamable HTTP endpoint.
fn connect_2026 #
fn connect_2026(url string, config ClientConfig) !Client
connect_2026 creates a sessionless 2026-07-28 client for a streamable HTTP endpoint. It never runs the initialize handshake.
fn connect_http #
fn connect_http(url string, config ClientConfig) !Client
connect_http creates an MCP client for a streamable HTTP endpoint.
fn connect_stdio #
fn connect_stdio(command string, args []string, config ClientConfig) !Client
connect_stdio creates an MCP client that talks to a local stdio server process.
fn decode_notification #
fn decode_notification(raw string) !Notification
decode_notification decodes a JSON payload into an MCP notification.
fn decode_request #
fn decode_request(raw string) !Request
decode_request decodes a JSON payload into an MCP request.
fn decode_response #
fn decode_response(raw string) !Response
decode_response decodes a JSON payload into an MCP response.
fn embedded_blob_resource #
fn embedded_blob_resource(uri string, mime_type string, blob string) string
embedded_blob_resource creates an embedded MCP binary resource content block. blob must be the base64-encoded payload.
fn embedded_blob_resource_with_annotations #
fn embedded_blob_resource_with_annotations(uri string, mime_type string, blob string, annotations Annotations) string
embedded_blob_resource_with_annotations behaves like embedded_blob_resource but attaches the provided annotations to the outer EmbeddedResource.
fn embedded_text_resource #
fn embedded_text_resource(uri string, mime_type string, text string) string
embedded_text_resource creates an embedded MCP text resource content block.
fn embedded_text_resource_with_annotations #
fn embedded_text_resource_with_annotations(uri string, mime_type string, text string, annotations Annotations) string
embedded_text_resource_with_annotations behaves like embedded_text_resource but attaches the provided annotations to the outer EmbeddedResource.
fn image_content #
fn image_content(data string, mime_type string) string
image_content creates a raw MCP image content block. data must be the base64-encoded payload and mime_type the IANA media type (e.g. image/png).
fn image_content_with_annotations #
fn image_content_with_annotations(data string, mime_type string, annotations Annotations) string
image_content_with_annotations behaves like image_content but attaches the provided annotations to the block.
fn new_client #
fn new_client(transport Transport, config ClientConfig) Client
new_client constructs an MCP client on top of a custom transport.
fn new_notification #
fn new_notification[P](method string, params P) Notification
new_notification constructs an MCP notification with a typed params payload.
fn new_request #
fn new_request[I, P](id I, method string, params P) Request
new_request constructs an MCP request with a typed id and params payload.
fn new_response #
fn new_response[I, R](id I, result R, err ResponseError) Response
new_response constructs an MCP response with a typed id and result payload.
fn new_server #
fn new_server(config ServerConfig) Server
new_server constructs a new MCP server.
fn parse_log_level #
fn parse_log_level(value string) ?LogLevel
parse_log_level decodes the wire string for a log level.
fn prompt_text_message #
fn prompt_text_message(role string, text string) PromptMessage
prompt_text_message creates a prompt message with text content.
fn resource_link_content #
fn resource_link_content(resource Resource) string
resource_link_content creates a raw MCP resource_link content block from a Resource description. The block carries the resource's metadata — including any annotations set on the resource — so the host can render or read it later.
fn subscription_id_of #
fn subscription_id_of(notification Notification) ?string
subscription_id_of returns the reserved subscriptionId _meta value of a notification that arrived on a listen stream. String IDs are decoded, and numeric IDs are returned as text for display.
fn text_content #
fn text_content(text string) string
text_content creates a raw MCP text content item.
fn text_content_with_annotations #
fn text_content_with_annotations(text string, annotations Annotations) string
text_content_with_annotations behaves like text_content but attaches the provided annotations (audience, priority, lastModified) to the block.
fn tool_text_result #
fn tool_text_result(text string) ToolResult
tool_text_result wraps plain text in an MCP tool result.
interface Transport #
interface Transport {
mut:
send(message string) !
receive() !string
close()
}
Transport is the boundary between MCP messages and the wire format.
type CompletionHandler #
type CompletionHandler = fn (ctx Context, current_value string, arguments_json string) !CompletionResult
CompletionHandler returns candidate values for a partial argument value. arguments_json is the JSON object of already-supplied sibling arguments (the spec's context.arguments).
type PromptHandler #
type PromptHandler = fn (ctx Context, arguments string) !GetPromptResult
PromptHandler handles prompts/get for a registered prompt.
type ResourceHandler #
type ResourceHandler = fn (ctx Context, uri string) !ReadResourceResult
ResourceHandler handles resources/read for a registered resource URI.
type ToolHandler #
type ToolHandler = fn (ctx Context, arguments string) !ToolResult
ToolHandler handles tools/call for a registered tool.
enum LogLevel #
enum LogLevel {
debug
info
notice
warning
error
critical
alert
emergency
}
LogLevel is the RFC 5424 severity used by notifications/message.
fn (LogLevel) str #
fn (l LogLevel) str() string
str returns the wire string for a log level (lowercase per spec).
enum SessionTransport #
enum SessionTransport {
stdio
http
}
SessionTransport identifies how an MCP session is connected.
struct Annotations #
struct Annotations {
pub:
audience []string @[omitempty]
priority ?f64
last_modified string @[json: lastModified; omitempty]
}
Annotations are the optional rendering hints attached to resources, resource templates and content blocks. audience is a list of MCP Role values (e.g. 'user', 'assistant'); priority is in the [0.0, 1.0] range with higher meaning more important; last_modified is an ISO 8601 timestamp.
struct Client #
struct Client {
mut:
transport Transport
config ClientConfig
next_id int = 1
initialized bool
init_result InitializeResult
pending_responses map[string]Response
notifications []Notification
server_requests []Request
}
fn (Client) is_stateless_2026 #
fn (c &Client) is_stateless_2026() bool
is_stateless_2026 reports whether this client speaks the sessionless 2026-07-28 protocol rather than the 2025-11-25 handshake.
fn (Client) initialize #
fn (mut c Client) initialize() !InitializeResult
initialize starts the MCP initialization handshake using the client's config.
fn (Client) initialize_with #
fn (mut c Client) initialize_with[X](capabilities X, client_info Implementation) !InitializeResult
initialize_with starts the MCP initialization handshake using typed capabilities.
fn (Client) send_request #
fn (mut c Client) send_request(request Request) !Response
send_request sends a typed request and waits for its response.
fn (Client) request_message #
fn (mut c Client) request_message[P](method string, params P) !Response
request_message sends a method call and returns the raw MCP response.
fn (Client) request #
fn (mut c Client) request[P, R](method string, params P) !R
request sends a method call and decodes its result into Result.
fn (Client) send_notification #
fn (mut c Client) send_notification(notification Notification) !
send_notification sends a typed notification message.
fn (Client) notify #
fn (mut c Client) notify[P](method string, params P) !
notify sends a method notification with a typed params payload.
fn (Client) take_notifications #
fn (mut c Client) take_notifications() []Notification
take_notifications drains notifications queued while waiting for responses.
fn (Client) take_requests #
fn (mut c Client) take_requests() []Request
take_requests drains server initiated requests queued while waiting for responses.
fn (Client) close #
fn (mut c Client) close()
close releases the underlying transport.
fn (Client) listen #
fn (mut c Client) listen[P](filter P) !SubscriptionFilter
listen subscribes to notifications and returns the subset the server acknowledged. A 2026-07-28 client only: the notifications themselves are queued for take_notifications, tagged with the subscription id. It returns after the matching acknowledgment without waiting for a live stdio subscription to end.
struct ClientConfig #
struct ClientConfig {
pub mut:
protocol_version string = protocol_version
client_info Implementation = Implementation{
name: default_client_name
version: default_client_version
}
capabilities string = '{}'
headers map[string]string
// stateless_2026 selects the sessionless 2026-07-28 client: no initialize
// handshake, every request carries its version in `_meta`, and the HTTP
// transport adds the `MCP-Protocol-Version`, `Mcp-Method` and `Mcp-Name`
// headers. It is also implied by asking for the 2026-07-28 revision.
stateless_2026 bool
// supported_versions is the fallback order used when a 2026-07-28 server
// rejects the requested revision with UnsupportedProtocolVersionError.
supported_versions []string = [protocol_version_2025_11_25, protocol_version_2026_07_28]
// log_level, when set, is sent as the reserved `_meta` logLevel key of
// every 2026-07-28 request, which is what opts that request into log
// notifications.
log_level string
// roots_handler answers a `roots/list` request embedded in a 2026-07-28
// MRTR result. It returns the raw JSON of a ListRootsResult.
roots_handler ?fn () string
// sampling_handler answers a `sampling/createMessage` request embedded in a
// 2026-07-28 MRTR result. It takes the raw params and returns the raw JSON
// of a CreateMessageResult.
sampling_handler ?fn (string) string
// elicitation_handler answers an `elicitation/create` request embedded in a
// 2026-07-28 MRTR result. It takes the raw params and returns the raw JSON
// of an ElicitResult.
elicitation_handler ?fn (string) string
}
struct CompletionRef #
struct CompletionRef {
pub:
ref_type string @[json: type] // 'ref/prompt' or 'ref/resource'
name string @[omitempty]
uri string @[omitempty]
}
CompletionRef identifies the prompt or resource template a completion targets. Prompt refs set name; resource refs set uri.
struct CompletionResult #
struct CompletionResult {
pub:
values []string
total ?int
has_more ?bool
}
CompletionResult is the payload returned by completion/complete.
struct Context #
struct Context {
pub:
session_id string @[json: sessionId]
request_id string @[json: requestId]
method string
transport SessionTransport
protocol_version string @[json: protocolVersion]
client_info Implementation @[json: clientInfo]
client_capabilities string @[json: clientCapabilities; raw]
progress_token string @[json: progressToken; raw]
// input_responses carries the `params.inputResponses` of a 2026-07-28 MRTR
// retry: the key a `require_*` helper asked for, mapped to the raw JSON
// result the client returned. Empty on the first attempt.
input_responses map[string]string
// request_state echoes the opaque `params.requestState` of the retry so a
// handler can hand it straight back to the client.
request_state string
mut:
server &Server = unsafe { nil } @[skip]
}
Context provides request-scoped server metadata to handlers.
fn (Context) take_input_response #
fn (ctx Context) take_input_response(key string) ?string
take_input_response returns the raw JSON result the client supplied for key, or none when this request has not been retried with that answer yet.
fn (Context) take_elicit_result #
fn (ctx Context) take_elicit_result(key string) ?ElicitResult
take_elicit_result returns the elicitation answer the client supplied for key, if this request already carries one.
fn (Context) take_roots_result #
fn (ctx Context) take_roots_result(key string) ?ListRootsResult
take_roots_result returns the roots answer the client supplied for key, if this request already carries one.
fn (Context) take_sampling_result #
fn (ctx Context) take_sampling_result(key string) ?CreateMessageResult
take_sampling_result returns the sampling answer the client supplied for key, if this request already carries one.
fn (Context) require_elicit #
fn (ctx Context) require_elicit(key string, params ElicitParams) InputRequired
require_elicit signals that this request cannot finish without an elicitation answer. The dispatcher turns it into an InputRequiredResult and re-invokes the handler when the client retries, so a handler pairs it with take_elicit_result for the take-or-require pattern.
fn (Context) require_roots #
fn (ctx Context) require_roots(key string) InputRequired
require_roots signals that this request cannot finish without the client's roots. Pair it with take_roots_result.
fn (Context) require_sampling #
fn (ctx Context) require_sampling(key string, params CreateMessageParams) InputRequired
require_sampling signals that this request cannot finish without a sampling answer. Pair it with take_sampling_result.
fn (Context) require_input #
fn (ctx Context) require_input(key string, method string, params_json string) InputRequired
require_input builds the InputRequired sentinel for one embedded request object, echoing any requestState the client already sent.
fn (Context) is_cancelled #
fn (ctx Context) is_cancelled() bool
is_cancelled reports whether the client has sent notifications/cancelled for this request. Handlers should poll this for cooperative cancellation.
fn (Context) notify_progress #
fn (ctx Context) notify_progress(progress f64, total f64, message string)
notify_progress sends notifications/progress for this request when the client included a _meta.progressToken. total and message are optional (pass 0 / '' to omit).
struct CreateMessageParams #
struct CreateMessageParams {
pub:
messages []SamplingMessage
model_preferences ModelPreferences @[json: modelPreferences]
system_prompt string @[json: systemPrompt; omitempty]
max_tokens int @[json: maxTokens]
temperature f64 @[omitempty]
stop_sequences []string @[json: stopSequences; omitempty]
metadata string @[omitempty; raw]
tools []Tool @[omitempty]
tool_choice ToolChoice @[json: toolChoice; omitempty]
include_context string @[json: includeContext; omitempty]
}
CreateMessageParams are the typed parameters for sampling/createMessage.
struct CreateMessageResult #
struct CreateMessageResult {
pub:
role string
content string @[raw]
model string
stop_reason string @[json: stopReason]
}
CreateMessageResult is the typed payload returned by the client.
struct DiscoverResult #
struct DiscoverResult {
pub:
supported_versions []string @[json: supportedVersions]
capabilities string @[raw]
instructions string
ttl_ms int @[json: ttlMs]
cache_scope string @[json: cacheScope]
result_type string @[json: resultType]
}
DiscoverResult is what server/discover answers with: the revisions the server speaks and the capabilities it offers.
struct ElicitParams #
struct ElicitParams {
pub:
mode string @[omitempty]
message string
requested_schema ElicitSchema @[json: requestedSchema; omitempty]
url string @[omitempty]
elicitation_id string @[json: elicitationId; omitempty]
}
ElicitParams are the typed parameters for elicitation/create. Set mode to 'url' and supply url + elicitation_id to send a URL-mode request; otherwise the call defaults to form mode and requested_schema is expected to describe the form fields.
struct ElicitResult #
struct ElicitResult {
pub:
action string
content string @[omitempty; raw]
}
ElicitResult is the typed payload returned by the client.
struct ElicitSchema #
struct ElicitSchema {
pub:
type_ string = 'object' @[json: type]
properties string @[raw]
required []string @[omitempty]
}
ElicitSchema is the requested object schema sent to the client for elicitation.
struct Empty #
struct Empty {}
Empty omits a JSON-RPC field when used with MCP helpers.
fn (Empty) str #
fn (e Empty) str() string
str returns the empty string.
struct EmptyObject #
struct EmptyObject {}
EmptyObject encodes to an empty JSON object.
fn (EmptyObject) str #
fn (e EmptyObject) str() string
str returns the JSON empty object literal.
struct GetPromptResult #
struct GetPromptResult {
pub:
description string @[omitempty]
messages []PromptMessage
}
GetPromptResult is returned by prompts/get.
struct Icon #
struct Icon {
pub:
src string
mime_type string @[json: mimeType; omitempty]
sizes []string @[omitempty]
theme string @[omitempty]
}
Icon describes a UI icon advertised by an MCP implementation, tool, resource, resource template or prompt. src is required; mime_type, sizes and theme are optional metadata mirroring the spec's Icon shape.
struct Implementation #
struct Implementation {
pub:
name string
version string
title string @[omitempty]
description string @[omitempty]
website_url string @[json: websiteUrl; omitempty]
icons []Icon @[omitempty]
}
Implementation identifies an MCP client or server implementation. name and version are required; title, description, website_url and icons are optional 2025-11-25 metadata extensions (BaseMetadata + Icons).
struct InitializeParams #
struct InitializeParams {
pub:
protocol_version string @[json: protocolVersion]
capabilities string @[raw]
client_info Implementation @[json: clientInfo]
}
InitializeParams is the typed payload for the initialize request.
struct InitializeResult #
struct InitializeResult {
pub:
protocol_version string @[json: protocolVersion]
capabilities string @[raw]
server_info Implementation @[json: serverInfo]
instructions string
}
InitializeResult is the typed result returned by an MCP server after initialization.
struct InputRequired #
struct InputRequired {
pub:
// input_requests maps a caller-chosen key to a full JSON-RPC request object
// (method plus params) that the client must answer. At least one of
// `input_requests` or `request_state` is set.
input_requests map[string]string
// request_state is an opaque token echoed back by the client on retry.
request_state string
}
InputRequired is the sentinel a 2026-07-28 handler returns to ask the client for input instead of returning a result. It is not a wire error: on the 2026-07-28 path the dispatcher turns it into a successful InputRequiredResult and re-invokes the handler when the client retries the original request.
struct ListRootsResult #
struct ListRootsResult {
pub:
roots []Root
}
ListRootsResult is the typed payload returned by the client to a server roots/list request.
struct MessageEnvelope #
struct MessageEnvelope {
pub:
jsonrpc string
id string @[raw]
method string
params string @[raw]
result string @[raw]
error ResponseError
}
MessageEnvelope is the shared wire representation used while decoding MCP messages.
struct MetaParams #
struct MetaParams {
pub:
meta MetaPayload @[json: '_meta']
}
MetaParams wraps the optional MCP _meta request field for JSON decoding.
struct MetaPayload #
struct MetaPayload {
pub:
progress_token string @[json: progressToken; raw]
// The reserved 2026-07-28 keys. The scalar fields decode to plain values;
// the object fields stay raw so they can be re-used verbatim.
protocol_version string @[json: 'io.modelcontextprotocol/protocolVersion']
client_info string @[json: 'io.modelcontextprotocol/clientInfo'; raw]
client_capabilities string @[json: 'io.modelcontextprotocol/clientCapabilities'; raw]
log_level string @[json: 'io.modelcontextprotocol/logLevel']
}
MetaPayload contains metadata extracted from an MCP request.
struct ModelHint #
struct ModelHint {
pub:
name string
}
ModelHint is a name hint for sampling/createMessage model selection.
struct ModelPreferences #
struct ModelPreferences {
pub:
hints []ModelHint
cost_priority f64 @[json: costPriority; omitempty]
speed_priority f64 @[json: speedPriority; omitempty]
intelligence_priority f64 @[json: intelligencePriority; omitempty]
}
ModelPreferences expresses sampling/createMessage routing weights.
struct Notification #
struct Notification {
pub:
jsonrpc string = jsonrpc_version
method string
params string @[omitempty; raw]
}
Notification is a JSON-RPC notification encoded for MCP.
fn (Notification) encode #
fn (notification Notification) encode() string
encode serializes the notification to JSON.
fn (Notification) decode_params #
fn (notification Notification) decode_params[T]() !T
decode_params decodes the raw notification params into T.
struct Null #
struct Null {}
Null represents the JSON null literal.
fn (Null) str #
fn (n Null) str() string
str returns the JSON null literal.
struct Prompt #
struct Prompt {
pub:
name string
title string @[omitempty]
description string @[omitempty]
arguments []PromptArgument
icons []Icon @[omitempty]
}
Prompt describes an MCP prompt exposed by the server.
struct PromptArgument #
struct PromptArgument {
pub:
name string
description string @[omitempty]
required bool
}
PromptArgument describes one prompt argument.
struct PromptMessage #
struct PromptMessage {
pub:
role string
content string @[raw]
}
PromptMessage is one message returned by prompts/get.
struct ReadResourceResult #
struct ReadResourceResult {
pub:
contents []ResourceContents
}
ReadResourceResult is returned by resources/read.
struct Request #
struct Request {
pub:
jsonrpc string = jsonrpc_version
id string @[raw]
method string
params string @[omitempty; raw]
}
Request is a JSON-RPC request message encoded for MCP.
fn (Request) encode #
fn (req Request) encode() string
encode serializes the request to JSON.
fn (Request) decode_params #
fn (req Request) decode_params[T]() !T
decode_params decodes the raw request params into T.
struct Resource #
struct Resource {
pub:
uri string
name string
title string @[omitempty]
description string @[omitempty]
mime_type string @[json: mimeType; omitempty]
size ?int
icons []Icon @[omitempty]
annotations Annotations
}
Resource describes a concrete MCP resource exposed by the server.
struct ResourceContents #
struct ResourceContents {
pub:
uri string
mime_type string @[json: mimeType; omitempty]
text string @[omitempty]
blob string @[omitempty]
}
ResourceContents contains the result of resources/read.
struct ResourceTemplate #
struct ResourceTemplate {
pub:
uri_template string @[json: uriTemplate]
name string
title string @[omitempty]
description string @[omitempty]
mime_type string @[json: mimeType; omitempty]
icons []Icon @[omitempty]
annotations Annotations
}
ResourceTemplate describes a parameterized MCP resource URI template.
struct Response #
struct Response {
pub:
jsonrpc string = jsonrpc_version
id string @[raw]
result string @[raw]
error ResponseError
}
Response is a JSON-RPC response message encoded for MCP.
fn (Response) encode #
fn (resp Response) encode() string
encode serializes the response to JSON.
fn (Response) decode_result #
fn (resp Response) decode_result[T]() !T
decode_result decodes the response result into T.
struct ResponseError #
struct ResponseError {
pub:
code int
message string
data string @[raw]
}
ResponseError is the JSON-RPC error payload used by MCP responses.
fn (ResponseError) code #
fn (err ResponseError) code() int
code returns the JSON-RPC error code.
fn (ResponseError) msg #
fn (err ResponseError) msg() string
msg returns the JSON-RPC error message.
fn (ResponseError) err #
fn (err ResponseError) err() IError
err casts the response error to IError.
struct Root #
struct Root {
pub:
uri string
name string @[omitempty]
}
Root identifies a filesystem-or-URI boundary advertised by the client in response to roots/list.
struct SamplingMessage #
struct SamplingMessage {
pub:
role string
content string @[raw]
}
SamplingMessage is one message of a sampling/createMessage exchange.
struct Server #
struct Server {
mut:
server_info Implementation
protocol_version string
supported_versions []string
cache_ttl_ms int
cache_scope string
capabilities_override string
instructions string
http_path string
allowed_origins []string
enable_logging bool
http_server &http.Server = unsafe { nil }
tools map[string]RegisteredTool
tool_names []string
resources map[string]RegisteredResource
resource_uris []string
resource_templates map[string]ResourceTemplate
resource_template_ids []string
prompts map[string]RegisteredPrompt
prompt_names []string
completions map[string]RegisteredCompletion
state shared ServerState
}
Server handles MCP protocol requests for stdio and HTTP transports.
fn (Server) add_tool #
fn (mut s Server) add_tool(tool Tool, handler ToolHandler) !
add_tool registers a tool and its handler.
fn (Server) add_resource #
fn (mut s Server) add_resource(resource Resource, handler ResourceHandler) !
add_resource registers a concrete resource and its read handler.
fn (Server) add_resource_template #
fn (mut s Server) add_resource_template(template ResourceTemplate) !
add_resource_template registers a resource template exposed by resources/templates/list.
fn (Server) add_prompt #
fn (mut s Server) add_prompt(prompt Prompt, handler PromptHandler) !
add_prompt registers a prompt and its handler.
fn (Server) add_completion #
fn (mut s Server) add_completion(ref CompletionRef, argument string, handler CompletionHandler) !
add_completion registers a completion handler for one argument of a prompt or resource template. argument is the argument name being completed.
fn (Server) notify_tools_list_changed #
fn (mut s Server) notify_tools_list_changed()
notify_tools_list_changed broadcasts the tool catalog change notification.
fn (Server) notify_resources_list_changed #
fn (mut s Server) notify_resources_list_changed()
notify_resources_list_changed broadcasts the resource catalog change notification.
fn (Server) notify_prompts_list_changed #
fn (mut s Server) notify_prompts_list_changed()
notify_prompts_list_changed broadcasts the prompt catalog change notification.
fn (Server) notify_log #
fn (mut s Server) notify_log(level LogLevel, logger string, data_json string)
notify_log emits a notifications/message payload at level filtered per session by the most recent logging/setLevel call, or - for the 2026-07-28 stateless sessions - by the logLevel the request asked for in its _meta. A stateless request that did not ask for logs gets none. data_json MUST be a valid JSON value (object preferred per spec). logger is optional.
fn (Server) list_roots #
fn (mut s Server) list_roots(session_id string, timeout time.Duration) !ListRootsResult
list_roots issues roots/list to the session and waits for the response.
fn (Server) sample #
fn (mut s Server) sample(session_id string, params CreateMessageParams, timeout time.Duration) !CreateMessageResult
sample issues sampling/createMessage and waits for the LLM result.
fn (Server) elicit #
fn (mut s Server) elicit(session_id string, params ElicitParams, timeout time.Duration) !ElicitResult
elicit issues elicitation/create and waits for the user-supplied content.
fn (Server) notify_elicitation_complete #
fn (mut s Server) notify_elicitation_complete(session_id string, elicitation_id string)
notify_elicitation_complete emits a notifications/elicitation/complete notification for the session that initiated a URL-mode elicitation. Pass the same elicitation_id that was supplied to elicit() so the client can correlate the out-of-band interaction with its pending request.
fn (Server) notify_resource_updated #
fn (mut s Server) notify_resource_updated(uri string)
notify_resource_updated emits notifications/resources/updated to every session that has subscribed to uri.
fn (Server) serve_stdio #
fn (mut s Server) serve_stdio() !
serve_stdio starts serving MCP messages over stdio using newline framing. The newline framing of stdio messages is unchanged by 2026-07-28 - that revision removes sessions, not the transport - so the same rule still holds: messages are delimited by newlines and MUST NOT contain embedded newlines. We bypass libc stdio buffering on the way in (raw read() on fd 0) and flush stdout after every frame on the way out, so peers behind a pipe see responses immediately and the server reacts to each line as soon as it arrives. This is also the only transport that can push notifications for a live subscriptions/listen stream.
fn (Server) serve_http #
fn (mut s Server) serve_http(addr string) !
serve_http starts serving MCP over a single HTTP endpoint.
fn (Server) close #
fn (mut s Server) close()
close stops the HTTP server if it is running.
fn (Server) wait_till_running #
fn (mut s Server) wait_till_running(params http.WaitTillRunningParams) !int
wait_till_running waits until the HTTP server transitions to the running state.
struct ServerConfig #
struct ServerConfig {
pub:
name string
version string
title string
description string
website_url string
icons []Icon
protocol_version string = protocol_version
// supported_versions lists every protocol revision the server can speak.
// When empty it defaults to `default_supported_versions`. The preferred
// `protocol_version` must be part of this list, otherwise `new_server`
// panics.
supported_versions []string
// cache_ttl_ms is the `ttlMs` a `server/discover` result advertises on
// sessions negotiated to 2026-07-28.
cache_ttl_ms int = 300_000
// cache_scope is the `cacheScope` a `server/discover` result advertises on
// sessions negotiated to 2026-07-28.
cache_scope string = 'private'
capabilities string
instructions string
http_path string = default_http_path
// enable_logging declares the `logging` capability and lets clients call
// `logging/setLevel`. Disabled by default — turn on when the server emits
// `notifications/message` payloads.
enable_logging bool
// allowed_origins lists the Origin header values accepted on Streamable
// HTTP. Use `*` to accept any origin (NOT recommended). When empty the
// server only accepts requests without an Origin header or from the
// loopback addresses (`http://localhost[:port]`, `http://127.0.0.1[:port]`,
// `http://[::1][:port]`, or the literal string `null`).
allowed_origins []string
}
ServerConfig configures an MCP server instance.
struct SubscriptionFilter #
struct SubscriptionFilter {
pub:
tools_list_changed bool @[json: toolsListChanged]
prompts_list_changed bool @[json: promptsListChanged]
resources_list_changed bool @[json: resourcesListChanged]
resource_uris []string @[json: resourceSubscriptions]
}
SubscriptionFilter is the set of notifications a subscriptions/listen request opts into. Every field is opt-in: the server MUST NOT send a type that was not asked for.
struct SubscriptionListenParams #
struct SubscriptionListenParams {
pub mut:
notifications SubscriptionFilter
}
SubscriptionListenParams is the payload of a subscriptions/listen request.
struct Tool #
struct Tool {
pub:
name string
title string @[omitempty]
description string @[omitempty]
input_schema string = default_tool_input_schema @[json: inputSchema; raw]
output_schema string @[json: outputSchema; omitempty; raw]
annotations ToolAnnotations
icons []Icon @[omitempty]
execution ToolExecution
}
Tool describes an MCP tool exposed by the server.
struct ToolAnnotations #
struct ToolAnnotations {
pub:
title string @[omitempty]
read_only_hint ?bool @[json: readOnlyHint]
destructive_hint ?bool @[json: destructiveHint]
idempotent_hint ?bool @[json: idempotentHint]
open_world_hint ?bool @[json: openWorldHint]
}
ToolAnnotations exposes the optional behavioural hints described by the MCP spec (tools/list Annotations object).
struct ToolChoice #
struct ToolChoice {
pub:
mode string
}
ToolChoice configures sampling/createMessage tool-use behaviour. mode is one of 'auto' (default), 'required', or 'none'.
struct ToolExecution #
struct ToolExecution {
pub:
task_support string @[json: taskSupport; omitempty]
}
ToolExecution carries optional execution metadata for a tool. Set task_support to one of 'forbidden', 'optional' or 'required' to advertise tasks/* support; the default (empty) omits the field on the wire.
struct ToolResult #
struct ToolResult {
pub:
content string @[omitempty; raw]
structured_content string @[json: structuredContent; omitempty; raw]
is_error bool @[json: isError]
}
ToolResult is returned by tools/call.
- README
- Constants
- fn audio_content
- fn audio_content_with_annotations
- fn connect
- fn connect_2026
- fn connect_http
- fn connect_stdio
- fn decode_notification
- fn decode_request
- fn decode_response
- fn embedded_blob_resource
- fn embedded_blob_resource_with_annotations
- fn embedded_text_resource
- fn embedded_text_resource_with_annotations
- fn image_content
- fn image_content_with_annotations
- fn new_client
- fn new_notification
- fn new_request
- fn new_response
- fn new_server
- fn parse_log_level
- fn prompt_text_message
- fn resource_link_content
- fn subscription_id_of
- fn text_content
- fn text_content_with_annotations
- fn tool_text_result
- interface Transport
- type CompletionHandler
- type PromptHandler
- type ResourceHandler
- type ToolHandler
- enum LogLevel
- enum SessionTransport
- struct Annotations
- struct Client
- struct ClientConfig
- struct CompletionRef
- struct CompletionResult
- struct Context
- struct CreateMessageParams
- struct CreateMessageResult
- struct DiscoverResult
- struct ElicitParams
- struct ElicitResult
- struct ElicitSchema
- struct Empty
- struct EmptyObject
- struct GetPromptResult
- struct Icon
- struct Implementation
- struct InitializeParams
- struct InitializeResult
- struct InputRequired
- struct ListRootsResult
- struct MessageEnvelope
- struct MetaParams
- struct MetaPayload
- struct ModelHint
- struct ModelPreferences
- struct Notification
- struct Null
- struct Prompt
- struct PromptArgument
- struct PromptMessage
- struct ReadResourceResult
- struct Request
- struct Resource
- struct ResourceContents
- struct ResourceTemplate
- struct Response
- struct ResponseError
- struct Root
- struct SamplingMessage
- struct Server
- fn add_tool
- fn add_resource
- fn add_resource_template
- fn add_prompt
- fn add_completion
- fn notify_tools_list_changed
- fn notify_resources_list_changed
- fn notify_prompts_list_changed
- fn notify_log
- fn list_roots
- fn sample
- fn elicit
- fn notify_elicitation_complete
- fn notify_resource_updated
- fn serve_stdio
- fn serve_http
- fn close
- fn wait_till_running
- struct ServerConfig
- struct SubscriptionFilter
- struct SubscriptionListenParams
- struct Tool
- struct ToolAnnotations
- struct ToolChoice
- struct ToolExecution
- struct ToolResult