Repository navigation
Releases: pbednarcik/FlexMCP
Releases · pbednarcik/FlexMCP
Release list
v0.3.1
v0.3.1 hardens input-schema validation against requests that are small to send and costly to check. Upgrading is recommended for any server that validates tool input (WithInputSchemaValidation).
Fixes
- Long numbers no longer cost seconds of CPU. The schema validator reads every number exactly, and its work grows with how many digits a literal has and how far its exponent reaches. A request with a few hundred literals like
1e-999999used to cost seconds. Arguments holding a number with more than 1,100 digits, or with a decimal scale past 1,100 either way, are now refused as out of range before the validator reads them. Every number afloat64can hold fits within these bounds. - Deep nesting no longer multiplies the cost. Under a recursive schema, the validator's error tree grows with the square of the nesting depth: a 20 KB request could allocate about 800 MB. Arguments nested deeper than 128 levels (objects and arrays, the arguments object counting as the first) are now refused before validation, with
value is nested deeper than 128 levels. - Validation refusals are bounded and honour the size limit. A refusal now cuts the place it names as the validator's own lines already did, so a long property name no longer makes the answer twice the request's size. Every validation refusal, on input and output, synchronous or in a task, now passes
WithToolResultSizeLimit.
Changes you may notice
- These refusals are
isErrortool results, like every validation failure. Their text follows the existing form; for an ordinary short path it is unchanged. - The two new input refusals apply whatever the schema says: a past-bounds number is refused even where no keyword reads it, and so is a deeply nested argument.
- Output validation is unchanged: a tool may still return large integers in structured content.
- The limits are described in Tools.
Known issues
- The validator records every failure in a request, so a request whose many values all fail still costs memory in proportion: about 1 KB per failing value, more when the values sit deep. A 200 KB request of 100,000 failing values measured 92 MB when they sat three levels deep, and 471 MB at 126 levels. This is not new in this release. Put a request body limit in front of a server that validates input from untrusted clients. A validator that stops early is planned.
Install
go get github.com/pbednarcik/flexmcp@v0.3.1The OpenTelemetry adapter is unchanged: github.com/pbednarcik/flexmcp/otel@v0.2.0.
v0.3.0
v0.3.0 adds the 2026-07-28 Tasks extension (io.modelcontextprotocol/tasks) to the server and the client.
Highlights
- Long-running tools as tasks. A tool handler can answer a
tools/callwith a task by returningserver.NewTaskResult(ctx, handlerID, ...). The client then pollstasks/get, and can cancel withtasks/cancelor answer input withtasks/update.- FlexMCP stores nothing about a task. A
TaskHandleryou provide owns the work and its state. server.NewMemoryTasksis a ready-made in-memory handler:MemoryTasks.Runhands the call over when the request allows it, and otherwise runs the work synchronously.
- FlexMCP stores nothing about a task. A
- Unguessable task ids, bound to the caller. A task id is sealed with an HMAC, not looked up. Set
WithTaskKeyso ids survive a restart and work across instances, andWithTaskCallerso one caller cannot read another's tasks. - Client support.
GetExtensionTask,CancelExtensionTask,UpdateExtensionTaskandWaitForExtensionTask. A refused id is a typedmcp.TaskRefusedError;mcp.IsTaskGonetells you to call the tool again. - Both eras kept apart. The 2025-11-25 tasks (
AddTaskTool,params.task) serve initialize-era requests only. On a 2026-07-28 request,params.taskis ignored, task-only tools are hidden, andexecutionis dropped fromtools/list. - Works with Claude Code. Checked live against Claude Code 2.1.312, which declares the extension on its
tools/callandtasks/getand polls at the interval the server asks for. It does not declare it on every call, so a tool must still answer synchronously when no task is possible;MemoryTasks.Rundoes. - Documented and runnable. See Tasks and
examples/tasks_extension.
Changes you may notice
- The task result options are
WithTaskResultPollInterval,WithTaskResultTTL,WithTaskResultStatusMessageandWithTaskResultCreatedAt. TheWithTask*names already belong to the 2025-11-25 tasks. - Run handed-over work under
server.DetachTaskContext(ctx). It keeps the request's values and drops its cancellation, its session and its task stamp.MemoryTasks.Rundoes this for you. - On a 2026-07-28 request,
tasks/get,tasks/cancelandtasks/updatefire only the untyped hooks (BeforeAny,OnSuccess,OnError). The typed task hooks are for earlier revisions. - On a 2026-07-28 connection, the client's old
GetTask,CancelTask,WaitForTask,TaskResultandListTasksreturnErrLegacyTaskMethod. WithExtensionscopies its map. Given a tasks entry without a task handler, it panics at construction.- A client may leave without cancelling its task, so choose a TTL that bounds abandoned work.
MemoryTasksdefaults to one hour. mcp.WithModelImmediateResponseis deprecated. The server never sends it in the answer to a task-augmented call.
Fixes
- Output schema validation now judges a result's raw structured content when the result carries it. Before, raw content that broke the schema could be sent unchecked.
- A tool re-registered while a call is in flight:
- its
Mcp-Param-*header bindings are now enforced on that call; - a 2025-11-25 task-augmented call now runs the registration the tool filters authorized.
- its
- Tool filters on a 2026-07-28
tools/callsee the same tool thattools/listshowed them.
Known issues
- At a very small result-size limit, the answer that hands a call over can be replaced by a size error after the task has started. Keep the limit well above the size of a task answer.
Install
go get github.com/pbednarcik/flexmcp@v0.3.0The OpenTelemetry adapter is unchanged: github.com/pbednarcik/flexmcp/otel@v0.2.0.
v0.3.0-rc.1
v0.2.0
FlexMCP is a high-performance Go implementation of the Model Context Protocol, tuned for busy servers that answer many complex tool calls. It began as a fork of mark3labs/mcp-go v1.1.1 and has since been largely reworked. This is its first release with notes.
Highlights
- Both protocol eras. The 2026-07-28 revision first, and the earlier revisions a client may still open with
initialize. The server is stateless: sessions, the GET stream, resumption, the event store, server-initiated requests and the HTTP+SSE server transport are gone. - Closer to the specification. The server was checked against the 2026-07-28 specification requirement by requirement. Request ids are handled exactly,
Originis validated, and OAuth keeps the MUSTs: HTTPS-only authorization servers, PKCE on by default, and a token sent only to its own server. - Fast and lean.
_metaand a tool call's arguments are decoded in one pass, input is validated without a second decode, header bindings are read once, andServerTool.ArgumentsOnDemandlets a tool skip building the arguments map. In our benchmarks a single in-process tool call is served up to 3 times faster than with mcp-go and up to 14 times faster than with the official Go SDK, with less than half of mcp-go's allocations (benchmarks on two machines). - Hardened. Bounded request bodies, HTTP timeouts, DNS-rebinding checks, bounded task lifetimes, and a typed-nil handler error answered instead of a panic.
- Upstream fixes. Fixes from mcp-go are ported by hand, through upstream's
mainafter v1.3.0, each commit naming the pull request it ports.
Upgrading from mcp-go
- The module path is
github.com/pbednarcik/flexmcp; the OpenTelemetry adapter is its own module,github.com/pbednarcik/flexmcp/otel. - Some client APIs and behaviours changed: read Client changes and Request ids and progress tokens first.
- The default client name in
clientInfois nowflexmcp.
Install
go get github.com/pbednarcik/flexmcp@v0.2.0
go get github.com/pbednarcik/flexmcp/otel@v0.2.0