Acme Enterprise Capability Hub
README.md
# Acme Enterprise Capability Hub
A runnable teaching project for **Model Context Protocol (MCP)** in a
realistic, polyglot enterprise environment.
The one idea it exists to demonstrate:
> **Claude/LLM sees BUSINESS CAPABILITIES, not underlying technologies.**
> Claude asks for `check_device_eligibility`. MCP decides that answering it
> means calling a Java service, PostgreSQL, a Python library, a Node.js
> service, and a Java pricing library, in that order, and merging the
> results. Claude never sees any of that.
Read [architecture.md](architecture.md) for the full design (with diagrams).
This README is the "how do I actually run it" doc.
---
## What's actually running under the hood
| Business capability (what Claude sees) | Technology (what Claude never sees) |
|---|---|
| `create_customer`, `get_customer` | Java HTTP service (plain JDK, no Spring/Maven needed) |
| `calculate_customer_discount` | Java library, invoked as a subprocess |
| `check_customer_credit_status` | Python library, in-process |
| `find_overdue_invoices`, `get_customer_balance`, `get_subscription_status`, `schedule_service_appointment` | PostgreSQL |
| `get_customer_preferences` | MongoDB |
| `get_available_inventory` | Node.js/Express service |
| `get_legacy_customer_status` | SOAP/XML service |
| `get_customer_network_status` | gRPC service |
| `get_customer_product_bundle` | GraphQL service |
| `search_customer_contracts` | Elasticsearch |
| `search_product_knowledge` | Qdrant vector database |
| `retrieve_customer_document` | MinIO (S3-compatible) |
| `create_support_case` | Mock CRM (REST) |
| `generate_billing_report` | Internal CLI utility (controlled subprocess) |
| `publish_customer_event` | Mock event bus (REST) |
| `check_device_eligibility` | **5 systems**: Java + PostgreSQL + Python + Node.js + Java library |
| `run_compliance_check` | **4 systems**: Java + PostgreSQL + SOAP + Python |
A note on pragmatic tech choices: the Java services are plain-JDK
(`com.sun.net.httpserver`), not Spring Boot, so the whole demo runs with
just `javac`/`java` — no Maven, no internet access to a Maven repo required.
They're genuinely Java, genuinely compiled, genuinely a separate process
communicating over HTTP; they're just not wearing a framework. See
`architecture.md` for the reasoning.
---
## Prerequisites
- Python 3.11+
- Node.js 18+
- A JDK (8+) — `javac` and `java` on your `PATH`
- Docker Desktop (for PostgreSQL / MongoDB / Elasticsearch / Qdrant / MinIO)
## 1. Install dependencies
```bash
python -m venv .venv
make install
```
(`make install` runs `pip install -r requirements.txt` into `.venv` and
`npm install` in both Node services. No `make`? See the equivalent raw
commands in each Makefile target — they're two or three lines each.)
## 2. Start the data stores
```bash
make db-up # docker compose up -d: Postgres, MongoDB, Elasticsearch, Qdrant, MinIO
make seed # seeds MongoDB, Elasticsearch, Qdrant, MinIO with demo data
# (Postgres seeds itself on first boot via database/postgres/init.sql)
```
> **Port note:** Postgres is published on host port **55432**, not 5432 —
> many dev machines already have a local PostgreSQL install on 5432 (or even
> 5433 if you have two versions installed). Check `docker compose ps` and
> `.env.example` if you ever need to confirm the mapping.
## 3. Build the Java pieces and start the mock services
```bash
make build-java # javac --release 8, plain JDK, no Maven
make run-services # starts java-customer-service, node-inventory-service,
# graphql-service, grpc-network-service, soap-legacy-service,
# mock-crm, event-bus-service - logs to logs/*.log
```
Verify everything is up:
```bash
curl http://localhost:8081/health # java-customer-service
curl http://localhost:3000/health # node-inventory-service
curl http://localhost:4000/health # graphql-service
curl http://localhost:8083/health # soap-legacy-service
curl http://localhost:8090/health # mock-crm
curl http://localhost:8095/health # event-bus-service
```
## 4. Register the MCP server with Claude Code
This project ships a project-level [.mcp.json](.mcp.json) already pointing
at `.venv/Scripts/python.exe mcp_server/server.py` with every backend URL
wired up. Just open this folder in Claude Code — it will pick up
`.mcp.json` automatically. To use Claude Desktop instead, copy
[claude_desktop_config.json.example](claude_desktop_config.json.example)
into your Claude Desktop config and replace the example paths with your
actual clone location (Claude Desktop requires absolute paths).
## 5. Try it
Ask Claude Code things like:
- *"What MCP tools does the acme-enterprise-capabilities server expose?"*
- *"Is customer C123 eligible for an iPhone 17 Pro?"*
- *"Show me overdue invoices for customer C123, then explain what happened internally."*
See [Demo scenarios](#demo-scenarios) below for a full script, and
[Claude Code experience](#claude-code-experience) for how to ask Claude to
explain the plumbing without it ever appearing in the tool descriptions
themselves.
---
## Demo data
Three seeded customers to experiment with:
| Customer | Plan | Status | Notable |
|---|---|---|---|
| `C123` (Alice Nakamura) | Premium 5G Unlimited | ACTIVE | 2 overdue invoices, 18mo tenure — good eligibility/discount demo |
| `C456` (Ben Torres) | Basic 4G | ACTIVE | Clean billing history — good "no issues" demo |
| `C789` (Carla Diaz) | Premium 5G Unlimited | SUSPENDED | Fraud flag in legacy system — good compliance-failure demo |
Devices: `iphone-17-pro` (in stock), `iphone-17` (in stock),
`galaxy-s26-ultra` (out of stock), `pixel-10-pro` (low stock).
---
## Demo scenarios
Each of these is a real prompt you can give Claude Code once the server and
mock services are running. "Expected" describes what you should see in the
server logs (`make logs`) as well as Claude's answer.
1. **"Create a customer named John Smith with email john@example.com."**
`create_customer` → Java REST customer service.
2. **"Show me all overdue invoices for customer C123."**
`find_overdue_invoices` → PostgreSQL. Returns 2 overdue invoices.
3. **"Check the credit status of C123."**
`check_customer_credit_status` → PostgreSQL (inputs) + Python credit-risk library. Returns FAIR (tenure is good, but 2 overdue invoices drag it down).
4. **"Is customer C123 eligible for an iPhone 17 Pro?"**
`check_device_eligibility` → Java → PostgreSQL → Python → Node.js → Java pricing library. Returns `eligible: true`, `discount: 112.5`.
5. **"Find contracts mentioning international roaming."**
`search_customer_contracts` → Elasticsearch full-text search.
6. **"What are the eligibility rules for the Premium 5G plan?"**
`search_product_knowledge` → Qdrant semantic search over the knowledge base.
7. **"Get the latest contract document for C123."**
`retrieve_customer_document` → MinIO/S3.
8. **"Create a support ticket for C123 because they can't make international calls."**
`create_support_case` → mock CRM.
9. **"Check whether C123 passes compliance."**
`run_compliance_check` → Java → PostgreSQL → SOAP → Python. Returns `compliant: false`, flag `MULTIPLE_OVERDUE_INVOICES` (try `C789` for a fraud-flag failure instead).
Bonus — the "bad vs good" lesson, in one prompt: *"Would you ever expose a
tool called `execute_sql` to me? Why does this server use
`find_overdue_invoices` instead?"* Claude can answer this directly from the
tool descriptions it's been given — none of which mention SQL.
---
## Claude Code experience
Useful things to ask once the server is registered:
- **"What MCP tools are available?"** — lists all 21 business-capability
tools, with zero mention of Java/Postgres/gRPC/etc.
- **"What tool would you use to check device eligibility?"** — Claude
should answer `check_device_eligibility` from the tool description alone.
- **"Call the device eligibility capability for C123 and the iPhone 17
Pro."** — invokes the real 5-adapter orchestration.
- **"Explain what happened internally."** — this is the interesting one.
Claude can only answer from the JSON result it got back (customer name,
credit status, discount, etc.) — it has no visibility into which
adapters ran, in what order, or what the raw Postgres/HTTP calls looked
like, because that's deliberately never exposed through MCP. *You* get
that story by reading the server logs (`make logs`) or the walkthroughs
in `architecture.md` §7 and §9 — which is the whole point: the
capability boundary is real, not just a naming convention.
---
## Running the tests
```bash
make test
# or directly:
.venv/Scripts/python.exe -m pytest tests/ -v
```
The suite has two layers:
- **Unit tests** (`tests/adapters/`, `tests/capabilities/`, `tests/test_models.py`,
`tests/test_server.py`) — HTTP-mocked (via `respx`) or subprocess-mocked,
run anywhere, no Docker or mock services required. Cover adapter success
paths, `NOT_FOUND`/timeout/backend-unavailable failure paths, Pydantic
validation rejection, and — for the two flagship capabilities — that
*every* expected adapter actually gets called (and that the Java pricing
subprocess is skipped when the customer isn't eligible).
- **Integration tests** (`tests/integration/`) — hit the real docker-compose
stack and real mock services. Each self-skips via a `requires_backend`
marker if its backend isn't reachable, so `pytest` stays green even before
you've run `make db-up`. Run `make db-up && make seed && make build-java
&& make run-services` first to exercise all of them, including a
real-subprocess, real-HTTP, real-SQL run of `check_device_eligibility`.
## Viewing logs
```bash
make logs # tails logs/*.log for all mock services
docker compose logs -f # data store logs
```
The MCP server itself logs to stderr with the `[MCP TOOL]` / `[CAPABILITY]`
/ `[ADAPTER]` / `[CALL]` / `[ADAPTER RESULT]` structure described in
`architecture.md` §9 — when Claude Code launches it, that output shows up
in Claude Code's MCP server log viewer; when you run `make run-mcp`
directly, it prints straight to your terminal.
## Inspecting tools directly (no Claude needed)
```bash
make tools
```
Prints every registered tool name and the first line of its description —
useful for confirming the "Claude sees capabilities, not technology" claim
for yourself.
## Stopping everything
```bash
make stop-services # kills the 7 mock service processes
make db-down # docker compose down (add -v to also drop volumes/data)
```
---
## Project structure
```
acme-enterprise-mcp/
├── mcp_server/
│ ├── server.py MCP tool definitions (the only file Claude talks to)
│ ├── models.py Pydantic request models + structured error types
│ ├── config.py Environment-driven settings (every backend URL)
│ ├── observability.py Correlation-ID logging ([MCP TOOL]/[CAPABILITY]/[ADAPTER]/...)
│ ├── adapters/ One class per technology (15 adapters)
│ └── capabilities/ Business logic + orchestration (6 files)
├── services/ Mock backend services (Java, Node, Python)
├── libraries/ Java pricing "library" + Python credit-risk library
├── database/ Postgres schema+seed, Mongo seed
├── documents/ Sample contracts (for Elasticsearch) + knowledge base (for Qdrant)
├── scripts/ Seed scripts + the internal billing-report CLI utility
├── tests/ Unit + integration tests
├── docker-compose.yml Postgres / MongoDB / Elasticsearch / Qdrant / MinIO
├── capability-registry.yaml Documentation: capability -> adapters -> technology map
├── architecture.md Full design doc with diagrams
├── .mcp.json Claude Code MCP server config (this project)
└── claude_desktop_config.json.example
```
## Environment variables
See [.env.example](.env.example) for the full list (every backend URL,
credential, and timeout). Copy it to `.env` if you want to override
anything — `mcp_server/config.py` loads it via `python-dotenv`. Docker
Compose reads plain values baked into `docker-compose.yml` directly (this is
a local demo; there are no real secrets anywhere in this repo).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues