runway-mcp 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- runway_mcp-0.1.0/LICENSE +21 -0
- runway_mcp-0.1.0/PKG-INFO +261 -0
- runway_mcp-0.1.0/README.md +209 -0
- runway_mcp-0.1.0/pyproject.toml +60 -0
- runway_mcp-0.1.0/runway_mcp.egg-info/PKG-INFO +261 -0
- runway_mcp-0.1.0/runway_mcp.egg-info/SOURCES.txt +27 -0
- runway_mcp-0.1.0/runway_mcp.egg-info/dependency_links.txt +1 -0
- runway_mcp-0.1.0/runway_mcp.egg-info/entry_points.txt +2 -0
- runway_mcp-0.1.0/runway_mcp.egg-info/requires.txt +15 -0
- runway_mcp-0.1.0/runway_mcp.egg-info/top_level.txt +2 -0
- runway_mcp-0.1.0/server.py +49 -0
- runway_mcp-0.1.0/setup.cfg +4 -0
- runway_mcp-0.1.0/tests/test_analyze.py +750 -0
- runway_mcp-0.1.0/tests/test_jobs.py +1196 -0
- runway_mcp-0.1.0/tests/test_match.py +341 -0
- runway_mcp-0.1.0/tests/test_profile_helpers.py +219 -0
- runway_mcp-0.1.0/tests/test_profile_models.py +139 -0
- runway_mcp-0.1.0/tests/test_profile_tools.py +313 -0
- runway_mcp-0.1.0/tests/test_server.py +75 -0
- runway_mcp-0.1.0/tests/test_uscis_cache.py +314 -0
- runway_mcp-0.1.0/tests/test_visa.py +165 -0
- runway_mcp-0.1.0/tools/__init__.py +0 -0
- runway_mcp-0.1.0/tools/_utils.py +11 -0
- runway_mcp-0.1.0/tools/analyze.py +189 -0
- runway_mcp-0.1.0/tools/jobs.py +738 -0
- runway_mcp-0.1.0/tools/match.py +178 -0
- runway_mcp-0.1.0/tools/profile.py +358 -0
- runway_mcp-0.1.0/tools/uscis_cache.py +150 -0
- runway_mcp-0.1.0/tools/visa.py +91 -0
runway_mcp-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 satovarb
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: runway-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: An MCP server that filters US job postings by technical fit AND visa sponsorship history for international students (F-1/OPT).
|
|
5
|
+
Author: satovarb
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 satovarb
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: Homepage, https://github.com/satovarb16/runwayMCP
|
|
29
|
+
Project-URL: Repository, https://github.com/satovarb16/runwayMCP
|
|
30
|
+
Project-URL: Issues, https://github.com/satovarb16/runwayMCP/issues
|
|
31
|
+
Keywords: mcp,visa,sponsorship,f-1,opt,job-search
|
|
32
|
+
Classifier: Programming Language :: Python :: 3
|
|
33
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
34
|
+
Classifier: Operating System :: OS Independent
|
|
35
|
+
Requires-Python: >=3.11
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
License-File: LICENSE
|
|
38
|
+
Requires-Dist: mcp<2,>=1.2
|
|
39
|
+
Requires-Dist: pydantic>=2
|
|
40
|
+
Requires-Dist: requests
|
|
41
|
+
Requires-Dist: beautifulsoup4
|
|
42
|
+
Requires-Dist: rapidfuzz
|
|
43
|
+
Provides-Extra: dev
|
|
44
|
+
Requires-Dist: pytest; extra == "dev"
|
|
45
|
+
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
46
|
+
Requires-Dist: responses; extra == "dev"
|
|
47
|
+
Requires-Dist: pre-commit; extra == "dev"
|
|
48
|
+
Requires-Dist: ruff; extra == "dev"
|
|
49
|
+
Provides-Extra: browser
|
|
50
|
+
Requires-Dist: playwright>=1.40; extra == "browser"
|
|
51
|
+
Dynamic: license-file
|
|
52
|
+
|
|
53
|
+
# runwayMCP
|
|
54
|
+
|
|
55
|
+
An MCP server that helps international students (F-1/OPT) filter US job postings by technical fit AND visa sponsorship history — in a single call.
|
|
56
|
+
|
|
57
|
+
## Quick install
|
|
58
|
+
|
|
59
|
+
Create a `.mcp.json` file in the directory where you run Claude Code:
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"mcpServers": {
|
|
64
|
+
"runway-mcp": {
|
|
65
|
+
"command": "uvx",
|
|
66
|
+
"args": ["runway-mcp"]
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
That's it. Open Claude Code — `uvx` downloads and runs the server automatically.
|
|
73
|
+
|
|
74
|
+
> **Don't have `uv`?** Install it: `pip install uv` (or see [uv docs](https://docs.astral.sh/uv/getting-started/installation/))
|
|
75
|
+
|
|
76
|
+
### Alternative: install from source
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
git clone https://github.com/satovarb16/runwayMCP
|
|
80
|
+
cd runwayMCP
|
|
81
|
+
pip install -e ".[dev]"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Then use `python -m server` instead of `uvx runway-mcp` in your `.mcp.json`, and add `"cwd": "/path/to/runwayMCP"`.
|
|
85
|
+
|
|
86
|
+
## First-time setup: ingest your CV
|
|
87
|
+
|
|
88
|
+
Before scoring job matches, store your CV once:
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
You: "Set up my profile using my CV at /path/to/resume.pdf"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Supports `.pdf` and `.docx`. Claude will ask for sampling approval — this is expected.
|
|
95
|
+
|
|
96
|
+
## Usage
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
You: "Evaluate this role for me: https://jobs.example.com/swe-123"
|
|
100
|
+
Claude:
|
|
101
|
+
→ analyze_job(url) — fetches job + checks visa + scores CV match
|
|
102
|
+
→ Returns APPLY / CONSIDER / SKIP + reasoning
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
On first run, the server downloads USCIS H-1B data (~2MB) automatically.
|
|
106
|
+
|
|
107
|
+
## Optional: Playwright for JavaScript-heavy job boards
|
|
108
|
+
|
|
109
|
+
Some Greenhouse custom domains require a headless browser to parse. Install the optional extra:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
pip install -e ".[dev,browser]"
|
|
113
|
+
playwright install chromium
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Without it, canonical `boards.greenhouse.io` URLs always work. The server warns you at startup if Playwright is missing.
|
|
117
|
+
|
|
118
|
+
> **Note**: `setup_profile`, `analyze_match`, and `analyze_job` use MCP Sampling — Claude Code will ask for your approval the first time these tools make a sampling request. This is expected behavior.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## How it works
|
|
123
|
+
|
|
124
|
+
Claude Code launches this server over stdio and calls its tools when relevant. You don't invoke the tools directly — Claude decides when to call them based on the conversation.
|
|
125
|
+
|
|
126
|
+
**One-call flow (recommended):**
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
You: "Evaluate this role for me: https://jobs.example.com/swe-123"
|
|
130
|
+
Claude:
|
|
131
|
+
1. analyze_job(url) → job details + visa verdict + match score + APPLY/CONSIDER/SKIP
|
|
132
|
+
2. [reasons over the data] → context, red flags, application advice
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Or use the individual tools directly:**
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
Claude:
|
|
139
|
+
1. fetch_job_posting(url) → job title, company, country, full JD
|
|
140
|
+
2. check_visa_sponsorship(company) → H-1B history, approval rate, verdict
|
|
141
|
+
3. analyze_match(job) → fit score vs your stored CV
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The visa check only runs for US roles — Claude skips it for positions in other countries.
|
|
145
|
+
|
|
146
|
+
## Status
|
|
147
|
+
|
|
148
|
+
| Tool | Status |
|
|
149
|
+
|------|--------|
|
|
150
|
+
| `fetch_job_posting` | ✅ Working — Greenhouse, Ashby, Lever, generic fallback |
|
|
151
|
+
| `check_visa_sponsorship` | ✅ Working — real USCIS FY2024 data, auto-refreshes on startup |
|
|
152
|
+
| `setup_profile` | ✅ Working — CV ingestion via MCP Sampling |
|
|
153
|
+
| `update_profile` | ✅ Working — update stored CV |
|
|
154
|
+
| `analyze_match` | ✅ Working — job-vs-CV scoring |
|
|
155
|
+
| `analyze_job` | ✅ Working — one-call orchestrator |
|
|
156
|
+
|
|
157
|
+
## Tools
|
|
158
|
+
|
|
159
|
+
### `analyze_job(url: str) -> AnalyzeJobResult`
|
|
160
|
+
|
|
161
|
+
One-call orchestrator. Fetches the job, checks visa sponsorship, and scores the match against your stored CV. Returns a combined envelope:
|
|
162
|
+
|
|
163
|
+
```json
|
|
164
|
+
{
|
|
165
|
+
"job": { "title": "...", "company": "...", "url": "..." },
|
|
166
|
+
"visa": { "verdict": "GREEN", "filings": 42, "approval_rate": 0.91 },
|
|
167
|
+
"match": { "score": 84, "matched_skills": [...], "missing_skills": [...], "summary": "..." },
|
|
168
|
+
"recommendation": "APPLY"
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Recommendation thresholds:**
|
|
173
|
+
- `APPLY` — visa GREEN and score ≥ 70
|
|
174
|
+
- `SKIP` — visa RED or score < 40 (SKIP takes precedence)
|
|
175
|
+
- `CONSIDER` — everything else
|
|
176
|
+
|
|
177
|
+
Requires a stored profile (run `setup_profile` first). If no profile exists, returns a clear error.
|
|
178
|
+
|
|
179
|
+
### `setup_profile(cv_path: str) -> ProfileSetupResult`
|
|
180
|
+
|
|
181
|
+
Reads a CV file (`.pdf` or `.docx`), sends it to Claude for structured extraction, and stores the result at `~/.config/runway-mcp/profile.json`. Required before `analyze_match` or `analyze_job`.
|
|
182
|
+
|
|
183
|
+
### `update_profile(cv_path: str) -> ProfileSetupResult`
|
|
184
|
+
|
|
185
|
+
Same as `setup_profile` but replaces an existing profile. Use when you update your CV.
|
|
186
|
+
|
|
187
|
+
### `analyze_match(job: JobPostingResult) -> MatchResult`
|
|
188
|
+
|
|
189
|
+
Scores a job posting against your stored CV using Claude. Returns a 0–100 score with matched skills, missing skills, and a summary. Requires a stored profile.
|
|
190
|
+
|
|
191
|
+
### `check_visa_sponsorship(company: str) -> VisaResult`
|
|
192
|
+
|
|
193
|
+
Looks up a company's H-1B petition history via the [USCIS H-1B Employer Data Hub](https://www.uscis.gov/tools/reports-and-studies/h-1b-employer-data-hub).
|
|
194
|
+
|
|
195
|
+
Returns: `company`, `total_filings`, `approval_rate` (0–1), `verdict` (green/yellow/red), `source`.
|
|
196
|
+
|
|
197
|
+
**Verdict thresholds** (calibrated against FY2024 data, ~36k employers):
|
|
198
|
+
- `green` — ≥ 5 filings AND approval rate ≥ 80% (active sponsor, top ~10%)
|
|
199
|
+
- `yellow` — ≥ 1 filing AND approval rate ≥ 50% (has sponsored before)
|
|
200
|
+
- `red` — no record or rate below threshold
|
|
201
|
+
|
|
202
|
+
Data is downloaded and cached at `~/.cache/runway-mcp/uscis_h1b.csv` on first call (~2MB) and auto-refreshes to the latest FY on every server startup.
|
|
203
|
+
|
|
204
|
+
### `fetch_job_posting(url: str) -> JobPostingResult`
|
|
205
|
+
|
|
206
|
+
Fetches and parses a job posting from a URL.
|
|
207
|
+
|
|
208
|
+
Returns: `title`, `company`, `country`, `location`, `description`, `posted_date`, `source_url`.
|
|
209
|
+
|
|
210
|
+
**Supported job boards**
|
|
211
|
+
|
|
212
|
+
| ATS | Canonical domain | Company custom domain | Notes |
|
|
213
|
+
|-----|------------------|-----------------------|-------|
|
|
214
|
+
| Greenhouse | ✅ `boards.greenhouse.io`, `job-boards.greenhouse.io` | ✅ with `[browser]` extra | Custom domains require Playwright |
|
|
215
|
+
| Ashby | ✅ `jobs.ashbyhq.com` | ❌ not yet | |
|
|
216
|
+
| Lever | ✅ `jobs.lever.co`, `lever.co` | ❌ not yet | |
|
|
217
|
+
| Any board with `schema.org/JobPosting` markup | ✅ generic fallback | ✅ generic fallback | Quality depends on the site's markup |
|
|
218
|
+
| Workday, ADP, others | ⚠️ generic fallback (best-effort) | ⚠️ generic fallback (best-effort) | Works if the page embeds JSON-LD or microdata |
|
|
219
|
+
| SmartRecruiters | ❌ not yet | ❌ not yet | Has public API — planned |
|
|
220
|
+
| BambooHR | ❌ not yet | ❌ not yet | Has public API — planned |
|
|
221
|
+
|
|
222
|
+
**Known gaps**
|
|
223
|
+
|
|
224
|
+
| Scenario | Behavior | Workaround |
|
|
225
|
+
|----------|----------|------------|
|
|
226
|
+
| Greenhouse custom domain without Playwright installed | Fails with an actionable error | Install `[browser]` extra |
|
|
227
|
+
| Greenhouse custom domain behind bot protection | Fails — bot protection blocks even headless browsers | Use the canonical `boards.greenhouse.io` URL |
|
|
228
|
+
| Lever custom domain | Unsupported | Find the `jobs.lever.co/company/uuid` URL directly |
|
|
229
|
+
| Any aggregator URL (LinkedIn, Indeed, Handshake) | Unsupported | Use the URL from the "Apply" redirect |
|
|
230
|
+
|
|
231
|
+
## Tool vs. reasoning boundary
|
|
232
|
+
|
|
233
|
+
These tools only **fetch and shape data**. Claude handles all reasoning:
|
|
234
|
+
- Whether to call `check_visa_sponsorship` (only for US roles)
|
|
235
|
+
- How to interpret the verdict and score in context
|
|
236
|
+
- Whether the role is a good fit overall
|
|
237
|
+
|
|
238
|
+
This is intentional — tools that encode judgment make Claude less useful, not more.
|
|
239
|
+
|
|
240
|
+
## Tests
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
pytest -m contract # fast contract tests
|
|
244
|
+
pytest -m integration # server tool registration
|
|
245
|
+
pytest # full suite (214 tests)
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Contributing
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
pip install -e ".[dev]"
|
|
252
|
+
pre-commit install # runs ruff lint + format before every commit
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Highest-value next features (in priority order):
|
|
256
|
+
1. **Workday parser** — dedicated parser for better reliability on Workday boards
|
|
257
|
+
2. **SmartRecruiters** — public API, clean integration
|
|
258
|
+
3. **BambooHR** — public API, clean integration
|
|
259
|
+
4. **Lever custom domains** — same pattern as Greenhouse custom domains
|
|
260
|
+
|
|
261
|
+
PRs welcome.
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# runwayMCP
|
|
2
|
+
|
|
3
|
+
An MCP server that helps international students (F-1/OPT) filter US job postings by technical fit AND visa sponsorship history — in a single call.
|
|
4
|
+
|
|
5
|
+
## Quick install
|
|
6
|
+
|
|
7
|
+
Create a `.mcp.json` file in the directory where you run Claude Code:
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"mcpServers": {
|
|
12
|
+
"runway-mcp": {
|
|
13
|
+
"command": "uvx",
|
|
14
|
+
"args": ["runway-mcp"]
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
That's it. Open Claude Code — `uvx` downloads and runs the server automatically.
|
|
21
|
+
|
|
22
|
+
> **Don't have `uv`?** Install it: `pip install uv` (or see [uv docs](https://docs.astral.sh/uv/getting-started/installation/))
|
|
23
|
+
|
|
24
|
+
### Alternative: install from source
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
git clone https://github.com/satovarb16/runwayMCP
|
|
28
|
+
cd runwayMCP
|
|
29
|
+
pip install -e ".[dev]"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Then use `python -m server` instead of `uvx runway-mcp` in your `.mcp.json`, and add `"cwd": "/path/to/runwayMCP"`.
|
|
33
|
+
|
|
34
|
+
## First-time setup: ingest your CV
|
|
35
|
+
|
|
36
|
+
Before scoring job matches, store your CV once:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
You: "Set up my profile using my CV at /path/to/resume.pdf"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Supports `.pdf` and `.docx`. Claude will ask for sampling approval — this is expected.
|
|
43
|
+
|
|
44
|
+
## Usage
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
You: "Evaluate this role for me: https://jobs.example.com/swe-123"
|
|
48
|
+
Claude:
|
|
49
|
+
→ analyze_job(url) — fetches job + checks visa + scores CV match
|
|
50
|
+
→ Returns APPLY / CONSIDER / SKIP + reasoning
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
On first run, the server downloads USCIS H-1B data (~2MB) automatically.
|
|
54
|
+
|
|
55
|
+
## Optional: Playwright for JavaScript-heavy job boards
|
|
56
|
+
|
|
57
|
+
Some Greenhouse custom domains require a headless browser to parse. Install the optional extra:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install -e ".[dev,browser]"
|
|
61
|
+
playwright install chromium
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Without it, canonical `boards.greenhouse.io` URLs always work. The server warns you at startup if Playwright is missing.
|
|
65
|
+
|
|
66
|
+
> **Note**: `setup_profile`, `analyze_match`, and `analyze_job` use MCP Sampling — Claude Code will ask for your approval the first time these tools make a sampling request. This is expected behavior.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## How it works
|
|
71
|
+
|
|
72
|
+
Claude Code launches this server over stdio and calls its tools when relevant. You don't invoke the tools directly — Claude decides when to call them based on the conversation.
|
|
73
|
+
|
|
74
|
+
**One-call flow (recommended):**
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
You: "Evaluate this role for me: https://jobs.example.com/swe-123"
|
|
78
|
+
Claude:
|
|
79
|
+
1. analyze_job(url) → job details + visa verdict + match score + APPLY/CONSIDER/SKIP
|
|
80
|
+
2. [reasons over the data] → context, red flags, application advice
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Or use the individual tools directly:**
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
Claude:
|
|
87
|
+
1. fetch_job_posting(url) → job title, company, country, full JD
|
|
88
|
+
2. check_visa_sponsorship(company) → H-1B history, approval rate, verdict
|
|
89
|
+
3. analyze_match(job) → fit score vs your stored CV
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The visa check only runs for US roles — Claude skips it for positions in other countries.
|
|
93
|
+
|
|
94
|
+
## Status
|
|
95
|
+
|
|
96
|
+
| Tool | Status |
|
|
97
|
+
|------|--------|
|
|
98
|
+
| `fetch_job_posting` | ✅ Working — Greenhouse, Ashby, Lever, generic fallback |
|
|
99
|
+
| `check_visa_sponsorship` | ✅ Working — real USCIS FY2024 data, auto-refreshes on startup |
|
|
100
|
+
| `setup_profile` | ✅ Working — CV ingestion via MCP Sampling |
|
|
101
|
+
| `update_profile` | ✅ Working — update stored CV |
|
|
102
|
+
| `analyze_match` | ✅ Working — job-vs-CV scoring |
|
|
103
|
+
| `analyze_job` | ✅ Working — one-call orchestrator |
|
|
104
|
+
|
|
105
|
+
## Tools
|
|
106
|
+
|
|
107
|
+
### `analyze_job(url: str) -> AnalyzeJobResult`
|
|
108
|
+
|
|
109
|
+
One-call orchestrator. Fetches the job, checks visa sponsorship, and scores the match against your stored CV. Returns a combined envelope:
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"job": { "title": "...", "company": "...", "url": "..." },
|
|
114
|
+
"visa": { "verdict": "GREEN", "filings": 42, "approval_rate": 0.91 },
|
|
115
|
+
"match": { "score": 84, "matched_skills": [...], "missing_skills": [...], "summary": "..." },
|
|
116
|
+
"recommendation": "APPLY"
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Recommendation thresholds:**
|
|
121
|
+
- `APPLY` — visa GREEN and score ≥ 70
|
|
122
|
+
- `SKIP` — visa RED or score < 40 (SKIP takes precedence)
|
|
123
|
+
- `CONSIDER` — everything else
|
|
124
|
+
|
|
125
|
+
Requires a stored profile (run `setup_profile` first). If no profile exists, returns a clear error.
|
|
126
|
+
|
|
127
|
+
### `setup_profile(cv_path: str) -> ProfileSetupResult`
|
|
128
|
+
|
|
129
|
+
Reads a CV file (`.pdf` or `.docx`), sends it to Claude for structured extraction, and stores the result at `~/.config/runway-mcp/profile.json`. Required before `analyze_match` or `analyze_job`.
|
|
130
|
+
|
|
131
|
+
### `update_profile(cv_path: str) -> ProfileSetupResult`
|
|
132
|
+
|
|
133
|
+
Same as `setup_profile` but replaces an existing profile. Use when you update your CV.
|
|
134
|
+
|
|
135
|
+
### `analyze_match(job: JobPostingResult) -> MatchResult`
|
|
136
|
+
|
|
137
|
+
Scores a job posting against your stored CV using Claude. Returns a 0–100 score with matched skills, missing skills, and a summary. Requires a stored profile.
|
|
138
|
+
|
|
139
|
+
### `check_visa_sponsorship(company: str) -> VisaResult`
|
|
140
|
+
|
|
141
|
+
Looks up a company's H-1B petition history via the [USCIS H-1B Employer Data Hub](https://www.uscis.gov/tools/reports-and-studies/h-1b-employer-data-hub).
|
|
142
|
+
|
|
143
|
+
Returns: `company`, `total_filings`, `approval_rate` (0–1), `verdict` (green/yellow/red), `source`.
|
|
144
|
+
|
|
145
|
+
**Verdict thresholds** (calibrated against FY2024 data, ~36k employers):
|
|
146
|
+
- `green` — ≥ 5 filings AND approval rate ≥ 80% (active sponsor, top ~10%)
|
|
147
|
+
- `yellow` — ≥ 1 filing AND approval rate ≥ 50% (has sponsored before)
|
|
148
|
+
- `red` — no record or rate below threshold
|
|
149
|
+
|
|
150
|
+
Data is downloaded and cached at `~/.cache/runway-mcp/uscis_h1b.csv` on first call (~2MB) and auto-refreshes to the latest FY on every server startup.
|
|
151
|
+
|
|
152
|
+
### `fetch_job_posting(url: str) -> JobPostingResult`
|
|
153
|
+
|
|
154
|
+
Fetches and parses a job posting from a URL.
|
|
155
|
+
|
|
156
|
+
Returns: `title`, `company`, `country`, `location`, `description`, `posted_date`, `source_url`.
|
|
157
|
+
|
|
158
|
+
**Supported job boards**
|
|
159
|
+
|
|
160
|
+
| ATS | Canonical domain | Company custom domain | Notes |
|
|
161
|
+
|-----|------------------|-----------------------|-------|
|
|
162
|
+
| Greenhouse | ✅ `boards.greenhouse.io`, `job-boards.greenhouse.io` | ✅ with `[browser]` extra | Custom domains require Playwright |
|
|
163
|
+
| Ashby | ✅ `jobs.ashbyhq.com` | ❌ not yet | |
|
|
164
|
+
| Lever | ✅ `jobs.lever.co`, `lever.co` | ❌ not yet | |
|
|
165
|
+
| Any board with `schema.org/JobPosting` markup | ✅ generic fallback | ✅ generic fallback | Quality depends on the site's markup |
|
|
166
|
+
| Workday, ADP, others | ⚠️ generic fallback (best-effort) | ⚠️ generic fallback (best-effort) | Works if the page embeds JSON-LD or microdata |
|
|
167
|
+
| SmartRecruiters | ❌ not yet | ❌ not yet | Has public API — planned |
|
|
168
|
+
| BambooHR | ❌ not yet | ❌ not yet | Has public API — planned |
|
|
169
|
+
|
|
170
|
+
**Known gaps**
|
|
171
|
+
|
|
172
|
+
| Scenario | Behavior | Workaround |
|
|
173
|
+
|----------|----------|------------|
|
|
174
|
+
| Greenhouse custom domain without Playwright installed | Fails with an actionable error | Install `[browser]` extra |
|
|
175
|
+
| Greenhouse custom domain behind bot protection | Fails — bot protection blocks even headless browsers | Use the canonical `boards.greenhouse.io` URL |
|
|
176
|
+
| Lever custom domain | Unsupported | Find the `jobs.lever.co/company/uuid` URL directly |
|
|
177
|
+
| Any aggregator URL (LinkedIn, Indeed, Handshake) | Unsupported | Use the URL from the "Apply" redirect |
|
|
178
|
+
|
|
179
|
+
## Tool vs. reasoning boundary
|
|
180
|
+
|
|
181
|
+
These tools only **fetch and shape data**. Claude handles all reasoning:
|
|
182
|
+
- Whether to call `check_visa_sponsorship` (only for US roles)
|
|
183
|
+
- How to interpret the verdict and score in context
|
|
184
|
+
- Whether the role is a good fit overall
|
|
185
|
+
|
|
186
|
+
This is intentional — tools that encode judgment make Claude less useful, not more.
|
|
187
|
+
|
|
188
|
+
## Tests
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
pytest -m contract # fast contract tests
|
|
192
|
+
pytest -m integration # server tool registration
|
|
193
|
+
pytest # full suite (214 tests)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Contributing
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
pip install -e ".[dev]"
|
|
200
|
+
pre-commit install # runs ruff lint + format before every commit
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Highest-value next features (in priority order):
|
|
204
|
+
1. **Workday parser** — dedicated parser for better reliability on Workday boards
|
|
205
|
+
2. **SmartRecruiters** — public API, clean integration
|
|
206
|
+
3. **BambooHR** — public API, clean integration
|
|
207
|
+
4. **Lever custom domains** — same pattern as Greenhouse custom domains
|
|
208
|
+
|
|
209
|
+
PRs welcome.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "runway-mcp"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "An MCP server that filters US job postings by technical fit AND visa sponsorship history for international students (F-1/OPT)."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = { file = "LICENSE" }
|
|
12
|
+
authors = [{ name = "satovarb" }]
|
|
13
|
+
keywords = ["mcp", "visa", "sponsorship", "f-1", "opt", "job-search"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
dependencies = [
|
|
21
|
+
"mcp>=1.2,<2",
|
|
22
|
+
"pydantic>=2",
|
|
23
|
+
"requests",
|
|
24
|
+
"beautifulsoup4",
|
|
25
|
+
"rapidfuzz",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Homepage = "https://github.com/satovarb16/runwayMCP"
|
|
30
|
+
Repository = "https://github.com/satovarb16/runwayMCP"
|
|
31
|
+
Issues = "https://github.com/satovarb16/runwayMCP/issues"
|
|
32
|
+
|
|
33
|
+
[project.scripts]
|
|
34
|
+
runway-mcp = "server:main"
|
|
35
|
+
|
|
36
|
+
[project.optional-dependencies]
|
|
37
|
+
dev = [
|
|
38
|
+
"pytest",
|
|
39
|
+
"pytest-asyncio",
|
|
40
|
+
"responses",
|
|
41
|
+
"pre-commit",
|
|
42
|
+
"ruff",
|
|
43
|
+
]
|
|
44
|
+
browser = [
|
|
45
|
+
"playwright>=1.40",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
[tool.setuptools]
|
|
49
|
+
py-modules = ["server"]
|
|
50
|
+
|
|
51
|
+
[tool.setuptools.packages.find]
|
|
52
|
+
include = ["tools*"]
|
|
53
|
+
|
|
54
|
+
[tool.pytest.ini_options]
|
|
55
|
+
testpaths = ["tests"]
|
|
56
|
+
asyncio_mode = "auto"
|
|
57
|
+
markers = [
|
|
58
|
+
"contract: contract tests that pin the Pydantic output shape",
|
|
59
|
+
"integration: tests that launch or import the full server",
|
|
60
|
+
]
|