This guide will help you get up and running with the MCP Dart SDK in minutes.
Add the MCP Dart SDK to your pubspec.yaml:
dependencies:
mcp_dart: ^2.1.1Then run:
dart pub getOr for Flutter projects:
flutter pub getThe Model Context Protocol enables bidirectional communication between:
- Servers: Provide capabilities (tools, resources, prompts)
- Clients: Use those capabilities (typically AI applications)
Functions that AI can call to perform actions:
// Server provides a tool
server.registerTool(
'search',
description: 'Search for information',
callback: (args, extra) async {
// Perform search
return CallToolResult(content: [TextContent(text: results)]);
},
);
// Client calls the tool
await client.callTool(
CallToolRequest(
name: 'search',
arguments: {'query': 'Dart programming'},
),
);Data and context that AI can access:
// Server provides a resource
server.registerResource(
'README',
'file:///docs/readme.md',
null,
(uri, extra) async => ReadResourceResult(
contents: [TextResourceContents(
uri: 'file:///docs/readme.md',
text: readmeContent,
)],
),
);
// Client reads the resource
await client.readResource(
ReadResourceRequest(
uri: 'file:///docs/readme.md',
),
);Reusable prompt templates with arguments:
// Server provides a prompt
server.registerPrompt(
'code-review',
description: 'Review code for issues',
argsSchema: {
'language': PromptArgumentDefinition(
type: String,
description: 'Programming language',
required: true,
),
},
callback: (args, extra) async => GetPromptResult(
messages: [
PromptMessage(
role: PromptMessageRole.user,
content: TextContent(
text: 'Review this ${args['language']} code...',
),
),
],
),
);
// Client gets the prompt
await client.getPrompt(
GetPromptRequest(
name: 'code-review',
arguments: {'language': 'Dart'},
),
);How clients and servers communicate:
- Stdio: Process-based (stdin/stdout)
- HTTP/SSE: Web-based (Server-Sent Events)
- Stream: In-process communication
The SDK emits internal runtime logs (for example transport/protocol diagnostics).
These logs are separate from MCP protocol logging messages (logging/setLevel
and notifications/message).
By default:
- Dart VM/Flutter: Logs are written to
stderr - Web: Logs are written to browser console (
print)
If your app does not already depend on package:logging, add it first:
dependencies:
logging: ^1.2.0Use import aliases to avoid Logger name collisions:
import 'package:logging/logging.dart' as app_log;
import 'package:mcp_dart/mcp_dart.dart' as mcp;
void configureMcpLogging() {
final appLogger = app_log.Logger('my_app.mcp');
mcp.setMcpLogHandler((loggerName, level, message) {
final appLevel = switch (level) {
mcp.LogLevel.debug => app_log.Level.FINE,
mcp.LogLevel.info => app_log.Level.INFO,
mcp.LogLevel.warn => app_log.Level.WARNING,
mcp.LogLevel.error => app_log.Level.SEVERE,
};
appLogger.log(appLevel, '[$loggerName] $message');
});
}import 'package:mcp_dart/mcp_dart.dart' as mcp;
void main() {
// Turn off internal SDK logs.
mcp.silenceMcpLogs();
// ... later, restore default behavior (stderr on VM, console on web).
mcp.resetMcpLogHandler();
}Create a file my_server.dart:
import 'package:mcp_dart/mcp_dart.dart';
void main() async {
// Create the server
final server = McpServer(
Implementation(
name: 'my-first-server',
version: '1.0.0',
),
options: McpServerOptions(
capabilities: ServerCapabilities(
tools: ServerCapabilitiesTools(),
resources: ServerCapabilitiesResources(),
),
),
);
// Add a simple tool
server.registerTool(
'greet',
description: 'Greet someone by name',
inputSchema: ToolInputSchema(
properties: {
'name': JsonSchema.string(description: 'Name of person to greet'),
},
required: ['name'],
),
callback: (args, extra) async {
final name = args['name'] as String;
return CallToolResult(
content: [
TextContent(text: 'Hello, $name! Welcome to MCP!'),
],
);
},
);
// Add a resource
server.registerResource(
'Server Info',
'info://server',
null,
(uri, extra) async => ReadResourceResult(
contents: [
TextResourceContents(
uri: uri.toString(),
text: 'This is my first MCP server!',
mimeType: 'text/plain',
),
],
),
);
// Connect via stdio
print('Starting MCP server...');
final transport = StdioServerTransport();
await server.connect(transport);
}Run your server:
dart run my_server.dartCreate a file my_client.dart:
import 'package:mcp_dart/mcp_dart.dart';
void main() async {
// Create the client
final client = McpClient(
Implementation(
name: 'my-first-client',
version: '1.0.0',
),
);
// Connect to the server
print('Connecting to server...');
final transport = StdioClientTransport(
StdioServerParameters(
command: 'dart',
args: ['run', 'my_server.dart'],
),
);
await client.connect(transport);
// List available tools
print('\nAvailable tools:');
final tools = await client.listTools();
for (final tool in tools.tools) {
print(' - ${tool.name}: ${tool.description}');
}
// Call a tool
print('\nCalling greet tool...');
final result = await client.callTool(
CallToolRequest(
name: 'greet',
arguments: {'name': 'Alice'},
),
);
print('Result: ${result.content.first.text}');
// Read a resource
print('\nReading server info resource...');
final resource = await client.readResource(
ReadResourceRequest(
uri: 'info://server',
),
);
print('Content: ${resource.contents.first.text}');
// Clean up
await client.close();
print('\nDone!');
}Run your client:
dart run my_client.dartExpected output:
Connecting to server...
Available tools:
- greet: Greet someone by name
Calling greet tool...
Result: Hello, Alice! Welcome to MCP!
Reading server info resource...
Content: This is my first MCP server!
Done!- Server Startup: Server declares its capabilities (tools, resources, prompts)
- Client Connection: Client connects and negotiates protocol version
- Capability Exchange: Client and server exchange supported features
- Client Requests: Client discovers and uses server capabilities
- Server Responses: Server processes requests and returns results
- Server Guide - Comprehensive server development guide
- Tools - Building powerful tools with validation
- MCP Apps - TypeScript-style
registerAppTool/registerAppResourcehelpers
- Client Guide - Building MCP clients
- Calling Tools - Advanced tool usage
- Reading Resources - Resource subscriptions
- Transports Guide - Detailed transport options
- Stdio - Best for CLI tools and local services
- HTTP/SSE - Best for web and remote services
try {
final result = await client.callTool(
CallToolRequest(
name: 'unknown-tool',
arguments: {},
),
);
} catch (e) {
if (e is McpError) {
print('MCP Error: ${e.message} (code: ${e.code})');
} else {
print('Unexpected error: $e');
}
}server.registerTool(
'calculate',
description: 'Perform calculation',
inputSchema: ToolInputSchema(
properties: {
'operation': JsonSchema.string(
enumValues: ['add', 'subtract', 'multiply', 'divide'],
),
'a': JsonSchema.number(),
'b': JsonSchema.number(),
},
required: ['operation', 'a', 'b'],
),
callback: (args, extra) async {
final op = args['operation'] as String;
final a = args['a'] as num;
final b = args['b'] as num;
final result = switch (op) {
'add' => a + b,
'subtract' => a - b,
'multiply' => a * b,
'divide' => a / b,
_ => throw McpError(
ErrorCode.invalidParams,
'Invalid operation',
),
};
return CallToolResult(
content: [TextContent(text: '$result')],
);
},
);server.registerResourceTemplate(
'User Profile',
ResourceTemplateRegistration(
'user://{username}/profile',
listCallback: null,
),
ResourceMetadata(description: 'Get user profile by username'),
(uri, vars, extra) async {
final username = vars['username'];
final profile = await fetchUserProfile(username);
return ReadResourceResult(
contents: [
TextResourceContents(
uri: uri.toString(),
text: jsonEncode(profile),
mimeType: 'application/json',
),
],
);
},
);Ensure the transport is properly initialized:
// Stdio: Check process is running
final transport = StdioClientTransport(
StdioServerParameters(command: 'dart', args: ['server.dart']),
);
// HTTP: Check port is available
final transport = StreamableHTTPClientTransport(
Uri.parse('http://localhost:3000'),
);Ensure your input matches the schema:
// Schema requires 'name' as string
inputSchema: ToolInputSchema(
properties: {
'name': JsonSchema.string(),
},
required: ['name'],
)
// Correct call
await client.callTool(
CallToolRequest(
name: 'greet',
arguments: {'name': 'Alice'}, // ✅ Valid
),
);
// Incorrect call
await client.callTool(
CallToolRequest(
name: 'greet',
arguments: {'name': 123}, // ❌ Wrong type
),
);Increase timeout for slow operations:
final client = McpClient(
Implementation(name: 'client', version: '1.0.0'),
requestTimeout: Duration(seconds: 30), // Default is 10 seconds
);The SDK includes many examples in the example/ directory:
- server_stdio.dart - Complete stdio server
- client_stdio.dart - Stdio client
- weather.dart - Real weather API integration
- oauth_server_example.dart - OAuth2 server
- completions_capability_demo.dart - Auto-completion
- elicitation_http_server.dart - User input collection
Browse the examples to see real-world usage patterns!
- Server Guide - Build comprehensive MCP servers
- Client Guide - Build MCP clients and applications
- Examples - Real-world usage examples