# NexusCRM Connect

Pull **read-only** NexusCRM data into your AI agent (**Claude Code** or **Codex**) as
*yourself*, then get tables, reports, dashboards, or spreadsheets — in plain English.
The connection is OAuth + PKCE: no password shared, no client secret, read-only scope,
and it runs as the signed-in user (you see exactly what you'd see logged into NexusCRM).

**Works on Windows, macOS, and Linux.** Requires **Node.js 18+** — check with
`node --version`; install from https://nodejs.org (LTS) if missing.

## What's in this folder

| File | For |
| --- | --- |
| `scripts/nexus_connect.mjs` | The engine — does the whole OAuth flow + queries. Shared by both tools. |
| `SKILL.md` | **Claude Code** instructions (auto-loads as a skill). |
| `AGENTS.md` | **Codex** (and any CLI agent that reads `AGENTS.md`). |
| `README.md` | This file — install steps for humans. |

## Install for Claude Code

Copy the whole `nexuscrm-connect` folder into your user skills directory:

- **Windows:** `C:\Users\<you>\.claude\skills\nexuscrm-connect\`
- **macOS / Linux:** `~/.claude/skills/nexuscrm-connect/`

The final path must end `…/.claude/skills/nexuscrm-connect/SKILL.md`. Restart Claude
Code, then just say: **"connect to NexusCRM and list my accounts."**

## Install for Codex

Codex reads `AGENTS.md`, not skills. Either:

- **Per project:** drop this folder into your project and copy `AGENTS.md` to the
  project root, **or**
- **Global:** append the contents of `AGENTS.md` to `~/.codex/AGENTS.md`, and note the
  absolute path to `scripts/nexus_connect.mjs`.

Then ask Codex for NexusCRM data and it will run the connector.

## Use it directly (any terminal — no agent needed)

```
node scripts/nexus_connect.mjs auth
node scripts/nexus_connect.mjs query "SELECT Id, Name, Industry FROM Account ORDER BY Name LIMIT 50"
```

`auth` opens your browser to sign in (once per ~hour); `query` prints JSON
(`{ status, totalSize, records }`). Also: `whoami`, `selftest`, `diagnose`.

## Something is failing — `diagnose`

```
node scripts/nexus_connect.mjs diagnose 2>&1
```

Offline, no token, no network. It answers two questions:

- **is this copy intact** — the installer records what it laid down, so a component replaced on its
  own (which reads as "just old" to every other check) shows as `drifted` with the file named
- **why calls keep failing** — counts by tool, and for each failure class a concrete action, e.g.
  "GROUP BY on more than one column is not supported" followed by what to do instead

**Redirect stderr.** The readable report goes to **stderr** so `query` can keep stdout clean JSON,
so `diagnose > out.txt` writes an empty file. Use `2>&1`, or `diagnose --json` which prints to
stdout.

**Scope.** The install check applies however this tooling is used. The activity summary covers
every call this machine makes through the tooling, tagged with the `surface` it came from so a
report can tell an MCP client apart from a CLI agent:

| how you are calling NexusCRM | recorded? |
|---|---|
| bundled MCP server registered from `mcp/mcp.json` | yes — `surface: mcp` |
| `nexus_connect.mjs query` / `auth` / `whoami` in a terminal | yes — `surface: cli` (since v1.9.0) |
| the shipped Claude plugin | no — it ships skills only, declares no MCP server, and contains no installer. Register the local server by downloading the separate CLI artifact (`nexuscrm-connect-cli.tgz`) and running the `install.sh` inside it; that is the path that records |

Entries written before v1.9.0 carry no surface and are reported as `unknown`; they all came from
the MCP server, but the report does not rewrite its own history. On the plugin-only path there is
nothing to record, so `diagnose` is the install check plus an empty activity section — expected,
not a fault. `diagnose` and `selftest` never record: reading the log must not append to it.

**What a record holds.** Entries carry OBSERVATIONS — HTTP status, the engine's own errorCode, the
thrown error's type, the redacted query — and `diagnose` turns those into an action when it reads
them, not when it writes them. One rule table serves both surfaces, so the same failure cannot be
advised two different ways, and improving a rule improves entries recorded months earlier.

A failed call: tool name, error code, duration, and the query with quoted
text, numbers and dates replaced by `?` — bare identifier-shaped tokens (field and object names)
survive by design. A successful call: tool name, row count and duration; the query text is not
kept, but the call is.

Recording is local only: `~/.nexuscrm-connect/diagnostics.jsonl`, mode 0600, and there is no upload
path. The report is written to be read in full before you decide to share it.
`NEXUS_NO_DIAGNOSTICS=1` turns recording off; deleting the file wipes it.

## If it won't connect

The **#1 cause**: the browser that opens **isn't signed in to NexusCRM**.

- Sign in at the environment URL first, then retry.
- If your **default** browser isn't the one you're signed in with, paste the URL the
  tool prints into the browser where you *are* signed in — it keeps waiting.
- Flaky first attempt? The sign-in step can need a warm-up — just run `auth` again.

## Staging instead of production

Use **flags** (identical on every OS — Windows PowerShell included):

```
node scripts/nexus_connect.mjs auth --base https://nexuscrm-new-staging.pingcap.com --client <staging_app_key>
```

Flags: `--base <url>`, `--client <client_id>`, `--port <n>`, `--scope <scope>`.

## Security

Read-only (`external.records.read`). Runs as the signed-in user — never logs in as
anyone else, never types a password, never reads tokens from browser storage. The
`client_id` is a public Consumer Key, not a secret. The cached token lives at
`~/.nexuscrm-connect/token.json` and expires automatically.
