gRPC Transport
NEKTE supports native gRPC as a transport for high-throughput and polyglot environments. The gRPC transport uses Protobuf wire format and server-streaming for delegate.
Setup
gRPC dependencies are optional peer dependencies — they are only loaded when you use createGrpcTransport:
pnpm add @grpc/grpc-js @grpc/proto-loaderServer
import { NekteServer, createGrpcTransport } from '@nekte/server';
const server = new NekteServer({ agent: 'fast-agent', version: '1.0.0' });
server.capability('sentiment', { inputSchema: z.object({ text: z.string() }), outputSchema: z.object({ score: z.number() }), category: 'nlp', description: 'Analyze text sentiment', handler: async (input, ctx) => ({ score: 0.9 }), toMinimal: (out) => `score: ${out.score}`,});
// HTTP on 4001, gRPC on 4002server.listen(4001);const grpc = await createGrpcTransport(server, { port: 4002 });Client
import { NekteClient, createGrpcClientTransport } from '@nekte/client';
const transport = await createGrpcClientTransport({ endpoint: 'localhost:4002',});
const client = new NekteClient('grpc://localhost:4002', { transport });
// Same API as HTTP -- transport is transparentconst catalog = await client.catalog();const result = await client.invoke('sentiment', { input: { text: 'Great!' }, budget: { max_tokens: 50, detail_level: 'minimal' },});Streaming over gRPC
delegate uses gRPC server-streaming instead of SSE:
// Same client API -- transport handles the differenceconst stream = client.delegateStream({ id: 'task-001', desc: 'Analyze reviews', timeout_ms: 30_000,});
for await (const event of stream.events) { // events arrive via gRPC server-streaming instead of SSE if (event.event === 'progress') console.log(event.data.processed); if (event.event === 'complete') console.log(event.data.out);}
// Cancel works the same wayawait stream.cancel('Done early');Proto Definition
The full proto file is at @nekte/core/proto/nekte.proto:
service Nekte { rpc Discover(DiscoverRequest) returns (DiscoverResponse); rpc Invoke(InvokeRequest) returns (InvokeResponse); rpc Delegate(DelegateRequest) returns (stream DelegateEvent); rpc Context(ContextRequest) returns (ContextResponse); rpc Verify(VerifyRequest) returns (VerifyResponse); rpc TaskCancel(TaskCancelRequest) returns (TaskLifecycleResponse); rpc TaskResume(TaskResumeRequest) returns (TaskLifecycleResponse); rpc TaskStatus(TaskStatusRequest) returns (TaskStatusResponse);}Key design decisions:
Delegateuses server-streaming (replaces SSE)- Dynamic JSON fields use
bytestype to preserve flexibility - An anti-corruption layer converts between proto messages and domain types
- The proto definitions live in
@nekte/coreso all packages can reference them
When to Use gRPC
Use gRPC when:
- You need high throughput (thousands of invocations/second)
- Your agents are in different languages (Python, Go, Java can use the same proto)
- You want Protobuf wire efficiency on top of NEKTE’s token efficiency
- You are in a microservices environment that already uses gRPC
Stick with HTTP when:
- You need browser compatibility (gRPC requires HTTP/2)
- Your setup is simple (single agent, low volume)
- You are behind proxies that do not support HTTP/2
Wire Size Comparison
| Payload | JSON (HTTP) | Protobuf (gRPC) | Savings |
|---|---|---|---|
| L0 discovery (3 caps) | 245 bytes | ~150 bytes | -39% |
| Invoke request | 189 bytes | ~115 bytes | -39% |
| Invoke response | 156 bytes | ~95 bytes | -39% |
| Delegate event | 134 bytes | ~82 bytes | -39% |
TLS
For production deployments, use TLS:
// Serverconst grpc = await createGrpcTransport(server, { port: 4002, credentials: grpcJs.ServerCredentials.createSsl( rootCert, [{ cert_chain: serverCert, private_key: serverKey }] ),});
// Clientconst transport = await createGrpcClientTransport({ endpoint: 'localhost:4002', credentials: grpcJs.credentials.createSsl(rootCert),});