# Connect a client

**Give your client the URL and sign in. There is nothing to paste.**

Every client below speaks the MCP authorization spec: it reads a `401` from the
endpoint, finds our authorization server, registers itself, and sends you to sign
in. No client id to create, no secret to store, and no key sitting in a config
file that ends up in a repo.

```
https://buyer.moxlade.com/mcp/
```

The trailing slash matters: without it you get a redirect, and a client that
does not replay a POST body across one fails in a way that looks like our bug.

A pasted token still works and is shown under each client — it is the fallback
for anything that cannot run a flow, and the only option for `curl`.

## Per client

#### Claude Code

Where: terminal. Sign in when it asks — nothing to paste. verified against this server 2026-09-04.

```
claude mcp add --transport http moxlade https://buyer.moxlade.com/mcp/
```

Or with a token instead:

```
claude mcp add --transport http moxlade https://buyer.moxlade.com/mcp/ \
  --header "Authorization: Bearer mxt_your_token"
```

#### Codex CLI

Where: terminal. Sign in when it asks — nothing to paste. per Codex's docs; the OAuth form is not exercised here.

```
codex mcp add moxlade --url https://buyer.moxlade.com/mcp/
codex exec "search_jobs for the work you do, then get_buyer_quality on the best one."
```

Or with a token instead:

```
codex mcp add moxlade --url https://buyer.moxlade.com/mcp/ --bearer-token-env-var MOXLADE_TOKEN
export MOXLADE_TOKEN=mxt_your_token
codex exec "search_jobs for the work you do, then get_buyer_quality on the best one."
```

#### Cursor

Where: \~/.cursor/mcp.json. Sign in when it asks — nothing to paste. per Cursor's MCP docs; not exercised here.

```
{
  "mcpServers": {
    "moxlade": {
      "url": "https://buyer.moxlade.com/mcp/"
    }
  }
}
```

Or with a token instead:

```
{
  "mcpServers": {
    "moxlade": {
      "url": "https://buyer.moxlade.com/mcp/",
      "headers": {
        "Authorization": "Bearer mxt_your_token"
      }
    }
  }
}
```

#### VS Code

Where: .vscode/mcp.json. Sign in when it asks — nothing to paste. per VS Code's MCP docs; not exercised here.

```
{
  "servers": {
    "moxlade": {
      "type": "http",
      "url": "https://buyer.moxlade.com/mcp/"
    }
  }
}
```

Or with a token instead:

```
{
  "servers": {
    "moxlade": {
      "type": "http",
      "url": "https://buyer.moxlade.com/mcp/",
      "headers": {
        "Authorization": "Bearer mxt_your_token"
      }
    }
  }
}
```

#### Gemini CLI

Where: terminal. Sign in when it asks — nothing to paste. per Gemini CLI's docs; not exercised here.

```
gemini mcp add --transport http moxlade https://buyer.moxlade.com/mcp/
```

Or with a token instead:

```
gemini mcp add --transport http moxlade https://buyer.moxlade.com/mcp/ \
  --header "Authorization: Bearer mxt_your_token"
```

#### Claude Desktop

Where: claude\_desktop\_config.json. Sign in when it asks — nothing to paste. per mcp-remote's docs; not exercised here.

```
{
  "mcpServers": {
    "moxlade": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://buyer.moxlade.com/mcp/"
      ]
    }
  }
}
```

Or with a token instead:

```
{
  "mcpServers": {
    "moxlade": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://buyer.moxlade.com/mcp/",
        "--header",
        "Authorization:${AUTH}"
      ],
      "env": {
        "AUTH": "Bearer mxt_your_token"
      }
    }
  }
}
```

#### curl

Where: terminal. verified against this server.

```
curl -s -X POST https://buyer.moxlade.com/mcp/ \
  -H "Authorization: Bearer mxt_your_token" \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'MCP-Protocol-Version: 2026-07-28' \
  -H 'Mcp-Method: tools/list' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{"_meta":{
        "io.modelcontextprotocol/protocolVersion":"2026-07-28",
        "io.modelcontextprotocol/clientCapabilities":{},
        "io.modelcontextprotocol/clientInfo":{"name":"curl","version":"0"}}}}'
```

A raw HTTP call cannot run an authorization flow, so this one needs a token.

#### Clients that cannot take a pasted token

- **claude.ai custom connectors (web)** — its Advanced settings take an OAuth client id and secret and no bearer header, which is why a token could not be used here. Our authorization server now supports dynamic client registration, so no id or secret has to be entered by hand. (server side verified; this client not yet tried against this endpoint.)
- **ChatGPT connectors** — documented as supporting OAuth and as not able to present a custom API key. (server side verified; this client not yet tried against this endpoint.)

Use OAuth for these: give the client the endpoint and it registers itself.

## Any other client

Any client that accepts a Streamable HTTP MCP server with a custom header works
the same way: URL `https://buyer.moxlade.com/mcp/`, header `Authorization: Bearer mxt_…`. The shapes
for Windsurf, Cline, Roo, Goose and Copilot CLI are analogous to the Cursor and
VS Code JSON above.

## Or connect with OAuth — no token to paste

The endpoint is an OAuth 2.1 resource server. A client that speaks the MCP
authorization spec needs nothing but the URL `https://buyer.moxlade.com/mcp/`: it gets a `401`
carrying `WWW-Authenticate`, follows `resource_metadata` to our
[protected-resource document](/.well-known/oauth-protected-resource), finds the
authorization server, registers itself, and sends you to sign in. There is no
client id to create and no secret to store.

|                      |                                                                                                                   |
| -------------------- | ----------------------------------------------------------------------------------------------------------------- |
| authorization server | `https://clerk.buyer.moxlade.com`                                                                                 |
| registration         | dynamic (RFC 7591) — clients register themselves                                                                  |
| PKCE                 | required, `S256`                                                                                                  |
| scopes               | `profile email mcp:tools offline_access`                                                                          |
| resource indicator   | required (RFC 8707) — pass `resource=https://buyer.moxlade.com/mcp/`; a token without a matching `aud` is refused |

OAuth and a pasted token land on the same account. Sign in with the address you
minted under and you keep one plan, one trial end date and one history —
connecting a second way does not start a second trial.

## Clients whose panel takes OAuth and not a header

The dividing line is the interface, not the vendor. A one-click connector panel
with no header field cannot take a bearer token; the same vendor's config file
usually can, which is why Claude Desktop is in the list above. Those panels speak
OAuth, which this endpoint now does.

> **Note** — Read the confidence notes in that list literally. The server side is verified end
> to end against production — discovery, registration, consent, an audience-bound
> token, and a real `tools/call`. Whether a particular vendor's panel drives that
> correctly is a different claim, and we have not tried those two. If one fails,
> write to us with what it said.

## First calls

1. `get_plan` — what your token is allowed to do right now, and how long a trial has left. Answers on every plan state.
2. `rate_benchmark` with a skill you know — the p25/median/p75/p90 of listed rates, or an honest "too few" below five freelancers.
3. `search_jobs` with a phrase — compact public postings, one page at a time; `next_cursor` walks the rest of what matched.

Reading a result: every tool returns `result.structuredContent` — the answer as
data, matching the tool's `outputSchema`, so you can validate it before using it.
A list arrives under `structuredContent.result`; an object is `structuredContent`
itself. `result.content[0].text` carries the same thing as JSON text for clients
that only read content. A refused or failed call is `result.isError: true` with
the reason in the text; the HTTP status stays 200.
