Build a local MCP server and inventory every tool it exposes

short · 45 min · Objective 3.1

Task

Set up a small Model Context Protocol server inside your lab, describe it in a client configuration you write yourself, and produce an inventory of what it lets a model see and do. One of its tools is deliberately too broad. Find it from the inventory, narrow it, and prove the narrowing with the canary string rather than by reading your own code and trusting it.

Steps

  1. Create ~/secai-lab/mcp/lab_server.py using the SDK's FastMCP helper. Expose four tools: search_docs(query), a keyword search over docs/ that returns file names and first lines; read_doc(name); append_note(text), which appends to lab/notes.md; and read_file(path), which reads whatever path it is given. The last one is deliberately too broad, and it is the finding this lab exists to catch. Add one resource that serves the product FAQ.
  2. Write the client configuration ~/secai-lab/mcp/client-config.json in the mcpServers shape most MCP clients read: a server name, the command and args that launch it, and an env block. The lab server needs no credentials, so nothing secret belongs in this file. That absence is one of the things you will check.
  3. Write ~/secai-lab/mcp/list_tools.py, which launches the server over stdio with the SDK's client, initialises a session and calls list_tools() and list_resources(). Write the tools to lab/mcp-tools.json as a list of objects with name, description and input_schema, and the resources to lab/mcp-resources.json. This is what a connected model is told it can do. Read the descriptions as model input, because that is what they are.
  4. Write lab/mcp-tool-inventory.csv with header server,kind,name,reads,writes,data_reach,needs_approval,verdict, one row per tool and per resource. kind is tool, resource or prompt; data_reach names the widest thing the entry can touch; writes and needs_approval are yes or no; verdict is keep, narrow or remove. Any entry that writes needs approval.
  5. Write ~/secai-lab/mcp/probe.py, which calls read_file with docs/restricted/salaries.md through the SDK client and prints the result. Run it now and count the canary in its output. It will be there. Write the count in lab/mcp-review.md, because a check you have never seen fail has not shown that it detects anything.
  6. Narrow the server. read_file must resolve the path it is given, so that .. cannot climb out, and refuse anything outside docs/ or under docs/restricted/. append_note must refuse text above a fixed length. Update the inventory's verdicts, then rerun list_tools.py and the probe.
  7. Add one line to lab/mcp-review.md for each of the lesson's six vetting questions as they apply to this server: data flow, permissions, identity and credentials, agency, provenance and observability. Start each line with - .

Verify

cd ~/secai-lab
python mcp/list_tools.py
python - <<'EOF'
import csv, json
exposed = {t['name'] for t in json.load(open('lab/mcp-tools.json'))}
inv = {r['name']: r for r in csv.DictReader(open('lab/mcp-tool-inventory.csv'))}
print('exposed tools:', sorted(exposed))
print('exposed but not inventoried:', sorted(exposed - set(inv)) or 'none')
loose = [n for n, r in inv.items() if r['writes'] == 'yes' and r['needs_approval'] != 'yes']
print('write-capable entries with no approval:', loose or 'none')
EOF
python mcp/probe.py | grep -c CANARY-7Q4X-SECAI
grep -ciE 'key|token|secret|password' mcp/client-config.json
grep -c '^- ' lab/mcp-review.md

Four exposed tools, none missing from the inventory, and no write-capable entry without approval. The probe's canary count must now be 0, where your first run in step 5 found at least one. The credential search of the client configuration must print 0, and the review must have six lines. If the probe still finds the canary, the path check runs before the path is resolved, which is the usual mistake.

Notes

The descriptions in lab/mcp-tools.json are worth a second look. A model decides which tool to call partly from that text, which is why a malicious server can steer a model through its own tool descriptions. Your server's descriptions are honest because you wrote them. A community server's are whatever its author wrote, and that is the supply-chain point the lesson makes.

This is an independent study companion for CompTIA SecAI+ CY0-001 and is not produced by or endorsed by CompTIA.