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.
Playwright Server
Automate web browsers and perform web scraping tasks using the Playwright framework.
ElToque MCP Server
Fetches USD and EUR prices from the Cuban parallel market via eltoque.com.
freesound-mcp
A Model Context Protocol (MCP) server that enables AI applications to search and download audio resources from the Freesound platform via natural language commands.
Puppeteer Real Browser
Enables powerful, detection-resistant browser automation for AI assistants using puppeteer-real-browser.
Extract Developer & LLM Docs
Extract documentation for AI agents from any site with llms.txt support. Features MCP server, REST API, batch processing, and multiple export formats.
Fetch MCP Server
Fetches web content from a URL and converts it from HTML to markdown for easier consumption by LLMs.
MCP Browser Use Secure
A secure MCP server for browser automation with enhanced security features like multi-layered protection and session isolation.
Patchright Lite MCP Server
A server that wraps the Patchright SDK to provide stealth browser automation for AI models.
Opengraph.io
Opengraph data, web scraping, screenshot features in a handy MCP tool
Crawl4AI MCP Server
An MCP server for advanced web crawling, content extraction, and AI-powered analysis using the crawl4ai library.