Lesson 27 of 30, Module 4: Ship It. It publishes to Substack, runs on the web, and anyone can install it.
What you can do after this lesson
The transport is only the carrier. The tools, the questions and the guard live in your server, so the same server can sit behind a local pipe or a web address by changing one call, and nothing it does changes.
The problem we inherited
The draft is finished in Drive and still goes nowhere. You copy it into Substack by hand, which is exactly where you came in.
Code in this lesson
Every block the lesson shows on screen, in the order it appears.
if __name__ == "__main__":
if os.environ.get("SCRIBE_TRANSPORT") == "http":
mcp.run(transport="streamable-http", host="0.0.0.0",
port=int(os.environ.get("PORT", "8000")))
else:
mcp.run()
from starlette.responses import PlainTextResponse
@mcp.custom_route("/", methods=["GET"])
def home(request) -> PlainTextResponse:
"""What a browser sees. Claude talks to /mcp, not here."""
return PlainTextResponse("Scribe is up. Claude talks to /mcp")
export SCRIBE_TRANSPORT=http
export PORT=8765
python3 scribe_server.py
Downloads
- Module 4 checkpoint (scribe-checkpoint-m4.zip): working code as the module leaves it, so a broken session costs you nothing
- The course GitHub repo: every checkpoint, the README and the full lesson list
Lesson transcript
Scribe gets a web address
Scribe has lived inside my laptop since lesson 12. Today it gets a web address, and the change is 1 call.
Hello, and welcome to lesson 27 of the MCP Masterclass. Last lesson a draft went all the way from Drive to the Publish button. Today we look at the pipe you have used since lesson 6, and at the thing that replaces it. And we start with our map, as always.
14 lit boxes. Look at the 2 pills on the left of your server. The top one is the pipe, and it has been lit since lesson 1. The bottom one is the web address, and it is dark. By the end of this lesson it lights up.
What the pipe really is
Well, let us start with what the pipe is, because you have never had to think about it.
Open your settings file from lesson 12. The scribe entry has a command in it. Your Python, and your server file.
That is the pipe. When Claude Desktop starts, it runs that command itself. Your server becomes a small program that Claude Desktop owns, and the 2 of them talk through the program's own in and out. The proper name is standard I-O, and you will see it written as s t d i o.
It is simple, it is fast, and it needs nothing installed. That is why every lesson so far used it.
But it has 1 rule. The app and the server have to be on the same machine. Claude Desktop cannot run a command on a computer it is not sitting on.
So the moment you want Scribe up while your laptop is shut, or you want a friend to use it, or you want Claude on your phone to reach it, the pipe cannot do it.
The web address, and the name SSE
That is the second pill. The web address.
Your server runs somewhere, and it listens at an address. Any app, anywhere, sends it the same messages it was sending through the pipe, over the web instead. Same tools, same questions, same answers. Only the carrier changed.
The proper name is Streamable HTTP. You will also see a third name online, S S E. That was the old web transport, and the current protocol spec calls it deprecated. I do not teach it, and you should not build on it.
The 1 call that changes
Now the change. Here are the last lines of scribe_server.py.
if __name__ == "__main__":
if os.environ.get("SCRIBE_TRANSPORT") == "http":
mcp.run(transport="streamable-http", host="0.0.0.0",
port=int(os.environ.get("PORT", "8000")))
else:
mcp.run()
In the recap of module 3 I said 1 line. It is 1 call. MCP dot run, the call you have had since lesson 12, with 3 things added. The transport, a host, and a port. At the width we keep on screen that takes 2 lines, so I am saying call, not line, from now on.
And I kept the old call underneath it, behind 1 setting. If scribe transport is set to h t t p, the server listens at an address. If it is not set, the pipe, exactly as before. One file, both ways, so nothing about your Claude Desktop setup breaks today.
The port comes from a setting called port, with eight thousand as the default, because a host tells you which port to use, and you will see that next lesson.
Why the host is four zeros
The host is the part I want to explain, because I hit this one.
Zero dot zero dot zero dot zero means listen on every address this machine has. The default is not that. The default is the address only your own machine can reach, and when the server listens there, it also switches on a safety check. It refuses any request that was not addressed to localhost.
That check has a name, DNS rebinding protection, and it is a good thing on a laptop. But on a host, every request arrives addressed to the host's own name. So it would be refused, with a 403, and you would be staring at a server that runs fine and answers nobody. Four zeros, and the check is off, because a host that serves the public needs no such check.
What a browser sees
One more thing, and it is small. What does a browser see?
I opened the m c p address in Chrome and got an error about a missing session, and the root of the address gave me not found. That is correct, because a browser does not speak MCP. But a page that says error is a bad first look at your own server. So 1 more route.
from starlette.responses import PlainTextResponse
@mcp.custom_route("/", methods=["GET"])
def home(request) -> PlainTextResponse:
"""What a browser sees. Claude talks to /mcp, not here."""
return PlainTextResponse("Scribe is up. Claude talks to /mcp")
A custom route is nothing but a plain web page your server also answers. This one answers the root of the address with 1 sentence. Scribe is up. Claude talks to slash m c p. And Plain Text Response is from a package called starlette, which MCP installed for you. Nothing new to install, here or for the web address itself.
Running it on the laptop
So let us run it, on the laptop, before it leaves.
export SCRIBE_TRANSPORT=http
export PORT=8765
python3 scribe_server.py
On screen: the terminal. "StreamableHTTP session manager started", "Application startup complete.", "Uvicorn running on http://0.0.0.0:8765".
Look at the last line. Running on, and an address. Your server is not waiting for Claude Desktop to start it any more. It is up, on its own, and it is listening.
Open a browser at that address.
On screen: Chrome at http://127.0.0.1:8765/ showing "Scribe is up. Claude talks to /mcp".
Scribe is up. That is your home route.
Now a second program, in a second terminal, reaches it with a web address instead of a command.
On screen: a short Python script. mcp.Client("http://127.0.0.1:8765/mcp"), then call_tool read_doc with draft.md. The draft comes back, "# My first draft", "This is a test. This line has a typo in it."
Same draft, same words, through a web address. And I tested the guard over it too. Ask for a change, say no, and the draft is untouched. The question travels with the transport, because it never lived in the transport. It lives in your server.
Why Claude Desktop cannot use it yet
So can Claude Desktop use that address right now?
No, and I want to be exact about why, because it decides the next lesson.
The settings file only takes a command. There is no line for a web address in it. A web address goes in through a different door, called a custom connector, under the connectors settings of your Claude account.
And a custom connector is reached from Anthropic's own computers, not from your laptop. So an address that only means something on your machine, like localhost, can never work there. Claude's cloud would be looking for a server on itself.
So before Claude can use the web address, Scribe has to be on the public internet. That is lesson 28, and it is the reason lesson 28 exists.
Where the map stands
So where does our map stand at the end of this lesson?
15 boxes lit. The web address is on, under the pipe. Both pills run into the same server, because 1 call chose between them.
Next lesson Scribe runs on a free host, and Claude reaches it from the cloud. One last box to go after that.
Bye now, and I will see you in the next lesson.