genkit
genkit copied to clipboard
expose flows in genkitx-mcp as MCP tools
Is your feature request related to a problem? Please describe. I am using genkit flows well, but I really need my MCP to interact at the flow level not necessarily the tool level.
Describe the solution you'd like It would be better to wrap each flow as an MCP tool as well.
Describe alternatives you've considered I am doing it manually now
function createMCPServer(uid: string): Server {
const server = new Server(
{
name: 'flipfeeds-mcp-server',
version: '1.0.0',
},
{
capabilities: {
tools: {},
},
}
);
// Register tool list handler
server.setRequestHandler(ListToolsRequestSchema, async () => {
// Get all Genkit flows and expose them as MCP tools
const actions = await ai.registry.listActions();
// Filter for flows only - exclude models and other action types
const allActions = Object.values(actions);
const flows = allActions.filter((a: any) => {
// Flows are callable functions with __action metadata
// Exclude googleai models (they start with 'googleai/')
const flowName = a.__action?.name || '';
return (
typeof a === 'function' &&
a.__action &&
!flowName.startsWith('googleai/') &&
flowName !== 'generate' // Exclude the default 'generate' flow
);
});
console.log(
`Found ${flows.length} FlipFeeds flows (filtered out ${allActions.length - flows.length} models/other actions)`
);
const tools = flows.map((flow: any) => {
const flowAction = flow.__action;
const flowName = flowAction.name;
console.log('Processing flow:', flowName);
// Convert Zod schema to JSON schema for MCP
const inputSchema = flowAction.inputSchema || {};
return {
name: flowName,
description: flowAction.description || `Execute the ${flowName} flow`,
inputSchema: {
type: 'object',
properties: inputSchema._def?.shape
? Object.entries(inputSchema._def.shape).reduce(
(acc: any, [key, value]: [string, any]) => {
acc[key] = {
type:
value._def?.typeName === 'ZodString'
? 'string'
: value._def?.typeName === 'ZodNumber'
? 'number'
: value._def?.typeName === 'ZodBoolean'
? 'boolean'
: value._def?.typeName === 'ZodArray'
? 'array'
: value._def?.typeName === 'ZodObject'
? 'object'
: 'string',
description: value._def?.description || `${key} parameter`,
};
return acc;
},
{}
)
: {},
},
};
});
console.log(`Listing ${tools.length} MCP tools for user ${uid}`);
return {
tools,
};
});
// Register tool call handler
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
console.log(`Executing flow: ${name} for user ${uid}`);
console.log('Flow arguments:', args);
try {
// Get the flow from Genkit registry
const actions = await ai.registry.listActions();
const flowEntry = Object.values(actions).find((a: any) => a.__action?.name === name);
if (!flowEntry) {
throw new Error(`Flow not found: ${name}`);
}
// Inject uid into the arguments if not already present
const flowArgs = {
uid,
...args,
};
// Execute the flow - flowEntry is the callable flow function
const result = await flowEntry(flowArgs);
// Return the result as MCP content
return {
content: [
{
type: 'text',
text: JSON.stringify(result, null, 2),
},
],
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
console.error(`Error executing flow ${name}:`, errorMessage);
return {
content: [
{
type: 'text',
text: JSON.stringify({ error: errorMessage }, null, 2),
},
],
isError: true,
};
}
});
return server;
}
Additional context Add any other context or screenshots about the feature request here.