Skip to content

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 initialize handshake plus MCP-Session-Id sessions.
  • 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, required
  • io.modelcontextprotocol/clientCapabilities — what the client can do, required
  • io.modelcontextprotocol/clientInfo — who is calling
  • io.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-stream only.
  • GET: opens an SSE stream of queued notifications. Resume with Last-Event-ID.
  • DELETE: terminates the session (MCP-Session-Id required).
  • 403 on disallowed Origin, 400 on unsupported MCP-Protocol-Version, 406 when Accept lists neither application/json nor text/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 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 #

@[params]
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 #

@[heap]
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 #

@[params]
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.