brainstorming
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or planning anything new, in software or out of it (a talk, a business, a renovation).
flutter-mcp-toolkit-custom-toolsUse this skill when the agent exposes app-specific surfaces by registering custom MCP tools and resources inside the Flutter app (mcp_toolkit dynamic registry — AgentCallEntry, addEntries). Covers tool vs resource vs evaluate-expression, Map-based handlers, schema strictness, discovery via fmt_list_client_tools_and_resources, fmt_client_tool, fmt_client_res…
Independent GitHub snapshot · Automated classification · Original documentation governs
Use this when bundled MCP tools (screenshot, semantic snapshot, tap, …) are not enough and you need app-specific read surfaces or actions — e.g. cart totals, feature flags, curated debug snapshots of internal state. Entries are registered in the Flutter process and exposed to the agent through the dynamic registry.
Migration: The legacy call-entry type was removed in intentcall Phase 6b. Use
AgentCallEntryormcpToolkitTool/mcpToolkitResource. Seeflutter-mcp-toolkit-intentcall-migration.
Boundary: Change app discovery, Flutter VM-service extensions, or app-owned debug surfaces here. Change canonical registry/session semantics, schema policy, platform projection, or publish behavior in the upstream IntentCall repository.
| Need | Use |
|---|---|
| One-off read of a simple value | fmt_evaluate_dart_expression (no app code change). |
| Stable read-only payload (diagnostics, JSON snapshot, “current route”) | AgentCallEntry.resource + fmt_client_resource. Prefer resources when the contract is “GET-like” and idempotent. |
| Parameterized or mutating action, or reusable named operation | AgentCallEntry.tool + fmt_client_tool. |
AgentCallEntry (preferred for new code)Handlers receive AgentArguments (Map<String, Object?>) and return AgentResult:
import 'package:mcp_toolkit/mcp_toolkit.dart';
final tool = AgentCallEntry.tool(
namespace: 'app',
name: 'cart_get_snapshot',
description: 'Return current cart total and items for a user.',
inputSchema: const {
'type': 'object',
'additionalProperties': false,
'properties': {
'userId': {'type': 'string'},
},
'required': ['userId'],
},
handler: (final args) async {
final userId = args['userId']?.toString() ?? '';
final cart = CartRepository.instance.forUser(userId);
return AgentResult.success(
message: 'ok',
data: {
'total': cart.total,
'items': cart.items.map((final i) => i.toJson()).toList(),
},
);
},
);
await MCPToolkitBinding.instance.addEntries(entries: {tool});
MCPToolDefinition + MCPCallResult (bridge)Built-in toolkits still use mcpToolkitTool / mcpToolkitResource to adapt
Map<String, String> service-extension handlers:
final tool = mcpToolkitTool(
namespace: 'app',
handler: (final request) async {
final userId = request['userId'] ?? '';
return MCPCallResult(
message: 'ok',
parameters: {'userId': userId},
);
},
definition: MCPToolDefinition(
name: 'cart_get_snapshot',
description: 'Return current cart total and items for a user.',
inputSchema: ObjectSchema(
properties: {
'userId': StringSchema(),
},
required: ['userId'],
),
),
);
int.tryParse, jsonDecode, etc.request.arguments on the app side.Prefer MCPToolkitBinding.instance.addEntries(entries: { ... }) before bootstrapFlutter(runApp: ...) so tools/resources register in one place with zone/error setup.
Register once at bootstrap — not inside build, not per-widget initState.
final resource = AgentCallEntry.resource(
namespace: 'app',
name: 'app_cart_digest',
description: 'Compact cart summary for agents (read-only).',
mimeType: 'application/json',
handler: (final args) async => AgentResult.success(
message: 'Cart digest',
data: {
'itemCount': CartRepository.instance.visibleCount,
'currency': CartRepository.instance.currencyCode,
},
),
);
Or via bridge:
mcpToolkitResource(
namespace: 'app',
definition: MCPResourceDefinition(
name: 'app_cart_digest',
description: 'Compact cart summary for agents (read-only).',
mimeType: 'application/json',
),
handler: (final request) async => MCPCallResult(
message: 'Cart digest',
parameters: {'itemCount': 3},
),
),
name must be snake_case. Published resource URIs follow visual://localhost/... conventions; agents use fmt_client_resource and listings from fmt_list_client_tools_and_resources.The MCP server enforces strict JSON Schema:
additionalProperties: false unless you intentionally accept arbitrary keys.required for anything the handler reads unconditionally.AgentResult.data / MCPCallResult.parameters must be JSON-serializable.fmt_list_client_tools_and_resources — enumerate app-registered tools and resources.fmt_client_tool — invoke a dynamic tool by name with JSON args.fmt_client_resource — fetch a registered resource URI from the listing.If something should appear but does not: confirm addEntries completed (await), then hot restart.
addEntries from widget code → duplicate registrations. Register once in main() / bootstrap.cart_, flags_, nav_). If a built-in entry already uses a name, the custom entry is skipped without warning; the built-in handles the call and the custom handler does not run.MCPToolkitBinding.instance.setAppIdentity(label: 'Fleet app · staging'), called again whenever the name changes (sign-in, workspace switch). Discovery reads that label, and a tool of your own called app_identity is one of the collisions above.mcp_toolkit is in pubspec.yaml.lib/mcp_tools/<domain>_surfaces.dart returning Set<AgentCallEntry> or calling addEntries once.addEntries(...) before bootstrapFlutter(...) — never from StatefulWidget lifecycle.fmt_list_client_tools_and_resources before first client invoke.AgentArguments with Map<String, String> — pick one API (native vs mcpToolkitTool).await on addEntries → race before discovery.inputSchema out of sync with the handler → agents trust the schema; update both.flutter-mcp-toolkit-intentcall-migration — CLI migrator, breaking upgradeflutter-mcp-toolkit-guide → inspect / control / debug skillsARCHITECTURE.md → “Dynamic Registry Architecture”