v0.1.0 Python 3.10+ MCP 1.8.0+ 58 Tools

Depverse Documentation

An MCP server that gives Claude structured, real-time access to the npm Registry — so it stops guessing package versions, dependencies, or license info, and starts fetching them.

TL;DR — Depverse exposes the public npm Registry as 18 typed tools Claude can call over stdio. Point Claude Code (or any MCP client) at mcp_server.py and you're done.

Why Depverse?

AI coding assistants are notoriously bad at one thing: knowing the current state of the npm ecosystem. Their training data is frozen at some cutoff — so when they suggest a version, a peer range, or a changelog, they're often months or years out of date.

❌ Without Depverse
Claude guesses from training data →
writes "react@17.0.0"
stale, wrong, silently broken
✅ With Depverse
Claude calls get_latest_version("react")
writes "react@19.2.5"
live registry data, always current

What it's good for

  • AI coding agents writing package.json, imports, or install commands.
  • Project scaffolding and dependency pinning.
  • Upgrade planning — "what changed between react 17 and 19?"
  • Pre-install audits — license, unpacked size, maintainers.
  • Peer-compat validation without a real install.

Architecture

Depverse is a Python MCP server built on FastMCP. An MCP client (Claude Code, Claude Desktop, Cursor, Cline, Windsurf, Copilot) spawns it as a subprocess and communicates over stdio using JSON-RPC. Depverse in turn talks to the public npm Registry, OSV.dev, and the npm download API over HTTPS via httpx.

Claude Codeor any MCP client
Depversemcp_server.py
npm Registryregistry.npmjs.org
JSON-RPC / stdio
HTTPS (httpx)
GitHub APIfallback for changelogs

Project layout

# repo root
Depverse/
├── mcp_server.py       # The MCP server — all 58 tools
├── mcp_client.py       # Thin MCP client wrapper (stdio)
├── test_npm_tool.py    # Manual end-to-end test
├── pyproject.toml
├── uv.lock
├── .mcp.json           # Example MCP config for clients
├── docs/               # This documentation site
│
│   # --- Optional: bundled CLI chat (ignore if using Claude Code / Cursor / etc.) ---
├── main.py             # Entrypoint for the optional CLI chat
└── core/
    ├── chat.py         # Tool-using chat loop
    ├── cli_chat.py     # CLI chat with @docs + /commands
    ├── cli.py          # prompt-toolkit UI
    ├── claude.py       # Anthropic API wrapper
    └── tools.py        # MCP ↔ Anthropic tool bridge

Installation

Depverse runs locally as a Python process. You'll need:

  • Python 3.10+
  • uv (recommended) or plain pip
No Anthropic API key required. The MCP server has no secrets, no tokens, no state — Claude Code (or any MCP client) brings its own auth.
Install uv (once)
Skip this if you already have it.
# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh

# or via pip
pip install uv
Clone the repo
git clone https://github.com/yash-neural/Depverse.git
cd Depverse
Create a virtualenv & install
uv venv
source .venv/bin/activate     # Windows: .venv\Scripts\activate
uv pip install -e .
Verify the server starts
Runs the manual end-to-end test; exits cleanly when done.
uv run test_npm_tool.py

Register with an MCP Client

Depverse is a standard MCP server. Any MCP-aware client can launch it over stdio and use its 58 tools. Pick your client below for a copy-paste config.

Common config shape. Every client just needs to know how to launch the server. Two fields always matter: the command (uv) and the absolute path to your cloned Depverse folder. Replace /absolute/path/to/Depverse below with your real path.

Claude Code

The CLI handles registration for you — no JSON editing required.

Project scope (recommended)

Writes .mcp.json into the current directory. Server active only when Claude Code is opened here.

cd /absolute/path/to/Depverse
claude mcp add Depverse --scope project \
  -- uv --directory "$(pwd)" run mcp_server.py

User scope (everywhere)

claude mcp add Depverse --scope user \
  -- uv --directory "/absolute/path/to/Depverse" run mcp_server.py

Verify

# restart Claude Code, then:
/mcp
# should list: Depverse → 58 tools

Other Claude Code commands

claude mcp list
Show all registered MCP servers.
claude mcp get Depverse
Show the launch config.
claude mcp remove Depverse
Unregister the server.
/mcp
Inspect servers inside Claude Code.

Claude Desktop

Edit Claude Desktop's config file directly — the app reads it on startup.

Config file location

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json
  • Linux: ~/.config/Claude/claude_desktop_config.json

Add Depverse to mcpServers

{
  "mcpServers": {
    "Depverse": {
      "command": "uv",
      "args": [
        "--directory",
        "/absolute/path/to/Depverse",
        "run",
        "mcp_server.py"
      ]
    }
  }
}

Restart Claude Desktop. The tools appear in the 🔌 menu next to the chat input.

Cursor

Cursor has native MCP support. Add the config via Settings → Cursor Settings → MCP → Add new MCP server, or edit the JSON directly.

Config file location

  • Project-specific: .cursor/mcp.json in the project root
  • Global: ~/.cursor/mcp.json

Config

{
  "mcpServers": {
    "Depverse": {
      "command": "uv",
      "args": [
        "--directory",
        "/absolute/path/to/Depverse",
        "run",
        "mcp_server.py"
      ]
    }
  }
}

Reload Cursor. Depverse's tools appear in the Agent panel's tool list — toggle them on/off as needed.

Cline (VS Code extension)

In VS Code, open the Cline side panel → click the MCP Servers icon (the server icon at the top) → Configure MCP Servers.

That opens cline_mcp_settings.json. Add:

{
  "mcpServers": {
    "Depverse": {
      "command": "uv",
      "args": [
        "--directory",
        "/absolute/path/to/Depverse",
        "run",
        "mcp_server.py"
      ]
    }
  }
}

Cline picks up changes automatically — no restart needed.

Windsurf

In Windsurf, open Settings → Cascade → Model Context Protocol, or edit the config file directly.

Config file

  • ~/.codeium/windsurf/mcp_config.json
{
  "mcpServers": {
    "Depverse": {
      "command": "uv",
      "args": [
        "--directory",
        "/absolute/path/to/Depverse",
        "run",
        "mcp_server.py"
      ]
    }
  }
}

Restart Cascade (or Windsurf) to load the server.

GitHub Copilot (VS Code)

VS Code's Copilot Chat supports MCP servers via workspace or user settings. Add the config under the "mcp" key.

Workspace config

Create .vscode/mcp.json in your project:

{
  "servers": {
    "Depverse": {
      "type": "stdio",
      "command": "uv",
      "args": [
        "--directory",
        "/absolute/path/to/Depverse",
        "run",
        "mcp_server.py"
      ]
    }
  }
}

Reload VS Code. In the Copilot Chat side panel, open the tools menu — Depverse tools appear under Depverse. Toggle them on before asking questions.

MCP support in GitHub Copilot is still evolving — the exact path and schema may shift between VS Code insiders and stable releases. Check the official VS Code MCP docs if the above doesn't load.

Generic / Other MCP Clients

Any client that speaks MCP over stdio can use Depverse. The launch command is always the same:

# command
uv

# args
--directory /absolute/path/to/Depverse run mcp_server.py

Or, without uv:

# command
python

# args
/absolute/path/to/Depverse/mcp_server.py

Make sure the Python environment used has Depverse installed (pip install -e . in the repo root). The server speaks JSON-RPC 2.0 over stdio — no network ports, no daemon.

Note on OpenAI / Codex: OpenAI's API and products don't natively support MCP (they use their own function-calling format). Depverse won't register directly with those tools — you'd need an MCP-to-function-calling adapter.

Tools

Depverse exposes 58 tools in ten groups. All return JSON; errors surface as typed MCP tool errors.

Version tools (6)

These answer "which version?" questions.

get_latest_version
Latest Stable
The current stable version string for any public npm package.
get_all_versions
Full Version List
Every version ever published, plus a count.
get_version_info
Manifest Inspector
Deps, devDeps, engines, license, repo for a specific version.
get_dist_tags
Dist-Tag Map
All labels (latest, beta, next…) mapped to versions.
get_changelog
Release Notes
GitHub Releases for a tag, or CHANGELOG.md from the linked repo.
check_version_exists
Existence Check
Boolean: does package@version exist on npm?

Package Info tools (7)

These answer "what kind of package?" questions.

get_package_info
Full Metadata
Name, description, author, license, homepage, maintainers, dates.
get_package_readme
README Content
README markdown (truncated at 20,000 chars).
get_package_keywords
Tags / Keywords
Keyword list declared in package.json.
get_package_repository
Source Repo
Repository URL plus parsed GitHub owner/repo slug.
get_package_homepage
Homepage / Docs
Declared homepage, with npm page URL as a fallback.
get_package_license
License
SPDX string (MIT, Apache-2.0…) or legacy array form.
get_package_size
Unpacked Size & File Count
Bytes + human-readable size + total file count from the tarball.

Dependency tools (5)

These answer "what does it need?" questions.

get_dependencies
Runtime Deps
Prod dependencies with declared version ranges.
get_peer_dependencies
Peer Deps
Packages the host app must provide itself.
get_dev_dependencies
Dev-only Deps
Build, test, lint tools — not installed by consumers.
get_dependency_tree
Transitive Tree
Walks the full nested graph. Parallel fetch, depth-capped, deduped.
check_peer_compatibility
Peer Satisfaction Check
Given what you have installed, reports each peer as yes / no / unknown / missing. Built-in semver matcher handles ^, ~, comparators, and || ranges.

Security & Health tools (12)

These answer "is this package safe and alive?" questions.

check_vulnerabilities
Known CVEs
Queries OSV.dev for all advisories (GHSA, CVE) affecting a package + version.
get_deprecation_status
Deprecation Check
Is a specific version — or the package as a whole — deprecated? Returns the deprecation message.
check_maintainer_activity
Is it Abandoned?
Last publish date, average cadence, and a status label: active / slowing / stale / abandoned.
get_download_stats
Downloads
Day / week / month download counts from the public npm API, plus a popularity tier.
check_typosquat_risk
Supply-Chain Typo Check
Flags names suspiciously close to popular packages via Levenshtein distance. exresshigh (1 edit from express); lodaashmedium; unrelated names → none.
get_download_trend
Trend Over Time
Day-by-day counts for last-month, last-year, or a custom date range, plus a growing / declining / flat label.
compare_popularity
Side-by-side Ranking
Rank 2–10 packages by downloads in one call. Returns each package's share of the total.
get_download_by_version
Per-version Breakdown
Which versions are users actually installing? Returns the top N versions + the most popular major line — essential for deprecation planning.
get_vulnerability_details
CVE / GHSA Lookup
Full details for an advisory ID — summary, severity, affected npm packages, patched versions. Accepts GHSA, CVE, OSV IDs.
audit_all_dependencies
Whole-project Audit
One-call vulnerability scan of a whole package.json via OSV's batch endpoint. Optionally includes devDependencies.
check_supply_chain_risk
Direct-dep Risk Tier
Resolves a package's dependencies, audits them all, returns a clean / low / medium / high risk tier.
get_patched_version
Minimum Safe Upgrade
Given an advisory ID, returns the first patched version per affected npm package — tells you exactly what to upgrade to.

Compatibility & Update tools (5)

These answer "will this upgrade break anything?" questions.

check_node_compatibility
Engines Field
What Node (and npm / yarn) versions does this package require? Reads the manifest's engines field.
compare_versions
Version Diff
Added / removed / range-changed deps (runtime + dev + peer) between two versions, plus engines changes.
get_breaking_changes
Major Bump Detector
Flags direct and peer dependencies whose declared range crossed a major boundary — the usual source of breaking changes.
resolve_semver
Range Resolver
Resolves a range like ^18.0.0 or ~4.17.20 to the highest published version that satisfies it.
check_outdated
Bulk Update Check
Pass {package_name: installed_version} and get each one's latest + a gap level (major / minor / patch). Parallel fan-out — fast even for whole lock files.

Utility tools (4)

Ergonomic helpers that don't fit the other groups.

batch_get_versions
Bulk Latest Lookup
Fetch /latest for many packages in parallel — one round-trip per package instead of sequential.
validate_package_json
Dep-range Validator
Checks each range in a package.json resolves to a published version. Catches typos like lodash@999.0.0.
generate_install_command
Install Command Builder
Produces install commands for npm, pnpm, yarn, and bun — with proper --dev / --exact dialects.
resolve_cdn_url
CDN URLs
Pinned URLs for jsDelivr, unpkg, and esm.sh. Optional file path. Auto-resolves "latest" to an immutable version.

Bundle Size tools (5)

These answer "how heavy is this on the client?" — powered by bundlephobia.com.

get_bundle_size
Minified + Gzipped Size
Raw and gzipped bytes for a package (optionally pinned to a version), with transitive dependency count.
get_bundle_size_history
Size Over Time
Size across recent versions. Reports growing / stable / shrinking trend and percent delta.
check_treeshakeable
Tree-Shakeability
Returns true only when the package ships ES modules and declares "sideEffects": false.
compare_bundle_sizes
Lightweight-first Ranking
Parallel size lookup for 2–10 packages, ranked by gzipped size. Classic axios vs ky vs got question.
get_bundle_size_impact
PR-Review Summary
Returns sizes + a one-line summary ready to paste into a PR: "Adding date-fns@4.1.0 will add ~17.5 KB gzipped (70.7 KB minified) to your bundle, pulling in 0 transitive dependencies. Impact: small."

Module & Compatibility tools (7)

These answer "what module systems and runtimes does this support?"

check_esm_support
ES Modules
Does the package ship ESM? Reads exports.import, module, and type: "module".
check_cjs_support
CommonJS
Does it support require()? Reads main, exports.require, and the default rules.
check_typescript_support
TypeScript Types
Built-in types? Or a DefinitelyTyped (@types/*) companion package? Reports which, or neither.
get_exports_map
Entry-point Map
Raw exports field + a flat list of subpaths (e.g. ., ./router) — useful for deep-import debugging.
check_browser_compatible
Browser-safe?
yes / likely / unlikely / no — based on browser field, native .node files, and CLI bin.
check_deno_compatible
Deno-safe?
Checks ESM/CJS, native modules, and JSR presence. Returns a compatibility verdict with reasoning.
get_package_on_jsr
JSR Lookup
Is this package also on JSR? Returns the JSR URL, description, and supported runtimes.

Migration & Upgrade tools (3)

These answer "how do I move from v1 to v5 without breaking everything?"

suggest_upgrade_path
Per-Major Hops
Walks your upgrade in per-major steps — recommends the highest stable release in each major line plus breaking-dep-bump counts per hop.
find_replacement_package
Deprecation Successor
Catches deprecated packages and extracts the suggested replacement from the deprecation message ("use X instead", "moved to Y").
check_migration_guide
Official Migration Docs
Pulls MIGRATION.md / UPGRADING.md / UPGRADE.md from the repo, with a repo-contents scan as a fallback for non-canonical filenames.

Full tool reference

Every tool, its parameters, which registry endpoint it hits, and what it returns.

Tool Parameters Endpoint Returns
Version Tools
get_latest_versionpackage_nameGET /{pkg}/latest{ name, version }
get_all_versionspackage_nameGET /{pkg}{ name, count, versions[] }
get_version_infopackage_name, versionGET /{pkg}/{version}Manifest (deps, license, repo…)
get_dist_tagspackage_nameGET /{pkg}{ name, dist_tags{} }
get_changelogpackage_name, version?GitHub Releases / raw CHANGELOG.md{ source, repo, body | content }
check_version_existspackage_name, versionGET /{pkg}/{version}{ package, version, exists }
Package Info Tools
get_package_infopackage_nameGET /{pkg} + /{pkg}/latestname, desc, author, license, maintainers, dates
get_package_readmepackage_nameGET /{pkg}{ content, truncated, has_readme }
get_package_keywordspackage_nameGET /{pkg}/latest{ count, keywords[] }
get_package_repositorypackage_nameGET /{pkg}/latest{ repository, github_slug }
get_package_homepagepackage_nameGET /{pkg}/latest{ homepage, npm_url }
get_package_licensepackage_nameGET /{pkg}/latest{ license, licenses }
get_package_sizepackage_name, version?GET /{pkg}/{version|latest}{ unpacked_bytes, file_count }
Dependency Tools
get_dependenciespackage_name, version?GET /{pkg}/{version|latest}{ count, dependencies{} }
get_peer_dependenciespackage_name, version?GET /{pkg}/{version|latest}{ peer_dependencies, peer_meta }
get_dev_dependenciespackage_name, version?GET /{pkg}/{version|latest}{ count, dev_dependencies{} }
get_dependency_treepackage_name, version?, max_depth?Recursive fetch (asyncio.gather)Nested tree with dedup caching
check_peer_compatibilitypackage_name, version?, installed{}Manifest + built-in semver matcher{ all_ok, report[] }
Security & Health Tools
check_vulnerabilitiespackage_name, version?POST api.osv.dev/v1/query{ vulnerable, count, advisories[] }
get_deprecation_statuspackage_name, version?GET /{pkg} or /{pkg}/{version}{ deprecated, message, deprecated_count }
check_maintainer_activitypackage_nameGET /{pkg} (reads time){ status, days_since_last_publish, abandoned }
get_download_statspackage_nameapi.npmjs.org/downloads/point/*{ last_day, last_week, last_month, popularity }
check_typosquat_riskpackage_nameLocal Levenshtein vs. top-50 list{ risk, closest_popular, edit_distance }
get_download_trendpackage_name, period?api.npmjs.org/downloads/range{ trend, total, series[], average_daily }
compare_popularitypackages[], period?api.npmjs.org/downloads/point bulk + parallel scoped{ winner, ranking[{rank, share}] }
get_download_by_versionpackage_name, top_n?api.npmjs.org/versions/{pkg}/last-week{ top_versions[], most_popular_major }
get_vulnerability_detailsvuln_idGET api.osv.dev/v1/vulns/{id}{ summary, severity, affected_npm[], references[] }
audit_all_dependenciespackage_json, dependencies{}, include_dev?POST api.osv.dev/v1/querybatch{ vulnerable_count, packages[], unresolved[] }
check_supply_chain_riskpackage_name, version?Manifest + OSV batch{ risk, vulnerable_deps, details[] }
get_patched_versionvuln_idGET api.osv.dev/v1/vulns/{id}{ patches[{package, first_patched}] }
Compatibility & Update Tools
check_node_compatibilitypackage_name, version?GET /{pkg}/{version|latest}{ engines, node, npm, yarn }
compare_versionspackage_name, from_version, to_versionParallel manifest fetch{ dependencies, dev_dependencies, peer_dependencies, engines }
get_breaking_changespackage_name, from_version, to_versionParallel manifest fetch{ breaking_major_bumps[], likely_breaking }
resolve_semverpackage_name, version_rangeGET /{pkg} + local matcher{ resolved, matched_count, candidates[] }
check_outdatedpackages{ name: version }Parallel /{pkg}/latest fetches{ outdated[], up_to_date[], errors[] }
Search & Discovery Tools
search_packagesquery, limit?GET /-/v1/search{ count, results[] } (scored hits)
get_similar_packagespackage_name, limit?Manifest keywords → GET /-/v1/search{ keywords_used, results[] }
get_packages_by_authorusername, limit?GET /-/v1/search?text=author:{u}{ author, count, packages[] }
get_organization_packagesscope, limit?GET /-/v1/search?text=@scope{ scope, count, packages[] }
Utility Tools
batch_get_versionspackage_names[]Parallel /{pkg}/latest{ versions{}, errors{} }
validate_package_jsonpackage_json, dependencies{}Per-dep GET /{pkg} + matcher{ valid, summary, results[] }
generate_install_commandpackages[], dev?, exact?Local (no HTTP){ commands: { npm, pnpm, yarn, bun } }
resolve_cdn_urlpackage_name, version?, file?GET /{pkg}/latest (only if no version){ jsdelivr, unpkg, esm_sh }
Bundle Size Tools (bundlephobia.com)
get_bundle_sizepackage_name, version?GET bundlephobia.com/api/size{ size_bytes, gzip_bytes, dependency_count, has_js_module }
get_bundle_size_historypackage_name, limit?GET bundlephobia.com/api/package-history{ trend, delta_percent, history[] }
check_treeshakeablepackage_name, version?GET bundlephobia.com/api/size{ treeshakeable, has_esm, side_effects_declared_none, reason }
compare_bundle_sizespackages[]Parallel bundlephobia fetches{ lightest, ranking[] }
get_bundle_size_impactpackage_name, version?GET bundlephobia.com/api/size{ impact, summary, size_bytes, gzip_bytes }
Module & Compatibility Tools
check_esm_supportpackage_name, version?Manifest (exports, module, type){ esm, reasons[], type, module }
check_cjs_supportpackage_name, version?Manifest (main, exports.require){ cjs, reasons[], main }
check_typescript_supportpackage_name, version?Manifest + /{'@types/'}pkg/latest{ summary, built_in_types, definitelytyped }
get_exports_mappackage_name, version?Manifest{ exports, subpaths[], has_exports_map }
check_browser_compatiblepackage_name, version?Manifest signals{ browser_compatible, reason, browser_field }
check_deno_compatiblepackage_name, version?Manifest + JSR lookup{ deno_compatible, has_esm, has_cjs, on_jsr }
get_package_on_jsrpackage_nameGET api.jsr.io/scopes/{scope}/packages/{pkg}{ on_jsr, jsr_url, latest, runtimes }
Migration & Upgrade Tools
suggest_upgrade_pathpackage_name, from_version, to_version?GET /{pkg} + per-hop get_breaking_changes{ hop_count, hops[{major, recommended, breaking_dep_bumps}] }
find_replacement_packagepackage_nameGET /{pkg} (manifest deprecated){ deprecated, replacement, message }
check_migration_guidepackage_nameGitHub raw + contents API{ path, content, truncated } or { matches[] }
Error handling. All tools route through a shared _fetch_json helper with a 10 s timeout. Registry 404s and network errors surface to Claude as clear ValueErrors that the model can reason about and retry.

Example queries

Ask Claude any of these in natural language — it picks the right tool automatically:

"What's the latest version of express?"
Calls get_latest_version(package_name="express").
"How many versions of lodash exist on npm?"
Calls get_all_versions and reports the count.
"What are react's dist-tags right now?"
Calls get_dist_tags(package_name="react") — shows latest, beta, next, canary.
"What changed in react 18.2.0?"
Calls get_changelog(package_name="react", version="18.2.0") — returns GitHub release body.
"How big is the react package when unpacked?"
Calls get_package_size — returns bytes, human-readable size, and file count.
"What license does express use?"
Calls get_package_license(package_name="express").
"Show me the README for zod"
Calls get_package_readme and streams back the markdown.
"Where is vue's source hosted?"
Calls get_package_repository — returns URL and GitHub slug.
"What are the peer dependencies of react-dom?"
Calls get_peer_dependencies — shows the required react range.
"I have react 18.2.0 — is it compatible with react-dom 19?"
Calls check_peer_compatibility — returns a per-peer yes/no.
"Show me the full dep tree of express to depth 2"
Calls get_dependency_tree with max_depth=2.

What is MCP?

The Model Context Protocol (MCP) is a communication layer that provides Claude with context and tools — without you writing tedious integration code.

MCP architecture diagram

An MCP Client (your server) connects to MCP Servers that contain tools, prompts, and resources. Each MCP Server acts as an interface to some outside service — GitHub, Slack, a database, or in our case, the npm Registry.

How MCP Works

MCP shifts the burden of tool definitions and execution onto MCP Servers.

How MCP works diagram

Instead of authoring all those GitHub tools yourself, an MCP Server for GitHub handles it — wrapping functionality and exposing it as a standardized set of tools. Any MCP-aware client (Claude Code, Claude Desktop, Cursor) can immediately use it.

MCP Servers Explained

MCP Servers provide access to data or functionality implemented by some outside service.

MCP Server for GitHub example

The MCP Server for GitHub contains tools like get_repos() and connects directly to GitHub's API. Your client talks to the MCP server, which handles all the GitHub-specific details.

MCP Message Types

Once connected, the client and server exchange specific message types defined in the MCP specification.

List Tools
ListTools request and result

ListToolsRequest / ListToolsResult — the client asks the server "what tools do you provide?" and gets back a list of available tools.

Call Tool
CallTool request and result

CallToolRequest / CallToolResult — the client asks the server to run a specific tool with given arguments, then receives the results.

Transports

These messages are transport-agnostic — they travel over any channel that reliably carries JSON-RPC. In practice:

  • stdio — local servers (what Depverse uses).
  • HTTP / SSE / WebSockets — remote servers.

You're free to pick whichever transport fits your setup. The message contract stays the same.

End-to-End Flow

A user asks "What repositories do I have?" — here's how the query flows through every piece of the system.

  1. User Query — the user submits their question to your server.
  2. Tool Discovery — your server needs to know what tools are available to send to Claude.
  3. List Tools Exchange — your server asks the MCP client for available tools.
  4. MCP Communication — the MCP client sends a ListToolsRequest and receives a ListToolsResult.
  5. Claude Request — your server sends the query + tools to Claude.
  6. Tool Use Decision — Claude decides it needs to call a tool.
  7. Tool Execution — your server asks the MCP client to run the tool.
  8. External API Call — the MCP server makes the actual GitHub API call.
  9. Results Flow Back — GitHub responds, data flows back as a CallToolResult.
  10. Tool Result to Claude — results sent back to Claude.
  11. Final Response — Claude formulates an answer.
  12. User Gets Answer — response delivered to the user.
Full MCP request flow sequence diagram
Understanding this flow is crucial — you'll see all these pieces when building your own MCP clients and servers in the next section.

Common Questions

Who authors MCP Servers?

Anyone can create an MCP server implementation. Often, service providers themselves will make their own official implementations — for example, AWS might release an official MCP server with tools for their various services.

How is this different from calling APIs directly?

MCP servers provide tool schemas and functions already defined for you. If you call an API directly, you'll be authoring those tool definitions on your own. MCP saves you that implementation work.

Isn't MCP just the same as tool use?

A common misconception. MCP servers and tool use are complementary but different concepts. MCP servers provide tool schemas and functions already defined for you; tool use is how Claude actually calls those tools. The key difference is who does the work — with MCP, someone else has already implemented the tools for you.

The benefit: instead of maintaining a complex set of integrations yourself, you leverage MCP servers that handle the heavy lifting of connecting to external services.

Building an MCP Server

Writing an MCP server becomes much simpler when you use the official Python SDK — define tools with decorators and let the SDK handle the JSON schema heavy lifting.

Our MCP Server with document tools
Example — Document Management Server

Two core tools:

  • Read a document — fetch content by document ID.
  • Update a document — overwrite content for a given ID.
In-memory storage

All documents live in a plain Python dictionary — keys are document IDs, values are the content.

docs = {
  "document.pdf":     "...",
  "spreadsheet.xlsx": "...",
  "report.txt":       "...",
  "spec.md":          "...",
}

Project Walkthrough

A CLI chat app where Claude talks to a local FastMCP document server via stdio.

File structure

cli_project/
├── main.py            # entry point: wires everything
├── mcp_server.py      # FastMCP server (docs dict)
├── mcp_client.py      # MCPClient wrapper (stdio)
├── .env               # ANTHROPIC_API_KEY, CLAUDE_MODEL
└── core/
    ├── claude.py      # Anthropic SDK wrapper
    ├── chat.py        # base chat loop
    ├── cli_chat.py    # @mentions + /commands
    ├── cli.py         # prompt-toolkit UI
    └── tools.py       # tool-call dispatch

How it runs

  1. main.py loads .env, creates a Claude service, then spawns mcp_server.py as a subprocess over stdio.
  2. MCPClient opens a session, calls list_tools() / list_prompts() to discover what the server offers.
  3. CliChat takes user input, resolves @docs and /commands, sends messages to Claude.
  4. Claude decides to call a toolMCPClient.call_tool() → the server runs it on the in-memory docs dict.
  5. Tool result flows back to Claude → final answer → displayed in the CLI.

Key features

@ Document Mentions

Type @deposition.md to inline that document's content into Claude's context.

/ Slash Commands

Type /summarize report.pdf to invoke a prompt defined by the MCP server.

🛠 Tool Use

Claude autonomously calls read_doc / edit_doc tools exposed by the server.

Setting Up the MCP Server

The Python MCP SDK makes server creation straightforward — initialize with a single line.

Initialize the server

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("DocumentMCP", log_level="ERROR")

FastMCP is the high-level helper from the MCP SDK that registers tools, resources, and prompts behind a clean decorator API.

Storage — plain Python dict

docs = {
    "deposition.md":  "... Angela Smith, P.E.",
    "report.pdf":     "... 20m condenser tower.",
    "financials.docx": "... budget and expenditures",
    "outlook.pdf":    "... projected performance",
    "plan.md":        "... implementation steps",
    "spec.txt":       "... technical requirements",
}
Tool definitions with decorators — instead of writing JSON schemas by hand, the SDK uses Python type hints and Pydantic Field descriptions to auto-generate the schema Claude consumes.

Reader Tool

The first tool reads document contents by ID. The decorator registers it; the function is the implementation.

@mcp.tool(
    name="read_doc_contents",
    description="Read the contents of a document and return it as a string."
)
def read_document(
    doc_id: str = Field(description="Id of the document to read")
):
    if doc_id not in docs:
        raise ValueError(f"Doc with id {doc_id} not found")

    return docs[doc_id]
1 — Decorator

@mcp.tool(...) registers the function as a callable tool. name and description are what Claude sees when discovering tools.

2 — Type Hints + Field

Parameters use Python type hints. Field(description=...) from Pydantic gives Claude a clear explanation of each argument.

3 — Error Handling

Raising ValueError is idiomatic — the SDK converts Python exceptions into a tool error Claude can reason about.

Editor Tool & SDK Benefits

The second tool performs simple find-and-replace operations on documents.

@mcp.tool(
    name="edit_document",
    description="Edit a document by replacing a string in the documents content with a new string."
)
def edit_document(
    doc_id:  str = Field(description="Id of the document that will be edited"),
    old_str: str = Field(description="The text to replace. Must match exactly, including whitespace."),
    new_str: str = Field(description="The new text to insert in place of the old text."),
):
    if doc_id not in docs:
        raise ValueError(f"Doc with id {doc_id} not found")

    docs[doc_id] = docs[doc_id].replace(old_str, new_str)

Key benefits of the SDK approach

  • No manual JSON schema writing required.
  • Type hints provide automatic validation.
  • Clear parameter descriptions help Claude understand tool usage.
  • Error handling integrates naturally with Python exceptions.
  • Tool registration happens automatically through decorators.

Register Your Server with Claude Code

Claude Code can itself consume MCP servers. Register yours and your tools become available in every conversation — no API key, no custom CLI.

Project scope (recommended)

Writes a .mcp.json file in the project directory. Server is only active when Claude Code is opened inside this folder.

$ cd /path/to/your_project
$ claude mcp add your-server --scope project \
    -- uv --directory "/path/to/your_project" \
        run mcp_server.py

User scope (everywhere)

Adds it to your Claude Code user config. Available in every project you open on this machine.

$ claude mcp add your-server --scope user \
    -- uv --directory "/path/to/your_project" \
        run mcp_server.py

Anatomy of the command

claude mcp add <name>           # how Claude Code labels it
  --scope project|user      # where to store the config
  --                       # everything after is the launch cmd
  uv --directory ... run mcp_server.py   # spawns server over stdio

Verify the registration

  1. Restart Claude Code — MCP servers are loaded at startup.
  2. Run /mcp inside Claude Code → the server should appear with its tools listed.
  3. Ask Claude to use it — e.g. "What's the latest version of react on npm?".

Other useful commands

claude mcp list
Show all registered servers.
claude mcp get <name>
Show the config for one server.
claude mcp remove <name>
Unregister the server.
/mcp
Inspect servers inside Claude Code.

Troubleshooting

"Server not showing up in /mcp"

  • Run claude mcp list — is Depverse listed?
  • Restart Claude Code fully (MCP servers are loaded at startup).
  • Check the --directory path in your mcp add command is absolute and points to the repo root.

"Tool calls fail with 'npm package not found'"

  • Verify the exact spelling — npm names are case-sensitive.
  • Scoped packages must include the scope: @types/node, not types/node.
  • Private packages behind auth aren't supported — Depverse only reads the public registry.

"Changelog tool returns an error"

  • Not every package links a GitHub repo in its manifest — in that case, no changelog source exists.
  • GitHub API rate limits unauthenticated requests. Rare for a dev machine, but possible on shared IPs.