diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index 8a9783c..eb0bfc6 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -14,7 +14,8 @@
-
+
+
diff --git a/src/Repl.Mcp.AspNetCore/CompositeServiceProvider.cs b/src/Repl.Mcp.AspNetCore/CompositeServiceProvider.cs
new file mode 100644
index 0000000..9ec3f90
--- /dev/null
+++ b/src/Repl.Mcp.AspNetCore/CompositeServiceProvider.cs
@@ -0,0 +1,9 @@
+namespace Repl.Mcp.AspNetCore;
+
+internal sealed class CompositeServiceProvider(
+ IServiceProvider primary,
+ IServiceProvider fallback) : IServiceProvider
+{
+ public object? GetService(Type serviceType) =>
+ primary.GetService(serviceType) ?? fallback.GetService(serviceType);
+}
diff --git a/src/Repl.Mcp.AspNetCore/McpHttpBinding.cs b/src/Repl.Mcp.AspNetCore/McpHttpBinding.cs
new file mode 100644
index 0000000..47aa53f
--- /dev/null
+++ b/src/Repl.Mcp.AspNetCore/McpHttpBinding.cs
@@ -0,0 +1,9 @@
+namespace Repl.Mcp.AspNetCore;
+
+internal sealed record McpHttpBinding(
+ string Host,
+ int Port,
+ string Path,
+ string ListenUrl,
+ string EndpointUrl,
+ bool AllowsRemote);
diff --git a/src/Repl.Mcp.AspNetCore/McpHttpBindingFactory.cs b/src/Repl.Mcp.AspNetCore/McpHttpBindingFactory.cs
new file mode 100644
index 0000000..5bec0f8
--- /dev/null
+++ b/src/Repl.Mcp.AspNetCore/McpHttpBindingFactory.cs
@@ -0,0 +1,89 @@
+using System.Globalization;
+using System.Net;
+using System.Net.Sockets;
+
+namespace Repl.Mcp.AspNetCore;
+
+internal static class McpHttpBindingFactory
+{
+ public static McpHttpBinding Create(
+ string? host,
+ int port,
+ string? path,
+ bool allowRemote)
+ {
+ if (port is < 1 or > 65535)
+ {
+ throw new InvalidOperationException("MCP HTTP port must be between 1 and 65535.");
+ }
+
+ var effectiveHost = string.IsNullOrWhiteSpace(host)
+ ? ReplMcpHttpServerOptions.DefaultHost
+ : host.Trim();
+
+ var isLoopback = IsLoopbackHost(effectiveHost);
+ if (!allowRemote && !isLoopback)
+ {
+ throw new InvalidOperationException(
+ "Remote MCP HTTP bindings require --allow-remote. Use 127.0.0.1 or localhost for local-only serving.");
+ }
+
+ var normalizedPath = NormalizePath(path);
+ var urlHost = FormatUrlHost(effectiveHost);
+ var listenUrl = string.Concat("http://", urlHost, ":", port.ToString(CultureInfo.InvariantCulture));
+ var endpointUrl = string.Concat(listenUrl, normalizedPath);
+
+ return new McpHttpBinding(
+ effectiveHost,
+ port,
+ normalizedPath,
+ listenUrl,
+ endpointUrl,
+ !isLoopback);
+ }
+
+ private static string NormalizePath(string? path)
+ {
+ if (string.IsNullOrWhiteSpace(path))
+ {
+ return ReplMcpHttpServerOptions.DefaultPath;
+ }
+
+ var normalized = path.Trim();
+ if (normalized[0] != '/')
+ {
+ normalized = "/" + normalized;
+ }
+
+ return normalized;
+ }
+
+ private static bool IsLoopbackHost(string host)
+ {
+ var normalized = TrimIpv6Brackets(host);
+ if (string.Equals(normalized, "localhost", StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+
+ return IPAddress.TryParse(normalized, out var address)
+ && IPAddress.IsLoopback(address);
+ }
+
+ private static string FormatUrlHost(string host)
+ {
+ var normalized = TrimIpv6Brackets(host);
+ if (IPAddress.TryParse(normalized, out var address)
+ && address.AddressFamily == AddressFamily.InterNetworkV6)
+ {
+ return $"[{normalized}]";
+ }
+
+ return normalized;
+ }
+
+ private static string TrimIpv6Brackets(string host) =>
+ host.Length > 1 && host[0] == '[' && host[^1] == ']'
+ ? host[1..^1]
+ : host;
+}
diff --git a/src/Repl.Mcp.AspNetCore/McpHttpModule.cs b/src/Repl.Mcp.AspNetCore/McpHttpModule.cs
new file mode 100644
index 0000000..6f76ff5
--- /dev/null
+++ b/src/Repl.Mcp.AspNetCore/McpHttpModule.cs
@@ -0,0 +1,122 @@
+using Repl.Mcp;
+
+namespace Repl.Mcp.AspNetCore;
+
+internal sealed class McpHttpModule : IReplModule
+{
+ private readonly ReplApp _app;
+ private readonly ReplMcpHttpServerOptions _options;
+ private readonly ReplMcpServerOptions _mcpOptions;
+
+ public McpHttpModule(ReplApp app, ReplMcpHttpServerOptions options)
+ {
+ _app = app;
+ _options = options;
+ _mcpOptions = new ReplMcpServerOptions();
+ options.Http.ConfigureServer?.Invoke(_mcpOptions);
+ }
+
+ public void Map(IReplMap map)
+ {
+ map.Context(_mcpOptions.ContextName, mcp =>
+ {
+ mcp.Map("httpserve",
+ HandleHttpServeAsync)
+ .WithDescription("Start MCP Streamable HTTP server for agent integration.")
+ .WithAlias("http", "http-serve")
+ .Hidden();
+ })
+ .Hidden();
+ }
+
+ private async Task