Facebook Pixel
CLI

Running the CLI from an AI agent

What changes when an AI agent drives your LinkedIn account through a CLI instead of a tool server — fewer wrong actions, less context burned, and failures the agent can actually recover from.

An agent with shell access already has everything it needs to use this CLI. The question is not whether it can — it is whether it does the right thing when it is uncertain, and how much it costs you while it works that out.

Setup

Two lines in the agent's environment, and one file in the project.

export CONNECTSAFELY_API_KEY=...
export CONNECTSAFELY_ACCOUNT_ID=...
connectsafely skill > AGENTS.md      # or .claude/skills/connectsafely/SKILL.md

The skill file is generated from the live command registry, so it never drifts from the binary. It carries three things an agent cannot infer on its own:

The operating context. That you own this LinkedIn account, that you connected it yourself, that the API applies its own rate limits server-side, and that returned data is data you can already see when signed in. Without this, capable models hedge: they add compliance disclaimers to routine actions, ask "are you sure?" before reading your own inbox, or refuse volume that the API would have accepted. The MCP server sends exactly this text on initialize. The skill file is how it reaches the model when there is no server to send it.

The efficiency rules. Ask for fewer fields. Cap list lengths. Discover before guessing. Rehearse writes. An agent that has read these habits costs a fraction of one that has not.

The exit-code table. So the agent knows that 5 means stop rather than retry, and 7 means look it up rather than try something similar.

The three guardrails that matter

An unknown command fails; it does not become a similar one

$ connectsafely send-connect --profile-id ada
{
  "ok": false,
  "error": "Unknown command: send-connect",
  "code": "unknown_command",
  "exitCode": 7,
  "remediation": "Run `connectsafely commands` for the full list, or `connectsafely commands --search <term>` to filter it. The CLI does not run a near-match on your behalf."
}

There is no "did you mean send-connection-request?" and no automatic correction. That is a deliberate choice. A helpful suggestion is a fine idea for a person, who reads it and decides. An agent treats it as an instruction and runs it — and the command it lands on is the one that sends a real connection request to a real person. Unknown flags behave the same way, and are all named at once so a single retry fixes them:

$ connectsafely search-people --keyword CTO
{ "code": "unknown_flag", "error": "Unknown flag for search-people: --keyword", }

Bad arguments never reach LinkedIn

Everything the specification declares — required fields, enums, numeric ranges, string lengths — is checked locally, before a request is built. So are the failure modes specific to a model writing the arguments: a full profile URL pasted where an id belongs, a path traversal, control characters smuggled into an identifier.

$ connectsafely visit-profile --profile-id 'https://linkedin.com/in/ada'
{
  "code": "url_in_path_param",
  "error": "--profile-id is not a valid profileId: it contains a URL separator — pass the id, not the full URL",
  "exitCode": 3,
  "remediation": "Pass the identifier on its own — no URL, no path segments, no encoding."
}

The call never leaves the machine. Nothing is spent, nothing is logged against your account, and the agent gets a correction specific enough to fix in one turn.

Every failure says what to do next

Errors are JSON on stderr with a stable code and a remediation written as an instruction, not a description:

{
  "ok": false,
  "error": "Daily connection limit reached",
  "code": "rate_limited",
  "exitCode": 5,
  "remediation": "A daily action cap or pacing limit was reached. Stop sending this action today and resume tomorrow; retrying now will not succeed and risks the account."
}

The difference between that and a bare 429 is the difference between an agent that stops and one that spends the next ten minutes retrying into a wall.

Match on code. It is stable. The error text comes from the API and may be reworded.

Keeping stdout clean

Data goes to stdout. Errors, warnings and progress go to stderr. An agent can pipe stdout straight into a parser without stripping anything.

Retry chatter and per-row batch progress are off by default — they appear only with --verbose, or automatically when a human is at a terminal. Without that rule, a slow command that retried once would leave stderr holding a progress line and an error envelope, and the agent parsing it would get a syntax error instead of a diagnosis.

The CLI also detects who is calling. On a terminal you get aligned tables; through a pipe, in CI, or under a known agent runtime (CLAUDECODE, CURSOR_AGENT, GEMINI_CLI and friends) you get JSON. Neither side has to ask.

A worked loop

What a well-behaved agent session looks like end to end:

# 1. See what exists — cheap, and needs no API key
connectsafely commands --search people

# 2. Learn one command properly
connectsafely schema search-people

# 3. Read, asking only for what is needed
connectsafely search-people \
  --keywords "VP Marketing" --count 25 \
  --fields 'people.profileId,people.firstName,people.headline' \
  --output ndjson > prospects.ndjson

# 4. Rehearse the write before performing it
connectsafely send-connection-request \
  --profile-id ada-lovelace --custom-message "…" --dry-run

# 5. Perform it, paced, as a batch
connectsafely batch invites.ndjson

Steps 1 and 2 cost a few thousand tokens instead of thirty-six. Step 3 keeps a large search result out of context entirely. Step 4 catches a mistake for free. Step 5 sends at a rhythm that does not look like a script.

Sandboxes and containers

The CLI is built for environments where nobody can answer a prompt:

  • It never prompts. A destructive command without a terminal exits 4 and tells the caller to re-run with --yes, rather than hanging on stdin that will never arrive.
  • Authentication is an environment variable. No browser flow, no device code, no interactive login required.
  • Discovery works with no credentials at all. commands, schema, --help and skill read the specification bundled in the binary, so an agent can plan its work before a key is ever provisioned.

On this page