protoc-gen-jsonschema
Health Warn
- License — License: MIT
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 6 GitHub stars
Code Pass
- Code scan — Scanned 12 files during light audit, no dangerous patterns found
Permissions Pass
- Permissions — No dangerous permissions requested
No AI report is available for this listing yet.
Define MCP tool JSON Schemas in Protobuf — a protoc/buf plugin & Go library that generates JSON Schema from proto, with constraints
protoc-gen-jsonschema
Define MCP tool schemas in Protobuf. Convert Protocol Buffers messages into JSON Schema — as a Go library (dynamic) or a protoc/buf plugin (static).
简体中文 · English
Table of Contents
- Features
- Install
- Quick start
- Plugin options
- Schema options
- Using with an LLM
- Performance
- Development
- License
Features
- Define MCP tool schemas in proto — generate the JSON Schema an MCP server
advertises as a tool'sinputSchemastraight from your message definitions. - Define JSON Schema constraints with Protobuf extension options.
- Two modes: dynamic generation (library) and static generation (plugin).
- Field-level and message-level options.
- Static output as JSON files or zero-overhead Go constants.
- High performance: ~8 μs dynamic, ~0.3 ns static access.
Install
One-line install (no Go toolchain required):
curl -fsSL https://raw.githubusercontent.com/sunerpy/protoc-gen-jsonschema/main/scripts/install.sh | sh
Windows (PowerShell):
irm https://raw.githubusercontent.com/sunerpy/protoc-gen-jsonschema/main/scripts/install.ps1 | iex
Override the version or destination:
PGJ_VERSION=0.0.7 PGJ_INSTALL_DIR=/usr/local/bin \
curl -fsSL https://raw.githubusercontent.com/sunerpy/protoc-gen-jsonschema/main/scripts/install.sh | sh
Prebuilt binary (no pipe-to-shell):
Download a prebuilt archive for your platform from the
Releases page,
extract it, and put protoc-gen-jsonschema on your PATH.
Go library:
go get github.com/sunerpy/protoc-gen-jsonschema
Plugin binary via Go:
go install github.com/sunerpy/protoc-gen-jsonschema/cmd/protoc-gen-jsonschema@latest
Make sure $(go env GOPATH)/bin is on your PATH.
Proto extensions via the Buf Schema Registry (BSR):
The extension definitions are published atbuf.build/sunerpy/protoc-gen-jsonschema.
Add it as a dependency in your buf.yaml:
version: v2
deps:
- buf.build/sunerpy/protoc-gen-jsonschema
Then run buf dep update.
Quick start
As a Go library (dynamic)
Annotate a message:
syntax = "proto3";
package example;
import "mcp/jsonschema/jsonschema.proto";
message UserRequest {
option (mcp.jsonschema.title) = "User Request";
string email = 1 [
(mcp.jsonschema.required) = true,
(mcp.jsonschema.format) = "email"
];
string name = 2 [
(mcp.jsonschema.required) = true,
(mcp.jsonschema.min_length) = 3,
(mcp.jsonschema.max_length) = 50
];
int32 age = 3 [
(mcp.jsonschema.minimum) = 18,
(mcp.jsonschema.maximum) = 120
];
}
Generate at runtime:
package main
import (
"encoding/json"
"fmt"
"log"
pb "example/pb"
"github.com/sunerpy/protoc-gen-jsonschema"
)
func main() {
schema, err := jsonschema.GenerateFromMessage(&pb.UserRequest{})
if err != nil {
log.Fatal(err)
}
out, _ := json.MarshalIndent(schema, "", " ")
fmt.Println(string(out))
}
As a protoc/buf plugin (static)
JSON files (default):
protoc --jsonschema_out=. user.proto
Go constants (zero-overhead, recommended for hot paths):
protoc --go_out=. --jsonschema_out=format=go_const:. user.proto
buf.gen.yaml:
version: v2
plugins:
- remote: buf.build/protocolbuffers/go
out: pb
opt:
- paths=source_relative
- local: protoc-gen-jsonschema
out: pb
opt:
- format=go_const
- paths=source_relative
The generated *_jsonschema.pb.go exposes zero-overhead accessors:
msg := &pb.UserRequest{}
schema := msg.GetJSONSchema() // string, ~0.29 ns
bytes := msg.GetJSONSchemaBytes() // []byte, HTTP-ready
raw := msg.GetJSONSchemaRawMessage() // json.RawMessage
Plugin options
| Option | Default | Description |
|---|---|---|
format |
json |
Output format: json or go_const. |
suffix |
_jsonschema |
Go file suffix (go_const only). |
paths |
— | source_relative or import. |
preserve_order |
false |
Preserve proto field order in the schema. |
schema_struct |
false |
Also emit a jsonschema.Schema struct literal. |
google_schema |
false |
Also emit a github.com/google/jsonschema-go struct literal. |
Schema options
Field options (mcp.jsonschema.*):
| Option | Type | Description |
|---|---|---|
required |
bool | Mark the field as required. |
description |
string | Field description. |
example |
string | Example value. |
format |
string | Format constraint (e.g. email, date-time). |
pattern |
string | Regular expression. |
min_length / max_length |
int32 | String length bounds. |
minimum / maximum |
double | Numeric bounds. |
default |
string | Default value (JSON-encoded). |
hidden |
bool | Exclude the field from the schema. |
json_name |
string | Override the JSON field name. |
Message options (mcp.jsonschema.*):
| Option | Type | Description |
|---|---|---|
title |
string | Schema title. |
message_description |
string | Schema description. |
generate_schema |
bool | Set false to skip generation for this message. |
Note on
google.protobuf.Timestamp: when a Timestamp appears as a field, the
generated schema uses aoneOfaccepting both an RFC3339 string and a{seconds, nanos}object. The proto runtime (protojson) itself only accepts
the RFC3339 string form; the object branch targets generic JSON consumers.
Using with an LLM
The Model Context Protocol (MCP) requires each
tool to advertise an inputSchema — a JSON Schema describing its arguments. This
plugin lets you define that schema in Protobuf and generate it, so your proto
message is the single source of truth for the tool contract: constraints
(required, format, pattern, min_length, minimum, …) become JSON Schema
keywords the LLM uses to produce valid arguments.
message SearchToolArgs {
option (mcp.jsonschema.title) = "search";
option (mcp.jsonschema.message_description) = "Full-text search over the corpus";
string query = 1 [
(mcp.jsonschema.required) = true,
(mcp.jsonschema.min_length) = 1,
(mcp.jsonschema.description) = "The search query"
];
int32 limit = 2 [
(mcp.jsonschema.minimum) = 1,
(mcp.jsonschema.maximum) = 100,
(mcp.jsonschema.example) = "10"
];
}
Generate the schema once at build time, then hand GetJSONSchemaBytes() to your
MCP server as the tool's inputSchema — no hand-written, drift-prone JSON.
Install in one line, then drive the plugin from an agent:
curl -fsSL https://raw.githubusercontent.com/sunerpy/protoc-gen-jsonschema/main/scripts/install.sh | sh
protoc --jsonschema_out=. user.proto— emit JSON Schema files for the messages.protoc --go_out=. --jsonschema_out=format=go_const:. user.proto— emit
zero-overhead Go constants (GetJSONSchema()/GetJSONSchemaBytes()), ideal
as an MCP tool'sinputSchema.- For runtime use, call
jsonschema.GenerateFromMessage(msg)— returns a schema
ready tojson.Marshal.
The generated schema is plain JSON (machine-readable); the plugin exits non-zero
with diagnostics on stderr when a message or option is invalid.
Performance
Static access is ~29,000× faster than dynamic generation, and a JSON string
constant is HTTP-ready with zero serialization cost. See
docs/PERFORMANCE.md for the full comparison.
Development
make help # list targets
make fmt # format Go + non-Go files
make lint # golangci-lint
make test # run tests
make buf-lint # lint proto files
make check # fmt-check + lint + buf-lint + test
make hooks # install git hooks (fmt+lint on commit, test on push)
Commit messages follow Conventional Commits
— feat:, fix:, feat!: for breaking changes — which drive automated version
bumps and the changelog.
Releases are automated via release-please:
merging the release PR cuts a v* tag, which triggers GoReleaser to build
multi-platform binaries and publish the GitHub Release. The proto module is
published to the BSR via buf push. The in-repo changelog lives inchangelog/, split by major version.
License
Reviews (0)
Sign in to leave a review.
Leave a reviewNo results found