Developer Guide

Developer Guide

How to extend policies, register new MCP tools, and add NATS queue workers.

This guide describes how to extend the Meta Business MCP platform, including adding new policies, creating new MCP tools, and registering new message queues.

1. Adding New Policies

The Policy Engine (pkg/policy/policy.go) evaluates business rules dynamically seeded from the database.

To add a new policy type:

Define the Policy Schema

Add the policy rules configuration to policies.yaml (or database directly). For example, adding a maximum daily token limit policy:

- id: "daily_user_token_limit"
  name: "Enforce daily token budget per user"
  type: "token_limit"
  channel: "whatsapp"
  message_type: "marketing"
  is_enabled: true
  rules:
    max_tokens: 1000

Implement the Evaluation Logic

In pkg/policy/policy.go, implement a handler for the new policy type in EvaluatePolicies:

case "token_limit":
    // Parse rules: { "max_tokens": 1000 }
    // Query usage stats from DB and verify against rules
    // Return allowed = false if exceeded

Business policies are seeded from policies.yaml on startup and stored in the policies table. The seed script is idempotent (ON CONFLICT DO UPDATE), so restarting the app is sufficient to apply changes.

2. Registering Custom MCP Tools

The Model Context Protocol Server exposes tools via standard input/output. All tools are registered in pkg/mcp/server.go.

To add a new tool (e.g., get_customer_profile):

Register the Tool Schema

In registerTools():

s.mcpServer.AddTool(mcp.NewTool("get_customer_profile",
    mcp.WithDescription("Retrieve customer profiles and metadata tags"),
    mcp.WithString("customer_id", mcp.Required(), mcp.Description("Recipient phone number")),
), s.handleGetCustomerProfile)

Implement the Tool Handler

Create a handler function in pkg/mcp/server.go:

func (s *Server) handleGetCustomerProfile(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
    customerID, err := req.RequireString("customer_id")
    if err != nil {
        return mcp.NewToolResultError("missing customer_id"), nil
    }
    profile, err := s.userManager.GetOrCreateCustomer(ctx, customerID, "whatsapp")
    if err != nil {
        return mcp.NewToolResultError(err.Error()), nil
    }
    resJSON, _ := json.Marshal(profile)
    return mcp.NewToolResultText(string(resJSON)), nil
}

3. Extending Message Queues (NATS)

The system uses NATS JetStream streams to organize asynchronous worker tasks.

To add a new queue (e.g. META_MCP_ANALYTICS for asynchronous analytics streaming):

Declare the Stream

In pkg/delivery/orchestrator.go:

_, err = o.js.CreateOrUpdateStream(ctx, jetstream.StreamConfig{
    Name:      "META_MCP_ANALYTICS",
    Subjects:  []string{"analytics.*"},
    Retention: jetstream.WorkQueuePolicy,
})

Add Publisher Method

To Orchestrator:

func (o *Orchestrator) PublishAnalytics(ctx context.Context, event string, data []byte) error {
    _, err := o.js.Publish(ctx, "analytics."+event, data)
    return err
}

Write a Worker Consumer

Similar to Worker in pkg/delivery/worker.go, create a new worker file under pkg/delivery that creates a consumer for the META_MCP_ANALYTICS stream and consumes its messages in a background loop.

4. Running Tests

# Run all tests
go test -count=1 -p 1 -cover ./...
 
# Run compliance benchmark
go test -bench=BenchmarkCheckCompliance -benchtime=5s ./pkg/compliance/

Package Coverage (Sprint 2)

PackageCoverage
pkg/compliance93.9%
pkg/errorintel100.0%
pkg/config96.2%
pkg/observability100.0%
pkg/ratelimit87.5%
pkg/webhook83.6%
pkg/state83.3%
pkg/userintel82.1%
pkg/policy79.5%
pkg/template78.9%
pkg/campaign77.1%
pkg/delivery73.8%
pkg/mcp61.6%

Benchmark Result

ToolLatencyvs. Target
check_compliance()1.69 ms~30× faster than 50ms target

5. Contributing

  1. Fork the repository.
  2. Create a feature branch: git checkout -b feature/my-change
  3. Run tests before submitting: go test -count=1 -p 1 ./...
  4. Submit a pull request with a clear description of the change.