Skip to content

Commit 0047e61

Browse files
NinjaRocksclaude
andcommitted
Port 15 placeholder samples to Microsoft.Agents.AI 1.3 / ModelContextProtocol 1.2
Replaces every placeholder Program.cs with a working implementation against the stable 1.x API surface. The .book.txt files (preserving the original 0.3-preview snippets that print in the manuscript) remain alongside for traceability. Key API shifts applied across the ports: - ChatClientAgentOptions.{Instructions,Tools} now live under .ChatOptions (or pass instructions/tools to the string-arg ChatClientAgent ctor). - AgentThread -> AgentSession; AgentRunResponse -> AgentResponse; GetNewThread() -> CreateSessionAsync(); session serialization is agent.SerializeSessionAsync / DeserializeSessionAsync. - WorkflowBuilder takes the start executor in its ctor; Executor.From/FromAsync became FunctionExecutor<TIn,TOut>; SetOutputExecutor -> WithOutputFrom; the workflow runs via InProcessExecution.RunAsync(workflow, input). - AsBuilder().Use(...) middleware delegate now receives the inner AIAgent directly (no next-delegate); call innerAgent.RunAsync(...). - ModelContextProtocol.{Client,Server} packages were merged into ModelContextProtocol / .Core / .AspNetCore. McpClientTool extends AIFunction so .AsAIFunction() is gone; rename via .WithName(...). - Server bootstrap uses .WithHttpTransport() (streamable HTTP only); SSE was retired. Client side uses HttpClientTransport in place of StreamableHttpClientTransport / SseClientTransport. - A2A server hosting requires an IAgentHandler, registered via services.AddA2AAgent<THandler>(card) and exposed via app.MapA2A("/path") from A2A.AspNetCore. The 0.3-preview's app.MapA2A(agent, "/path") shorthand no longer exists. - McpClient.OnToolsListChanged(...) replaced by RegisterNotificationHandler (NotificationMethods.ToolListChangedNotification, ...) which returns an IAsyncDisposable. A2A.AspNetCore 1.0.0-preview2 added to Directory.Packages.props (preview-only, referenced via VersionOverride from samples/ch04-agent-framework/04.7-a2a-server). README.md "API-update-pending samples" section replaced with the 1.x API delta cheatsheet. All 37 sample projects build clean. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent b0c577f commit 0047e61

20 files changed

Lines changed: 861 additions & 89 deletions

File tree

Directory.Packages.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
<!-- Preview-only members of the family. -->
2828
<PackageVersion Include="Microsoft.Agents.AI.A2A" Version="1.3.0-preview.260423.1" />
2929
<PackageVersion Include="Microsoft.Agents.AI.Hosting" Version="1.3.0-preview.260423.1" />
30+
<PackageVersion Include="A2A.AspNetCore" Version="1.0.0-preview2" />
3031
</ItemGroup>
3132

3233
<ItemGroup Label="Model Context Protocol (1.x)">

README.md

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -97,35 +97,19 @@ These samples need **only** Ollama (no API keys, no cloud calls):
9797

9898
The rest assume an OpenAI / Anthropic / Azure OpenAI key.
9999

100-
## API-update-pending samples
101-
102-
Microsoft's `Microsoft.Agents.AI` and `ModelContextProtocol` packages went stable (1.x) with substantial API changes from the 0.3.x previews used while the manuscript was being written. To keep the build green, the following samples currently ship a one-line placeholder `Program.cs` and the original code is preserved alongside as `Program.cs.book.txt` (and any other `*.cs.book.txt` files in the same folder).
103-
104-
These need a manuscript-aligned API rewrite (target: `Microsoft.Agents.AI` 1.3 + `ModelContextProtocol` 1.2):
105-
106-
- `samples/ch04-agent-framework/04.2.1-hello-agent/`
107-
- `samples/ch04-agent-framework/04.2.4-anthropic-agents/`
108-
- `samples/ch04-agent-framework/04.3-persistent-session/`
109-
- `samples/ch04-agent-framework/04.4-tools-and-approval/`
110-
- `samples/ch04-agent-framework/04.5-agent-middleware/`
111-
- `samples/ch04-agent-framework/04.6.3-text-processing-walkthrough/`
112-
- `samples/ch04-agent-framework/04.6.7-content-workflow/`
113-
- `samples/ch04-agent-framework/04.7-a2a-server/`
114-
- `samples/ch04-agent-framework/04.8-agent-with-mcp/`
115-
- `samples/ch05-mcp/05.3.1-stdio-transport/`
116-
- `samples/ch05-mcp/05.3.2-sse-transport/`
117-
- `samples/ch05-mcp/05.3.3-streamable-http/`
118-
- `samples/ch05-mcp/05.4-mcp-client/`
119-
- `samples/ch05-mcp/05.6.3-mcp-agent-factory/`
120-
- `samples/ch05-mcp/cached-mcp-tool-provider/`
121-
122-
Notable shape changes to plan around when updating:
123-
124-
- `ChatClientAgentOptions` no longer has `Instructions` / `Tools` -- both move under `ChatOptions` / `ChatHistoryProvider`.
125-
- `AgentThread` -> `AgentSession`; `AgentRunResponse` -> `AgentResponse`; `AIAgent.GetNewThread()` is gone.
126-
- `WorkflowBuilder` requires a starting executor in its constructor; `Executor.From` / `Executor.FromAsync` were renamed.
127-
- `ModelContextProtocol.Client` and `ModelContextProtocol.Server` are no longer separate packages -- everything is in `ModelContextProtocol` / `ModelContextProtocol.Core`.
128-
- `WithHttpServerTransport(...)` / `HttpTransportType` were replaced by `WithHttpTransport(...)` (streamable HTTP only).
100+
## 1.x API surface (Agent Framework 1.3 / MCP 1.2)
101+
102+
All samples target `Microsoft.Agents.AI` 1.3 and `ModelContextProtocol` 1.2. The manuscript was originally drafted against the 0.3.x previews, so the printed code occasionally differs from what's in this repo. The original 0.3-preview snippets are preserved alongside the live code as `*.cs.book.txt` for traceability. The shape changes that recur most often:
103+
104+
- `ChatClientAgentOptions` no longer carries `Instructions` / `Tools` -- they move to `ChatClientAgentOptions.ChatOptions` (`Instructions`, `Tools`) or to the string-arg `ChatClientAgent` constructor.
105+
- `AgentThread` -> `AgentSession`; `AgentRunResponse` -> `AgentResponse`; `AIAgent.GetNewThread()` -> `AIAgent.CreateSessionAsync(...)`. Session serialization is `agent.SerializeSessionAsync(session)` / `agent.DeserializeSessionAsync(jsonElement)`.
106+
- `WorkflowBuilder()` parameterless ctor is gone -- the start executor is passed to the constructor (`new WorkflowBuilder(start)`). `Executor.From` / `FromAsync` lambdas became `new FunctionExecutor<TIn, TOut>(id, handler)`. `SetOutputExecutor` -> `WithOutputFrom(...)`. `workflow.RunAsync(...)` was replaced by `InProcessExecution.RunAsync(workflow, input)`.
107+
- The `AsBuilder().Use(...)` middleware delegate now receives `(messages, session, options, innerAgent, ct)` -- you call `innerAgent.RunAsync(...)` instead of a `next` delegate.
108+
- `ModelContextProtocol.Client` / `ModelContextProtocol.Server` are no longer separate packages -- everything is in `ModelContextProtocol` / `ModelContextProtocol.Core` / `ModelContextProtocol.AspNetCore`. `McpClientTool` now extends `AIFunction` directly (no `.AsAIFunction()` needed); rename via `tool.WithName(...)`.
109+
- Server bootstrap is `.AddMcpServer().WithHttpTransport().WithToolsFromAssembly()` then `app.MapMcp(...)`. `WithHttpServerTransport(...)` and `HttpTransportType.Sse` have been retired -- streamable HTTP is the only HTTP transport.
110+
- Client transports: `StreamableHttpClientTransport` -> `HttpClientTransport` (with `HttpClientTransportOptions`). `SseClientTransport` is gone.
111+
- A2A server hosting requires implementing `A2A.IAgentHandler`, registering it via `services.AddA2AAgent<THandler>(agentCard)`, then `app.MapA2A("/path")` from `A2A.AspNetCore`. The 0.3-preview's one-liner `app.MapA2A(agent, "/path")` no longer exists.
112+
- `McpClient.OnToolsListChanged(...)` was replaced by `client.RegisterNotificationHandler(NotificationMethods.ToolListChangedNotification, handler)`, which returns an `IAsyncDisposable`.
129113

130114
## Versioning and tags
131115

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,30 @@
1-
// API-update-pending: this sample is being updated for the Microsoft.Agents.AI 1.x
2-
// and ModelContextProtocol 1.x API surface. See README.md and the Program.cs.book.txt
3-
// (and any other *.cs.book.txt) files for the original code as written for the manuscript.
4-
Console.WriteLine("Sample placeholder. See README.md and Program.cs.book.txt for the original implementation.");
1+
using Microsoft.Agents.AI;
2+
using Microsoft.Extensions.AI;
3+
using OllamaSharp;
4+
5+
IChatClient chat = new OllamaApiClient(
6+
new Uri(Environment.GetEnvironmentVariable("OLLAMA_ENDPOINT") ?? "http://localhost:11434"),
7+
Environment.GetEnvironmentVariable("OLLAMA_MODEL") ?? "phi4-mini");
8+
9+
ChatClientAgent agent = new(
10+
chat,
11+
instructions: """
12+
You are Curio, a curious assistant that asks one short clarifying question
13+
before answering. Keep replies under three sentences.
14+
""",
15+
name: "Curio");
16+
17+
AgentSession session = await agent.CreateSessionAsync();
18+
19+
string[] turns =
20+
[
21+
"I'd like to plan a weekend hike.",
22+
"Two people, intermediate fitness, prefer woodland trails.",
23+
];
24+
25+
foreach (var turn in turns)
26+
{
27+
Console.WriteLine($"\n[User] {turn}");
28+
AgentResponse response = await agent.RunAsync(turn, session);
29+
Console.WriteLine($"[{agent.Name}] {response}");
30+
}
Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,35 @@
1-
// API-update-pending: this sample is being updated for the Microsoft.Agents.AI 1.x
2-
// and ModelContextProtocol 1.x API surface. See README.md and the Program.cs.book.txt
3-
// (and any other *.cs.book.txt) files for the original code as written for the manuscript.
4-
Console.WriteLine("Sample placeholder. See README.md and Program.cs.book.txt for the original implementation.");
1+
using Anthropic.SDK;
2+
using Microsoft.Agents.AI;
3+
using Microsoft.Extensions.AI;
4+
5+
var apiKey = Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY")
6+
?? throw new InvalidOperationException("Set ANTHROPIC_API_KEY.");
7+
8+
var modelId = Environment.GetEnvironmentVariable("ANTHROPIC_MODEL") ?? "claude-haiku-4-5-20251001";
9+
10+
IChatClient chat = new AnthropicClient(apiKey).Messages;
11+
12+
ChatClientAgent agent = new(chat, new ChatClientAgentOptions
13+
{
14+
Name = "Claude",
15+
ChatOptions = new ChatOptions
16+
{
17+
ModelId = modelId,
18+
Instructions = "You are a thoughtful tutor. Explain things clearly with one concrete example.",
19+
},
20+
});
21+
22+
AgentSession session = await agent.CreateSessionAsync();
23+
24+
string[] turns =
25+
[
26+
"Explain how garbage collection generations work in .NET.",
27+
"When does an object actually move from gen 0 to gen 1?",
28+
];
29+
30+
foreach (var turn in turns)
31+
{
32+
Console.WriteLine($"\n[User] {turn}");
33+
AgentResponse response = await agent.RunAsync(turn, session);
34+
Console.WriteLine($"[{agent.Name}] {response}");
35+
}
Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,47 @@
1-
// API-update-pending: this sample is being updated for the Microsoft.Agents.AI 1.x
2-
// and ModelContextProtocol 1.x API surface. See README.md and the Program.cs.book.txt
3-
// (and any other *.cs.book.txt) files for the original code as written for the manuscript.
4-
Console.WriteLine("Sample placeholder. See README.md and Program.cs.book.txt for the original implementation.");
1+
using System.Text.Json;
2+
using Microsoft.Agents.AI;
3+
using Microsoft.Extensions.AI;
4+
using OllamaSharp;
5+
6+
const string SessionStateFile = "session.json";
7+
8+
IChatClient chat = new OllamaApiClient(
9+
new Uri("http://localhost:11434"),
10+
Environment.GetEnvironmentVariable("OLLAMA_MODEL") ?? "phi4-mini");
11+
12+
ChatClientAgent agent = new(
13+
chat,
14+
instructions: "You are a personal notebook assistant. Remember anything the user tells you.",
15+
name: "Note");
16+
17+
AgentSession session = await LoadSessionAsync(agent) ?? await agent.CreateSessionAsync();
18+
19+
Console.WriteLine($"Session file: {Path.GetFullPath(SessionStateFile)}");
20+
Console.WriteLine("Type messages (blank to save and exit). Re-run to resume.");
21+
22+
while (true)
23+
{
24+
Console.Write("\n> ");
25+
var input = Console.ReadLine();
26+
if (string.IsNullOrWhiteSpace(input)) break;
27+
28+
AgentResponse response = await agent.RunAsync(input, session);
29+
Console.WriteLine($"[{agent.Name}] {response}");
30+
}
31+
32+
await SaveSessionAsync(agent, session);
33+
Console.WriteLine($"Saved {SessionStateFile}.");
34+
35+
static async Task<AgentSession?> LoadSessionAsync(AIAgent agent)
36+
{
37+
if (!File.Exists(SessionStateFile)) return null;
38+
using var stream = File.OpenRead(SessionStateFile);
39+
var element = await JsonSerializer.DeserializeAsync<JsonElement>(stream);
40+
return await agent.DeserializeSessionAsync(element);
41+
}
42+
43+
static async Task SaveSessionAsync(AIAgent agent, AgentSession session)
44+
{
45+
JsonElement element = await agent.SerializeSessionAsync(session);
46+
await File.WriteAllTextAsync(SessionStateFile, element.GetRawText());
47+
}
Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,47 @@
1-
// API-update-pending: this sample is being updated for the Microsoft.Agents.AI 1.x
2-
// and ModelContextProtocol 1.x API surface. See README.md and the Program.cs.book.txt
3-
// (and any other *.cs.book.txt) files for the original code as written for the manuscript.
4-
Console.WriteLine("Sample placeholder. See README.md and Program.cs.book.txt for the original implementation.");
1+
using System.ComponentModel;
2+
using Microsoft.Agents.AI;
3+
using Microsoft.Extensions.AI;
4+
using OpenAI;
5+
6+
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY")
7+
?? throw new InvalidOperationException("Set OPENAI_API_KEY (function calling needs a tool-capable provider).");
8+
9+
IChatClient chat = new OpenAIClient(apiKey).GetChatClient("gpt-4o-mini").AsIChatClient();
10+
11+
var tools = new List<AITool>
12+
{
13+
AIFunctionFactory.Create(GetCustomer),
14+
AIFunctionFactory.Create(DeleteCustomerProtected),
15+
};
16+
17+
ChatClientAgent agent = new(chat, new ChatClientAgentOptions
18+
{
19+
Name = "AdminBot",
20+
ChatOptions = new ChatOptions
21+
{
22+
Instructions = "You manage customers. Use the tools provided.",
23+
Tools = tools,
24+
},
25+
});
26+
27+
AgentSession session = await agent.CreateSessionAsync();
28+
AgentResponse response = await agent.RunAsync("Look up customer 42, then delete them.", session);
29+
30+
Console.WriteLine($"\n[{agent.Name}] {response}");
31+
32+
33+
[Description("Look up a customer by ID. Safe -- read only.")]
34+
static string GetCustomer([Description("Customer ID")] int id)
35+
=> $"Customer {id}: Anna Lee, premium tier, joined 2024-02-09.";
36+
37+
[Description("Delete a customer permanently. PROTECTED -- requires explicit human approval before execution.")]
38+
[RequiresApproval]
39+
static string DeleteCustomerProtected([Description("Customer ID")] int id)
40+
=> $"Customer {id} permanently deleted.";
41+
42+
43+
// Marker attribute. A real production app would have a function-invocation
44+
// middleware that checks for this and emits FunctionApprovalRequest events
45+
// before invoking the underlying delegate.
46+
[AttributeUsage(AttributeTargets.Method)]
47+
internal sealed class RequiresApprovalAttribute : Attribute { }
Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,39 @@
1-
// API-update-pending: this sample is being updated for the Microsoft.Agents.AI 1.x
2-
// and ModelContextProtocol 1.x API surface. See README.md and the Program.cs.book.txt
3-
// (and any other *.cs.book.txt) files for the original code as written for the manuscript.
4-
Console.WriteLine("Sample placeholder. See README.md and Program.cs.book.txt for the original implementation.");
1+
using System.Diagnostics;
2+
using Microsoft.Agents.AI;
3+
using Microsoft.Extensions.AI;
4+
using OllamaSharp;
5+
6+
IChatClient chat = new OllamaApiClient(
7+
new Uri("http://localhost:11434"),
8+
Environment.GetEnvironmentVariable("OLLAMA_MODEL") ?? "phi4-mini");
9+
10+
ChatClientAgent inner = new(
11+
chat,
12+
instructions: "Reply with a single concise sentence.",
13+
name: "Helper");
14+
15+
AIAgent agent = inner
16+
.AsBuilder()
17+
.Use(LoggingRunMiddleware, runStreamingFunc: null)
18+
.Build();
19+
20+
AgentSession session = await inner.CreateSessionAsync();
21+
AgentResponse response = await agent.RunAsync("Give me a one-sentence travel tip for visiting Iceland in winter.", session);
22+
Console.WriteLine($"\n[{agent.Name}] {response}");
23+
24+
25+
static async Task<AgentResponse> LoggingRunMiddleware(
26+
IEnumerable<ChatMessage> messages,
27+
AgentSession? session,
28+
AgentRunOptions? options,
29+
AIAgent next,
30+
CancellationToken ct)
31+
{
32+
var sw = Stopwatch.StartNew();
33+
var lastUserMessage = messages.LastOrDefault();
34+
Console.WriteLine($" [run] start: \"{lastUserMessage?.Text}\"");
35+
AgentResponse response = await next.RunAsync(messages, session, options, ct);
36+
sw.Stop();
37+
Console.WriteLine($" [run] done in {sw.ElapsedMilliseconds} ms: {response.Messages.Count} message(s)");
38+
return response;
39+
}
Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,40 @@
1-
// API-update-pending: this sample is being updated for the Microsoft.Agents.AI 1.x
2-
// and ModelContextProtocol 1.x API surface. See README.md and the Program.cs.book.txt
3-
// (and any other *.cs.book.txt) files for the original code as written for the manuscript.
4-
Console.WriteLine("Sample placeholder. See README.md and Program.cs.book.txt for the original implementation.");
1+
using Microsoft.Agents.AI.Workflows;
2+
3+
var upper = new FunctionExecutor<string, string>(
4+
"uppercase",
5+
(s, ctx, ct) => ValueTask.FromResult(s.ToUpperInvariant()));
6+
7+
var reverse = new FunctionExecutor<string, string>(
8+
"reverse",
9+
(s, ctx, ct) => ValueTask.FromResult(new string(s.Reverse().ToArray())));
10+
11+
Workflow workflow = new WorkflowBuilder(upper)
12+
.AddEdge(upper, reverse)
13+
.WithOutputFrom(reverse)
14+
.Build();
15+
16+
await using var run = await InProcessExecution.RunAsync(workflow, "Hello, agent!");
17+
foreach (var evt in run.NewEvents)
18+
{
19+
if (evt is WorkflowOutputEvent output)
20+
{
21+
Console.WriteLine($"Result: {output.Data}");
22+
}
23+
}
24+
25+
Console.WriteLine();
26+
Console.WriteLine("Streaming events:");
27+
28+
await using var stream = await InProcessExecution.RunStreamingAsync(workflow, "Streaming demo");
29+
await foreach (WorkflowEvent evt in stream.WatchStreamAsync())
30+
{
31+
switch (evt)
32+
{
33+
case WorkflowOutputEvent output:
34+
Console.WriteLine($" output: {output.Data}");
35+
break;
36+
case ExecutorCompletedEvent completed:
37+
Console.WriteLine($" executor done: {completed.ExecutorId}");
38+
break;
39+
}
40+
}

0 commit comments

Comments
 (0)