The client launches your server as a subprocess and pipes JSON through stdin / stdout.
python my_server.py
Only on your machine. Nobody else can touch it. What 90% of tutorials show.
remote · HTTP
Your server runs somewhere on the internet. It has a URL. Clients connect over HTTP.
https://notes.example.com/mcp
How every real product does it. GitHub, Notion, Stripe. Works from your laptop, phone, work machine.
The moment your server is a URL, you have a problem the local version never had.
06 / access control
Anyone with the URL can call your tools
Your tools read data, write data, delete data. So every request now needs three answers.
01Who is calling this?
02What are they allowed to do?
03Who are they doing it on behalf of?
06 / access control
How it's going out there
~25%
of public MCP servers have no authentication at all
most
of the rest use a static API key. One long-lived secret in a config file, shared by everyone.
few
actually use proper OAuth, which is what the MCP spec says a remote server should do.
2026 audits of public MCP servers. Rough numbers, the shape is what matters.
06 / access control
Why a static key is a problem
agent 1
agent 2
agent 3
agent 4
agent 5
API_KEY= sk-a83f...
Which one is which? Same key, so you can't tell the agents apart.
Who told it to do that? No user attached to any request.
One gets compromised? Kill the key, break everybody.
Fine for a weekend project. Not fine for anything real.
07 / what the spec says: OAuth 2.1
Your MCP server does not do login
Claude / CursorMCP client
Your MCP serverjust validates tokens
Authorization serverlogin, consent, tokens
1
Claude calls your server with no tokenserver replies 401 + "here is where you go to log in"
2
User logs in at the authorization serverGoogle, email, whatever. Normal login screen.
3
User sees a consent screen"Claude wants to read your notes and create notes. Allow?"
4
Claude gets a token, sends it with every requestAuthorization: Bearer eyJ...
5
Your server checks the token, then runs the toolsignature, meant for this server, has the right scopes
08 / what's inside the token
Scopes say what. sub says who.
access token (decoded)
{
"sub": "U2abc...", ← the user"aud": "https://notes.example.com/mcp", ← YOUR server only"scope": "notes:read notes:write", ← what they approved"azp": "cursor-7f3a", ← which client (agent)"exp": 1757450000← short-lived
}
Per-tool permissions. list_my_notes needs notes:read, delete_a_note needs notes:write. Only approved read? Write fails cleanly and the client can ask for more.
Per-user data. Every call has a user id, so notes are stored per user with one line of code.
Revoke one thing. One user, one agent, without touching anyone else. That's the difference between a demo and a product.
09 / one more piece
Clients register themselves
Dynamic client registration. Claude, Cursor and VS Code tell the authorization server "hi, I'm a client, here's where to send the user back" and get an id automatically.
You never set up Claude as an app by hand. Paste a URL into the client, the rest just happens.
what Cursor does when you paste the URL
POST /mcp → 401
GET /.well-known/oauth-protected-resource
GET /.well-known/oauth-authorization-server
POST /register → client_id# browser opens: login → consent
POST /token → access_token
POST /mcp Authorization: Bearer → 200
10 / so who builds all that
You could build the authorization server yourself
You'd need
Do it yourself
Plug one in
Login page + consent screen
✗weeks
✓config
Token signing, expiry, refresh
✗get it wrong once, game over
✓done
Dynamic client registration
✗spec reading
✓toggle
Scopes per tool, per user
✗your own policy engine
✓dashboard
See who connected, revoke one
✗build an admin panel
✓dashboard
We're going to plug one in. But first, let's build the server.
let's build it
Three versions, same three tools
v1 · local
FastMCP, stdio, three tools over SQLite. Add it to Cursor. Watch the tool calls.
15 lines
v2 · remote
One line change: it's a URL now. Then hit it from another terminal with zero login.
the problem
v3 · protected
The 401 dance for real. Login, consent, scopes, per-user notes. Claude and Cursor sign in.