# Authentication

Your agent acts as you, with your permission. It signs in through OAuth: you approve the connection once in your browser, and your client keeps it working after that.

## How sign-in works

1. Your client calls `https://mcp.meetgravity.ai/api/mcp` without a token and gets `401 Unauthorized`. The response's `WWW-Authenticate` header points to Gravity's protected resource metadata.
2. The client reads that metadata and finds Gravity's authorization server. Gravity supports hosted Client ID Metadata Documents (CIMD), and retains dynamic registration for clients that use it.
3. Your browser opens Gravity's consent screen, signed in as you. You choose **Allow access**.
4. The client receives tokens and sends `Authorization: Bearer <access token>` with every request.

MCP clients that support authorization do all of this for you.

## Endpoints

| Document | URL |
| --- | --- |
| Protected resource metadata (RFC 9728) | `https://mcp.meetgravity.ai/.well-known/oauth-protected-resource/api/mcp` |
| Authorization server metadata (RFC 8414) | `https://mcp.meetgravity.ai/.well-known/oauth-authorization-server/api/oauth` |

| Property | Value |
| --- | --- |
| Issuer | `https://mcp.meetgravity.ai/api/oauth` |
| Resource (token audience) | `https://mcp.meetgravity.ai/api/mcp` |
| Grant types | `authorization_code`, `refresh_token` |
| PKCE | Required, `S256` only |
| `resource` parameter | Required when authorizing, and must equal the resource above |
| Client registration | CIMD with the MCP metadata profile, or dynamic client registration. Native clients use loopback redirect URIs on `localhost`, `127.0.0.1`, or `[::1]`. |
| CIMD client authentication | Public clients use `none` with PKCE. Clients with signing keys may use `private_key_jwt`. |
| Access tokens | Opaque bearer tokens, valid for 1 hour, sent in the `Authorization` header |
| Refresh tokens | Issued with `offline_access`, valid until 90 days without use |
| `prompt` and `max_age` | `prompt=consent` is supported. `prompt=login`, `prompt=create`, `prompt=select_account`, and `max_age` aren't, because the connection uses your existing browser sign-in. |

## Permissions

| Scope | Requested by default | What it allows |
| --- | --- | --- |
| `gravity:read` | Yes | Read your accessible groups, rosters, goals, private goal details, saved matches, and your own Rolodex identities, profile sections, and private person notes through Gravity's privacy rules. |
| `gravity:edit` | Yes | Save, change, share, or end goals, start matching searches, and add, correct, or forget your private person notes when you request them. |
| `offline_access` | Yes | Obtain refresh tokens to stay connected between sessions. |
| `openid`, `profile`, `email` | Client choice | Read your name, email address, and profile photo from the userinfo endpoint, so the client can show which Gravity account is connected. |

The normal connection requests read and edit together up front. Edit includes read, because every change starts from current state, so a request for `gravity:edit` alone grants both. A client can deliberately request read-only access; Gravity accepts it and rejects writes before any changes or searches. Consent describes only the permissions requested. Routine features within these capabilities do not require another consent prompt. Permission to edit still requires your request for an individual change, and your client's approval policy also applies.

Every MCP challenge and the protected-resource metadata ask for `offline_access` alongside the resource scopes, so connections persist. The OpenID scopes appear in authorization-server metadata, which then also names the `userinfo_endpoint`. A request without an explicit scope uses the registered client's default scopes, which include read, edit, offline access, and the OpenID scopes for new clients.

Read permission includes finding people in your own Rolodex and reading their private notes. Edit permission includes adding, correcting, and explicitly forgetting those notes when you ask. No permission lets an agent browse another member’s Rolodex, see contact details for people found through other members, or contact anyone.

## The consent screen

The consent screen shows the app asking for access and the Gravity account you're signed in as, then lists what the connection allows. Choose **Allow access** to connect, or **Cancel** to refuse.

Only approve a connection you started yourself. The app supplies its name and optional HTTPS logo. Gravity uses a generic icon when the logo is missing or unusable. CIMD connections show their metadata domain; other connections may show a website labeled as supplied by the app. A name, logo, or supplied website is not a verification badge.

## Staying connected

Access tokens last an hour. Your client refreshes them automatically, so you won't notice. If the refresh fails, for example after 90 days without use, your client asks you to sign in again.

Each request is checked on its own. Gravity confirms the token is active, was issued by Gravity for this server, and belongs to a current member. Tool calls require their read or edit permission. Your agent always acts as the account that approved it. Tool arguments can't change that.

If a connection can no longer refresh or sign in, remove Gravity from your MCP client and add it again. This lets the client register or discover its identity again and request the current permissions.

## Disconnecting

In Gravity, open **Profile → Connectors** to see your connected agents and their permissions. Choose **Disconnect** to end an agent's access, including its refresh tokens. It will need your consent again to reconnect. Gmail and LinkedIn stay under **Sources**.

- **Claude Code:** `claude mcp logout gravity` signs out. `claude mcp remove gravity` removes the server and its sign-in.
- **Codex and other clients:** remove the server or sign out in the client.

Signing out in a client deletes its tokens on that device; use Gravity's **Disconnect** control to end the server-side grant. Gravity's revocation endpoint, `https://mcp.meetgravity.ai/api/oauth/oauth2/revoke`, also revokes a token when the client calls it. A token that wasn't revoked stops working when it expires.

Leaving Gravity ends every agent's access immediately.

## Errors

| Response | Meaning | What to do |
| --- | --- | --- |
| `401` with `WWW-Authenticate` | No token, or the token expired or was revoked | Let your client refresh, or sign in again. |
| `403` with `error="insufficient_scope"` | The token lacks permission for this operation | Continue reading with a read-only connection, or authorize the scopes in the challenge if you want the additional capability. |
| `429` with `Retry-After` | The connection exceeded the member's read or write limit | Wait for the indicated delay before retrying. |
| `503` | Gravity's sign-in service is temporarily unavailable | Try again later. |
