SSE Transport Mode in grep-mcp: A Complete Guide to Starlette Integration
The SSE transport mode in grep-mcp enables HTTP-based Server-Sent Events communication, allowing the MCP server to stream real-time updates to clients via a Starlette ASGI application instead of using standard input/output.
The grep-mcp repository provides a Model Context Protocol (MCP) server that supports two transport modes: the default stdio mode and an HTTP-based SSE transport mode in grep-mcp. When running in SSE mode, the server leverages Starlette to handle persistent connections and unidirectional event streaming, making it accessible to web clients and browser-based applications.
Why Use SSE Transport in grep-mcp?
Server-Sent Events provide several advantages for MCP communication:
- Unidirectional push: The server pushes events (tool calls, responses, diagnostics) to the client without requiring polling.
- Web-friendly: Any modern browser or JavaScript runtime can consume the stream via the native
EventSourceAPI. - Scalable: Starlette runs on an ASGI server (e.g., uvicorn) and can handle many simultaneous connections efficiently.
Core Architecture and Integration Points
The SSE transport implementation spans several key components in src/grep_mcp/server.py and related files.
SseServerTransport Implementation
The SseServerTransport class from the MCP library provides the foundational read/write streams over an SSE connection. In src/grep_mcp/server.py at lines 61-66, the transport is instantiated with a base URL path:
from mcp.server.sse import SseServerTransport
# Initialize SSE transport with the messages endpoint path
sse = SseServerTransport("/messages/")
This configuration establishes where clients must POST messages back to the server.
Starlette Application Setup
The create_starlette_app function (lines 61-78 and 98-105 in src/grep_mcp/server.py) constructs the Starlette application and wires the SSE transport:
from starlette.applications import Starlette
from starlette.routing import Route, Mount
async def handle_sse(request):
async with sse.connect_sse(
request.scope,
request.receive,
request._send,
) as (read_stream, write_stream):
await mcp_server.run(
read_stream,
write_stream,
mcp_server.create_initialization_options(),
)
def create_starlette_app(mcp_server_instance):
routes = [
Route("/sse", endpoint=handle_sse),
Mount("/messages/", app=sse.handle_post_message),
]
return Starlette(routes=routes)
The handle_sse function manages the connection lifecycle, extracting the read and write streams from the SSE connection and passing them to the MCP server's run method.
CLI Entry Point and Transport Selection
The main function in src/grep_mcp/server.py (lines 19-30 and 32-44) handles transport selection:
import argparse
import uvicorn
def main():
parser = argparse.ArgumentParser(description="Grep MCP Server")
parser.add_argument(
"--transport",
choices=["stdio", "sse"],
default="stdio",
help="Transport mode (default: stdio)"
)
# ... host and port arguments ...
args = parser.parse_args()
if args.transport == "sse":
starlette_app = create_starlette_app(mcp._mcp_server)
uvicorn.run(
starlette_app,
host=args.host,
port=args.port,
log_level="info"
)
else:
# stdio mode implementation
mcp.run(transport="stdio")
When --transport sse is specified, the server creates the Starlette application and launches it with uvicorn on the configured host and port.
How the SSE Transport Works in grep-mcp
The message flow follows this sequence:
- Client Connection: The client opens an
EventSourceconnection toGET /sse. - Stream Establishment: The
handle_ssefunction callssse.connect_sse(), which returns a tuple of(read_stream, write_stream). - MCP Protocol Handshake: The
mcp_server.run()method initializes the MCP session using these streams. - Bidirectional Communication:
- Server → Client: Messages flow through the
write_streamand are pushed as SSE events. - Client → Server: The client POSTs JSON-RPC messages to
/messages/, handled bysse.handle_post_message.
- Server → Client: Messages flow through the
- Session Management: The connection remains open until the client disconnects or the server shuts down.
Client Integration Examples
Browser-based EventSource
Modern browsers can connect directly using the native EventSource API:
const source = new EventSource('http://localhost:8080/sse');
source.onmessage = (e) => {
const msg = JSON.parse(e.data);
console.log('MCP →', msg);
};
// To send a request back to the server:
fetch('http://localhost:8080/messages/', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
jsonrpc: '2.0',
method: 'grep_query',
params: {query: 'asyncio'}
}),
});
Python Async Client with httpx
For Python applications, httpx provides robust async SSE support:
import asyncio
import httpx
import json
async def main():
async with httpx.AsyncClient(timeout=None) as client:
# Open SSE stream
async with client.stream("GET", "http://localhost:8080/sse") as sse:
async for line in sse.aiter_lines():
if line.startswith("data:"):
data = json.loads(line[5:].strip())
print("← Server event:", data)
# Send a request back to the server
payload = {
"jsonrpc": "2.0",
"method": "grep_query",
"params": {"query": "asyncio"}
}
await client.post(
"http://localhost:8080/messages/",
json=payload,
headers={"Content-Type": "application/json"},
)
asyncio.run(main())
Direct Tool Invocation
For testing or internal use, you can bypass the transport layer entirely:
from grep_mcp.server import mcp
import asyncio
# Directly call the tool (bypasses transport)
result_json = asyncio.run(mcp.tools['grep_query'](
query="asyncio", language="python"
))
print(result_json)
Benefits and Limitations of SSE Transport
| Benefit | Description |
|---|---|
| Zero-polling | Server pushes results as soon as they are ready. |
| Web-native | Works in any browser without extra libraries. |
| Simple deployment | One ASGI process (uvicorn) serves both the SSE stream and POST endpoint. |
| Limitation | Description |
|---|---|
| Uni-directional | SSE only streams from server → client; client → server uses the POST /messages/ endpoint. |
| No binary | SSE payloads are text-based JSON; binary data must be Base64-encoded. |
| Connection limits | Long-standing HTTP connections can be limited by proxies/load-balancers. |
Key Files and Implementation Details
| File | Purpose |
|---|---|
src/grep_mcp/server.py |
Core server implementation – defines create_starlette_app, SSE handling, CLI main, and the grep_query tool. |
src/grep_mcp/__main__.py |
Module entry point (python -m grep_mcp). |
src/grep_mcp/__init__.py |
Exposes the FastMCP instance (mcp). |
These files together illustrate how Grep-MCP toggles between stdio and SSE transports and how the SSE transport is seamlessly wrapped inside a Starlette application.
Summary
- The SSE transport mode in grep-mcp enables HTTP-based communication using Server-Sent Events, providing an alternative to the default stdio transport.
- The implementation relies on
SseServerTransportfrom the MCP library, instantiated atsrc/grep_mcp/server.pylines 61-66 with the/messages/endpoint. - Starlette serves as the ASGI framework, with routes defined in
create_starlette_app(lines 61-78) handlingGET /ssefor connections andPOST /messages/for client requests. - The server runs via uvicorn when
--transport sseis specified in the CLI (lines 32-44), falling back to stdio mode by default.
Frequently Asked Questions
How do I start grep-mcp in SSE mode instead of stdio mode?
Run the server with the --transport sse flag. By default, it binds to 0.0.0.0:8080, but you can customize the host and port with --host and --port arguments. For example: python -m grep_mcp --transport sse --port 3000.
Why does the SSE transport use a separate POST endpoint for client messages?
Server-Sent Events are unidirectional by design, streaming only from server to client. To receive messages from the client, the MCP protocol requires a separate channel. The SseServerTransport creates a POST endpoint at /messages/ that handles incoming JSON-RPC requests from clients, while the /sse endpoint pushes responses and notifications via the SSE stream.
Can I use grep-mcp with browsers or only with Python clients?
You can use both. Modern browsers support the native EventSource API to consume the SSE stream at /sse, and JavaScript's fetch API can POST messages to /messages/. Python clients can use libraries like httpx or requests with SSE extensions to achieve the same result, making the server accessible to any HTTP-capable environment.
What happens if I don't specify the transport mode?
If you run python -m grep_mcp without the --transport argument, it defaults to stdio mode. In this mode, the server communicates over standard input and output using JSON-RPC messages, which is the traditional MCP transport suitable for local process communication with hosts like Claude Desktop or other MCP clients.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →