UNPKG

@heroku/mcp-server

Version:
111 lines (110 loc) 4.34 kB
import { z } from 'zod'; import { handleCliOutput } from '../utils/handle-cli-output.js'; import { CommandBuilder } from '../utils/command-builder.js'; import { TOOL_COMMAND_MAP } from '../utils/tool-commands.js'; /** * Schema for listing Heroku apps with filters */ export const listAppsOptionsSchema = z.object({ all: z.boolean().optional().describe('Show owned apps and collaborator access. Default: owned only'), personal: z.boolean().optional().describe('List personal account apps only, ignoring default team'), space: z.string().optional().describe('Filter by private space name. Excludes team param'), team: z.string().optional().describe('Filter by team name. Excludes space param') }); /** * Register list_apps tool * * @param server MCP server instance * @param herokuRepl Heroku REPL instance */ export const registerListAppsTool = (server, herokuRepl) => { server.tool('list_apps', 'List Heroku apps: owned, collaborator access, team/space filtering', listAppsOptionsSchema.shape, async (options) => { const command = new CommandBuilder(TOOL_COMMAND_MAP.LIST_APPS) .addFlags({ all: options.all, personal: options.personal, space: options.space, team: options.team }) .build(); const output = await herokuRepl.executeCommand(command); return handleCliOutput(output); }); }; /** * Schema for app info retrieval */ export const getAppInfoOptionsSchema = z.object({ app: z.string().describe('Target app name. Requires access permissions'), json: z.boolean().optional().describe('JSON output with full metadata. Default: text format') }); /** * Register get_app_info tool * * @param server MCP server instance * @param herokuRepl Heroku REPL instance */ export const registerGetAppInfoTool = (server, herokuRepl) => { server.tool('get_app_info', 'Get app details: config, dynos, addons, access, domains', getAppInfoOptionsSchema.shape, async (options) => { const command = new CommandBuilder(TOOL_COMMAND_MAP.GET_APP_INFO) .addFlags({ app: options.app, json: options.json }) .build(); const output = await herokuRepl.executeCommand(command); return handleCliOutput(output); }); }; /** * Schema for app creation */ export const createAppOptionsSchema = z.object({ app: z.string().optional().describe('App name. Auto-generated if omitted'), region: z.enum(['us', 'eu']).optional().describe('Region: us/eu. Default: us. Excludes space param'), space: z.string().optional().describe('Private space name. Inherits region. Excludes region param'), team: z.string().optional().describe('Team name for ownership') }); /** * Register create_app tool * * @param server MCP server instance * @param herokuRepl Heroku REPL instance */ export const registerCreateAppTool = (server, herokuRepl) => { server.tool('create_app', 'Create app: custom name, region (US/EU), team, private space', createAppOptionsSchema.shape, async (options) => { const command = new CommandBuilder(TOOL_COMMAND_MAP.CREATE_APP) .addPositionalArguments({ app: options.app }) .addFlags({ region: options.region, space: options.space, team: options.team }) .build(); const output = await herokuRepl.executeCommand(command); return handleCliOutput(output); }); }; /** * Schema for app rename */ export const renameAppOptionsSchema = z.object({ app: z.string().describe('Current app name. Requires access'), newName: z.string().describe('New unique app name') }); /** * Register rename_app tool * * @param server MCP server instance * @param herokuRepl Heroku REPL instance */ export const registerRenameAppTool = (server, herokuRepl) => { server.tool('rename_app', 'Rename app: validate and update app name', renameAppOptionsSchema.shape, async (options) => { const command = new CommandBuilder(TOOL_COMMAND_MAP.RENAME_APP) .addFlags({ app: options.app }) .addPositionalArguments({ newName: options.newName }) .build(); const output = await herokuRepl.executeCommand(command); return handleCliOutput(output); }); };