mcpserver

package module
v0.0.16 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 6, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package mcpserver serves agenttool.Tool values over MCP: the serve side of MCP and the inverse of mcpclient.

The native contract is the centre and MCP is an edge: a tool's name, description and Parameters become the MCP tool definition, and the SDK's handler decodes the call, runs Execute and maps the output back to MCP content. The official Go SDK owns transports (stdio, streamable HTTP) and protocol negotiation.

server, err := mcpserver.NewServer("files", "1.0", readFile, writeFile)
...
err = server.Run(ctx, &sdk.StdioTransport{})

No policy runs here. A loop's hooks around tool calls belong to whoever hosts the loop, and this package hosts only tools; a caller who wants a policy applies it to the tools before serving them.

One server serves many clients and one Tool value serves them all, so the call's context carries the session it arrived on: a tool that owns anything per client, a working directory, a container or a shell, keys it on SessionFrom.

Index

Constants

View Source
const DefaultAbandonAfter = 30 * time.Minute

DefaultAbandonAfter is how long a call waits for the client to come back with the answers to its questions before it is stopped; see Options.AbandonAfter. The client comes back once the user has answered, so this is human time.

View Source
const DefaultMaxRequests = 10

DefaultMaxRequests is how many requests the client makes of one call at most; see Options.MaxRequests. It is the Go SDK's limit: its client gives up on a call whose tenth request brings back a question rather than a result.

View Source
const RecordMetaKey = "io.github.christopherdavenport.agenttool/record"

RecordMetaKey is the _meta key under which a result's record is served; see Handler. It is mcpclient.RecordMetaKey byte for byte, spelt out here so this module builds against the mcpclient release that predates it, and a test holds the two together.

Variables

This section is empty.

Functions

func AddTools

func AddTools(s *sdk.Server, tools ...agenttool.Tool) error

AddTools registers tools on an SDK server. Each tool's Parameters is the MCP input schema verbatim, so no reflection happens in the SDK; a nil Parameters becomes an empty object schema. The handler validates the arguments against that schema, as MCP servers are expected to, so a missing required property or a wrong type is refused before the tool runs. A schema the validator cannot parse or resolve is a registration error, "mcpserver: tool NAME: schema: ...", reported before any tool is added, as agenttool.New panics for the same mistake; the validator handles JSON Schema draft-07 and 2020-12. The handler then runs Execute with the raw arguments and maps the result with ContentOf; a returned error, a validation failure included, sets isError with the message as text so the calling model can see it and retry.

The call's context carries the MCP session it arrived on, which SessionFrom returns, so one Tool value can serve two clients without sharing what belongs to each. It carries the agenttool.Call too, as the executor's does, so a tool finds its call with agenttool.CallFrom on either side of the boundary.

A record crosses. A result whose Details implements agenttool.Recordable is served with its agenttool.Record under RecordMetaKey in the result's _meta, the data as a JSON string so it arrives byte for byte, and mcpclient makes it the Details on its side, so the container that served a call or the directory it spilled to reaches a session recorder through a server as it would in process. A Details that says it is recordable and cannot be recorded, an empty namespace or a value that does not marshal, does not fail the call, since the side effect has happened and a failure would invite the model to retry it: the output is served and the error text takes the record's place under the same key, where a subscriber reading the SDK result finds it.

A record a tool writes while it runs, with agenttool.WriteRecord, reaches whatever agenttool.ContextWithRecorder installed on the context the call runs under, which the handler leaves in place. The go-sdk derives each call's context from the one the session was opened with: over stdio and in memory that is the context the host passed to Run or Connect, and over streamable HTTP it is the initialize request's context in stateful mode and each request's own in stateless mode, both of which the SDK says middleware may add values to, so an HTTP host installs the recorder in middleware on the handler. That is how the SDK behaves at v1.8.0 and not a documented guarantee; a test here pins it for the in-memory transport, and the HTTP routes are described from the SDK's source rather than pinned. A host that wants no dependence on any of it wraps its tools with agenttool.Wrap and installs the recorder on the context there.

A tool asks the user a question through the agenttool.Elicitor the handler puts on the call's context when the client offers elicitation, so a tool that asks in process asks the same way served. From protocol 2026-07-28 a server may not send elicitation/create while it serves a call: the question goes back as the call's input request, under a request state that names the waiting call, and the answer arrives with the client's next request, which resumes it. The tool runs on its own goroutine meanwhile, on a context detached from the first request and carrying its values, and is cancelled when the client cancels the request it is waiting on, when the session it came on ends, or when the client does not come back within Options.AbandonAfter; over stateless HTTP a session lasts one request, so only the first and the last apply. A client that gives up and answers the open questions cancel, as mcpclient does, ends the wait at once, and the tool hears that nobody chose. The state is random and names a call only this process holds, so a server behind a load balancer routes a client's requests to one process. The Go SDK's client makes ten requests of a call at most, Options.MaxRequests, so a tool asks in nine rounds at most, and a tenth question is answered with an error at once; questions it asks together go in one. A client before 2026-07-28 is asked with elicitation/create while the call runs. A form's answer is checked against its schema, a question of a mode the client does not offer is answered agenttool.ActionCancel, and a client that offers no elicitation leaves the context as the host set it.

When the request carries a progress token, Call.OnUpdate forwards each update as a progress notification whose message is the update's text. An update whose Details is an agenttool.ProgressInfo supplies the notification's progress, total and message, so a tool that mcpclient consumed and this package serves again keeps its numbers; otherwise updates are numbered in order with no total.

func ContentOf

ContentOf maps a function call output to MCP content. Text becomes one text block. Parts map by type: textual parts to text blocks, an input_image with a data URL to an image block and one with a plain URL to a resource link, an input_file with data to an embedded blob resource and one with a URL to a resource link. Anything else is emitted as its JSON.

func ContextWithSession added in v0.0.6

func ContextWithSession(ctx context.Context, session *sdk.ServerSession) context.Context

ContextWithSession returns ctx carrying the MCP session a call arrived on. Handler does this for every call, so a tool reaches it with SessionFrom; a host that runs a tool outside a server, in a test or a direct call, can install one the same way.

func Definition

func Definition(tl agenttool.Tool) *sdk.Tool

Definition builds the MCP tool definition for tl. A tool that carries agenttool.Annotations serves them as MCP tool annotations, each hint stated rather than left to MCP's defaults, so a tool consumed by mcpclient and served again here keeps the hints it arrived with. A tool that carries none serves none.

func Handler

func Handler(tl agenttool.Tool) (sdk.ToolHandler, error)

Handler builds the SDK handler that runs tl. It fails when the tool's schema cannot be resolved for validation.

func NewServer

func NewServer(name, version string, tools ...agenttool.Tool) (*sdk.Server, error)

NewServer builds an SDK server named name that serves tools. It fails as AddTools does.

func SessionFrom added in v0.0.6

func SessionFrom(ctx context.Context) (*sdk.ServerSession, bool)

SessionFrom returns the MCP session of the call on ctx. One Tool value serves every client that connects, so a tool whose behaviour belongs to a client, one holding a working directory, a container or a shell, keys its state on the session: the pointer is unique to the connection and is what a host maps to whatever it owns per client. It reports false outside an MCP call, where a tool that needs a session must say so rather than share one.

func SessionID added in v0.0.6

func SessionID(ctx context.Context) string

SessionID returns the MCP session ID of the call on ctx, and "" when there is no session or the transport has none. Only a transport that negotiates session IDs, streamable HTTP, sets one; over stdio the session is the process and the ID is empty, so a host that keys state on a client uses SessionFrom and reads the ID for logs and records.

Types

type Options added in v0.0.11

type Options struct {
	// AbandonAfter is how long a call whose question is with the client
	// waits for the answer before its tool is stopped, zero meaning
	// [DefaultAbandonAfter]. A call whose session ends is stopped then,
	// and one whose client answers its questions cancel when it gives
	// up, as mcpclient does, hears that at once. Over stateless HTTP,
	// where each request is a session of its own, this is the only
	// thing that stops a call whose client gave up without doing so.
	AbandonAfter time.Duration

	// MaxRequests is how many requests the client makes of one call at
	// most, zero meaning [DefaultMaxRequests], and negative no limit. A
	// question the tool asks on the last is answered with an error at
	// once rather than sent to a client that would give up on the call
	// instead of answering.
	MaxRequests int
}

Options tunes how tools are served. The zero value is the default, and the package's functions of the same names use it.

func (Options) AddTools added in v0.0.11

func (o Options) AddTools(s *sdk.Server, tools ...agenttool.Tool) error

AddTools is AddTools with o.

func (Options) Handler added in v0.0.11

func (o Options) Handler(tl agenttool.Tool) (sdk.ToolHandler, error)

Handler is Handler with o.

func (Options) NewServer added in v0.0.11

func (o Options) NewServer(name, version string, tools ...agenttool.Tool) (*sdk.Server, error)

NewServer is NewServer with o.

Directories

Path Synopsis
examples
stdio command
Command stdio serves three agenttool.New tools over MCP on stdin/stdout, so any MCP client can call Go tools written against the agenttool contract.
Command stdio serves three agenttool.New tools over MCP on stdin/stdout, so any MCP client can call Go tools written against the agenttool contract.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL