/**
* LiteFarm MCP Server - Farm Management Tools
*/
import { z } from "zod";
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { LiteFarmClient } from "../litefarm-client.js";
import { ResponseFormat, type LiteFarmFarm } from "../types.js";
import {
createToolResponse,
createErrorResponse,
formatListResponse,
formatDate,
objectToMarkdownTable,
validateRequiredFields
} from "../tool-utils.js";
import { DEFAULT_LIMIT, MAX_LIMIT } from "../constants.js";
/**
* Register all farm-related tools
*/
export function registerFarmTools(server: McpServer, client: LiteFarmClient): void {
// List all farms
server.registerTool(
"litefarm_list_farms",
{
title: "List All Farms",
description: `List all farms associated with the authenticated user.
This tool retrieves all farms that the current user has access to, including farms they own and farms they've been invited to.
Args:
- response_format ('markdown' | 'json'): Output format (default: 'markdown')
Returns:
For JSON format: Array of farm objects with schema:
[{
"farm_id": string, // Unique farm identifier
"farm_name": string, // Name of the farm
"address": string, // Physical address (optional)
"farm_phone_number": string, // Contact number (optional)
"created_at": string, // ISO 8601 timestamp
"updated_at": string // ISO 8601 timestamp
}]
Examples:
- Use when: "Show me all my farms" or "What farms do I have access to?"
- Use when: Starting to explore farm data or selecting a farm to work with
Error Handling:
- Returns empty list if no farms are found
- Returns authentication error if login fails`,
inputSchema: z.object({
response_format: z.nativeEnum(ResponseFormat)
.default(ResponseFormat.MARKDOWN)
.describe("Output format: 'markdown' or 'json'")
}).strict(),
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true
}
},
async (params) => {
try {
const farms = await client.getFarms();
if (!farms || farms.length === 0) {
return createToolResponse("No farms found for this account.");
}
const formatter = (farm: LiteFarmFarm) => `**${farm.farm_name}** (ID: ${farm.farm_id})
- Address: ${farm.address || "Not specified"}
- Phone: ${farm.farm_phone_number || "Not specified"}
- Created: ${formatDate(farm.created_at)}`;
const response = formatListResponse(farms, formatter, params.response_format);
return createToolResponse(response.text, response.structuredContent);
} catch (error) {
return createErrorResponse(error);
}
}
);
// Get specific farm details
server.registerTool(
"litefarm_get_farm",
{
title: "Get Farm Details",
description: `Get detailed information about a specific farm by ID.
This tool retrieves comprehensive information about a single farm, including its location, contact details, and metadata.
Args:
- farm_id (string): The unique identifier of the farm
- response_format ('markdown' | 'json'): Output format (default: 'markdown')
Returns:
For JSON format: Farm object with schema:
{
"farm_id": string,
"farm_name": string,
"address": string,
"farm_phone_number": string,
"grid_points": object, // Geographic coordinates
"created_at": string,
"updated_at": string
}
Examples:
- Use when: "Tell me about farm ABC123"
- Use when: Need detailed information about a specific farm
- Don't use when: You need a list of all farms (use litefarm_list_farms)
Error Handling:
- Returns "Farm not found" if farm_id doesn't exist
- Returns permission error if user doesn't have access to the farm`,
inputSchema: z.object({
farm_id: z.string()
.min(1, "Farm ID is required")
.describe("The unique identifier of the farm"),
response_format: z.nativeEnum(ResponseFormat)
.default(ResponseFormat.MARKDOWN)
.describe("Output format: 'markdown' or 'json'")
}).strict(),
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true
}
},
async (params) => {
try {
const farm = await client.getFarm(params.farm_id);
if (params.response_format === ResponseFormat.JSON) {
return createToolResponse(JSON.stringify(farm, null, 2), farm);
}
const markdown = `# ${farm.farm_name}
${objectToMarkdownTable({
"Farm ID": farm.farm_id,
"Address": farm.address || "Not specified",
"Phone": farm.farm_phone_number || "Not specified",
"Created": formatDate(farm.created_at),
"Updated": formatDate(farm.updated_at)
})}`;
return createToolResponse(markdown, farm);
} catch (error) {
return createErrorResponse(error);
}
}
);
// Create new farm
server.registerTool(
"litefarm_create_farm",
{
title: "Create New Farm",
description: `Create a new farm in the LiteFarm system.
This tool creates a new farm with the specified details and associates it with the current user as the owner.
Args:
- farm_name (string): Name of the farm (required, 1-100 characters)
- address (string): Physical address of the farm (optional)
- farm_phone_number (string): Contact phone number (optional)
- grid_points (object): Geographic coordinates (optional, JSON object)
Returns:
The newly created farm object with generated farm_id
Examples:
- Use when: "Create a new farm called Green Valley Farm"
- Use when: User wants to start managing a new farming operation
- Don't use when: Updating an existing farm (use litefarm_update_farm)
Error Handling:
- Returns validation error if farm_name is missing or invalid
- Returns error if user has reached farm creation limit`,
inputSchema: z.object({
farm_name: z.string()
.min(1, "Farm name is required")
.max(100, "Farm name must not exceed 100 characters")
.describe("Name of the farm"),
address: z.string()
.optional()
.describe("Physical address of the farm"),
farm_phone_number: z.string()
.optional()
.describe("Contact phone number"),
grid_points: z.record(z.unknown())
.optional()
.describe("Geographic coordinates (JSON object)")
}).strict(),
annotations: {
readOnlyHint: false,
destructiveHint: false,
idempotentHint: false,
openWorldHint: true
}
},
async (params) => {
try {
const validation = validateRequiredFields(params, ["farm_name"]);
if (!validation.valid) {
return createErrorResponse(`Missing required fields: ${validation.missing.join(", ")}`);
}
const newFarm = await client.createFarm({
farm_name: params.farm_name,
...(params.address && { address: params.address }),
...(params.farm_phone_number && { farm_phone_number: params.farm_phone_number }),
...(params.grid_points && { grid_points: params.grid_points })
});
const markdown = `✅ **Farm Created Successfully!**
${objectToMarkdownTable({
"Farm ID": newFarm.farm_id,
"Farm Name": newFarm.farm_name,
"Address": newFarm.address || "Not specified",
"Created": formatDate(newFarm.created_at)
})}`;
return createToolResponse(markdown, newFarm);
} catch (error) {
return createErrorResponse(error);
}
}
);
// Update farm
server.registerTool(
"litefarm_update_farm",
{
title: "Update Farm",
description: `Update an existing farm's information.
This tool allows updating various farm properties. Only provided fields will be updated.
Args:
- farm_id (string): The unique identifier of the farm to update (required)
- farm_name (string): New name for the farm (optional)
- address (string): New address (optional)
- farm_phone_number (string): New phone number (optional)
Returns:
The updated farm object
Examples:
- Use when: "Update the address for farm ABC123 to 123 Main St"
- Use when: "Change farm name to Organic Valley Farm"
- Don't use when: Creating a new farm (use litefarm_create_farm)
Error Handling:
- Returns "Farm not found" if farm_id doesn't exist
- Returns permission error if user doesn't own the farm`,
inputSchema: z.object({
farm_id: z.string()
.min(1, "Farm ID is required")
.describe("The unique identifier of the farm"),
farm_name: z.string()
.min(1)
.max(100)
.optional()
.describe("New name for the farm"),
address: z.string()
.optional()
.describe("New physical address"),
farm_phone_number: z.string()
.optional()
.describe("New contact phone number")
}).strict(),
annotations: {
readOnlyHint: false,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true
}
},
async (params) => {
try {
const { farm_id, ...updateData } = params;
if (Object.keys(updateData).length === 0) {
return createErrorResponse("No update fields provided. Specify at least one field to update.");
}
const updatedFarm = await client.updateFarm(farm_id, updateData);
const markdown = `✅ **Farm Updated Successfully!**
${objectToMarkdownTable({
"Farm ID": updatedFarm.farm_id,
"Farm Name": updatedFarm.farm_name,
"Address": updatedFarm.address || "Not specified",
"Updated": formatDate(updatedFarm.updated_at)
})}`;
return createToolResponse(markdown, updatedFarm);
} catch (error) {
return createErrorResponse(error);
}
}
);
}