Skip to main content
You have seen how the TypeScript SDK works. Now imagine doing the same thing in Rust, Go, Swift, or any language that has gRPC support. That is what this page is for. The key insight is that the goal is not to rewrite the core. The goal is to build a new driver that can plug into the same engine. Because the sidecar keeps infrastructure centralized, each new language only needs a thin adapter — usually 200–400 lines of actual logic.
The architectural rule is simple: new languages get new drivers, not new engines.

What an SDK Does

Before we dive into the steps, it helps to understand the four responsibilities every SDK shares. Under the hood, the SDK does four things:
  1. Implements AgentHandler — a gRPC server that receives HandleMessages calls from the core and invokes the developer’s handler
  2. Calls BinduService.RegisterAgent — a gRPC client that registers the agent with the core
  3. Launches the Python core — spawns bindu serve --grpc as a child process
  4. Exposes bindufy(config, handler) — the developer-facing API that orchestrates all of the above
The proto contract at proto/agent_handler.proto is the single source of truth. As long as your SDK speaks that contract, it works with the core.

Step 1: Generate gRPC Stubs

Before your SDK can talk to the core, it needs to speak the same language. The first job is to generate typed client and server stubs from the protobuf definition. Every gRPC ecosystem has its own tooling for this:
LanguageToolCommand
Rusttonic-buildAdd tonic-build to build.rs, it compiles the proto at build time
Goprotoc-gen-go-grpcprotoc --go_out=. --go-grpc_out=. proto/agent_handler.proto
Swiftgrpc-swiftprotoc --swift_out=. --grpc-swift_out=. proto/agent_handler.proto
C#Grpc.ToolsNuGet package auto-generates from .proto in the project
The generated code gives you typed messages and service interfaces. Everything else in the SDK is built on top of those stubs.

Step 2: Implement AgentHandler (Server)

This is the server that the core will call when work arrives. It is the language-specific half of the sidecar — how the engine talks back to the driver. Of the three RPCs, HandleMessages is the critical one.

HandleMessages / HandleMessagesStream

This RPC receives conversation history, calls the developer’s handler, and returns the response. Here is the shape of the request and response:
Input:  HandleRequest { messages: [ChatMessage{role, content}, ...] }
Output: HandleResponse {
  content: string,
  state: string,
  prompt: string,
  is_final: bool,
  metadata: map<string, string>
}
The rules for converting your developer’s handler return value into a HandleResponse are straightforward:
  • If the handler returns a plain string, set content to the string and leave state empty.
  • If the handler returns a state transition, set state to "input-required" or "auth-required" and prompt to the follow-up question.
  • If the handler throws, return a gRPC INTERNAL error with the error message.
  • Unary path (HandleMessages): Set is_final to true (there is only one response).
  • Streaming path (HandleMessagesStream): Yield multiple HandleResponse chunks with is_final: false, and set is_final: true on the last chunk.
  • Both paths use the exact same HandleResponse message shape.

GetCapabilities

Return static info about the SDK. This is how the core discovers what your agent supports.
Output: GetCapabilitiesResponse { name, description, version, supports_streaming }

HealthCheck

Return {healthy: true, message: "OK"}. The core uses this during heartbeat processing to verify your SDK is still responsive.

Step 3: Implement BinduService Client

Now build the other direction — the client that talks from your SDK into the core. This is how your SDK registers the agent and keeps it alive.

RegisterAgent

This is the main entry point. It sends config, skills, and the SDK’s callback address:
Input: RegisterAgentRequest {
  config_json: string,            // Full config as JSON
  skills: [SkillDefinition],      // Skills with raw file content
  grpc_callback_address: string   // e.g., "localhost:50052"
}
Output: RegisterAgentResponse { success, agent_id, did, agent_url, error }
The config_json is a JSON string matching the Python bindufy() config format. That is intentional. The schema lives in one place, and SDKs serialize to it.

Heartbeat

Call this every 30 seconds to signal liveness. Without it, the core has no way to know whether your SDK process is still running.
Input: HeartbeatRequest { agent_id, timestamp }
Together, RegisterAgent and Heartbeat are the minimum control plane for the sidecar.

Step 4: Implement Core Launcher

The SDK must be able to start the engine automatically. This is what makes the developer experience feel local even though the architecture is split. Without it, the developer would need to manually start the Python core in a separate terminal — and that would break the illusion of a single-process experience.
Pass the executable and arguments as a properly split array to your language’s process spawner — not as a single shell string.
The detection logic tries three approaches in order:
Pass the executable and arguments as a properly split array to your language’s process spawner — not as a single shell string.
1

Check if bindu CLI is available

Run bindu with args ["serve", "--grpc", "--grpc-port", "3774"]
2

If not, check if uv is available

Run uv with args ["run", "bindu", "serve", "--grpc", "--grpc-port", "3774"]
3

If not, fall back to Python module

Run python3 with args ["-m", "bindu.cli", "serve", "--grpc", "--grpc-port", "3774"]
4

Wait for :3774 to accept TCP connections

Poll every 500ms, timeout 30s.
5

On parent exit (Ctrl+C)

Kill the child process cleanly.

Step 5: Implement bindufy()

At this point, you have all the pieces. The last step is to wire them behind one developer-facing function. This is the moment where your SDK goes from “a collection of gRPC plumbing” to “a one-line developer experience.”
function bindufy(config, handler):
    skills = read_skill_files(config.skills)
    callback_port = start_agent_handler_server(handler)
    launch_python_core(grpc_port=3774)
    wait_for_port(3774)
    result = register_agent(config, skills, callback_address="localhost:{callback_port}")
    start_heartbeat_loop(result.agent_id)
    print("Agent registered! A2A URL: {result.agent_url}")
That is the whole idea. The SDK should feel small because the core already does the heavy lifting.

Skill Loading

Skills live in the developer’s project, not in the core. Your SDK is responsible for reading them and shipping their contents to the core during registration. This way the core does not need filesystem access to the SDK’s project directory.
1

For each skill path in config.skills

Look for skill.yaml or SKILL.md.
2

Read the file content

Load the raw file data.
3

Parse the metadata

Extract fields from YAML frontmatter or YAML body.
4

Send as SkillDefinition

{ name, description, tags, input_modes, output_modes, version, author, raw_content, format }

Testing Your SDK

The fastest way to keep an SDK honest is to test both the transport and the sidecar lifecycle. Start with unit tests to verify serialization, then work up to integration tests that exercise the full round trip.
Mock the gRPC channel and verify HandleMessages correctly invokes the handler and serializes the response.
Start a real Bindu core with bindu serve --grpc, register an agent from your SDK, send an A2A message, and verify the response. The Python E2E tests in tests/integration/grpc/test_grpc_e2e.py show exactly this pattern.
Run one of the examples end to end and curl the agent.

Reference: TypeScript SDK

If you want a concrete model to follow, the TypeScript SDK is the reference implementation. It is intentionally small — most of the complexity lives in the core, not in the SDK.
FileWhat it doesLines
src/index.tsbindufy() function + skill loader~220
src/server.tsAgentHandler gRPC server~130
src/client.tsBinduService gRPC client~105
src/core-launcher.tsSpawns Python core~170
src/types.tsTypeScript interfaces~120
Total: ~745 lines. Most of that is type definitions and error handling. The core logic is under 300 lines.

TypeScript SDK

See how the reference driver behaves from the developer’s point of view

API Reference

Review the protobuf messages and service contracts directly

Publishing

Once your SDK works, publish it to the language’s standard package registry so others can use it:
LanguageRegistryPackage name convention
Rustcrates.iobindu-sdk
GoGo modulesgithub.com/getbindu/bindu-sdk-go
SwiftSwift Package Managerbindu-sdk
C#NuGetBindu.Sdk
Include the proto file in the package so users do not need to download it separately.
Sunflower LogoBecause the infrastructure is entirely decoupled, building a new SDK is just a matter of satisfying the gRPC contract, not rewriting the core engine.