We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Bundelkund/Litefarm-mcp-server'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
/**
* LiteFarm MCP Server - Animal 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 LiteFarmAnimal, type LiteFarmAnimalBatch, type LiteFarmAnimalType } from "../types.js";
import {
createToolResponse,
createErrorResponse,
formatListResponse,
formatDate,
objectToMarkdownTable,
validateRequiredFields
} from "../tool-utils.js";
/**
* Register all animal-related tools
*/
export function registerAnimalTools(server: McpServer, client: LiteFarmClient): void {
// Get available animal types
server.registerTool(
"litefarm_get_animal_types",
{
title: "Get Animal Types",
description: `Get all available animal types (e.g., CHICKEN, COW, PIG).
This tool retrieves the default animal types that can be used when creating animals or animal batches.
Use this to prevent errors with invalid type_id values.
Args:
- response_format ('markdown' | 'json'): Output format (default: 'markdown')
Returns:
For JSON format: Array of animal type objects with schema:
[{
"id": number, // Type ID to use in create_animal
"key": string, // Type key (e.g., "CHICKEN", "COW")
"default_type_name": string // Display name
}]
Examples:
- Use when: "What animal types are available?" or "Show me valid animal species"
- Use when: Before creating animals to get valid default_type_id values
Error Handling:
- Returns empty list if no types 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 types = await client.get<LiteFarmAnimalType[]>("/default_animal_types");
if (!types || types.length === 0) {
return createToolResponse("No animal types found.");
}
const formatter = (type: LiteFarmAnimalType) => `**${type.default_type_name}** (ID: ${type.id}, Key: ${type.key})`;
const response = formatListResponse(types, formatter, params.response_format);
return createToolResponse(response.text, response.structuredContent);
} catch (error) {
return createErrorResponse(error);
}
}
);
// List all animals
server.registerTool(
"litefarm_list_animals",
{
title: "List All Animals",
description: `List all animals for the currently selected farm.
This tool retrieves all individual animals (not batches) with their type names and internal identifiers.
Args:
- farm_id (string): Farm ID to list animals for (optional, uses selected farm if not provided)
- response_format ('markdown' | 'json'): Output format (default: 'markdown')
Returns:
For JSON format: Array of animal objects with schema:
[{
"id": number,
"name": string,
"default_type_id": number,
"sex_id": number, // 1=MALE, 2=FEMALE
"birth_date": string, // ISO 8601
"internal_identifier": string, // Auto-assigned identifier
"farm_id": string,
"created_at": string,
"updated_at": string
}]
Examples:
- Use when: "Show me all animals" or "List all animals on the farm"
- Use when: Verifying animals were created successfully
Error Handling:
- Returns empty list if no animals are found
- Returns error if farm not selected`,
inputSchema: z.object({
farm_id: z.string()
.optional()
.describe("Farm ID (optional, uses selected farm if not provided)"),
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 {
// Select farm if provided
if (params.farm_id) {
await client.selectFarm(params.farm_id);
}
const farmId = client.getSelectedFarmId();
if (!farmId) {
return createErrorResponse("No farm selected. Please provide farm_id or select a farm first.");
}
const animals = await client.get<LiteFarmAnimal[]>(`/animals/farm/${farmId}`);
if (!animals || animals.length === 0) {
return createToolResponse("No animals found for this farm.");
}
const formatter = (animal: LiteFarmAnimal) => `**${animal.name || "Unnamed"}** (ID: ${animal.id})
- Internal ID: ${animal.internal_identifier || "N/A"}
- Type ID: ${animal.default_type_id || animal.custom_type_id || "N/A"}
- Sex: ${animal.sex_id === 1 ? "Male" : animal.sex_id === 2 ? "Female" : "Not specified"}
- Birth Date: ${animal.birth_date ? formatDate(animal.birth_date) : "Not specified"}
- Created: ${formatDate(animal.created_at!)}`;
const response = formatListResponse(animals, formatter, params.response_format);
return createToolResponse(response.text, response.structuredContent);
} catch (error) {
return createErrorResponse(error);
}
}
);
// Create individual animal
server.registerTool(
"litefarm_create_animal",
{
title: "Create Individual Animal",
description: `Create a single animal in the LiteFarm system.
This tool creates an individual animal with specified properties. Only default_type_id (or custom_type_id or type_name) is required.
Args:
- default_type_id (number): Default animal type ID (required, use litefarm_get_animal_types to get valid IDs)
- custom_type_id (number): Custom farm-specific type ID (alternative to default_type_id)
- type_name (string): Create new type on-the-fly (alternative to default_type_id)
- name (string): Animal name (optional)
- sex_id (number): Sex identifier - 1=MALE, 2=FEMALE (optional)
- birth_date (string): Birth date in ISO 8601 format (optional, e.g., "2023-01-15")
- default_breed_id (number): Default breed ID (optional)
- custom_breed_id (number): Custom breed ID (optional)
- identifier (string): Custom identifier (optional)
- identifier_type_id (number): Identifier type (optional)
- origin_id (number): Origin ID (optional)
- notes (string): Notes about the animal (optional)
- farm_id (string): Farm ID (optional, uses selected farm if not provided)
Returns:
The newly created animal object with generated id and internal_identifier
Examples:
- Use when: "Create a cow named Bessie born on 2023-01-15"
- Use when: "Add a female chicken to the farm"
- Don't use when: Creating multiple animals at once (use litefarm_create_animal_batch)
Error Handling:
- Returns validation error if default_type_id is missing and no alternative provided
- Returns error if invalid type_id`,
inputSchema: z.object({
default_type_id: z.number()
.optional()
.describe("Default animal type ID (required unless custom_type_id or type_name provided)"),
custom_type_id: z.number()
.optional()
.describe("Custom farm-specific type ID (alternative to default_type_id)"),
type_name: z.string()
.optional()
.describe("Create new type on-the-fly (alternative to default_type_id)"),
name: z.string()
.optional()
.describe("Animal name"),
sex_id: z.number()
.optional()
.describe("Sex identifier: 1=MALE, 2=FEMALE"),
birth_date: z.string()
.optional()
.describe("Birth date in ISO 8601 format (e.g., '2023-01-15')"),
default_breed_id: z.number()
.optional()
.describe("Default breed ID"),
custom_breed_id: z.number()
.optional()
.describe("Custom breed ID"),
identifier: z.string()
.optional()
.describe("Custom identifier"),
identifier_type_id: z.number()
.optional()
.describe("Identifier type"),
origin_id: z.number()
.optional()
.describe("Origin ID"),
notes: z.string()
.optional()
.describe("Notes about the animal"),
farm_id: z.string()
.optional()
.describe("Farm ID (optional, uses selected farm if not provided)")
}).strict(),
annotations: {
readOnlyHint: false,
destructiveHint: false,
idempotentHint: false,
openWorldHint: true
}
},
async (params) => {
try {
// Validate that at least one type identifier is provided
if (!params.default_type_id && !params.custom_type_id && !params.type_name) {
return createErrorResponse("Missing required field: Provide one of default_type_id, custom_type_id, or type_name");
}
// Select farm if provided
if (params.farm_id) {
await client.selectFarm(params.farm_id);
}
const farmId = client.getSelectedFarmId();
if (!farmId) {
return createErrorResponse("No farm selected. Please provide farm_id or select a farm first.");
}
// Build animal payload (API expects array)
const animalData: Partial<LiteFarmAnimal> = {
...(params.default_type_id && { default_type_id: params.default_type_id }),
...(params.custom_type_id && { custom_type_id: params.custom_type_id }),
...(params.type_name && { type_name: params.type_name }),
...(params.name && { name: params.name }),
...(params.sex_id && { sex_id: params.sex_id }),
...(params.birth_date && { birth_date: params.birth_date }),
...(params.default_breed_id && { default_breed_id: params.default_breed_id }),
...(params.custom_breed_id && { custom_breed_id: params.custom_breed_id }),
...(params.identifier && { identifier: params.identifier }),
...(params.identifier_type_id && { identifier_type_id: params.identifier_type_id }),
...(params.origin_id && { origin_id: params.origin_id }),
...(params.notes && { notes: params.notes })
};
// POST expects array of animals
const response = await client.post<LiteFarmAnimal[]>("/animals", [animalData]);
const newAnimal = response[0]; // API returns array, take first element
const markdown = `✅ **Animal Created Successfully!**
${objectToMarkdownTable({
"Animal ID": newAnimal.id?.toString() || "N/A",
"Name": newAnimal.name || "Unnamed",
"Internal Identifier": newAnimal.internal_identifier || "Assigned by API",
"Type ID": (newAnimal.default_type_id || newAnimal.custom_type_id)?.toString() || "N/A",
"Sex": newAnimal.sex_id === 1 ? "Male" : newAnimal.sex_id === 2 ? "Female" : "Not specified",
"Birth Date": newAnimal.birth_date ? formatDate(newAnimal.birth_date) : "Not specified",
"Created": formatDate(newAnimal.created_at!)
})}`;
return createToolResponse(markdown, newAnimal);
} catch (error) {
return createErrorResponse(error);
}
}
);
// Create animal batch
server.registerTool(
"litefarm_create_animal_batch",
{
title: "Create Animal Batch",
description: `Create a batch of multiple animals at once.
This tool creates a group of animals with the same properties (e.g., "10 Laying Hens").
Only default_type_id (or custom_type_id or type_name) and count are required.
Args:
- count (number): Number of animals in the batch (required)
- default_type_id (number): Default animal type ID (required, use litefarm_get_animal_types to get valid IDs)
- custom_type_id (number): Custom farm-specific type ID (alternative to default_type_id)
- type_name (string): Create new type on-the-fly (alternative to default_type_id)
- name (string): Batch name (optional, e.g., "Laying Hens")
- sex_id (number): Sex identifier - 1=MALE, 2=FEMALE (optional)
- farm_id (string): Farm ID (optional, uses selected farm if not provided)
Returns:
The newly created animal batch object with generated id and internal_identifier
Examples:
- Use when: "Create 10 laying hens"
- Use when: "Add a batch of 5 female chickens"
- Don't use when: Creating a single named animal (use litefarm_create_animal)
Error Handling:
- Returns validation error if count or default_type_id is missing
- Returns error if invalid type_id`,
inputSchema: z.object({
count: z.number()
.min(1, "Count must be at least 1")
.describe("Number of animals in the batch"),
default_type_id: z.number()
.optional()
.describe("Default animal type ID (required unless custom_type_id or type_name provided)"),
custom_type_id: z.number()
.optional()
.describe("Custom farm-specific type ID (alternative to default_type_id)"),
type_name: z.string()
.optional()
.describe("Create new type on-the-fly (alternative to default_type_id)"),
name: z.string()
.optional()
.describe("Batch name (e.g., 'Laying Hens')"),
sex_id: z.number()
.optional()
.describe("Sex identifier: 1=MALE, 2=FEMALE"),
farm_id: z.string()
.optional()
.describe("Farm ID (optional, uses selected farm if not provided)")
}).strict(),
annotations: {
readOnlyHint: false,
destructiveHint: false,
idempotentHint: false,
openWorldHint: true
}
},
async (params) => {
try {
// Validate required fields
const validation = validateRequiredFields(params, ["count"]);
if (!validation.valid) {
return createErrorResponse(`Missing required fields: ${validation.missing.join(", ")}`);
}
// Validate that at least one type identifier is provided
if (!params.default_type_id && !params.custom_type_id && !params.type_name) {
return createErrorResponse("Missing required field: Provide one of default_type_id, custom_type_id, or type_name");
}
// Select farm if provided
if (params.farm_id) {
await client.selectFarm(params.farm_id);
}
const farmId = client.getSelectedFarmId();
if (!farmId) {
return createErrorResponse("No farm selected. Please provide farm_id or select a farm first.");
}
// Build batch payload (API expects array)
const batchData: Partial<LiteFarmAnimalBatch> = {
count: params.count,
...(params.default_type_id && { default_type_id: params.default_type_id }),
...(params.custom_type_id && { custom_type_id: params.custom_type_id }),
...(params.type_name && { type_name: params.type_name }),
...(params.name && { name: params.name }),
...(params.sex_id && { sex_id: params.sex_id })
};
// POST expects array of batches
const response = await client.post<LiteFarmAnimalBatch[]>("/animal_batches", [batchData]);
const newBatch = response[0]; // API returns array, take first element
const markdown = `✅ **Animal Batch Created Successfully!**
${objectToMarkdownTable({
"Batch ID": newBatch.id?.toString() || "N/A",
"Name": newBatch.name || "Unnamed batch",
"Count": newBatch.count?.toString() || "0",
"Internal Identifier": newBatch.internal_identifier || "Assigned by API",
"Type ID": (newBatch.default_type_id || newBatch.custom_type_id)?.toString() || "N/A",
"Sex": newBatch.sex_id === 1 ? "Male" : newBatch.sex_id === 2 ? "Female" : "Not specified",
"Created": formatDate(newBatch.created_at!)
})}`;
return createToolResponse(markdown, newBatch);
} catch (error) {
return createErrorResponse(error);
}
}
);
}