MCP (Model Context Protocol) integration for Genkit Dart
Expose Genkit tools, prompts, and resources as an MCP server, or connect to external MCP servers as a client — all with a unified API.
Documentation • API Reference • MCP Specification
dart pub add genkit_mcpgenkit_mcp provides three main components:
- MCP Server — Expose your Genkit actions (tools, prompts, resources) over the MCP protocol.
- MCP Client — Connect to a remote MCP server and use its tools, prompts, and resources.
- MCP Host — Manage multiple MCP server connections and aggregate their capabilities.
flowchart LR
subgraph YourApp["Your Dart App"]
Genkit["Genkit"]
end
subgraph genkit_mcp
direction TB
Server["GenkitMcpServer"]
Client["GenkitMcpClient"]
Host["GenkitMcpHost"]
Host --- Client
end
subgraph External
ExtClient["Claude, Cursor, etc."]
ExtServerA["MCP Server A"]
ExtServerB["MCP Server B"]
end
Genkit -- expose --> Server
Server -- stdio / HTTP --> ExtClient
ExtServerA -- stdio / HTTP --> Client
ExtServerB -- stdio / HTTP --> Host
Client -- use --> Genkit
Host -- use --> Genkit
- As an MCP server (
GenkitMcpServer)- Tools, prompts, resources (including templates and subscribe/unsubscribe)
- Completions (
completion/complete) - Logging (
logging/setLevel) - Tasks (
tasks/*) and progress notifications
- As an MCP client (
GenkitMcpClient/GenkitMcpHost)- Connect over stdio or Streamable HTTP
- Discover and call tools, prompts, and resources from remote servers
- Handle server-initiated requests for roots (
roots/list) - Optional handlers for sampling (
sampling/createMessage) and elicitation (elicitation/create) - Task lifecycle support for long-running inbound requests (client-side
tasks/*)
- Stdio — Standard input/output (for CLI tools and subprocess-based servers)
- Streamable HTTP — HTTP with Server-Sent Events (for web-based deployments)
To connect to one or more MCP servers, use the defineMcpHost function. This returns a GenkitMcpHost instance that manages connections to the configured MCP servers and registers their tools, prompts, and resources as Genkit actions.
import 'package:genkit/genkit.dart';
import 'package:genkit_mcp/genkit_mcp.dart';
void main() async {
final ai = Genkit();
// Each key (e.g., 'fs', 'memory') becomes a namespace for the server's tools.
final host = defineMcpHost(
ai,
McpHostOptionsWithCache(
name: 'my-host',
mcpServers: {
'fs': McpServerConfig(
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-filesystem', '.'],
),
'memory': McpServerConfig(
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-memory'],
),
},
),
);
final tools = await host.getActiveTools(ai);
for (final tool in tools) {
print('Tool: ${tool.name}');
}
}name: (required) A name for the MCP host plugin.cacheTtlMillis: (optional) Cache TTL in milliseconds for tool/prompt/resource listings.version: (optional) Version string for this host.mcpServers: (optional) A map where each key is a namespace for an MCP server, and the value is itsMcpServerConfig.rawToolResponses: (optional) Whentrue, tool responses are returned in their raw MCP format.roots: (optional) Roots to advertise to servers that requestroots/list.
To connect to a single MCP server and register its tools, prompts, and resources in the Genkit registry, use defineMcpClient. This is the recommended approach — it mirrors defineMcpHost and enables registry-based action discovery.
import 'package:genkit/genkit.dart';
import 'package:genkit_mcp/genkit_mcp.dart';
void main() async {
final ai = Genkit();
final client = defineMcpClient(
ai,
McpClientOptionsWithCache(
name: 'my-client',
mcpServer: McpServerConfig(
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-filesystem', '.'],
),
),
);
await client.ready();
// Tools are automatically available through the registry.
final tools = await client.getActiveTools(ai);
for (final tool in tools) {
print('Tool: ${tool.name}');
}
}If you need manual control over the client lifecycle without registry integration, use createMcpClient instead:
final client = createMcpClient(McpClientOptions(
name: 'my-client',
mcpServer: McpServerConfig(
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-filesystem', '.'],
),
));
await client.ready();
final tools = await client.getActiveTools(ai);name: (required) A unique name for this client instance.serverName: (optional) Overrides the name used when prefixing tools/resources returned bygetActiveTools()/getActiveResources().version: (optional) Version string for this client.rawToolResponses: (optional) Whentrue, tool responses are returned in their raw MCP format. Defaults tofalse.mcpServer: (required) AMcpServerConfigwith one of the following:command+args: Launch a local server process via stdio transport.url: Connect to a remote server via Streamable HTTP transport.transport: Provide a customMcpClientTransportinstance.
samplingHandler: (optional) Handler for server-initiated sampling requests.elicitationHandler: (optional) Handler for server-initiated elicitation requests.notificationHandler: (optional) Handler for server notifications.cacheTtlMillis: (optional,McpClientOptionsWithCacheonly) Cache TTL in milliseconds for the registry plugin.
Expose all tools, prompts, and resources from a Genkit instance as an MCP server.
import 'package:genkit/genkit.dart';
import 'package:genkit_mcp/genkit_mcp.dart';
import 'package:schemantic/schemantic.dart';
void main() async {
final ai = Genkit();
ai.defineTool(
name: 'add',
description: 'Add two numbers together',
inputSchema: mapSchema(stringSchema(), dynamicSchema()),
fn: (input, _) async {
final a = num.parse(input['a'].toString());
final b = num.parse(input['b'].toString());
return (a + b).toString();
},
);
ai.defineResource(
name: 'my-resource',
uri: 'my://resource',
fn: (_, _) async {
return ResourceOutput(content: [TextPart(text: 'my resource')]);
},
);
ai.defineResource(
name: 'file',
template: 'file://{path}',
fn: (input, _) async {
return ResourceOutput(
content: [TextPart(text: 'file contents for ${input.uri}')],
);
},
);
// Create and start the MCP server (stdio transport by default).
final server = createMcpServer(ai, McpServerOptions(name: 'my-server'));
await server.start();
}import 'dart:io';
final transport = await StreamableHttpServerTransport.bind(
address: InternetAddress.loopbackIPv4,
port: 3000,
);
await server.start(transport);
// Server is now available at http://localhost:3000/mcpname: (required) The name your server will advertise to MCP clients.version: (optional) The version your server will advertise. Defaults to"1.0.0".
Actions can be namespaced to avoid conflicts (behavior depends on whether you use a host plugin or a single client):
defineMcpHost/McpHostPlugin: Actions are exposed ashostName/serverKey:actionName(e.g.,my-host/fs:read_file).GenkitMcpClient.getActiveTools()/getActiveResources(): Actions are namedclient.serverName/actionName.client.serverNamedefaults to the remote server'sinitialize.serverInfo.name(if provided), otherwiseMcpClientOptions.serverName, otherwiseMcpClientOptions.name.- Set
McpClientOptions.serverNameif you want a stable prefix independent of the remote server's self-reported name.
- Prompts in
GenkitMcpClient.getActivePrompts()keep their original MCP prompt names (no automatic prefixing). If you need strict namespacing across multiple servers, use aGenkitMcpHost.
MCP tools return a content array as opposed to a structured response like most Genkit tools. The plugin attempts to parse and coerce returned content:
- If all
contentparts are text, they are concatenated into a single string.- If the concatenated text looks like JSON (starts with
{or[after left-trimming), the client triesjsonDecode(...)and returns the decoded object/list on success. - Otherwise the concatenated text is returned as a
String.
- If the concatenated text looks like JSON (starts with
- If
contenthas exactly one non-text part, that part map is returned (e.g. an image/audio block). - If
contenthas multiple or mixed parts, the raw MCP result map is returned. - If the MCP result contains
isError: true, the processed value is returned as{'error': '<text>'}.
Set rawToolResponses: true in client options to skip this processing and receive raw MCP responses.
- MCP prompts only accept string (and nullable string) parameters, so prompt input schemas must be objects with only string (or null) property values.
- MCP prompt message roles only support
userandassistant(GenkitRole.user/Role.model).systemis not supported. - Media in prompts/resources must be provided as base64 data URLs (
data:<mimeType>;base64,<data>). HTTP/HTTPS URLs are rejected. - Resource templates only support simple
{var}substitutions (no URI-template operators like{+path}).
You can test your MCP server using the official MCP Inspector.
If your server uses the stdio transport (the default), launch the inspector with the command and arguments separated:
# During development (replace with your entrypoint)
npx @modelcontextprotocol/inspector dart run bin/server.dart
# With a compiled binary
npx @modelcontextprotocol/inspector ./my_mcp_serverNote: The MCP Inspector is a Node.js tool. You need
npx(from Node.js) installed to use it. It launches your Dart server as a subprocess and communicates via stdio.
If your server uses the Streamable HTTP transport, start your server first, then connect the inspector to its URL:
# Start your server (replace with your entrypoint)
dart run bin/server.dart
# In another terminal, open the inspector and connect to the URL
npx @modelcontextprotocol/inspector
# Then enter the server URL (e.g., http://localhost:3000/mcp) in the inspector UI.Once the inspector is connected, you can list tools, prompts, and resources, and test them interactively.
This package implements the MCP specification version 2025-11-25.
Apache 2.0 — see LICENSE for details.
Built by Google with contributions from the Open Source Community.