unstop-mcp is a Python Model Context Protocol (MCP) server for discovering Unstop hackathons.
It exposes:
- MCP tools for searching hackathons, fetching event details, running location-based discovery, and managing cache state
- MCP resources for read-only snapshots and per-hackathon lookups
- MCP prompts that help an LLM plan searches, compare hackathons, and recommend relevant events
This repository is now MCP-first. The old direct-import Python wrapper API is not the supported public interface anymore.
stdiotransport only- Unstop hackathons only in v1
- Automatic caching of open hackathons for fast repeated lookups
- Detail enrichment for descriptions, rounds, contacts, registration counts, and views
- Optional location-based search using geocoding
- Deterministic unit tests with mocked upstream behavior
- Optional live smoke tests kept separate from the default suite
- Python 3.12+
uvrecommended for local development- Network access for real Unstop calls
git clone https://github.com/your-org/unstop-mcp.git
cd unstop-mcp
uv venv
source .venv/bin/activate
uv pip install -e .git clone https://github.com/your-org/unstop-mcp.git
cd unstop-mcp
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e .unstop-mcp --helpYou should see the stdio transport option.
Run the server locally over stdio:
unstop-mcpEquivalent module form:
python -m unstop_mcpThe server intentionally supports only:
unstop-mcp --transport stdioSearch Unstop hackathons with filters, sorting, pagination, and optional cache usage.
Arguments:
oppstatus:open | recent | expired | closedregion:online | offlinepayment:paid | unpaidteamsize:1 | 2 | 3usertype:college_students | fresher | professionals | school_studentssort:prize | days_leftdirection:asc | descsearch: free-text keyword searchpage:>= 1per_page:1-100use_cache:true | false
Returns a structured object with:
items: normalized hackathon summariespagination: total/current page/last page/per-page/has-morecache: cache freshness metadataapplied_filters: the validated input used for the call
Fetch full details for a single hackathon by numeric ID.
Arguments:
hackathon_id
Returns:
item: normalized detail viewcache: metadata describing current server cache state
Find offline hackathons near a location using geocoding and radius filtering.
Arguments:
locationradius_kmregionpaymentteamsizeusertypesearchsort:prize | days_left | distancedirectionpageper_page
Returns the same normalized structure as search_hackathons, plus:
location.search_locationlocation.search_radius_kmlocation.search_coordinates
Force a rebuild of the cached open-hackathon dataset.
Returns:
cache
Inspect cache state without rebuilding the full dataset.
Returns:
cache
Read-only JSON view of cache freshness, TTL, and item counts.
Read-only JSON snapshot of all cached open hackathons.
Read-only JSON detail view for a specific hackathon. The server serves cached data when possible and falls back to a direct detail fetch when needed.
Guides an LLM to gather missing user preferences, call the right search tool, and summarize the best matches.
Arguments:
user_goal
Guides an LLM to fetch multiple hackathons and compare them.
Arguments:
hackathon_ids
Guides an LLM on which tools and resources to call, in what order, for a discovery task.
Arguments:
user_request
Tool and resource results are normalized into stable JSON-friendly fields rather than returning raw upstream Unstop payloads as the primary contract.
Each hackathon item includes:
idtitlestatusregionis_paidpublic_urldescriptionprize_amountprize_summaryfiltersrequired_skillsorganisationaddressregistrationroundscontactsdistance_kmwhen applicable
This keeps the MCP contract predictable even if upstream response shapes vary.
Point your MCP client at this command:
unstop-mcpIf your client needs an absolute command path, use the one from your environment, for example:
/absolute/path/to/.venv/bin/unstop-mcpExample claude_desktop_config.json entry:
{
"mcpServers": {
"unstop": {
"command": "/absolute/path/to/.venv/bin/unstop-mcp",
"args": []
}
}
}Configure a local MCP server entry that launches:
{
"command": "/absolute/path/to/.venv/bin/unstop-mcp",
"args": []
}If your Codex setup manages MCP servers through a separate config file or UI, use the same command and no extra arguments.
src/unstop_mcp/server.py: FastMCP server definition and MCP registrationsrc/unstop_mcp/service.py: Unstop fetching, caching, normalization, and geocoding logicsrc/unstop_mcp/schemas.py: validated inputs and normalized output modelssrc/unstop_mcp/config.py: environment-driven runtime configurationtests/: unit, MCP registration, stdio smoke, and optional live tests
These are optional:
UNSTOP_MCP_TIMEOUTUNSTOP_MCP_MAX_RETRIESUNSTOP_MCP_RETRY_DELAYUNSTOP_MCP_CACHE_TTL_SECONDSUNSTOP_MCP_DETAIL_WORKERSUNSTOP_MCP_DETAIL_DELAYUNSTOP_MCP_GEOCACHE_PATHUNSTOP_MCP_USER_AGENT
- Searches for open hackathons can use an in-memory snapshot instead of hitting Unstop on every call
- The snapshot is rebuilt automatically when stale
refresh_cacheforces an immediate rebuild- The geocode cache is persisted to disk so repeated location lookups do not re-query the geocoder unnecessarily
- Location search depends on
geopy - Offline hackathons with explicit coordinates are used directly
- When coordinates are missing, the server tries multiple address variants and caches the result
- Online-only hackathons are excluded from radius searches unless the query explicitly requests
region="online"
Run the default test suite:
python -m pytest -qWhat the default suite covers:
- validation and parsing
- normalized search responses
- cache metadata and staleness behavior
- location filtering
- MCP tool/resource/prompt registration
- basic end-to-end
stdiostartup and tool listing
These are excluded by default:
UNSTOP_MCP_RUN_LIVE_TESTS=1 python -m pytest -q -m liveUse live tests only when you want to verify the current Unstop integration against the real network.
Useful local checks:
python -m pytest -q
python -m unstop_mcp --helpIf you already use an MCP Inspector, point it at the local unstop-mcp command and stdio transport to inspect registered tools, resources, and prompts interactively.
The server validates inputs before making upstream calls.
Common failure cases:
- invalid enum values such as unsupported
oppstatusorsort - empty or unresolvable location input
- transient or permanent upstream Unstop failures
- missing geocoding support for location search
When possible, tool failures are surfaced as clear MCP-facing validation or request errors.
stdioonly in v1- hackathons only in v1
- upstream Unstop fields and availability can change
- location accuracy depends on source address quality and geocoding quality
- cache refresh can take longer than simple detail fetches because it enriches open hackathons in bulk
If you want to add more Unstop opportunity types later, keep this split:
- add new upstream fetch/parse logic in
service.py - define new validated contracts in
schemas.py - register new MCP surfaces in
server.py
That keeps transport concerns separate from domain logic.
MIT