career-mcp-server
A production-grade FastAPI-based MCP server for career-oriented tools, currently exposing GitHub integration (profile, repos, pinned, search, rate limits) to LLM clients via the Model Context Protocol.
README
career-mcp-server
A production-grade AI Career MCP Server — a FastAPI application that exposes career-oriented tools to LLM clients via the Model Context Protocol (MCP).
This repository currently contains Phase 1 (foundation), Phase 2 (GitHub integration), and Phase 3 (AI career intelligence & resume engine). LinkedIn integration is not implemented yet — that's the next phase, built on top of this foundation.
Overview
career-mcp-server is designed as a long-lived, extensible service:
- A FastAPI HTTP layer for health checks, versioning, and REST endpoints.
- An MCP server, built on the official MCP Python SDK, that exposes tools an LLM client can call directly.
- A clean separation between API, core (cross-cutting concerns), MCP, integrations, ai, resume, career, services, models, and schemas, so future features plug in without restructuring the app.
- A GitHub integration (Phase 2): an async REST client, typed Pydantic models, a caching service layer, and the same functionality exposed identically through both REST endpoints and MCP tools.
- A resume & AI career intelligence engine (Phase 3): PDF upload and parsing, AI-powered analysis and scoring, resume-to-job matching, and AI-generated rewrites, cover letters, and interview prep — all behind a provider-agnostic AI abstraction, again exposed identically through REST and MCP.
Phase 1 registered a single demonstration MCP tool — ping, which returns "pong" — solely to
prove the MCP integration initializes and works end-to-end. Phase 2 added 22 GitHub tools, and
Phase 3 adds 17 more (13 resume + 4 career) on top of that same registry — 40 MCP tools in total.
Architecture
Client (HTTP) Client (MCP)
│ │
▼ ▼
FastAPI app ───────────────────► MCPServer instance
│ middleware, exception │
│ handlers, routers │ tools from ToolRegistry
▼ ▼
app/api/routers/* app/mcp/{server,registry,tools/*}.py
│ │
└───────────────┬─────────────────────┘
▼
┌────────────────┼─────────────────────┬───────────────────────┐
▼ ▼ ▼
GitHubService ResumeService CareerService
(caching, validation) (storage, parsing, analysis, (advice, summaries,
│ matching, AI rewriting) bio rewriting)
▼ │ │ │
GitHubClient │ └──────────┬──────────┘
(httpx, retries, ▼ ▼
rate limits) ResumeStorage AIProvider (abstract)
│ (local FS, swappable │
▼ for S3 later) ┌────────┴────────┐
GitHub REST API ▼ ▼
OpenAIProvider (future: Anthropic,
Gemini, local model)
│
▼
app/core/* (DI, exceptions, middleware, startup/shutdown, cache)
│
▼
app/config/* (settings, logging) ◄── app/services, app/models, app/schemas, app/utils
Both the REST routes and the MCP tools are thin pass-throughs to the same service classes — there is exactly one implementation of business logic, validation, caching, and error handling per domain, regardless of which transport a client uses.
Design principles:
- Dependency injection — routes and services depend on
get_settings()/get_logger()/get_github_service()/get_resume_service()/get_career_service()/get_ai_provider()rather than importing configuration or integrations directly. - SOLID / clean architecture — each layer has one responsibility; base classes
(
AppBaseModel,BaseService) exist so domain code stays consistent across integrations. - Provider-agnostic AI —
ResumeServiceandCareerServicedepend on the abstractAIProviderinterface (app/ai/provider.py), never on the OpenAI SDK directly. SwappingLLM_PROVIDERswaps the implementation everywhere at once; adding Anthropic, Gemini, or a local model means writing one new provider class, not touching business logic. - Structured errors — every handled failure, from either transport, returns the same JSON
error envelope (
{"success": false, "error": {...}}) or the equivalent MCPis_errorresult. - Typed data only — no integration ever returns raw JSON or unstructured AI text; every method returns a validated Pydantic model or a list of them.
- Async-first — all I/O-bound code paths are
async def, including the GitHub and AI clients. - Graceful degradation — the app starts and resume storage (list/get/delete) works fully even
with no
OPENAI_API_KEYconfigured; only AI-dependent calls fail, cleanly, when actually invoked. See Graceful Degradation.
Folder Structure
career-mcp-server/
├── app/
│ ├── main.py # FastAPI app factory + entry point
│ ├── config/
│ │ ├── settings.py # Pydantic Settings (env-driven configuration)
│ │ └── logging.py # Loguru sinks: console + rotating file logs
│ ├── api/
│ │ └── routers/
│ │ ├── health.py # GET /health
│ │ ├── github.py # 22 REST endpoints for the GitHub integration
│ │ ├── resume.py # 13 REST endpoints for resume management & AI features
│ │ ├── career.py # 4 REST endpoints for general career intelligence
│ │ └── base.py # Aggregates all routers
│ ├── core/
│ │ ├── exceptions.py # Exception hierarchy + handlers
│ │ ├── dependencies.py # DI entry points (get_settings, get_logger)
│ │ ├── cache.py # Generic in-memory TTL cache
│ │ ├── middleware.py # Request ID, timing, logging, CORS, etc.
│ │ ├── startup.py # Graceful startup hook
│ │ └── shutdown.py # Graceful shutdown hook
│ ├── mcp/
│ │ ├── server.py # Builds the MCPServer instance, registers all tool modules
│ │ ├── registry.py # Tool registry + demo `ping` tool
│ │ └── tools/
│ │ ├── github_tools.py # 22 GitHub MCP tools (thin wrappers over GitHubService)
│ │ ├── resume_tools.py # 13 resume MCP tools (thin wrappers over ResumeService)
│ │ └── career_tools.py # 4 career MCP tools (thin wrappers over CareerService)
│ ├── integrations/
│ │ └── github/
│ │ ├── client.py # Async httpx client: auth, retries, pagination, rate limits
│ │ ├── models.py # Typed GitHub resource models (User, Repository, Commit, ...)
│ │ ├── errors.py # Maps GitHub HTTP errors to application exceptions
│ │ ├── validation.py # owner/repo/pagination/search input validation
│ │ ├── service.py # GitHubService: business logic + caching
│ │ └── dependencies.py # get_github_client() / get_github_service() DI factories
│ ├── ai/
│ │ ├── provider.py # AIProvider abstract interface (complete/complete_json)
│ │ ├── base_provider.py # Shared scaffolding for concrete providers
│ │ ├── openai_provider.py # OpenAIProvider implementation
│ │ ├── factory.py # get_ai_provider() DI factory, keyed by LLM_PROVIDER
│ │ └── prompts.py # Every prompt template used anywhere, centralized
│ ├── resume/
│ │ ├── models.py # Parsed-resume domain models (ContactInfo, Experience, ...)
│ │ ├── schemas.py # REST/MCP request & response schemas
│ │ ├── validators.py # Upload/filename/resume-id validation
│ │ ├── storage.py # ResumeStorage abstraction + LocalResumeStorage
│ │ ├── parser.py # PDF text extraction + AI-powered structured parsing
│ │ ├── job_parser.py # AI-powered job description parsing
│ │ ├── service.py # ResumeService: storage, analysis, matching, AI rewriting
│ │ └── dependencies.py # get_resume_service() DI factory
│ ├── career/
│ │ ├── schemas.py # REST/MCP request & response schemas
│ │ ├── service.py # CareerService: advice, summaries, bio rewriting
│ │ └── dependencies.py # get_career_service() DI factory
│ ├── services/
│ │ └── base_service.py # Base class for future services
│ ├── models/
│ │ └── base_model.py # Base class for future domain models
│ ├── schemas/
│ │ └── common.py # Shared response schemas
│ └── utils/
│ └── helpers.py # Small generic helpers
├── tests/ # pytest suite
├── Dockerfile
├── docker-compose.yml # Includes a named volume for uploads/ persistence
├── requirements.txt
├── pyproject.toml # ruff, mypy, pytest configuration
├── .env.example
└── .github/workflows/ci.yml
Installation
Requires Python 3.12+.
git clone <repository-url> career-mcp-server
cd career-mcp-server
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env
Environment Variables
All configuration is read from environment variables (or a local .env file). See
.env.example for defaults.
| Variable | Default | Description |
|---|---|---|
APP_NAME |
career-mcp-server |
Application name |
APP_VERSION |
0.1.0 |
Application version |
HOST |
0.0.0.0 |
Bind host for the HTTP server |
PORT |
8000 |
Bind port for the HTTP server |
LOG_LEVEL |
INFO |
Minimum log level (DEBUG, INFO, ...) |
DEBUG |
false |
Enables verbose diagnostics |
ENVIRONMENT |
development |
development / staging / production / test |
CORS_ORIGINS |
* |
Comma-separated list of allowed CORS origins |
GITHUB_TOKEN |
(none) | GitHub Personal Access Token (see Authentication) |
GITHUB_API |
https://api.github.com |
Base URL for the GitHub REST API |
REQUEST_TIMEOUT |
30 |
Outbound HTTP request timeout, in seconds |
CACHE_TTL |
300 |
TTL, in seconds, for cached GitHub data (0 effectively disables caching) |
OPENAI_API_KEY |
(none) | OpenAI API key (required for AI-powered resume/career features) |
LLM_PROVIDER |
openai |
Which AIProvider implementation backs AI features |
LLM_MODEL |
gpt-5 |
Model name requested from the configured provider |
MAX_TOKENS |
4000 |
Maximum tokens generated per AI completion |
TEMPERATURE |
0.3 |
Sampling temperature for AI completions |
UPLOAD_DIRECTORY |
uploads |
Local directory where uploaded resumes are stored |
MAX_UPLOAD_SIZE_MB |
10 |
Maximum allowed resume upload size, in megabytes |
Running Locally
uvicorn app.main:app --reload
- App: http://localhost:8000
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
- OpenAPI schema: http://localhost:8000/openapi.json
- Health check: http://localhost:8000/health
- Version: http://localhost:8000/version
Docker
docker compose up --build
This builds a non-root, slim production image and starts the API on the port configured by
PORT (default 8000).
Running the MCP Server
The MCP server is built in app/mcp/server.py and initialized automatically when app.main is
imported (e.g. on application startup, it's attached to app.state.mcp_server). It registers 40
tools in total:
ping— the Phase 1 demonstration tool, proving MCP wiring works end-to-end.- 22 GitHub tools from
app/mcp/tools/github_tools.py. - 13 resume tools + 4 career tools from
app/mcp/tools/resume_tools.pyandcareer_tools.py(see Available MCP Tools below).
Each integration owns a register(registry) function in its own app/mcp/tools/* module;
app/mcp/server.py just calls each of them once before building the server, so adding a future
integration's tools never requires touching existing tool modules.
MCP tool arguments must be JSON-safe, so file uploads (upload_resume, replace_resume) take the
PDF's bytes as a base64-encoded string instead of a multipart upload.
GitHub Integration
Authentication
Generate a GitHub Personal Access Token (a fine-grained
token with read access to the repositories you want to query is sufficient) and set it in .env:
GITHUB_TOKEN=ghp_your_token_here
The token is sent as a Bearer header on every request and is never logged. Without a token,
public endpoints (public repos, search, unauthenticated rate limit) still work — GitHub just
applies its much lower unauthenticated rate limit (60 requests/hour) and endpoints that require a
user context, like /github/profile, return a structured 401 AuthenticationException.
Available REST Endpoints
All endpoints are under /github and return typed JSON (never raw GitHub payloads):
| Method | Path | Description |
|---|---|---|
| GET | /github/profile |
The authenticated user |
| GET | /github/pinned |
The authenticated user's pinned repositories (GraphQL) |
| GET | /github/repos |
List the authenticated user's repositories |
| GET | /github/search?q= |
Search public repositories |
| GET | /github/rate-limit |
Current GitHub API rate limit status |
| GET | /github/repos/{owner}/{repo} |
Repository details |
| GET | /github/repos/{owner}/{repo}/stats |
Aggregated statistics |
| GET | /github/repos/{owner}/{repo}/languages |
Language breakdown |
| GET | /github/repos/{owner}/{repo}/contributors |
Top contributors |
| GET | /github/repos/{owner}/{repo}/commits |
Latest commits |
| GET | /github/repos/{owner}/{repo}/release |
Latest release |
| GET | /github/repos/{owner}/{repo}/releases |
All releases |
| GET | /github/repos/{owner}/{repo}/readme |
Decoded README |
| GET | /github/repos/{owner}/{repo}/topics |
Repository topics |
| GET | /github/repos/{owner}/{repo}/license |
Repository license |
| GET | /github/repos/{owner}/{repo}/branches |
Branches |
| GET | /github/repos/{owner}/{repo}/issues |
Issues (excludes pull requests) |
| GET | /github/repos/{owner}/{repo}/pulls |
Pull requests |
| GET | /github/repos/{owner}/{repo}/workflows |
GitHub Actions workflows |
| GET | /github/repos/{owner}/{repo}/stars |
Star count |
| GET | /github/repos/{owner}/{repo}/forks |
Fork count |
| GET | /github/repos/{owner}/{repo}/watchers |
Watcher count |
| DELETE | /github/cache |
Manually invalidate the GitHub integration cache |
Example:
curl http://localhost:8000/github/repos/octocat/Hello-World/stats
Available MCP Tools
The same functionality, one tool per REST endpoint above: github_profile,
pinned_repositories, list_repositories, search_repositories, rate_limit,
repository_details, repository_statistics, repository_languages,
repository_contributors, latest_commits, latest_release, repository_releases,
repository_readme, repository_topics, repository_license, repository_branches,
repository_issues, repository_pull_requests, repository_workflows, repository_stars,
repository_forks, repository_watchers.
Each tool's input/output schema is generated by the MCP SDK directly from its Python type hints and docstring — there is no separate schema to keep in sync.
Caching
GitHubService caches five read-heavy, slow-changing calls in an in-memory TTL cache (CACHE_TTL
seconds, default 300): the authenticated profile, the repository list, repository statistics,
language breakdowns, and releases. Everything else (commits, issues, PRs, branches, search, rate
limit, etc.) always hits GitHub directly. Call DELETE /github/cache to clear it manually.
Rate Limits
GitHub enforces 5,000 requests/hour for authenticated requests and 60/hour unauthenticated (10/min
for search). GET /github/rate-limit reports current usage. The client logs remaining quota on
every response and raises a structured RateLimitException (HTTP 429) the moment GitHub signals
either the primary or secondary rate limit has been hit — transient 5xx errors and network
failures are retried up to 3 times with exponential backoff before that.
Resume & AI Career Intelligence
Phase 3 adds resume upload/management, AI-powered resume analysis and scoring, resume-to-job
matching, and AI-generated rewrites, cover letters, and interview prep. All of it sits behind the
same provider-agnostic AIProvider abstraction — no business logic imports the OpenAI SDK
directly.
AI Provider Architecture
ResumeService / CareerService
│ depends only on the abstract interface
▼
AIProvider (app/ai/provider.py)
├── complete(prompt, system=..., temperature=..., max_tokens=...) -> str [abstract]
└── complete_json(...) -> dict [concrete, built on complete()]
│
▼
BaseAIProvider (app/ai/base_provider.py) — settings storage, default resolution
│
▼
OpenAIProvider (app/ai/openai_provider.py) — the only OpenAI-specific code in the app
app/ai/factory.py exposes get_ai_provider(), a cached DI factory keyed by LLM_PROVIDER.
Adding a new provider (Anthropic, Gemini, a local model) means writing one new class implementing
complete() and registering it in the factory's provider map — nothing in ResumeService,
CareerService, the REST routes, or the MCP tools changes.
Supported Providers
| Provider | LLM_PROVIDER value |
Status |
|---|---|---|
| OpenAI | openai |
Implemented |
| Anthropic, Gemini, local models | — | Not yet implemented; add via the factory above |
Prompt Library
Every prompt used anywhere in the app lives in app/ai/prompts.py as a small function returning
plain text — nothing is hardcoded inline in a service. The module has no dependency on resume or
job-description models; callers pass already-serialized text (model.model_dump_json()), which
keeps app/ai reusable for any future feature, not just resumes.
Graceful Degradation
The AI provider is injected into ResumeService/CareerService as a factory callable, not a
pre-built instance. That means:
- The app starts successfully with no
OPENAI_API_KEYset. GET /resume,GET /resume/{id},DELETE /resume/{id}work with no AI configured at all.POST /resume/uploadstill works with no AI configured — parsing degrades to deterministic, regex-based contact extraction (name/email/phone) with every AI-only field (skills, education, experience, ...) left as an empty default, rather than failing the upload.- Everything genuinely AI-dependent (analyze, score, match, rewrite, cover letters, interview
questions, career advice, ...) fails with a clean
502 ExternalServiceException—"OPENAI_API_KEY is not configured; AI-powered features are unavailable"— the moment it's actually called, not at startup.
Available REST Endpoints
Resume (/resume) — returns typed JSON, never raw AI text:
| Method | Path | Description |
|---|---|---|
| POST | /resume/upload |
Upload a PDF resume (multipart), parse, and store it |
| GET | /resume |
List stored resumes' metadata |
| GET | /resume/{resume_id} |
Get a stored resume's metadata + parsed content |
| PUT | /resume/{resume_id} |
Replace a stored resume's file, re-parsing it |
| DELETE | /resume/{resume_id} |
Delete a stored resume |
| POST | /resume/analyze |
Full multi-part AI analysis (score, gaps, summaries, strengths/weaknesses) |
| POST | /resume/score |
Focused resume quality / ATS score |
| POST | /resume/match |
Compare a resume against a job description |
| POST | /resume/rewrite |
AI-rewrite the full resume, summary, or experience section |
| POST | /resume/cover-letter |
Generate a tailored cover letter |
| POST | /resume/interview |
Generate likely interview questions |
| POST | /resume/project-descriptions |
Turn raw project notes into a polished description |
| POST | /resume/achievements |
Turn raw notes into quantified achievement bullets |
Career (/career) — general career intelligence, not necessarily tied to a stored resume:
| Method | Path | Description |
|---|---|---|
| POST | /career/advice |
AI-powered career advice for a free-form question |
| POST | /career/professional-summary |
Standalone summary from a stored resume or raw text |
| POST | /career/rewrite-bio |
AI-rewrite a short personal bio |
| POST | /career/parse-job-description |
Extract structured fields from raw job posting text |
Example Requests & Responses
Upload a resume:
curl -X POST http://localhost:8000/resume/upload \
-F "file=@resume.pdf;type=application/pdf"
{
"metadata": {
"id": "437afbe1254745e89c24197157a92a0c",
"original_filename": "resume.pdf",
"content_type": "application/pdf",
"size_bytes": 48213,
"uploaded_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T10:30:00Z"
},
"parsed": {
"contact": {"name": "Jane Doe", "email": "jane@example.com", "phone": "+1-555-0100", "location": "Remote"},
"summary": "Backend engineer with 6 years building distributed systems...",
"skills": ["Python", "FastAPI", "PostgreSQL"],
"education": ["..."], "experience": ["..."], "projects": ["..."],
"certifications": [], "languages": [], "publications": [],
"raw_text": "..."
}
}
Match a resume against a job description:
curl -X POST http://localhost:8000/resume/match \
-H "Content-Type: application/json" \
-d '{"resume_id": "437afbe1254745e89c24197157a92a0c", "job_description": "We need a backend engineer with FastAPI and PostgreSQL experience..."}'
{
"overall_score": 88,
"skill_match_score": 90,
"experience_match_score": 85,
"keyword_coverage_score": 80,
"missing_skills": ["Kubernetes"],
"recommendations": ["Add a metric quantifying the API's scale"],
"ats_compatibility_score": 92
}
Available MCP Tools
Resume (13): upload_resume, list_resumes, get_resume, replace_resume,
delete_resume, analyze_resume, resume_score, match_resume, rewrite_resume,
generate_cover_letter, interview_questions, project_descriptions, achievement_generator.
Career (4): career_advice, professional_summary, rewrite_bio, parse_job_description.
Each tool's input/output schema is generated by the MCP SDK directly from its Python type hints
and docstring. upload_resume and replace_resume take content_base64 (a base64-encoded PDF)
instead of a file upload, since MCP tool arguments must be JSON-safe.
Security
- File type validation — only
application/pdfis accepted; anything else is rejected with a structured415 UnsupportedMediaTypeExceptionbefore any file processing happens. - PDF signature check — uploaded bytes must start with the
%PDF-magic bytes, rejecting mislabeled files even if the declared content type says PDF. - Size limits — uploads over
MAX_UPLOAD_SIZE_MBare rejected with413 PayloadTooLargeException. - Path traversal prevention — storage is always keyed by a server-generated UUID
(
{resume_id}.pdf), never by the user-supplied filename, so path traversal via a crafted filename is structurally impossible regardless of storage backend. The original filename is still sanitized (directory components stripped, unsafe characters rejected) before being stored as display metadata. - No public file exposure — there is no static file route; the only way to retrieve a resume's
content is through the authenticated-by-deployment
GET /resume/{resume_id}API, which returns parsed data, not the raw file.
Testing
pytest --cov=app --cov-report=term-missing
Tests cover:
- The
/healthand/versionendpoints - Configuration loading, validation, and caching
- The MCP server's initialization and the
pingtool - Application startup/shutdown lifecycle
- The GitHub HTTP client: auth headers, 401/403/404/429/5xx mapping, retry-with-backoff,
pagination — all against
httpx.MockTransport, no live network calls GitHubService: every method, JSON-to-model mapping, caching behavior, input validation- Every
/github/*REST endpoint, including error-envelope translation, via dependency overrides - Every GitHub MCP tool: registration, delegation, and typed-exception propagation
- The AI provider abstraction:
complete_jsonJSON/code-fence parsing,OpenAIProvider(with the OpenAI SDK client mocked), and theget_ai_provider()factory's provider selection/error paths - Resume upload validation, local storage (save/get/list/delete/replace), PDF text extraction against a real (hand-built, dependency-free) PDF, and AI-powered field extraction with graceful degradation when the AI provider is unavailable or fails
ResumeServiceandCareerService: every method, against in-memory storage and AI provider fakes — no filesystem I/O beyond atmp_path, no real AI calls- Every
/resume/*and/career/*REST endpoint and every resume/career MCP tool: registration, delegation, file upload handling, and error-envelope translation
No test makes a real GitHub API call or a real AI API call — every external client is fully swappable via dependency injection.
CI/CD
GitHub Actions (.github/workflows/ci.yml) runs on every push and
pull request to main:
- Install dependencies
ruff check .— lintmypy app— type-checkpytest --cov=app— testdocker build— verify the production image builds
The pipeline fails on any lint, type, or test error.
Roadmap
- Phase 1 — Project foundation: FastAPI app, MCP server bootstrap, configuration, logging, middleware, structured error handling, Docker, CI.
- Phase 2 — GitHub integration: async client, typed models, caching service layer, 22 REST endpoints, and 22 matching MCP tools.
- Phase 3 (this repository) — AI career intelligence & resume engine: provider-agnostic AI abstraction, PDF resume upload/parsing/storage, AI-powered analysis/scoring/matching/rewriting, 17 REST endpoints, and 17 matching MCP tools.
- Phase 4 — LinkedIn integration.
- Phase 5 — Further AI-powered career insights and recommendations.
Contributing
- Create a feature branch.
- Make your changes with type hints, docstrings, and tests.
- Run
ruff check .,mypy app, andpytestlocally. - Open a pull request; CI must pass before merge.
License
Distributed under the MIT License.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.