Skip to main content
Glama
iNileshW

Licensing Hardening MCP Server

by iNileshW

Harden the local-authority licensing assistant's MCP server

Local authority — licensing team. The assistant answers questions about taxi and premises licences — "is licence 9003 active, and what did the last inspection say?" — so officers don't dig through the case system by hand. This MCP server is how it reaches the licensing database. A colleague "improved" it before they left; it works, it demos cleanly, and it carries every native MCP weakness from this afternoon. Work through it on your own.

Get it running

Plain Python — no Docker, no Git.

pip install -r requirements.txt
python seed.py            # builds licensing.db (includes a planted inspection note)

See the attacks land before you fix anything:

python exploit.py         # SQL injection + over-exposure of home addresses / DBS status
python poisoning_demo.py  # a tool whose DESCRIPTION carries hidden instructions

Related MCP server: Vulnerable MCP Server

Your task

Harden server.py so the native attacks fail while legitimate use still works. Work through them in this order:

  1. Result injectionread_inspection_note hands raw note text back to the model. Return it labelled as untrusted data, and rely on a system-prompt rule that labelled data is content, never command. (Licensee 3's note is an instruction to suspend a licence — the note-reader and suspend_licence, each harmless, compose into a suspension.)

  2. Tool poisoningformat_reference's description contains hidden instructions. Review every description; a server you do not trust should not reach the model's context. Neutralise it.

  3. Confused deputysuspend_licence is state-changing and unscoped. Put it behind a scope check, and reject any bearer token whose aud is not this server (the Week 5 discipline).

  4. Inherited — parameterise find_licensee and return only the columns the assistant needs (never home_address, dbs_status, or inspection_notes).

  5. Audit — append every tool call to a log.

You are done when poisoning_demo.py finds nothing in your descriptions and exploit.py's payloads are rejected, while a normal find_licensee("Aisha") still returns a result.

Stretch

  • Add a real aud check: decode the presented token and refuse a foreign audience.

  • With two servers connected to one host, how would a malicious second server reach this one's suspend_licence? Write down the control that stops it.

Files

File

Contents

server.py

The vulnerable MCP server — the thing you harden.

seed.py

Builds licensing.db — the licensees and licences tables.

exploit.py

Proves the inherited flaw (SQL injection + over-exposure).

poisoning_demo.py

Prints the model-facing descriptions; flags the poisoned one.

requirements.txt

fastmcp.

Questions worth asking yourself

  • Which of your fixes are MCP-specific, and which are ordinary secure coding?

  • A description you did not write reached your model. Whose job is it to have caught that — the server author, the host, or the person who approved it?

F
license - not found
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/iNileshW/mcp-licensing-hardening'

If you have feedback or need assistance with the MCP directory API, please join our Discord server