
A permission-aware search tool
The most common tool pattern: search the workspace on the user's behalf and register citations.
[Tool("Search support tickets. Use when user describes a problem or asks 'have we seen this?'")]
public static async Task<string> SearchTickets(ToolScope scope,
[Parameter("User's question or symptom description", required: true)] string query,
[Parameter("Optional product SKU to scope the search", required: false)] string productSku,
[Parameter("Max results to return (default 5)", required: false)] int limit)
{
var search = SearchRequest.For(query);
search.BeforeTypesFacet = new(["Ticket"]);
if (!string.IsNullOrWhiteSpace(productSku))
search.TargetUIDs = scope.Graph.Q()
.StartAt("Product", productSku).In("ForProduct")
.AsUIDEnumerable().ToArray();
var results = (await scope.Graph.CreateSearchAsUserAsync(
search, scope.CurrentUser, scope.CancellationToken)).Take(limit > 0 ? limit : 5);
var output = results.Select(n => {
var text = scope.ChatAI.GetTextFromNode(n.UID, limit: 4000);
var id = scope.AddSnippet(uid: n.UID, text: text); // register citation
return new { snippetId = id, subject = n.GetString("Subject") };
}).ToArray();
scope.SetToolCallDisplayName($"Searched tickets for '{query}'");
return output.ToJson();
}
Three things to note: CurrentUser threads the user's identity through the search, TargetUIDs scopes by graph, AddSnippet wires citations into the chat response.
→ AI tools