Octopus
Tool Schema
Every MCP tool has a JSON Schema definition that describes its name, description, and input parameters. The LLM uses this schema to decide when to call the tool and to construct valid input arguments. Well-written schemas are critical to reliable tool selection.
ToolDefinition Structure
public class ToolDefinition
{
public string Name { get; init; } = string.Empty;
public string Description { get; init; } = string.Empty;
public JsonDocument InputSchema { get; init; } = JsonDocument.Parse("{}");
}
// Example: well-written tool definition
var leaveTool = new ToolDefinition
{
Name = "get_employee_leave_balance",
Description = "Returns the number of annual leave days remaining for an employee. " +
"Call this when the user asks about their leave balance, remaining days, " +
"or how many days they can take off.",
InputSchema = JsonDocument.Parse("""
{
"type": "object",
"properties": {
"employeeId": {
"type": "string",
"description": "The employee's unique ID (e.g. 'EMP-1042'). " +
"Obtain from the user if not already known."
},
"year": {
"type": "integer",
"description": "The leave year (defaults to current year if not specified)."
}
},
"required": ["employeeId"]
}
""")
};
JSON Schema Types
| JSON Schema Type | C# Equivalent | LLM Behaviour |
|---|---|---|
"type": "string" | string | LLM passes a quoted string value |
"type": "integer" | int | LLM passes a numeric integer |
"type": "number" | double | LLM passes a numeric value (may have decimals) |
"type": "boolean" | bool | LLM passes true or false |
"type": "array" | List<T> | LLM passes a JSON array |
"type": "object" | class | LLM passes a nested JSON object |
"enum": [...] | enum | LLM picks from the specified values |
Writing Effective Descriptions
The tool description is the most important field for reliable tool selection. Guidelines:
- State what it does: "Returns the remaining leave days for an employee"
- State when to call it: "Call this when the user asks about leave balance, remaining days..."
- State what it does NOT do: "Does not update the balance — use update_leave_balance for that"
- Keep it under 200 words: Longer descriptions waste tokens and may confuse the LLM
- Name parameters explicitly: Each parameter description should explain what it expects
Schema Anti-Patterns
| Anti-Pattern | Problem | Fix |
|---|---|---|
| Vague description: "Gets leave data" | LLM doesn't know when to call it | "Returns remaining annual leave days. Call when user asks about leave balance." |
| All parameters required | LLM fails if it can't determine a value | Make optional parameters optional; provide defaults in the handler |
| Overlapping tool names | LLM picks the wrong one | Ensure each tool has a distinct, unambiguous purpose |
| No parameter descriptions | LLM guesses parameter format | Describe expected format for each parameter |
Generating Schemas from C# Types
// Use a schema generator to avoid hand-writing JSON Schema
public class LeaveBalanceInput
{
[JsonPropertyName("employeeId")]
[Description("The employee's unique ID, e.g. 'EMP-1042'")]
public string EmployeeId { get; set; } = string.Empty;
[JsonPropertyName("year")]
[Description("Leave year; defaults to current year if omitted")]
public int? Year { get; set; }
}
// Generate schema using NJsonSchema or System.Text.Json.Schema
JsonSchema schema = JsonSchema.FromType<LeaveBalanceInput>();
string jsonSchemaString = schema.ToJson();