Lesson 20 / 25
Least Privilege and Approvals
Scope server credentials narrowly and keep a human in the loop for dangerous tools.
Narrow, read-only, approved
Give a server a credential that can only do what its tools need, such as a read-only database user. Expose separate read and write tools so you can allow reads freely and require approval for writes. Hosts show an approval prompt for tool calls; read it before clicking, and do not set "always allow" on destructive tools.
Split read and write
Two small tools are easier to secure than one that does both. The write tool can be gated while the read tool runs freely. db stands for your database helper.
@mcp.tool()
def get_order(order_id: int) -> dict:
"""Read one order. Read-only."""
return db.query_one("SELECT * FROM orders WHERE id = ?", order_id)
@mcp.tool()
def cancel_order(order_id: int) -> str:
"""Cancel an order. Changes data; requires approval."""
db.execute("UPDATE orders SET status = 'cancelled' WHERE id = ?", order_id)
return "cancelled"Parameterise queries
The example uses ? placeholders. Never build SQL by joining model-provided strings, or the model (or an attacker behind it) can run arbitrary SQL.
Quick check: Why split read and write into separate tools?
- So reads can run freely while writes need approval
- Two tools are always faster
- MCP forbids combined tools
- It removes the need for authentication
Answer
So reads can run freely while writes need approval — Separate tools allow different permissions and approval rules for different risk levels.