Model Context Protocol (MCP)#
Bialet includes built-in support for the Model Context Protocol (MCP), allowing you to create MCP servers that expose tools, resources, and prompts to AI assistants like Claude, GitHub Copilot, and others.
What is MCP?#
The Model Context Protocol is an open standard that enables seamless integration between AI applications and external data sources. With Bialet’s MCP support, you can:
Expose Tools: Create callable functions that AI assistants can invoke
Provide Resources: Share data and content that assistants can access
Define Prompts: Offer pre-configured prompts for specific tasks
Creating an MCP Server#
To create an MCP server in Bialet, you need a _route.wren file that defines
your tools and serves them using the Mcp class.
Important: The _route.wren file is always needed for MCP servers. You
don’t need to place it inside a specific mcp folder - any directory can host
an MCP server through its _route.wren file.
Basic Structure#
import "gh:bialet/extra/mcp" for Mcp
// Define your tools as classes
class MyTool {
construct new(params) {
// Initialize with parameters
}
// Define the tool's logic
call() {
// Return the result
}
}
// Create and configure the MCP server
var mcp = Mcp.new('my-server-name', '1.0.0')
mcp.addTool(MyTool)
mcp.addPrompt("System prompt for the AI assistant")
mcp.serve
Defining Tools#
Tools are defined as Wren classes with specific annotations that describe their parameters and behavior.
Tool Annotations#
Use special comment annotations to describe your tool and its parameters:
#!doc = "description": Describes the tool or parameter#!required: Marks a parameter as required#!type = TypeName: Specifies the parameter type (String, Number, Boolean, etc.)#!format = "format": Specifies the format for strings (e.g., “date”, “email”, “uri”)
Example 1: Simple Greeting Tool#
import "gh:bialet/extra/mcp" for Mcp
#!doc = "A simple greeting tool"
class Greet {
construct new(params) {
_name = params["name"]
}
#!doc = "Name of the person to greet"
#!required
name(name) { _name = name }
call() { "Hello, %(_name)!" }
}
var mcp = Mcp.new('greeter', '1.0.0')
mcp.addTool(Greet)
mcp.addPrompt("You are a friendly assistant that greets people.")
mcp.serve
Example 2: Flight Search Tool#
import "gh:bialet/extra/mcp" for Mcp
#!doc = "Search for available flights"
class SearchFlights {
construct new(params) {
_origin = params["origin"]
_destination = params["destination"]
_date = params["date"]
}
#!doc = "Departure city"
#!type = String
#!required
origin{}
#!doc = "Arrival city"
#!type = String
#!required
destination{}
#!doc = "Travel date"
#!type = String
#!format = "date"
#!required
date{}
call() {
// Query your database or external API
var flights = `SELECT * FROM flights
WHERE origin = ? AND destination = ? AND date = ?`
.fetch([_origin, _destination, _date])
return {
"from": _origin,
"to": _destination,
"date": _date,
"flights": flights
}
}
}
var mcp = Mcp.new('flight-search', '1.0.0')
mcp.addTool(SearchFlights)
mcp.addPrompt("You are a travel assistant that helps users find flights.")
mcp.serve
Example 3: Database Query Tool#
import "gh:bialet/extra/mcp" for Mcp
#!doc = "Query user information from the database"
class GetUser {
construct new(params) {
_userId = params["userId"]
}
#!doc = "User ID to query"
#!type = Number
#!required
userId{}
call() {
var user = `SELECT * FROM users WHERE id = ?`.first(_userId)
if (user == null) {
return {"error": "User not found"}
}
return {
"id": user["id"],
"name": user["name"],
"email": user["email"]
}
}
}
var mcp = Mcp.new('user-api', '1.0.0')
mcp.addTool(GetUser)
mcp.addPrompt("You are an assistant that provides user information.")
mcp.serve
Tool Parameters#
Parameter Types#
The following types are supported:
String: Text valuesNumber: Numeric valuesBoolean: True/false valuesArray: Lists of valuesObject: Key-value pairs (Map)
Parameter Formats#
For String type parameters, you can specify formats:
"date": ISO 8601 date format (YYYY-MM-DD)"date-time": ISO 8601 datetime format"email": Email address format"uri": URI/URL format"uuid": UUID format
Optional vs Required Parameters#
By default, parameters are optional unless marked with #!required:
class MyTool {
construct new(params) {
_required = params["required"]
_optional = params["optional"]
}
#!doc = "This parameter is required"
#!required
required{}
#!doc = "This parameter is optional"
optional{}
call() {
// Handle both required and optional parameters
}
}
Mcp Class Methods#
Mcp.new(name, version)#
Creates a new MCP server instance.
name: The name of your MCP server (String)version: The version number (String)
var mcp = Mcp.new('my-server', '1.0.0')
addTool(toolClass)#
Adds a tool class to the MCP server.
toolClass: The class that implements the tool
mcp.addTool(Greet)
mcp.addTool(SearchFlights)
addPrompt(prompt)#
Sets the system prompt for the MCP server. This prompt helps guide the AI assistant on how to use your tools.
prompt: The system prompt text (String)
mcp.addPrompt("You are a helpful assistant that can greet people and search for flights.")
serve#
Starts the MCP server and begins listening for requests. This must be the last
call in your _route.wren file.
mcp.serve
Returning Data from Tools#
Tools can return various types of data:
Simple Values#
call() { "Hello, World!" }
call() { 42 }
call() { true }
Maps (Objects)#
call() {
return {
"status": "success",
"data": "Some value",
"timestamp": Date.now
}
}
Lists (Arrays)#
call() {
return ["item1", "item2", "item3"]
}
Database Results#
call() {
var users = `SELECT * FROM users`.fetch
return users
}
Complete Example#
Here’s a complete example that combines database queries, file handling, and multiple tools:
import "gh:bialet/extra/mcp" for Mcp
#!doc = "Get user profile information"
class GetUserProfile {
construct new(params) {
_userId = params["userId"]
}
#!doc = "User ID"
#!type = Number
#!required
userId{}
call() {
var user = `SELECT u.*, f.id as avatar_id
FROM users u
LEFT JOIN files f ON u.avatar_file_id = f.id
WHERE u.id = ?`.first(_userId)
if (user == null) {
return {"error": "User not found"}
}
return {
"id": user["id"],
"name": user["name"],
"email": user["email"],
"avatar_url": user["avatar_id"] ? "/files/%(user["avatar_id"])" : null
}
}
}
#!doc = "Create a new user"
class CreateUser {
construct new(params) {
_name = params["name"]
_email = params["email"]
}
#!doc = "User's full name"
#!type = String
#!required
name{}
#!doc = "User's email address"
#!type = String
#!format = "email"
#!required
email{}
call() {
var userData = {
"name": _name,
"email": _email,
"created_at": Date.now
}
var userId = Db.save("users", userData)
return {
"status": "success",
"userId": userId,
"message": "User created successfully"
}
}
}
#!doc = "Search users by name or email"
class SearchUsers {
construct new(params) {
_query = params["query"]
}
#!doc = "Search query"
#!type = String
#!required
query{}
call() {
var searchTerm = "%%(_query)%"
var users = `SELECT id, name, email
FROM users
WHERE name LIKE ? OR email LIKE ?
LIMIT 10`.fetch([searchTerm, searchTerm])
return {
"count": users.count,
"users": users
}
}
}
// Set up the MCP server
var mcp = Mcp.new('bialet-user-api', '1.0.0')
mcp.addTool(GetUserProfile)
mcp.addTool(CreateUser)
mcp.addTool(SearchUsers)
mcp.addPrompt("You are a user management assistant that can retrieve, create, and search for users.")
mcp.serve
Connecting to AI Assistants#
Once your MCP server is running in Bialet, AI assistants can connect to it using the standard MCP protocol. The server will be accessible at the URL where your Bialet application is running.
Configuration Example#
For Claude Desktop or other MCP clients, you would configure the connection like:
{
"mcpServers": {
"bialet-server": {
"url": "http://localhost:7001/api/mcp",
"transport": "http"
}
}
}
Replace the URL with your actual Bialet server address and the path to your
_route.wren file.
Best Practices#
Use Descriptive Documentation: Always include
#!docannotations to help AI assistants understand your toolsValidate Input: Check parameters in your
call()method before using themHandle Errors Gracefully: Return error objects instead of crashing
Keep Tools Focused: Each tool should do one thing well
Use Database Efficiently: Leverage SQLite queries for data operations
Return Structured Data: Use Maps and Lists for complex return values
Test Your Tools: Verify each tool works correctly before deployment
Debugging#
To debug your MCP server, you can use System.log() to output messages:
call() {
System.log("Received parameters: %(_userId)")
var user = `SELECT * FROM users WHERE id = ?`.first(_userId)
System.log("Found user: %(user)")
return user
}
These messages will appear in the Bialet server logs, helping you track down issues.
Security Considerations#
Validate User Input: Always validate and sanitize parameters
Use Parameterized Queries: Never concatenate user input into SQL queries
Limit Data Exposure: Only return necessary information
Implement Rate Limiting: Consider adding rate limits to prevent abuse
Authenticate Requests: Add authentication if your MCP server handles sensitive data
Further Reading#
Database Documentation - Learn more about SQLite in Bialet
File Handling - Work with files in your MCP tools
Structure - Understanding
_route.wrenand routing