workatastartup-mcp
README.md
# workatastartup-mcp
[](https://github.com/moutasem-isentemiz/workatastartup-mcp/actions/workflows/ci.yml)
An [MCP](https://modelcontextprotocol.io) server that reads [Work at a Startup](https://www.workatastartup.com) (Y Combinator) job postings through your own logged-in session.
Work at a Startup keeps its job pages behind a login, so tools that only fetch public URLs get a title and nothing else. This server signs the request with your session cookie, so an AI assistant can pull the full job description the same way you'd see it in the browser.
## What it does
It exposes two tools:
- **`get_job(jobId)`** — takes the numeric id from a `/jobs/<id>` URL and returns the job as structured data: title, company, location, whether it's remote, employment type, role family, minimum experience, salary and equity ranges, visa policy, skills, when the posting went up, whether your account's location is eligible for the role, and the full description. It also hands back the company's slug and URL, so a job can be followed straight to `get_company`.
- **`get_company(slug)`** — takes the slug from a `/companies/<slug>` URL (e.g. `siphox-health`) and returns the company's details — name, batch, tagline, website, location, team size, industry, and founders — along with every role it currently has open (title, id, url, role family, employment type, location, remote flag, experience, salary and equity ranges, visa policy, and skills).
## Requirements
- Node.js 18 or newer
- A Work at a Startup account you're logged into
## Install
```bash
npm install -g workatastartup-mcp
```
Or run it straight from npm without installing:
```bash
npx workatastartup-mcp
```
## Getting your session cookie
The server reuses your existing session instead of storing your password. You give it your cookie once:
1. Log in to https://www.workatastartup.com in your browser.
2. Open the developer tools (`F12`), go to the **Network** tab, and reload the page.
3. Click the first request, find the **Cookie** request header, and copy its whole value.
4. Put that value in a `WAAS_COOKIE` environment variable.
The cookie expires after a while. When the server starts returning "session expired", repeat the steps above for a fresh one.
## Connecting it to an MCP client
Add the server to your client's MCP config. For a Claude-style config it looks like this:
```json
{
"mcpServers": {
"workatastartup": {
"command": "npx",
"args": ["workatastartup-mcp"],
"env": {
"WAAS_COOKIE": "paste-your-cookie-here"
}
}
}
}
```
Once it's connected, ask your assistant to fetch a posting by id, for example: *"Get Work at a Startup job 100105."* — or ask for a company's open roles: *"What is siphox-health hiring for on Work at a Startup?"*
## How it works
The job pages are an [Inertia.js](https://inertiajs.com) app: instead of server-rendering the content, they ship the whole page as JSON inside a `data-page` attribute and let the browser draw it. The server reads that JSON straight from the attribute, so you get clean structured fields rather than scraped text — and it needs nothing heavier than a single authenticated HTTP request per job, no headless browser.
The same job is serialised differently depending on which page you land on: camelCase on a job page (`minExperience`), snake_case with `pretty_*` display variants inside a company's payload (`pretty_min_experience`). The parser accepts either spelling and prefers the display variant when both are there, since that's the string the site itself would show — `Remote (US only)` rather than a bare office city.
## Batch mode from the command line
Run with one or more job ids and the same binary works as a plain CLI instead of the MCP server — handy for scripting, a saved queue of ids, or a quick check without wiring up a client. Each job streams to stdout as it's fetched, one JSON record per line, so you can pipe it straight into `jq`:
```bash
WAAS_COOKIE=... npx workatastartup-mcp --json 100105 100106 | jq .title
```
Without `--json` each record is pretty-printed. A bad id doesn't stop the run — it prints an `{ "jobId": ..., "error": ... }` record and the command exits non-zero if any id failed. Run it with `--help` for the full usage.
### Draining a staged queue
If you collect postings as you browse, put them in a file and let `--from` chew through it:
```bash
WAAS_COOKIE=... npx workatastartup-mcp --json --from queue.txt > jobs.jsonl
```
One entry per line, in whatever shape you copied it — a bare id, a `/jobs/<id>` path, or the whole URL out of the address bar. Blank lines and `#` comments are skipped, so the file can carry notes:
```
# worth a closer look
100105
https://www.workatastartup.com/jobs/100106?utm_source=share # remote, hardware
```
A job listed twice is fetched once, the ids are fetched in the order you staged them, and a line that isn't an id is reported on stderr and skipped rather than ending the run. `--from -` reads the queue from stdin, so `grep`, `jq` or a browser export can pipe straight in. Ids on the command line work the same way and are fetched first.
## Development
```bash
npm test
```
The tests run the parsers against saved pages in `test/fixtures` — trimmed copies of what the site serves, payload and all — so the whole extraction path is covered without a session cookie or a network call. If Work at a Startup changes its markup, save a fresh page into `test/fixtures` and the tests will show what moved.
GitHub Actions runs the same suite on every push and pull request, across Node 18 through 24 — see [`.github/workflows/ci.yml`](.github/workflows/ci.yml).
## Limitations
- Only reads public-to-you postings — anything your account can't see, the server can't see either.
- Field extraction is best-effort against the current page markup; if Work at a Startup restructures a page, the selectors in `src/job-client.js` may need a small update.
- Through the MCP tools it's one posting per call; use the batch CLI above to fetch several ids at once.
- The CLI fetches one job at a time, on purpose — it's your own session doing the reading, so a queue of a few hundred takes a few minutes.
## License
MIT — see [LICENSE](LICENSE).
TDQS
A4/5.0
Scored across 1 tool
Disambiguation5/5
With only one tool, there is zero ambiguity. An agent will always select get_company.
Naming Consistency5/5
The single tool name 'get_company' follows a clear verb_noun pattern. Consistent and descriptive.
Tool Count2/5
A single tool for what implies a broader domain (company and role retrieval) is too thin. Ideally there would be at least listing or search capabilities.
Completeness2/5
Only a read operation exists. Missing essential operations like list, search, or any mutation endpoints, leaving obvious gaps for the domain.
Maintenance
ActivitySlowing
ResponsivenessNo issues