Skip to content

.NET: [Bug]: CodeAct/Hyperlight: host tool parameter names/schema are never surfaced to the model, calling contract must be hand-written into the tool [Description] #8125

Open
1 of 2 issues completed

Description

Description

When host tools are registered on HyperlightCodeActProviderOptions.Tools, the model reaches them via call_tool("name", { ... }) inside execute_code. But the instructions injected for execute_code include only each tool's name and description, never its parameter names or JSON schema. As a result, the model cannot know what argument names to pass unless the developer manually encodes the calling contract inside the tool's [Description]. When the contract is omitted, call_tool fails at runtime with a "missing required parameter" error.

This differs from normal function/MCP tool calling, where the full parameter schema (names + per-parameter descriptions) is sent to the model.

Repro (.NET, Microsoft.Agents.AI.Hyperlight 1.15.0-preview.260722.1)

public static class InventoryTools
{
    [Description("Gets the stock level (units on hand) of a spare part.")]
    public static int GetStock(string partNumber) => partNumber switch
    {
        "BRG-4410" => 12, "SEAL-220" => 3, "FLT-901" => 47, _ => 0
    };

    [Description("Gets the unit price in EUR of a spare part.")]
    public static decimal GetPrice(string partNumber) => partNumber switch
    {
        "BRG-4410" => 89.50m, "SEAL-220" => 14.25m, "FLT-901" => 6.80m, _ => 0m
    };
}

var options = HyperlightCodeActProviderOptions.CreateForJavaScript();
options.Tools =
[
    AIFunctionFactory.Create(InventoryTools.GetStock, "get_stock"),
    AIFunctionFactory.Create(InventoryTools.GetPrice, "get_price"),
];

using var codeFunction = new HyperlightExecuteCodeFunction(options);
Console.WriteLine(codeFunction.BuildInstructions()); // inspect what the model sees

The generated execute_code description lists the tools like this. note there is no parameter information at all.

The following host tools are available inside the sandbox via `call_tool("<name>", **kwargs)`:
- `get_stock`: Gets the stock level (units on hand) of a spare part.
- `get_price`: Gets the unit price in EUR of a spare part.

Running a prompt that needs these tools then fails, because the model can't guess the parameter name.
tool 'get_stock': The arguments dictionary is missing a value for the required parameter 'partNumber'.

Workaround — bake the calling contract into the description:

[Description("""
    Gets the stock level (units on hand) of a spare part.
    Call as: call_tool("get_stock", { partNumber: "BRG-4410" })
    """)]

With this, the model emits the correct argument name and the calls succeed.

Code Sample

public static class InventoryTools
{
    // NOTE: no calling contract in the description -> model cannot learn the param name
    [Description("Gets the stock level (units on hand) of a spare part.")]
    public static int GetStock(string partNumber) => partNumber switch
    {
        "BRG-4410" => 12, "SEAL-220" => 3, "FLT-901" => 47, _ => 0
    };

    [Description("Gets the unit price in EUR of a spare part.")]
    public static decimal GetPrice(string partNumber) => partNumber switch
    {
        "BRG-4410" => 89.50m, "SEAL-220" => 14.25m, "FLT-901" => 6.80m, _ => 0m
    };
}

var options = HyperlightCodeActProviderOptions.CreateForJavaScript();
options.Tools =
[
    AIFunctionFactory.Create(InventoryTools.GetStock, "get_stock"),
    AIFunctionFactory.Create(InventoryTools.GetPrice, "get_price"),
];

// Inspect exactly what the model is told about the tools:
using var codeFunction = new HyperlightExecuteCodeFunction(options);
Console.WriteLine(codeFunction.BuildInstructions());
// -> lists get_stock / get_price with NAME + DESCRIPTION only. No parameter info.

// Running a prompt that needs these tools then fails (see Error Messages).

Error Messages / Stack Traces

tool 'get_stock': The arguments dictionary is missing a value for the required parameter 'partNumber'. (Parameter 'arguments')

Package Versions

Microsoft.Agents.AI: 1.15.0 Microsoft.Agents.AI.OpenAI: 1.15.0 Microsoft.Agents.AI.Hyperlight: 1.15.0-preview.260722.1

.NET Version

.NET 10.0

Additional Context

Suspected root cause (from decompiling the shipped 1.15.0-preview.260722.1 DLL): InstructionBuilder.BuildExecuteCodeDescription, in its foreach (var tool in tools) loop, appears to append only tool.Name and tool.Description per tool — I don't see tool.JsonSchema read anywhere in the method. If that's correct, parameter-level [Description] attributes are dropped entirely, leaving the tool-level description as the only channel that reaches the model.

There also seems to be no fallback schema channel: HyperlightExecuteCodeFunction.JsonSchema returns a hardcoded static declaring only execute_code's own code parameter.

{ "type": "object", "properties": { "code": { "type": "string" } }, "required": ["code"] }

so nothing structured about the host tools' parameters would reach the model. Please correct me if I'm misreading the decompiled source.

What the model actually receives from BuildInstructions().

The following host tools are available inside the sandbox via `call_tool("<name>", **kwargs)`:
- `get_stock`: Gets the stock level (units on hand) of a spare part.
- `get_price`: Gets the unit price in EUR of a spare part.

Workaround: encode the calling contract in the description, e.g. Call as: call_tool("get_stock", { partNumber: "BRG-4410" }). With this present, the model emits the right argument name and calls succeed.

Minor doc note: the Hyperlight docs limitation #6 mentions "Tool descriptions, parameter annotations, and return shapes matter more here…", which I initially read as parameter annotations being passed to the model. If the behavior above is correct, that line might be worth a small clarification. The samples all happen to use a single self-evident parameter, so this case doesn't surface in them.

Is the schema is left out deliberately for prompt-size reasons or anything else. Then I'm happy to prepare a focused fix with test coverage serializing each host tool's parameter schema into the execute_code listing if the maintainers are comfortable with that. Please let me know whether this can be assigned to me.

Related: this came out of feedback on my article; a maintainer asked me to file it. See also Discussion #5328.

Activity

  1. added
    reproducedUsage: [Issues], Target: all issues that can be reproduced by the triage workflow
    on Sep 7, 2026
  2. github-actions commented on Sep 7, 2026

    @github-actions
    Contributor

    🤖 Automated triage reproduction notes (agent-authored — trust but verify)

    Agent analysis

    The failure occurs in dotnet/src/Microsoft.Agents.AI.Hyperlight/Internal/InstructionBuilder.cs::BuildExecuteCodeDescription lines 58-75 when a host AIFunction with required parameters is registered. Minimal repro: create get_stock with a described partNumber parameter, confirm its JsonSchema contains that metadata, then assert the generated execute-code description contains it; the latter assertion fails.

    • Failing test: dotnet/tests/Microsoft.Agents.AI.Hyperlight.UnitTests/InstructionBuilderTests.cs
    • Files examined: dotnet/src/Microsoft.Agents.AI.Hyperlight/Internal/InstructionBuilder.cs, dotnet/src/Microsoft.Agents.AI.Hyperlight/HyperlightExecuteCodeFunction.cs, dotnet/src/Microsoft.Agents.AI.Hyperlight/HyperlightCodeActProvider.cs, dotnet/tests/Microsoft.Agents.AI.Hyperlight.UnitTests/InstructionBuilderTests.cs, dotnet/src/Microsoft.Agents.AI.Hyperlight/Microsoft.Agents.AI.Hyperlight.csproj, dotnet/nuget/nuget-package.props
    • Tests run: existing InstructionBuilderTests baseline (6 passed), BuildExecuteCodeDescription_WithToolParameters_IncludesParameterMetadata (failed)
    • Reported version: 1.15.0-preview.260722.1
    • Current version: 1.20.0-preview
  3. added
    skillsUsage: [Issues, PRs], Target: skills related features
    and removed
    triageUsage: [Issues], Target: All issues that still need to be triaged
    on Sep 7, 2026
  4. eavanvalkenburg commented on Sep 7, 2026

    @eavanvalkenburg
    Member

    Thanks Pasindu Madusha (@epcm18) I indeed asked for this! We'll have a look and fix!

  5. atty57 commented on Sep 7, 2026

    @atty57
    Contributor

    Eduard van Valkenburg (@eavanvalkenburg) - I'd like to pick this one up if it isn't already in progress on your side.

    Scope looks contained: InstructionBuilder.BuildExecuteCodeDescription (dotnet/src/Microsoft.Agents.AI.Hyperlight/Internal/InstructionBuilder.cs) currently emits only tool.Name + tool.Description for each host tool. Each AIFunction already exposes JsonSchema, so the parameter metadata is available at that point , it just isn't rendered. Fix plus a case in InstructionBuilderTests should be a small diff, no public API change.

    One call I'd want your steer on before writing it on how the parameters should be rendered:

    1. Raw JSON schema per tool. Exact and complete (types, required, enums, per-parameter descriptions), matches what normal function calling sends, but verbose in the execute_code description when several tools are registered.
    2. Compact signature line, e.g. get_stock(partNumber: string): Gets the stock level... - much cheaper on tokens, but drops per-parameter descriptions and richer constraints.
    3. Hybrid - signature line plus a nested bullet per parameter that has a description.
      Scope looks contained: InstructionBuilder.BuildExecuteCodeDescription (dotnet/src/Microsoft.Agents.AI.Hyperlight/Internal/InstructionBuilder.cs) currently emits only tool.Name + tool.Description for each host tool. Each AIFunction already exposes JsonSchema, so the parameter metadata is available at that point ,it just isn't rendered. Fix plus a case in InstructionBuilderTests should be a small diff, no public API change.

    One call I'd want your steer on before writing it - how the parameters should be rendered

    1. Raw JSON schema per tool. Exact and complete (types, required, enums, per-parameter demal function calling sends, but verbose in the execute_code description when several tools are registered.
    2. Compact signature line, e.g. get_stock(partNumber: string): Gets the stock level... drops per-parameter descriptions and richer constraints.
    3. Hybrid - signature line plus a nested bullet per parameter that has a description.

    I'd lean towards 3 as the default, since it keeps the token cost near 2 while not losing the per-parameter descriptions that developers write. Happy to go with whichever you prefer, or to leave it to you if you've already started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

.NETUsage: [Issues, PRs], Target: .NetreproducedUsage: [Issues], Target: all issues that can be reproduced by the triage workflowskillsUsage: [Issues, PRs], Target: skills related features

Type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions