Skip to content
Shiny.Maui.Shell v6 support for AI routing tools Learn More

AI Tools

Expose your document store operations as Microsoft.Extensions.AI tool functions that LLM agents can call directly. Register the document types you want to expose, control which operations are allowed, and restrict field visibility — all with a fluent builder API.

The AI layer sits on top of IDocumentStore and does not modify its behavior. It wraps your registered types with AIFunction instances that translate LLM requests into store operations.

  1. Install the AI extensions package

    Terminal window
    dotnet add package Shiny.DocumentDb.Extensions.AI
  2. Create your JSON context (for AOT)

    [JsonSerializable(typeof(Customer))]
    [JsonSerializable(typeof(Order))]
    public partial class AppJsonContext : JsonSerializerContext;
  3. Register the document store and AI tools

    var jsonContext = new AppJsonContext(new JsonSerializerOptions
    {
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase
    });
    services.AddDocumentStore(opts =>
    {
    opts.DatabaseProvider = new SqliteDatabaseProvider("Data Source=mydata.db");
    opts.JsonSerializerOptions = jsonContext.Options;
    });
    services.AddDocumentStoreAITools(tools =>
    {
    tools.AddType(
    jsonContext.Customer,
    capabilities: DocumentAICapabilities.All,
    configure: b => b
    .Description("Customer records with contact info")
    .MaxPageSize(50)
    );
    tools.AddType(
    jsonContext.Order,
    capabilities: DocumentAICapabilities.ReadOnly,
    configure: b => b
    .Description("Customer orders")
    .Property(o => o.Status, "Order status: Pending, Shipped, Delivered, Cancelled")
    .IgnoreProperties(o => o.InternalNotes)
    );
    });
  4. Pass the tools to your IChatClient

    var aiTools = serviceProvider.GetRequiredService<DocumentStoreAITools>();
    var options = new ChatOptions { Tools = aiTools.Tools.ToList() };
    var response = await chatClient.GetResponseAsync(messages, options);

Control which operations the LLM can perform on each type using the DocumentAICapabilities flags enum:

FlagTool GeneratedDescription
Get{type}_get_by_idFetch a single document by ID
Query{type}_queryQuery with structured filters, sorting, and paging
Count{type}_countCount documents with optional filter
Aggregate{type}_aggregateCompute sum/min/max/avg/count over documents
Insert{type}_insertCreate a new document
Update{type}_updateReplace an existing document
Delete{type}_deleteDelete a document by ID

Convenience combinations:

CombinationIncludes
ReadOnlyGet, Query, Count, Aggregate
AllAll seven operations
// Read-only access (default)
tools.AddType(jsonContext.Customer, capabilities: DocumentAICapabilities.ReadOnly);
// Full CRUD
tools.AddType(jsonContext.Order, capabilities: DocumentAICapabilities.All);
// Specific operations
tools.AddType(jsonContext.AuditLog, capabilities: DocumentAICapabilities.Get | DocumentAICapabilities.Query);

The builder callback lets you control descriptions, field visibility, and page size limits:

tools.AddType(jsonContext.Customer, capabilities: DocumentAICapabilities.All, configure: b =>
{
// Type-level description used in tool descriptions and JSON schema
b.Description("Customer records with contact information");
// Override the description for a specific property
b.Property(c => c.Age, "Customer's age in years");
b.Property(c => c.Status, "Active, Inactive, or Suspended");
// Restrict which properties the LLM can see/filter on
// Option A: Only expose listed properties (allowlist)
b.AllowProperties(c => c.Id, c => c.Name, c => c.Email, c => c.Age);
// Option B: Hide specific properties (blocklist)
b.IgnoreProperties(c => c.InternalNotes, c => c.PasswordHash);
// Cap the maximum page size for query results (default 100)
b.MaxPageSize(50);
});

The query, count, and aggregate tools accept a structured filter parameter that supports nested boolean logic. The LLM constructs filter objects; the library translates them to LINQ expressions against the document store.

{ "field": "age", "op": "gt", "value": 30 }
OperatorDescription
eqEquals
neNot equals
gtGreater than
gteGreater than or equal
ltLess than
lteLess than or equal
containsString contains (string fields only)
startsWithString starts with (string fields only)
inValue is in array
{
"and": [
{ "field": "age", "op": "gte", "value": 18 },
{ "field": "status", "op": "eq", "value": "Active" }
]
}
{
"or": [
{ "field": "city", "op": "eq", "value": "Portland" },
{ "field": "city", "op": "eq", "value": "Seattle" }
]
}
{
"not": { "field": "status", "op": "eq", "value": "Cancelled" }
}

Combinators can be nested arbitrarily.

Fetches a single document by its identifier. Returns { found: true, document: {...} } or { found: false }.

Queries documents with optional filter, sorting, and pagination. Parameters:

ParameterTypeDescription
filterobjectStructured filter (optional)
orderBystringField name to sort by (optional)
orderDirectionstring"asc" or "desc" (default "asc")
limitintegerMax results to return (default 50, capped at MaxPageSize)
offsetintegerNumber of results to skip (default 0)

Returns { count, offset, limit, documents: [...] }.

Counts documents matching an optional filter. Returns { count }.

Computes a scalar aggregate over documents. Parameters:

ParameterTypeDescription
functionstring"count", "sum", "min", "max", or "avg"
fieldstringNumeric field to aggregate (required for sum/min/max/avg)
filterobjectStructured filter (optional)

Returns { function, field, value }.

Creates a new document. Accepts a document object and returns { inserted: true, document: {...} } with the auto-generated ID populated.

Replaces an existing document. Accepts a document object (must include the ID). Returns { updated: true }.

Deletes a document by identifier. Returns { deleted: true } or { deleted: false } if not found.

The Sample.CopilotConsole project in the repository demonstrates a complete interactive chat application that authenticates with GitHub Copilot and uses the DocumentDb AI tools to let the LLM query and manage documents through natural language:

// Seed data, authenticate with Copilot, then chat
var aiTools = host.Services.GetRequiredService<DocumentStoreAITools>();
var options = new ChatOptions { Tools = aiTools.Tools.ToList() };
while (true)
{
Console.Write("You: ");
var input = Console.ReadLine();
history.Add(new ChatMessage(ChatRole.User, input));
var response = await chatClient.GetResponseAsync(history, options);
Console.WriteLine($"Copilot: {response.Text}");
}

Try prompts like:

  • “How many customers do we have?”
  • “Show me all pending orders”
  • “What’s the average customer age?”
  • “Add a new customer named Dave, age 40, email dave@example.com
  • “Delete customer cust-3”