Oppy MCP — Connect Guide (canonical)

· · 8 min read
View as Markdown Open in ChatGPT Open in Claude

Oppy MCP — Connect Guide

Endpoint: https://mcp.oppy.pro/mcp · Transport: Streamable HTTP (JSON-RPC 2.0) Canonical connect documentation. Verified against production August 19, 2026.

Oppy is an AI workforce platform: AI teammates (“oppies”) that qualify leads, book appointments, answer questions, and keep the record straight across SMS, email, voice, and chat. This one endpoint reaches all of it — contacts, conversations, calendars, boards, notes, files, knowledge bases, the oppies themselves, and the audit stream.

Works with any MCP-compatible client, from any vendor. Nothing here is specific to one model or lab.

Reading this as an agent? Fetch the machine-readable version: curl -H "Accept: text/markdown" https://mcp.oppy.pro/


Quick reference

   
MCP endpoint https://mcp.oppy.pro/mcp
Transport Streamable HTTP, JSON-RPC 2.0. Stateless — every request is independent
Auth OAuth 2.1 (recommended) or Authorization: Bearer <api-key>
Protected-resource metadata https://mcp.oppy.pro/.well-known/oauth-protected-resource/mcp
Authorization server https://io.oppy.pro
Scope mcp
Health https://mcp.oppy.pro/health
Docs (human) https://mcp.oppy.pro
Data boundary Everything is scoped to your Oppy account. You only ever see your own data

Choose an auth method

Sign in with OAuth if a person is connecting their own client. Nothing secret to paste or store, every action attributed to that person in their own audit trail, revocable any time, and access expires on its own so a forgotten client doesn’t stay connected. Needs a browser once, at setup.

Use a bearer API key if no human is present. Right for scheduled jobs, CI, and always-on agents: no browser, stable across restarts, works anywhere a static header is accepted. The cost is a long-lived secret you store and rotate yourself, shared by everyone using it — so actions attribute to the key, not a person.


1. OAuth 2.1

Most clients handle this end to end once you give them the URL. Add https://mcp.oppy.pro/mcp as a remote/HTTP MCP server, sign in to Oppy when the browser opens, approve, done.

The flow, for clients implementing it directly

  1. Probe. POST https://mcp.oppy.pro/mcp without credentials → 401 with a WWW-Authenticate header pointing at the protected-resource metadata URL. An unauthenticated tools/call also returns _meta["mcp/www_authenticate"].
  2. Discover the resource. GET https://mcp.oppy.pro/.well-known/oauth-protected-resource/mcp (RFC 9728; the root path /.well-known/oauth-protected-resource also resolves). It returns the authorization_servers array — follow it rather than hardcoding endpoints.
  3. Discover the authorization server. GET https://io.oppy.pro/.well-known/oauth-authorization-server.
  4. Register. Dynamic client registration is open — POST to the registration_endpoint. No pre-provisioning, no manual client ID request.
  5. Authorize + exchange. Authorization code with PKCE S256. Pass resource=https://mcp.oppy.pro/mcp through both the authorization request and the token request. Request scope mcp.
  6. Call. Send the access token as Authorization: Bearer <access_token>. Refresh with the refresh_token grant.

Endpoints

As advertised by the authorization server at the time of writing. Discover them; don’t hardcode.

Endpoint URL
Issuer https://io.oppy.pro
Authorization https://io.oppy.pro/api/oauth/authorize
Token https://io.oppy.pro/api/oauth/token
Dynamic client registration https://io.oppy.pro/api/oauth/register
Revocation https://io.oppy.pro/api/oauth/revoke

Supported: authorization_code and refresh_token grants · code response type · PKCE S256 · client auth none, client_secret_basic, client_secret_post. Public clients should use none + PKCE.

The access token is an Oppy-issued token bound to the user, account, scope, and MCP resource. Internal API keys are never exposed as OAuth tokens.


2. Bearer API key

  1. Create a key in your Oppy account settings → API keys.
  2. Point the client at https://mcp.oppy.pro/mcp.
  3. Send Authorization: Bearer <your-key> on every request.

Store it in a secret manager, never in a repo, and rotate it on a schedule you decide. A key carries account-admin reach — treat it like a password. Delete it to revoke; there is no automatic expiry.

curl -sS https://mcp.oppy.pro/mcp \
  -H "Authorization: Bearer $OPPY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

Client setup

ChatGPT / Codex

Add Oppy from the plugins directory, or add https://mcp.oppy.pro/mcp as a remote MCP server. Sign in when prompted.

Claude — desktop, web, and mobile

Settings → Connectors → Add custom connector → https://mcp.oppy.pro/mcp. Sign in when the browser opens.

Claude Code

{
  "mcpServers": {
    "oppy": {
      "type": "http",
      "url": "https://mcp.oppy.pro/mcp",
      "headers": { "Authorization": "Bearer ${OPPY_API_KEY}" }
    }
  }
}

Export OPPY_API_KEY in your shell profile. Verify with claude mcp list, then try a manage_boards list call. Or use claude mcp add --transport http oppy https://mcp.oppy.pro/mcp and let OAuth handle it.

Cursor

Add to .cursor/mcp.json using the same shape as Claude Code, or add it as an HTTP server in settings and sign in with OAuth.

Anything else

Any client that speaks streamable-HTTP MCP works. Give it the URL and either auth method.


What the tools look like

21 tools, dispatched by action. Rather than one tool per operation, each tool covers a domain and takes an actionmanage_contacts with action: "list", action: "create", action: "bulk_update", and so on. Read the tool’s own description for its action list; several also expose action: "help" with a topic parameter for in-context reference.

Domain Tool
People manage_contacts
Threads manage_conversations
Scheduling manage_calendar (events, analytics), manage_calendars (connections, assignment)
Pipeline manage_boards (boards → lists → items)
Written record manage_notes, manage_notes_bulk, note_audio
Files manage_files
Knowledge manage_indexes
The workforce manage_channels (the oppies), manage_loops (standing instructions on a schedule)
Agent-to-agent list_teammates, ask_oppy
Automation manage_automation_rules
Audit manage_events, manage_feedbacks
Web manage_browser, extract_data
Generation generate_content
Telephony manage_twilio

Conventions worth knowing before your first call

Start broad when searching. Every list action defaults to something useful. Call with no filters first, then narrow. Thin results almost always mean over-filtering, not empty data.

Control the payload. Most list actions accept fields (comma-separated include list, or *), exclude_fields, truncate (e.g. {description: 100}), and count_only. On large lists, narrow fields — it’s the difference between a clean response and one truncated mid-JSON.

Paginate per tool. Most use page / per_page. manage_conversations uses limit plus a latest_message_before cursor — there is no page or offset. manage_events timeline uses before_timestamp.

Read before you write. List responses may be partial: manage_boards item_list marks any shortened field in a truncated_fields array. Never write a list record straight back — item_get the full record first, or you’ll silently truncate the stored value.

Confirmation gates are deliberate. manage_channels create and archive require confirmed_by_user: true. Board and list deletes require confirm_delete. Hard-deleting notes requires confirm. Bulk contact writes accept dry_run: true for a preview. A prompt-shrink guard rejects system-message edits that cut more than 70% unless you pass force. These exist because a wrong call here is expensive — don’t route around them, use them.

A record you can’t reach reads as “not found.” Scoping is enforced before existence. If something seems missing, check scope and permissions before reporting a bug — and read the help topic where one exists.

Run browser sessions one at a time. manage_browser is for sequential automation. Lead with DOM extraction (snapshot, evaluate, content, locator_text) rather than screenshots — it’s faster, cheaper, and more reliable. Screenshot only for final visual verification or when there’s no DOM to read.


Permissions, attribution, and safety

An agent gets exactly the access its authenticating user has. Connecting through an assistant never widens permissions.

Every action is attributed. It lands in the same shared timeline as work done by hand, so delegating doesn’t lose the thread of who did what. manage_events reads that stream back.

Some tools act in the real world. Three are read-only: list_teammates, manage_twilio, extract_data. The rest write. Several reach outside Oppy entirely:

  • manage_conversations sends real SMS, email, and voice to real people. A sent message cannot be unsent.
  • manage_calendar emails external attendees and can delete meetings off their calendars.
  • manage_loops fires unattended on a schedule with full tool access, including outbound comms.
  • manage_automation_rules posts to third-party webhooks and can trigger outbound conversations.
  • manage_browser drives a real browser and can submit forms on third-party sites.
  • manage_notes, manage_notes_bulk, and manage_indexes can publish pages to the open internet via visibility: public or public_unlisted.

Tool annotations (readOnlyHint, openWorldHint, destructiveHint) reflect this honestly, so a well-behaved client can gate on them.

Two habits that prevent nearly every bad outcome:

  1. Draft first when the audience is a person. For outbound messaging, produce the draft and hold it for review rather than sending on a single instruction.
  2. Count before you act in bulk. count or dry_run first, show the number, then proceed. “Delete everyone who hasn’t replied” deserves a number and a confirmation, not a mutation.

Troubleshooting

401 from /mcp. Expected before auth. The response carries WWW-Authenticate pointing at the protected-resource metadata. With a bearer key, a 401 means the key is missing, revoked, or malformed.

403. The credential authenticated but its scope is too narrow for that action.

“No authorization support detected.” Confirm these return 200 without credentials: https://mcp.oppy.pro/.well-known/oauth-protected-resource, .../oauth-protected-resource/mcp, and https://io.oppy.pro/.well-known/oauth-authorization-server.

Sign-in succeeds but tool calls fail. Check the token carries scope mcp, that its bound resource matches https://mcp.oppy.pro/mcp exactly, and that the client completed the PKCE exchange.

Empty results. Usually the credential is keyed to a different account than expected, or the filters are too narrow. List with no filters to confirm.

A tool returns a 404-shaped error for every action. That feature may be disabled on this backend. Read the error message — it says so — before assuming a bug.

OAuth loop in a desktop client. Clear the client’s stored MCP credentials and reconnect.


Known gap: enterprise workspace domain restrictions

The authorization server does not yet advertise openid or email scopes and publishes no UserInfo endpoint. Platforms that gate connections on a verified email domain — including ChatGPT workspace domain restrictions — require both. Until that ships, enterprise admins cannot restrict Oppy to their own domain.


Change history

  • 2026-08-19 — Rewritten as the canonical connect guide. Corrected the stale banner claiming OAuth was non-functional (it shipped 2026-08-06). Documented the authorization server at https://io.oppy.pro, the full OAuth flow, bearer auth, per-client setup, the 21-tool inventory, the API conventions agents need, and honest safety guidance. Absorbed the former separate developer-setup note.
  • 2026-07-23 — Marked OAuth non-functional pending auth consolidation.
  • 2026-04-22 — Initial guide.