Scan, Extract & Call
Stop typing numbers manually. Point your camera at business cards, docs, or screens to extract and dial numbers instantly.
Get Scan2Call 📱AI integrations are undergoing a massive structural shift. Applications have progressed beyond basic conversational wrappers to active, autonomous agent runtimes. Tools like Cursor, GitHub Copilot Agent Skills, and custom LangGraph/Autogen frameworks construct dynamic plans and execute system-level operations directly. To perform meaningful tasks, these agents require deep, bi-directional context from enterprise database schemas, storage nodes, and real-time transaction streams.
This access pattern presents a critical security vulnerability. Directly linking an LLM-driven runtime to production persistence layers or messaging brokers bypasses core network security, authorization loops, and structural validation steps. If an agent hallucinates a toxic command or falls victim to an indirect prompt injection attack, it can execute destructive raw commands against databases like MySQL or emit invalid events to system brokers like Apache Kafka.
To eliminate this threat vector without reducing agent speed, enterprises require a decoupled, high-performance middleware architecture: a distributed Model Context Protocol (MCP) Gateway. By placing a structured gateway layer between the model runtime and physical services, you enforce structural command authorization, validate execution patterns, stream progressive telemetry, and maintain strict data-isolation boundaries.
The Model Context Protocol (MCP) is an open-source standard designed to declare context inputs and action tools to LLMs via standard JSON-RPC 2.0 frames over secure transports (such as standard input/output streams or Server-Sent Events). The architecture described below scales this standard to enterprise-grade operations by separating host management from execution environments:
Client Layer: The agent executor, such as Cursor, Claude Desktop, or an enterprise wrapper running on GCP or AWS. It communicates with the gateway using JSON-RPC over stdio or HTTP/SSE.
Gateway Layer (NestJS): Functions as the unified model context protocol server typescript implementation. This host parses JSON-RPC 2.0 requests, authorizes active client sessions, performs rate-limiting, and converts standard MCP tool schemas into fast, multiplexed gRPC payloads. When organizations look to scale these complex gateways, they frequently choose to hire typescript developer engineers who are highly skilled in NestJS microservices and gRPC clients.
Microservice Layer (Golang): A lightweight worker pool running gRPC microservices go engines. These Go workers receive structured request schemas, execute high-concurrency queries against isolated MySQL read-replicas, and stream raw output segments back to the gateway.
Event-Driven Broker (Apache Kafka): Asynchronously records all agent actions, system states, and query responses to construct immutable transaction audit logs.
For a deeper dive into structuring the underlying data storage and message handling for this architecture, explore our articles on Architecting Multi-Tenant SaaS Databases in MySQL & TypeScript and Architecting Event-Driven Microservices: NestJS, Kafka & GCP.
This hybrid approach is highly efficient. TypeScript excels at processing asynchronous JSON structures, managing session lifecycles, and keeping pace with the rapidly evolving MCP SDK ecosystem. Conversely, Golang provides unmatched performance for low-level system processing, direct network socket operations, and highly parallel database query execution.
To run distributed transactions, the interface between the NestJS gateway and the Go workers must be strictly typed, low-latency, and support streaming protocols. Protocol Buffers (Protobuf) over HTTP/2 provide multiplexed streams that avoid the performance overhead of traditional REST-based REST/JSON APIs.
Below is the complete Protobuf schema (mcp_bridge.proto). It defines tools for running structured queries and streaming long-running agent workflows progressively to prevent model context timeouts.
syntax = "proto3";
package mcp.bridge.v1;
option go_package = "github.com/staksoft/mcp-bridge/gen/v1;bridgev1";
service MCPBridgeService {
// Standard unary RPC for fast database queries
rpc ExecuteTool (ToolRequest) returns (ToolResponse);
// Server streaming RPC for progressive, token-by-token or chunked output
rpc StreamToolOutput (ToolRequest) returns (stream ToolStreamResponse);
}
message ToolRequest {
string tool_name = 1;
string arguments_json = 2;
string session_id = 3;
}
message ToolResponse {
bool success = 1;
string output_json = 2;
string error_message = 3;
}
message ToolStreamResponse {
string chunk = 1;
int32 progress_percentage = 2;
}This definition decouples the underlying storage engines from the client interfaces. The NestJS gateway does not require any knowledge of database mechanics; it simply acts as an orchestrator, passing string-encoded JSON payloads directly to Go workers over high-speed gRPC channels.
The Go microservice executes database reads against a MySQL instance, logs asynchronous tracing telemetry to Kafka, and returns clean results to the NestJS gateway. Building Go microservices with robust gRPC integration allows systems to process millions of operations with minimal memory consumption.
An primary challenge when executing agent-generated inputs is preventing SQL injection and LLM hallucination exploits (such as agents trying to read system configuration tables or drop key records). To prevent this, the Go microservice acts as a strict guardrail: it uses strict JSON unmarshalling, enforces query argument whitelisting, and uses structured, parameterized queries exclusively. This approach isolates agent execution streams from the raw database engine.
To ensure queries are fully optimized and do not degrade production performance, enterprises often hire mysql developer specialists to handle query tuning, layout indexing schemes, and manage secure connection pooling inside microservices. The code below illustrates a secure Go service implementing these exact patterns:
package main
import (
"context"
"database/sql"
"encoding/json"
"errors"
"fmt"
"log"
"net"
"strings"
"time"
"github.com/IBM/sarama"
_ "github.com/go-sql-driver/mysql"
"google.com/grpc"
"google.com/grpc/codes"
"google.com/grpc/status"
pb "github.com/staksoft/mcp-bridge/gen/v1"
)
type BridgeServer struct {
pb.UnimplementedMCPBridgeServiceServer
db *sql.DB
kafkaProd sarama.SyncProducer
}
type QueryArgs struct {
Limit int `json:"limit"`
Filter string `json:"filter"`
}
func (s *BridgeServer) ExecuteTool(ctx context.Context, req *pb.ToolRequest) (*pb.ToolResponse, error) {
if req.ToolName != "query_inventory" {
return nil, status.Errorf(codes.InvalidArgument, "Unsupported tool: %s", req.ToolName)
}
var args QueryArgs
if err := json.Unmarshal([]byte(req.ArgumentsJson), &args); err != nil {
return nil, status.Errorf(codes.InvalidArgument, "Invalid arguments JSON: %v", err)
}
// Direct injection mitigation: Enforce strict whitelist validation
if err := validateFilter(args.Filter); err != nil {
return nil, status.Errorf(codes.InvalidArgument, "Policy violation: %v", err)
}
if args.Limit <= 0 || args.Limit > 100 {
args.Limit = 10
}
// Execute parameterized query against isolated MySQL replica
rows, err := s.db.QueryContext(ctx, "SELECT id, name, sku, qty FROM inventory WHERE name LIKE ? LIMIT ?", "%"+args.Filter+"%", args.Limit)
if err != nil {
return nil, status.Errorf(codes.Internal, "Database execution failed: %v", err)
}
defer rows.Close()
var results []map[string]interface{}
for rows.Next() {
var id int
var name, sku string
var qty int
if err := rows.Scan(&id, &name, &sku, &qty); err != nil {
return nil, status.Errorf(codes.Internal, "Row scan error: %v", err)
}
results = append(results, map[string]interface{}{
"id": id,
"name": name,
"sku": sku,
"qty": qty,
})
}
outputBytes, _ := json.Marshal(results)
// Fire-and-forget logging to Kafka partition for audit trails
go s.logAudit(req.SessionId, req.ToolName, string(outputBytes))
return &pb.ToolResponse{
Success: true,
OutputJson: string(outputBytes),
}, nil
}
func validateFilter(filter string) error {
badTokens := []string{";", "DROP", "DELETE", "UPDATE", "UNION", "--"}
for _, token := range badTokens {
if strings.Contains(strings.ToUpper(filter), token) {
return errors.New("forbidden sequence detected in query filter")
}
}
return nil
}
func (s *BridgeServer) logAudit(session, tool, out string) {
msg := &sarama.ProducerMessage{
Topic: "mcp-tool-audit",
Key: sarama.StringEncoder(session),
Value: sarama.StringEncoder(fmt.Sprintf("{\"tool\":\"%s\",\"ts\":%d,\"len\":%d}", tool, time.Now().Unix(), len(out))),
}
_, _, _ = s.kafkaProd.SendMessage(msg)
}
func main() {
listener, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("Failed to listen: %v", err)
}
// Initialize real MySQL handle with pool configuration
db, err := sql.Open("mysql", "mcp_read:safe_pass@tcp(127.0.0.1:3306)/enterprise_db")
if err != nil {
log.Fatalf("DB init failed: %v", err)
}
db.SetMaxOpenConns(50)
// Setup Kafka client
cfg := sarama.NewConfig()
cfg.Producer.Return.Successes = true
prod, err := sarama.NewSyncProducer([]string{"localhost:9092"}, cfg)
if err != nil {
log.Fatalf("Kafka connection error: %v", err)
}
srv := grpc.NewServer()
pb.RegisterMCPBridgeServiceServer(srv, &BridgeServer{db: db, kafkaProd: prod})
log.Println("gRPC MCP Tool Worker running on port :50051...")
if err := srv.Serve(listener); err != nil {
log.Fatalf("Failed to serve: %v", err)
}
}By enforcing clean boundaries at the SQL level, this worker protects the database from malicious execution paths. Even if an LLM is hijacked by malicious prompts, it can only query safe, parameter-bound tables, preserving database integrity.
The gateway serves as the primary system coordinator. It translates external MCP JSON-RPC requests into efficient gRPC client calls, manages backpressure, and returns standard payload schemas to the AI client.
For enterprise-scale architectures, utilizing TypeScript for host gateways ensures type safety across schema updates. High-growth product groups often look to hire typescript developer specialists to construct unified data structures, map protocol formats cleanly, and manage active system endpoints.
The following example shows how to configure the official @modelcontextprotocol/sdk to handle tool definitions, initialize gRPC client channels, and route requests securely:
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
import * as grpc from '@grpc/grpc-js';
import * as protoLoader from '@grpc/proto-loader';
import { join } from 'path';
@Injectable()
export class McpGatewayService implements OnModuleInit, OnModuleDestroy {
private mcpServer: Server;
private grpcClient: any;
async onModuleInit() {
this.initGrpcClient();
this.initMcpServer();
}
private initGrpcClient() {
const protoPath = join(__dirname, '../../proto/mcp_bridge.proto');
const packageDefinition = protoLoader.loadSync(protoPath, {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true,
});
const protoDescriptor = grpc.loadPackageDefinition(packageDefinition) as any;
const mcpService = protoDescriptor.mcp.bridge.v1.MCPBridgeService;
this.grpcClient = new mcpService(
'localhost:50051',
grpc.credentials.createInsecure() // Secure with mTLS in production environments
);
}
private initMcpServer() {
this.mcpServer = new Server(
{
name: 'staksoft-enterprise-mcp-gateway',
version: '1.0.0',
},
{
capabilities: {
tools: {},
},
}
);
this.setupMcpHandlers();
const transport = new StdioServerTransport();
this.mcpServer.connect(transport).catch(console.error);
}
private setupMcpHandlers() {
// Expose available tools to the LLM agent client
this.mcpServer.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: 'query_inventory',
description: 'Queries the enterprise database to check safe SKU levels and physical inventory.',
inputSchema: {
type: 'object',
properties: {
filter: { type: 'string', description: 'Substring match for item name' },
limit: { type: 'number', description: 'Maximum records to return (1-100)' },
},
required: ['filter'],
},
},
],
}));
// Handle tool execution requests from the LLM client
this.mcpServer.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name !== 'query_inventory') {
throw new Error(`Tool not found: ${name}`);
}
return new Promise((resolve) => {
this.grpcClient.ExecuteTool(
{
tool_name: name,
arguments_json: JSON.stringify(args),
session_id: 'session-id-token-999', // Propagate authorization context
},
(err: Error | null, response: any) => {
if (err) {
return resolve({
content: [
{
type: 'text',
text: `Execution failed: ${err.message}`,
},
],
isError: true,
});
}
resolve({
content: [
{
type: 'text',
text: response.output_json,
},
],
});
}
);
});
});
}
async onModuleDestroy() {
await this.mcpServer.close();
}
}By mapping incoming schemas dynamically and delegating heavy workloads to gRPC, the TypeScript gateway remains responsive even during high-traffic bursts of agent execution cycles.
Exposing business tools to autonomous agents requires robust audit trails to meet regulatory compliance and safety standards. If an AI agent executes incorrect tool actions, developers must be able to trace every step of the decision tree to inspect the inputs, context, and raw outputs.
Using Apache Kafka to record agent transactions enables asynchronous audit tracking with zero impact on query execution times. To manage these highly distributed clusters, enterprises often hire apache developer experts to tune Kafka partition strategies, optimize offsets, and configure robust broker topologies.
This event-driven telemetry stream provides several key capabilities:
Immutable Audit Trails: Records every transaction, system input, and tool response across historical partitions.
Anomalous Event Detection: Connects real-time consumers to flag repetitive or suspicious tool-calling patterns, such as an agent querying a sequence of contiguous SKUs.
Human-in-the-Loop Validation: Routes sensitive actions to authorization queues for manual confirmation before execution.
For more detailed patterns on configuring event brokers with NestJS, refer to our comprehensive guide on Architecting Event-Driven Microservices: NestJS, Kafka & GCP.
In high-throughput agent environments, latency is a critical performance bottleneck. Traditional JSON-over-HTTP setups often suffer from slow transport serialization and socket-recreation overhead, especially during multi-step reasoning processes where an agent executes tools in sequence.
By routing JSON-RPC schemas through a NestJS gateway and executing database operations via lightweight Go workers, the system achieves significant performance improvements over direct HTTP tool paths:
Pipeline Stage | Standard REST / Node Tool Routing | Decoupled NestJS + Go gRPC Gateway | Performance Delta |
|---|---|---|---|
Connection Prep | 15ms - 40ms (New HTTP handshake) | < 0.5ms (Multiplexed gRPC Channel) | ~98% reduction |
Serialization | 8ms - 15ms (JSON-to-JSON parsing) | < 1.0ms (Binary Protobuf Streams) | ~90% reduction |
Execution Latency | 45ms - 120ms (Direct SQL query) | < 8.0ms (Pooled, Optimized Go/MySQL) | ~93% reduction |
Total End-to-End | 68ms - 175ms | < 10ms | ~94% speedup |
To prepare this distributed MCP gateway for production, implement the following security practices:
mTLS Authentication: Configure strict mutual TLS (mTLS) between the NestJS gateway and Go worker instances to prevent unauthorized internal service communication.
Tight Connection Timeouts: Set aggressive database connection timeouts (e.g., 500ms limit per query) to prevent runaway or looping agent operations from consuming worker connection pools.
Token Sandboxing: Inject runtime context, such as session identifiers, directly into the gRPC metadata layer. This allows the Go worker to dynamically restrict query paths based on the calling user's permissions.
The Model Context Protocol (MCP) standardizes how LLM runtimes discover and call tools, read data sources, and manage prompt context. Instead of building custom, brittle endpoints for every tool integration, implementing MCP enables seamless out-of-the-box compatibility with next-generation agent clients like Cursor or Claude Desktop.
NestJS is ideal for fast application development and integrates seamlessly with the official TypeScript MCP SDK, while Golang provides highly optimized database client drivers, low-level concurrency primitives, and minimal memory usage. Combining them via gRPC delivers a developer-friendly API wrapper with high-performance execution speed.
The system prevents injection attacks by ensuring the Go microservice only runs parameterized queries with bound variables. Additionally, strict Whitelist parsing validates inputs before they reach the database engine, ensuring malicious agent prompts cannot execute arbitrary commands.
Building a high-performance Model Context Protocol gateway creates a secure, highly scalable isolation layer for enterprise AI tools. By pairing a NestJS MCP Host with Go microservices over gRPC, developers can expose databases like MySQL and event brokers like Apache Kafka with sub-10ms execution latency. This decoupled structure delivers the security, auditability, and speed required for enterprise AI agent systems.
syntax = "proto3";
package mcp.bridge.v1;
option go_package = "github.com/staksoft/mcp-bridge/gen/v1;bridgev1";
service MCPBridgeService {
rpc ExecuteTool (ToolRequest) returns (ToolResponse);
rpc StreamToolOutput (ToolRequest) returns (stream ToolStreamResponse);
}
message ToolRequest {
string tool_name = 1;
string arguments_json = 2;
string session_id = 3;
}
message ToolResponse {
bool success = 1;
string output_json = 2;
string error_message = 3;
}
message ToolStreamResponse {
string chunk = 1;
int32 progress_percentage = 2;
}package main
import (
"context"
"database/sql"
"encoding/json"
"errors"
"fmt"
"log"
"net"
"strings"
"time"
"github.com/IBM/sarama"
_ "github.com/go-sql-driver/mysql"
"google.com/grpc"
"google.com/grpc/codes"
"google.com/grpc/status"
pb "github.com/staksoft/mcp-bridge/gen/v1"
)
type BridgeServer struct {
pb.UnimplementedMCPBridgeServiceServer
db *sql.DB
kafkaProd sarama.SyncProducer
}
type QueryArgs struct {
Limit int `json:"limit"`
Filter string `json:"filter"`
}
func (s *BridgeServer) ExecuteTool(ctx context.Context, req *pb.ToolRequest) (*pb.ToolResponse, error) {
if req.ToolName != "query_inventory" {
return nil, status.Errorf(codes.InvalidArgument, "Unsupported tool: %s", req.ToolName)
}
var args QueryArgs
if err := json.Unmarshal([]byte(req.ArgumentsJson), &args); err != nil {
return nil, status.Errorf(codes.InvalidArgument, "Invalid arguments JSON: %v", err)
}
// Direct injection mitigation: Enforce strict whitelist validation
if err := validateFilter(args.Filter); err != nil {
return nil, status.Errorf(codes.InvalidArgument, "Policy violation: %v", err)
}
if args.Limit <= 0 || args.Limit > 100 {
args.Limit = 10
}
// Execute parameterized query against isolated MySQL replica
rows, err := s.db.QueryContext(ctx, "SELECT id, name, sku, qty FROM inventory WHERE name LIKE ? LIMIT ?", "%"+args.Filter+"%", args.Limit)
if err != nil {
return nil, status.Errorf(codes.Internal, "Database execution failed: %v", err)
}
defer rows.Close()
var results []map[string]interface{}
for rows.Next() {
var id int
var name, sku string
var qty int
if err := rows.Scan(&id, &name, &sku, &qty); err != nil {
return nil, status.Errorf(codes.Internal, "Row scan error: %v", err)
}
results = append(results, map[string]interface{}{
"id": id,
"name": name,
"sku": sku,
"qty": qty,
})
}
outputBytes, _ := json.Marshal(results)
// Fire-and-forget logging to Kafka partition for audit trails
go s.logAudit(req.SessionId, req.ToolName, string(outputBytes))
return &pb.ToolResponse{
Success: true,
OutputJson: string(outputBytes),
}, nil
}
func validateFilter(filter string) error {
badTokens := []string{";", "DROP", "DELETE", "UPDATE", "UNION", "--"}
for _, token := range badTokens {
if strings.Contains(strings.ToUpper(filter), token) {
return errors.New("forbidden sequence detected in query filter")
}
}
return nil
}
func (s *BridgeServer) logAudit(session, tool, out string) {
msg := &sarama.ProducerMessage{
Topic: "mcp-tool-audit",
Key: sarama.StringEncoder(session),
Value: sarama.StringEncoder(fmt.Sprintf("{\"tool\":\"%s\",\"ts\":%d,\"len\":%d}", tool, time.Now().Unix(), len(out))),
}
_, _, _ = s.kafkaProd.SendMessage(msg)
}import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
import * as grpc from '@grpc/grpc-js';
import * as protoLoader from '@grpc/proto-loader';
import { join } from 'path';
@Injectable()
export class McpGatewayService implements OnModuleInit, OnModuleDestroy {
private mcpServer: Server;
private grpcClient: any;
async onModuleInit() {
this.initGrpcClient();
this.initMcpServer();
}
private initGrpcClient() {
const protoPath = join(__dirname, '../../proto/mcp_bridge.proto');
const packageDefinition = protoLoader.loadSync(protoPath, {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true,
});
const protoDescriptor = grpc.loadPackageDefinition(packageDefinition) as any;
const mcpService = protoDescriptor.mcp.bridge.v1.MCPBridgeService;
this.grpcClient = new mcpService(
'localhost:50051',
grpc.credentials.createInsecure() // Secure with mTLS in production
);
}
private initMcpServer() {
this.mcpServer = new Server(
{
name: 'staksoft-enterprise-mcp-gateway',
version: '1.0.0',
},
{
capabilities: {
tools: {},
},
}
);
this.setupMcpHandlers();
const transport = new StdioServerTransport();
this.mcpServer.connect(transport).catch(console.error);
}
private setupMcpHandlers() {
this.mcpServer.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: 'query_inventory',
description: 'Queries the enterprise database to check safe SKU levels and physical inventory.',
inputSchema: {
type: 'object',
properties: {
filter: { type: 'string', description: 'Substring match for item name' },
limit: { type: 'number', description: 'Maximum records to return (1-100)' },
},
required: ['filter'],
},
},
],
}));
this.mcpServer.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name !== 'query_inventory') {
throw new Error(`Tool not found: ${name}`);
}
return new Promise((resolve, reject) => {
this.grpcClient.ExecuteTool(
{
tool_name: name,
arguments_json: JSON.stringify(args),
session_id: 'session-id-token-999', // Propagate authorization context
},
(err: Error | null, response: any) => {
if (err) {
return resolve({
content: [
{
type: 'text',
text: `Execution failed: ${err.message}`,
},
],
isError: true,
});
}
resolve({
content: [
{
type: 'text',
text: response.output_json,
},
],
});
}
);
});
});
}
async onModuleDestroy() {
await this.mcpServer.close();
}
}Architecting Event-Driven Microservices: NestJS, Kafka & GCP: Explores how to model highly available event loops and pub/sub pipelines within NestJS microservices, ideal for understanding the broader Kafka setup of our MCP gateway.
Architecting Multi-Tenant SaaS Databases in MySQL & TypeScript: Details critical isolation structures for MySQL environments, which directly feed into secure tool-access patterns implemented in this MCP worker guide.
Zero-Shot Tabular AI: Integrating Google's TabFM with MySQL and TypeScript on GCP: Complements this gateway by illustrating how structured tabular schemas in MySQL can be contextualized and digested by AI models using zero-shot prompt engineering.
Node.js, NestJS, Golang, and distributed systems engineering from Staksoft.