MCP Server
3 minute read
Overview
The app package lets you expose business logic to AI agents using the Model Context Protocol. MCP provides a standard way for LLMs to call your application’s functions and read its data.
Think of it as dual-protocol: your HTTP API serves humans and traditional clients, while MCP serves AI agents — same business logic, two entry points.
Developers register tools and resources using Rivaas-native types. There is no need to import mcp-go directly.
Defining a Tool
A tool is a function that an AI agent can call. Register tools with WithMCPTool.
Input is strongly typed: define a Go struct and use json + jsonschema tags.
type GetOrderInput struct {
OrderID string `json:"order_id" jsonschema:"Order ID,minLength=1"`
}
app.WithMCP(
app.WithMCPTool("get_order", "Get an order by ID",
func(ctx context.Context, input GetOrderInput) (any, error) {
return orderService.GetByID(ctx, input.OrderID)
},
),
)
The handler returns any JSON-serializable value, or an error.
Defining a Resource
A resource is a read-only data source that an AI agent can fetch. Register resources with WithMCPResource:
app.WithMCP(
app.WithMCPResource("orders://recent", "Recent Orders",
"The 10 most recently placed orders",
func(ctx context.Context) (any, error) {
return orderService.ListRecent(ctx, 10)
},
),
)
Input Schema via Struct Tags
Tool input schema is derived from struct tags.
Common jsonschema directives
| Directive | Example |
|---|---|
| description (first value) | jsonschema:"Search query" |
minLength / maxLength | jsonschema:"...,minLength=1,maxLength=100" |
pattern | jsonschema:"...,pattern=^P[0-9]+$" |
enum | jsonschema:"...,enum=a,enum=b" |
minimum / maximum | jsonschema:"...,minimum=0,maximum=100" |
default | jsonschema:"...,default=1" |
type SearchInput struct {
Query string `json:"query" jsonschema:"Search text,minLength=1"`
Category string `json:"category" jsonschema:"Category,enum=all,enum=books,enum=electronics"`
MinPrice float64 `json:"min_price" jsonschema:"Minimum price,minimum=0"`
InStockOnly bool `json:"in_stock" jsonschema:"Only in-stock items"`
Page int `json:"page" jsonschema:"Page number,minimum=1,default=1"`
}
Validation
Rivaas validates MCP configuration at init. These errors surface from app.New():
- Nil handler:
WithMCPToolandWithMCPResourcerequire a non-nil handler. - Nil options: Nil
MCPOptionvalues produce an error (not silently skipped). - Duplicate tool names: Each tool name must be unique.
- Empty names or descriptions: Tool names and descriptions must be non-empty.
Nil MCPOption and nil handlers always return config errors. Input-schema validation errors from invalid names, duplicate tools, or schema registration also surface at init.
Security Considerations
MCP is opt-in. It is only enabled when you call WithMCP().
For conditional enablement, use WithMCPIf:
app.WithMCPIf(os.Getenv("MCP_ENABLED") == "true",
app.WithMCPTool(...),
)
In production, protect MCP endpoints with authentication middleware, just like your HTTP API.
Complete Example
package main
import (
"context"
"log"
"net/http"
"rivaas.dev/app"
)
type SearchInput struct {
Query string `json:"query" jsonschema:"Search query,minLength=1"`
MinPrice float64 `json:"min_price" jsonschema:"Minimum price,minimum=0"`
InStockOnly bool `json:"in_stock_only" jsonschema:"Only in-stock items"`
Page int `json:"page" jsonschema:"Page number,minimum=1,default=1"`
}
func main() {
a, err := app.New(
app.WithServiceName("orders-api"),
app.WithServiceVersion("v1.0.0"),
app.WithMCP(
app.WithMCPTool("search_products", "Search the product catalog",
func(ctx context.Context, input SearchInput) (any, error) {
return productService.Search(
ctx,
input.Query,
input.MinPrice,
input.InStockOnly,
input.Page,
)
},
),
app.WithMCPResource("orders://recent", "Recent Orders",
"The 10 most recently placed orders",
func(ctx context.Context) (any, error) {
return orderService.ListRecent(ctx, 10)
},
),
),
)
if err != nil {
log.Fatal(err)
}
// HTTP API for humans
a.GET("/products", func(c *app.Context) {
c.JSON(http.StatusOK, map[string]string{"message": "use the API"})
})
// MCP server for AI agents: http://localhost:8080/mcp
if err = a.Start(context.Background()); err != nil {
log.Fatal(err)
}
}
Next Steps
- MCP Debug - Expose runtime internals to AI tools
- Debug Endpoints - pprof profiling
- OpenAPI - Generate OpenAPI specs
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.