Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions deployment/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Remote MCP production release

This spec belongs only to `oilpriceapi-openai-mcp`, DigitalOcean app
`4a89cb34-72a3-4daf-a893-0628699ded26`. The API and frontend use other apps.

Build `Dockerfile.remote` for `linux/amd64` from the reviewed source commit in
the spec header. Label the image with `org.opencontainers.image.revision`, run
the remote container smoke with an isolated PostgreSQL database, push it to
the existing DO registry, and pin its immutable digest. Never deploy a mutable
tag or infer the served source from a successful build alone.

The dedicated `mcp_oauth` database and role on `opa-2025-nyc-db` are prepared.
The role owns only the new database; public connection/temp privileges on that
database and public schema-creation privileges were revoked. The application
creates its own table and indexes at startup. Its pool is capped at four
connections per instance. Keep `instance_count: 1` unless capacity is reviewed.

Before applying the spec, inject `OILPRICEAPI_MCP_OAUTH_ENCRYPTION_KEY` as a
`RUN_TIME` environment variable of type `SECRET`. The 32-byte key is backed up
in the Mac login Keychain under service `OilPriceAPI MCP production OAuth`,
account `OILPRICEAPI_MCP_OAUTH_ENCRYPTION_KEY`; retrieve it through Keychain
Access or the macOS keyring API. Verify readback without printing the key.
Keep it out of command arguments, logs, source control and release receipts.
Retain the same key on later deployments: changing or losing it invalidates
stored connections. The versioned YAML deliberately contains no key value
and is not a complete deploy command on its own.

Reconcile against the current live app spec before every update. Preserve its
domain, allowed origins, ingress, capacity and unrelated environment settings.
The database binding supplies its URL and CA certificate. Confirm the MCP app
is a trusted source on the database cluster. A production boot without the
database/key fails closed; a bound CA is verified with hostname validation.

After activation, verify the serving deployment's image digest, `/health`
version `0.1.2`, and `oauth_store_ready` with `durable: true`. Run anonymous and
authenticated tool smokes, then obtain a synthetic OAuth grant, deploy the
same image once, and prove refresh and authenticated retrieval still work.
Reject refresh-token replay and verify revocation rejects both old and new
access tokens. Review runtime logs for new errors and failed requests.

The initial transition from the old in-memory store requires existing users
to reconnect once. Later restarts retain the registered client and unexpired
encrypted tokens. Rollback pins the prior digest recorded in the spec header,
preserving the database and key. Rolling back to the old memory-only image
also requires reconnecting; never drop the database as part of rollback.
91 changes: 60 additions & 31 deletions deployment/openai-mcp.yaml
Original file line number Diff line number Diff line change
@@ -1,34 +1,63 @@
# Isolated App Platform app. APP_URL binds its stable HTTPS ingress.
# Reviewed source 55e7da9856c78de0c4298080600534088e8ba92d, immutable DOCR image.
# Do not apply this spec to either existing API/frontend app.
# Isolated MCP app; do not apply to the API or frontend app.
# Reviewed image source: d11cd0315987e9831381a7fe74f5b67d0cb943f8
# Rollback image: sha256:2755490dc9798fc318819313c16fa20c429534102e081ddd6eeccc7073d505dd
# Inject OILPRICEAPI_MCP_OAUTH_ENCRYPTION_KEY as a RUN_TIME SECRET
# from the password manager before applying. Never commit its value.
# See deployment/README.md for the complete rollout and rollback.
name: oilpriceapi-openai-mcp
region: nyc
services:
- name: energy-mcp
image:
registry_type: DOCR
registry: opa-2025-registry
repository: oilpriceapi-openai-mcp
digest: sha256:02b58d701a7f4cd74def1f8045c6529afb9f0cb616429e57ec78b5e3b000b1e2
http_port: 8080
instance_count: 1
instance_size_slug: apps-s-1vcpu-1gb
envs:
- key: OILPRICEAPI_MCP_PUBLIC_URL
scope: RUN_TIME
value: ${APP_URL}
- key: NODE_ENV
scope: RUN_TIME
value: production
health_check:
http_path: /health
initial_delay_seconds: 10
period_seconds: 15
timeout_seconds: 5
success_threshold: 1
failure_threshold: 3
routes:
- path: /
- name: energy-mcp
image:
registry_type: DOCR
registry: opa-2025-registry
repository: oilpriceapi-openai-mcp
digest: sha256:003efbb235c11502c037ab0e1034a045e4498c4403bd13f8cc2fb28c46d74137
envs:
- key: OILPRICEAPI_MCP_PUBLIC_URL
value: ${APP_URL}
scope: RUN_TIME
- key: NODE_ENV
value: production
scope: RUN_TIME
- key: MCP_ALLOWED_ORIGINS
value: https://claude.ai,https://claude.com
scope: RUN_TIME
- key: OILPRICEAPI_MCP_OAUTH_DATABASE_URL
scope: RUN_TIME
value: ${mcp-oauth-db.DATABASE_URL}
- key: OILPRICEAPI_MCP_OAUTH_DATABASE_CA_CERT
scope: RUN_TIME
value: ${mcp-oauth-db.CA_CERT}
instance_size_slug: apps-s-1vcpu-1gb
instance_count: 1
http_port: 8080
health_check:
initial_delay_seconds: 10
period_seconds: 15
timeout_seconds: 5
success_threshold: 1
failure_threshold: 3
http_path: /health
domains:
- domain: mcp.oilpriceapi.com
type: PRIMARY
region: nyc
alerts:
- rule: DEPLOYMENT_FAILED
- rule: DOMAIN_FAILED
- rule: DEPLOYMENT_FAILED
- rule: DOMAIN_FAILED
ingress:
rules:
- match:
path:
prefix: /
component:
name: energy-mcp
features:
- buildpack-stack=ubuntu-22
databases:
- name: mcp-oauth-db
engine: PG
production: true
cluster_name: opa-2025-nyc-db
db_name: mcp_oauth
db_user: mcp_oauth
Loading