import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({ name: "jobs", version: "2.0.0" });
// Track last search time for job alerts
let lastUpworkCheck = 0;
// Proposal templates
const PROPOSALS: Record<string, { match: string[]; body: string }> = {
A: {
match: ["mcp", "model context protocol", "mcp server", "mcp integration"],
body: `I've built production MCP servers with 177+ tools, OAuth2 auth flows, and multi-service integrations. This is my primary focus, not a side skill.
Here's what I can deliver for this project:
- Custom MCP server in TypeScript using the official @modelcontextprotocol/sdk
- Clean tool definitions with Zod input validation
- OAuth2 integration where needed (Google, Discord, Stripe, etc.)
- Compatible with Claude Code, Claude Desktop, Cursor, and any MCP client
- Full source code, setup docs, and deployment instructions
Relevant work I've shipped:
- 177-tool MCP server for Discord community management (moderation, tickets, analytics, admin commands)
- Gmail MCP server with OAuth2, token auto-refresh, full read/search/send
- Financial data MCP server with transaction analysis, spending breakdowns, and anomaly detection
I work fast and I communicate clearly. Happy to do a quick call to scope this out, or I can start from your requirements doc if you have one.
GitHub: github.com/0xCybin`,
},
B: {
match: ["ai agent", "autonomous", "claude api", "anthropic", "ai pipeline", "llm", "ai automation"],
body: `I build AI agents that run in production, not demos. My most recent system is an autonomous Discord agent that handles moderation, customer support tickets, and admin commands with zero human intervention.
For your project, I can deliver:
- Event-driven AI pipeline with configurable processing stages
- Claude API / Anthropic SDK integration with proper error handling and retries
- MCP (Model Context Protocol) tool integration so your agent can interact with external services
- Persistent state management (SQLite or PostgreSQL)
- Deployment-ready with systemd service files and backup scripts
What I've built:
- Autonomous Discord agent: event pipeline > AI classifier > escalation engine > tool executor (178 internal tools)
- Document analysis pipeline with NLP entity extraction and vector search
- AI-powered ticket system with debounced responses, priority routing, and CSAT feedback
I don't over-scope or under-deliver. Let me know the specifics of what you need and I'll give you a realistic timeline.
GitHub: github.com/0xCybin`,
},
C: {
match: ["discord bot", "discord", "discord.js", "discord server"],
body: `I've been building on the Discord API for 2+ years. My flagship bot has 177 management tools, AI moderation, a full ticket system, and natural language admin commands.
For your project I can deliver:
- Custom Discord bot built from scratch with discord.js v14 and TypeScript
- AI-powered features using Claude API (moderation, auto-responses, command parsing)
- Ticket system, welcome flows, role management, analytics -- whatever you need
- Clean codebase with proper error handling, not a template hack
- Hosting setup on your VPS with systemd for auto-restart
I ship fast and I don't disappear. You'll get source code, documentation, and deployment support.
GitHub: github.com/0xCybin/argus`,
},
D: {
match: ["typescript", "python", "backend", "api", "node.js", "nodejs", "full stack", "fullstack"],
body: `I specialize in AI tooling and backend systems in TypeScript and Python. I've shipped production systems with autonomous agents, 177-tool API platforms, and real-time event pipelines.
I can help with:
- Backend development in TypeScript or Python
- AI/LLM integration (Claude API, Anthropic SDK, MCP)
- Database design and management (SQLite, PostgreSQL)
- API development and third-party integrations
- Chrome extensions (Manifest V3)
- Deployment and infrastructure setup
I'm self-taught and I ship working software. No fluff, no over-engineering, no ghosting. Happy to discuss scope and timeline whenever works for you.
GitHub: github.com/0xCybin`,
},
};
// RemoteOK API - free, no auth, JSON
server.tool(
"search_remote_jobs",
"Search remote jobs from RemoteOK. Returns title, company, tags, salary, URL.",
{
tags: z
.string()
.optional()
.describe("Comma-separated tags to filter (e.g. 'typescript,ai,mcp,python,discord')"),
limit: z
.number()
.min(1)
.max(50)
.default(20)
.describe("Max results to return"),
},
async ({ tags, limit }) => {
const url = tags
? `https://remoteok.com/api?tags=${encodeURIComponent(tags)}`
: "https://remoteok.com/api";
const res = await fetch(url, {
headers: { "User-Agent": "jobs-mcp/1.0" },
});
if (!res.ok) {
return { content: [{ type: "text" as const, text: `RemoteOK API error: ${res.status}` }] };
}
const data = await res.json();
// First element is metadata, skip it
const jobs = data.slice(1, limit + 1);
if (!jobs.length) {
return { content: [{ type: "text" as const, text: "No jobs found for those tags." }] };
}
const text = jobs
.map(
(j: any, i: number) =>
`[${i + 1}] ${j.position}\n Company: ${j.company}\n Tags: ${(j.tags || []).join(", ")}\n Salary: ${j.salary_min ? `$${j.salary_min}-$${j.salary_max}` : "Not listed"}\n Posted: ${j.date}\n URL: ${j.url}\n Apply: ${j.apply_url || j.url}`
)
.join("\n\n");
return { content: [{ type: "text" as const, text }] };
}
);
// HackerNews Who's Hiring - Algolia API, free, no auth
server.tool(
"search_hn_jobs",
"Search Hacker News 'Who is Hiring' threads for job posts. Good for startup jobs.",
{
query: z
.string()
.describe("Search query (e.g. 'MCP typescript', 'AI agent remote', 'discord bot')"),
limit: z
.number()
.min(1)
.max(30)
.default(15)
.describe("Max results to return"),
},
async ({ query, limit }) => {
// Search HN comments tagged as job posts
const url = `https://hn.algolia.com/api/v1/search?query=${encodeURIComponent(query)}&tags=comment,ask_hn&hitsPerPage=${limit}`;
const res = await fetch(url);
if (!res.ok) {
return { content: [{ type: "text" as const, text: `HN API error: ${res.status}` }] };
}
const data = await res.json();
const hits = data.hits || [];
if (!hits.length) {
return { content: [{ type: "text" as const, text: "No HN job posts found for that query." }] };
}
const text = hits
.map((h: any, i: number) => {
const comment = (h.comment_text || "")
.replace(/<[^>]+>/g, " ")
.replace(/\s+/g, " ")
.trim()
.slice(0, 500);
return `[${i + 1}] ${comment}\n Date: ${h.created_at}\n Link: https://news.ycombinator.com/item?id=${h.objectID}`;
})
.join("\n\n---\n\n");
return { content: [{ type: "text" as const, text }] };
}
);
// GitHub Jobs via Google search (structured query)
server.tool(
"search_github_jobs",
"Search GitHub repositories and discussions for job postings related to a technology.",
{
query: z
.string()
.describe("Technology or role to search for (e.g. 'MCP server developer', 'discord bot')"),
},
async ({ query }) => {
// Use GitHub API to search for repos/issues mentioning hiring
const url = `https://api.github.com/search/issues?q=${encodeURIComponent(query + " hiring OR job OR freelance")}&sort=created&order=desc&per_page=10`;
const res = await fetch(url, {
headers: {
"User-Agent": "jobs-mcp/1.0",
Accept: "application/vnd.github.v3+json",
},
});
if (!res.ok) {
return { content: [{ type: "text" as const, text: `GitHub API error: ${res.status}` }] };
}
const data = await res.json();
const items = data.items || [];
if (!items.length) {
return { content: [{ type: "text" as const, text: "No GitHub job posts found." }] };
}
const text = items
.map(
(item: any, i: number) =>
`[${i + 1}] ${item.title}\n Repo: ${item.repository_url?.replace("https://api.github.com/repos/", "")}\n Date: ${item.created_at}\n URL: ${item.html_url}`
)
.join("\n\n");
return { content: [{ type: "text" as const, text }] };
}
);
// Arbeitnow API - free, no auth, JSON, includes remote jobs
server.tool(
"search_arbeitnow_jobs",
"Search Arbeitnow for remote and on-site jobs. Good international coverage.",
{
query: z
.string()
.describe("Search query (e.g. 'typescript AI', 'MCP developer', 'discord')"),
remote: z
.boolean()
.default(true)
.describe("Only show remote jobs"),
limit: z
.number()
.min(1)
.max(30)
.default(15)
.describe("Max results"),
},
async ({ query, remote, limit }) => {
const url = `https://www.arbeitnow.com/api/job-board-api`;
const res = await fetch(url, {
headers: { "User-Agent": "jobs-mcp/1.0" },
});
if (!res.ok) {
return { content: [{ type: "text" as const, text: `Arbeitnow API error: ${res.status}` }] };
}
const data = await res.json();
const queryLower = query.toLowerCase();
let jobs = (data.data || []).filter((j: any) => {
const text = `${j.title} ${j.description} ${(j.tags || []).join(" ")}`.toLowerCase();
const matchesQuery = queryLower.split(" ").some((q: string) => text.includes(q));
const matchesRemote = remote ? j.remote === true : true;
return matchesQuery && matchesRemote;
});
jobs = jobs.slice(0, limit);
if (!jobs.length) {
return { content: [{ type: "text" as const, text: "No jobs found matching that query." }] };
}
const text = jobs
.map(
(j: any, i: number) =>
`[${i + 1}] ${j.title}\n Company: ${j.company_name}\n Location: ${j.location}${j.remote ? " (Remote)" : ""}\n Tags: ${(j.tags || []).join(", ")}\n Posted: ${j.created_at}\n URL: ${j.url}`
)
.join("\n\n");
return { content: [{ type: "text" as const, text }] };
}
);
// Simple saved searches / profile for context
server.tool(
"my_profile",
"Returns your job search profile and skills for use in proposals and applications.",
{},
async () => {
const profile = `Name: John Evans
Email: 0xcybin@gmail.com
Phone: (817) 966-7023
Location: Grand Prairie, TX (open to remote)
GitHub: github.com/0xCybin
Target roles: MCP Developer, AI Engineer, TypeScript Developer, Discord Bot Developer, AI Agent Builder
Key skills: TypeScript, Python, MCP (Model Context Protocol), Claude API, Anthropic SDK, discord.js, SQLite, PostgreSQL, OAuth2, Docker, systemd, VPS deployment
Top projects:
- Argus: 177-tool MCP server + autonomous Discord agent (TypeScript, discord.js, SQLite, Claude API)
- Gmail MCP: OAuth2 email integration server (read/search/send)
- Financial MCP: Bank transaction analysis and budgeting tools
- Daemon: AI agent launcher with Discord bridge and voice activation
- watchdog-pipeline: Document analysis with NLP and vector search
Experience: Self-taught, shipping since 2023. No formal degree. Strong in AI tooling, backend systems, and MCP architecture.`;
return { content: [{ type: "text" as const, text: profile }] };
}
);
// Upwork search via HTML scraping (RSS feeds are dead as of 2026)
server.tool(
"search_upwork_jobs",
"Search Upwork for freelance jobs via RSS feed. Returns title, budget, posted date, description snippet, and apply URL.",
{
query: z
.string()
.default("MCP server")
.describe("Search query (e.g. 'MCP server', 'Claude API', 'AI agent TypeScript', 'discord bot')"),
limit: z
.number()
.min(1)
.max(30)
.default(15)
.describe("Max results to return"),
},
async ({ query, limit }) => {
const searchUrl = `https://www.upwork.com/nx/search/jobs/?q=${encodeURIComponent(query)}&sort=recency&per_page=${limit}`;
const res = await fetch(searchUrl, {
headers: {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
"Accept": "text/html,application/xhtml+xml",
"Accept-Language": "en-US,en;q=0.9",
},
});
if (!res.ok) {
return { content: [{ type: "text" as const, text: `Upwork search error: ${res.status}. Use web search for "site:upwork.com ${query}" as fallback.` }] };
}
const html = await res.text();
// Try to extract job data from the page's embedded JSON
const jobs: { title: string; link: string; description: string; budget: string }[] = [];
// Method 1: Look for job section titles and links
const titleRegex = /data-test="JobTile"[\s\S]*?<a[^>]*href="([^"]*)"[^>]*>([^<]+)<\/a>/g;
let match;
while ((match = titleRegex.exec(html)) !== null && jobs.length < limit) {
jobs.push({
title: match[2].trim(),
link: match[1].startsWith("http") ? match[1] : `https://www.upwork.com${match[1]}`,
description: "",
budget: "See listing",
});
}
// Method 2: Look for JSON-LD or embedded data
if (jobs.length === 0) {
const jsonMatch = html.match(/window\.__INITIAL_DATA__\s*=\s*({[\s\S]*?});/);
if (jsonMatch) {
try {
const data = JSON.parse(jsonMatch[1]);
const searchResults = data?.searchResults?.jobs || data?.jobs || [];
for (const j of searchResults.slice(0, limit)) {
jobs.push({
title: j.title || j.name || "Untitled",
link: j.ciphertext ? `https://www.upwork.com/freelance-jobs/apply/${j.ciphertext}/` : "",
description: (j.description || j.snippet || "").slice(0, 300),
budget: j.amount?.amount ? `$${j.amount.amount}` : j.hourlyBudget ? `$${j.hourlyBudget.min}-$${j.hourlyBudget.max}/hr` : "Not listed",
});
}
} catch {}
}
}
// Method 3: Simple link extraction fallback
if (jobs.length === 0) {
const linkRegex = /href="(\/freelance-jobs\/apply\/[^"]+)"[^>]*>([^<]+)</g;
while ((match = linkRegex.exec(html)) !== null && jobs.length < limit) {
const title = match[2].trim();
if (title.length > 10) {
jobs.push({
title,
link: `https://www.upwork.com${match[1]}`,
description: "",
budget: "See listing",
});
}
}
}
lastUpworkCheck = Date.now();
if (!jobs.length) {
// Fallback: return search URL so user/agent can check manually
return { content: [{ type: "text" as const, text: `Could not parse Upwork results for "${query}". Direct search link:\n${searchUrl}\n\nTip: Use web search for "site:upwork.com ${query}" as alternative.` }] };
}
const text = jobs
.map(
(j, i) =>
`[${i + 1}] ${j.title}\n Budget: ${j.budget}\n URL: ${j.link}${j.description ? `\n Preview: ${j.description.slice(0, 200)}...` : ""}`
)
.join("\n\n");
return { content: [{ type: "text" as const, text: `Found ${jobs.length} Upwork jobs for "${query}":\n\n${text}` }] };
}
);
// Draft a proposal based on job description
server.tool(
"draft_proposal",
"Draft a customized Upwork proposal based on a job description. Picks the best template (MCP/AI Agent/Discord/General) and writes a custom opening referencing the specific job.",
{
job_title: z
.string()
.describe("The job title from Upwork"),
job_description: z
.string()
.describe("The full job description text. Paste the whole thing."),
bid_amount: z
.string()
.optional()
.describe("Optional bid amount to include (e.g. '$500', '$40/hr')"),
},
async ({ job_title, job_description, bid_amount }) => {
const combined = `${job_title} ${job_description}`.toLowerCase();
// Score each template against the job
let bestKey = "D";
let bestScore = 0;
for (const [key, tmpl] of Object.entries(PROPOSALS)) {
const score = tmpl.match.reduce((s, keyword) => {
return s + (combined.includes(keyword) ? (keyword.length > 5 ? 3 : 1) : 0);
}, 0);
if (score > bestScore) {
bestScore = score;
bestKey = key;
}
}
const template = PROPOSALS[bestKey];
// Extract key details from job for custom opening
const details: string[] = [];
if (combined.includes("mcp")) details.push("MCP");
if (combined.includes("claude") || combined.includes("anthropic")) details.push("Claude/Anthropic");
if (combined.includes("discord")) details.push("Discord");
if (combined.includes("oauth") || combined.includes("auth")) details.push("OAuth");
if (combined.includes("typescript") || combined.includes("node")) details.push("TypeScript");
if (combined.includes("python")) details.push("Python");
if (combined.includes("api")) details.push("API integration");
if (combined.includes("database") || combined.includes("sql")) details.push("database");
const techMention = details.length > 0
? `I see you need ${details.slice(0, 3).join(", ")} work -- that's exactly what I specialize in.`
: `This is right in my wheelhouse.`;
const customOpening = `Re: ${job_title}\n\n${techMention}\n\n`;
const bidLine = bid_amount ? `\n\nProposed rate: ${bid_amount}` : "";
const proposal = `${customOpening}${template.body}${bidLine}`;
return {
content: [{
type: "text" as const,
text: `--- PROPOSAL (Template ${bestKey}) ---\n\n${proposal}\n\n--- END ---\n\nTemplate used: ${bestKey} (score: ${bestScore})\nMatched keywords: ${details.join(", ") || "general match"}\n\nCopy everything between the --- markers. Customize the first 2 lines to reference something specific from their job post.`,
}],
};
}
);
// Multi-source job scan
server.tool(
"job_scan",
"Scan multiple job sources at once for MCP, AI, TypeScript, and Discord jobs. Returns results from Upwork, RemoteOK, and HN. Use this for a daily job check.",
{
keywords: z
.string()
.default("MCP server,Claude API,AI agent TypeScript,discord bot AI")
.describe("Comma-separated search terms to scan across all sources"),
},
async ({ keywords }) => {
const terms = keywords.split(",").map((k) => k.trim());
const allResults: string[] = [];
// Scan Upwork for each term via HTML scraping
for (const term of terms) {
try {
const searchUrl = `https://www.upwork.com/nx/search/jobs/?q=${encodeURIComponent(term)}&sort=recency&per_page=5`;
const res = await fetch(searchUrl, {
headers: {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
"Accept": "text/html,application/xhtml+xml",
"Accept-Language": "en-US,en;q=0.9",
},
});
if (!res.ok) continue;
const html = await res.text();
const linkRegex = /href="(\/freelance-jobs\/apply\/[^"]+)"[^>]*>([^<]+)</g;
let match;
let count = 0;
while ((match = linkRegex.exec(html)) !== null && count < 5) {
const title = match[2].trim();
if (title.length > 10) {
allResults.push(`[Upwork] ${title}\n Search: "${term}"\n URL: https://www.upwork.com${match[1]}`);
count++;
}
}
} catch {}
}
// Scan RemoteOK
try {
const res = await fetch("https://remoteok.com/api", { headers: { "User-Agent": "jobs-mcp/2.0" } });
if (res.ok) {
const data = await res.json();
const jobs = data.slice(1, 50);
const keywordsLower = keywords.toLowerCase();
const matched = jobs.filter((j: any) => {
const text = `${j.position} ${j.company} ${(j.tags || []).join(" ")}`.toLowerCase();
return terms.some((t) => text.includes(t.toLowerCase()));
}).slice(0, 5);
for (const j of matched) {
allResults.push(`[RemoteOK] ${j.position} at ${j.company}\n Tags: ${(j.tags || []).join(", ")} | Salary: ${j.salary_min ? `$${j.salary_min}-$${j.salary_max}` : "N/A"}\n URL: ${j.url}`);
}
}
} catch {}
lastUpworkCheck = Date.now();
if (!allResults.length) {
return { content: [{ type: "text" as const, text: "No jobs found across any source. Try different keywords." }] };
}
// Deduplicate by URL
const seen = new Set<string>();
const unique = allResults.filter((r) => {
const urlMatch = r.match(/URL: (.+)/);
const url = urlMatch?.[1] || r;
if (seen.has(url)) return false;
seen.add(url);
return true;
});
return {
content: [{
type: "text" as const,
text: `Job Scan Results (${unique.length} found):\n\n${unique.join("\n\n")}\n\n---\nTo draft a proposal for any of these, use the draft_proposal tool with the job title and description.`,
}],
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);