Creating Your First Dynamic Tool

This guide will walk you through the process of creating and registering your first dynamic tool.

1. Set Up Your Project

Make sure you have followed the execution playbook and have the MCP server and a Flutter app running.

2. Create the Entry

In your Flutter app, create a new file called lib/my_tools.dart and add the following code:

dart
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.

3. Register At Bootstrap

In your lib/main.dart file, pass the entries to bootstrapFlutter:

dart
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.

4. Verify And Use The Tool

Hot restart after changing registrations, then list the app-owned dynamic surfaces:

json
{
  "name": "fmt_list_client_tools_and_resources",
  "arguments": {}
}

Invoke by the exact tool name from the listing:

json
{
  "name": "fmt_client_tool",
  "arguments": {
    "toolName": "hello",
    "arguments": {}
  }
}

Lower-Level Registration

Use addEntries directly only when the app needs custom startup choreography:

dart
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});
}

Server-side registry (intentcall)

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.