# Mode: HTTP + OAuth (DCR)

For MCP servers that authenticate via OAuth Dynamic Client Registration ([RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591)). The dominant pattern for hosted MCPs (Linear, Sentry, Notion, etc.).

## When to pick this mode

The MCP server publishes an [OAuth 2.0 Authorization Server Metadata](https://datatracker.ietf.org/doc/html/rfc8414) document at:
- `https://<host>/.well-known/oauth-authorization-server` with `registration_endpoint`, `authorization_endpoint`, `token_endpoint`.
- `grant_types_supported` includes `authorization_code` and `refresh_token`.
- PKCE support per [RFC 7636](https://datatracker.ietf.org/doc/html/rfc7636) (`code_challenge_methods_supported` includes `S256`).

If unsure, `curl https://<host>/.well-known/oauth-authorization-server` and check. If the file 404s or doesn't list a `registration_endpoint`, this is not OAuth/DCR — pick a different mode.

## Required inputs

In addition to the universal `<slug>` and `--out <path>` (covered in [SKILL.md § "Step 2"](../SKILL.md)):

- `<base-url>` — the MCP endpoint, e.g. `https://mcp.linear.app/mcp`. **HTTPS only** (mcporter rejects `http://` without `--allow-http`, and OAuth over plain HTTP is insecure).

## Computed values

- **Env-var prefix** = `<slug>` with a trailing `-mcp` or `-mcp-<digits>` stripped, then uppercased, hyphens → underscores, suffix `_MCP_`. Slug `acme-linear` → `ACME_LINEAR_MCP_`; slug `acme-linear-mcp` also → `ACME_LINEAR_MCP_`.
- The skill requires three env vars: `<PREFIX>REFRESH_TOKEN`, `<PREFIX>CLIENT_ID`, `<PREFIX>ACCESS_TOKEN`.
- If a provider-specific confidential-client OAuth variant declares `oauth_client_secret_env` in `mcporter.json`, that secret env is also required and must be included in `requires.env`.

## Files to generate

OAuth shape — see [`conventions.md` § "Files generated by scaffolding"](conventions.md#files-generated-by-scaffolding). OAuth is the only mode that ships `scripts/invoke.sh` and `scripts/init-mcporter.sh` — the runtime wrapper seeds the OAuth vault before calls when the env-supplied credential material changes or the vault entry is missing.

## Template — `<slug>/SKILL.md`

````markdown
---
name: <SLUG>
description: "TODO: rewrite before publishing — durable purpose framing. Example shape: Read and write <Service> data via <Service>'s hosted MCP server. Thin pass-through to the official <Service> MCP; the live tool catalog is whatever that server advertises. Use when the user asks about <Service> work or wants to read or write <Service> data."
homepage: "TODO: replace with upstream MCP docs URL, e.g. https://docs.example.com/mcp"
metadata:
  {
    "openclaw":
      {
        "emoji": "📡",
        "requires":
          {
            "bins": ["mcporter", "jq", "flock", "shasum"],
            "env":
              [
                "<PREFIX>REFRESH_TOKEN",
                "<PREFIX>CLIENT_ID",
                "<PREFIX>ACCESS_TOKEN",
              ],
          },
        "primaryEnv": "<PREFIX>REFRESH_TOKEN",
        "install":
          [
            {
              "id": "node",
              "kind": "node",
              "package": "mcporter",
              "bins": ["mcporter"],
              "label": "Install mcporter (node)",
            },
          ],
      },
  }
---

# <SLUG>

## How to use this skill

This skill is a thin pass-through to the hosted MCP server at `<BASE_URL>`. The live server is the source of truth for what tools exist, what they're called, what arguments they take, and any per-server instructions the server publishes.

Always invoke through `bash {baseDir}/scripts/invoke.sh` — never call `mcporter` directly. The wrapper seeds the OAuth vault from the env-supplied tokens when needed, then calls `mcporter`.

**Step 1 — Discover the live tool catalog and any server-published usage instructions.** Always run this first; do not rely on tool names from memory:

```sh
bash {baseDir}/scripts/invoke.sh list <SLUG> --schema
```

The output includes the server's `Instructions:` field (read it) and a JSON Schema for every tool's parameters. Treat this as the authoritative reference for the rest of the session.

**Step 2 — Call any tool from the catalog** using the form `<SLUG>.<tool>`:

```sh
bash {baseDir}/scripts/invoke.sh call <SLUG>.<tool> <arg>=<value> ...
```

Add `--output json` for structured output (also surfaces transport errors as JSON envelopes):

```sh
bash {baseDir}/scripts/invoke.sh call --output json <SLUG>.<tool> ...
```

## Authentication

Credentials are available to the agent runtime through required env vars. The wrapper seeds mcporter's vault as needed before each call. mcporter handles authentication automatically: it reads tokens from the vault, sends them with each request, and refreshes them on expiry. Just call tools.

The only failure mcporter can't recover from on its own is grant revocation (the user revoking access in the upstream service's UI). It manifests as calls persistently failing with auth errors that don't clear on retry — at that point surface it to the user and ask them to re-authorize the integration.
````

**Substitutions to make before writing the SKILL.md.** Substitute `<SLUG>` (the bundle's slug), `<BASE_URL>` (the MCP endpoint), and `<PREFIX>` (the env-var prefix derived from the slug — see [§ "Computed values"](#computed-values) above) throughout the template. No mode-specific bullet expansion — OAuth's Authentication block has no per-credential bullets.

**Editability rules** for the Discovery flow and Authentication block live in [`conventions.md` § "Editability — Discovery flow and Authentication block"](conventions.md#editability--discovery-flow-and-authentication-block). Read it before publishing.

**Optional sections.** Between the discovery flow and the Authentication block, you *may* insert `## Conventions worth knowing` and/or `## Safety` sections if they apply to this MCP. Their templates and inclusion criteria live in [`conventions.md` § "Optional sections — Conventions worth knowing & Safety"](conventions.md#optional-sections--conventions-worth-knowing--safety). Omit entirely if they don't apply — these are opt-in.

## Template — `<slug>/mcporter.json`

```json
{
  "imports": [],
  "mcpServers": {
    "<SLUG>": {
      "baseUrl": "<BASE_URL>",
      "transport": "http",
      "auth": "oauth"
    }
  }
}
```

## Templates — `<slug>/scripts/invoke.sh` and `<slug>/scripts/init-mcporter.sh`

These two scripts ship as copy targets in the builder bundle. They are not per-skill source code — do not edit the copies under `<slug>/scripts/`. Bug fixes and new functionality go upstream, into [`{baseDir}/scripts/templates/invoke.sh`](../scripts/templates/invoke.sh) or [`{baseDir}/scripts/templates/init-mcporter.sh`](../scripts/templates/init-mcporter.sh); existing skills pick up the fix when they're next re-scaffolded or re-published. Rationale for each line lives in those files' inline comments — read those if you're considering an upstream change.

```sh
mkdir -p <out>/<slug>/scripts
cp {baseDir}/scripts/templates/invoke.sh         <out>/<slug>/scripts/invoke.sh
cp {baseDir}/scripts/templates/init-mcporter.sh  <out>/<slug>/scripts/init-mcporter.sh
```

Use `cp` rather than file-tool round-trips (`Read` + `Write`) — `cp` preserves bytes exactly, while file tools can silently normalize whitespace, line endings, or trailing newlines depending on the host runtime.

No string substitution is needed — both templates are slug-agnostic. `invoke.sh` reads the server key from the skill-local `mcporter.json`; `init-mcporter.sh` takes that key as its `<server-name>` argument, passed in by `invoke.sh`.

### What each script does

- **`invoke.sh`** — agent-runtime wrapper. Resolves `SKILL_DIR`, reads the first `mcpServers` key from `mcporter.json`, runs `init-mcporter.sh <server> <config>`, then execs `mcporter "$@"` with `MCPORTER_CONFIG=<config>` in the child environment.
- **`init-mcporter.sh`** — idempotent vault seeder. Reusable across every OAuth-mode MCP skill. Detects credential rotation by hashing the env-supplied OAuth client/refresh credential material and comparing against `${SKILL_DIR}/.provisioned`; reseeds the mcporter vault when the hash differs or the vault entry is missing; no-ops otherwise.

## Mode-specific validation

In addition to the universal checks in `validation.md`:

- [ ] `mcpServers.<slug>.transport` = `"http"` and `auth` = `"oauth"`.
- [ ] `requires.env` lists REFRESH_TOKEN, CLIENT_ID, and ACCESS_TOKEN vars with the same `<PREFIX>`, plus any provider-specific confidential-client secret declared by `mcporter.json`.
- [ ] `requires.bins` is exactly `["mcporter", "jq", "flock", "shasum"]`.
- [ ] `primaryEnv` = `<PREFIX>REFRESH_TOKEN`.
- [ ] `scripts/invoke.sh` and `scripts/init-mcporter.sh` match the templates byte-for-byte. The universal `verify-bundle.sh` check (in [`validation.md`](validation.md#bundle--template-consistency)) enforces this.

## Reporting back to the user

Follow the universal shape in [`conventions.md` § "Reporting back to the user"](conventions.md#reporting-back-to-the-user). OAuth-specific notes:

- The three env vars (`<PREFIX>REFRESH_TOKEN`, `<PREFIX>CLIENT_ID`, `<PREFIX>ACCESS_TOKEN`) come from completing the OAuth flow against `<BASE_URL>/.well-known/oauth-authorization-server`. The wrapper reads them at agent runtime. Mention this if the user hasn't asked the provisioner for them yet.

## References

- [RFC 7591 — OAuth 2.0 Dynamic Client Registration Protocol](https://datatracker.ietf.org/doc/html/rfc7591) — the spec the MCP servers in scope implement
- [RFC 8414 — OAuth 2.0 Authorization Server Metadata](https://datatracker.ietf.org/doc/html/rfc8414) — `/.well-known/oauth-authorization-server` discovery
- [RFC 7636 — Proof Key for Code Exchange (PKCE)](https://datatracker.ietf.org/doc/html/rfc7636) — required by most MCP authorization servers
- [openclaw/mcporter — `src/oauth-vault.ts`](https://github.com/openclaw/mcporter/blob/main/src/oauth-vault.ts) — vault key derivation and JSON shape
- [openclaw/mcporter — `src/oauth-persistence.ts`](https://github.com/openclaw/mcporter/blob/main/src/oauth-persistence.ts) — `tokens()` / `saveTokens()` implementation
- [openclaw/mcporter — `src/oauth.ts`](https://github.com/openclaw/mcporter/blob/main/src/oauth.ts) — `OAuthClientProvider` interface implementation
- [MCP TypeScript SDK — `src/client/auth.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/src/client/auth.ts) — `refreshAuthorization()` semantics on 401
- [openclaw/mcporter — `docs/config.md` § "XDG Base Directory"](https://github.com/openclaw/mcporter/blob/main/docs/config.md) — vault path resolution rules
