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
- Build a
FileAccessProvider over an `InMem
- Take the
file_access_ls tool from ProvideAIContextAsync.
- 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:
- 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.
- 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.
Description
The
[Description]text on severalFileAccessProviderandFileMemoryProvidertools refers to arguments by snake_case names, butAIFunctionFactorygenerates 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 aglob_pattern", while the generated schema exposes the argument asglobPattern. A model that follows the description emitsglob_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):
file_access_ls,file_memory_ls,file_access_grep,file_memory_grepglob_patternglobPatternfile_access_replace,file_memory_replaceold_stringoldStringfile_access_replace,file_memory_replacefile_access_replace,file_memory_replacereplace_allreplaceAllNot affected, and worth stating so a fix does not "correct" them by mistake:
line_numberandnew_linein thereplace_linesdescriptions are genuinely snatsets them explicitly with[JsonPropertyName("line_number")]/[JsonPropertyName("new_line")]. Only bare method parameters are affected, since they carry no such attribute andAgentJsonUtilt onJsonSerializerDefaults.Web.Steps to reproduce
FileAccessProviderover an `InMemfile_access_lstool fromProvideAIContextAsync.tool.JsonSchemaand compare the pros[Description].Code Sample
Error Messages / Stack Traces
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:
file_access_read_lines), so the arguments end up in a different convention from the tools that carry them.FileLineEditand 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.