business-data-mcp
Provides read-only tools to search and retrieve customer and order data from PostgreSQL, with tenant isolation, PII masking, and source references.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@business-data-mcpsearch customers named Acme"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Business Data MCP
Secure, Auditable Read-only Search · 中文
A Python MCP server for Codex and Claude Code. Search PostgreSQL customers and orders, a JSON mock CRM, and Markdown business documents through authenticated, tenant-scoped tools with masking, source references, and audit logs.
All bundled business data is fictional. This is a reproducible local demonstration, not a production security certification. See security boundaries.
Architecture
flowchart TD
A[Codex / Claude Code / SDK client] -->|HTTP + Bearer token| B[Authentication]
B --> C[Validation / scopes / rate limits / audit]
C --> D[Business search service]
D --> P[Read-only PostgreSQL]
D --> R[JSON mock CRM]
D --> F[Controlled Markdown files]
P --> E[Masking / references / size limits]
R --> E
F --> E
E --> C
C --> L[JSON Lines audit]
C --> APython 3.12, uv, MCP Python SDK 2.2, Pydantic, Psycopg async pools, PostgreSQL 17. The service exposes four typed read-only tools and no arbitrary SQL, arbitrary file reads, writes, pagination, or export.
Related MCP server: PostgreSQL MCP Server
Quick start
Prerequisites: Docker with Compose and uv. Run commands from the repository root. uv selects or downloads Python 3.12.
uv sync --locked
uv run business-data-init
docker compose up -d --wait
uv run business-data-mcpInitialization creates private (0600) .env and .local credential files without printing secrets or overwriting existing credentials. Development tokens expire after 30 days.
MCP: http://127.0.0.1:8000/mcp. PostgreSQL: loopback port 5433, with a dedicated Compose volume.
In a second terminal at the repository root:
curl http://127.0.0.1:8000/health/ready
uv run business-data-demoThe SDK demo exercises four identities and all four tools without a paid model API. Use --identity support, pii, tenant-b, or restricted to select one. If BUSINESS_DATA_MCP_TOKEN is set, only that token is used.
Tools and results
Tool | Parameters | Behavior |
|
| Literal customer name substring or exact ID |
|
| Customer and authorized CRM summaries |
|
| At least one filter, combined with AND |
|
| Tenant-scoped keywords, excerpts and line numbers |
Queries must contain 2–100 characters after trimming. IDs allow letters, digits, underscores and hyphens, up to 64 characters. Limits are integers from 1 to 20. Phone matching is exact. Order statuses: pending, paid, shipped, cancelled. Dates use inclusive UTC calendar days (YYYY-MM-DD). Document terms are whitespace-separated, all must match; results sort by occurrence count then document ID.
Results contain records, sources, query_id, notices, and truncated, in both structuredContent and text. Each record references source URIs such as business://tenant-a/customers/C10001 or crm://tenant-a/customers/C10001. These are logical identifiers, not browsable URLs. Source timestamps come from the fixture metadata.
No match and inaccessible IDs both return empty records. Failures use MCP isError, with a stable error code and query ID; they are never disguised as empty results.
Identities and audit
Identity | Tenant | Permissions |
| tenant-a | All tools and CRM, masked PII |
| tenant-a | Same, plus original PII |
| tenant-b | Only tenant B data, masked PII |
| tenant-a | Customers only; CRM omitted, orders/docs denied |
The server loads SHA-256 token digests and principals from .local/tokens.json at startup. Raw local demo tokens are in .local/client-tokens.json. Restart the server after changing the token store.
Audit events in .local/audit.jsonl contain start/finish, identity, tenant, tool, filter field names, result count, elapsed time, and status. They exclude raw tokens, query values, and business payloads. Authentication rejections have a separate request ID. Audit failure blocks data delivery.
rg 'qry_RETURNED_ID' .local/audit.jsonlConnect Codex / Claude Code: CLI and desktop
These instructions target macOS clients and the MCP server on the same machine. Run terminal commands from the repository root after completing Quick start. Model-account login and MCP token authentication are separate.
Shared preparation: server and identity
curl -fsS http://127.0.0.1:8000/health/readyExpect {"status":"ready"}. If unavailable, start the database and keep the server running in one terminal:
docker compose up -d --wait
uv run business-data-mcpOpen another terminal at the repository root. CLI examples use an environment variable; desktop examples use a local static header to avoid terminal environment inheritance:
export BUSINESS_DATA_MCP_TOKEN="$(uv run python -c 'import json; print(json.load(open(".local/client-tokens.json"))["support"])')"This does not print the token. Repeat in new terminals and launch the CLI from the same terminal. To copy a token for desktop configuration on macOS:
uv run python -c 'import json; print(json.load(open(".local/client-tokens.json"))["support"], end="")' | pbcopyReplace PASTE_SUPPORT_TOKEN_HERE below with the clipboard contents, keeping one space after Bearer. Store actual credentials only in private local configuration, never in README files, examples, or Git.
1. Codex CLI
Check codex --version. If it returns zsh: command not found: codex and your macOS app includes this executable:
# Check the file first; installation locations can differ.
test -x /Applications/ChatGPT.app/Contents/Resources/codex
export PATH="/Applications/ChatGPT.app/Contents/Resources:$PATH"
codex --versionAdd the PATH export to ~/.zshrc if you want it in future terminals. If this executable is absent, install the CLI using the official Codex CLI instructions.
After loading the token environment variable above, register the server:
codex mcp add business-data \
--url http://127.0.0.1:8000/mcp \
--bearer-token-env-var BUSINESS_DATA_MCP_TOKEN
codex mcp get business-data
codexRegister once. get should show enabled: true, transport: streamable_http, the URL and token variable name; this confirms saved configuration only. Inside Codex, use /mcp to check the connection, then send the verification prompt below.
2. Codex desktop
Desktop and CLI share ~/.codex/config.toml on the same Codex host, but an app launched from the Dock does not automatically inherit a terminal's export. For this local demo, use a static header:
Copy the token with the
pbcopycommand above.Open
~/.codex/config.tomlusingopen -e ~/.codex/config.toml. If absent, first add a Streamable HTTP server namedbusiness-datathrough Settings → MCP servers → Add server.Replace only the existing server section, preserving other configuration and avoiding duplicate sections:
[mcp_servers.business-data]
url = "http://127.0.0.1:8000/mcp"
enabled = true
http_headers = { Authorization = "Bearer PASTE_SUPPORT_TOKEN_HERE" }Remove this server's bearer_token_env_var and any conflicting Authorization configuration in env_http_headers or a header helper. The CLI will then use the same static token without an export.
Save, enable the server under Settings → MCP servers, and select Restart. If unavailable, fully quit and reopen the app.
Start a new local conversation, inspect
/mcp, and send the verification prompt.
See the official Codex MCP documentation. This server uses a development Bearer token, so OAuth sign-in is not needed.
3. Claude Code CLI
Check claude --version and sign in to the client. With the token exported in the same terminal:
claude --mcp-config examples/claude.mcp.json --strict-mcp-configThe example expands the environment variable:
{
"mcpServers": {
"business-data": {
"type": "http",
"url": "http://127.0.0.1:8000/mcp",
"headers": {
"Authorization": "Bearer ${BUSINESS_DATA_MCP_TOKEN}"
}
}
}
}--strict-mcp-config limits this demo session to the specified MCP configuration. Use /mcp to inspect status, follow normal client tool approvals, then send the verification prompt. See the official Claude Code MCP documentation.
4. Claude Code desktop (the Claude app's Code tab)
Select Code → a local session and open this repository. The Code tab can read the project's .mcp.json. To work when launched from the Dock, use a static header in .mcp.json at the repository root:
{
"mcpServers": {
"business-data": {
"type": "http",
"url": "http://127.0.0.1:8000/mcp",
"headers": {
"Authorization": "Bearer PASTE_SUPPORT_TOKEN_HERE"
}
}
}
}If the file exists, merge only mcpServers.business-data; preserve other servers. This repository gitignores .mcp.json. Run chmod 600 .mcp.json to restrict local access. Do not copy actual credentials back into examples/claude.mcp.json.
Save and reopen a local Code session, trust the project and enable the MCP server when prompted, then send the verification prompt. Select the original checkout: a new worktree does not automatically include this gitignored configuration. CLI --mcp-config flags do not carry over to the desktop app. See the official Claude Code Desktop documentation.
Optional: Claude Desktop's ordinary Chat tab
Chat and Code have different setup entry points. Do not put http://127.0.0.1:8000/mcp into the account-level Add custom connector form: those requests originate from Anthropic's cloud and cannot reach your loopback address. See remote connector network requirements.
For ordinary Chat, a local stdio-to-HTTP bridge can connect to this server. It requires Node.js / npx and third-party mcp-remote, which are not Python runtime dependencies of this project:
Check
node --versionandnpx --version.Open Settings → Developer → Edit Config in Claude. The usual macOS file is
~/Library/Application Support/Claude/claude_desktop_config.json.Merge this server into
mcpServersand replace the token:
{
"mcpServers": {
"business-data": {
"command": "npx",
"args": [
"-y", "mcp-remote", "http://127.0.0.1:8000/mcp",
"--transport", "http-only", "--header", "Authorization:${AUTH_HEADER}"
],
"env": {
"AUTH_HEADER": "Bearer PASTE_SUPPORT_TOKEN_HERE"
}
}
}
}Fully quit and reopen Claude, enable the tool in a new Chat conversation, and send the verification prompt. First use downloads the bridge. The desktop process must be able to find Node/npx. With nvm, set
commandto the absolute path fromcommand -v npxand setenv.PATHto include the directory containingcommand -v node, plus system paths. Recheck paths after Node upgrades.
These are configuration instructions; Claude desktop Code / Chat and the bridge have not been empirically verified here. See verification results for clients actually tested.
Verify, switch identities, and troubleshoot
Send in any client:
Actually call business-data's get_customer tool for C10001. Show customer details, CRM follow-ups, source URIs, and query_id. Do not read local files or use terminal commands to obtain the data.
With support, expect 星河科技, phone 138****8000, the follow-up “已发送产品方案,等待技术评估”, source URIs business://tenant-a/customers/C10001 and crm://tenant-a/customers/C10001, and a new qry_... ID. Match that ID to query_started / query_finished in .local/audit.jsonl to confirm a real call.
Switch identity: for CLI environment-based auth, change
supportin the export command topii,tenant-b, orrestricted, then restart the client. For static auth, replace the configured token and reload. Do not reinitialize server credentials.Authorization checks:
restrictedqueryingORD-10001returnsFORBIDDEN;piiqueryingC10001shows the full phone;tenant-bquerying the same order returnscancelled,9900.00.401 / missing variable: check the token, expiry and configured auth source. Avoid relying on terminal-only variables in desktop apps.
Connection refused: check health and the server process; match the URL to
PORT.Tools missing: reload MCP or start a new session; check enabled state, project directory, and duplicate server configuration overriding your entry.
Remote sessions: in cloud or SSH sessions,
127.0.0.1refers to the execution host, not your computer. These instructions target local sessions.
Automated checks
make check # Ruff, formatting, mypy
make test # Unit tests; database tests explicitly skipped
make test-integration # Full suite against the running demo databaseGitHub Actions uses a PostgreSQL service, the same initialization script, and locked dependencies.
Configuration and operations
See .env.example. Environment variables override .env. Paths resolve from the working directory.
Setting | Default / meaning |
| Required read-only connection string |
|
|
|
|
|
|
| 60 tool calls per identity per minute |
| 10 seconds; database statements separately limited to 5 seconds |
| Up to 65536 bytes per MCP tool result, including text and structured output |
Oversized results lose whole records and set truncated; a single oversized record returns RESULT_TOO_LARGE. Request bodies are limited to 16 KiB. /health/live checks process liveness; /health/ready checks the database.
Stop the server with Ctrl+C and the database with docker compose stop. Normal restarts preserve data.
Reset deletes this project's demo database volume. Stop the MCP server first:
bash scripts/reset-demo.sh --confirm-delete-demo-dataReset keeps credentials. To regenerate credentials, first stop services and manually back up/move .env, .local/tokens.json, and .local/client-tokens.json; initialize again and reset the database. Changing passwords does not update an existing PostgreSQL volume.
Troubleshooting
macOS
docker-credential-desktopmissing: runexport PATH="/Applications/Docker.app/Contents/Resources/bin:$PATH", then retry Compose.Port 5433 occupied: use
POSTGRES_PORT=55433 uv run business-data-initon first setup. For existing setup, update both the database port andDATABASE_URL.Port 8000 occupied: run
PORT=8001 uv run business-data-mcpand change the client URL.Database unavailable: inspect
docker compose psand verify credentials match the initialized volume.HTTP 401: check token environment inheritance and expiry; restart server/client after token changes.
FORBIDDEN: check scopes. The restricted identity's order denial is intentional.Remote client cannot connect: loopback only works on the same machine; this release does not configure remote HTTPS hosting.
Roadmap
Real OAuth → PostgreSQL RLS → real CRM → centralized audit and distributed rate limiting → application containerization and deployment.
License: Apache-2.0.
This server cannot be deployed
Maintenance
Related MCP Connectors
Safe, read-only Postgres and MySQL access for AI agents. Audit log + column-level controls.
Query your org's data in natural language — read-only MCP access to SQL, NoSQL, files & warehouses.
- RumboOAuthcom.rumboar
Securely query and analyze business data, dashboards, projections, alerts, and knowledge.
Read-only SaaS business intelligence from GA4, Stripe, and Google Search Console.
Related MCP Servers
- AlicenseNot gradedqualityNot gradedmaintenanceEnables read-only access to PostgreSQL databases with multi-tenant support, allowing users to query data, explore schemas, inspect table structures, and view function definitions across different tenant schemas safely.55 npm1-
- FlicenseNot gradedqualityDmaintenanceProvides read-only access to PostgreSQL databases with schema inspection, query execution in multiple formats (JSON, CSV, Markdown), and query history tracking with built-in security features.-
- AlicenseNot gradedqualityBmaintenanceProvides secure read-only SQL access to PostgreSQL and ClickHouse databases with built-in safety features like read-only enforcement, timeouts, and managed result files.221 PyPIMIT
- AlicenseNot gradedqualityBmaintenanceEnables e-commerce clients to query their own analytics data in plain English with strict tenant isolation enforced by the database.MIT