DOTNET EXPERT BLOG

Build Production-Ready AI Agent Tools in C# | Day 11

10/6/2026 12:55:31 AM Noor All Safaet Loading... 0

Our AI agent can now use real application capabilities.

In Day 10, we gave it tools such as:

get_product_stock
search_knowledge

That allowed the agent to combine structured business data with document knowledge and decide which capability was needed to complete a user goal.

But giving an agent a C# method is only the beginning.

In a real application, every tool should answer important questions:

Who is allowed to call it?
What inputs are valid?
What should it return?
How should failures be represented?
Can it modify business data?
Does the action require approval?
How do we monitor its execution?

A poorly designed tool can make even a capable model unreliable.

A well-designed tool gives the agent a narrow, predictable, and secure way to interact with your application.

In Day 11, we will turn our basic agent tools into production-ready C# capabilities.

Our architecture will evolve toward:

User Goal
   ↓
AI Agent
   ↓
Tool Selection
   ↓
Tool Contract
   ↓
Validation
   ↓
Authorization
   ↓
Application Service
   ↓
Execution
   ↓
Structured Result
   ↓
Agent

We will cover:

  • function tool design

  • AIFunctionFactory

  • tool names and descriptions

  • input validation

  • authorization and tenant isolation

  • structured results

  • predictable errors

  • read vs write tools

  • human approval boundaries

  • idempotency

  • observability

  • tool versioning

  • testing

  • API-backed tools

  • MCP tools

  • agent composition

By the end, our agent will not simply have more tools.

It will have better tools.

Previously in This Series

We are building one practical AI application with C# and .NET.

Day 1: Build Your First AI Application
Connected C# to an AI model.

Day 2: Understanding IChatClient
Introduced the common .NET chat abstraction.

Day 3: Build a Reusable AI Chat Service
Added ASP.NET Core and dependency injection.

Day 4: Conversation Memory
Added multi-turn conversations.

Day 5: Streaming
Streamed AI responses.

Day 6: Structured Output
Returned strongly typed C# results.

Day 7: Function Calling
Allowed the model to request C# functions.

Day 8: AI + Database
Connected tools to EF Core and real business data.

Day 9: RAG
Retrieved document knowledge using vector search.

Day 10: AI Agent
Combined multiple capabilities through Microsoft Agent Framework.

Today we focus on the boundary between the agent and our application:

Agent Tools

A Tool Is an Application Contract

It is easy to think of a tool as:

Method the AI can call

A better definition is:

A tool is a controlled application contract exposed to an AI agent.

Suppose our agent needs current stock information.

We could expose:

Task<ProductStockInfo?>
    GetProductStockAsync(
        string productName);

But the actual architecture should remain:

Agent
   ↓
Tool Contract
   ↓
Application Service
   ↓
Business Rules
   ↓
EF Core
   ↓
Database

The tool should not become a shortcut around the rest of the application.

A production-quality tool should usually be:

Focused
Predictable
Validated
Authorized
Bounded
Observable

For example:

get_product_stock

is focused.

This is not:

execute_business_operation

Narrow capabilities are easier for the model to select and easier for developers to secure, test, and monitor.

Function Tools in Microsoft Agent Framework

Microsoft Agent Framework can expose custom application code as function tools.

A .NET method can be converted into an AIFunction using:

AIFunctionFactory.Create(...)

For example:

using Microsoft.Extensions.AI;

AIFunction tool =
    AIFunctionFactory.Create(
        GetProductStockAsync,
        name: "get_product_stock",
        description:
            "Gets the current stock and reorder level for a product.");

Conceptually:

C# Method
    ↓
AIFunctionFactory
    ↓
AIFunction
    ↓
Tool Schema
    ↓
AI Model

The model can use the resulting metadata to understand the tool's name, purpose, parameters, and expected types.

However, schema generation does not replace application validation.

The model remains an untrusted caller.

Step 1: Start with a Clear Tool Contract

Instead of exposing a generic method:

public Task<object> ExecuteAsync(
    string action,
    object data)

prefer:

public Task<ProductStockToolResult>
    GetProductStockAsync(
        string productName,
        CancellationToken cancellationToken = default)

The agent does not need to understand:

Tables
Joins
SQL
DbContext
Connection Strings

It only needs a business capability:

Get the current stock information
for this product.

Step 2: Write Clear Tool Descriptions

Tool descriptions help the model decide when a capability should be used.

Create:

AI/InventoryAgentTools.cs

Then:

using System.ComponentModel;

public sealed class InventoryAgentTools
{
    private readonly IInventoryService
        _inventoryService;

    public InventoryAgentTools(
        IInventoryService inventoryService)
    {
        _inventoryService =
            inventoryService;
    }

    [Description(
        "Gets current inventory information for a product, including available quantity and reorder level.")]
    public async Task<ProductStockToolResult>
        GetProductStockAsync(
            [Description(
                "The product name to search for.")]
            string productName,
            CancellationToken cancellationToken = default)
    {
        // Implementation comes next.
        throw new NotImplementedException();
    }
}

Prefer specific names such as:

get_product_stock
search_knowledge
get_shipping_status

instead of generic names such as:

get_data
search
execute

A tool's name and description should clearly communicate one capability.

Step 3: Validate Tool Inputs

Model-generated arguments should be treated like API input.

For example:

private static string ValidateProductName(
    string productName)
{
    if (string.IsNullOrWhiteSpace(productName))
    {
        throw new ArgumentException(
            "Product name is required.",
            nameof(productName));
    }

    string value =
        productName.Trim();

    if (value.Length > 200)
    {
        throw new ArgumentException(
            "Product name cannot exceed 200 characters.",
            nameof(productName));
    }

    return value;
}

Then:

public async Task<ProductStockToolResult>
    GetProductStockAsync(
        string productName,
        CancellationToken cancellationToken = default)
{
    productName =
        ValidateProductName(productName);

    // Continue with application service.
}

Tool schemas help the model construct arguments.

Validation protects the application.

Step 4: Return Structured Results

Avoid returning loosely formatted text such as:

Mouse stock is 4 and reorder is 10.

Instead:

public sealed record ProductStockToolResult(
    bool Found,
    Guid? ProductId,
    string? ProductName,
    decimal? AvailableQuantity,
    decimal? ReorderLevel,
    bool? IsBelowReorderLevel,
    string? Message);

Implement the tool:

public async Task<ProductStockToolResult>
    GetProductStockAsync(
        string productName,
        CancellationToken cancellationToken = default)
{
    productName =
        ValidateProductName(productName);

    ProductStockInfo? product =
        await _inventoryService
            .GetProductStockAsync(
                productName,
                cancellationToken);

    if (product is null)
    {
        return new ProductStockToolResult(
            Found: false,
            ProductId: null,
            ProductName: null,
            AvailableQuantity: null,
            ReorderLevel: null,
            IsBelowReorderLevel: null,
            Message:
                "Product was not found.");
    }

    bool isBelowReorderLevel =
        product.StockQuantity <
        product.ReorderLevel;

    return new ProductStockToolResult(
        Found: true,
        ProductId: product.Id,
        ProductName: product.Name,
        AvailableQuantity:
            product.StockQuantity,
        ReorderLevel:
            product.ReorderLevel,
        IsBelowReorderLevel:
            isBelowReorderLevel,
        Message: null);
}

The agent now receives predictable data:

{
  "found": true,
  "productId": "a94d...",
  "productName": "Wireless Mouse",
  "availableQuantity": 4,
  "reorderLevel": 10,
  "isBelowReorderLevel": true,
  "message": null
}

This is easier to reason about than application-generated prose.

Keep Deterministic Logic in C#

Notice:

bool isBelowReorderLevel =
    product.StockQuantity <
    product.ReorderLevel;

The model could compare the two numbers, but this is deterministic business logic.

C# should calculate it.

The model can explain the result.

The application should determine the fact.

Let C# calculate facts. Let the model explain them.

This improves consistency and testability.

Step 5: Keep Trusted Context Out of Tool Arguments

Suppose our inventory system is multi-tenant.

Avoid:

GetProductStockAsync(
    Guid clientId,
    string productName)

because ClientId should not be chosen by the model.

Instead:

GetProductStockAsync(
    string productName)

and obtain the client from trusted application context:

public interface ICurrentClientContext
{
    Guid ClientId { get; }
}

Then:

public sealed class InventoryAgentTools
{
    private readonly IInventoryService
        _inventoryService;

    private readonly ICurrentClientContext
        _clientContext;

    public InventoryAgentTools(
        IInventoryService inventoryService,
        ICurrentClientContext clientContext)
    {
        _inventoryService =
            inventoryService;

        _clientContext =
            clientContext;
    }
}

The secure flow becomes:

Authenticated Request
      ↓
Current Client Context
      ↓
Agent Tool
      ↓
Inventory Service
      ↓
Client-Filtered Query

The same principle applies to:

UserId
OrganizationId
Role
Permission
Security Scope

Trusted identity should come from application state, not model arguments.

Step 6: Enforce Authorization in C#

Agent instructions may guide behavior, but they are not a security boundary.

Suppose:

public interface IAgentAuthorizationService
{
    Task<bool> CanReadInventoryAsync(
        CancellationToken cancellationToken = default);
}

The tool can enforce:

if (!await _authorizationService
        .CanReadInventoryAsync(
            cancellationToken))
{
    return new ProductStockToolResult(
        Found: false,
        ProductId: null,
        ProductName: null,
        AvailableQuantity: null,
        ReorderLevel: null,
        IsBelowReorderLevel: null,
        Message:
            "You are not authorized to access inventory information.");
}

The distinction is simple:

Prompt
=
Behavior Guidance

Application Code
=
Security Enforcement

Authorization belongs in application code.

Step 7: Return Predictable Errors

Infrastructure exceptions may expose information the model does not need.

Instead of returning:

NpgsqlException...
Host=...
Username=...
Connection refused...

use a controlled result:

public sealed record AgentToolResult<T>(
    bool Success,
    T? Data,
    string? ErrorCode,
    string? Message);

A successful result might be:

{
  "success": true,
  "data": {
    "productName": "Wireless Mouse",
    "availableQuantity": 4
  },
  "errorCode": null,
  "message": null
}

A controlled failure:

{
  "success": false,
  "data": null,
  "errorCode": "inventory_unavailable",
  "message": "Inventory information is temporarily unavailable."
}

Internally, log the technical exception.

The agent receives only the information it needs.

Also distinguish expected business outcomes from exceptions.

Examples:

Product not found
Order already cancelled
No relevant policy found
Insufficient stock

These are usually normal application states and should be represented explicitly.

Step 8: Separate Read Tools from Write Tools

Read tools include:

get_product_stock
search_knowledge
get_order_status

Write tools include:

create_purchase_order
update_product_price
cancel_order
issue_refund

The risk is different.

A read tool typically follows:

Agent
 ↓
Validation
 ↓
Authorization
 ↓
Retrieve Data
 ↓
Return Result

A write tool may require:

Agent
 ↓
Validation
 ↓
Authorization
 ↓
Business Rules
 ↓
Approval
 ↓
Transaction
 ↓
Audit
 ↓
Return Result

Write operations deserve stronger controls.

Step 9: Separate Recommendation from Execution

Suppose low stock may require a purchase order.

Instead of immediately exposing:

create_purchase_order

start with:

prepare_purchase_order

For example:

public sealed record PurchaseOrderProposal(
    Guid ProductId,
    string ProductName,
    decimal CurrentStock,
    decimal SuggestedQuantity,
    string Reason);

The result might be:

{
  "productId": "a94d...",
  "productName": "Wireless Mouse",
  "currentStock": 4,
  "suggestedQuantity": 50,
  "reason": "Current stock is below the configured reorder level."
}

At this point:

No purchase order has been created.

The architecture becomes:

Recommendation
      ↓
Approval
      ↓
Execution

For high-impact operations such as:

Create Purchase Order
Issue Refund
Cancel Order
Send Payment
Change Price
Publish Content

human approval may be appropriate.

Application authorization and validation must still run even after approval.

Step 10: Make Important Write Tools Idempotent

Suppose:

create_purchase_order

succeeds, but a timeout prevents the result from reaching the agent.

The agent retries.

Without protection:

PO #1001 created
PO #1002 created

For side-effecting operations, consider an idempotency key:

CreatePurchaseOrderAsync(
    PurchaseOrderRequest request,
    string idempotencyKey)

The application checks whether the same operation was already processed.

Request
   ↓
Idempotency Key
   ↓
Already Processed?
   ├── Yes → Return Existing Result
   └── No  → Execute

This is particularly important for:

Payments
Orders
Refunds
Bookings
Notifications
External API actions

Step 11: Keep Tool Results Small and Bounded

Do not return an entire entity graph when the agent only needs stock information.

Avoid unnecessarily returning:

Product
Supplier
Category
Stock History
Sales History
Purchase History
Audit History

Use a focused projection instead.

Benefits include:

Less sensitive data
Fewer tokens
Lower latency
Clearer reasoning
Smaller attack surface

Collection tools should also have limits.

For example:

int limit =
    Math.Clamp(
        requestedLimit,
        1,
        20);

Return the top relevant matches rather than thousands of records.

Step 12: Expose Business Capabilities, Not Infrastructure

Avoid tools such as:

execute_sql
call_any_url

Instead expose:

get_product_stock
find_low_stock_products
get_order_status
get_shipping_status

The application remains responsible for:

SQL
Endpoints
Authentication
Headers
Timeouts
Retries
Response Projection

For database access:

Agent
 ↓
Business Tool
 ↓
Application Service
 ↓
EF Core
 ↓
Database

For an external API:

Agent
 ↓
Shipping Tool
 ↓
Shipping Service
 ↓
Approved External API

The model expresses business intent without receiving unrestricted infrastructure access.

Step 13: Create an API-Backed Tool

Suppose we have:

public interface IShippingService
{
    Task<ShipmentStatus?>
        GetStatusAsync(
            string trackingNumber,
            CancellationToken cancellationToken = default);
}

Create:

public sealed class ShippingAgentTools
{
    private readonly IShippingService
        _shippingService;

    public ShippingAgentTools(
        IShippingService shippingService)
    {
        _shippingService =
            shippingService;
    }

    [Description(
        "Gets the current shipping status for an approved tracking number.")]
    public async Task<ShipmentStatus?>
        GetShippingStatusAsync(
            [Description(
                "The shipment tracking number.")]
            string trackingNumber,
            CancellationToken cancellationToken = default)
    {
        if (string.IsNullOrWhiteSpace(
            trackingNumber))
        {
            throw new ArgumentException(
                "Tracking number is required.",
                nameof(trackingNumber));
        }

        return await _shippingService
            .GetStatusAsync(
                trackingNumber.Trim(),
                cancellationToken);
    }
}

API credentials remain inside the application service.

They never become tool parameters.

Step 14: Use Dependency Injection

Register the tool classes:

builder.Services
    .AddScoped<InventoryAgentTools>();

builder.Services
    .AddScoped<KnowledgeAgentTools>();

builder.Services
    .AddScoped<ShippingAgentTools>();

They may depend on normal application components:

Application Services
Authorization Services
Current User Context
Tenant Context
HTTP Clients
Repositories
DbContext

Those dependencies remain internal.

Only the tool method signature becomes model-visible.

Step 15: Build a Focused Tool Set

Create the functions:

AIFunction getProductStock =
    AIFunctionFactory.Create(
        inventoryTools.GetProductStockAsync,
        name: "get_product_stock",
        description:
            "Gets current inventory information for a product.");

AIFunction searchKnowledge =
    AIFunctionFactory.Create(
        knowledgeTools.SearchKnowledgeAsync,
        name: "search_knowledge",
        description:
            "Searches approved internal business documentation.");

AIFunction getShippingStatus =
    AIFunctionFactory.Create(
        shippingTools.GetShippingStatusAsync,
        name: "get_shipping_status",
        description:
            "Gets the current shipping status for a tracking number.");

Then expose only the tools this agent needs:

AIAgent agent =
    new ChatClientAgent(
        chatClient,
        instructions:
            """
            You are a business assistant.

            Use available tools when verified
            business information is required.

            Never invent inventory quantities,
            company policies, or shipment status.

            If a tool cannot provide the required
            information, explain the limitation.
            """,
        tools:
        [
            getProductStock,
            searchKnowledge,
            getShippingStatus
        ]);

As the application grows, avoid giving every agent every capability.

For example:

Inventory Agent
   ├── get_product_stock
   ├── find_low_stock_products
   └── search_inventory_policy

Support Agent
   ├── get_ticket
   ├── search_documentation
   └── get_shipping_status

Start with the smallest useful capability set.

Step 16: Add Observability

When an agent produces a wrong or slow response, we need to understand what happened.

Useful telemetry may include:

Agent Run ID
Tool Name
Execution Duration
Success / Failure
Error Code
Result Count
Retry Count
Approval Status

Agent latency includes more than model latency:

Model
  ↓
Tool A
  ↓
Model
  ↓
Tool B
  ↓
Model

Monitor:

Tool execution time
Database query time
External API latency
Vector search latency
Model latency
Total agent duration

But avoid unnecessarily logging:

Passwords
API Keys
Connection Strings
Sensitive Documents
Payment Details
Private Customer Data

Observability should explain execution without creating another data-leak risk.

Step 17: Treat Tool Contracts Like Versioned APIs

Agent tools are application contracts.

As your application evolves, those contracts may evolve too.

For example, today:

get_product_stock

may return:

AvailableQuantity
ReorderLevel

Later, the business may need:

AvailableQuantity
ReservedQuantity
IncomingQuantity
ReorderLevel
Warehouse

Adding information is often straightforward, but changing the meaning of an existing field can affect agent behavior.

For example, imagine:

AvailableQuantity

originally means:

Physical stock currently in the warehouse

but later silently changes to:

Physical stock - reserved stock

The property name has not changed.

Its business meaning has.

An agent that previously relied on that value may now make a different decision.

Treat important tool contracts similarly to APIs:

Stable Tool Name
      ↓
Stable Input Contract
      ↓
Stable Output Contract
      ↓
Stable Business Meaning

When a breaking change is necessary, consider introducing a new contract instead of silently changing the existing one.

For example:

get_product_stock

could remain stable while a more specialized capability is introduced:

get_product_inventory_availability

You do not need to add v1, v2, and v3 to every tool from the beginning.

The important principle is:

Do not silently change a tool contract in a way that changes what the agent believes the tool means.

Before deploying an important tool-contract change:

  • review the input schema

  • review the output schema

  • check whether field meanings changed

  • update the tool description if necessary

  • rerun tool-level tests

  • test important agent workflows

  • verify authorization and tenant boundaries still apply

This becomes increasingly important when the same capability is consumed by multiple agents, applications, or MCP clients.

Step 18: Test Tools Without the Agent

A tool should be testable as normal C# code.

For example:

[Fact]
public async Task GetProductStockAsync_ReturnsLowStock()
{
    // Arrange
    // Act
    // Assert
}

Useful cases include:

Valid product
Unknown product
Empty product name
Unauthorized user
Wrong tenant
Database unavailable
Cancellation
Low stock
Normal stock

Test these separately:

Tool Logic
Tool Selection
Final AI Response

This makes failures easier to diagnose.

Step 19: When Should You Use MCP?

Our current tools live inside the ASP.NET Core application.

That is often the simplest choice for application-specific business logic.

But suppose the same capabilities need to be consumed by:

Your AI Agent
Another AI Application
Developer Tools
IDE Assistants
Other Agent Systems

The Model Context Protocol (MCP) can provide a standardized integration boundary.

Conceptually:

AI Application
      ↓
MCP Client
      ↓
MCP Server
      ↓
Tools / Resources

Microsoft Agent Framework supports MCP-based tool integrations.

MCP does not replace:

Authentication
Authorization
Validation
Secrets Management
Approval
Auditing

It standardizes how capabilities can be exposed and consumed.

A simple decision guide:

RequirementFunction ToolMCP Tool
Logic inside current applicationExcellentUsually unnecessary
Simple C# business serviceExcellentOptional
Shared by multiple AI clientsPossibleStrong fit
External standardized tool serverLess naturalStrong fit
Lowest complexityStrong fitMore infrastructure
Cross-application reuseLimitedStrong fit

For our inventory application, GetProductStockAsync can remain a local function tool.

MCP becomes more attractive when that capability needs to be reused outside the application.

Agents Can Also Become Tools

As responsibilities grow, a specialized agent can become a capability used by another agent.

Conceptually:

Coordinator Agent
   ├── Inventory Agent
   ├── Support Agent
   └── Finance Agent

This can be useful when responsibilities genuinely diverge.

But do not introduce multiple agents merely because the framework supports them.

A single agent with three focused tools is often easier to understand and operate than several unnecessary agents.

Recommended Production Architecture

A scalable design might look like:

                    AI Agent
                       ↓
                 Tool Contracts
                       ↓
        ┌──────────────┼──────────────┐
        ↓              ↓              ↓
   Inventory       Knowledge       Shipping
     Tools            Tools           Tools
        ↓              ↓              ↓
   Application     Retrieval       Application
    Services        Service         Service
        ↓              ↓              ↓
      EF Core      Vector Store   External API
        ↓
     Database

Around those capabilities:

Authentication
Authorization
Validation
Tenant Isolation
Approval
Idempotency
Logging
Metrics
Cancellation

The agent sees business capabilities.

The application keeps control of security and infrastructure.

Common Agent Tool Mistakes

Generic Tool Contracts

Avoid:

get_data
execute
process

Prefer one clearly defined business capability.

Returning Too Much Data

Return only what the agent needs.

Trusting Model Arguments

Validate model-generated values like any other untrusted input.

Passing Trusted Identity Through the Model

Resolve tenant, user, and permissions from application context.

Using Prompts as Authorization

Enforce permissions in C#.

Exposing Infrastructure

Avoid arbitrary SQL, URLs, or system-level capabilities.

Ignoring Write Risk

State-changing tools need stronger controls than read tools.

Ignoring Duplicate Execution

Use idempotency where repeated execution can create duplicate side effects.

Silently Changing Tool Contracts

Treat tool inputs, outputs, and business semantics as application contracts. Test important agent workflows before deploying breaking changes.

Exposing Internal Exceptions

Return controlled errors and keep technical details in internal logs.

Testing Only Through the LLM

Unit test tool behavior independently.

Production Checklist

Before exposing a tool to an AI agent, verify:

  • the tool has one clear responsibility

  • its name and description are specific

  • inputs are validated

  • model-generated values are treated as untrusted

  • tenant and user identity come from trusted context

  • authorization is enforced in application code

  • deterministic business logic remains in C#

  • outputs are structured and minimal

  • collection results are bounded

  • expected business failures are explicit

  • infrastructure exceptions are not exposed

  • write operations have stronger controls

  • sensitive actions can require approval

  • duplicate side effects are prevented where necessary

  • important tool contracts remain stable

  • breaking contract changes are tested before deployment

  • cancellation is propagated

  • execution is observable

  • logs avoid sensitive data

  • tools can be tested independently

  • MCP is introduced only when its reuse benefits justify it

What We Built Today

In Day 10, our architecture was:

User
 ↓
Agent
 ↓
Tools
 ↓
Application

Today we strengthened that boundary:

User
 ↓
Agent
 ↓
Tool Selection
 ↓
Validated Contract
 ↓
Trusted User / Tenant Context
 ↓
Authorization
 ↓
Business Logic
 ↓
Execution
 ↓
Structured Result
 ↓
Agent

We also introduced important production concepts:

Read vs Write Tools
Human Approval
Idempotency
Observability
Tool Versioning
Independent Testing
MCP
Agent Composition

The agent is becoming more capable.

But the application remains in control.

Day 11 Checklist

Before moving to Day 12, make sure you understand:

  • why a tool is an application contract

  • how AIFunctionFactory.Create() exposes C# methods

  • why names and descriptions matter

  • why tool arguments must be validated

  • why structured results are preferable

  • why deterministic logic belongs in C#

  • why trusted identity stays outside model arguments

  • why prompts cannot replace authorization

  • how business failures differ from infrastructure failures

  • why read and write tools have different risk

  • why high-impact actions may require approval

  • why write operations may need idempotency

  • why results should be minimal and bounded

  • why arbitrary SQL and HTTP tools are risky

  • how dependency injection supports tools

  • why tool execution needs observability

  • why tool contracts should remain stable

  • why tools should be tested independently

  • when MCP may be useful

What's Next: Day 12

Our agent now has production-quality capabilities.

But we intentionally stopped at an important boundary:

Agent recommends an action
        ↓
Should it actually execute it?

In Day 12: Add Human Approval to AI Agent Actions in C#, we will build that boundary properly.

We will explore:

Agent
 ↓
Proposed Action
 ↓
Approval Required
 ↓
Human Decision
 ├── Approve
 │      ↓
 │   Validate Again
 │      ↓
 │   Execute
 │
 └── Reject
        ↓
     Stop Action

We will focus on high-impact operations such as:

Create Purchase Order
Issue Refund
Cancel Order
Send External Message

The goal is to let the agent help prepare and coordinate actions without giving the model uncontrolled authority over real business state.

Final Thoughts

The quality of an AI agent depends heavily on the quality of its tools.

A strong production boundary looks like:

AI Reasoning
     ↓
Narrow Tool Contract
     ↓
Validation
     ↓
Authorization
     ↓
Deterministic Business Logic
     ↓
Controlled Execution

The model should understand intent.

Your application should control authority.

Give the agent capabilities, not unrestricted access.

A good tool tells the agent:

Here is one thing you are allowed to ask
the application to do.

It does not say:

Here is the entire system.
Do whatever you think is appropriate.

Day 10 taught our agent how to choose capabilities.

Day 11 made those capabilities safer, clearer, version-aware, and more predictable.

In Day 12, we will add human approval before sensitive agent actions are allowed to change real business state.

Comments 0