This guide will walk you through the process of creating and registering your first dynamic tool.
Make sure you have followed the execution playbook and have the MCP server and a Flutter app running.
In your Flutter app, create a new file called lib/my_tools.dart and add the
following code:
import 'package:mcp_toolkit/mcp_toolkit.dart';
Set<AgentCallEntry> buildMyTools() {
return {
AgentCallEntry.tool(
namespace: 'app',
name: 'hello',
description: 'A simple tool that says hello.',
inputSchema: const {
'type': 'object',
'additionalProperties': false,
'properties': {},
},
handler: (_) async => AgentResult.success(
message: 'Hello from a dynamic tool!',
),
),
};
}Use AgentCallEntry.resource instead when the surface is read-only and
idempotent, such as app configuration, current route, or compact diagnostics.
In your lib/main.dart file, pass the entries to bootstrapFlutter:
import 'package:flutter/material.dart';
import 'package:mcp_toolkit/mcp_toolkit.dart';
import 'my_tools.dart';
Future<void> main() async {
await MCPToolkitBinding.instance.bootstrapFlutter(
additionalEntries: buildMyTools(),
runApp: () => runApp(const MyApp()),
);
}bootstrapFlutter performs WidgetsFlutterBinding.ensureInitialized(),
initializes the toolkit, registers entries once, forwards zone errors, and then
runs the app.
Hot restart after changing registrations, then list the app-owned dynamic surfaces:
{
"name": "fmt_list_client_tools_and_resources",
"arguments": {}
}Invoke by the exact tool name from the listing:
{
"name": "fmt_client_tool",
"arguments": {
"toolName": "hello",
"arguments": {}
}
}Use addEntries directly only when the app needs custom startup choreography:
Future<void> registerMyTools() async {
final helloTool = AgentCallEntry.tool(
namespace: 'app',
name: 'hello',
description: 'A simple tool that says hello.',
inputSchema: const {
'type': 'object',
'additionalProperties': false,
'properties': {},
},
handler: (_) async => AgentResult.success(
message: 'Hello from a dynamic tool!',
),
);
await MCPToolkitBinding.instance.addEntries(entries: {helloTool});
}On the MCP server, dynamic tools are stored as RegisteredAgentIntent entries in DynamicRegistry (not raw dart_mcp.Tool handlers). MCP publish still uses the tool schema for listing; invocation routes through AgentRegistry.invoke and VM extension forwarding.
Change app-owned Flutter registration here. Change reusable registry/session semantics, schema policy, or platform publication behavior in the upstream IntentCall repository.
