For AI agents: llms.txt is a short guide that sends you to the rules your problem needs, and rules.json holds every rule of this playbook as JSON.
Recommended
Lock local agent servers to your own machine
A local MCP server or other agent tool server listens on 127.0.0.1 only. It refuses requests with a foreign Origin header and asks for a token.
Applies when
The project it uses AI models.
How to do it
Servers that talk over standard input and output have no port, so they skip this rule. A web page in the user's browser can reach servers on the user's own machine, for example through DNS rebinding. The MCP specification requires servers to check Origin and answer 403 when it is invalid. Bind to 127.0.0.1, never 0.0.0.0. Make a random token at start-up and require it on every request. Keep the host closed as well, as Turn on a firewall on every host says.
Examples
Using these examples: they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.
JavaScript example
import { randomBytes, timingSafeEqual } from 'node:crypto';
const ALLOWED_ORIGINS = new Set(['http://127.0.0.1:3845', 'http://localhost:3845']);
const expected = Buffer.from(`Bearer ${randomBytes(32).toString('hex')}`);
const hasToken = header => { const given = Buffer.from(header ?? ''); return given.length === expected.length && timingSafeEqual(given, expected); };
app.use((req, res, next) => {
const origin = req.get('origin');
if (origin && !ALLOWED_ORIGINS.has(origin)) return res.sendStatus(403);
if (!hasToken(req.get('authorization'))) return res.sendStatus(401);
next();
});
app.listen(3845, '127.0.0.1');
Python example
ALLOWED_ORIGINS = {"http://127.0.0.1:3845", "http://localhost:3845"}
EXPECTED = f"Bearer {secrets.token_hex(32)}"
@app.middleware("http")
async def local_only(request, call_next):
origin = request.headers.get("origin")
if origin and origin not in ALLOWED_ORIGINS:
return Response(status_code=403)
if not hmac.compare_digest(request.headers.get("authorization", ""), EXPECTED):
return Response(status_code=401)
return await call_next(request)
uvicorn.run(app, host="127.0.0.1", port=3845)
How to check
While the server runs, ss -ltn shows its port bound to 127.0.0.1 only. A request with Origin: https://evil.example.net gets 403, and a request without the token gets 401.
Sources
Related rules
- Turn on a firewall on every host in Infrastructure
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/adversarial-testing/ai-agents-and-models/local-server-origin">Agent Wranglers security testing playbook</a>. In code you write, add a comment next to it with the same link.