import { z } from "zod"
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"
import type { ChainlinkApiClient } from "../utils/api-client.js"
import { validateNetwork, validateAddress } from "../utils/validators.js"
import { formatNetworkName, formatTimestamp, formatAddress } from "../utils/formatters.js"
export function registerAutomationTools(server: McpServer, apiClient: ChainlinkApiClient) {
// Tool: Create Automation
server.tool(
"create_automation",
"Create a new Chainlink Automation job to automatically execute smart contract functions based on time or custom conditions.",
{
name: z.string().describe("Automation job name"),
targetContract: z.string().describe("Contract address to automate"),
functionSelector: z.string().describe("Function selector or signature to call"),
triggerType: z.enum(["time", "conditional", "log"]).describe("Type of trigger for automation"),
triggerConfig: z.record(z.any()).describe("Configuration for the trigger"),
network: z.string().optional().describe("Blockchain network (default: ethereum)")
},
async ({ name, targetContract, functionSelector, triggerType, triggerConfig, network = "ethereum" }) => {
try {
const networkValidation = validateNetwork(network)
if (!networkValidation.valid) {
throw new Error(networkValidation.error)
}
const addressValidation = validateAddress(targetContract)
if (!addressValidation.valid) {
throw new Error(addressValidation.error)
}
// Mock automation creation
const automationId = `auto_${Math.random().toString(36).substr(2, 9)}`
const result = {
automationId,
name,
targetContract: formatAddress(targetContract),
functionSelector,
triggerType,
triggerConfig,
network: formatNetworkName(network),
status: "active",
createdAt: formatTimestamp(Date.now() / 1000),
nextExecution: triggerType === "time" ? formatTimestamp(Date.now() / 1000 + 3600) : "Conditional",
executionCount: 0
}
return {
content: [
{
type: "text",
text: `Automation "${name}" created successfully with ID: ${automationId}\n\n` +
`Details:\n${JSON.stringify(result, null, 2)}`
}
]
}
} catch (error) {
return {
content: [
{
type: "text",
text: `Error: ${error instanceof Error ? error.message : String(error)}`
}
]
}
}
}
)
// Tool: List Automations
server.tool(
"list_automations",
"List all active Chainlink Automation jobs. Shows status, execution history, and configuration details.",
{
network: z.string().optional().describe("Filter by network"),
status: z.enum(["active", "paused", "cancelled"]).optional().describe("Filter by status"),
limit: z.number().min(1).max(50).default(10).describe("Maximum results")
},
async ({ network, status, limit = 10 }) => {
try {
if (network) {
const networkValidation = validateNetwork(network)
if (!networkValidation.valid) {
throw new Error(networkValidation.error)
}
}
// Mock automation list
const mockAutomations = [
{
automationId: "auto_harvest123",
name: "Yield Harvesting",
targetContract: "0x1234567890123456789012345678901234567890",
triggerType: "conditional",
status: "active",
network: "ethereum",
executionCount: 45,
lastExecuted: "2024-01-20T14:30:00Z",
nextExecution: "Conditional"
},
{
automationId: "auto_rebase456",
name: "Token Rebase",
targetContract: "0x9876543210987654321098765432109876543210",
triggerType: "time",
status: "active",
network: "ethereum",
executionCount: 12,
lastExecuted: "2024-01-20T12:00:00Z",
nextExecution: "2024-01-21T12:00:00Z"
}
]
let filteredAutomations = mockAutomations
if (network) {
filteredAutomations = filteredAutomations.filter(a => a.network.toLowerCase() === network.toLowerCase())
}
if (status) {
filteredAutomations = filteredAutomations.filter(a => a.status === status)
}
const limitedAutomations = filteredAutomations.slice(0, limit)
const result = {
automations: limitedAutomations.map(automation => ({
...automation,
targetContract: formatAddress(automation.targetContract, true),
network: formatNetworkName(automation.network),
lastExecuted: automation.lastExecuted ? formatTimestamp(automation.lastExecuted) : "Never",
nextExecution: automation.nextExecution.includes("T") ? formatTimestamp(automation.nextExecution) : automation.nextExecution
})),
total: filteredAutomations.length,
showing: limitedAutomations.length
}
return {
content: [
{
type: "text",
text: `Found ${filteredAutomations.length} automation job(s)\n\n` +
`Details:\n${JSON.stringify(result, null, 2)}`
}
]
}
} catch (error) {
return {
content: [
{
type: "text",
text: `Error: ${error instanceof Error ? error.message : String(error)}`
}
]
}
}
}
)
// Tool: Get Automation Status
server.tool(
"get_automation_status",
"Get detailed status and execution history for a specific automation job.",
{
automationId: z.string().describe("Automation job ID"),
network: z.string().optional().describe("Blockchain network")
},
async ({ automationId, network = "ethereum" }) => {
try {
const networkValidation = validateNetwork(network)
if (!networkValidation.valid) {
throw new Error(networkValidation.error)
}
// Mock status retrieval
const mockStatus = {
automationId,
name: "Yield Harvesting Bot",
status: "active",
network: formatNetworkName(network),
targetContract: formatAddress("0x1234567890123456789012345678901234567890"),
functionSelector: "harvest()",
triggerType: "conditional",
executionCount: 45,
successfulExecutions: 44,
failedExecutions: 1,
lastExecuted: formatTimestamp("2024-01-20T14:30:00Z"),
nextExecution: "When conditions are met",
gasUsed: "~150,000 per execution",
totalCost: "0.15 ETH",
uptime: "99.8%"
}
return {
content: [
{
type: "text",
text: `Automation job "${mockStatus.name}" is running successfully\n\n` +
`Details:\n${JSON.stringify(mockStatus, null, 2)}`
}
]
}
} catch (error) {
return {
content: [
{
type: "text",
text: `Error: ${error instanceof Error ? error.message : String(error)}`
}
]
}
}
}
)
// Tool: Pause Automation
server.tool(
"pause_automation",
"Pause a running automation job. Can be resumed later without losing configuration.",
{
automationId: z.string().describe("Automation job ID to pause"),
network: z.string().optional().describe("Blockchain network")
},
async ({ automationId, network = "ethereum" }) => {
try {
const networkValidation = validateNetwork(network)
if (!networkValidation.valid) {
throw new Error(networkValidation.error)
}
const result = {
automationId,
previousStatus: "active",
newStatus: "paused",
network: formatNetworkName(network),
pausedAt: formatTimestamp(Date.now() / 1000)
}
return {
content: [
{
type: "text",
text: `Automation job ${automationId} has been paused\n\n` +
`Details:\n${JSON.stringify(result, null, 2)}`
}
]
}
} catch (error) {
return {
content: [
{
type: "text",
text: `Error: ${error instanceof Error ? error.message : String(error)}`
}
]
}
}
}
)
// Tool: Resume Automation
server.tool(
"resume_automation",
"Resume a paused automation job. Will continue executing based on original configuration.",
{
automationId: z.string().describe("Automation job ID to resume"),
network: z.string().optional().describe("Blockchain network")
},
async ({ automationId, network = "ethereum" }) => {
try {
const networkValidation = validateNetwork(network)
if (!networkValidation.valid) {
throw new Error(networkValidation.error)
}
const result = {
automationId,
previousStatus: "paused",
newStatus: "active",
network: formatNetworkName(network),
resumedAt: formatTimestamp(Date.now() / 1000),
nextExecution: "Will execute when conditions are met"
}
return {
content: [
{
type: "text",
text: `Automation job ${automationId} has been resumed\n\n` +
`Details:\n${JSON.stringify(result, null, 2)}`
}
]
}
} catch (error) {
return {
content: [
{
type: "text",
text: `Error: ${error instanceof Error ? error.message : String(error)}`
}
]
}
}
}
)
// Tool: Cancel Automation
server.tool(
"cancel_automation",
"Cancel an automation job permanently. This action cannot be undone.",
{
automationId: z.string().describe("Automation job ID to cancel"),
network: z.string().optional().describe("Blockchain network")
},
async ({ automationId, network = "ethereum" }) => {
try {
const networkValidation = validateNetwork(network)
if (!networkValidation.valid) {
throw new Error(networkValidation.error)
}
const result = {
automationId,
previousStatus: "active",
newStatus: "cancelled",
network: formatNetworkName(network),
cancelledAt: formatTimestamp(Date.now() / 1000),
finalExecutionCount: 45,
refundAmount: "0.05 ETH"
}
return {
content: [
{
type: "text",
text: `Automation job ${automationId} has been cancelled permanently\n\n` +
`Details:\n${JSON.stringify(result, null, 2)}`
}
]
}
} catch (error) {
return {
content: [
{
type: "text",
text: `Error: ${error instanceof Error ? error.message : String(error)}`
}
]
}
}
}
)
}