Skip to content

.NET: [Bug]: Harness file tool descriptions name arguments in snake_case, but the generated schema is camelCase #7672

Description

Description

The [Description] text on several FileAccessProvider and FileMemoryProvider tools refers to arguments by snake_case names, but AIFunctionFactory generates the tool schema from the C# parameter names, which are camelCase. The model is told to use one name and offered another.

What happened. file_access_ls's description says "Optionally filter entries with a glob_pattern", while the generated schema exposes the argument as globPattern. A model that follows the description emits glob_pattern, and the argument fails to bind — silently dropped, so the tool runs with the filter missing rather than reporting an error.

What I expected. The description names the argument exactly as the schema does.

Affected tools and arguments (both providers):

Tool Description says Schema exposes
file_access_ls, file_memory_ls, file_access_grep, file_memory_grep glob_pattern globPattern
file_access_replace, file_memory_replace old_string oldString
file_access_replace, file_memory_replace
file_access_replace, file_memory_replace replace_all replaceAll

Not affected, and worth stating so a fix does not "correct" them by mistake: line_number and new_line in the
replace_lines descriptions are genuinely snat sets them explicitly with[JsonPropertyName("line_number")]/[JsonPropertyName("new_line")]. Only bare method parameters are affected, since they carry no such attribute and AgentJsonUtilt on JsonSerializerDefaults.Web.

Steps to reproduce

  1. Build a FileAccessProvider over an `InMem
  2. Take the file_access_ls tool from ProvideAIContextAsync.
  3. Print tool.JsonSchema and compare the pros [Description].

Code Sample

var provider = new FileAccessProvider(new InMemoryAgentFileStore());
var context = new AIContextProvider.InvokingContext(agent, session, new AIContext());
AIContext ctx = await provider.InvokingAsync(context);

AIFunction ls = ctx.Tools!.OfType<AIFunction>().First(t => t.Name == "file_access_ls");
Console.WriteLine(ls.JsonSchema);
// {"type":"object","properties":{"directory":{...},"globPattern":{...}},...}
//                                                ^^^^^^^^^^^ description says glob_pattern


Observed on `file_access_read_lines` while reviewing #7671, where the generated schema was:


{"type":"object","properties":{"fileName":{"type":"string"},"startLine":{"type":"integer"},"endLine":{"type":["integer","null"],"default":null}},"required":["fileName","startLine"]}

Error Messages / Stack Traces

None — this is the problem. An unrecognized argument name does not raise; the call proceeds with the argument unset, so `file_access_ls` returns an unfiltered listing and `file_access_replace` fails its own "old_string not found" check for a reason that has nothing to do with the file.

Package Versions

Microsoft.Agents.AI: 1.17.0

.NET Version

No response

Additional Context

Found during Copilot review of #7671, which fixes the same defect in the tool that PR adds (file_access_read_lines) but deliberately leaves these pre-existing cases alone to keep that diff scoped to the new tool.

Two ways to fix, and they are not equivalent:

  1. Change the descriptions to camelCase — smallest change, no behavioural risk, but the harness tool names are snake_case (file_access_read_lines), so the arguments end up in a different convention from the tools that carry them.
  2. Give the parameters snake_case schema names so the schema matches the existing descriptions — more consistent with FileLineEdit and with the tool names, but it changes the advertised schema, which is a behavioural break for anyone who has pinned the current argument names.

Option 2 is the more coherent end state; option 1 is what is safe to do without maintainer agreement. Happy to submit either.

Metadata

Metadata

Assignees

No one assigned

    Labels

    .NETUsage: [Issues, PRs], Target: .NetreproducedUsage: [Issues], Target: all issues that can be reproduced by the triage workflowtriageUsage: [Issues], Target: All issues that still need to be triaged

    Type

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions