visa-jobs-mcp
Identify US visa-sponsoring opportunities on LinkedIn and access the right contacts to accelerate your outreach.
visa-jobs-mcp

visa-jobs-mcp is an MCP server that helps agents find relevant jobs from fresh LinkedIn searches, with optional visa-aware filtering.
It is built for speed and practical outcomes:
- search jobs in a location,
- match employers against sponsorship history,
- return actionable results with links and contact info,
- keep user data local.
Get Started
1. Install on macOS (Homebrew)
brew tap neosh11/visa-jobs-mcp
brew install neosh11/visa-jobs-mcp/visa-jobs-mcp
2. Install on macOS/Linux (bash installer)
curl -fsSL https://raw.githubusercontent.com/neosh11/visa-jobs-mcp/main/scripts/install.sh | bash
Pin a specific version:
curl -fsSL https://raw.githubusercontent.com/neosh11/visa-jobs-mcp/main/scripts/install.sh | bash -s -- --version 0.3.1
3. Install on Windows (PowerShell installer)
irm https://raw.githubusercontent.com/neosh11/visa-jobs-mcp/main/scripts/install.ps1 | iex
Pin a specific version:
$env:VISA_JOBS_MCP_VERSION = "0.3.1"
irm https://raw.githubusercontent.com/neosh11/visa-jobs-mcp/main/scripts/install.ps1 | iex
4. Register in Codex (macOS/Linux)
codex mcp add visa-jobs-mcp --env VISA_JOB_SITES=linkedin -- visa-jobs-mcp
Register in Codex (Windows PowerShell):
codex mcp add visa-jobs-mcp --env VISA_JOB_SITES=linkedin -- "$env:LOCALAPPDATA\\Programs\\visa-jobs-mcp\\bin\\visa-jobs-mcp.exe"
Verify:
codex mcp list
codex mcp get visa-jobs-mcp
5. Use it in chat
In a new Codex session, ask naturally:
Find IT jobs in Delhi for my skills.Set my visa preference to E3 and find software engineer jobs in New York that sponsor E3.
Build from source (optional)
go build -o visa-jobs-mcp ./cmd/visa-jobs-mcp
./visa-jobs-mcp --version
If you need to refresh data/companies.csv from source, run:
./scripts/run_internal_pipeline.sh
Note: MCP runtime is Go-only. Python is only needed for maintainers running the internal dataset pipeline.
Live LinkedIn E2E (manual)
Run a real end-to-end search against LinkedIn (not stubbed tests):
./scripts/run_live_linkedin_e2e.sh
Optional timeout override:
VISA_E2E_TEST_TIMEOUT=8m ./scripts/run_live_linkedin_e2e.sh
Optional live test parameters:
VISA_E2E_VISA_TYPE=E3 \
VISA_E2E_LOCATION="New York, NY" \
VISA_E2E_JOB_TITLE="Software Engineer" \
./scripts/run_live_linkedin_e2e.sh
What It Supports
- LinkedIn-only search.
- General skill/location job search with no visa setup required.
- Optional visa preference matching when
preferred_visa_typesis set. - Search sessions with pagination and resume support.
- Saved jobs and ignored jobs.
- Employer contact extraction when available.
- Local-first private data storage.
- No proxy usage.
- No LLM calls inside MCP runtime (agent handles reasoning).
Core MCP Tools
start_job_searchget_job_search_statusget_job_search_resultsset_user_preferencesstart_visa_job_searchget_visa_job_search_statusget_visa_job_search_resultssave_job_for_laterignore_joblist_saved_jobslist_ignored_jobsget_mcp_capabilities
Tip: ask the agent to call get_mcp_capabilities first for a machine-readable contract.
MCP Contract (Generated)
Expand MCP Contract (auto-generated)
Generated from get_mcp_capabilities() via scripts/generate_contract_docs.py.
Server
server:visa-jobs-mcpversion:0.3.1capabilities_schema_version:1.2.0confidence_model_version:v1.1.0-rules-go
Required Before Search
tool:start_job_searchrequired_fields:user_id
Design Decisions
agent_is_reasoning_layer:Truebackground_search_runs_local_persistence:Truedata_not_shared_or_sold:Truefirst_class_job_management:Truefree_forever:Truefresh_job_search_per_query:Trueignored_companies_local_persistence:Trueignored_jobs_local_persistence:Truelicense:MITllm_api_keys_required_by_mcp:Falsellm_runtime_inside_mcp:Falseno_fake_reviews_or_bot_marketing:Trueproxies_used:Falserate_limit_backoff_retries:Truesaved_jobs_local_persistence:Truesearch_sessions_local_persistence:Truestrict_user_visa_match:Falsestrictness_modes_supported:['balanced', 'strict']supported_job_sites:['linkedin']visa_matching_optional:True
Defaults
dataset_stale_after_days:30job_db_path:data/app/visa_jobs.dbmax_scan_results:1200max_search_sessions_per_user:20rate_limit_initial_backoff_seconds:2rate_limit_max_backoff_seconds:30rate_limit_retry_window_seconds:180scan_multiplier:8search_run_ttl_seconds:21600search_session_ttl_seconds:21600strictness_mode:stricttool_call_soft_timeout_seconds:48
Tools
| Tool | Description | Required Inputs | Optional Inputs |
|---|---|---|---|
get_mcp_capabilities | Return MCP capabilities, tools, and contracts for agent self-discovery. | - | - |
set_user_preferences | Save the user's visa preferences for optional visa-specific matching. | user_id, preferred_visa_types | - |
set_user_constraints | Save urgency and work-mode constraints used for personalized guidance. | user_id | - |
get_user_preferences | Fetch the saved user preferences and constraints. | user_id | - |
get_user_readiness | Report whether the user and local dataset are ready for search. | user_id | - |
find_related_titles | Return adjacent role titles to widen low-yield searches. | job_title | - |
add_user_memory_line | Append a profile memory line (skills, goals, fears, constraints). | user_id, content | - |
query_user_memory_blob | Query the user's local memory blob with optional text filtering. | user_id | - |
delete_user_memory_line | Delete one memory line by id from the local blob. | user_id, line_id | - |
save_job_for_later | Save a job to the user's local shortlist for follow-up. | user_id | job_url, result_id, session_id |
list_saved_jobs | List saved jobs in reverse-chronological order. | user_id | - |
delete_saved_job | Remove one saved job from the local shortlist. | user_id, saved_job_id | - |
ignore_job | Hide one job from future results for this user. | user_id | job_url, result_id, session_id |
list_ignored_jobs | List ignored jobs in reverse-chronological order. | user_id | - |
unignore_job | Unhide a previously ignored job by id. | user_id, ignored_job_id | - |
ignore_company | Hide all jobs from a company in future searches. | user_id | - |
list_ignored_companies | List ignored companies in reverse-chronological order. | user_id | - |
unignore_company | Remove one company from the ignored list. | user_id, ignored_company_id | - |
mark_job_applied | Mark a job as applied and persist pipeline state. | user_id | - |
update_job_stage | Update lifecycle stage for a tracked job (saved/applied/interview/etc). | user_id, stage | - |
list_jobs_by_stage | List tracked jobs filtered by lifecycle stage. | user_id, stage | - |
add_job_note | Attach or append a note to a tracked job record. | user_id, note | - |
list_recent_job_events | List recent stage transitions and lifecycle events. | user_id | - |
get_job_pipeline_summary | Summarize tracked pipeline counts by stage for one user. | user_id | - |
clear_search_session | Delete one cached search session or all sessions for a user. | user_id | - |
export_user_data | Export all local records for a user across stores. | user_id | - |
delete_user_data | Permanently delete all local records for a user. | user_id, confirm | - |
get_best_contact_strategy | Suggest best outreach channel/contact for a job. | user_id | - |
generate_outreach_message | Generate a practical outreach draft tailored to user and role. | user_id | - |
start_job_search | Start a background job search without requiring visa preferences. | location, job_title, user_id | - |
get_job_search_status | Poll incremental progress/events for a background job search run. | user_id, run_id | - |
get_job_search_results | Fetch current result page from a background job search run. | user_id, run_id | - |
cancel_job_search | Request cancellation of an in-progress background job search run. | user_id, run_id | - |
start_visa_job_search | Start a background search run for long scans. | location, job_title, user_id | - |
get_visa_job_search_status | Poll incremental progress/events for a background search run. | user_id, run_id | - |
get_visa_job_search_results | Fetch current result page from a background search run. | user_id, run_id | - |
cancel_visa_job_search | Request cancellation of an in-progress background run. | user_id, run_id | - |
discover_latest_dol_disclosure_urls | Discover latest DOL LCA/PERM disclosure sources. | - | - |
run_internal_dol_pipeline | Run internal pipeline to refresh sponsor-company dataset. | - | - |
refresh_company_dataset_cache | Clear and reload in-memory company dataset cache. | - | - |
Search Response Fields
runstatusstatsguidancedataset_freshnesspaginationrecovery_suggestionsjobs[].result_idjobs[].job_urljobs[].titlejobs[].companyjobs[].locationjobs[].sitejobs[].date_postedjobs[].description_fetchedjobs[].descriptionjobs[].description_excerptjobs[].salary_textjobs[].salary_currencyjobs[].salary_intervaljobs[].salary_min_amountjobs[].salary_max_amountjobs[].salary_sourcejobs[].job_typejobs[].job_leveljobs[].company_industryjobs[].job_functionjobs[].job_url_directjobs[].is_remotejobs[].employer_contactsjobs[].visa_countsjobs[].visas_sponsoredjobs[].visa_match_strengthjobs[].eligibility_reasonsjobs[].confidence_scorejobs[].confidence_model_versionjobs[].agent_guidance
Paths
dataset_default:data/companies.csvignored_companies_default:data/config/ignored_companies.jsonignored_jobs_default:data/config/ignored_jobs.jsonjob_management_db_default:data/app/visa_jobs.dbpipeline_manifest_default:data/pipeline/last_run.jsonsaved_jobs_default:data/config/saved_jobs.jsonsearch_runs_store_default:data/config/search_runs.jsonsearch_session_store_default:data/config/search_sessions.jsonuser_memory_blob_default:data/config/user_memory_blob.jsonuser_preferences_default:data/config/user_preferences.json
Deprecations
build_company_dataset_from_dol_disclosures->run_internal_dol_pipeline(soft_deprecated)
Raw Capabilities JSON
{
"capabilities_schema_version": "1.2.0",
"confidence_model_version": "v1.1.0-rules-go",
"defaults": {
"dataset_stale_after_days": 30,
"job_db_path": "data/app/visa_jobs.db",
"max_scan_results": 1200,
"max_search_sessions_per_user": 20,
"rate_limit_initial_backoff_seconds": 2,
"rate_limit_max_backoff_seconds": 30,
"rate_limit_retry_window_seconds": 180,
"scan_multiplier": 8,
"search_run_ttl_seconds": 21600,
"search_session_ttl_seconds": 21600,
"strictness_mode": "strict",
"tool_call_soft_timeout_seconds": 48
},
"deprecations": [
{
"name": "build_company_dataset_from_dol_disclosures",
"replacement": "run_internal_dol_pipeline",
"status": "soft_deprecated"
}
],
"design_decisions": {
"agent_is_reasoning_layer": true,
"background_search_runs_local_persistence": true,
"data_not_shared_or_sold": true,
"first_class_job_management": true,
"free_forever": true,
"fresh_job_search_per_query": true,
"ignored_companies_local_persistence": true,
"ignored_jobs_local_persistence": true,
"license": "MIT",
"llm_api_keys_required_by_mcp": false,
"llm_runtime_inside_mcp": false,
"no_fake_reviews_or_bot_marketing": true,
"proxies_used": false,
"rate_limit_backoff_retries": true,
"saved_jobs_local_persistence": true,
"search_sessions_local_persistence": true,
"strict_user_visa_match": false,
"strictness_modes_supported": [
"balanced",
"strict"
],
"supported_job_sites": [
"linkedin"
],
"visa_matching_optional": true
},
"pagination_contract": {
"next_step": "use pagination.next_offset to request the next page",
"offset_model": "offset is applied to accepted jobs, not raw scraped jobs",
"result_id_aliases": "use jobs[].result_id in save_job_for_later/ignore_job to avoid URL copy friction",
"scan_behavior": "server can increase raw scan depth when auto_expand_scan=true",
"session_behavior": "pass search_session.session_id for stable paging without redundant rescans"
},
"paths": {
"dataset_default": "data/companies.csv",
"ignored_companies_default": "data/config/ignored_companies.json",
"ignored_jobs_default": "data/config/ignored_jobs.json",
"job_management_db_default": "data/app/visa_jobs.db",
"pipeline_manifest_default": "data/pipeline/last_run.json",
"saved_jobs_default": "data/config/saved_jobs.json",
"search_runs_store_default": "data/config/search_runs.json",
"search_session_store_default": "data/config/search_sessions.json",
"user_memory_blob_default": "data/config/user_memory_blob.json",
"user_preferences_default": "data/config/user_preferences.json"
},
"rate_limit_contract": {
"failure_message": "asks agent to retry shortly when the retry window is exhausted",
"max_retry_window_seconds": 180,
"retry_behavior": "automatic exponential backoff on rate-limit errors (429/Too Many Requests)"
},
"required_before_search": {
"required_fields": [
"user_id"
],
"tool": "start_job_search"
},
"search_response_fields_for_agents": [
"run",
"status",
"stats",
"guidance",
"dataset_freshness",
"pagination",
"recovery_suggestions",
"jobs[].result_id",
"jobs[].job_url",
"jobs[].title",
"jobs[].company",
"jobs[].location",
"jobs[].site",
"jobs[].date_posted",
"jobs[].description_fetched",
"jobs[].description",
"jobs[].description_excerpt",
"jobs[].salary_text",
"jobs[].salary_currency",
"jobs[].salary_interval",
"jobs[].salary_min_amount",
"jobs[].salary_max_amount",
"jobs[].salary_source",
"jobs[].job_type",
"jobs[].job_level",
"jobs[].company_industry",
"jobs[].job_function",
"jobs[].job_url_direct",
"jobs[].is_remote",
"jobs[].employer_contacts",
"jobs[].visa_counts",
"jobs[].visas_sponsored",
"jobs[].visa_match_strength",
"jobs[].eligibility_reasons",
"jobs[].confidence_score",
"jobs[].confidence_model_version",
"jobs[].agent_guidance"
],
"server": "visa-jobs-mcp",
"tools": [
{
"description": "Return MCP capabilities, tools, and contracts for agent self-discovery.",
"name": "get_mcp_capabilities",
"required_inputs": []
},
{
"description": "Save the user's visa preferences for optional visa-specific matching.",
"name": "set_user_preferences",
"required_inputs": [
"user_id",
"preferred_visa_types"
]
},
{
"description": "Save urgency and work-mode constraints used for personalized guidance.",
"name": "set_user_constraints",
"required_inputs": [
"user_id"
]
},
{
"description": "Fetch the saved user preferences and constraints.",
"name": "get_user_preferences",
"required_inputs": [
"user_id"
]
},
{
"description": "Report whether the user and local dataset are ready for search.",
"name": "get_user_readiness",
"required_inputs": [
"user_id"
]
},
{
"description": "Return adjacent role titles to widen low-yield searches.",
"name": "find_related_titles",
"required_inputs": [
"job_title"
]
},
{
"description": "Append a profile memory line (skills, goals, fears, constraints).",
"name": "add_user_memory_line",
"required_inputs": [
"user_id",
"content"
]
},
{
"description": "Query the user's local memory blob with optional text filtering.",
"name": "query_user_memory_blob",
"required_inputs": [
"user_id"
]
},
{
"description": "Delete one memory line by id from the local blob.",
"name": "delete_user_memory_line",
"required_inputs": [
"user_id",
"line_id"
]
},
{
"description": "Save a job to the user's local shortlist for follow-up.",
"name": "save_job_for_later",
"optional_inputs": [
"job_url",
"result_id",
"session_id"
],
"required_inputs": [
"user_id"
]
},
{
"description": "List saved jobs in reverse-chronological order.",
"name": "list_saved_jobs",
"required_inputs": [
"user_id"
]
},
{
"description": "Remove one saved job from the local shortlist.",
"name": "delete_saved_job",
"required_inputs": [
"user_id",
"saved_job_id"
]
},
{
"description": "Hide one job from future results for this user.",
"name": "ignore_job",
"optional_inputs": [
"job_url",
"result_id",
"session_id"
],
"required_inputs": [
"user_id"
]
},
{
"description": "List ignored jobs in reverse-chronological order.",
"name": "list_ignored_jobs",
"required_inputs": [
"user_id"
]
},
{
"description": "Unhide a previously ignored job by id.",
"name": "unignore_job",
"required_inputs": [
"user_id",
"ignored_job_id"
]
},
{
"description": "Hide all jobs from a company in future searches.",
"name": "ignore_company",
"required_inputs": [
"user_id"
]
},
{
"description": "List ignored companies in reverse-chronological order.",
"name": "list_ignored_companies",
"required_inputs": [
"user_id"
]
},
{
"description": "Remove one company from the ignored list.",
"name": "unignore_company",
"required_inputs": [
"user_id",
"ignored_company_id"
]
},
{
"description": "Mark a job as applied and persist pipeline state.",
"name": "mark_job_applied",
"required_inputs": [
"user_id"
]
},
{
"description": "Update lifecycle stage for a tracked job (saved/applied/interview/etc).",
"name": "update_job_stage",
"required_inputs": [
"user_id",
"stage"
]
},
{
"description": "List tracked jobs filtered by lifecycle stage.",
"name": "list_jobs_by_stage",
"required_inputs": [
"user_id",
"stage"
]
},
{
"description": "Attach or append a note to a tracked job record.",
"name": "add_job_note",
"required_inputs": [
"user_id",
"note"
]
},
{
"description": "List recent stage transitions and lifecycle events.",
"name": "list_recent_job_events",
"required_inputs": [
"user_id"
]
},
{
"description": "Summarize tracked pipeline counts by stage for one user.",
"name": "get_job_pipeline_summary",
"required_inputs": [
"user_id"
]
},
{
"description": "Delete one cached search session or all sessions for a user.",
"name": "clear_search_session",
"required_inputs": [
"user_id"
]
},
{
"description": "Export all local records for a user across stores.",
"name": "export_user_data",
"required_inputs": [
"user_id"
]
},
{
"description": "Permanently delete all local records for a user.",
"name": "delete_user_data",
"required_inputs": [
"user_id",
"confirm"
]
},
{
"description": "Suggest best outreach channel/contact for a job.",
"name": "get_best_contact_strategy",
"required_inputs": [
"user_id"
]
},
{
"description": "Generate a practical outreach draft tailored to user and role.",
"name": "generate_outreach_message",
"required_inputs": [
"user_id"
]
},
{
"description": "Start a background job search without requiring visa preferences.",
"name": "start_job_search",
"required_inputs": [
"location",
"job_title",
"user_id"
]
},
{
"description": "Poll incremental progress/events for a background job search run.",
"name": "get_job_search_status",
"required_inputs": [
"user_id",
"run_id"
]
},
{
"description": "Fetch current result page from a background job search run.",
"name": "get_job_search_results",
"required_inputs": [
"user_id",
"run_id"
]
},
{
"description": "Request cancellation of an in-progress background job search run.",
"name": "cancel_job_search",
"required_inputs": [
"user_id",
"run_id"
]
},
{
"description": "Start a background search run for long scans.",
"name": "start_visa_job_search",
"required_inputs": [
"location",
"job_title",
"user_id"
]
},
{
"description": "Poll incremental progress/events for a background search run.",
"name": "get_visa_job_search_status",
"required_inputs": [
"user_id",
"run_id"
]
},
{
"description": "Fetch current result page from a background search run.",
"name": "get_visa_job_search_results",
"required_inputs": [
"user_id",
"run_id"
]
},
{
"description": "Request cancellation of an in-progress background run.",
"name": "cancel_visa_job_search",
"required_inputs": [
"user_id",
"run_id"
]
},
{
"description": "Discover latest DOL LCA/PERM disclosure sources.",
"name": "discover_latest_dol_disclosure_urls",
"required_inputs": []
},
{
"description": "Run internal pipeline to refresh sponsor-company dataset.",
"name": "run_internal_dol_pipeline",
"required_inputs": []
},
{
"description": "Clear and reload in-memory company dataset cache.",
"name": "refresh_company_dataset_cache",
"required_inputs": []
}
],
"version": "0.3.1"
}
Regenerate this section and website contract block with:
python3 scripts/generate_contract_docs.py
Validate generated blocks are current:
python3 scripts/generate_contract_docs.py --check
Manual CLI (optional)
Check binary version:
visa-jobs-mcp --version
Run the MCP server directly (for debugging):
visa-jobs-mcp
Run the internal DOL pipeline (maintainer workflow, from source checkout):
./scripts/run_internal_pipeline.sh
Troubleshooting
- If search returns no jobs, keep polling
get_visa_job_search_statusand callget_visa_job_search_resultsagain for the samerun_id. - If upstream rate limits happen, wait a few minutes and retry.
- If Homebrew install fails due missing release assets, retry after release workflows complete.
Data and Privacy
- Data is stored locally by default.
- No telemetry or external data selling.
- Sponsorship matching uses
data/companies.csvand DOL-based pipeline outputs.
For Maintainers
- Homebrew tap repository:
https://github.com/neosh11/homebrew-visa-jobs-mcp - Contributor guide:
AGENTS.md - Release workflow:
.github/workflows/build-release-binaries.yml - Local release artifact build:
./scripts/build_release_binaries.sh(native Go binary + bundleddata/companies.csv)
License
MIT. See LICENSE.
相關伺服器
Bright Data
贊助Discover, extract, and interact with the web - one interface powering automated access across the public internet.
Yahoo Finance
Interact with Yahoo Finance to get stock data, market news, and financial information using the yfinance Python library.
Puppeteer Real Browser
Enables powerful, detection-resistant browser automation for AI assistants using puppeteer-real-browser.
Skyvern
MCP Server to let Claude / your AI control the browser
Douyin MCP Server
Extract watermark-free video links and copy from Douyin.
Scrapfly
Scrapfly MCP Server gives AI agents a simple, unified way to scrape live web data with built-in anti-bot handling.
Booli MCP Server
Access Swedish real estate data from Booli.se through a GraphQL API.
scrape-do-mcp
MCP Server for Scrape.do - Web Scraping & Google Search with anti-bot bypass
RedNote MCP
Access and interact with content from Xiaohongshu (RedNote).
yt-dlp-mcp
Download video and audio from various platforms like YouTube, Facebook, and TikTok using yt-dlp.
Read Website Fast
Fast, token-efficient web content extraction that converts websites to clean Markdown. Features Mozilla Readability, smart caching, polite crawling with robots.txt support, and concurrent fetching with minimal dependencies.