# Developer quickstart

## 1. Obtain a least-privilege connection

In the target environment, sign in as an organization owner/admin and open Settings → MCP → New connection. For this quickstart select **sites** and **read-only**, with a short expiry. The bearer token is shown once. Store it using your local secret manager; never commit it or paste it into a prompt.

Open MCP → Security → Connection permissions to narrow modules, actions, tools and records further. Empty narrowing lists do not add modules to the original grant. Document and export sharing are disabled by default on each connection.

Use the MCP URL for that environment: the published HTTPS origin followed by `/api/mcp`. Do not use the login page, `/api/mcp-public`, or a development URL for a production connection. Tokens are not portable between environments.

For OAuth-capable providers, add the remote URL and complete browser account linking instead of manually creating a token. See [Authentication](authentication.md).

## 2. Run the dependency-free Node client

Node.js 18+ is required. The example uses the real Streamable HTTP protocol, initializes the connection, sends the initialized notification, lists tools and calls only an advertised read-only tool.

```sh
# Supply MCP_TOKEN privately through your local environment/secret manager.
export MCP_URL="https://YOUR_PUBLISHED_HOST/api/mcp"
node docs/mcp/examples/client.mjs --tool list_sites --args '{"limit":10,"offset":0}'
```

Without `--tool` it prints tool names only. The example refuses mutation tools and deliberately does not request or execute approvals. Responses may contain business data: do not upload its stdout to public logs.

Download the runnable example from `/api/mcp-public/docs/client.mjs`. No inference API key, provider SDK or npm installation is needed.

## 3. Inspect available schemas

Use `tools/list` and read each `inputSchema` before calling a tool. Resolve names to UUIDs with an authorized list tool; never guess IDs. Send decimal monetary values as strings without currency symbols, and ISO dates according to the schema.

```json
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "list_sites",
    "arguments": {"limit": 10, "offset": 0}
  }
}
```

POST requests require `Authorization: Bearer …`, `Content-Type: application/json` and `Accept: application/json, text/event-stream`. After initialization send the negotiated `MCP-Protocol-Version`. Prefer a compatible MCP client SDK for production OAuth lifecycle and transport handling; this small client demonstrates the wire contract, not a full OAuth implementation.

## 4. Page safely

List results normally live at `result.structuredContent.data`:

```json
{"items": [], "total": 0, "limit": 10, "offset": 0, "nextOffset": null}
```

Continue only when `nextOffset` is a number; pass that value as the next `offset`. Stop at `null`. Scoped pages may be empty while another page exists, and may omit the global `total` and set `scopeFiltered: true`. Do not interpret an empty scoped page as the end of all data.

## 5. Try an interactive dashboard

For a properly granted connection call `render_retailhardhat_dashboard`. MCP Apps hosts can render the returned UI resource; other clients retain structured/text results. Dashboard modules, underlying read tools, document/export grants and quotas must all permit the requested workflow. See [Native dashboards](../mcp-providers/native-views-and-discovery.md).

## 6. Understand writes before enabling them

Guarded-write access makes mutations eligible; it does not bypass action permissions or business workflows. Depending on current approval rules, a write returns either its completed result or `pending_approval`. A pending request has made no business change.

A human reviews exact stored arguments in the app. The same connection then uses `get_approval_status` and `execute_approved_action` with the approval UUID. Execution is single-use. Never automatically retry an uncertain mutation. See [Security](security.md) and [Response reference](reference.md).
