Skip to content

Connect UI and OAuth Setup

Local MCP setup

Connect clients to one memory endpoint

The Connect UI is the user-facing setup page for MCP clients. It shows the active MCP URL and renders copyable URL-only client setup snippets with a style that matches the app page.

Open local Connect UI

Open it at:

http://127.0.0.1:8000/connect

Responsibility Boundary

Connect page

The MCP client owns sign-in, reauthentication, and token storage: - `/connect` renders setup and client snippets. - `/connect` does not ask humans to sign in or copy bearer tokens. - Clients should use the MCP OAuth metadata and authorization flow when auth is needed.

Protected resource

The MCP endpoint remains a protected resource server: - `/mcp` and `/memory/*` require a valid bearer token when `api.auth: oauth_resource_server` is enabled. - The hub exposes protected-resource metadata and OAuth authorization-server metadata for compliant MCP clients. - Client account switching is client-driven: use the client's reauth/logout flow rather than copying tokens from `/connect`.

Access Modes

/connect renders whenever api.connect.enabled: true. The page adapts to the configured auth mode:

No auth Local

api.auth: none

Shows the endpoint and client snippets for loopback or trusted-network development. Do not expose this mode to untrusted networks.

Bearer/API key Protected

api.auth: bearer_token

Shows setup guidance without rendering configured secrets. Client header support still needs client-specific verification.

OAuth resource server Preferred

api.auth: oauth_resource_server

Shows OAuth-oriented setup. MCP clients own sign-in, reauthentication, and token storage.

Safe Diagnostics

The Connect UI includes a small diagnostics panel for setup and support. It is intentionally allowlisted:

  • Service readiness mode.
  • Metadata, vector, and embedding provider names.
  • Vector fallback state.
  • Structured logging state.
  • OpenTelemetry tracing and metrics state.
  • Whether an OTLP endpoint is in use.

The panel does not render memory counts, user identities, bearer tokens, API keys, DSNs, raw queries, embeddings, or request payloads.

Supported Providers

Current live support is Google OpenID Connect.

The config model also includes meta and x provider slots so the Connect UI can stay provider-shaped, but those providers are not marked supported until their provider-specific authorization, callback, claim mapping, and tests are implemented. Treat them as disabled placeholders.

Provider Status Notes
Google Supported Verifies OIDC ID tokens with JOSE/JWKS through the oauth extra.
Meta Placeholder Config slot exists; keep disabled.
X Placeholder Config slot exists; keep disabled.

Packages

Base installs include the server-rendered UI dependencies:

  • jinja2
  • itsdangerous

Live provider sign-in requires the OAuth extra:

uv sync --extra oauth

For package installs:

pip install "ai-memory-hub[oauth]"

The oauth extra installs HTTPX and JOSE/OIDC token verification dependencies. Without it, the Connect UI can render, but live OAuth sign-in returns a configuration error.

Google Setup

Create an OAuth client in Google Cloud and add the exact redirect URI used by your hub config. For the local example:

http://127.0.0.1:8000/auth/google/callback

Export these secrets before starting the hub:

export GOOGLE_CLIENT_ID="your-google-client-id"
export GOOGLE_CLIENT_SECRET="your-google-client-secret"
export AMH_OAUTH_JWT_SECRET="$(openssl rand -base64 48)"
export AMH_SESSION_SECRET="$(openssl rand -base64 48)"

AMH_OAUTH_JWT_SECRET signs hub-issued MCP bearer tokens returned by the OAuth token endpoint. Keep it stable while you want existing tokens to remain valid.

AMH_SESSION_SECRET signs browser session state. Keep it stable while you want browser sign-in sessions to survive restarts.

Dynamic MCP Clients

MCP clients can dynamically register with the hub's local authorization server. Dynamic registration is intentionally limited to loopback redirect URIs such as http://127.0.0.1:<port>/callback, http://localhost:<port>/callback, or http://[::1]:<port>/callback. Remote hosts, fragments, userinfo, relative paths, and non-HTTP schemes are rejected.

When a browser is already signed in, /oauth/authorize does not silently issue an authorization code for a newly registered dynamic client. The request is parked in the browser session and /connect requires an explicit CSRF-protected approval before redirecting back to the local client.

Registered dynamic clients and unredeemed authorization codes are process-local state. Restarting the hub requires clients to register again, and unredeemed codes expire after a short TTL. The in-memory stores are capped so repeated unauthenticated registrations or abandoned authorization attempts cannot grow without bound.

Run the built-in local authorization server as a single hub process. Startup fails when api.auth: oauth_resource_server is paired with common multi-worker deployment hints such as WEB_CONCURRENCY=2, UVICORN_WORKERS=2, or GUNICORN_WORKERS=2. This keeps the first local MCP setup simple; use a durable/shared authorization component before relying on dynamic client state across multiple hub processes.

Hub Config

User-facing MCP setup should use api.auth: oauth_resource_server, set api.public_base_url, and enable api.connect.

api:
  host: 127.0.0.1
  port: 8000
  auth: oauth_resource_server
  public_base_url: http://127.0.0.1:8000
  oauth:
    authorization_servers:
      - http://127.0.0.1:8000
    resource: http://127.0.0.1:8000/mcp
    jwt_secret_env: AMH_OAUTH_JWT_SECRET
    scopes_supported:
      - memory:read
      - memory:write
  connect:
    enabled: true
    session_secret_env: AMH_SESSION_SECRET
    session_ttl_seconds: 43200
    token_ttl_seconds: 3600
    passport:
      providers: [google]
      google:
        enabled: true
        client_id_env: GOOGLE_CLIENT_ID
        client_secret_env: GOOGLE_CLIENT_SECRET
        callback_url: http://127.0.0.1:8000/auth/google/callback
        allowed_domains: []
        allowed_emails: []
      meta:
        enabled: false
        client_id_env: META_CLIENT_ID
        client_secret_env: META_CLIENT_SECRET
      x:
        enabled: false
        client_id_env: X_CLIENT_ID
        client_secret_env: X_CLIENT_SECRET

allowed_domains and allowed_emails are optional allow-lists for Google identities. Leave both empty for local testing.

Local Run

From the repository root:

uv sync --extra oauth
export GOOGLE_CLIENT_ID="your-google-client-id"
export GOOGLE_CLIENT_SECRET="your-google-client-secret"
export AMH_OAUTH_JWT_SECRET="$(openssl rand -base64 48)"
export AMH_SESSION_SECRET="$(openssl rand -base64 48)"
uv run aim serve --config examples/google-oauth-connect/config.yaml --host 127.0.0.1 --port 8000

Then open:

http://127.0.0.1:8000/connect

Docker Compose

The main Docker example combines Postgres, PGVector, Ollama embeddings, Google OAuth, and a public HTTPS URL for agents running off the local host. It installs the postgres, tokenizer, and oauth extras in the hub image and binds the hub service to loopback:

export GOOGLE_CLIENT_ID="your-google-client-id"
export GOOGLE_CLIENT_SECRET="your-google-client-secret"
export AMH_OAUTH_JWT_SECRET="$(openssl rand -base64 48)"
export AMH_SESSION_SECRET="$(openssl rand -base64 48)"
cd examples/local-stack
export AMH_CONFIG_FILE=config.oauth-ngrok.yaml
docker compose up --build

Open:

https://YOUR-NGROK-DOMAIN.ngrok-free.app/connect

The included template uses ngrok because it is convenient for a local machine. Any stable HTTPS tunnel or reverse proxy works if the public base URL and Google redirect URI match. Before starting Compose, replace every https://YOUR-NGROK-DOMAIN.ngrok-free.app placeholder in examples/local-stack/config.oauth-ngrok.yaml with the active HTTPS URL and register the matching Google redirect URI:

https://YOUR-NGROK-DOMAIN.ngrok-free.app/auth/google/callback

Restart behavior:

  • Identities and web sessions persist while the Compose volume and AMH_SESSION_SECRET stay the same.
  • Hub bearer tokens remain valid until expiry or logout while the metadata volume and AMH_OAUTH_JWT_SECRET stay the same.
  • Changing either secret invalidates the matching session or token class.

Client Verification Matrix

The Connect UI can render snippets before every client has been verified against the current local release. Keep snippets labeled Unverified until the exact command or config has been tested.

Codex Verified

codex mcp add ai-memory-hub-local --url <mcp-url>

Verified for streamable HTTP setup. Use the MCP URL shown by `/connect`.

Copilot CLI Verified

copilot mcp add --transport http ai-memory-hub http://127.0.0.1:8000/mcp

Verified for streamable HTTP setup.

Claude CLI Verified

claude mcp add --transport http ai-memory-hub-local <mcp-url>

Verified for streamable HTTP setup. Use the MCP URL shown by `/connect`.

Gemini CLI Verified

gemini mcp add ai-memory-hub-local <mcp-url> -t http

After adding it, run `/mcp auth` inside Gemini CLI.

OpenCode Verified

opencode mcp add ai-memory-hub-local --url <mcp-url>

After adding it, run `opencode mcp add ai-memory-hub-local auth`.

Pi Verified

pi install npm:pi-mcp-adapter

Install the adapter, export an existing MCP config from Codex or OpenCode, then run `/mcp auth` inside Pi.

Hermes Verified

hermes mcp add ai-memory-hub-local --url <mcp-url> --auth oauth

Verified for OAuth-backed streamable HTTP setup.

OpenClaw Unverified

openclaw mcp add ai-memory-hub-local --url <mcp-url> --transport streamable-http --auth oauth

Then run `openclaw mcp login ai-memory-hub-local`. After approval, run `openclaw mcp login ai-memory-hub-local --code <code>`.

Do not commit OAuth client secrets or captured bearer tokens.