sett-mcp
This server provides read-only contact lookup over a demo contacts store.
Search contacts by a case-insensitive query across name, company, and other fields, with an optional limit from 1 to 100 (default 10); results include contact IDs and masked fields show as
[masked].Retrieve every field for a single contact by its ID, with masked fields returned as
[masked].Both exposed tools are read-only; no write or delete operations are available.
Python MCP servers that are read-only until you say otherwise. One command gives you a working server on the official MCP Python SDK v2, with guardrails that are tested and on by default.
uvx sett-mcp new my-serverA sett is a badger's den, dug deep and hard to break into. Setts are also the square stones that pave a solid road.
Why
Connecting an AI assistant to a real system is easy. Being comfortable leaving it connected is the hard part: what happens when the model decides to "clean up" some records? Most MCP starters give you a tool decorator and leave the rest to you. This one starts from the question your ops or security lead will ask.
Related MCP server: MCP Mingdao
What you get
Guardrail | How it works |
Write gate | Write tools aren't registered unless |
Confirmations | Destructive tools ask the user first ("Permanently delete contact c001?"), using multi-round-trip requests on the 2026-07-28 protocol and elicitation on older ones. Clients that can't show a prompt are refused, not waved through. See client support. |
Allow / deny lists | Tools the policy excludes are never registered. |
Field masking |
|
Audit log | One JSON line per call: tool, kind, outcome, duration, masked arguments. |
Tool hints | Every tool is marked read-only or destructive so clients can warn users. |
Strict policy file | Unknown keys fail at startup, so a typo can't silently turn masking off. |
Plus the plumbing that usually takes an afternoon:
A runnable example (a small contacts store) you replace with your real API client
install <client>for Claude Desktop, Cursor, Claude Code and Codex, with--writesto turn write tools on, and a troubleshooting guide in every projectStdio by default, stateless Streamable HTTP with
serve --http, tested against the edge cases that break real clientsRemote in one command:
deploy vercel --auth supabasehosts the server on your own Vercel account with Supabase for sign-in: a consent page, invite-only users, a writer role for write tools, confirmations that work across server instances, and an audit table. Remote mode also works with any OAuth provider on any hostSetup instructions for Claude Desktop, Claude Code, Cursor, the MCP Inspector and Docker
Tests that drive the server through a real MCP client on both protocol versions
GitHub Actions for CI and PyPI publishing, so users can run
uvx your-serverA
Dockerfile,glama.jsonand a publishing guide for PyPI, Glama, the awesome-mcp-servers list and the official MCP RegistryA
CLAUDE.mdthat keeps Claude Code from bypassing the guardrails when it adds tools, and shows it how to describe tools well
Quick start
uvx sett-mcp new my-server
cd my-server
uv sync
uv run --extra dev pytest # the guardrail test suite
uv run my-server install claude-desktop # or cursor, claude-code, codex, all; then restart itTo host it for others, with sign-in, on your own Vercel and Supabase accounts:
uv run my-server deploy vercel --auth supabase --supabase-project <ref>
uv run my-server users add you@example.com --writerCopier asks for a name, tool prefix and environment-variable prefix. Everything else is filled in. New to this? docs/getting-started.md walks through it from installing uv, on Windows and macOS.
Adding a tool looks like this:
@guard.read(name="acme_list_invoices")
async def list_invoices(status: str = "open") -> list[dict]:
"""List invoices by status."""
return await api.invoices(status=status)
@guard.write(name="acme_void_invoice", destructive=True,
confirm_message="Void invoice {invoice_id}? This can't be undone.")
async def void_invoice(invoice_id: str) -> dict:
"""Void an invoice."""
return await api.void(invoice_id)Client support
Tested in November 2026 with the example server this template generates:
Client | Tools and masking | Confirmation prompts |
Claude Code | ✅ | ✅ Shows an Accept / Decline prompt, including in auto mode |
Claude on the web (claude.ai, remote connector) | ✅ | ✅ Shows a "requests your input" card with Submit / Decline. Tested against a server deployed with |
Cursor | ✅ | ✅ Shows a Submit / Cancel prompt. Answer it promptly: Cursor gives up on a call that waits too long, then retries |
Codex CLI 0.167 | ✅ | ✅ Shows an Allow / Deny / Cancel prompt, after Codex's own approval for the tool call. Sett 0.2 and earlier sent a form schema Codex rejects, so it cancelled without asking (openai/codex#60158) |
Claude Desktop 4.x (Windows) | ✅ | ✅ for remote servers (a connector such as one made with |
What that means in practice:
A confirmation is only as strong as the client. Sett can't tell a person clicking Accept from a client that accepts automatically. The write gate and the allow / deny lists don't depend on the client at all, so treat those as the hard limits.
Where prompts can't reach (local servers in Claude Desktop today), keep the default
confirm: destructive, or deploy the server remotely so the prompt can be shown:deploy vercel --auth supabase --supabase-project <ref> --writes, give the person the writer role withusers add <email> --writer, and add the/mcpaddress as a connector. Without--writes, write tools aren't registered at all. Ordinary writes still work, and destructive ones are refused. Settingconfirm: offmakes them run without asking.Claude may refuse on its own. In our tests Claude sometimes declined to call a tool described as permanently deleting data, or asked in chat first. That's a welcome extra layer, but it depends on the model and the conversation, which is why the server enforces confirmation itself.
Tried another client? Open an issue with what you saw and we'll add it to the table.
Keeping generated projects current
Guardrail fixes and SDK updates land here. Pull them into any project made from the template with:
uvx sett-mcp updatesett-mcp is a thin wrapper around Copier, so uvx copier copy gh:kclarkedesign/sett-mcp my-server and copier update work too.
Migrating from FastMCP v1
SDK v2 renamed FastMCP to MCPServer and changed a dozen other things. See docs/migrating-from-v1.md for the checklist and how to move a hand-rolled write gate onto the guardrails.
Roadmap
A preflight
sett-mcp checkthat tests a running server against real clients' quirksOne-click
.mcpbbundles for Claude DesktopMore deploy targets: Cloudflare Workers, Cloud Run, Fly
Example connectors, starting with a read-only Salesforce analyst server
Contributing
Issues and pull requests are welcome. To work on the template itself:
uvx copier copy --trust --defaults --vcs-ref HEAD . /tmp/check
cd /tmp/check && uv run --extra dev pytestLicense
Apache-2.0. Projects you generate can choose Apache-2.0 or MIT. Logo and icon files are in assets/, including an animated reveal and a looping loading mark (rebuild them with node scripts/build_motion.mjs).
Available Tools
2 toolsacme_get_contactARead-only
Get every field of one contact by its id.
Use this when you already have an id, for example from search results. If you only know a name or company, use search instead.
| Name | Required | Description | Default |
|---|---|---|---|
| contact_id | Yes | The contact's id, as returned by search (for example c001). |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
acme_search_contactsARead-only
Find contacts whose name, company or any other field contains the query (case-insensitive).
Use this first when you don't know a contact's id. Each result includes the id to pass to the other contact tools. Masked fields show as "[masked]".
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Most results to return, from 1 to 100. | |
| query | Yes | Text to look for, such as a name, company or email domain. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
2 tool updates
v0.1.1- Changed
acme_get_contact1 field changed- added
Input schema / properties / contact_id / descriptionAdded value: +"The contact's id, as returned by search (for example c001)."
- Changed
acme_search_contacts4 fields changed- added
Input schema / properties / limit / descriptionAdded value: +"Most results to return, from 1 to 100." - added
Input schema / properties / limit / maximumAdded value: +100 - added
Input schema / properties / limit / minimumAdded value: +1 - added
Input schema / properties / query / descriptionAdded value: +"Text to look for, such as a name, company or email domain."
2 tool updates
- First observed
acme_get_contact - First observed
acme_search_contacts
TDQS
Scored across 2 tools
The two tools are cleanly separated: search by fuzzy field match versus get by exact id. Descriptions explicitly tell the agent which to use and reference each other, leaving no realistic chance of misselection.
Both names follow the same `acme_verb_noun` convention, which is predictable. The only nit is `search_contacts` (plural) versus `get_contact` (singular), a minor deviation that does not hinder readability.
Two tools is thin for a contact-management surface, though it is plausible for a deliberately read-only gateway. It sits at the borderline where the surface feels minimal rather than well-scoped.
The surface covers only read access: search and retrieve by id. There is no way to create, update, delete, or enumerate contacts, so any write workflow or bulk listing is a dead end.
Maintenance
Related MCP Connectors
Primarily to be used as a template repository for developing MCP servers with FastMCP in Python, P…
An MCP server that let you interact with Cycloid.io Internal Development Portal and Platform
Security scanner for MCP servers. Detect vulnerabilities, prompt injection, and tool poisoning.
An MCP server for deep research or task groups
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceA minimal demonstration MCP server showcasing basic tools, resources, and prompts functionality. Built as a template for learning and starting new MCP server projects with Smithery SDK.8 npmMIT

MCP Mingdaoofficial
AlicenseNot gradedqualityDmaintenanceA minimal MCP server template demonstrating basic tools, resources, and prompts functionality built with Smithery SDK.ISC- AlicenseNot gradedqualityDmaintenanceA production-ready Python template for building MCP servers with enterprise features including registry integration, configuration management, structured logging, and extensible patterns for tools, resources, and prompts.MIT
- AlicenseNot gradedqualityDmaintenanceA minimal template MCP server demonstrating basic tools, resources, and prompts functionality, built with Smithery SDK for quick prototyping and deployment.9 npmISC