wemakedevs-mcp
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
wemakedevs-mcp
An MCP (Model Context Protocol) server that turns wemakedevs.org — a site with no documented public API — into 16 typed agent tools. Hackathons, timelines, resource hubs, rules, blogs, meetups, scholarships and careers become clean, structured JSON an AI agent can actually use. TinyFish does the real web reading on every call; there is no cached or canned data in the serving path.
Built for the TinyFish “Site → MCP” bounty. Not affiliated with WeMakeDevs.
Live status (2026-11-09): npm run health → 9/9 checks green against the live site; 31 unit tests green; 16/16 tools exercised live through stdio and HTTP. Both MCP protocol eras are served from one tool surface: legacy 2025-11-25 (initialize) and modern 2026-07-28 (server/discover, per-request metadata). Verified with the official MCP Inspector 2.13 in both eras, and on Node 20 (Alpine, Docker) as well as Node 24.
Why this exists
WeMakeDevs runs 30+ hackathons a year, meetups in 9+ cities, community scholarships and occasional hiring — and none of it is available as an API. If you want your coding agent to answer “what should I enter this month, what are the prizes, when is the deadline, and is registration still open?”, the web is the only interface. This server is that interface, and it is structured enough for any MCP client.
Related MCP server: serpdive
Install (from source)
The package is not published to npm yet, so the commands below run the built server directly. Requires Node.js ≥ 20 and a TinyFish API key (get one).
git clone https://github.com/Yash-2003P/wemakedevs-mcp.git
cd wemakedevs-mcp
npm ci
npm run build
export TINYFISH_API_KEY=sk-...
node dist/src/index.js # stdio MCP serverClaude Code
claude mcp add wemakedevs-mcp \
-e TINYFISH_API_KEY=$TINYFISH_API_KEY \
-- node /absolute/path/to/wemakedevs-mcp/dist/src/index.jsClaude Desktop / Cursor / Zed / other stdio clients
{
"mcpServers": {
"wemakedevs": {
"command": "node",
"args": ["/absolute/path/to/wemakedevs-mcp/dist/src/index.js"],
"env": { "TINYFISH_API_KEY": "sk-..." }
}
}
}VS Code
Add .vscode/mcp.json (or run MCP: Add Server):
{
"servers": {
"wemakedevs": {
"type": "stdio",
"command": "node",
"args": ["/absolute/path/to/wemakedevs-mcp/dist/src/index.js"],
"env": { "TINYFISH_API_KEY": "sk-..." }
}
}
}OpenCode
{
"mcp": {
"servers": {
"wemakedevs-mcp": {
"type": "local",
"command": ["node", "/absolute/path/to/wemakedevs-mcp/dist/src/index.js"],
"environment": { "TINYFISH_API_KEY": "sk-..." }
}
}
}
}ChatGPT
ChatGPT connectors expect a remote HTTP MCP endpoint with OAuth (or no auth). This repo ships both the stdio transport (default) and a Streamable HTTP transport (src/http.ts) that speaks both protocol eras, so the tools are reachable from remote hosts today. A bearer token is the only auth implemented, which is not the OAuth flow ChatGPT connectors require — put an OAuth flow (or an authenticated bridge) in front for ChatGPT. The tools themselves are client-agnostic.
Streamable HTTP (remote clients)
npm run build
HOST=127.0.0.1 PORT=3000 node dist/src/http.js # loopback, no token
HOST=0.0.0.0 MCP_HTTP_TOKEN=secret node dist/src/http.jsEndpoint: POST /mcp (stateless: no sessions, no standalone SSE). GET /health reports liveness and whether a token is required.
Protocol eras. One tool surface serves both:
Legacy 2025-11-25 — the
initializehandshake, answered by the SDK's stateless legacy path.Modern 2026-07-28 — no
initialize;server/discover, per-request_meta, and the requiredMCP-Protocol-Version/Mcp-Methodheaders. Modern clients can calltools/listdirectly once those headers are present.
This is verified with MCP Inspector 2.13 in --protocol-era legacy and --protocol-era modern, over both stdio and HTTP.
Safety defaults:
Binds
127.0.0.1unlessHOSTis set, and refuses to bind a non-loopback address withoutMCP_HTTP_TOKEN.Authorization: Bearer <MCP_HTTP_TOKEN>is compared withtimingSafeEqual.Origins are parsed, never prefix-matched. Loopback origins (
http://localhost:5173,https://127.0.0.1,[::1]) are allowed on any port; everything else must match an exactMCP_HTTP_ORIGINSentry.http://localhost.evil.comis rejected.Host is DNS-rebinding protected with exact values. The allowlist is built from the actual bind address and port plus
MCP_HTTP_HOSTS; the incomingHostheader is never added to its own allowlist. A spoofed Host gets403before the body is read.Bounds:
MCP_HTTP_MAX_BODY_BYTES(default 4 MiB) caps request bodies, andmaxToolInputElements: 256caps combined array/object elements in atools/callpayload.
Claude Code, as a remote client:
claude mcp add --transport http wemakedevs \
-H "Authorization: Bearer $MCP_HTTP_TOKEN" \
http://127.0.0.1:3000/mcpcurl -s -X POST http://127.0.0.1:3000/mcp \
-H "content-type: application/json" \
-H "accept: application/json, text/event-stream" \
-H "authorization: Bearer $MCP_HTTP_TOKEN" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | head -c 300Verify the whole path, including auth rejection:
TINYFISH_API_KEY=sk-... npm run smoke:httpEnv var | Default | Meaning |
|
| Bind address. Non-loopback requires a token. |
|
| Bind port. |
| (unset) | Required bearer token. Unset = loopback-only, no auth. |
| (unset) | Extra allowed |
| (unset) | Extra allowed |
|
| Maximum POST body size. |
Tools
Tool | What it does |
| List events (upcoming/ongoing/past), filter by |
| Full structured detail of one event: hero meta (prize pool, when/where/teams), page sections (challenge, prizes, tracks, judging, schedule…), FAQ, outbound links. Use |
| The Official Rules page as structured sections — eligibility, exclusions, submissions, prizes, disputes. |
| The event timeline: every milestone with the site’s display text ( |
| The resource hub as grouped links (Start here, Build your workflow, Go deeper, Contribute, Videos…) with each link’s note, plus CLI one-liners shown on the page. |
| The registration panel is client-side and cannot be read with a plain fetch. TinyFish Agent opens a real browser, waits for the panel to resolve and reports |
| Meetups with venue/city, date text and Luma link. Filter by |
| Live openings from |
| Current scholarship program: coverage (flights, hotel, pass, local transport, visa), application status, past winners, FAQs. |
| Posts from the live blog RSS feed ( |
| Full-text search across the same feed (title, summary, author, tags). |
| One full post: RSS metadata merged with the live article body — paragraphs, every outbound link, author, date, tags. |
| Hackathon starts and submission closes within the next N days — the “what should I enter?” answer. |
| One feed of what is new and what is coming: blog posts from the last N days plus hackathon start/end dates in the next N days. |
| Diff the live listing against the previous observation saved on this machine: added, removed and field-level changes (title, prize, dates, location, format). Optional single-event scope. |
| Live self-check of every parser (nine checks). Fails loudly. |
All tools return structured JSON (structuredContent) validated against zod schemas. Tools never return raw HTML dumps, and errors are typed and human-readable: CONFIG, FETCH_FAILED, AGENT_FAILED, NOT_FOUND, SITE_CHANGED, VALIDATION_FAILED, PARSE_FAILED.
Real multi-step run (live, 2026-11-07)
A fresh OpenCode session asked: "I want to enter a WeMakeDevs hackathon this month." It found and chained seven tools:
→ list_hackathons({status: "ongoing"}) 1 event Flip the Switch, Nov 4–7
→ list_hackathons({status: "upcoming"}) 3 events Cloud Native, Festive Hacks, Toggle Wars
→ get_hackathon({hackathon: "switch"}) prizePool "$10,000" · Solo or up to 4 · Online
→ get_hackathon_schedule({hackathon: "switch"})
6 milestones · submissions close 2026-11-07T23:59+05:30
→ get_hackathon_resources({hackathon: "switch"})
5 groups · 21 links · 2 CLI snippets
→ check_registration_status({hackathon: "switch"}) // TinyFish Agent, ≈41 s
{ state: "open", registeredCount: 7690, closesAt: null }
→ search_blog({query: "switch"}) 1 postThe full log — tool outputs, tables, the 21 resolved resource links and the agent's verbatim final answer — is in docs/DEMO.md.
The run surfaced a real site inconsistency: the listing card says $8000 while the detail page says $10,000. The agent reported both and refused to pick one; the tools return each source verbatim rather than silently normalizing it.
Across the verification session, all 16 tools were exercised against the live site through a real MCP stdio client (including get_hackathon_rules, get_blog_post, latest_updates, upcoming_deadlines, get_hackathon_changes, list_meetups, list_open_roles, list_scholarships and health_check).
How TinyFish is used
Exactly two TinyFish endpoints, both load-bearing. No ceremonial calls, no padding.
TinyFish Fetch — every page read
POST https://api.fetch.tinyfish.ai with ttl: 0 so every call is live.
Verbatim page reads. Fetch’s default output is a readability-style summary that drops scripts and attributes. The server asks for
include_selectors: ["body"], which returns the matched element’s content verbatim. This is what makes the section AST, FAQ parsing, careers cards and resource links work.The listing is parsed from the rendered cards. Fetch strips
<script>blocks even in verbatim mode, so the Next.js flight payload (self.__next_f,"cards") is not reachable. The server parses the server-rendered card DOM instead:<a><h3>title</h3><p>prize</p></a>plus the location/date chips. Display dates ("Nov 9–10","Sep 30 – Oct 31","February 2026") are converted to ISO dates; the site prints a year only for non-current years, so a missing year means the current year. This limitation is surfaced in everynoteand enforced by the health check.RSS is read raw.
/blogs/rss.xmlis fetched without a selector because selector scoping mangles<link>tags into HTML void elements. Fetch also lowercases tag names (<pubdate>), so the RSS parser is deliberately case-insensitive and tolerant of both raw XML and Fetch’s HTML-serialized form.Detail, rules, schedule, resources, meetups, careers, scholarships all use the same verbatim body read plus a page-specific cheerio parser.
TinyFish Agent — the one thing Fetch cannot see
POST https://agent.tinyfish.ai/v1/automation/run is used only for check_registration_status: the registration panel renders client-side behind “Loading…”, so a page read sees the placeholder. The agent opens a real browser, waits for the panel, and returns schema-validated JSON (state, opensAt, closesAt, registeredCount, notes). The goal is strictly read-only: observe, never sign in, register or click an action.
Live lesson baked into the code: TinyFish Agent’s output_schema rejects additionalProperties and description, so the schema is the minimal JSON Schema subset it accepts. Live runs took 39 s, 41 s and 62 s; the first returned state=open, closesAt="Nov 7, 2026", registeredCount=7689.
Nothing is pre-scraped, cached at bake time, or mocked in the serving path. TINYFISH_API_KEY is required; without it every tool fails fast with a clear CONFIG error.
Health check (when the site changes, this breaks loudly)
npm run health # live via TinyFish (needs TINYFISH_API_KEY)
WEMAKEDEVS_MCP_HEALTH_OFFLINE=1 npm run health # parse committed fixtures, no network
npm run health -- hackathons-listing careers # a subsetNine checks, all required:
Check | Asserts |
| ≥ 10 cards, embedded source or rendered DOM, ≥ 1 ISO start date, sentinel event present |
| h1, ≥ 4 |
| ≥ 4 milestones, at least one machine-readable |
| ≥ 3 groups, ≥ 8 links |
| official-rules legal markers |
| cards parse, or an explicit empty state |
| roles parse, or the explicit “No open roles right now” empty state |
| program, coverage list and FAQs all parse |
| ≥ 1 item, parseable |
The CLI prints a per-check JSON report and exits non-zero on any failure. The MCP tool health_check exposes the same checks to agents (isError: true when unhealthy). CI runs build + tests + offline health on every push; a weekly workflow runs the live check when secrets.TINYFISH_API_KEY is configured.
Change history (diffs)
get_hackathon_changes compares the live listing with the last observation on this machine. The first call creates a baseline; later calls report events added, removed, or changed. Only prior observations are stored — every value in the current side of the diff comes from a fresh TinyFish Fetch.
Default state file: ~/.cache/wemakedevs-mcp/hackathon-snapshots.json. Override the directory with WEMAKEDEVS_MCP_STATE_DIR. Pass update_baseline: false to compare against the same baseline repeatedly.
Development
npm ci
npm test # builds, then runs 31 unit tests against test/fixtures/*
TINYFISH_API_KEY=sk-... npm run smoke # live: asserts the 16-tool surface, calls every fast tool
TINYFISH_API_KEY=sk-... npm run smoke:all # also the slow Agent call (~40–70 s)
TINYFISH_API_KEY=sk-... npm run smoke:http # starts the Streamable HTTP server, checks auth + a live callnpm run smoke starts the built server over stdio with the real MCP SDK client, checks that all 16 tools are advertised, then calls each one once against the live site. It is the fastest way to verify a fresh install (and doubles as a client example).
test/fixtures/ holds public page snapshots captured 2026-11-05 (plus the RSS feed, schedule, resources and a blog post). Parser tests assert on those; live drift is caught by npm run health, not by silently serving stale data.
Verified environments (2026-11-09):
Node 24.21.0 on the host and Node 20.20.2 on Alpine (Docker):
npm ci→npm test→ 31/31; offline health 9/9.MCP Inspector 2.13.0, both
--protocol-era legacyand--protocol-era modern, over stdio and HTTP: 16 tools,tools/list --strict→ 0 errors, livetools/callin modern mode.npm run smoke15/15 fast tools;npm run smoke:all16/16 including the Agent call;npm run smoke:httpauth rejection + 16 tools + a live call.Live health 9/9; spoofed
Hostand lookalikeOriginrequests rejected with 403 on the running server.
Limitations (honest ones)
Listing dates are inferred from display text. The site does not expose ISO dates in the rendered listing (scripts are stripped by Fetch). A display date without a year is assumed to be the current year; the site prints a year for other years.
dateTextis always returned verbatim alongside the inferred ISO dates.The listing parser depends on the card DOM. If WeMakeDevs redesigns the event cards,
health_checkfails with the exact check and reason instead of returning partial garbage.Agent latency is 20–120 s. MCP clients must allow a request timeout of at least ~120 s for
check_registration_status; the server itself has no timeout. Observed live: 39 s, 41 s and 62 s — the last one exceeds the SDK's default 60 s client timeout.npm run smoke:allsets 180 s.Meetup dates are display text (“Oct 27”), not ISO; they are excluded from
upcoming_deadlines./careerscan legitimately be empty. The live page said “No open roles right now” during the final run; the tool returns[]with a note rather than inventing roles.Archive and external events. Older events on
archive.wemakedevs.orgare listed and parsed best-effort. External events (Devpost/Luma) are listed with their URLs;get_hackathonrefuses non-WeMakeDevs hosts by design.Node package not published. Install from source until an npm release exists.
Health sentinel defaults to
switch; override withHEALTH_SENTINEL_SLUGif that page is retired.The HTTP transport is stateless and POST-only. No sessions, no standalone SSE stream, no resumability — deliberate for a read-only tool set. Each POST gets a fresh
McpServer; both protocol eras share the same registrations.Modern clients must send the spec's headers. A 2026-07-28 request without
MCP-Protocol-VersionandMcp-Methodis rejected by the SDK with a400that names the missing header. This is protocol-correct, not a bug.Auth is a bearer token only. That covers Claude Code's
-Hheader and other Streamable HTTP clients; it is not the OAuth flow ChatGPT connectors expect. No OAuth server is implemented.Browser CORS is closed by default. A request with an unblessed
Origingets 403. Loopback origins on any port are allowed; other browser origins need an exactMCP_HTTP_ORIGINSentry.Nullable output fields use JSON Schema type arrays. Inspector
--strictreports 52 portability warnings (0 errors) because zod emitstype: ["string","null"]rather thananyOf. Hosts that accept JSON Schema 2020-12 are unaffected; a client that maps schemas onto a single-type OpenAPI dialect may drop the null branch.
Why there is no UI
MCP Apps is an official extension (spec 2026-01-26): a server declares a ui:// resource, and a supporting host renders it in a sandboxed iframe. It is optional, and this server deliberately does not ship one:
Every tool already returns
structuredContentplus a text rendering, which Claude, Cursor, OpenCode, Inspector and generic MCP clients display without any server-side UI.MCP Apps only works in hosts that implement the extension; a UI would make the server less universal, not more.
The bounty scores typed tools and real TinyFish coverage. A timeline iframe would add a bundle, CSP and
postMessagesurface without adding a single capability or TinyFish call.The only genuinely useful future view — a hackathon timeline/registration badge — is presentation over data the tools already expose. It can be added later without changing the tool contracts.
The full decision record is in docs/UI-ASSESSMENT.md.
Try it without a TinyFish key (local stub)
For demos and development you can run every tool against committed fixtures — clearly labeled, not TinyFish, not live web:
npm ci && npm run build
npm run stub & # frozen fixtures on 127.0.0.1:8788
TINYFISH_API_KEY=stub \
TINYFISH_FETCH_BASE_URL=http://127.0.0.1:8788 \
TINYFISH_AGENT_RUN_URL=http://127.0.0.1:8788/v1/automation/run \
node dist/src/index.js # stdio MCP server, fully backed by fixturesEvery tool works here EXCEPT the data is frozen (2026-11-05 snapshot) and health_check reports mode: "stub". The stub never runs in production — the overrides are opt-in only.
Related
TinyFish docs: https://docs.tinyfish.ai
WeMakeDevs: https://www.wemakedevs.org
Blog RSS: https://www.wemakedevs.org/blogs/rss.xml
License
MIT
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
This server cannot be deployed
Maintenance
Related MCP Connectors
Web data for agents: YouTube transcripts, screenshots, Google News, WHOIS, jobs, tech stack, more.
Give your agent live data from Twitter, Reddit, the web and GitHub. No API keys, no scraping stack.
Live web data for AI agents: structured JSON from 248 websites through one API key.
Direct access to 60+ scraping and search tools. Extract structured data from Google (Search, Maps, Trends), Amazon, Airbnb, Social Media, and any web page directly into your AI agent.
Related MCP Servers
- FlicenseNot gradedqualityCmaintenanceProvides AI agents with 6 tools for searching Hugging Face models, GitHub trending repos, analyzing GitHub repositories, fetching dev.to articles, Show HN launches, and Product Hunt daily launches. Built for dev tooling research and AI ecosystem analysis.-
- AlicenseNot gradedqualityDmaintenanceProvides AI coding agents with a live web search tool that returns extracted, answer-ready text from web pages. Enables real-time information retrieval without any local installation or maintenance.6 npmMIT
- AlicenseAqualityCmaintenanceEnables AI assistants and coding agents to inspect builder status and hackathon details, generate competition-grade project ideas, scaffold repositories, and retrieve submission guidelines. It also supports live event intelligence, profile tracking, and official rules access through MCP tools, resources, and prompts.7MIT

Stophy MCPofficial
AlicenseNot gradedqualityCmaintenanceGive your agents the live web. One API to search the web, read what people say, and look up products, places, jobs and homes.74 npmMIT