ServiceStack/src/ServiceStack.AI.Chat/API_TOOLS.md
ServiceStack API Tools let an AI assistant discover, understand, and call an application's existing ServiceStack APIs. Applications do not need to create a separate set of AI-specific endpoints. The same typed Request DTOs, validation rules, authorization requirements, service implementations, and response DTOs used by human clients become available to AI agents.
API Tools are used by the built-in AI.Chat interface and can also be exposed to external assistants through the built-in MCP server described in MCP.md.
An application may contain hundreds of APIs. Publishing every API as an individual model tool would consume a large context window before the user asks a question. API Tools instead expose three stable tools:
api_search finds relevant APIs using compact metadata.api_describe loads complete schemas only for APIs the assistant intends to use.api_call invokes one selected API as the current user.This search, describe, call sequence keeps the initial tool context small while allowing the assistant to use the application's full opted-in API surface.
When API Tools are available, follow this sequence:
api_search using words from the user's request. Do not guess an API name.api_describe for the likely APIs, including prerequisite, preview, and final write APIs when applicable.inputSchema, required properties, validation metadata, examples, safety, prerequisites, preview, and follow-ups.preview API before a write. Use its normalized response as the basis for the final request.api_call with the exact API name and an args object matching the described schema.Search again with broader user vocabulary or an available tag when no API matches.
services.AddPlugin(new ChatFeature
{
Tools =
{
EnableApiTools = true,
},
ApiTools =
{
IncludeTags = ["CoffeeShop"],
IncludeTypes = ["GetSpecialReport"],
ExcludeTypes = ["InternalMaintenance"],
DefaultTake = 25,
MaxTake = 100,
MaxResultLength = 32 * 1024,
},
});
An API is exposed when any of these conditions applies:
[Tool].[Tag] is listed in ApiTools.IncludeTags.ApiTools.IncludeTypes.ExcludeTypes always wins. APIs excluded from ServiceStack metadata or restricted with [Restrict] are not exposed.
Existing ServiceStack metadata remains the source of truth. Add [Tool] only for agent-specific selection and workflow hints.
[Tag("CoffeeShop")]
[Description("Submits a validated coffee shop order")]
[Tool(
"the user has finished choosing an order and wants to place it",
Safety = ToolSafety.Write,
RequiresApproval = true,
Keywords = ["buy", "checkout", "place order"],
Prerequisites = [nameof(GetCoffeeShopMenu)],
Preview = nameof(PreviewCoffeeShopOrder),
FollowUps = [nameof(GetCoffeeShopOrder)],
Aliases = ["PlaceCoffeeShopOrder"],
Examples = ["""{"customerName":"Sam","items":[{"productId":7,"quantity":2}]}"""])]
[Route("/coffee-shop/orders", "POST")]
public class CreateCoffeeShopOrder : IPost, IReturn<CreateCoffeeShopOrderResponse>
{
[Description("Name to put on the order")]
[ValidateNotEmpty]
public string CustomerName { get; set; } = "";
[Description("Final order items")]
[ValidateNotEmpty]
public List<OrderItemRequest> Items { get; set; } = [];
}
Useful metadata includes:
[Description], [Notes], and [ApiMember] for API and property meaning.[Validate*], required fields, enums, and [ApiAllowableValues] for valid input.[Tag] for API grouping and bulk exposure.[Input], [Ref], and other UI metadata for schema-driven approval forms and lookups.IReturn<T> or the registered response type for the output schema.ToolAttribute features| Property | Meaning |
|---|---|
WhenToUse | User situation in which the assistant should select the API. The positional constructor argument sets this value. |
Name | Stable tool-facing API name. Defaults to the Request DTO name. |
Keywords | Additional user vocabulary used by search. |
Aliases | Alternative names that participate in search and resolve in api_call. |
Examples | Realistic JSON request examples returned by api_describe. |
Prerequisites | APIs normally called before this API. |
Preview | Read-only API that validates or prices the proposed operation. |
FollowUps | APIs commonly useful after success. |
Safety | Auto, ReadOnly, Write, or Destructive. |
RequiresApproval | Requires human approval even if the operation would otherwise run unattended. |
Fields | Default response field projection for query APIs. |
Take | Default row limit for query APIs. |
Group | Tool group used to enable or disable related APIs together; defaults to the first API tag. |
Exclude | Prevents exposure. |
api_searchInput:
{
"query": "place coffee order",
"tag": "CoffeeShop",
"take": 20
}
Search considers API names, split CamelCase names, aliases, keywords, tags, when-to-use text, descriptions, and routes. Minor name typos are tolerated. Results are filtered to APIs the current caller can access.
Success returns structured API summaries:
{
"status": "success",
"count": 2,
"apis": [
{
"name": "CreateCoffeeShopOrder",
"request": "CreateCoffeeShopOrder",
"summary": "Submits a validated coffee shop order",
"tags": ["CoffeeShop"],
"safety": "write",
"method": "POST",
"route": "/coffee-shop/orders"
}
],
"next": "Call api_describe with the names of the APIs you intend to use"
}
A no-match result includes availableTags, suggestedApis, and a recovery instruction.
api_describeInput:
{
"names": [
"GetCoffeeShopMenu",
"PreviewCoffeeShopOrder",
"CreateCoffeeShopOrder"
]
}
Each returned API includes:
inputSchema, a copy of the request schema.outputSchema when the response type is known.tool.name, tool.safety, tool.requiresApproval, tool.whenToUse, and examples.prerequisites, preview, and followUps when declared.If an API does not exist or is unavailable to the caller, its entry contains an error instead of revealing inaccessible schema.
api_callInput:
{
"name": "PreviewCoffeeShopOrder",
"args": {
"CustomerName": "Sam",
"Items": [
{
"ProductId": 7,
"Quantity": 2,
"Size": "Grande",
"Temperature": "Hot",
"Options": [
{ "Type": "Milks", "Name": "Oat Milk" },
{ "Type": "Syrups", "Name": "Vanilla Syrup", "Quantity": "light" }
]
}
]
}
}
Successful calls return:
{
"status": "success",
"api": "PreviewCoffeeShopOrder",
"request": {},
"response": {},
"truncated": false,
"next": ["CreateCoffeeShopOrder"]
}
The request is deserialized into the real Request DTO and sent through ServiceStack's in-process Service Gateway. DTO validation, service logic, and database behavior are therefore shared with ordinary API clients.
Unknown argument fields are rejected before execution, including a nearest-field suggestion when possible. Standard ServiceStack validation remains authoritative for required values, ranges, and business rules.
API Tools require an HTTP request to act on behalf of. They do not execute as an unrestricted application service account.
Discovery and execution enforce the caller's:
An inaccessible API is omitted from search, cannot be described, and cannot be called. The in-process gateway executes using the same request context after access is asserted.
ToolSafety.Auto is inferred conservatively:
GET, HEAD, and OPTIONS become ReadOnly.DELETE becomes Destructive.POST, PUT, PATCH, unknown verbs, and ambiguous handlers become Write.Set safety explicitly when HTTP semantics do not describe the real consequence.
In interactive AI.Chat:
RequiresApproval is set.Approval is a pre-execution decision. An assistant must not report success merely because it proposed a call.
MCP has a separate policy because an external client cannot display AI.Chat's approval form. See RejectToolsRequiringApproval in MCP.md.
For QueryBase requests, API Tools apply a default Take, cap it at MaxTake, and optionally apply [Tool(Fields)] when the assistant did not specify fields. MaxResultLength truncates oversized serialized results.
Assistants should still narrow queries themselves:
truncated: true as a signal to make a narrower call, not as complete data.For “Order two grande hot oat milk lattes with light vanilla syrup for Sam”:
api_search({"query":"order oat milk latte vanilla syrup"})api_describe the menu, preview, and create APIs.api_call(GetCoffeeShopMenu) to resolve Latte to the current product ID and verify choices.api_call(PreviewCoffeeShopOrder) to validate and price the normalized order.api_call(CreateCoffeeShopOrder) with the validated arguments.This pattern generalizes to purchasing, bookings, ticket creation, deployments, messaging, and other workflows where current data must be resolved before a consequential operation.
availableTags and suggestedApis.Never fabricate a successful mutation, identifier, total, or status when a tool call failed or remains pending approval.