MCP (Model Context Protocol) Developer Guide
The Model Context Protocol (MCP) enables Ragwalla agents to connect with external tools and services through a standardized interface. This guide covers everything you need to integrate MCP servers with Ragwalla.
Table of Contents
- Overview
- Quick Start
- Creating MCP Servers
- Registering MCP Servers
- Using MCP Tools in Agents
- Building Custom MCP Servers
- Authentication
- Transport Options
- Best Practices
- Troubleshooting
Overview
MCP provides a standard way for AI assistants to interact with external systems. Key benefits:
- Language Agnostic: Build MCP servers in any language
- Tool Discovery: Agents automatically discover available tools
- Standardized Protocol: Consistent interface across all integrations
- Security: Built-in authentication and transport encryption
- Flexibility: Support for various transport mechanisms
How MCP Works
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ Agent │────▶│ Ragwalla │────▶│ MCP Server │
│ │◀────│ MCP Client │◀────│ │
└─────────────┘ └──────────────┘ └─────────────┘
1. Tool Request 2. Forward 3. Execute
6. Use Result 5. Return 4. Result
Quick Start
1. Deploy an MCP Server
Deploy a simple MCP server to Cloudflare Workers:
npm create cloudflare@latest -- my-mcp-server --template=cloudflare/ai/demos/remote-mcp-authless
cd my-mcp-server
npm run deploy
This creates an MCP server at: https://my-mcp-server.<your-account>.workers.dev/mcp
2. Register the MCP Server with Ragwalla
const mcpServer = await fetch('https://example.ai.ragwalla.com/v1/mcp-servers', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'my-tools-server',
url: 'https://my-mcp-server.example.workers.dev/mcp',
description: 'Custom tools for my agents'
})
});
const server = await mcpServer.json();
console.log('Registered MCP server:', server.id);
3. Discover Available Tools
const tools = await fetch(
`https://example.ai.ragwalla.com/v1/mcp-servers/${server.id}/tools`,
{ headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
).then(r => r.json());
console.log('Available tools:', tools);
// Output: [
// { name: 'get_weather', description: 'Get weather for a location' },
// { name: 'search_news', description: 'Search recent news articles' }
// ]
4. Add MCP Tools to Your Agent
// Add a specific tool to an agent
await fetch(`https://example.ai.ragwalla.com/v1/agents/${agentId}/tools`, {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
type: 'mcp',
mcp: {
serverId: server.id,
toolName: 'get_weather'
}
})
});
5. Use the Agent
const ws = new WebSocket(`wss://example.ai.ragwalla.com/v1/agents/${agentName}/main?token=${token}`);
ws.onopen = () => {
ws.send(JSON.stringify({
type: 'message',
content: "What's the weather in San Francisco?"
}));
};
// The agent will automatically use the MCP tool to get weather data
Creating MCP Servers
MCP Server Structure
An MCP server must implement these endpoints:
interface MCPServer {
// List available tools
'/tools/list': () => Tool[]
// Execute a tool
'/tools/call': (request: ToolCallRequest) => ToolCallResponse
// Optional: List resources
'/resources/list': () => Resource[]
// Optional: Read resource contents
'/resources/read': (uri: string) => ResourceContent
}
Basic MCP Server Example
// Cloudflare Worker MCP Server
import { McpAgent } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
export class MyMCPServer extends McpAgent {
get server() {
return this._mcpServer;
}
private _mcpServer = new McpServer({
name: "My MCP Server",
version: "1.0.0",
description: "Custom tools for my application"
});
async init() {
// Define a weather tool
this._mcpServer.tool(
"get_weather",
"Get current weather for a location",
{
location: z.string().describe("City name"),
units: z.enum(["celsius", "fahrenheit"]).default("celsius")
},
async ({ location, units }) => {
// Tool implementation
const weather = await fetchWeatherData(location, units);
return {
content: [{
type: "text",
text: JSON.stringify(weather)
}]
};
}
);
// Define a search tool
this._mcpServer.tool(
"search_database",
"Search internal database",
{
query: z.string().describe("Search query"),
limit: z.number().default(10)
},
async ({ query, limit }) => {
const results = await searchDatabase(query, limit);
return {
content: [{
type: "text",
text: JSON.stringify(results)
}]
};
}
);
}
}
export default {
fetch(request, env, ctx) {
const url = new URL(request.url);
if (url.pathname === "/mcp") {
return MyMCPServer.serve("/mcp").fetch(request, env, ctx);
}
return new Response("MCP Server", { status: 200 });
}
};
Registering MCP Servers
Server Configuration
const serverConfig = {
name: 'enterprise-tools',
url: 'https://tools.company.com/mcp',
description: 'Internal company tools and data access',
// Optional: Authentication
authRequired: true,
authConfig: {
type: 'bearer',
token: 'YOUR_MCP_SERVER_TOKEN'
},
// Optional: Custom headers
headers: {
'X-API-Version': '2.0',
'X-Company-ID': 'acme-corp'
},
// Optional: Metadata
metadata: {
department: 'engineering',
owner: 'platform-team'
}
};
const response = await fetch('https://example.ai.ragwalla.com/v1/mcp-servers', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify(serverConfig)
});
Managing MCP Servers
// List all MCP servers
const servers = await fetch('https://example.ai.ragwalla.com/v1/mcp-servers', {
headers: { 'Authorization': 'Bearer YOUR_API_KEY' }
}).then(r => r.json());
// Update server configuration
await fetch(`https://example.ai.ragwalla.com/v1/mcp-servers/${serverId}`, {
method: 'PUT',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
description: 'Updated description',
authConfig: { type: 'bearer', token: 'NEW_TOKEN' }
})
});
// Test connection
const testResult = await fetch(
`https://example.ai.ragwalla.com/v1/mcp-servers/${serverId}/test`,
{
method: 'POST',
headers: { 'Authorization': 'Bearer YOUR_API_KEY' }
}
).then(r => r.json());
console.log('Connection test:', testResult.success ? 'Passed' : 'Failed');
// Delete server
await fetch(`https://example.ai.ragwalla.com/v1/mcp-servers/${serverId}`, {
method: 'DELETE',
headers: { 'Authorization': 'Bearer YOUR_API_KEY' }
});
Using MCP Tools in Agents
Adding MCP Tools to Agents
// Add all tools from an MCP server
const tools = await discoverTools(serverId);
for (const tool of tools) {
await fetch(`https://example.ai.ragwalla.com/v1/agents/${agentId}/tools`, {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
type: 'mcp',
mcp: {
serverId: serverId,
toolName: tool.name
}
})
});
}
// Or add specific tools with custom descriptions
await addToolToAgent(agentId, {
type: 'mcp',
mcp: {
serverId: serverId,
toolName: 'complex_calculation'
},
// Override the description for this agent
description: 'Perform advanced mathematical calculations',
metadata: {
category: 'math',
complexity: 'high'
}
});
MCP Tool Execution Flow
When an agent uses an MCP tool:
- Agent receives user message requiring external data/computation
- LLM decides to use MCP tool based on available tools
- Ragwalla MCP client calls the server with tool name and arguments
- MCP server executes the tool and returns results
- Agent incorporates results into the response
Example: Weather Assistant with MCP
// Create agent with MCP weather tools
const weatherAgent = await createAgent({
name: 'weather-assistant',
description: 'Provides weather information and forecasts',
instructions: 'You are a helpful weather assistant. Use the available tools to get current weather and forecasts.',
tools: [
{
type: 'mcp',
mcp: {
serverId: weatherServerId,
toolName: 'get_current_weather'
}
},
{
type: 'mcp',
mcp: {
serverId: weatherServerId,
toolName: 'get_forecast'
}
},
{
type: 'mcp',
mcp: {
serverId: weatherServerId,
toolName: 'get_alerts'
}
}
]
});
// Use the agent
const ws = connectToAgent('weather-assistant');
ws.send({
type: 'message',
content: 'Is it going to rain in Seattle this week?'
});
// Agent response flow:
// 1. Calls get_forecast tool via MCP
// 2. Analyzes forecast data
// 3. Provides natural language summary
Building Custom MCP Servers
Python MCP Server Example
from mcp.server import Server, NotificationOptions
from mcp.server.models import InitializationOptions
import mcp.server.stdio
import mcp.types as types
# Create server instance
server = Server("python-tools")
@server.list_tools()
async def list_tools() -> list[types.Tool]:
return [
types.Tool(
name="analyze_data",
description="Analyze dataset and return insights",
inputSchema={
"type": "object",
"properties": {
"dataset_id": {"type": "string"},
"analysis_type": {"type": "string", "enum": ["summary", "correlation", "trends"]}
},
"required": ["dataset_id", "analysis_type"]
}
),
types.Tool(
name="generate_report",
description="Generate PDF report from data",
inputSchema={
"type": "object",
"properties": {
"data": {"type": "object"},
"template": {"type": "string"}
},
"required": ["data", "template"]
}
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
if name == "analyze_data":
result = await analyze_dataset(
arguments["dataset_id"],
arguments["analysis_type"]
)
return [types.TextContent(type="text", text=json.dumps(result))]
elif name == "generate_report":
report_url = await generate_pdf_report(
arguments["data"],
arguments["template"]
)
return [types.TextContent(type="text", text=f"Report generated: {report_url}")]
raise ValueError(f"Unknown tool: {name}")
# Run the server
async def main():
async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
await server.run(
read_stream,
write_stream,
InitializationOptions(
server_name="python-tools",
server_version="1.0.0"
)
)
if __name__ == "__main__":
import asyncio
asyncio.run(main())
Node.js MCP Server Example
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
const server = new Server(
{
name: 'nodejs-tools',
version: '1.0.0',
},
{
capabilities: {
tools: {},
},
}
);
// Define tools
server.setRequestHandler('tools/list', async () => ({
tools: [
{
name: 'fetch_api_data',
description: 'Fetch data from external API',
inputSchema: {
type: 'object',
properties: {
endpoint: { type: 'string' },
method: { type: 'string', enum: ['GET', 'POST'] },
headers: { type: 'object' }
},
required: ['endpoint', 'method']
}
}
]
}));
// Handle tool calls
server.setRequestHandler('tools/call', async (request) => {
const { name, arguments: args } = request.params;
if (name === 'fetch_api_data') {
const response = await fetch(args.endpoint, {
method: args.method,
headers: args.headers || {}
});
const data = await response.json();
return {
content: [
{
type: 'text',
text: JSON.stringify(data, null, 2)
}
]
};
}
throw new Error(`Unknown tool: ${name}`);
});
// Start server
const transport = new StdioServerTransport();
await server.connect(transport);
Authentication
Bearer Token Authentication
// Register server with bearer token auth
const server = await registerMCPServer({
name: 'secure-tools',
url: 'https://api.company.com/mcp',
authRequired: true,
authConfig: {
type: 'bearer',
token: process.env.MCP_SERVER_TOKEN
}
});
Custom Headers Authentication
// Use custom headers for authentication
const server = await registerMCPServer({
name: 'custom-auth-tools',
url: 'https://api.company.com/mcp',
headers: {
'X-API-Key': process.env.API_KEY,
'X-Client-ID': process.env.CLIENT_ID
}
});
OAuth Authentication (Coming Soon)
// Future support for OAuth
const server = await registerMCPServer({
name: 'oauth-tools',
url: 'https://api.company.com/mcp',
authRequired: true,
authConfig: {
type: 'oauth2',
clientId: process.env.OAUTH_CLIENT_ID,
clientSecret: process.env.OAUTH_CLIENT_SECRET,
tokenUrl: 'https://auth.company.com/token'
}
});
Transport Options
HTTP Transport (Default)
// Standard HTTP transport
const server = await registerMCPServer({
name: 'http-tools',
url: 'https://tools.example.com/mcp',
transport: 'http' // Optional, this is the default
});
WebSocket Transport (Coming Soon)
// For real-time, bidirectional communication
const server = await registerMCPServer({
name: 'websocket-tools',
url: 'wss://tools.example.com/mcp',
transport: 'websocket'
});
Server-Sent Events (SSE)
// For servers that push updates
const server = await registerMCPServer({
name: 'sse-tools',
url: 'https://tools.example.com/sse',
transport: 'sse',
// SSE is used for initialization, HTTP for tool calls
skipSSE: false // Set to true to use HTTP only
});
Best Practices
1. Tool Design
- Single Purpose: Each tool should do one thing well
- Clear Naming: Use descriptive, action-oriented names
- Rich Descriptions: Help the LLM understand when to use each tool
- Input Validation: Use schemas to validate inputs
// Good tool design
this._mcpServer.tool(
"search_customer_orders",
"Search customer orders by various criteria. Returns order details including status, items, and shipping information.",
{
customerId: z.string().optional().describe("Customer ID to filter by"),
status: z.enum(["pending", "shipped", "delivered", "cancelled"]).optional(),
dateFrom: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional().describe("Start date (YYYY-MM-DD)"),
dateTo: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional().describe("End date (YYYY-MM-DD)"),
limit: z.number().min(1).max(100).default(20)
},
async (params) => {
// Implementation
}
);
2. Error Handling
this._mcpServer.tool(
"process_data",
"Process data with comprehensive error handling",
{ data: z.any() },
async ({ data }) => {
try {
const result = await processData(data);
return {
content: [{
type: "text",
text: JSON.stringify({ success: true, result })
}]
};
} catch (error) {
// Return error in a format the LLM can understand
return {
content: [{
type: "text",
text: JSON.stringify({
success: false,
error: error.message,
errorType: error.constructor.name,
suggestion: "Please check the input data format"
})
}]
};
}
}
);
3. Performance Optimization
// Implement caching for expensive operations
const cache = new Map();
this._mcpServer.tool(
"expensive_calculation",
"Perform complex calculation with caching",
{ input: z.any() },
async ({ input }) => {
const cacheKey = JSON.stringify(input);
if (cache.has(cacheKey)) {
return {
content: [{
type: "text",
text: cache.get(cacheKey)
}]
};
}
const result = await performExpensiveCalculation(input);
cache.set(cacheKey, result);
// Implement cache expiration
setTimeout(() => cache.delete(cacheKey), 3600000); // 1 hour
return {
content: [{
type: "text",
text: result
}]
};
}
);
4. Security
- Input Sanitization: Always validate and sanitize inputs
- Rate Limiting: Implement rate limits for resource-intensive tools
- Access Control: Verify permissions before executing sensitive operations
- Audit Logging: Log all tool invocations for security analysis
this._mcpServer.tool(
"delete_record",
"Delete a record with security checks",
{
recordId: z.string().uuid(),
confirmationToken: z.string()
},
async ({ recordId, confirmationToken }, context) => {
// Verify permissions
if (!context.user?.permissions?.includes('delete')) {
throw new Error('Insufficient permissions');
}
// Validate confirmation token
if (!isValidToken(confirmationToken)) {
throw new Error('Invalid confirmation token');
}
// Log the action
await auditLog({
action: 'delete_record',
recordId,
user: context.user.id,
timestamp: new Date()
});
// Perform deletion
await deleteRecord(recordId);
return {
content: [{
type: "text",
text: `Record ${recordId} deleted successfully`
}]
};
}
);
5. Documentation
Document your MCP tools thoroughly:
this._mcpServer.tool(
"analyze_sentiment",
`Analyze sentiment of text using advanced NLP.
Returns sentiment scores:
- positive: 0.0 to 1.0
- negative: 0.0 to 1.0
- neutral: 0.0 to 1.0
- compound: -1.0 to 1.0 (overall sentiment)
Also returns detected emotions: joy, anger, fear, sadness, surprise`,
{
text: z.string().min(1).max(5000).describe("Text to analyze (max 5000 chars)"),
language: z.string().default("en").describe("ISO 639-1 language code"),
options: z.object({
includeEmotions: z.boolean().default(true),
includeKeyPhrases: z.boolean().default(false)
}).optional()
},
async ({ text, language, options }) => {
// Implementation
}
);
Troubleshooting
Common Issues
1. Connection Timeouts
// Increase timeout for slow servers
const server = await registerMCPServer({
name: 'slow-tools',
url: 'https://tools.example.com/mcp',
timeout: 60000, // 60 seconds
skipSSE: true // Use HTTP only to avoid SSE timeouts
});
2. Tool Discovery Fails
// Debug tool discovery
try {
const response = await fetch(
`https://example.ai.ragwalla.com/v1/mcp-servers/${serverId}/tools`,
{ headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
if (!response.ok) {
const error = await response.json();
console.error('Discovery failed:', error);
// Common fixes:
// 1. Check server URL is correct
// 2. Verify authentication credentials
// 3. Ensure server implements /tools/list endpoint
// 4. Check network connectivity
}
} catch (error) {
console.error('Network error:', error);
}
3. Tool Execution Errors
// Handle tool execution errors gracefully
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'tool_result' && data.error) {
console.error('Tool execution failed:', {
tool: data.tool,
error: data.error,
// Common causes:
// - Invalid input parameters
// - Server-side errors
// - Network issues
// - Authentication failures
});
}
};
4. Authentication Issues
// Test authentication separately
const testAuth = async (server) => {
const response = await fetch(server.url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${server.authConfig.token}`,
...server.headers
},
body: JSON.stringify({
jsonrpc: '2.0',
method: 'tools/list',
params: {},
id: 1
})
});
if (response.status === 401) {
console.error('Authentication failed. Check your credentials.');
}
return response.ok;
};
Debug Mode
Enable detailed logging for troubleshooting:
// Set environment variables
process.env.LOG_LEVEL = 'DEBUG';
process.env.LOG_MCP_REQUESTS = 'true';
process.env.LOG_MCP_RESPONSES = 'true';
// Or configure per-server debugging
const server = await registerMCPServer({
name: 'debug-tools',
url: 'https://tools.example.com/mcp',
metadata: {
debug: true,
logRequests: true,
logResponses: true
}
});
Next Steps
- Browse MCP server examples
- Read the MCP specification
- Join the MCP community
- Explore pre-built MCP servers
For Ragwalla-specific support, visit docs.ragwalla.com or contact support@ragwalla.com.