Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
e4d9401
Add Core metadata enrichment for MCP server support
carldebilly Mar 18, 2026
62d1d07
Add Repl.Mcp package with MCP server integration
carldebilly Mar 18, 2026
ac7a52f
Add MCP interaction channel and pipeline dispatch
carldebilly Mar 18, 2026
33440e6
Add MCP resources, prompts, and sample app
carldebilly Mar 18, 2026
b899674
Wire MCP progress notifications to WriteProgressAsync
carldebilly Mar 18, 2026
3ca16e7
Add elicitation, sampling, MCP logging, and list_changed support
carldebilly Mar 18, 2026
b4fae04
Fix CI pack failure and make NuGet publish resilient
carldebilly Mar 18, 2026
9e9a793
Add MCP server documentation and fix API naming
carldebilly Mar 18, 2026
fb9456b
Improve annotation docs and wire LongRunning to MCP tasks
carldebilly Mar 18, 2026
25a7383
Fix resources and prompts not registered with MCP server
carldebilly Mar 18, 2026
0a63bc1
Add MCP end-to-end integration tests and fix list_changed scope
carldebilly Mar 18, 2026
51add07
Fail NuGet publish step when all packages fail
carldebilly Mar 18, 2026
8c17217
Address PR review: fix bugs, exclusions, and Programmatic channel
carldebilly Mar 18, 2026
776487d
Add opt-in resource/prompt fallback to tools and AutoPromoteReadOnly
carldebilly Mar 18, 2026
d786f9b
Remove unused capturedPath variable and unused test binding
carldebilly Mar 18, 2026
04e8094
Fix AskConfirmation fail mode and remove dead answer: handling
carldebilly Mar 18, 2026
c2cbfdf
Fix thread safety and CI secret handling
carldebilly Mar 18, 2026
c3f1759
Fix Programmatic channel resolution and session isolation
carldebilly Mar 18, 2026
23b333a
Document output behavior and token cost in MCP mode
carldebilly Mar 18, 2026
84ef2fb
Fix Hidden/AutomationHidden leaking into resources and prompts,
carldebilly Mar 18, 2026
40012e3
Use slash separator for resource URIs regardless of ToolNamingSeparator
carldebilly Mar 18, 2026
3cf5bce
Fix optional segment tokens, collection schema, prompt collisions,
carldebilly Mar 18, 2026
4b21d1f
Remove numeric index parsing from choice resolution — match by name only
carldebilly Mar 18, 2026
c6b9379
Fix prompt parameter exposure, error surfacing, and context Details
carldebilly Mar 18, 2026
3c97c5b
Add end-to-end fallback tests via real McpServerHandler pipeline
carldebilly Mar 18, 2026
1cbd6a0
Clear stale routes on invalidation, surface resource errors, add IEnu…
carldebilly Mar 18, 2026
b455592
Align MCP interaction channel with console channel behavior
carldebilly Mar 18, 2026
ebe4cdc
Add injectable transport factory and interaction error tests
carldebilly Mar 18, 2026
e3053ec
Fix review findings: IsProgrammatic restore, DestructiveHint, confirm…
carldebilly Mar 18, 2026
a8fc449
Add debounce and concurrent session tests, use injectable TimeProvider
carldebilly Mar 18, 2026
296954d
Fix parameterized resources passing empty arguments dictionary
carldebilly Mar 19, 2026
6e374d7
Add configurable ResourceUriScheme for MCP resource URIs
carldebilly Mar 19, 2026
25fe7d7
Fix timer crash resilience, optimistic route rebuild, prompt server p…
carldebilly Mar 19, 2026
310e630
Make AskConfirmationAsync defaultValue nullable for consistent fallback
carldebilly Mar 19, 2026
624c77e
Add BuildMcpServerOptions for custom transport and HTTP integration
carldebilly Mar 19, 2026
f4db7ab
Document multi-session pattern for stdio-over-anything transports
carldebilly Mar 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,38 @@ jobs:
- name: Publish to NuGet
if: success()
shell: pwsh
env:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
run: |
dotnet nuget push packages/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
dotnet nuget push packages/*.snupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
$source = 'https://api.nuget.org/v3/index.json'
$apiKey = $env:NUGET_API_KEY
$failed = @()

foreach ($pkg in Get-ChildItem packages/*.nupkg) {
Write-Host "Pushing $($pkg.Name)..."
dotnet nuget push $pkg.FullName --api-key $apiKey --source $source --skip-duplicate 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Warning "Failed to push $($pkg.Name) (exit code $LASTEXITCODE) — continuing with remaining packages."
$failed += $pkg.Name
}
}

foreach ($pkg in Get-ChildItem packages/*.snupkg -ErrorAction SilentlyContinue) {
Write-Host "Pushing $($pkg.Name)..."
dotnet nuget push $pkg.FullName --api-key $apiKey --source $source --skip-duplicate 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Warning "Failed to push $($pkg.Name) (exit code $LASTEXITCODE) — continuing with remaining packages."
$failed += $pkg.Name
}
}

$nupkgCount = @(Get-ChildItem packages/*.nupkg).Count
$failedNupkgCount = @($failed | Where-Object { $_ -like '*.nupkg' }).Count
$succeeded = $nupkgCount - $failedNupkgCount
if ($failed.Count -gt 0) {
Write-Warning "The following packages failed to publish: $($failed -join ', ')"
Write-Warning "This may be expected for new packages awaiting NuGet validation."
}
if ($succeeded -eq 0 -and $nupkgCount -gt 0) {
throw "All $nupkgCount packages failed to publish. Check API key and NuGet source."
}
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/yllibed/repl)

**A .NET framework for building composable command surfaces.**
Define your commands once — run them as a CLI, explore them in an interactive REPL,
host them in session-based terminals, or drive them from automation and AI agents.
- Define your commands once — run them as a CLI, explore them in an interactive REPL,
- host them in session-based terminals, expose them as MCP servers for AI agents,
- or drive them from automation scripts.

> **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.

Expand All @@ -32,6 +33,7 @@ return app.Run(args);
- **POSIX-like semantics** — familiar flag syntax, `--` separator, predictable parsing
- **Hierarchical scopes** — stateful navigation with `..`, contexts, route constraints (`{id:int}`, `{when:date}`)
- **Multiple output formats** — `--json`, `--xml`, `--yaml`, `--markdown`, or `--human`
- **MCP server** — expose your commands as tools for AI agents with `app.UseMcpServer()` — zero boilerplate
- **AI/agent-friendly** — machine-readable contracts, deterministic outputs, pre-answered prompts (`--answer:*`)
- **Typed results** — `Ok`, `Error`, `NotFound`, `Cancelled` with payloads — not raw strings
- **Typed interactions** — prompts, progress, status, timeouts, cancellation
Expand Down Expand Up @@ -84,6 +86,18 @@ ACME
Globex
```

**MCP mode** (same command graph, exposed to AI agents):

```csharp
app.UseMcpServer(); // add one line
```

```json
{ "command": "myapp", "args": ["mcp", "serve"] }
```

One command graph. CLI, REPL, remote sessions, and AI agents — all from the same code.

## Packages

| Package | Description |
Expand All @@ -94,6 +108,7 @@ Globex
| [`Repl.Protocol`](https://www.nuget.org/packages/Repl.Protocol) | Machine-readable contracts (help, errors, tool schemas) |
| [`Repl.WebSocket`](https://www.nuget.org/packages/Repl.WebSocket) | Session hosting over WebSocket |
| [`Repl.Telnet`](https://www.nuget.org/packages/Repl.Telnet) | Telnet framing, negotiation, session adapters |
| [`Repl.Mcp`](https://www.nuget.org/packages/Repl.Mcp) | MCP server: expose commands as AI agent tools, resources, and prompts |
| [`Repl.Spectre`](https://www.nuget.org/packages/Repl.Spectre) | Spectre.Console integration: rich prompts, `IAnsiConsole`, table rendering |
| [`Repl.Testing`](https://www.nuget.org/packages/Repl.Testing) | In-memory multi-session test harness |

Expand All @@ -108,6 +123,7 @@ Progressive learning path — start with 01:
5. **[Hosting Remote](samples/05-hosting-remote/)** — WebSocket / Telnet session hosting
6. **[Testing](samples/06-testing/)** — multi-session typed assertions
7. **[Spectre](samples/07-spectre/)** — Spectre.Console renderables, visualizations, rich prompts
8. **[MCP Server](samples/08-mcp-server/)** — expose commands as MCP tools for AI agents

## Documentation

Expand All @@ -121,6 +137,7 @@ Progressive learning path — start with 01:
| Shell completion | [`docs/shell-completion.md`](docs/shell-completion.md) |
| Comparison & migration | [`docs/comparison.md`](docs/comparison.md) |
| Interaction channel | [`docs/interaction.md`](docs/interaction.md) |
| MCP server (AI agents) | [`docs/mcp-server.md`](docs/mcp-server.md) |
| Conditional module presence | [`docs/module-presence.md`](docs/module-presence.md) |
| Publishing & deployment | [`docs/publishing.md`](docs/publishing.md) |
| Interactive docs & AI Q\&A | [deepwiki.com/yllibed/repl](https://deepwiki.com/yllibed/repl) |
Expand Down
105 changes: 105 additions & 0 deletions docs/mcp-advanced.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# MCP Advanced: Custom Transports & HTTP Integration

This guide covers two advanced integration scenarios beyond the default stdio transport.

> **Prerequisite**: read [mcp-server.md](mcp-server.md) first for the basics of exposing a Repl app as an MCP server.

## Scenario A: Stdio-over-anything

The MCP protocol is JSON-RPC over stdin/stdout. The `TransportFactory` option lets you replace the physical transport while keeping the same protocol — useful for WebSocket bridges, named pipes, SSH tunnels, etc.

### How it works

`TransportFactory` receives the server name and an I/O context, and returns an `ITransport`. The MCP server uses this transport instead of `StdioServerTransport`.

```csharp
app.UseMcpServer(o =>
{
o.TransportFactory = (serverName, io) =>
{
// Bridge a WebSocket connection to MCP via streams.
var (inputStream, outputStream) = CreateWebSocketBridge();
return new StreamServerTransport(inputStream, outputStream, serverName);
};
});
```

The app still launches via `myapp mcp serve` — the framework handles the full MCP lifecycle (tool registration, routing invalidation, shutdown). This approach gives you **one session per process**.

### Multi-session (accept N connections)

For multiple concurrent sessions over a custom transport (e.g. a WebSocket listener accepting many clients), use `BuildMcpServerOptions` to build the options once, then create a server per connection:

```csharp
var mcpOptions = app.Core.BuildMcpServerOptions();

// For each incoming WebSocket connection:
async Task HandleConnectionAsync(Stream input, Stream output, CancellationToken ct)
{
var transport = new StreamServerTransport(input, output, "my-server");
var server = McpServer.Create(transport, mcpOptions);
await server.RunAsync(ct);
await server.DisposeAsync();
}
```

Each session is fully isolated — tool invocations run in separate `AsyncLocal` scopes with their own I/O streams, just like hosted sessions.

### When to use

- You have a non-stdio transport (WebSocket, named pipe, TCP) that carries the standard MCP JSON-RPC protocol
- Single-session: use `TransportFactory` via `mcp serve` (simplest)
- Multi-session: use `BuildMcpServerOptions` + one `McpServer.Create` per connection

## Scenario B: MCP-over-HTTP (Streamable HTTP)

The MCP spec defines a native HTTP transport: POST for client→server messages, GET/SSE for server→client streaming, with session management. This requires an HTTP host (typically ASP.NET Core) rather than a CLI command.

### How it works

`BuildMcpServerOptions()` constructs the full `McpServerOptions` (tools, resources, prompts, capabilities) from your Repl app's command graph — without starting a server. You pass these options to the MCP C# SDK's HTTP integration.

```csharp
var app = ReplApp.Create();
app.Map("greet {name}", (string name) => $"Hello, {name}!");
app.Map("status", () => "all systems go").ReadOnly();

// Build MCP options from the command graph.
var mcpOptions = app.Core.BuildMcpServerOptions(configure: o =>
{
o.ServerName = "MyApi";
o.ResourceUriScheme = "myapi";
});

// Use with McpServer.Create for a custom HTTP handler...
var server = McpServer.Create(httpTransport, mcpOptions);

// ...or pass the collections to ASP.NET Core's MapMcp.
```

### Multi-session

Each HTTP request creates an isolated MCP session. This uses the same mechanism as Repl's hosted sessions:

- `ReplSessionIO.SetSession()` creates an `AsyncLocal` scope per request
- Each session has its own output writer, input reader, and session ID
- Tool invocations are fully isolated — concurrent requests don't interfere

This is identical to how the framework handles concurrent tool calls in stdio mode (via `McpToolAdapter.ExecuteThroughPipelineAsync`).

### When to use

- You're building a web API that also exposes MCP endpoints
- You need multiple concurrent MCP sessions (agents connecting via HTTP)
- You want to integrate with the ASP.NET Core pipeline (auth, middleware, etc.)

## Configuration reference

| Option | Default | Description |
|--------|---------|-------------|
| `TransportFactory` | `null` (stdio) | Custom transport factory for Scenario A |
| `ResourceUriScheme` | `"repl"` | URI scheme for MCP resources (`{scheme}://path`) |
| `ServerName` | Assembly product name | Server name in MCP `initialize` response |
| `ServerVersion` | `"1.0.0"` | Server version in MCP `initialize` response |

See [mcp-server.md](mcp-server.md) for the full configuration reference.
Loading
Loading