Skip to main content
Glama
sudsho

mcp-server-enterprise-tools

by sudsho

mcp-server-enterprise-tools

An opinionated tool gateway for LLM-driven backends, designed around the MCP tool model but shipped with its own JSON transports. It focuses on the governance layer: per-tool RBAC from a declarative YAML policy, a structured audit row for every invocation, PII redaction on arguments before persistence, and a typed error envelope so clients can branch on codes instead of string-matching messages.

The transport is a small HTTPS API (POST /mcp/call) plus a Prometheus /metrics endpoint. The stdio path that would let a full MCP host discover and call these tools is out of scope in this repo.

The problem

Enterprise backends that want to expose internal APIs to LLM-driven callers hit three gaps that are not addressed at the protocol layer:

  • caller identity is not first-class and needs to be resolved out-of-band

  • tool arguments and results can carry PII (SSNs, PANs, account numbers) and those cannot land in cleartext in logs or audit tables

  • error handling needs to be machine-readable so internal callers can branch on codes without string-matching messages

This repo is an opinionated take on filling those gaps while keeping the tool implementations small and readable.

Related MCP server: MCP Enterprise Tool Gateway

What is exposed

Three bank tools (four operations):

Tool

Verb

Purpose

customer_360.lookup

read

profile, primary account summary, risk flags

statement_search.query

read

full-text with date and amount filters

dispute_resolution.open

mutating

open a dispute against a transaction

dispute_resolution.status

read

dispute state by id

Each has a JSON input schema. Adding a fourth is a single file under src/mcp_server/tools/ plus a line in app.build_registry.

Architecture

              +--------------------+          +------------------+
              |  Internal caller   |          |  Internal agent  |
              |  (any HTTP client) |          |  (http)          |
              +---------+----------+          +---------+--------+
                        |                               |
                        |               X-Bank-Identity |
                        v                               v
                    +---+-------------------------------+---+
                    |          Tool gateway (this repo)      |
                    |  transport -> registry.call -> handler |
                    |                    |                   |
                    |            rbac.check + audit.write    |
                    +----+-----------------+----------+------+
                         |                 |          |
                         v                 v          v
                +------------------+  +---------+  +------------------+
                | internal APIs    |  | Postgres|  | Prometheus       |
                | (customer,       |  | audit   |  | metrics scraped  |
                | statements,      |  | log     |  | at /metrics      |
                | disputes)        |  +---------+  +------------------+
                +------------------+

Longer version with request flow: docs/architecture.md.

Quick start (runs offline, no keys)

No cloud creds, no api keys, no database to stand up. The audit log defaults to a local sqlite file, and the three bank tools default to in-repo synthetic upstreams (MCP_UPSTREAM_MODE=fake). Point AUDIT_DB_URL at postgres and set MCP_UPSTREAM_MODE=http when you have the real services.

pip install -r requirements.txt
python -m examples.smoke     # in-process server + client, exits 0
PYTHONPATH=src pytest -q     # 31 passed

python -m examples.smoke starts the FastAPI gateway in-process, drives it through the full governance path with the http client, and reads the audit table back. Real output (trimmed):

1. client lists tools
  - customer_360.lookup: Look up a customer 360 view: profile, primary account...
  - statement_search.query: Search a customer's statements. Supports date range...
  - dispute_resolution.open: Open a dispute against a specific transaction...
  - dispute_resolution.status: Get the current status of an open dispute by id.

2. customer_360.lookup  (allowed for svc-analyst-01)
{
  "result": {
    "profile": {
      "name": "Jane A. Doe",
      "email": "[REDACTED]<email>",
      "ssn": "[REDACTED]<ssn>",
      "notes": "primary card [REDACTED]<pan>on file; backup acct [REDACTED]<acct>"
    },
    ...
  }
}

5. dispute_resolution.open  (DENIED for svc-analyst-01 by RBAC)
{
  "error": {
    "code": "forbidden",
    "message": "identity 'svc-analyst-01' is not permitted to call 'dispute_resolution.open'",
    ...
  }
}

6. audit log rows (read back from sqlite; args redacted)
  [ok   ] identity=svc-analyst-01         tool=customer_360.lookup        error=-
          args_redacted={"customer_id": "C-[REDACTED]<acct>"}
  [ok   ] identity=svc-dispute-agent-07   tool=dispute_resolution.open    error=-
          args_redacted={... "notes": "caller gave card [REDACTED]<pan>and ssn [REDACTED]<ssn>"}
  [deny ] identity=svc-analyst-01         tool=dispute_resolution.open    error=forbidden

  4 audit rows written, no raw PAN/SSN present.

SMOKE OK

And pytest:

31 passed in 1.21s

Quick start (production shape)

Local (http + postgres via compose):

docker compose up --build
# server on :8080, prometheus on :9090, postgres on :5432
curl -s -X POST http://localhost:8080/mcp/call \
  -H "X-Bank-Identity: svc-analyst-01" \
  -H "Content-Type: application/json" \
  -d '{"tool":"customer_360.lookup","args":{"customer_id":"C-01234567"}}'

Bare local run against the http transport:

pip install -e '.[dev]'
export AUDIT_DB_URL=sqlite:///./audit.db   # or a postgres url
make run-http                              # serves on :8080

RBAC model

Deny by default. Policies in configs/rbac.yaml map identities to allowed tool names, with a trailing .* wildcard for grouping. Every call, allowed or denied, writes an audit row. Full spec in docs/rbac_model.md.

Audit log design

One row per tool call. It defaults to a local sqlite file so the server runs offline, and swaps to Postgres for a real deploy by setting AUDIT_DB_URL (the store is plain SQLAlchemy, so nothing else changes). Columns: request_id, identity, tool, outcome, args_redacted, error_code, created_at.

Arguments are passed through Redactor before persistence: SSNs, PANs (Luhn-checked), email addresses, and 8-17 digit account-shaped numbers are replaced with marked tokens. The two read tools also apply the same redactor to their upstream response as defense-in-depth. Details in src/mcp_server/redaction.py and the tests.

Structured errors

Every failure comes back as an ErrorEnvelope:

{
  "error": {
    "code": "forbidden",
    "message": "identity 'svc-analyst-01' is not permitted to call 'dispute_resolution.open'",
    "request_id": "0d3f4a...",
    "tool": "dispute_resolution.open",
    "retryable": false,
    "details": {}
  }
}

Codes: unauthenticated, forbidden, not_found, invalid_argument, upstream_unavailable, rate_limited, internal. Clients can branch on code without string-matching messages.

Deploy

Terraform under terraform/ is a sketch of an internal ALB, ECS Fargate service, and RDS Postgres for the audit log. It has not been applied against a live account; treat it as a reference layout.

Prod checklist for a real rollout:

  1. Populate a real configs/rbac.yaml. Prefer many narrow identities over a few broad ones.

  2. Wire the X-Bank-Identity header to be set by a trusted upstream (mTLS terminator, OIDC proxy). The server does not authenticate; it only reads the resolved identity.

  3. Ship the audit table to your SIEM nightly. The table is the source of truth.

  4. Rotate the break-glass credential regularly and alert on every use.

Repo layout

src/
  mcp_server/       server, registry, rbac, audit, redaction, tools/
  mcp_client/       http client used for testing and examples
configs/            default.yaml, rbac.yaml, prometheus.yml
terraform/          aws vpc + ecs + rds reference layout
tests/              rbac, audit, redaction, tools, registry
examples/           request/response transcripts
docs/               architecture, rbac model

License

MIT. See LICENSE.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    C
    maintenance
    A production-grade MCP server for a fictional digital bank, exposing tools for an AI copilot to service customers across the full risk spectrum from read-only lookups to money movement and destructive admin actions, with OAuth 2.1 security and a realistic dataset.
    13 npm
    1
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    A universal MCP server for registering internal, external, and OpenAPI-based APIs as MCP tools. It exposes them to MCP clients via Streamable HTTP and provides admin portal, RBAC/session auth, credential injection, and audit logging.
    Academic Free v1.1
  • A
    license
    Not graded
    quality
    C
    maintenance
    An enterprise MCP server scaffold that provides secure, governed access to internal tools through RBAC, audit logging, rate limiting, and prompt-injection boundaries, with a FastAPI control plane and OpenTelemetry observability.
    MIT