Skip to content

Feature: reusable OAuth/MCP agent connector with GroundControl deployment tools #81

Open

Description

@teckedd-code2save

Context

RentAWeekend Surface work is currently blocked operationally when the operator is away from a desktop/VPS terminal. GroundControl already owns the VPS/deployment boundary, so expose a narrow, authenticated remote MCP surface that ChatGPT and other MCP clients can connect to using the familiar provider login/consent flow.

This is an enabling layer for RentAWeekend first, not a product-direction change.

Goal

Build a portable agent-connect layer that can later be extracted/bundled for other Serendepify projects. GroundControl is the reference host and RentAWeekend is the first acceptance workload.

Authentication / authorization

  • Preserve the existing GroundControl gc_token login and requireAuth() flow.
  • OAuth Authorization Code + S256 PKCE for delegated clients.
  • Short-lived access tokens, rotating refresh tokens, revocation.
  • RFC 8414 authorization-server metadata.
  • RFC 9728 protected-resource metadata and WWW-Authenticate challenge from /mcp.
  • Support current MCP authorization discovery. Prefer Client ID Metadata Documents (CIMD) for the 2026-07-28 profile; keep standards-compatible fallback registration only where needed for client compatibility.
  • Scope grants to the authenticated GroundControl user and continue enforcing GroundControl RBAC on every operation.
  • Never expose VPS credentials, environment secret values, or unrestricted shell access.

First MCP tool surface

Read:

  • deployment.list
  • deployment.inspect
  • deployment.logs
  • deployment.health

Write:

  • deployment.redeploy

All host operations must go through the existing execOnVps() / deployment abstractions per AGENTS.md.

Acceptance test: RentAWeekend

From ChatGPT/custom MCP client:

  1. Connect GroundControl.
  2. Browser opens GroundControl login/consent.
  3. User authenticates and approves requested scopes.
  4. Client can list/inspect RentAWeekend and read its logs/health.
  5. A redeploy request can deploy latest RentAWeekend main through GroundControl without exposing SSH credentials.
  6. Disconnect/revoke removes future MCP access.
  7. Existing GroundControl browser auth and UI continue to work unchanged.

Portability boundary

Keep OAuth grant/token logic, scope definitions, protected-resource discovery, and MCP bearer validation isolated from GroundControl-specific deployment executors so this can later become a package or sidecar.

Activity

  1. teckedd-code2save commented on Sep 18, 2026

    @teckedd-code2save
    OwnerAuthor

    Implementation landed in #87 / main commit 41ea9b1.

    Completed:

    • stateless remote MCP at /mcp
    • OAuth Authorization Code + S256 PKCE
    • protected-resource + authorization-server discovery
    • CIMD client resolution + dynamic-registration fallback
    • hashed short-lived access tokens and rotating refresh tokens
    • deployment/capability-scoped consent and revocation
    • no generic shell/terminal MCP tool
    • deployment.list / inspect / logs / health / redeploy / operation.get
    • idempotent durable AgentOperation handles
    • detached redeploy verification survives agent disconnect/reconnect
    • interrupted non-reconcilable mutations become uncertain instead of being replayed
    • Agents UI for endpoint, connection guidance, grants and revocation

    Production migration and deployment are green.

    Keep this issue open for the remaining acceptance test: connect a real ChatGPT/custom MCP client, complete OAuth consent, inspect/redeploy an approved workload, disconnect/reconnect, and retrieve final operation evidence. Connector reliability is deliberately parked in #86.

  2. teckedd-code2save commented on Sep 18, 2026

    @teckedd-code2save
    OwnerAuthor

    ChatGPT dev-mode acceptance uncovered a real discovery regression after OAuth succeeded: the app showed connected but "No app actions available yet" and Refresh Actions failed.

    Fixed in #89 / main commit 0b8d4aa:

    • public tool catalog discovery independent of bearer invocation auth
    • per-tool OAuth securitySchemes + mirrored _meta.securitySchemes
    • outputSchema for every tool returning structuredContent
    • correct 2026-07-28 server/discover supportedVersions shape
    • legacy initialize compatibility without modern response-header leakage
    • runtime OAuth challenges via WWW-Authenticate + mcp/www_authenticate
    • transport/catalog regression tests
    • production deployment now fails unless public /mcp exposes deployment.list + outputSchema + securitySchemes and modern discovery metadata

    Production deploy passed, including:
    "MCP discovery smoke passed: https://groundcontrol.serendepify.com/mcp"

    Keep #81 open until the already-connected ChatGPT dev app is refreshed/reconnected and successfully invokes deployment.list/health and a durable redeploy end-to-end.

  3. teckedd-code2save commented on Sep 18, 2026

    @teckedd-code2save
    OwnerAuthor

    Live ChatGPT usage exposed another useful read-side gap: the agent could verify managed environment activity but could not answer whether a named key such as PAYSTACK_SECRET_KEY was configured without secret access.

    Implemented in #90 / main commit ed79aca:

    • new deployment.config.check MCP action
    • exact-key lookup only (1–25 keys), no bulk environment-name inventory
    • reuses deployment:read, so existing grants do not need wider authorization
    • returns configured / missing / unmanaged
    • returns component, required flag, and env profile/provider/status metadata
    • never returns a value, masked value, prefix/suffix, or secret fragment
    • supports shared and component-scoped managed envs
    • distinguishes unmanaged from missing to avoid false negatives
    • regression tests prove source secret strings cannot appear in the result
    • production MCP smoke now requires deployment.config.check to be discoverable

    Production deploy passed and MCP discovery smoke passed.

  4. teckedd-code2save commented on Sep 18, 2026

    @teckedd-code2save
    OwnerAuthor

    Live ChatGPT acceptance progressed further on 2026-09-18.

    Confirmed end-to-end from ChatGPT web:

    • invoked GroundControl deployment.redeploy for rentaweekend
    • received durable operation id: cmu6xvgdl0001wrjixto4lg9f
    • operation completed with status success on first attempt
    • GroundControl verification evidence reported:
      • Compose config validation passed
      • deployment/API/Compose environments reconciled
      • Postgres healthy
      • Redis healthy
      • migration container exited 0
      • API healthy
      • web container started successfully
      • expected/running images matched
      • public endpoint returned HTTP 200
      • release verification passed
    • verified runtime image tags were returned for API and web
    • ChatGPT correctly surfaced the distinction between source Git fingerprint and effective deployed image revision instead of conflating them

    This proves the write/durable-operation path from ChatGPT -> OAuth -> MCP -> AgentOperation -> deployment execution -> verification -> structured evidence.

    Remaining acceptance items before closing #81:

    1. reconnect/recovery: from a separate/new ChatGPT conversation, query operation cmu6xvgdl0001wrjixto4lg9f and retrieve its final evidence
    2. scope boundary: prove an ungranted deployment cannot be read or mutated
    3. optionally verify deployment.config.check live for named-key presence without returning secret values
  5. teckedd-code2save commented on Sep 20, 2026

    @teckedd-code2save
    OwnerAuthor

    2026-09-20 long-lived ChatGPT access regression found and fixed in #102 / main commit 92a1ac5.

    Symptom:

    • Ground Control worked after initial OAuth, then hours later ChatGPT reported it no longer had access.

    Root cause:

    • GC access tokens expire after 1 hour.
    • The refresh-token endpoint required client_id on every refresh request.
    • That is too strict for public PKCE clients using token_endpoint_auth_method=none; OAuth refresh requests need refresh_token, while client authentication/identity requirements depend on client type.
    • A missing client_id was rejected before the stored refresh token was rotated/revoked.

    Fix:

    • refresh_token remains required
    • client_id may be omitted only for registered public clients with tokenEndpointAuthMethod=none
    • if client_id is supplied, it must match exactly
    • clients requiring token-endpoint authentication still cannot omit identity
    • regression tests cover all three cases

    Production deploy passed:

    • container healthy
    • public login smoke passed
    • public MCP discovery smoke passed

    Existing ChatGPT refresh credentials should remain usable unless the client already discarded them and requires one reconnect.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions