Skip to content

Commit b4e09f8

Browse files
authored
Merge pull request #20 from yllibed/dev/cdb/mcp-apps
* Add MCP Apps support * Document MCP Apps patterns * Feature MCP Apps in documentation * Simplify MCP Apps resource mapping * Address MCP Apps review findings
2 parents 51e70e4 + 202385b commit b4e09f8

37 files changed

Lines changed: 1921 additions & 37 deletions

README.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
**A .NET framework for building composable command surfaces.**
1010

1111
- Define your commands once — run them as a CLI, explore them in an interactive REPL,
12-
- host them in session-based terminals, expose them as MCP servers for AI agents,
12+
- host them in session-based terminals, expose them as MCP servers and MCP Apps for AI agents,
1313
- or drive them from automation scripts.
1414

1515
> **New here?** The [DeepWiki](https://deepwiki.com/yllibed/repl) has full architecture docs, diagrams, and an AI assistant you can ask questions about the toolkit.
@@ -83,6 +83,14 @@ app.UseMcpServer(); // add one line
8383
{ "command": "myapp", "args": ["mcp", "serve"] }
8484
```
8585

86+
**MCP Apps** (same server, host-rendered UI for capable clients):
87+
88+
```csharp
89+
app.Map("contacts dashboard", (IContactStore contacts) => BuildHtml(contacts))
90+
.WithDescription("Open the contacts dashboard")
91+
.AsMcpAppResource();
92+
```
93+
8694
One command graph. CLI, REPL, remote sessions, and AI agents — all from the same code.
8795

8896
## What's included
@@ -93,7 +101,7 @@ One command graph. CLI, REPL, remote sessions, and AI agents — all from the sa
93101
| Interactive REPL — scopes, history, autocomplete | [![Repl.Defaults](https://img.shields.io/nuget/vpre/Repl.Defaults?logo=nuget&label=Repl.Defaults)](https://www.nuget.org/packages/Repl.Defaults) | <ul><li>[Interactive loop](docs/interactive-loop.md)</li><li>[Configuration](docs/configuration-reference.md)</li></ul> |
94102
| Parameters & options — typed binding, options groups, response files | [![Repl.Core](https://img.shields.io/nuget/vpre/Repl.Core?logo=nuget&label=Repl.Core)](https://www.nuget.org/packages/Repl.Core) | <ul><li>[Parameter system](docs/parameter-system.md)</li><li>[Route system](docs/route-system.md)</li></ul> |
95103
| Multiple output formats — JSON, XML, YAML, Markdown | [![Repl.Core](https://img.shields.io/nuget/vpre/Repl.Core?logo=nuget&label=Repl.Core)](https://www.nuget.org/packages/Repl.Core) | <ul><li>[Output system](docs/output-system.md)</li></ul> |
96-
| MCP server — expose commands as AI agent tools | [![Repl.Mcp](https://img.shields.io/nuget/vpre/Repl.Mcp?logo=nuget&label=Repl.Mcp)](https://www.nuget.org/packages/Repl.Mcp) | <ul><li>[MCP server](docs/mcp-server.md)</li><li>[MCP advanced](docs/mcp-advanced.md)</li></ul> |
104+
| MCP server + MCP Apps — expose commands as agent tools, resources, prompts, and UI | [![Repl.Mcp](https://img.shields.io/nuget/vpre/Repl.Mcp?logo=nuget&label=Repl.Mcp)](https://www.nuget.org/packages/Repl.Mcp) | <ul><li>[MCP server](docs/mcp-server.md)</li><li>[MCP advanced](docs/mcp-advanced.md)</li><li>[MCP sample](samples/08-mcp-server/)</li></ul> |
97105
| Typed results & interactions — prompts, progress, cancellation | [![Repl.Core](https://img.shields.io/nuget/vpre/Repl.Core?logo=nuget&label=Repl.Core)](https://www.nuget.org/packages/Repl.Core) | <ul><li>[Interaction channel](docs/interaction.md)</li></ul> |
98106
| Session hosting — WebSocket, Telnet, remote terminals | [![Repl.WebSocket](https://img.shields.io/nuget/vpre/Repl.WebSocket?logo=nuget&label=Repl.WebSocket)](https://www.nuget.org/packages/Repl.WebSocket) [![Repl.Telnet](https://img.shields.io/nuget/vpre/Repl.Telnet?logo=nuget&label=Repl.Telnet)](https://www.nuget.org/packages/Repl.Telnet) | <ul><li>[Runtime channels](docs/runtime-channels.md)</li><li>[Terminal metadata](docs/terminal-metadata.md)</li></ul> |
99107
| Shell completion — Bash, PowerShell, Zsh, Fish, Nushell | [![Repl.Core](https://img.shields.io/nuget/vpre/Repl.Core?logo=nuget&label=Repl.Core)](https://www.nuget.org/packages/Repl.Core) | <ul><li>[Shell completion](docs/shell-completion.md)</li></ul> |
@@ -115,7 +123,7 @@ Progressive learning path — each sample builds on the previous:
115123
5. **[Hosting Remote](samples/05-hosting-remote/)** — WebSocket / Telnet session hosting
116124
6. **[Testing](samples/06-testing/)** — multi-session typed assertions
117125
7. **[Spectre](samples/07-spectre/)** — Spectre.Console renderables, visualizations, rich prompts
118-
8. **[MCP Server](samples/08-mcp-server/)** — expose commands as MCP tools for AI agents
126+
8. **[MCP Server](samples/08-mcp-server/)** — expose commands as MCP tools, resources, prompts, and a minimal MCP Apps UI
119127

120128
## More documentation
121129

docs/architecture.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
- `Repl.Spectre`
2121
- Spectre.Console integration: `SpectreInteractionHandler` for rich prompts, `IAnsiConsole` DI injection, `"spectre"` output transformer for auto-rendered tables, `SpectreConsoleOptions` for capability configuration.
2222
- `Repl.Mcp`
23-
- MCP (Model Context Protocol) integration: `UseMcpServer()`, `BuildMcpServerOptions()`, tool/resource/prompt mapping, client roots, transport factory.
23+
- MCP (Model Context Protocol) integration: `UseMcpServer()`, `BuildMcpServerOptions()`, tool/resource/prompt mapping, MCP Apps UI resources, client roots, transport factory.
2424
- `Repl.Testing`
2525
- In-memory multi-session testing toolkit (`ReplTestHost`, `ReplSessionHandle`, typed execution results/events).
2626
- `Repl.Tests`
@@ -30,7 +30,7 @@
3030
- `Repl.ProtocolTests`
3131
- Contract tests for machine-readable help/error payloads.
3232
- `Repl.McpTests`
33-
- Tests for MCP server options, tool mapping, and transport integration.
33+
- Tests for MCP server options, tool/resource/prompt/app mapping, and transport integration.
3434
- `Repl.SpectreTests`
3535
- Tests for Spectre.Console integration.
3636
- `Repl.ShellCompletionTestHost`

docs/best-practices.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,16 @@ app.Map("clear", static async (IReplInteractionChannel ch, CancellationToken ct)
229229
.AutomationHidden(); // not exposed to agents
230230
```
231231

232+
For MCP Apps, mark the HTML-producing command as an app resource:
233+
234+
```csharp
235+
app.Map("contacts dashboard", static (IContactStore contacts) => BuildHtml(contacts))
236+
.WithDescription("Open the contacts dashboard")
237+
.AsMcpAppResource();
238+
```
239+
240+
This lets capable hosts render the UI while keeping raw HTML out of the model-facing transcript. The handler is still a normal Repl mapping, so it can use DI, cancellation tokens, and the usual command pipeline.
241+
232242
Declare answer slots for interactive prompts so agents and `--answer:` flags can provide values:
233243

234244
```csharp

docs/comparison.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ Repl Toolkit is a command-surface framework — not just a CLI parser. It builds
8888
| Structured help output | ❌ Text only | ❌ Text only | ✅ JSON / XML / YAML |
8989
| Documentation export |||`doc export` command |
9090
| Protocol passthrough (MCP, LSP...) |||`AsProtocolPassthrough()` |
91+
| MCP server tools/resources/prompts |||`Repl.Mcp` |
92+
| MCP Apps UI resources |||`AsMcpAppResource()` |
9193
| Shell completion | ⚠️ Tab completion API || ✅ Bash, PS, Zsh, Fish, Nu |
9294

9395
## When to Use What
@@ -113,7 +115,7 @@ Repl Toolkit is a command-surface framework — not just a CLI parser. It builds
113115
- Commands involve multi-step guided workflows (prompts, progress, confirmations)
114116
- Remote terminal hosting is planned (WebSocket, Telnet)
115117
- The command model must be testable in both one-shot and interactive contexts
116-
- AI/LLM agent readiness matters (structured help, protocol passthrough, pre-answered prompts)
118+
- AI/LLM agent readiness matters (structured help, MCP tools/resources/prompts, MCP Apps UI, protocol passthrough, pre-answered prompts)
117119

118120
## Migration from System.CommandLine
119121

docs/glossary.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ Static text in a route template matched exactly.
6666

6767
Model Context Protocol. Allows AI agents to discover and invoke commands.
6868

69+
### MCP App
70+
71+
MCP UI extension that lets a command open a `ui://` HTML resource. Repl maps this with `.AsMcpAppResource()` on the HTML-producing command and returns launcher text for normal tool calls.
72+
6973
### Middleware
7074

7175
Pipeline function registered via `app.Use()` that wraps handler execution.

docs/help-system.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ Each `ReplDocCommand` includes:
5656
This model powers:
5757

5858
- `--help` text rendering
59-
- MCP tool/resource/prompt schema generation
59+
- MCP tool/resource/prompt schema generation and MCP Apps metadata
6060
- Shell completion candidate generation
6161

6262
See also: [Commands](commands.md) | [MCP Server](mcp-server.md) | [Parameter System](parameter-system.md)

docs/mcp-advanced.md

Lines changed: 137 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
# MCP Advanced: Dynamic Tools, Roots, and Session-Aware Patterns
1+
# MCP Advanced: Dynamic Tools, Roots, MCP Apps, and Session-Aware Patterns
22

33
This guide covers advanced MCP usage patterns for Repl apps:
44

55
- Tool visibility that changes per session
66
- Native MCP client roots
77
- Soft roots for clients that don't support roots
88
- Compatibility shims for clients that don't refresh dynamic tool lists well
9+
- Advanced MCP Apps patterns
910

1011
> **Prerequisite**: read [mcp-server.md](mcp-server.md) first for the basic setup.
1112
>
@@ -23,6 +24,7 @@ Use the techniques in this page when:
2324
- The agent needs to know which directories it is allowed to work in
2425
- Your MCP client does not support native roots
2526
- Your MCP client does not seem to refresh its tool list after `list_changed`
27+
- Your MCP App should render HTML without exposing that HTML as the model-facing tool result
2628

2729
If your tool list is static, stay with the default setup from [mcp-server.md](mcp-server.md).
2830

@@ -248,8 +250,142 @@ Avoid it when:
248250
| Client supports tools but misses dynamic refreshes | Enable `DiscoverAndCallShim` |
249251
| Client has both issues | Use soft roots and, if needed, the dynamic tool shim |
250252

253+
## MCP Apps advanced patterns
254+
255+
For the basic MCP Apps setup, start with [mcp-server.md](mcp-server.md#mcp-apps). This section covers the patterns that matter once the UI is more than a trivial inline HTML card.
256+
257+
MCP Apps support is experimental in this version. Resource handlers should return generated HTML as `string`, `Task<string>`, or `ValueTask<string>`; the API is expected to become more flexible as host support and Repl's asset story evolve.
258+
259+
### One mapping, two MCP surfaces
260+
261+
`AsMcpAppResource()` keeps the Repl authoring model simple: one mapping produces both the launcher tool metadata and the UI resource.
262+
263+
```csharp
264+
app.Map("contacts dashboard", (IContactDb contacts) => BuildHtml(contacts))
265+
.WithDescription("Open the contacts dashboard")
266+
.AsMcpAppResource();
267+
```
268+
269+
The handler return value is used for `resources/read` and is returned as `text/html;profile=mcp-app`. When a client calls the MCP tool, Repl returns launcher text instead of raw HTML, using `WithMcpAppLauncherText(...)`, `WithDescription(...)`, or a generated fallback.
270+
271+
Use `WithMcpAppLauncherText(...)` when the description is not the text you want in the chat transcript:
272+
273+
```csharp
274+
app.Map("contacts dashboard", (IContactDb contacts) => BuildHtml(contacts))
275+
.WithDescription("Open the contacts dashboard")
276+
.AsMcpAppResource()
277+
.WithMcpAppLauncherText("Opening the contacts dashboard.");
278+
```
279+
280+
`WithMcpApp("ui://...")` remains available for advanced cases where a normal tool should point at a separately registered UI resource, but it is not the default pattern.
281+
282+
```csharp
283+
app.Map("status dashboard", (IStatusStore store) => store.GetSummary())
284+
.ReadOnly()
285+
.WithMcpApp("ui://status/dashboard");
286+
```
287+
288+
### Generated UI resource URIs
289+
290+
`AsMcpAppResource()` generates a `ui://` URI from the route path, matching how `AsResource()` generates `repl://` URIs:
291+
292+
```csharp
293+
app.Map("contact {id:int} panel", (int id, IContactDb contacts) => BuildHtml(contacts.Get(id)))
294+
.AsMcpAppResource();
295+
```
296+
297+
This produces a resource template like `ui://contact/{id}/panel`.
298+
299+
The generated URI uses the full route path, including contexts:
300+
301+
```csharp
302+
app.Context("viewer", viewer =>
303+
{
304+
viewer.Context("session {id:int}", session =>
305+
{
306+
session.Map("attach", (int id) => BuildHtml(id))
307+
.AsMcpAppResource();
308+
});
309+
});
310+
```
311+
312+
This produces `ui://viewer/session/{id}/attach`. MCP URI templates keep the variable name but not the Repl route constraint, so `{id:int}` becomes `{id}` in the URI and is validated when Repl dispatches the resource read through the normal command pipeline.
313+
314+
Pass an explicit URI only when you need a stable public URI that is decoupled from the route path:
315+
316+
```csharp
317+
app.Map("contacts dashboard", (IContactDb contacts) => BuildHtml(contacts))
318+
.AsMcpAppResource("ui://contacts/summary");
319+
```
320+
321+
### Display preferences
322+
323+
MCP Apps standard display modes are `inline`, `fullscreen`, and `pip`, but hosts decide what they support. Repl can express a preference:
324+
325+
```csharp
326+
app.Map("contacts dashboard", (IContactDb contacts) => BuildHtml(contacts))
327+
.AsMcpAppResource()
328+
.WithMcpAppDisplayMode(McpAppDisplayModes.Fullscreen);
329+
```
330+
331+
As of April 2026, VS Code renders MCP Apps inline only. Microsoft 365 Copilot declarative agents support fullscreen display requests for widgets. Other hosts vary; check [mcp-server.md](mcp-server.md#mcp-apps-host-compatibility) for the current compatibility notes.
332+
333+
If the HTML uses the MCP Apps JavaScript bridge, it should still ask the host what is available before requesting a different display mode:
334+
335+
```javascript
336+
const modes = app.getHostContext()?.availableDisplayModes ?? [];
337+
if (modes.includes("fullscreen")) {
338+
await app.requestDisplayMode({ mode: "fullscreen" });
339+
}
340+
```
341+
342+
For host-specific hints that are not yet modeled by Repl, use simple string metadata:
343+
344+
```csharp
345+
app.Map("contacts dashboard", (IContactDb contacts) => BuildHtml(contacts))
346+
.AsMcpAppResource()
347+
.WithMcpAppUiMetadata("presentation", "flyout");
348+
```
349+
350+
### HTML now, assets later
351+
352+
The v1 Repl API expects the UI resource handler to return generated HTML. This is enough for small cards, forms, and proof-of-concept dashboards.
353+
354+
For WebAssembly UIs such as Uno-Wasm, the likely shape is:
355+
356+
1. Map a `ui://` app resource that returns a small HTML shell.
357+
2. Serve published static assets such as `embedded.js`, `_framework/*`, `.wasm`, fonts, and images from an HTTP endpoint.
358+
3. Inject the HTTP base URL into the generated shell.
359+
4. Set CSP metadata for asset and fetch domains.
360+
361+
```csharp
362+
var assetBaseUri = new Uri("http://127.0.0.1:5123/");
363+
364+
app.Map("contacts dashboard", () => BuildUnoShellHtml(assetBaseUri))
365+
.AsMcpAppResource()
366+
.WithMcpAppCsp(new McpAppCsp
367+
{
368+
ResourceDomains = [assetBaseUri.ToString()],
369+
ConnectDomains = [assetBaseUri.ToString()],
370+
});
371+
```
372+
373+
Keep the shell and asset server host-aware: clients may preload or cache UI resources, and not every host supports every display mode or browser capability.
374+
251375
## Troubleshooting patterns
252376

377+
### My MCP App shows HTML text in the chat
378+
379+
Use `.AsMcpAppResource()` on the HTML-producing command instead of linking a normal tool to raw HTML manually. Repl will return launcher text for tool calls and reserve the HTML for `resources/read`.
380+
381+
Also restart or reload the MCP server in the client. Some hosts cache tool lists and will not pick up MCP Apps metadata changes until the server is refreshed.
382+
383+
### My MCP App does not open fullscreen
384+
385+
Check whether the host supports fullscreen. VS Code currently renders MCP Apps inline only, even when Repl sets `preferredDisplayMode: McpAppDisplayModes.Fullscreen`.
386+
387+
For hosts that support display mode changes, request fullscreen from inside the HTML app after checking host capabilities.
388+
253389
### The agent doesn't see tools that should appear later
254390
255391
Check:

0 commit comments

Comments
 (0)