Native Shell
Native Shell: AI-generated UI, native in your app, on every platform. Apps on iOS, Android, macOS, Windows and Linux, GPUI apps and web pages load signed, sandboxed TSX modules at run time and draw th
Code-defined agents and toolsets for .NET on Microsoft.Extensions.AI
Developer toolsC# · ShellGitHub @weboost-atOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Code-defined AI agents and tools for .NET, checked at compile time. Built on Microsoft.Extensions.AI.
Taken from the repository README
Loading the full README.
Read the full README ↗Code-defined agents and toolsets for .NET 10, built on Microsoft.Extensions.AI.
Documentation · Getting started · Changelog
Build AI agents for .NET with just a few lines of code, and let the compiler catch your mistakes.
[!WARNING] Typeshift is in alpha: the API may change between releases. Status lists what is open.
AddTool(FindCompanies) turns a static method into a tool, and its XML docs become what
the model reads. Group your tools by feature, domain or permission level, as you like.Settings record of what you mark configurable. Store
it in your database, under the id type you already use.IChatClient serves a model, nothing uses reflection, and your app
publishes with Native AOT.dotnet add package Typeshift --prerelease
The types are in the Typeshift namespace, so add using Typeshift; to your files. Turn on XML docs and pick your id
type once, in a Directory.Build.props next to your solution:
<Project>
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<!-- Ids are Guids unless you name another type -->
<TypeshiftIdType>int</TypeshiftIdType>
</PropertyGroup>
</Project>
A tool is a static method. Its XML docs describe it to the model, and its settings stay out of the model's reach:
internal partial class CompanyToolset : Toolset
{
public CompanyToolset()
{
WithDisplayName("Companies");
AddTool(FindCompanies)
.WithSettings<FindCompaniesSettings>()
.Configurable(s => s.Limit);
}
/// <summary>
/// Finds the companies of a city.
/// </summary>
/// <param name="city">The city the companies are based in.</param>
public static IReadOnlyList<Company> FindCompanies(string city, FindCompaniesSettings settings) =>
[
.. Companies
.Where(company => company.City.Equals(city, StringComparison.OrdinalIgnoreCase))
.Where(company => company.Active || settings.IncludeInactive)
.Take(settings.Limit)
];
private static readonly Company[] Companies =
[
new("Danube Robotics", "Vienna", Active: true),
new("Prater Motion", "Vienna", Active: true),
new("Graz Gearworks", "Graz", Active: false)
];
}
internal sealed record Company(string Name, string City, bool Active);
snippet source | anchor
{
"name": "find_companies",
"description": "Finds the companies of a city.",
"parameters": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "The city the companies are based in." }
},
"required": ["city"]
}
}
The constructor picks the model, the tools and their settings. Configurable names what may change at runtime:
internal partial class ResearchAgent : Agent
{
public ResearchAgent()
{
WithDescription("Researches the companies of a city.");
WithChatOptions()
.WithModel("openai", "gpt-5-mini")
.WithTemperature(0.2f)
.Configurable(o => o.Instructions)
.Configurable(o => o.Temperature);
UseToolset<CompanyToolset>();
ConfigureFindCompanies()
.WithLimit(3);
}
protected override string GetInstructions() =>
"""
Find companies with the find_companies tool and list them.
""";
}
snippet source | anchor
AddTypeshift() registers every toolset and agent. You bring a chat client provider, which returns the IChatClient
of a provider's model, and a store for settings and conversations:
var services = new ServiceCollection();
services.AddTypeshift()
.WithStore<InMemoryTypeshiftStore>()
.WithChatClientProvider<KeyedChatClientProvider>();
services.AddKeyedSingleton<IChatClient>("openai", new CannedChatClient());
await using var provider = services.BuildServiceProvider(validateScopes: true);
snippet source | anchor
CannedChatClient is a fake that lets the samples run without a network. In your app, register a real client in its
place, such as OpenAI's.
internal sealed class KeyedChatClientProvider(IServiceProvider services) : IChatClientProvider
{
// One client per provider serves all its models: Typeshift puts the model into every request
public ValueTask<IChatClient> GetChatClientAsync(string provider, string model, CancellationToken ct) =>
ValueTask.FromResult(
services.GetKeyedService<IChatClient>(provider)
?? throw new InvalidOperationException($"No IChatClient is registered for the provider '{provider}'."));
}
snippet source | anchor
Store settings under an id you own, then run the agent:
await using var scope = provider.CreateAsyncScope();
var research = scope.ServiceProvider.GetRequiredService<AgentHost<ResearchAgent>>();
var settingsId = 42;
await research.ConfigureAsync(settingsId, settings => settings with
{
Temperature = 0.4f,
FindCompanies = settings.FindCompanies with { Limit = 2 }
});
snippet source | anchor
var answer = await research.RunAsync("Vienna", settingsId, conversationId: 1);
Console.WriteLine($"{research.Agent.Name}: {answer.Text}");
snippet source | anchor
The getting started guide explains every line, and lists what your app implements.
| Feature | What it gives you |
|---|---|
| Tool settings | Limits and timeouts the model cannot choose, set in code, per customer or per run |
| Composable agents | Agents built from toolsets, sub-agents and handoffs |
| One store | Settings and conversations in your database, behind four methods |
| Conversations | Resume after a crash, steer a run while it works, watch it live |
| Tool approval | Let your app approve a call before it runs |
| Plan and auto modes | Let the agent plan first, or run without interruptions |
| Middleware | Wrap every run or tool call |
| Context providers | Add memory, documents or other data to every run |
| MCP servers | Offer an MCP server's tools to an agent |
| Long-running agents | Compaction and crash recovery for hundreds of model calls |
A workspace toolset, a background sub-agent, plan mode and compaction, together:
internal partial class CodingAgent : Agent
{
public CodingAgent()
{
WithChatOptions()
.WithModel("anthropic", "claude-sonnet-5-5");
UseToolset<WorkspaceToolset>();
UseAgent<ExploreAgent>()
.InBackground();
WithRunOptions()
.WithMode(AgentMode.Plan)
.WithMaxToolIterations(500)
.WithMaxInputTokens(150_000)
.WithSummaryModel("claude-haiku-5-5")
.Configurable(r => r.Mode)
.Configurable(r => r.MaxToolIterations)
.Configurable(r => r.SummaryModel);
}
protected override string GetInstructions() =>
"""
Work in the customer's workspace. Read before you change anything, and run the tests after every change.
""";
}
snippet source | anchor
Contributions are welcome. The repository is set up for coding agents: AGENTS.md maps each task to its
playbook, and the contributing guide covers building, testing
and the docs. Code may be written with AI. Docs may be drafted with AI, but a person reviews and rewrites them, because
clear docs are what make Typeshift easy to pick up.
| Observability | OpenTelemetry spans and metrics for every run |
| Native AOT | Publish your app without reflection or trim warnings |