This pull request introduces major improvements to the `granc` gRPC CLI, focusing on enhanced introspection and discovery features, a more user-friendly command-line interface, and improved error and output formatting. The changes include new commands for listing and describing services, methods, and messages, a restructured CLI argument parser, and a new formatter for colored, readable output. Additionally, the core client is extended to support these new features, and error handling is refactored for clarity. **New CLI features and UX improvements:** * Added new `list` and `describe` commands to the CLI, allowing users to discover available services and inspect service/message definitions directly from the server using reflection. The CLI argument structure is now subcommand-based for better usability. [[1]](diffhunk://#diff-dfa67e7f5e147119fe8d665da6b31b3605f5e196734ec7407aab2bcc9e2f656cL8-R84) [[2]](diffhunk://#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5L71-R139) * Updated the README with documentation for the new commands and improved usage instructions. [[1]](diffhunk://#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5R21) [[2]](diffhunk://#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5L71-R139) **Core client and reflection enhancements:** * Implemented new methods in the core client for listing services and fetching symbol descriptors via reflection, including robust error types for each operation. [[1]](diffhunk://#diff-46d757daaa6737f1a6247142e8abff1cb5079109e641c447e8a9793ea1f063adL25-R27) [[2]](diffhunk://#diff-46d757daaa6737f1a6247142e8abff1cb5079109e641c447e8a9793ea1f063adL38-R78) [[3]](diffhunk://#diff-46d757daaa6737f1a6247142e8abff1cb5079109e641c447e8a9793ea1f063adR149-R211) [[4]](diffhunk://#diff-46d757daaa6737f1a6247142e8abff1cb5079109e641c447e8a9793ea1f063adL125-R229) [[5]](diffhunk://#diff-46d757daaa6737f1a6247142e8abff1cb5079109e641c447e8a9793ea1f063adL154-R252) [[6]](diffhunk://#diff-46d757daaa6737f1a6247142e8abff1cb5079109e641c447e8a9793ea1f063adL164-R262) [[7]](diffhunk://#diff-13deee04dd97de938cc46f0ef4faca083f3b471800e94cf45937122b83f01d57R19) [[8]](diffhunk://#diff-13deee04dd97de938cc46f0ef4faca083f3b471800e94cf45937122b83f01d57R124-R161) **Output formatting and error handling:** * Added a new `formatter` module for producing colored, human-friendly output for all major CLI operations, including pretty-printing of service lists, descriptors, and errors. * Improved error handling throughout the client and CLI, with more specific error types and user-facing messages. [[1]](diffhunk://#diff-46d757daaa6737f1a6247142e8abff1cb5079109e641c447e8a9793ea1f063adL38-R78) [[2]](diffhunk://#diff-0de6f761cf394791a15b0707e1a41f54559b5626f7aedb06ef339bc1a7ca6287R1-R248) **Dependency and project structure updates:** * Updated dependencies and added the `colored` crate for output styling. |
||
|---|---|---|
| .github | ||
| echo-service | ||
| granc | ||
| granc-core | ||
| granc-tools | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| CHANGELOG.md | ||
| LICENSE | ||
| Makefile.toml | ||
| README.md | ||
| release-plz.toml | ||
| rust-toolchain.toml | ||
| yamlfmt.yml | ||
Granc 🦀
⚠️ Status: Experimental
This project is currently in a highly experimental phase. It is a working prototype intended for testing and development purposes. APIs, command-line arguments, and internal logic are subject to breaking changes. Please use with caution.
Granc (gRPC + Cranc, Crab in Catalan) is a lightweight, dynamic gRPC CLI tool written in Rust.
It allows you to make gRPC calls to any server using simple JSON payloads, without needing to compile the specific Protobuf files into the client. By loading a FileDescriptorSet at runtime, granc acts as a bridge between human-readable JSON and binary Protobuf wire format.
It is heavily inspired by tools like grpcurl but built to leverage the safety and performance of the Rust ecosystem (Tonic + Prost).
🚀 Features
- Dynamic Encoding/Decoding: Transcodes JSON to Protobuf (and vice versa) on the fly using
prost-reflect. - Smart Dispatch: Automatically detects if a call is Unary, Server Streaming, Client Streaming, or Bidirectional based on the descriptor.
- Server Reflection: Can fetch schemas directly from the server, removing the need to pass a local file descriptor set file (
.binor.pb). - Metadata Support: Easily attach custom headers (authorization, tracing) to your requests.
- Fast Fail Validation: Validates your JSON before hitting the network.
- Introspection Tools: Commands to list services and describe services/messages.
- Zero Compilation Dependencies: Does not require generating Rust code for your protos. Just point to a descriptor file.
- Tonic 0.14: Built on the latest stable Rust gRPC stack.
📦 Installation
cargo install --locked granc
🛠️ Prerequisites
Granc needs to know the schema of the service you are calling. It can obtain this in two ways:
- Automatic Server Reflection: If the server has Server Reflection enabled, Granc can download the schema automatically.
- Local Descriptor File: You can provide a binary
FileDescriptorSet(.bin) generated byprotoc.
Generating Descriptors (Optional)
If your server does not support reflection, you must generate a descriptor file:
# Generate descriptor.bin including all imports
protoc \
--include_imports \
--descriptor_set_out=descriptor.bin \
--proto_path=. \
my_service.proto
Note
: The
--include_importsflag is crucial. It ensures that types defined in imported files (likegoogle/protobuf/timestamp.proto) are available for reflection.
📖 Usage
Syntax:
granc <URL> <COMMAND> [ARGS]
Global Arguments
| Argument | Description | Required |
|---|---|---|
<URL> |
Server address (e.g., http://[::1]:50051). Must be the first argument. |
Yes |
Commands
1. call (Make Requests)
Performs a gRPC call using a JSON body.
granc http://localhost:50051 call <ENDPOINT> --body <JSON> [OPTIONS]
| Argument/Flag | Description | Required |
|---|---|---|
<ENDPOINT> |
Fully qualified method name (e.g., my.package.Service/Method). |
Yes |
--body |
The request body in JSON format. Object {} for unary, Array [] for streaming. |
Yes |
--header, -H |
Custom header key:value. Can be used multiple times. |
No |
--file-descriptor-set |
Path to the binary FileDescriptorSet (.bin) if not using reflection. |
No |
JSON Body Format
- Unary / Server Streaming: Provide a single JSON object
{ ... }. - Client / Bidirectional Streaming: Provide a JSON array of objects
[ { ... }, { ... } ].
Automatic Server Reflection
If you omit the --file-descriptor-set flag, Granc will automatically attempt to connect to the server's reflection service to download the necessary schemas.
granc http://localhost:50051 call --body '{"name": "Ferris"}' helloworld.Greeter/SayHello
This requires the server to have the grpc.reflection.v1 service enabled.
2. list (Service Discovery) (Server reflection required)
Lists all services exposed by the server.
granc http://localhost:50051 list
3. describe (Introspection) (Server reflection required)
Inspects services, messages or enums and prints their Protobuf definition.
Describe Service:
Describe in detail all methods of a service.
granc http://localhost:50051 describe my.package.Greeter
Describe Message:
Shows the fields of a specific message type.
granc http://localhost:50051 describe my.package.HelloRequest
🔮 Roadmap
- Interactive Mode: A REPL for streaming requests interactively.
- Pretty Printing JSON: Enhanced colored output for JSON responses.
- TLS Support: Configurable root certificates and client identity.
🧩 Using as a Library
The core logic of Granc is decoupled into a separate library crate, granc-core.
If you want to build your own tools using the dynamic gRPC engine (e.g., for custom integration testing, proxies, or automation tools), you can depend on granc-core directly.
- Documentation & Usage: See the
granc-coreREADME for examples on how to use theGrancClientprogrammatically. - Crate:
granc-core
⚠️ Common Errors
**1. Service 'x' not found**
- Cause: The service name in the command does not match the package defined in your proto file.
- Fix: Check your
.protofile. If it haspackage my.app;andservice API {}, the full name ismy.app.API.
**2. Method 'y' not found in service 'x'**
- Cause: Typo in the method name or the method doesn't exist.
- Fix: Ensure case sensitivity matches (e.g.,
GetUservsgetUser).
**3. h2 protocol error**
- Cause: This often occurs when the JSON payload fails to encode after the connection has already been established, or the server rejected the stream structure.
- Fix: Double-check your JSON payload against the Protobuf schema.
🤝 Contributing
Contributions are welcome! Please run the Makefile checks before submitting a PR:
cargo make ci # Checks formatting, lints, and runs tests
📄 License
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT license (LICENSE-MIT)
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.