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, the Compras app's purchase requests/POs, and item/BOM master data (schemaBIO). - SAP Business One — mostly via a local Oracle cache (
BIO.SB1_*tables, refreshed by ETL at 3am; up to ~24h stale). Live SAP B1 reads are also possible for verification:get_supplier_sapfetches straight from SAP B1, with the HTTPS call made by Oracle (theBIO.PK_SAPB1package), not by the MCP process. The standalone Python Service Layer client (sap_client.py) exists but remains unused by any active tool.
Built with FastMCP (mcp[cli]), Python 3.13. Publishes thirteen 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', tipo=None) |
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 non-sensitive global config entry (sensitive keys are blocked — see security notes) |
get_open_purchase_orders(compania, proveedor=None, grupo=None, moneda=None) |
Open POs for a supplier, optionally filtered by supplier group / currency |
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 |
list_supplier_groups(compania) |
Supplier groups of a company, with supplier counts |
get_supplier_sap(compania, proveedor) |
Live SAP B1 lookup of a supplier (bypasses the cache); returns SAP vs. local snapshot + differences, for verifying suspected drift |
list_items_produccion(compania, prefijo=None, estado=None) |
Producible items (those with a BOM) of a producing company |
get_item(compania, codigo) |
Item detail by exact code |
get_bom(compania, codigo) |
Bill of materials (direct components) of a producible item |
find_products_using(compania, componente, estado=None) |
Reverse BOM: products that use a given component (MP/ME/CF/MO) |
get_item_purchase_history(compania, item) |
Effective purchase history of an item by supplier, from posted A/P invoices (SB1_INVOICES type CFC, cancelled excluded; not open POs/quotes) — invoice count, last purchase, and last/min/max/avg unit price, grouped by currency |
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.
- get_config enforces a denylist: it refuses to return sensitive keys — SAP Service-Layer
credentials (SL_*), per-company CompanyDB, electronic-invoice keys (CLAVE_API_FE_*), the
Oracle wallet (WALLET_*), and any token/user entries — raising an error instead of the value.
A stopgap until those secrets are moved out of BIO.SB1_GENERAL (Oracle Wallet, under
evaluation by Olger).
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 — and those credential rows are shielded from the get_config
tool by the denylist (see security notes).
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. Most queries hit the local Oracle cache (ETL at 3am) — up to 24h behind
SAP B1, not real-time.
get_supplier_sapis the exception (live SAP read viaBIO.PK_SAPB1). - Purchasing + production scope. Covers companies, config, purchase orders, suppliers, items, and BOMs — no sales orders, invoices, inventory movements, etc.
- Single shared token. Auth is one bearer token in the URL, not per-user identity — no rotation automation.