← back to Linkedin Agent
docs/setup.md
92 lines
# Setup walkthrough
Estimated time: 30 min from "no LinkedIn dev app" to "first post via Claude Code MCP."
## 1. Register a LinkedIn developer app (10 min)
1. Go to https://developer.linkedin.com/ → "My apps" → "Create app".
2. Fill out the form:
- **App name:** `Steve Personal Agent` (or whatever — only you see this)
- **Company page:** select your existing Designer Wallcoverings page, or create a new throwaway company you control
- **App logo:** any 100×100 PNG (LinkedIn requires one)
- **Legal agreement:** check the box
3. After creation, in the **Auth** tab:
- Add redirect URL: `http://localhost:7480/api/auth/linkedin/callback`
- Note the **Client ID** + **Client Secret** (Secret only displayed once — copy now)
4. In the **Products** tab, request access to:
- **Sign In with LinkedIn using OpenID Connect** (instant approval)
- **Share on LinkedIn** (instant approval)
- **Marketing Developer Platform** (manual review — usually 3-7 days, only needed for Company Page posts and lead gen)
## 2. Route credentials via /secrets (2 min)
Per Steve's standing rule, paste both values via the secrets-manager skill:
```
LINKEDIN_CLIENT_ID=<value>
LINKEDIN_CLIENT_SECRET=<value>
```
The skill will fan them to `~/Projects/linkedin-agent/.env.local` automatically.
## 3. Install + boot (5 min)
```sh
cd ~/Projects/linkedin-agent
npm install
cp .env.local.example .env.local # if it doesn't exist after secrets-manager
# Set AUTH_PASSWORD + SESSION_SECRET in .env.local:
echo "AUTH_PASSWORD=$(openssl rand -hex 12)" >> .env.local
echo "SESSION_SECRET=$(openssl rand -hex 32)" >> .env.local
npm run dev
```
Server boots on http://localhost:7480.
## 4. First OAuth flow (2 min)
1. Open http://localhost:7480/auth in your browser.
2. LinkedIn login screen → grant permissions.
3. You'll land on `/api/auth/linkedin/callback?code=...` and see a success page.
4. Token written to `.env.local` (`LINKEDIN_ACCESS_TOKEN`, `LINKEDIN_REFRESH_TOKEN`, `LINKEDIN_TOKEN_EXPIRES_AT`).
## 5. Smoke-test a post (1 min)
```sh
curl -X POST http://localhost:7480/api/post \
-H "Content-Type: application/json" \
-H "X-Admin-Key: $(grep AUTH_PASSWORD .env.local | cut -d= -f2)" \
-d '{"text":"Testing my new LinkedIn agent. If you see this, the OAuth flow worked. Building lately: Grant, Patty, Norma."}'
```
Should return `{"ok": true, "id": "urn:li:share:..."}`.
Refresh your LinkedIn feed. The post should be there.
## 6. Wire MCP into Claude Code (5 min)
See [`mcp-config.md`](./mcp-config.md).
## 7. (Optional) Run as pm2 (1 min)
```sh
cd ~/Projects/linkedin-agent
pm2 start ecosystem.config.js
pm2 save
```
Now `linkedin-agent` boots on Mac restart.
## Token refresh
LinkedIn access tokens last 60 days. Refresh tokens last 365 days. The
agent doesn't auto-refresh yet (tracked in TODO). For now, when you get a
401 from a post call, re-run the OAuth flow at `/auth`.
## Troubleshooting
- **"unauthorized_scope_error"** — your app doesn't have the right Products approved. Go back to the Products tab and add Sign In with LinkedIn + Share on LinkedIn.
- **"redirect_uri_mismatch"** — the redirect URI in your app settings must EXACTLY match `LINKEDIN_REDIRECT_URI` in `.env.local`. Trailing slashes count.
- **Posts return 422** — usually means LinkedIn rejected the post body. Check for over-3000-char text, broken Unicode, or restricted hashtags.
- **Connection requests** — not supported by the API. Use Browserbase mode (see README) or do them manually.