Skip to main content
Glama
qilobit
by qilobit

mcp-server — tenant-safe, read-only

A Model Context Protocol server exposing operational tools over a multi-tenant dataset to any MCP client, with the tenant bound server-side and never present in the published tool schema.

The argument

An MCP server publishes its tool schema to whatever client connects — Claude Desktop, an IDE, a third-party agent. That makes the schema a trust boundary, and it changes the calculus compared to an agent you own end to end.

If a tool took a company parameter, tenant selection would be delegated to a model on the other side of the connection: one you did not write, cannot audit, and whose version can change without telling you. No prompt on your side constrains it, because the prompt lives over there.

So the tenant is not a parameter. It is read once, at process start, from the server's own configuration:

TENANT = resolve_tenant()   # from MCP_TENANT_ID; fails hard if unset

Cross-tenant access is not "unlikely" or "blocked by instructions" — there is no argument in which to express it. That is the entire design, and everything below exists to prove it holds rather than assert it.

This matches how MCP is actually deployed: one server process per user, launched by the client with its own configuration. Identity belongs to that config, not to the conversation.

Related MCP server: Multi-Tenant Analytics MCP

Verified, not asserted

MCP_TENANT_ID=acme python scripts/check_isolation.py   # 5 properties
python scripts/check_e2e.py                            # real stdio handshake

check_isolation.py asserts, against the running server:

#

Property

1

No published tool exposes a tenant-like parameter (company, tenant_id, merchant, …)

2

Every tool is annotated read_only_hint

3

Executing every published tool leaks no other tenant's canary

4

Injecting company=<other> into a call returns nothing belonging to that tenant

5

Without MCP_TENANT_ID the server fails to start instead of defaulting

Check 3 enumerates whatever the server publishes and builds arguments from each schema, rather than calling a hardcoded list. A hardcoded list would silently skip exactly the tool someone adds later — which is the case worth catching.

check_e2e.py starts the server as a subprocess and drives it through initialize → list_tools → call_tool over stdio: proof it speaks the protocol, not just that the Python objects behave.

The checks are themselves checked

MCP_UNSAFE_MODE=1 publishes a deliberately broken tool that accepts company and honours it. The isolation check must go red under it:

MCP_TENANT_ID=acme MCP_UNSAFE_MODE=1 python scripts/check_isolation.py
# FAIL  no tool exposes a tenant parameter   -> list_orders_unsafe.company
# FAIL  no output contains another tenant's canary -> leaked: ['globex']

A guard that has never gone red has not been shown to detect anything. Shipping the broken variant is what makes the green run mean something.

Run it

python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt

cp .env.example .env
MCP_TENANT_ID=acme python -m server.main    # stdio

In an MCP client (Claude Desktop and similar), register one process per tenant:

{
  "mcpServers": {
    "acme-ops": {
      "command": "/path/to/.venv/bin/python",
      "args": ["-m", "server.main"],
      "cwd": "/path/to/mcp-server",
      "env": { "MCP_TENANT_ID": "acme" }
    }
  }
}

Tools

All read-only. None takes a tenant argument.

Tool

Returns

sales_summary(start_date, end_date)

Order count and total for a date range

list_orders(status)

Orders with status paid / pending / refunded

top_customers()

Customers ranked by total spend

Structure

mcp-server/
├── server/
│   ├── tenant.py   # the only place a tenant is decided
│   ├── data.py     # synthetic multi-tenant dataset with per-tenant canaries
│   └── main.py     # MCP server; tools + the gated unsafe variant
├── scripts/
│   ├── check_isolation.py   # the 5 properties above
│   └── check_e2e.py         # stdio protocol handshake

The dataset is fictional so the repo is clonable and runnable as-is.

Scope

This is a portfolio implementation of one pattern, not a product. See SECURITY.md for what it does and does not cover — notably that MCP_TENANT_ID stands in for a verified token, which is what a real deployment would use.

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.

Related MCP Servers

  • F
    license
    -
    quality
    C
    maintenance
    Enables AI to query a business database for customers, orders, and revenue using natural language through safe, well-defined tools.
  • A
    license
    -
    quality
    B
    maintenance
    Enables e-commerce clients to query their own analytics data in plain English with strict tenant isolation enforced by the database.
    MIT
  • F
    license
    -
    quality
    C
    maintenance
    Provides read-only tools for querying a synthetic e-commerce dataset, including product categories, products, sales data, customer history, and order details.
  • F
    license
    -
    quality
    C
    maintenance
    Provides read-only access to a sales database, letting LLMs answer analytics questions like revenue by region or top customers via MCP tools.

View all related MCP servers

Related MCP Connectors

  • Read-only MCP server for Muovi, Argentina's trust-first local services marketplace (6 tools).

  • Bounded tools for rendering, extraction, RAG, enrichment, local discovery and review analysis.

  • Read-only access to your VortexIQ store data: audits, KPIs, alerts, Brand DNA, reports, Ask VIQ.

View all MCP Connectors

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/qilobit/mcp-server'

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