Skip to content

Commit 04853b5

Browse files
v0.4.0: Streamable HTTP transport — single POST /message endpoint, no SSE
- New streamable_http.py module: simpler HTTP transport without SSE - New CLI commands: serve-http-streamable / demo-http-streamable (port 8001) - New library API: run_http_streamable() - Batch message support (JSON-RPC arrays) - 13 new tests; 34 total, all passing
1 parent e5a0536 commit 04853b5

6 files changed

Lines changed: 646 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,24 @@
22

33
All notable changes to click-to-mcp will be documented in this file.
44

5+
## [0.4.0] — 2026-05-15
6+
7+
### Added
8+
9+
- **Streamable HTTP transport** — New transport mode using a single POST `/message` endpoint. No SSE (Server-Sent Events) required, compatible with more MCP clients.
10+
- `click-to-mcp serve-http-streamable <name>` — Serve any discovered CLI
11+
- `click-to-mcp demo-http-streamable` — Run the built-in demo
12+
- `run_http_streamable()` — Library API (from `click_to_mcp` import)
13+
- `serve_http_streamable()` — Low-level API (from `click_to_mcp.streamable_http`)
14+
- Default port 8001 (different from HTTP+SSE's 8000)
15+
- **Batch message support** — Streamable HTTP transport accepts both single JSON-RPC messages and arrays (batch).
16+
- 13 new tests covering Streamable HTTP transport (health, initialize, tools/list, tools/call, notifications, batch, edge cases)
17+
18+
### Changed
19+
20+
- Version bumped to 0.4.0
21+
- `initialize` response now includes `"streamableHttp": {}` capability
22+
523
## [0.2.1] — 2026-05-15
624

725
### Added

click_to_mcp/__init__.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
from .server import serve_stdio
2323
from .discover import scan_entry_points, load_cli, find_our_clis, DiscoveredCLI
2424

25-
__version__ = "0.3.0"
25+
__version__ = "0.4.0"
2626

2727

2828
def run(app: Any, prefix: str = "", name: str = "") -> None:
@@ -95,7 +95,48 @@ def run_http(app: Any, prefix: str = "", name: str = "",
9595
)
9696

9797

98+
def run_http_streamable(app: Any, prefix: str = "", name: str = "",
99+
host: str = "127.0.0.1", port: int = 8001) -> None:
100+
"""High-level entry point: serve a Click/typer app as an MCP Streamable HTTP server.
101+
102+
Uses a single POST /message endpoint — no SSE required. Simpler than the
103+
HTTP+SSE transport, compatible with more MCP clients.
104+
105+
Requires optional dependencies: pip install 'click-to-mcp[http]'
106+
107+
Args:
108+
app: A click.Group or typer.Typer instance.
109+
prefix: Optional tool name prefix.
110+
name: Optional server name (defaults to app name or 'cli').
111+
host: Host to bind (default: 127.0.0.1).
112+
port: Port to bind (default: 8001).
113+
"""
114+
from .streamable_http import serve_http_streamable
115+
116+
if not name:
117+
name = getattr(app, "name", None) or "cli"
118+
desc = ""
119+
if hasattr(app, "info"):
120+
info_desc = getattr(app.info, "help", None)
121+
if info_desc and "DefaultPlaceholder" not in str(type(info_desc)):
122+
desc = str(info_desc)
123+
if not desc:
124+
desc = getattr(app, "help", None) or ""
125+
if not isinstance(desc, str):
126+
desc = str(desc) if desc else ""
127+
128+
serve_http_streamable(
129+
app,
130+
name=name,
131+
description=desc,
132+
prefix=prefix,
133+
host=host,
134+
port=port,
135+
)
136+
137+
98138
__all__ = [
99139
"cli_to_mcp_tools", "CliToolDef", "serve_stdio", "run", "run_http",
140+
"run_http_streamable",
100141
"scan_entry_points", "load_cli", "find_our_clis", "DiscoveredCLI",
101142
]

click_to_mcp/cli.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,69 @@ def demo_http(host: str, port: int):
160160
_serve_http(demo_cli, name="click-to-mcp-demo", host=host, port=port)
161161

162162

163+
@cli.command("serve-http-streamable")
164+
@click.argument("name", required=False, default=None)
165+
@click.option("--host", default="127.0.0.1", help="Host to bind (default: 127.0.0.1)")
166+
@click.option("--port", default=8001, type=int, help="Port to bind (default: 8001)")
167+
@click.option("--all", "serve_all", is_flag=True, help="Serve all discoverable CLIs")
168+
def serve_http_streamable(name: Optional[str], host: str, port: int, serve_all: bool):
169+
"""Serve a CLI as an MCP server over Streamable HTTP (no SSE).
170+
171+
Uses a single POST /message endpoint — simpler than HTTP+SSE, compatible
172+
with more MCP clients. Default port is 8001 (different from serve-http).
173+
Requires: pip install 'click-to-mcp[http]'
174+
"""
175+
if not name and not serve_all:
176+
click.echo("Error: Specify a CLI name or --all", err=True)
177+
sys.exit(1)
178+
179+
try:
180+
from .streamable_http import serve_http_streamable as _serve
181+
except ImportError as e:
182+
click.echo(f"Error: {e}", err=True)
183+
sys.exit(1)
184+
185+
if serve_all:
186+
clis = scan_entry_points()
187+
if not clis:
188+
click.echo("No CLIs found to serve.", err=True)
189+
sys.exit(1)
190+
click.echo(f"Multiple CLIs found. Serving the first one: {clis[0].name}", err=True)
191+
target_cli = load_cli(clis[0].name)
192+
if target_cli is None:
193+
click.echo(f"Error: Could not load CLI '{clis[0].name}'", err=True)
194+
sys.exit(1)
195+
_serve(target_cli, name=clis[0].name, host=host, port=port)
196+
return
197+
198+
target_cli = load_cli(name)
199+
if target_cli is None:
200+
click.echo(f"Error: CLI '{name}' not found.", err=True)
201+
click.echo("Run 'click-to-mcp discover' to see available CLIs.", err=True)
202+
sys.exit(1)
203+
204+
_serve(target_cli, name=name, host=host, port=port)
205+
206+
207+
@cli.command("demo-http-streamable")
208+
@click.option("--host", default="127.0.0.1", help="Host to bind (default: 127.0.0.1)")
209+
@click.option("--port", default=8001, type=int, help="Port to bind (default: 8001)")
210+
def demo_http_streamable(host: str, port: int):
211+
"""Run the built-in demo CLI as a Streamable HTTP MCP server (no SSE).
212+
213+
Requires: pip install 'click-to-mcp[http]'
214+
"""
215+
try:
216+
from .streamable_http import serve_http_streamable as _serve
217+
except ImportError as e:
218+
click.echo(f"Error: {e}", err=True)
219+
sys.exit(1)
220+
221+
from .demo import cli as demo_cli
222+
223+
_serve(demo_cli, name="click-to-mcp-demo", host=host, port=port)
224+
225+
163226
def main():
164227
"""Entry point for the click-to-mcp CLI."""
165228
cli()

0 commit comments

Comments
 (0)