> ## Documentation Index
> Fetch the complete documentation index at: https://openworklabs.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Model Context Protocol (MCP)

> The OpenWork MCP server is a hosted endpoint that lets your AI agent use OpenWork from Claude Code, Codex, Claude Desktop, Cursor, VS Code, or any MCP client.

export const OpenWorkConnectInstaller = () => {
  const MCP_SERVER_URL = "https://api.openworklabs.com/mcp/agent";
  const CODEX_CONNECTIONS_DEEPLINK = "codex://settings/connections";
  const CHATGPT_SETTINGS_URL = "https://chatgpt.com/#settings/Connectors";
  const CODEX_LOGIN_COMMAND = "codex mcp login openwork";
  const CODEX_RECONNECT_COMMAND = `codex mcp logout openwork
codex mcp login openwork`;
  const OPENCODE_AUTH_COMMAND = "opencode mcp auth openwork";
  const OPENCODE_RECONNECT_COMMAND = `opencode mcp logout openwork
opencode mcp auth openwork`;
  const installs = [{
    id: "cursor",
    label: "Cursor",
    eyebrow: "Cursor Web/Agents HTTPS callback",
    helper: "Setup-only for Cursor Web/Agents with its HTTPS OAuth callback. Cursor Desktop OAuth uses cursor://anysphere.cursor-mcp/oauth/callback, which OpenWork's MCP profile intentionally rejects.",
    supportStatus: "Setup only",
    supportExplanation: "Setup guide only: use Cursor Web/Agents with its HTTPS OAuth callback. Cursor Desktop OAuth uses cursor://anysphere.cursor-mcp/oauth/callback, which OpenWork's MCP profile intentionally rejects.",
    copyText: MCP_SERVER_URL
  }, {
    id: "codex",
    label: "Codex",
    eyebrow: "Codex desktop, CLI, and IDE",
    helper: "Add OpenWork once, then sign in with Codex's MCP login command.",
    supportStatus: "Setup only",
    supportExplanation: "Setup guide only: add OpenWork, run codex mcp login openwork, and reconnect with logout then login. Native proof must be rerun on this exact branch.",
    copyText: `codex mcp add openwork --url ${MCP_SERVER_URL}`,
    authText: CODEX_LOGIN_COMMAND,
    reconnectText: CODEX_RECONNECT_COMMAND
  }, {
    id: "chatgpt-desktop",
    label: "ChatGPT Desktop",
    eyebrow: "Guided desktop setup",
    helper: "Open ChatGPT Settings > MCP servers, paste this URL, then start OAuth from ChatGPT's connection prompt.",
    supportStatus: "Setup only",
    supportExplanation: "Setup guide only: paste the URL in ChatGPT Settings > MCP servers and start OAuth there. Native proof is not complete.",
    copyText: MCP_SERVER_URL
  }, {
    id: "claude-code",
    label: "Claude Code",
    eyebrow: "One terminal command",
    helper: "Add the remote HTTP server, then use /mcp in Claude Code and follow the client auth flow.",
    supportStatus: "Setup only",
    supportExplanation: "Setup guide only: add the server, then use /mcp in Claude Code to run the client auth flow. Native proof is not complete.",
    copyText: `claude mcp add --transport http openwork ${MCP_SERVER_URL}`
  }, {
    id: "opencode",
    label: "OpenCode",
    eyebrow: "opencode.json MCP config",
    helper: "Add this remote MCP server entry to your OpenCode config, then authenticate.",
    supportStatus: "Verified",
    supportExplanation: "Verified with OpenCode native remote MCP OAuth flow.",
    copyText: `{
  "mcp": {
    "openwork": {
      "type": "remote",
      "enabled": true,
      "url": "${MCP_SERVER_URL}",
      "oauth": {}
    }
  }
}`,
    authText: OPENCODE_AUTH_COMMAND,
    reconnectText: OPENCODE_RECONNECT_COMMAND
  }, {
    id: "vs-code",
    label: "VS Code",
    eyebrow: "VS Code MCP command",
    helper: "Run this from a shell with the VS Code CLI on your path, then start OAuth from VS Code's MCP server prompt.",
    supportStatus: "Setup only",
    supportExplanation: "Setup guide only: add the server with the VS Code CLI, then start OAuth from VS Code's MCP server prompt. Native proof is not complete.",
    copyText: `code --add-mcp '{"name":"openwork","type":"http","url":"${MCP_SERVER_URL}"}'`
  }, {
    id: "any-client",
    label: "Any client",
    eyebrow: "Bring your own MCP client",
    helper: "Paste this URL only into clients that support remote Streamable HTTP MCP servers and OAuth.",
    supportStatus: "Setup only",
    supportExplanation: "Setup guide only: use clients that support remote Streamable HTTP MCP servers and OAuth. Native proof depends on the client.",
    copyText: MCP_SERVER_URL
  }];
  const clientFromHash = () => {
    if (typeof window === "undefined") return "cursor";
    const requested = window.location.hash.slice(1).replace("connect-mcp-install-", "");
    return installs.some(install => install.id === requested) ? requested : "cursor";
  };
  const copyText = async value => {
    try {
      await navigator.clipboard.writeText(value);
      return true;
    } catch {
      const textarea = document.createElement("textarea");
      textarea.value = value;
      textarea.setAttribute("readonly", "");
      textarea.style.cssText = "position:absolute;left:-9999px;top:-9999px";
      document.body.appendChild(textarea);
      textarea.select();
      const didCopy = document.execCommand("copy");
      textarea.remove();
      return didCopy;
    }
  };
  const [activeClient, setActiveClient] = useState(clientFromHash);
  const [copied, setCopied] = useState("");
  const activeInstall = installs.find(install => install.id === activeClient) || installs[0];
  useEffect(() => {
    const selectHashClient = () => setActiveClient(clientFromHash());
    window.addEventListener("hashchange", selectHashClient);
    selectHashClient();
    return () => window.removeEventListener("hashchange", selectHashClient);
  }, []);
  const copy = async (id, value) => {
    const didCopy = await copyText(value);
    setCopied(didCopy ? id : "error");
    window.setTimeout(() => setCopied(""), 2500);
  };
  return <div id="connect-mcp-install" className="not-prose my-8 overflow-hidden rounded-2xl border border-gray-200 bg-white shadow-sm dark:border-white/10 dark:bg-gray-950">
      <div className="border-b border-gray-100 px-5 py-5 dark:border-white/10">
        <div className="text-sm text-gray-600 dark:text-gray-300">
          Developers: point your own agent at your org — verified clients and setup guides.
        </div>
        <div className="mt-1 text-xs text-gray-400">
          Verified for OpenCode only; setup guides for Codex, Cursor, ChatGPT, Claude Code, VS Code, and more
        </div>
      </div>

      <div className="overflow-x-auto border-b border-gray-100 px-4 pt-4 dark:border-white/10" role="tablist" aria-label="OpenWork MCP client install options">
        <div className="flex min-w-max gap-1">
          {installs.map(install => <button key={install.id} id={`connect-mcp-install-${install.id}`} type="button" role="tab" aria-selected={install.id === activeClient} aria-controls={`connect-mcp-panel-${install.id}`} onClick={() => {
    setActiveClient(install.id);
    window.history.replaceState(null, "", `#connect-mcp-install-${install.id}`);
  }} className={`rounded-t-lg border-b-2 px-3 py-2 text-sm font-medium ${install.id === activeClient ? "border-gray-950 text-gray-950 dark:border-white dark:text-white" : "border-transparent text-gray-500 hover:text-gray-900 dark:hover:text-white"}`}>
              {install.label}
            </button>)}
        </div>
      </div>

      <div id={`connect-mcp-panel-${activeInstall.id}`} role="tabpanel" aria-labelledby={`connect-mcp-install-${activeInstall.id}`} className="p-5">
        <p className="m-0 text-xs font-semibold uppercase tracking-wider text-gray-400">{activeInstall.eyebrow}</p>
        <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
          <div>
            <h3 className="mb-0 mt-2 text-xl font-semibold text-gray-950 dark:text-white">{activeInstall.label}</h3>
            <p className="mb-4 mt-1 text-sm text-gray-500 dark:text-gray-400">{activeInstall.helper}</p>
          </div>
          {activeInstall.id === "codex" ? <a href={CODEX_CONNECTIONS_DEEPLINK} onClick={() => void copyText(MCP_SERVER_URL)} className="shrink-0 rounded-full bg-[#011627] px-5 py-2.5 text-sm font-medium text-white">
              Open settings + copy URL
            </a> : activeInstall.id === "chatgpt-desktop" ? <a href={CHATGPT_SETTINGS_URL} target="_blank" rel="noreferrer" onClick={() => copy("chatgpt-url", MCP_SERVER_URL)} className="shrink-0 rounded-full bg-[#011627] px-5 py-2.5 text-sm font-medium text-white">
              {copied === "chatgpt-url" ? "Copied URL" : "Open settings + copy URL"}
            </a> : null}
        </div>
        <div className="mb-4 mt-2 flex flex-col gap-1 sm:flex-row sm:items-center sm:gap-2">
          <span className={`w-fit rounded-full border px-2.5 py-1 text-[11px] font-semibold uppercase tracking-wider ${activeInstall.supportStatus === "Verified" ? "border-green-200 bg-green-50 text-green-700" : "border-amber-200 bg-amber-50 text-amber-700"}`}>{activeInstall.supportStatus}</span>
          <span className="text-xs text-gray-500 dark:text-gray-400">{activeInstall.supportExplanation}</span>
        </div>
        <pre className="m-0 max-h-80 overflow-auto whitespace-pre-wrap rounded-xl bg-[#011627] p-4 text-xs leading-6 text-white"><code>{activeInstall.copyText}</code></pre>
        {activeInstall.id === "any-client" ? <p className="mb-0 mt-3 text-sm text-gray-500 dark:text-gray-400">Use this URL only with MCP clients that support remote Streamable HTTP servers with OAuth.</p> : null}
        {activeInstall.authText ? <div className="mt-3">
            <p className="mb-1.5 mt-0 text-xs font-semibold uppercase tracking-wider text-gray-400">Authenticate</p>
            <pre className="m-0 overflow-auto whitespace-pre-wrap rounded-xl bg-[#011627] p-4 text-xs leading-6 text-white"><code>{activeInstall.authText}</code></pre>
          </div> : null}
        {activeInstall.reconnectText ? <div className="mt-3">
            <p className="mb-1.5 mt-0 text-xs font-semibold uppercase tracking-wider text-gray-400">Reconnect or switch org</p>
            <pre className="m-0 overflow-auto whitespace-pre-wrap rounded-xl bg-[#011627] p-4 text-xs leading-6 text-white"><code>{activeInstall.reconnectText}</code></pre>
          </div> : null}
        <div className="mt-4 flex items-center justify-between gap-3">
          <p className="m-0 text-xs text-gray-500">Works with your OpenWork account — <a href="https://app.openworklabs.com?mode=sign-up" className="font-medium underline">create one free</a>.</p>
          <button type="button" aria-label="Copy the OpenWork MCP install command" onClick={() => copy(activeInstall.id, activeInstall.copyText)} className="shrink-0 rounded-lg bg-[#011627] px-4 py-2 text-xs font-medium text-white">
            {copied === activeInstall.id ? "Copied" : copied === "error" ? "Couldn't copy" : "Copy"}
          </button>
        </div>
      </div>

      <div className="flex flex-col gap-2 border-t border-gray-100 px-5 py-4 text-xs sm:flex-row sm:items-center sm:justify-between dark:border-white/10">
        <span className="font-semibold uppercase tracking-wider text-gray-400">Server URL</span>
        <div className="flex min-w-0 items-center gap-2">
          <code className="break-all text-gray-950 dark:text-white">{MCP_SERVER_URL}</code>
          <button type="button" aria-label="Copy the OpenWork MCP server URL" onClick={() => copy("server-url", MCP_SERVER_URL)} className="shrink-0 font-medium underline">
            {copied === "server-url" ? "Copied" : "Copy URL"}
          </button>
        </div>
      </div>
    </div>;
};

The OpenWork Connect MCP server lets your AI agent use the OpenWork capabilities your organization has made available to you. The public hosted endpoint is:

```text theme={null}
https://api.openworklabs.com/mcp/agent
```

This guide is for connecting an external MCP client to OpenWork. To connect
Gmail, calendar, Drive, Slack, Notion, Linear, or another service inside the
OpenWork desktop app, use
[Connect your services](/docs/start-here/connect-your-stack/connect-services).

Your client opens a browser, you sign in to OpenWork, choose the organization to authorize, and the client stores the OAuth token for that organization.

`app.openworklabs.com/api/den` is an internal same-origin desktop proxy used by OpenWork first-party flows. Do not paste it into external MCP clients.

<OpenWorkConnectInstaller />

## Set up your client

Step-by-step guides for every client:

<CardGroup cols={3}>
  <Card title="OpenCode" href="/docs/model-context-protocol/opencode" />

  <Card title="Claude Code" href="/docs/model-context-protocol/claude-code" />

  <Card title="Codex" href="/docs/model-context-protocol/codex" />

  <Card title="Claude Desktop" href="/docs/model-context-protocol/claude-desktop" />

  <Card title="ChatGPT" href="/docs/model-context-protocol/chatgpt" />

  <Card title="Cursor" href="/docs/model-context-protocol/cursor" />

  <Card title="VS Code" href="/docs/model-context-protocol/vs-code" />

  <Card title="Windsurf" href="/docs/model-context-protocol/windsurf" />

  <Card title="Zed" href="/docs/model-context-protocol/zed" />

  <Card title="Gemini CLI" href="/docs/model-context-protocol/gemini-cli" />
</CardGroup>

## In the OpenWork desktop app

If you use the OpenWork desktop app, there is no MCP endpoint to configure.
When you sign in, the app injects OpenWork Connect with access scoped to your
active organization and refreshes that first-party access automatically. Use
`Settings` > `Connect` to sign in to the services available to you.

## Client support status

| Client          | Status     | Notes                                                                                                                                                                                                                                    | Guide                                                                     |
| --------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| OpenCode        | Verified   | Native remote MCP OAuth passed end-to-end implementation testing.                                                                                                                                                                        | [OpenWork MCP for OpenCode](/docs/model-context-protocol/opencode)             |
| Codex           | Setup only | Add/login/reconnect steps are below; native remote MCP OAuth must be rerun on this exact branch before Codex is marked verified.                                                                                                         | [OpenWork MCP for Codex](/docs/model-context-protocol/codex)                   |
| Cursor          | Setup only | Cursor Web/Agents can use an HTTPS OAuth callback. Cursor Desktop OAuth requires `cursor://anysphere.cursor-mcp/oauth/callback`, which OpenWork's MCP profile intentionally rejects, so Cursor Desktop OAuth is not currently supported. | [OpenWork MCP for Cursor](/docs/model-context-protocol/cursor)                 |
| ChatGPT Desktop | Setup only | Setup guide only; use ChatGPT `Settings > MCP servers`. Native proof is not complete.                                                                                                                                                    | [OpenWork MCP for ChatGPT](/docs/model-context-protocol/chatgpt)               |
| Claude Code     | Setup only | Setup guide only; native proof is not complete.                                                                                                                                                                                          | [OpenWork MCP for Claude Code](/docs/model-context-protocol/claude-code)       |
| Claude Desktop  | Setup only | Setup guide only; native proof is not complete.                                                                                                                                                                                          | [OpenWork MCP for Claude Desktop](/docs/model-context-protocol/claude-desktop) |
| VS Code         | Setup only | Setup guide only; native proof is not complete.                                                                                                                                                                                          | [OpenWork MCP for VS Code](/docs/model-context-protocol/vs-code)               |
| Windsurf        | Setup only | Setup guide only via mcp-remote; native proof is not complete.                                                                                                                                                                           | [OpenWork MCP for Windsurf](/docs/model-context-protocol/windsurf)             |
| Zed             | Setup only | Setup guide only via mcp-remote; native proof is not complete.                                                                                                                                                                           | [OpenWork MCP for Zed](/docs/model-context-protocol/zed)                       |
| Gemini CLI      | Setup only | Setup guide only; native proof is not complete.                                                                                                                                                                                          | [OpenWork MCP for Gemini CLI](/docs/model-context-protocol/gemini-cli)         |
| Any client      | Setup only | Use only with clients that support remote Streamable HTTP MCP servers and OAuth.                                                                                                                                                         | —                                                                         |

## OpenCode setup

Add this MCP entry to your OpenCode config:

```json theme={null}
{
  "mcp": {
    "openwork": {
      "type": "remote",
      "enabled": true,
      "url": "https://api.openworklabs.com/mcp/agent",
      "oauth": {}
    }
  }
}
```

Then authenticate:

```sh theme={null}
opencode mcp auth openwork
```

If OpenCode runs on a remote machine but the browser opens locally, follow
[Connect OpenWork MCP from a remote machine](/docs/cloud/run-in-the-cloud/connect-openwork-mcp-remote-machine).

To reconnect or switch organizations, remove the old token and authenticate again:

```sh theme={null}
opencode mcp logout openwork
opencode mcp auth openwork
```

Full guide: [OpenWork MCP for OpenCode](/docs/model-context-protocol/opencode).

## Codex setup

Codex is setup-only for this PR until its native OAuth flow is rerun on this exact branch.

Add OpenWork, then log in:

```sh theme={null}
codex mcp add openwork --url https://api.openworklabs.com/mcp/agent
codex mcp login openwork
```

To reconnect or switch organizations, log out and log in again:

```sh theme={null}
codex mcp logout openwork
codex mcp login openwork
```

Full guide: [OpenWork MCP for Codex](/docs/model-context-protocol/codex).

## Cursor Web/Agents setup-only guide

Use this guide only for Cursor Web/Agents flows that provide an HTTPS OAuth callback. Paste the OpenWork Connect endpoint where Cursor Web/Agents asks for the remote MCP server URL:

```text theme={null}
https://api.openworklabs.com/mcp/agent
```

Cursor Desktop OAuth is not currently supported. The official desktop OAuth callback is `cursor://anysphere.cursor-mcp/oauth/callback`, and OpenWork's MCP profile intentionally rejects private-use callback schemes for public OAuth clients.

Full guide: [OpenWork MCP for Cursor](/docs/model-context-protocol/cursor).

## ChatGPT setup-only guide

Open ChatGPT `Settings > MCP servers` ([open settings](https://chatgpt.com/#settings/Connectors)), paste the OpenWork Connect endpoint, and start OAuth from ChatGPT's connection prompt:

```text theme={null}
https://api.openworklabs.com/mcp/agent
```

Full guide: [OpenWork MCP for ChatGPT](/docs/model-context-protocol/chatgpt).

## Organization selection

The organization you choose in the browser is pinned into the token. Changing the active organization in OpenWork later does not change an external client's existing token. Use the logout/auth or logout/login reconnect commands above when you need a different organization.

## What you can ask the agent

Once connected, your agent can use OpenWork capabilities from plain English in the composer. These flows are exercised end to end against a live OpenWork Cloud deployment (see `evals/cloud-mcp-agent-flows.md` in the repository):

* **Check your cloud identity** — "Which OpenWork Cloud organization am I
  connected to?" The agent reports the organization, your account, and your
  role.
* **Invite teammates** — "Add [omar@example.com](mailto:omar@example.com) to my organization." The agent
  sends the invitation, updates the allowed email domains when needed, and
  surfaces seat-limit billing rules instead of failing silently.
* **Manage teams** — "Assign Priya to the Sales team." Works for active
  members; the agent explains when someone still has a pending invitation.
* **Share skills with your org** — "Create a skill that writes weekly status
  reports and share it with my whole organization." The agent writes the
  `SKILL.md` locally, then creates a plugin, attaches the skill, publishes it
  to your marketplace, and grants org-wide access — teammates see it in their
  marketplace and install it with one click.

Org policy still applies: invitations respect seat limits and domain rules, and resource access follows organization membership, roles, policies, and exposure allowlists.

## OAuth and protocol details

OpenWork Connect is a remote Streamable HTTP MCP server with OAuth:

* The protected resource is exactly `https://api.openworklabs.com/mcp/agent`.
* Clients discover OAuth metadata with RFC9728 protected-resource discovery.
* The authorization server and browser sign-in origin is `https://app.openworklabs.com/api/auth`.
* OAuth authorize and token requests must include exactly one `resource` value: `https://api.openworklabs.com/mcp/agent`.
* Clients that need registration use dynamic client registration as a fallback during discovery.
* PKCE is required for public/dynamic clients, and only the `S256` code challenge method is supported.
* Redirect URIs must be HTTPS callbacks or HTTP loopback callbacks such as `http://127.0.0.1:<port>/callback`. Private-use callback schemes are not accepted for public OAuth clients.

## Token lifetime and refresh

Clients normally refresh automatically. The values matter when diagnosing reconnects:

* Public OAuth access tokens are JWTs signed and validated with EdDSA. The issuer is exactly `https://app.openworklabs.com/api/auth`, the audience is exactly `https://api.openworklabs.com/mcp/agent`, and access tokens expire after 45 minutes.
* Refresh tokens are opaque rotating grants with a 30-day inactivity window and a 30-second rotation overlap for near-simultaneous refreshes.
* Each refresh revokes the previous refresh grant; because OpenWork stores only token hashes, a replay during the overlap can issue another successor instead of returning prior plaintext.
* Successor grants remain normal rotating grants. If an old refresh grant is replayed after the overlap, expired, or revoked, the client may receive `invalid_grant` and the client/user refresh-token family is revoked. Reconnect by logging out of the MCP entry and authenticating again.
* Access is rechecked against the active OpenWork session and organization membership, so removing a member or revoking the session cuts off MCP access.

## What the server exposes

`/mcp/agent` exposes two MCP tools:

* `search_capabilities` searches the capabilities available to the signed-in member.
* `execute_capability` runs an exact capability name returned by `search_capabilities`.

Search can include OpenWork Cloud resources such as config objects, connectors, plugins, marketplaces, skills, workers, members, roles, teams, model providers, and org-connected external MCP tools when those capabilities are enabled and shared with the member.

Availability is governed by organization membership, role, policy, and exposure allowlists. It intentionally does not expose authentication internals, admin-only system routes, webhooks, API-key creation or deletion, or credential-returning endpoints.

## Troubleshooting

* **401 missing or invalid token**: run the client's auth/login command again. Confirm the client points to `https://api.openworklabs.com/mcp/agent`.
* **403 membership or scope error**: confirm you selected the right organization and still have membership and required permissions there.
* **`invalid_grant`**: the refresh grant was expired, revoked, or replayed after the 30-second rotation overlap. Run logout, then auth/login again.
* **429 rate limit**: wait for the response's `Retry-After` value before retrying.
* **Support requests**: include the response `X-Request-Id` header plus any JSON body MCP `referenceId` or OAuth `reference_id` field so OpenWork support can find the server-side trace.

## Requirements

* An OpenWork Cloud account and organization.
* An MCP client that supports remote Streamable HTTP MCP servers.
* OAuth support in the MCP client, including browser-based authorization.

If your client does not support OAuth for remote MCP servers, it cannot connect to OpenWork Connect yet.
