Migrating to 2.x #926
DaleSeo
announced in
Announcements
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Upgrading to RMCP 2.x — Migration Guide
Why 2.0?
Over time the
rmcpmodel drifted from the MCP Schema Reference. The same content union existed in three slightly different forms (Content/RawContent,PromptMessageContent,SamplingMessageContent), many types sat behind anAnnotated<T>wrapper, several names didn't match the spec (ResourceReference,CreateElicitation*,GetTaskInfo*,PrimitiveSchema, and others), and some spec fields were missing (_metain several places,audioin prompt content). In practice this made it hard to find the type that corresponds to a given spec construct, or to tell whether it was complete and correctly named, and that cognitive load fell on SDK users and maintainers alike.We had deferred this cleanup because doing it properly is a breaking change, and it wasn't worth spending a major version on by itself. The upcoming 2026-07-28 revision changes that: it requires breaking changes regardless, so realigning the existing model to the current 2025-11-25 schema in the same major (consistent with our versioning approach in #813) avoids an extra bump and a second round of migration.
It also gives the 2026-07-28 work a much better starting point. Building support for the new spec on a model that already maps cleanly to the reference is far simpler than layering it on types that diverge, which would carry the duplication and naming debt forward into v3.
The change is large in line count, but the JSON wire format is unchanged (only additive
_metaand optional fields). Most of the volume is mechanical renames and regenerated schema snapshots, and every breaking change is covered below.TL;DR
Content/RawContentContentBlockRawTextContent,RawImageContent, … wrapped inAnnotated<T>TextContent,ImageContent,AudioContent,EmbeddedResourcewith inlineannotations/_metaRawResource,Annotated<RawResource>Resource(size: u64)PromptMessageContent+PromptMessageRoleContentBlock+RoleSamplingMessageContentSamplingMessageContentBlockResourceReferenceResourceTemplateReferenceCreateElicitation{Request,RequestParams,Result},PrimitiveSchemaElicit{Request,RequestParams,Result},PrimitiveSchemaDefinitionGetTaskInfo*,GetTaskResult*GetTask*,GetTaskPayload*(+ newTaskMetadata,RelatedTaskMetadata,TaskStatusNotification)TaskStatusNotificationParam = TaskTaskStatusNotificationParamwraps{ _meta, task }and preserves task field access viaDerefToolResultContent.contentcould deserialize as[]ToolResultContent.contentis required; usecontent: []for empty resultsFoo { field, .. }literalsFoo::new(..).with_*(..)(most wire types are now#[non_exhaustive])CancelledNotificationParam.request_idRequestIdOption<RequestId>Old names are kept as
#[deprecated]aliases for one release where practical, so most code keeps compiling with warnings pointing at the new name.1. Unified content model (
ContentBlock)The
Annotated<T>wrapper and the parallelRaw*structs are gone. Each content type is now a single flat struct, and the union isContentBlock(text | image | audio | resource_link | resource).annotationsand_metaare inline fields now; set them with builders.AudioContentalso gained_meta.The
Annotated<T>wrapper and theAnnotateAbletrait are replaced by inlineannotationsand_metafields on each content/resource type. Use.with_annotations(..)in place of.annotate(..)/.optional_annotate(..)(build theAnnotationswith.with_priority(..)/.with_audience(..)/.with_timestamp(..)), and drop.no_annotation()(annotations are simply left unset).ToolResultContent.contentis now required for sampling tool results. Send an empty vector (JSON"content": []) when there are no content blocks. This does not changeCallToolResult, which still keeps its missing-content compatibility behavior.2.
#[non_exhaustive]on wire typesContent, resource, capability, and most spec structs are now
#[non_exhaustive], so struct-literal construction from downstream crates no longer compiles. Use the constructors/builders:This also keeps routine spec additions from forcing a breaking release (#865 is an example of one such forced change). See #813 for the versioning rationale.
3. Prompts use
Role+ContentBlockPromptMessageContentis replaced by the unifiedContentBlock, andPromptMessageRoleby the sharedRole.PromptMessage.contentis now aContentBlockandPromptMessage.roleaRole.PromptMessage::new_image/new_audionow take ameta: Option<Meta>argument:(role, data, mime_type, meta, annotations).4. Tasks
TaskMetadata { ttl }— thetaskfield onCallToolRequestParams/CreateMessageRequestParamsis nowOption<TaskMetadata>(wasOption<JsonObject>); construct withTaskMetadata::new().with_ttl(..).RelatedTaskMetadata { task_id }plus theio.modelcontextprotocol/related-task_metakey.TaskStatusNotification(notifications/tasks/status) on both client and server notification unions.TaskStatusNotificationParamnow modelsNotificationParams & Taskinstead of being a directTaskalias. Construct it withTaskStatusNotificationParam::new(task)ortask.into(), access.taskwhen you need the wrappedTask, and existing task field access such asparams.task_idremains available throughDeref.GetTaskInfoRequest/GetTaskInfoParamsGetTaskRequest/GetTaskParamsGetTaskResultRequest/GetTaskResultParamsGetTaskPayloadRequest/GetTaskPayloadParamsListTasksResultdropped the non-spectotalfield and gained_meta.TaskListtype was removed; useListTasksResult.5. Spec-name renames
Old names remain as
#[deprecated]aliases.SamplingMessageContentSamplingMessageContentBlockResourceReferenceResourceTemplateReferenceCreateElicitationRequestElicitRequestCreateElicitationRequestParamsElicitRequestParamsCreateElicitationResultElicitResultPrimitiveSchemaPrimitiveSchemaDefinitionElicitationCompletionNotificationElicitationCompleteNotification6.
_metacoverage_metawas added to the result / notification-param types that were missing it for spec parity:InitializeResult,GetPromptResult,ReadResourceResult,CompleteResult,ListTasksResult,ListRootsResult,CreateTaskResult,ProgressNotificationParam,CancelledNotificationParam,ResourceUpdatedNotificationParam,LoggingMessageNotificationParam, and the elicitation-complete notification params. Constructors default these toNone.7. Cancellation
CancelledNotificationParam.request_idis nowOption<RequestId>(wasRequestId), per 2025-11-25 — task cancellation usestasks/cancelinstead, sorequestIdis optional.Questions or migration snags? Reply here and we'll help.
Beta Was this translation helpful? Give feedback.
All reactions