Skip to main content
Glama
ntxptrevor

eGordian AEO MCP Service

by ntxptrevor

eGordian AEO MCP Service

A remote, headless, horizontally stateless MCP service that exposes:

  • the AEO (Assignment Estimate Operator) nine-stage deterministic estimating pipeline,

  • the private, licensed Gordian CTC catalogue behind a hard licensing firewall,

  • an exact allowlist of every operation documented on the eGordian JOC Service Help page,

over MCP protocol revision 2026-07-28 at a single POST /mcp endpoint, plus a minimal REST/status surface and an operator console.

The backend is the product. The console exists only to observe the service.


1. What it is (and is not)

Property

Value

MCP revision

2026-07-28 — no initialize handshake, no Mcp-Session-Id, no GET event stream, no sticky sessions

Transport

POST /mcp, JSON-RPC 2.0, one request object per call

Methods

server/discover, tools/list, tools/call, resources/list, resources/read

State

None in-process. All cross-call state lives behind a repository abstraction

Auth

Bearer only, private by default; /healthz is the sole public route

Writes

Disabled by default; require scope and actor and approval object and idempotency key

DELETE

Registered but permanently blocked (human_gate_required), never executed

Price proposal submit

Capability-blocked — no write route is documented (see §7)


Related MCP server: Trae MCP Monitor

2. Quick start (container)

docker build -t egordian-aeo-mcp .
docker run --rm -p 8080:8080 \
  -e DEPLOYMENT_ENV=production \
  -e AUTH_MODE=bearer \
  -e 'SERVICE_TOKENS=REPLACE_TOKEN|trevor|P-1|catalogue:read,egordian:read,aeo:run,aeo:approve' \
  egordian-aeo-mcp

The service listens on 0.0.0.0:$PORT (default 8080). Nothing else needs to be run locally — clients connect to the deployed URL.

Local development only:

pip install -r requirements-dev.txt
AUTH_MODE=dev DEPLOYMENT_ENV=preview uvicorn app.main:app --host 0.0.0.0 --port 8080
python -m pytest

AUTH_MODE=dev is refused whenever DEPLOYMENT_ENV=production.


3. Remote MCP client configuration

The service is a plain HTTP MCP endpoint: URL + bearer token. There is no local process, no stdio bridge, and no session to keep alive.

Claude (remote/custom MCP connector):

{
  "mcpServers": {
    "egordian-aeo": {
      "type": "http",
      "url": "https://YOUR-HOST/mcp",
      "headers": { "Authorization": "Bearer YOUR_TOKEN" }
    }
  }
}

Perplexity (remote MCP connector): add a custom connector with Server URL = https://YOUR-HOST/mcp and header Authorization: Bearer YOUR_TOKEN.

Any other 2026-07-28 client: point it at https://YOUR-HOST/mcp with the bearer header. The client must send MCP-Protocol-Version: 2026-07-28, Mcp-Method, and — for tools/call / resources/read — a matching Mcp-Name.

Raw call:

curl -sS https://YOUR-HOST/mcp \
  -H "Authorization: Bearer $TOKEN" \
  -H "MCP-Protocol-Version: 2026-07-28" \
  -H "Mcp-Method: server/discover" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"server/discover",
       "params":{"_meta":{"clientInfo":{"name":"curl","version":"8"}}}}'

Legacy stateless 2025 clients are refused unless ALLOW_LEGACY_2025_CLIENTS=true; even then they run on an isolated path that still creates no session and still rejects initialize.

First calls

  1. tools/call handle_create {"project_ids":["P-1"]} → opaque, user- and project-bound, expiring handle.

  2. Pass handle + project_id on every subsequent tool call.

  3. Any side-effecting tool additionally needs actor, approval {approved, actor, rationale, approved_at}, and idempotency_key.


4. Deployment notes

Perplexity private deploy (preview)

Two pieces:

  1. Backend — run the service in the sandbox on port 8080, bound to 0.0.0.0: uvicorn app.main:app --host 0.0.0.0 --port 8080. Set AUTH_MODE=bearer and SERVICE_TOKENS. The SQLite overlay (data.db) plus the bundled read-only catalogue are sufficient. Treat this as preview: the container filesystem is not durable.

  2. Static console bundlepython scripts/build_static_console.py writes dist/public/ (index.html, docs.html, assets/, robots.txt). Deploy that directory. Every backend call in the bundle is built from the __PORT_8080__ placeholder, which the deploy proxy rewrites to port/8080, so the console reaches the sandbox backend through the same authenticated proxy as the static assets.

The bundle is read-only (GET-only, endpoint allowlist), uses no cookies or web storage, uses only relative links so it works from any /computer/a/<id>/ iframe path, and ships no service data, credentials, or catalogue content. scripts/preview_proxy.py emulates the deploy proxy locally for QA; it is not part of the bundle.

MCP clients still talk to the backend URL (https://…/mcp), not to the static bundle.

Vercel — limitations

Vercel's serverless functions are ephemeral and read-only apart from /tmp, cap the deployment bundle well below the 115 MiB catalogue, and recycle instances between requests. That is incompatible with the bundled immutable catalogue and with a SQLite overlay. If you must use Vercel: set DATABASE_URL to an external PostgreSQL instance and host the catalogue-backed tools elsewhere, or accept catalogue tools reporting unavailable. Vercel is not a supported target for the full service.

Registry-safe variant (no SSH upload) — Dockerfile.registry

When the catalogue cannot be uploaded to the host over SSH and the image must travel through a container registry, use the registry variant: no layer ever contains the plaintext catalogue. The catalogue is sealed with AES-256-GCM before the build; the plaintext exists only inside a private volume at run time, and only when CATALOGUE_DECRYPTION_KEY and the pinned CATALOGUE_SHA256 are both supplied.

 export CATALOGUE_DECRYPTION_KEY='<long passphrase>'   # leading space: no shell history
./scripts/seal_catalogue.sh          # -> build/catalogue.enc + <=60 MiB parts + manifest

# A) registry path
DOCKER_BUILDKIT=1 docker build -f Dockerfile.registry -t "$EGORDIAN_IMAGE" .
docker push "$EGORDIAN_IMAGE"
docker compose -f docker-compose.hostinger.yml --env-file ./egordian.env up -d

# B) public-source path - no registry credentials
./scripts/prepare_public_build_repo.sh /tmp/egordian-aeo-runtime   # stages + leak-scans
docker compose -f docker-compose.hostinger-build.yml --env-file ./egordian.env up -d --build

The sealed container is above GitHub's 100 MiB single-file limit, so the public repo carries deterministic ciphertext parts plus a hash manifest; a throwaway builder stage verifies and reassembles them inside the image.

Full procedure, container format, fail-closed behaviour and the OpenSSL fallback (with its limitations) are in docs/REGISTRY_DEPLOY.md.

  1. Compute: Hostinger VPS (or any container host) running the image above with a persistent volume for /app/data.

  2. Control plane: Supabase PostgreSQL. Set DATABASE_URL=postgresql://... and the service switches to PostgresRepository automatically; migrations run at start-up. No local filesystem is then required for state.

  3. Catalogue: ship the licensed SQLite catalogue inside the image (private registry) or mount it read-only. It is never served, never listed, never downloadable.

  4. TLS + secrets: terminate TLS at the reverse proxy; inject SERVICE_TOKENS and any eGordian credentials from the platform's secret store.

  5. Scaling: run N replicas behind a plain round-robin load balancer. No session affinity is needed or supported.


5. Configuration

See env.example for the full list (names only, never values). Key variables:

Variable

Purpose

SERVICE_TOKENS

token|user|projects|scopes entries separated by ;

DATABASE_URL

Set → PostgreSQL control plane; unset → SQLite data.db

CATALOGUE_DB_PATH

Immutable licensed catalogue (read-only, immutable=1)

EGORDIAN_AUTH_PROVIDER

none | basic | bearer | headers

ALLOW_EGORDIAN_WRITES

Default false; writes also need scope + approval envelope

ALLOW_ADMIN_OPERATIONS

Default false; gates HealthCheck/RefreshCache

No eGordian credentials are currently available for jocservice.egordian.com. With EGORDIAN_AUTH_PROVIDER=none the service runs normally: status shows disconnected and eGordian tools return an actionable credential_required error instead of crashing.


6. Scopes

catalogue:read, egordian:read, egordian:write, aeo:run, aeo:approve, admin (admin implies all). Writes require both the scope and the approval envelope.


7. Deliberate refusals

Refusal

Why

human_gate_required on every documented DELETE

Destructive; a named human acts directly in eGordian

admin_operation_disabled on GET api/HealthCheck/RefreshCache

Mutates remote cache state

capability_not_documented on price-proposal draft/submit

The Help page documents only GET v1/Owners/{ownerId}/PriceProposals; no write route exists to call, and this service never invents one. AEO stage 8 therefore stays assisted and emits a submission packet for a human to file

Licensing firewall

No download, dump, bulk browse or export of catalogue content; result counts capped; cost fields redacted without authorization; data/ is never served


8. AEO pipeline

#

Stage

Tier

0

Detect & authenticate

T1

1

Assemble the assignment dossier

T1

2

Build the knowledge base (input gates)

T2

3

Quantify takeoff with N-run consensus

T2

4

Crosswalk scope → CTC catalogue

T1

5

Assemble the price proposal (known-target reconciliation, never estimate-from-scratch)

T1

6

Self-check & confidence tier

T1

7

Human gate (mandatory before any dollar commitment)

T3

8

Submit & log (capability-blocked, assisted only)

T1

Code owns every transition. Stages 2–4 may propose candidates but can never invent a catalogue line, a quantity, or a price. Every run emits a manifest with version hashes, evidence spans, exceptions and the gate decision — no manifest, no deliverable.


9. Project layout

app/
  main.py               FastAPI app: /mcp, REST, console, OpenAPI
  config.py             environment-only settings
  security.py           auth, RBAC, approval envelopes, rate limits, redaction
  catalogue_gateway.py  licensing firewall over the sealed catalogue
  mcp/                  protocol.py · dispatcher.py · tools.py · resources.py
  egordian/             registry.py · client.py · auth.py
  aeo/                  machine.py · runner.py
  repo/                 base.py · sqlite_repo.py · postgres_repo.py · migrations.py
gordian_ctc/            the existing deterministic engine (imported unchanged)
fixtures/               fetched eGordian Help page snapshot (registry source of truth)
console/                operator console + API documentation page
data/catalogue.sqlite   licensed, immutable, never served
tests/                  144 tests

10. Licensing

Private and contract-bound. The catalogue is supplied for an authorized owner/contract and must never be exported, published, mined, or placed in a model prompt. See vendor_docs/licensing-boundaries.md.

F
license - not found
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • A paid remote MCP for Statewright, built to return verdicts, receipts, usage logs, and audit-ready J

  • A paid remote MCP for CodeG, built to return verdicts, receipts, usage logs, and audit-ready JSON.

  • A paid remote MCP for Unity-MCP, built to return verdicts, receipts, usage logs, and audit-ready JSO

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ntxptrevor/egordian-aeo-runtime'

If you have feedback or need assistance with the MCP directory API, please join our Discord server