Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

Non-object outputSchema and structuredContent are sent to clients that negotiated 2025-06-18 / 2025-11-25, where an object is required

Abierto
#1,337 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 3 días

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
3/5
Tiempo estimado
1-2 días
Aptitud para principiantes
28/100
Tipo de issue
Error
Claridad
Bastante claro
Estado de actividad
Activo
Stack tecnológico
rust
Área
api

Línea de trabajo

Start with strip_output in crates/rmcp/src/handler/server/common.rs, which always keeps non-object output schemas, and with the version gating for result_type in crates/rmcp/src/handler/server.rs. Check that Json<Vec<T>> advertises an array outputSchema and returns a non-object structuredContent for a session negotiated to 2025-06-18. Done means that session omits both for non-object roots while 2026-07-28 keeps them. The issue lists three options, so confirm with maintainers which one to build first.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

bug P1 ready for work T-handler T-model

Summary

A tool that returns Json<Vec<T>> (or any non-object type) makes the server advertise a non-object outputSchema, and return a non-object structuredContent, to every client, including clients that negotiated protocol 2025-06-18 or 2025-11-25. In those versions both must be objects, so such a client sees a schema that is invalid for the version it asked for.

This is not a request to undo SEP-2106 (#874); allowing arrays and primitives is right for 2026-07-28. The gap is that the choice does not depend on the negotiated version, although other 2026-07-28 behaviour in handler/server.rs is gated on it.

What the spec versions say

  • 2025-06-18, schema.ts: outputSchema?: { type: "object"; properties?: ...; required?: string[] }
  • 2025-11-25, schema.ts: outputSchema?: { $schema?: string; type: "object"; ... }, and structuredContent?: { [key: string]: unknown }
  • 2026-07-28 (SEP-2106): any JSON Schema 2020-12 schema, any JSON value.

crates/rmcp/src/handler/server/common.rs (strip_output) says output schemas "are not restricted to type: "object" (per SEP-2106)" unconditionally.

Reproduction

rmcp 3.5.1, features = ["server", "macros", "transport-io"].

use rmcp::handler::server::router::tool::ToolRouter;
use rmcp::model::{Implementation, ServerCapabilities, ServerConfig};
use rmcp::{tool, tool_handler, tool_router, Json, ServerHandler, ServiceExt};

#[derive(Clone)]
struct Server { tool_router: ToolRouter<Self> }

#[tool_router]
impl Server {
    #[tool(description = "Returns a list of names")]
    async fn names(&self) -> Json<Vec<String>> {
        Json(vec!["a".to_string(), "b".to_string()])
    }
}

#[tool_handler]
impl ServerHandler for Server {
    fn get_info(&self) -> ServerConfig {
        ServerConfig::new(ServerCapabilities::builder().enable_tools().build())
            .with_server_info(Implementation::new("repro", "0.1.0"))
    }
}

#[tokio::main(flavor = "current_thread")]
async fn main() {
    let server = Server { tool_router: Server::tool_router() };
    server.serve(rmcp::transport::stdio()).await.unwrap().waiting().await.unwrap();
}

A client that asks for 2025-06-18:

{ printf '%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"probe","version":"1"}}}' \
  '{"jsonrpc":"2.0","method":"notifications/initialized"}' \
  '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
  '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"names","arguments":{}}}'; sleep 1; } | ./target/debug/repro

Output (trimmed):

{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-06-18", ...}}
{"jsonrpc":"2.0","id":2,"result":{"tools":[{"name":"names", ...,
  "outputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","items":{"type":"string"},"type":"array"}}]}}
{"jsonrpc":"2.0","id":3,"result":{"content":[{"type":"text","text":"[\"a\",\"b\"]"}],"structuredContent":["a","b"],"isError":false}}

The server answers protocolVersion: 2025-06-18 and then advertises outputSchema.type: "array" and returns structuredContent: ["a","b"]. The same happens when the client asks for 2025-11-25. When I asked for 2026-07-28 in initialize, the server answered 2025-11-25 and still advertised the array schema.

Why it matters

A client that validates tools/list against the schema of the negotiated version will reject the tool, and some validate the whole list at once, so one tool with a list result can make every tool unavailable. We hit this shipping an MCP server (metamug/plunger#97); the report there was that the output schema was not an object. I have not checked which specific clients reject it, only that the advertised schema is invalid for the version that was negotiated.

The workaround is to wrap every list in a struct (as suggested in #532), which works everywhere, but it is easy to miss because Json<Vec<T>> compiles and works with clients that are lenient or new.

Suggested behaviour

Any of these would help, in rough order of preference:

  1. For a session that negotiated a version before 2026-07-28, omit outputSchema when its root type is not "object" (the call still works; the result is also in content), and do the same for structuredContent. This follows the version gating already used for result_type.
  2. Offer a wrapper (for example Json<Wrapped<T>>, or an attribute on #[tool]) that produces { "result": T } and an object schema in all versions, so authors have a supported way to be compatible without writing a struct for every list.
  3. At minimum, document on Json<T> and on schema_for_output that a non-object T is only valid for peers on 2026-07-28 and later.

Related: #532 (the same symptom, closed before SEP-2106), #874 (SEP-2106).

Lenguaje dominante
Rust
Estrellas
4k
Forks
654
Merge medio
3 d 15 h
PR fusionados (30 d)
37

Preparar el entorno

Abrir en Codespaces

Inicia el contenedor de desarrollo del proyecto en tu navegador, con tu propia cuenta de GitHub.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de modelcontextprotocol/rust-sdk

Todos los issues de modelcontextprotocol/rust-sdk

Issues similares

Más issues de Rust

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.