Alembic¶
| Field | Value |
|---|---|
| Owner | Olger Avila (IT) |
| Status | live |
| Runs on | Windows Server, "Jetty box" (192.168.1.6, office LAN) |
| Connectivity | Public HTTPS via Caddy reverse proxy, NOT via Cloudflare Tunnel |
| Hostname | mcp.alquemis.com |
| Repository | Alquemis-Automation/Alembic (private) — moved from Olger's personal account, renamed from "MCP-Internal" |
What it is¶
A read-only MCP server giving Claude (and other authorized MCP clients) access to two Alquemis data sources:
- Oracle
INTG4PDB— company catalog, global config, and the Compras app's purchase requests/POs (schemaBIO). - SAP Business One — via a local Oracle cache (
BIO.SB1_*tables, refreshed by ETL at 3am; up to ~24h stale), and optionally the live SAP B1 Service Layer (client exists, currently unused by any active tool — kept on standby for future real-time needs).
Built with FastMCP (mcp[cli]), Python 3.13. Publishes six read-only JSON-RPC tools; nothing
creates, modifies, or cancels data in either system.
Athanor built its own SAP B1 connector/MCP independently in the same window this one was being
finished (2026-07-09) — see connectors/sap-b1/README.md for that history. Olger's is the
canonical implementation going forward; Athanor's was retired to avoid duplicate work.
Tools published¶
| Tool | Function |
|---|---|
list_companias(estado='A', etl='S') |
List group companies (initials, alias, name, tax ID) |
get_compania(compania) |
Detail for one company, by initials or alias |
get_config(config) |
Value of a global config entry |
get_open_purchase_orders(compania, proveedor) |
Open POs for a supplier |
get_purchase_order_lines(compania, doc_entry) |
Lines of a PO by DocEntry |
get_purchase_order_full(compania, doc_entry) |
Combined SAP + INT_SOLCOMP view, including inconsistencies |
How it's reached¶
Endpoint: https://mcp.alquemis.com/<TOKEN>/, MCP streamable-HTTP transport.
Claude ──HTTPS──► mcp.alquemis.com/<TOKEN>/ (443, TLS)
│ 201.202.107.128 → NAT → 192.168.1.6
▼
Caddy
│ 1. terminates TLS (Let's Encrypt)
│ 2. validates the token in the URL path
│ 3. rewrites Host → localhost:8000
▼
127.0.0.1:8000 (FastMCP / Uvicorn, loopback-only)
│ 4. validates Host (anti DNS-rebinding)
│ 5. dispatches the requested tool
▼
Oracle (192.168.1.5:1521) or, optionally, SAP B1 Service Layer
Security notes:
- The Python MCP process only listens on loopback (127.0.0.1:8000) — never directly
reachable from the network. Caddy is the only component with public ports (80/443).
- The token is embedded in the URL path (handle_path /<TOKEN>/*); any request without it
gets a 404, not a 401 — the service's existence isn't revealed to unauthenticated requests.
- FastMCP rejects requests with an unexpected Host header (anti DNS-rebinding), which is why
Caddy must rewrite Host to localhost:8000 before proxying.
- Oracle access uses a dedicated read-only user (BIOMCP) with grants limited to select/
execute on specific objects — even a compromised MCP process can't write to Oracle.
Configuration¶
.env file on the server (not versioned) holds Oracle/SAP connection parameters. SAP B1
Service Layer credentials are read dynamically from the Oracle table BIO.SB1_GENERAL, not
hardcoded anywhere in the app.
Dependencies¶
- Oracle
INTG4PDBat192.168.1.5:1521(Easy Connect:192.168.1.5:1521/INTG4PDB.Bioland.local) - SAP B1 Service Layer at
sap-bioland-sl.skyinone.net:50000(/b1s/v2/) — client exists, not currently used by any active tool - Caddy + Let's Encrypt (auto-renewed TLS for
mcp.alquemis.com) - Windows services via NSSM (
caddy,mcp-sapb1) - Consumer: Mycelium's procurement agent (Hyphae / "Compras Agent")
Known limitations¶
- Read-only. No creates, updates, or cancellations of POs, business partners, or items.
- Cache staleness. PO queries hit the local Oracle cache (ETL at 3am) — up to 24h behind SAP B1, not real-time.
- Narrow scope. Only the six tools listed — no items, sales orders, invoices, etc.
- Manual deploy. No CI/CD; deploy is
git push+ copyserver.pyto the server + restart the Windows service. - Single shared token. Auth is one bearer token in the URL, not per-user identity — no rotation automation.