Repository navigation
feat: generate JSON Schema for tool inputs from proto descriptors (port from Go) #5
Copy link
Copy link
Closed
Labels
factory:in-progressAn sf-factory worker is on itAn sf-factory worker is on itfactory:readyReady for the sf-factory to pick upReady for the sf-factory to pick up
Description
Part of #2. Port Go's cmd/protoc-gen-go-mcp/schema.go, with schema_test.go and testdata/schemapb/schema.proto, at 9e072f9. The output is the JSON Schema (draft 2020-12) for an RPC's request message, used as the tool's inputSchema. It must describe exactly what the pbjson-generated serde accepts. Go's equivalent describes what protojson.Unmarshal accepts, and the two mappings agree on everything below. Where pbjson differs, call it out in the PR.
Rules (same as Go; read the doc comments in schema.go for the reasoning)
- Messages:
{"type":"object","properties":{…},"additionalProperties":false}, withdescriptiontaken from the message's leading comment.- No
required(proto3 has no required fields). No field acceptsnull. - Property names are the field's
json_name(lowerCamel), never the snake_case proto name.
- No
- Fields:
descriptioncomes from the leading comment. For a member of a real (non-synthetic) oneof, appendPart of the "<oneof>" oneof: at most one of its fields may be set.A proto3optionalfield is just a normal property. - Scalars:
bool→ boolean;string→ string;bytes→{"type":"string","contentEncoding":"base64"}.- int32/sint32/sfixed32 → integer; uint32/fixed32 → integer with
minimum: 0. - int64/sint64/sfixed64 →
{"type":["integer","string"]}; uint64/fixed64 → the same plusminimum: 0. - float/double →
anyOfa number, or a string from the enumNaN,Infinity,-Infinity.
- Enums:
{"type":"string","enum":[value names…]}, with the enum's description. Names only, no numbers. - Repeated and maps: repeated →
{"type":"array","items":…}. map →{"type":"object","additionalProperties":<value schema>}, where the value schema comes from the map entry's field number 2. - Well-known types:
- Timestamp → string with
format: date-time; Duration → string; FieldMask → string. - Struct → object; Value →
{}; ListValue → array; Empty → object withadditionalProperties: false. - Any → object with required
@typestring and additional properties allowed. - Wrapper types → the schema of the scalar they wrap.
- Timestamp → string with
- Recursion: messages in a reference cycle become
$defsentries keyed by full name, referenced as{"$ref":"#/$defs/<full.name>"}. Everything else is inlined. If the root message is itself recursive, unwrap it so the root is still"type":"object"(seeMessageInputSchema). - Build the schema as a
serde_json::Valuewith sorted keys, i.e. without serde_json'spreserve_orderfeature, so the output is deterministic and orders keys like Go's.
Acceptance criteria
- Copy
schema.protointo this repo's test data, and port every test inschema_test.go, including the validation tests (TestSchemaValidates*,TestAnySchemaAllowsAdditionalProperties,TestSchemaValidatesNonFiniteFloats,TestMessageInputSchema_RecursiveRootMessage). Validate with thejsonschemacrate against pbjson-serialized messages, the counterpart of Go's protojson-marshaled ones.- Get descriptors with source info from
protoxin tests (a pure-Rust protobuf compiler, like Go'sprotocompile) or from protoc. Either way,cargo testmust not depend on network access.
- Get descriptors with source info from
- Parity: for each request message in
example.proto, the schema is JSON-equal (compare parsed values, not bytes, because Go's encoder HTML-escapes<>&) to theInputSchemain Go's committedexamples/gen/example/v1/example_mcp.pb.go. - Any pbjson/protojson difference you find (e.g. an
Anyencoding) is listed in the PR, with a test that pins down the current behavior. - The checks in
AGENTS.mdpass.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
factory:in-progressAn sf-factory worker is on itAn sf-factory worker is on itfactory:readyReady for the sf-factory to pick upReady for the sf-factory to pick up
Activity
erikist commentedon Oct 9, 2026
🤖 Factory: picked up by dispatcher; working on branch factory/5-feat-generate-json-schema-for-tool.
erikist commentedon Oct 9, 2026
🤖 Factory: opened PR #18 implementing this issue: #18
Summary: ports Go's
schema.goto a newprotoc_gen_rust_mcp::schemamodule, with the vendoredschemapb/schema.prototest data, all ofschema_test.go's cases (including the property/validation tests), and a parity test against Go's committedexample_mcp.pb.goinput schemas. Also documents and tests two pbjson/protojson differences found along the way:google.protobuf.Anyandgoogle.protobuf.FieldMaskboth get ordinary (not protojson-special-cased) message serde from pbjson, unlike Timestamp/Duration/Struct/Value/ListValue/the wrappers.All of
AGENTS.md's checks pass (cargo fmt,cargo clippy -D warnings,cargo build --workspace,cargo test --workspace), andmake generatestill produces no diff inexamples/src/gen(this module isn't wired into the generator yet — that's issue #6). See the PR body for full details and test evidence.fix(schema): address review feedback on JSON Schema generation (#5)
feat: comment processing, description fallback and file header (#4) (#19
refactor(schema): use #19's shared comment helpers (#5)
feat: generate JSON Schema for tool inputs from proto descriptors (#5) (
feat: generate MCP tool definitions and server registration API (#6)
feat: generate MCP tool definitions and the server registration API (#6…