Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a1e5a9a9b1 |
99 changed files with 4143 additions and 8238 deletions
6
.github/dependabot.yml
vendored
6
.github/dependabot.yml
vendored
|
|
@ -1,6 +0,0 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: 'github-actions'
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: 'weekly'
|
||||
44
.github/workflows/python.yml
vendored
44
.github/workflows/python.yml
vendored
|
|
@ -2,13 +2,11 @@ name: Python
|
|||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
# Superseded runs on the same ref are cancelled (e.g. rapid amend-pushes),
|
||||
# except on main, where every merge keeps its own CI run
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
detect-packages:
|
||||
|
|
@ -36,10 +34,10 @@ jobs:
|
|||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
uses: astral-sh/setup-uv@v3
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v7
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version-file: "src/${{ matrix.package }}/.python-version"
|
||||
|
||||
|
|
@ -73,10 +71,10 @@ jobs:
|
|||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
uses: astral-sh/setup-uv@v3
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v7
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version-file: "src/${{ matrix.package }}/.python-version"
|
||||
|
||||
|
|
@ -93,7 +91,31 @@ jobs:
|
|||
run: uv build
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: dist-${{ matrix.package }}
|
||||
path: src/${{ matrix.package }}/dist/
|
||||
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build, detect-packages]
|
||||
if: github.event_name == 'release'
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
package: ${{ fromJson(needs.detect-packages.outputs.packages) }}
|
||||
name: Publish ${{ matrix.package }}
|
||||
|
||||
environment: release
|
||||
permissions:
|
||||
id-token: write # Required for trusted publishing
|
||||
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: dist-${{ matrix.package }}
|
||||
path: dist/
|
||||
|
||||
- name: Publish package to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
|
|
|
|||
9
.github/workflows/readme-pr-check.yml
vendored
9
.github/workflows/readme-pr-check.yml
vendored
|
|
@ -1,10 +1,7 @@
|
|||
name: README PR Check
|
||||
|
||||
on:
|
||||
# pull_request_target, not pull_request: fork PRs get a read-only token under
|
||||
# pull_request, so the label and comment calls fail with 403. This job only
|
||||
# calls the API and never checks out PR code, so the write token is safe.
|
||||
pull_request_target:
|
||||
pull_request:
|
||||
types: [opened]
|
||||
paths:
|
||||
- 'README.md'
|
||||
|
|
@ -13,7 +10,7 @@ on:
|
|||
|
||||
jobs:
|
||||
check-readme-only:
|
||||
if: github.event_name == 'pull_request_target'
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
|
|
@ -49,7 +46,7 @@ jobs:
|
|||
body: [
|
||||
'Thanks for your contribution!',
|
||||
'',
|
||||
'**We are no longer accepting PRs to add new servers to the README.** The third-party server list has been removed from this README in favor of the [MCP Server Registry](https://registry.modelcontextprotocol.io/).',
|
||||
'**We are no longer accepting PRs to add new servers to the README.** The server lists are deprecated and will eventually be removed entirely, replaced by the registry.',
|
||||
'',
|
||||
'👉 **To add a new MCP server:** Please publish it to the [MCP Server Registry](https://github.com/modelcontextprotocol/registry) instead. You can browse published servers at [registry.modelcontextprotocol.io](https://registry.modelcontextprotocol.io/).',
|
||||
'',
|
||||
|
|
|
|||
43
.github/workflows/release.yml
vendored
43
.github/workflows/release.yml
vendored
|
|
@ -1,7 +1,9 @@
|
|||
name: Release
|
||||
name: Automatic Release Creation
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 10 * * *'
|
||||
|
||||
jobs:
|
||||
create-metadata:
|
||||
|
|
@ -25,7 +27,7 @@ jobs:
|
|||
echo "Using last release hash: ${HASH}"
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
uses: astral-sh/setup-uv@v5
|
||||
|
||||
- name: Create version name
|
||||
id: create-version
|
||||
|
|
@ -41,7 +43,7 @@ jobs:
|
|||
cat RELEASE_NOTES.md
|
||||
|
||||
- name: Release notes
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: release-notes
|
||||
path: RELEASE_NOTES.md
|
||||
|
|
@ -77,7 +79,7 @@ jobs:
|
|||
fetch-depth: 0
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
uses: astral-sh/setup-uv@v5
|
||||
|
||||
- name: Update packages
|
||||
run: |
|
||||
|
|
@ -121,10 +123,10 @@ jobs:
|
|||
ref: ${{ needs.create-metadata.outputs.version }}
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
uses: astral-sh/setup-uv@v5
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v7
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version-file: "src/${{ matrix.package }}/.python-version"
|
||||
|
||||
|
|
@ -136,15 +138,6 @@ jobs:
|
|||
working-directory: src/${{ matrix.package }}
|
||||
run: uv run --frozen pyright
|
||||
|
||||
- name: Run tests
|
||||
working-directory: src/${{ matrix.package }}
|
||||
run: |
|
||||
if [ -d "tests" ] || [ -d "test" ]; then
|
||||
uv run --frozen pytest
|
||||
else
|
||||
echo "No tests directory, skipping"
|
||||
fi
|
||||
|
||||
- name: Build package
|
||||
working-directory: src/${{ matrix.package }}
|
||||
run: uv build
|
||||
|
|
@ -153,7 +146,6 @@ jobs:
|
|||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
packages-dir: src/${{ matrix.package }}/dist
|
||||
skip-existing: true # re-runs tolerate already-uploaded files
|
||||
|
||||
publish-npm:
|
||||
needs: [update-packages, create-metadata]
|
||||
|
|
@ -164,25 +156,18 @@ jobs:
|
|||
package: ${{ fromJson(needs.create-metadata.outputs.npm_packages) }}
|
||||
name: Build ${{ matrix.package }}
|
||||
environment: release
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write # Required for npm trusted publishing (OIDC)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.create-metadata.outputs.version }}
|
||||
|
||||
- uses: actions/setup-node@v7
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 22
|
||||
cache: npm
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
# OIDC trusted publishing requires npm >=11.5.1; Node 22's bundled npm is 10.x.
|
||||
- name: Ensure npm CLI supports OIDC trusted publishing
|
||||
run: npm install -g npm@^11.5.1
|
||||
|
||||
- name: Install dependencies
|
||||
working-directory: src/${{ matrix.package }}
|
||||
run: npm ci
|
||||
|
|
@ -197,22 +182,16 @@ jobs:
|
|||
fi
|
||||
echo "Version $VERSION is new, proceeding with publish"
|
||||
|
||||
- name: Run tests
|
||||
working-directory: src/${{ matrix.package }}
|
||||
run: npm test --if-present
|
||||
|
||||
- name: Build package
|
||||
working-directory: src/${{ matrix.package }}
|
||||
run: npm run build
|
||||
|
||||
# Authenticates via OIDC trusted publishing (no token) — each package's
|
||||
# npm trusted publisher is bound to this workflow + the release environment
|
||||
- name: Publish package
|
||||
working-directory: src/${{ matrix.package }}
|
||||
run: |
|
||||
npm publish --access public
|
||||
env:
|
||||
NPM_CONFIG_PROVENANCE: "true"
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
create-release:
|
||||
needs: [update-packages, create-metadata, publish-pypi, publish-npm]
|
||||
|
|
@ -228,7 +207,7 @@ jobs:
|
|||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Download release notes
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: release-notes
|
||||
|
||||
|
|
|
|||
46
.github/workflows/typescript.yml
vendored
46
.github/workflows/typescript.yml
vendored
|
|
@ -2,13 +2,11 @@ name: TypeScript
|
|||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
# Superseded runs on the same ref are cancelled (e.g. rapid amend-pushes),
|
||||
# except on main, where every merge keeps its own CI run
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
detect-packages:
|
||||
|
|
@ -34,7 +32,7 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: actions/setup-node@v7
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 22
|
||||
cache: npm
|
||||
|
|
@ -57,7 +55,7 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: actions/setup-node@v7
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 22
|
||||
cache: npm
|
||||
|
|
@ -70,3 +68,35 @@ jobs:
|
|||
working-directory: src/${{ matrix.package }}
|
||||
run: npm run build
|
||||
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build, detect-packages]
|
||||
if: github.event_name == 'release'
|
||||
environment: release
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
package: ${{ fromJson(needs.detect-packages.outputs.packages) }}
|
||||
name: Publish ${{ matrix.package }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 22
|
||||
cache: npm
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
|
||||
- name: Install dependencies
|
||||
working-directory: src/${{ matrix.package }}
|
||||
run: npm ci
|
||||
|
||||
- name: Publish package
|
||||
working-directory: src/${{ matrix.package }}
|
||||
run: npm publish --access public
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
|
|
|||
|
|
@ -1,94 +0,0 @@
|
|||
# Additional links
|
||||
|
||||
This is a curated collection of community-built frameworks and resources that simplify building MCP servers and clients, along with other useful tools, libraries, and references for working with MCP.
|
||||
|
||||
## 📚 Frameworks
|
||||
|
||||
### For servers
|
||||
|
||||
* **[Anubis MCP](https://github.com/zoedsoupe/anubis-mcp)** (Elixir) - A high-performance and high-level Model Context Protocol (MCP) implementation in Elixir. Think like "Live View" for MCP.
|
||||
* **[ModelFetch](https://github.com/phuctm97/modelfetch/)** (TypeScript) - Runtime-agnostic SDK to create and deploy MCP servers anywhere TypeScript/JavaScript runs
|
||||
* **[EasyMCP](https://github.com/zcaceres/easy-mcp/)** (TypeScript)
|
||||
* **[FastAPI to MCP auto generator](https://github.com/tadata-org/fastapi_mcp)** – A zero-configuration tool for automatically exposing FastAPI endpoints as MCP tools by **[Tadata](https://tadata.com/)**
|
||||
* **[FastMCP](https://github.com/punkpeye/fastmcp)** (TypeScript)
|
||||
* **[Foobara MCP Connector](https://github.com/foobara/mcp-connector)** - Easily expose Foobara commands written in Ruby as tools via MCP
|
||||
* **[Foxy Contexts](https://github.com/strowk/foxy-contexts)** – A library to build MCP servers in Golang by **[strowk](https://github.com/strowk)**
|
||||
* **[Higress MCP Server Hosting](https://github.com/alibaba/higress/tree/main/plugins/wasm-go/mcp-servers)** - A solution for hosting MCP Servers by extending the API Gateway (based on Envoy) with wasm plugins.
|
||||
* **[MCP Declarative Java SDK](https://github.com/codeboyzhou/mcp-declarative-java-sdk)** Annotation-driven MCP servers development with Java, no Spring Framework Required, minimize dependencies as much as possible.
|
||||
* **[MCP-Framework](https://mcp-framework.com)** Build MCP servers with elegance and speed in TypeScript. Comes with a CLI to create your project with `mcp create app`. Get started with your first server in under 5 minutes by **[Alex Andru](https://github.com/QuantGeekDev)**
|
||||
* **[MCP Plexus](https://github.com/Super-I-Tech/mcp_plexus)**: A secure, **multi-tenant** and Multi-user MCP python server framework built to integrate easily with external services via OAuth 2.1, offering scalable and robust solutions for managing complex AI applications.
|
||||
* **[mcp_sse (Elixir)](https://github.com/kEND/mcp_sse)** An SSE implementation in Elixir for rapidly creating MCP servers.
|
||||
* **[mxcp](https://github.com/raw-labs/mxcp)** (Python) - Open-source framework for building enterprise-grade MCP servers using just YAML, SQL, and Python, with built-in auth, monitoring, ETL and policy enforcement.
|
||||
* **[Next.js MCP Server Template](https://github.com/vercel-labs/mcp-for-next.js)** (Typescript) - A starter Next.js project that uses the MCP Adapter to allow MCP clients to connect and access resources.
|
||||
* **[PayMCP](https://github.com/blustAI/paymcp)** (Python & TypeScript) - Lightweight payments layer for MCP servers: turn tools into paid endpoints with a two-line decorator. [PyPI](https://pypi.org/project/paymcp/) · [npm](https://www.npmjs.com/package/paymcp) · [TS repo](https://github.com/blustAI/paymcp-ts)
|
||||
* **[Perl SDK](https://github.com/mojolicious/mojo-mcp)** - An SDK for building MCP servers and clients with the Perl programming language.
|
||||
* **[Quarkus MCP Server SDK](https://github.com/quarkiverse/quarkus-mcp-server)** (Java)
|
||||
- **[R mcptools](https://github.com/posit-dev/mcptools)** - An R SDK for creating R-based MCP servers and retrieving functionality from third-party MCP servers as R functions.
|
||||
* **[SAP ABAP MCP Server SDK](https://github.com/abap-ai/mcp)** - Build SAP ABAP based MCP servers. ABAP 7.52 based with 7.02 downport; runs on R/3 & S/4HANA on-premises, currently not cloud-ready.
|
||||
* **[Spring AI MCP Server](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-starter-docs.html)** - Provides auto-configuration for setting up an MCP server in Spring Boot applications.
|
||||
* **[Template MCP Server](https://github.com/mcpdotdirect/template-mcp-server)** - A CLI tool to create a new Model Context Protocol server project with TypeScript support, dual transport options, and an extensible structure
|
||||
* **[AgentR Universal MCP SDK](https://github.com/universal-mcp/universal-mcp)** - A python SDK to build MCP Servers with inbuilt credential management by **[Agentr](https://agentr.dev/home)**
|
||||
* **[Vercel MCP Adapter](https://github.com/vercel/mcp-adapter)** (TypeScript) - A simple package to start serving an MCP server on most major JS meta-frameworks including Next, Nuxt, Svelte, and more.
|
||||
* **[PHP MCP Server](https://github.com/php-mcp/server)** (PHP) - Core PHP implementation for the Model Context Protocol (MCP) server
|
||||
|
||||
### For clients
|
||||
|
||||
* **[codemirror-mcp](https://github.com/marimo-team/codemirror-mcp)** - CodeMirror extension that implements the Model Context Protocol (MCP) for resource mentions and prompt commands
|
||||
* **[llm-analysis-assistant](https://github.com/xuzexin-hz/llm-analysis-assistant)** <img height="12" width="12" src="https://raw.githubusercontent.com/xuzexin-hz/llm-analysis-assistant/refs/heads/main/src/llm_analysis_assistant/pages/html/imgs/favicon.ico" alt="Langfuse Logo" /> - A very streamlined mcp client that supports calling and monitoring stdio/sse/streamableHttp, and can also view request responses through the /logs page. It also supports monitoring and simulation of ollama/openai interface.
|
||||
* **[MCP-Agent](https://github.com/lastmile-ai/mcp-agent)** - A simple, composable framework to build agents using Model Context Protocol by **[LastMile AI](https://www.lastmileai.dev)**
|
||||
* **[Spring AI MCP Client](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-client-boot-starter-docs.html)** - Provides auto-configuration for MCP client functionality in Spring Boot applications.
|
||||
* **[MCP CLI Client](https://github.com/vincent-pli/mcp-cli-host)** - A CLI host application that enables Large Language Models (LLMs) to interact with external tools through the Model Context Protocol (MCP).
|
||||
* **[OpenMCP Client](https://github.com/LSTM-Kirigaya/openmcp-client/)** - An all-in-one vscode/trae/cursor plugin for MCP server debugging. [Document](https://kirigaya.cn/openmcp/) & [OpenMCP SDK](https://kirigaya.cn/openmcp/sdk-tutorial/).
|
||||
* **[PHP MCP Client](https://github.com/php-mcp/client)** - Core PHP implementation for the Model Context Protocol (MCP) Client
|
||||
* **[Runbear](https://runbear.io/solutions/integrations/slack/mcp)** - No-code MCP client for team chat platforms, such as Slack, Microsoft Teams, and Discord.
|
||||
|
||||
## 📚 Resources
|
||||
|
||||
- **[A2A-MCP Java Bridge](https://github.com/vishalmysore/a2ajava)** - A2AJava brings powerful A2A-MCP integration directly into your Java applications. It enables developers to annotate standard Java methods and instantly expose them as MCP Server, A2A-discoverable actions — with no boilerplate or service registration overhead.
|
||||
- **[AiMCP](https://www.aimcp.info)** - A collection of MCP clients&servers to find the right mcp tools by **[Hekmon](https://github.com/hekmon8)**
|
||||
- **[Awesome Crypto MCP Servers by badkk](https://github.com/badkk/awesome-crypto-mcp-servers)** - A curated list of MCP servers by **[Luke Fan](https://github.com/badkk)**
|
||||
- **[Awesome MCP Servers by appcypher](https://github.com/appcypher/awesome-mcp-servers)** - A curated list of MCP servers by **[Stephen Akinyemi](https://github.com/appcypher)**
|
||||
- **[Awesome MCP Servers by punkpeye](https://github.com/punkpeye/awesome-mcp-servers)** (**[website](https://glama.ai/mcp/servers)**) - A curated list of MCP servers by **[Frank Fiegel](https://github.com/punkpeye)**
|
||||
- **[Awesome MCP Servers by wong2](https://github.com/wong2/awesome-mcp-servers)** (**[website](https://mcpservers.org)**) - A curated list of MCP servers by **[wong2](https://github.com/wong2)**
|
||||
- **[Awesome Remote MCP Servers by JAW9C](https://github.com/jaw9c/awesome-remote-mcp-servers)** - A curated list of **remote** MCP servers, including their authentication support by **[JAW9C](https://github.com/jaw9c)**
|
||||
- **[Discord Server](https://glama.ai/mcp/discord)** – A community discord server dedicated to MCP by **[Frank Fiegel](https://github.com/punkpeye)**
|
||||
- **[Install This MCP](https://installthismcp.com)** - Reduce Installation Friction with beautiful installation guides
|
||||
- **[Klavis AI](https://www.klavis.ai)** - Open Source MCP Infra. Hosted MCP servers and MCP clients on Slack and Discord.
|
||||
- **[MCP Badges](https://github.com/mcpx-dev/mcp-badges)** – Quickly highlight your MCP project with clear, eye-catching badges, by **[Ironben](https://github.com/nanbingxyz)**
|
||||
- **[MCPProxy](https://github.com/smart-mcp-proxy/mcpproxy-go)** - Open-source local app that enables access to multiple MCP servers and thousands of tools with intelligent discovery via MCP protocol, runs servers in isolated environments, and features automatic quarantine protection against malicious tools.
|
||||
- **[MCPRepository.com](https://mcprepository.com/)** - A repository that indexes and organizes all MCP servers for easy discovery.
|
||||
- **[mcp-cli](https://github.com/wong2/mcp-cli)** - A CLI inspector for the Model Context Protocol by **[wong2](https://github.com/wong2)**
|
||||
- **[mcp-dockmaster](https://mcp-dockmaster.com)** - An Open-Sourced UI to install and manage MCP servers for Windows, Linux and macOS.
|
||||
- **[mcp-get](https://mcp-get.com)** - Command line tool for installing and managing MCP servers by **[Michael Latman](https://github.com/michaellatman)**
|
||||
- **[mcp-guardian](https://github.com/eqtylab/mcp-guardian)** - GUI application + tools for proxying / managing control of MCP servers by **[EQTY Lab](https://eqtylab.io)**
|
||||
- **[MCP Linker](https://github.com/milisp/mcp-linker)** - A cross-platform Tauri GUI tool for one-click setup and management of MCP servers, supporting Claude Desktop, Cursor, Windsurf, VS Code, Cline, and Neovim.
|
||||
- **[mcp-manager](https://github.com/zueai/mcp-manager)** - Simple Web UI to install and manage MCP servers for Claude Desktop by **[Zue](https://github.com/zueai)**
|
||||
- **[MCP Marketplace Web Plugin](https://github.com/AI-Agent-Hub/mcp-marketplace)** MCP Marketplace is a small Web UX plugin to integrate with AI applications, Support various MCP Server API Endpoint (e.g pulsemcp.com/deepnlp.org and more). Allowing user to browse, paginate and select various MCP servers by different categories. [Pypi](https://pypi.org/project/mcp-marketplace) | [Maintainer](https://github.com/AI-Agent-Hub) | [Website](http://www.deepnlp.org/store/ai-agent/mcp-server)
|
||||
- **[mcp.natoma.ai](https://mcp.natoma.ai)** – A Hosted MCP Platform to discover, install, manage and deploy MCP servers by **[Natoma Labs](https://www.natoma.ai)**
|
||||
- **[mcp.run](https://mcp.run)** - A hosted registry and control plane to install & run secure + portable MCP Servers.
|
||||
- **[MCPHub](https://www.mcphub.com)** - Website to list high quality MCP servers and reviews by real users. Also provide online chatbot for popular LLM models with MCP server support.
|
||||
- **[MCP Router](https://mcp-router.net)** – Free Windows and macOS app that simplifies MCP management while providing seamless app authentication and powerful log visualization by **[MCP Router](https://github.com/mcp-router/mcp-router)**
|
||||
- **[MCP Servers Hub](https://github.com/apappascs/mcp-servers-hub)** (**[website](https://mcp-servers-hub-website.pages.dev/)**) - A curated list of MCP servers by **[apappascs](https://github.com/apappascs)**
|
||||
- **[MCPServers.com](https://mcpservers.com)** - A growing directory of high-quality MCP servers with clear setup guides for a variety of MCP clients. Built by the team behind the **[Highlight MCP client](https://highlightai.com/)**
|
||||
- **[MCP Servers Rating and User Reviews](http://www.deepnlp.org/store/ai-agent/mcp-server)** - Website to rate MCP servers, write authentic user reviews, and [search engine for agent & mcp](http://www.deepnlp.org/search/agent)
|
||||
- **[MCP Sky](https://bsky.app/profile/brianell.in/feed/mcp)** - Bluesky feed for MCP related news and discussion by **[@brianell.in](https://bsky.app/profile/brianell.in)**
|
||||
- **[MCP X Community](https://x.com/i/communities/1861891349609603310)** – A X community for MCP by **[Xiaoyi](https://x.com/chxy)**
|
||||
- **[MCPHub](https://github.com/Jeamee/MCPHub-Desktop)** – An Open Source macOS & Windows GUI Desktop app for discovering, installing and managing MCP servers by **[Jeamee](https://github.com/jeamee)**
|
||||
- **[mcpm](https://github.com/pathintegral-institute/mcpm.sh)** ([website](https://mcpm.sh)) - MCP Manager (MCPM) is a Homebrew-like service for managing Model Context Protocol (MCP) servers across clients by **[Pathintegral](https://github.com/pathintegral-institute)**
|
||||
- **[MCPVerse](https://mcpverse.dev)** - A portal for creating & hosting authenticated MCP servers and connecting to them securely.
|
||||
- **[MCP Servers Search](https://github.com/atonomus/mcp-servers-search)** - An MCP server that provides tools for querying and discovering available MCP servers from this list.
|
||||
- **[Search MCP Server](https://github.com/krzysztofkucmierz/search-mcp-server)** - Recommends the most relevant MCP servers based on the client's query by searching this README file.
|
||||
- **[MCPWatch](https://github.com/kapilduraphe/mcp-watch)** - A comprehensive security scanner for Model Context Protocol (MCP) servers that detects vulnerabilities and security issues in your MCP server implementations.
|
||||
- **[mkinf](https://mkinf.io)** - An Open Source registry of hosted MCP Servers to accelerate AI agent workflows.
|
||||
- **[Open-Sourced MCP Servers Directory](https://github.com/chatmcp/mcp-directory)** - A curated list of MCP servers by **[mcpso](https://mcp.so)**
|
||||
- **[OpenTools](https://opentools.com)** - An open registry for finding, installing, and building with MCP servers by **[opentoolsteam](https://github.com/opentoolsteam)**
|
||||
- **[Programmatic MCP Prototype](https://github.com/domdomegg/programmatic-mcp-prototype)** - Experimental agent prototype demonstrating programmatic MCP tool composition, progressive tool discovery, state persistence, and skill building through TypeScript code execution by **[Adam Jones](https://github.com/domdomegg)**
|
||||
- **[PulseMCP](https://www.pulsemcp.com)** ([API](https://www.pulsemcp.com/api)) - Community hub & weekly newsletter for discovering MCP servers, clients, articles, and news by **[Tadas Antanavicius](https://github.com/tadasant)**, **[Mike Coughlin](https://github.com/macoughl)**, and **[Ravina Patel](https://github.com/ravinahp)**
|
||||
- **[r/mcp](https://www.reddit.com/r/mcp)** – A Reddit community dedicated to MCP by **[Frank Fiegel](https://github.com/punkpeye)**
|
||||
- **[MCP.ing](https://mcp.ing/)** - A list of MCP services for discovering MCP servers in the community and providing a convenient search function for MCP services by **[iiiusky](https://github.com/iiiusky)**
|
||||
- **[MCP Hunt](https://mcp-hunt.com)** - Realtime platform for discovering trending MCP servers with momentum tracking, upvoting, and community discussions - like Product Hunt meets Reddit for MCP
|
||||
- **[Smithery](https://smithery.ai/)** - A registry of MCP servers to find the right tools for your LLM agents by **[Henry Mao](https://github.com/calclavia)**
|
||||
- **[Toolbase](https://gettoolbase.ai)** - Desktop application that manages tools and MCP servers with just a few clicks - no coding required by **[gching](https://github.com/gching)**
|
||||
- **[ToolHive](https://github.com/StacklokLabs/toolhive)** - A lightweight utility designed to simplify the deployment and management of MCP servers, ensuring ease of use, consistency, and security through containerization by **[StacklokLabs](https://github.com/StacklokLabs)**
|
||||
- **[NetMind](https://www.netmind.ai/AIServices)** - Access powerful AI services via simple APIs or MCP servers to supercharge your productivity.
|
||||
- **[Webrix MCP Gateway](https://github.com/webrix-ai/secure-mcp-gateway)** - Enterprise MCP gateway with SSO, RBAC, audit trails, and token vaults for secure, centralized AI agent access control. Deploy via Helm charts on-premise or in your cloud. [webrix.ai](https://webrix.ai)
|
||||
104
CLAUDE.md
104
CLAUDE.md
|
|
@ -1,104 +0,0 @@
|
|||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
Official MCP reference server implementations. This is an npm workspaces monorepo containing 7 servers (4 TypeScript, 3 Python) under `src/`. Each server is a standalone package published to npm or PyPI.
|
||||
|
||||
## Monorepo Structure
|
||||
|
||||
```
|
||||
src/
|
||||
everything/ TS @modelcontextprotocol/server-everything (reference server, all MCP features)
|
||||
filesystem/ TS @modelcontextprotocol/server-filesystem (file operations with Roots access control)
|
||||
memory/ TS @modelcontextprotocol/server-memory (knowledge graph persistence)
|
||||
sequentialthinking/ TS @modelcontextprotocol/server-sequential-thinking (step-by-step reasoning)
|
||||
fetch/ Py mcp-server-fetch (web content fetching)
|
||||
git/ Py mcp-server-git (git repository operations)
|
||||
time/ Py mcp-server-time (timezone queries and conversion)
|
||||
```
|
||||
|
||||
## Build & Test Commands
|
||||
|
||||
### TypeScript servers
|
||||
|
||||
```bash
|
||||
# Single server
|
||||
cd src/<server> && npm ci && npm run build && npm test
|
||||
|
||||
# All TS servers from root
|
||||
npm install && npm run build
|
||||
```
|
||||
|
||||
- Build: `tsc` (target ES2022, module Node16, strict mode)
|
||||
- Tests: **vitest** with `@vitest/coverage-v8` (required for new tests)
|
||||
- Node version: **22**
|
||||
|
||||
### Python servers
|
||||
|
||||
```bash
|
||||
cd src/<server> && uv sync --frozen --all-extras --dev
|
||||
|
||||
# Run tests (if tests/ or test/ directory exists)
|
||||
uv run pytest
|
||||
|
||||
# Type checking
|
||||
uv run pyright
|
||||
|
||||
# Linting
|
||||
uv run ruff check .
|
||||
```
|
||||
|
||||
- Build system: **hatchling** (`uv build`)
|
||||
- Package manager: **uv** (not pip)
|
||||
- Python version: **>= 3.10** (per-server `.python-version` file)
|
||||
- Type checking: **pyright** (enforced in CI)
|
||||
- Linting: **ruff**
|
||||
|
||||
## Code Style
|
||||
|
||||
### TypeScript
|
||||
|
||||
- ES modules with `.js` extension in import paths
|
||||
- Strict TypeScript typing for all functions and variables
|
||||
- Zod schemas for tool input validation
|
||||
- 2-space indentation, trailing commas in multi-line objects
|
||||
- camelCase for variables/functions, PascalCase for types/classes, UPPER_CASE for constants
|
||||
- kebab-case for file names and registered tools/prompts/resources
|
||||
- Verb-first tool names (e.g., `get-file-info`, not `file-info`)
|
||||
- Imports grouped: external first, then internal
|
||||
|
||||
### Python
|
||||
|
||||
- Type hints enforced via pyright
|
||||
- Async/await patterns (especially in fetch server with pytest-asyncio)
|
||||
- Follow existing module layout per server
|
||||
|
||||
## Contributing Guidelines
|
||||
|
||||
**Accepted:** Bug fixes, usability improvements, enhancements demonstrating MCP protocol features (Resources, Prompts, Roots -- not just Tools).
|
||||
|
||||
**Selective:** New features outside a server's core purpose or highly opinionated additions.
|
||||
|
||||
**Not accepted:** New server implementations (use the [MCP Server Registry](https://github.com/modelcontextprotocol/registry)), README server listing changes.
|
||||
|
||||
## CI/CD Pipeline
|
||||
|
||||
Both TypeScript and Python workflows use **dynamic package detection** (find + jq matrix strategy):
|
||||
|
||||
1. `detect-packages` -- finds all `package.json` / `pyproject.toml` under `src/`
|
||||
2. `test` -- runs tests per package
|
||||
3. `build` -- compiles and type-checks per package
|
||||
4. `publish` -- on release events only (npm for TS, PyPI trusted publishing for Python)
|
||||
|
||||
## MCP Protocol Reference
|
||||
|
||||
The repo is configured with an MCP docs server (`.mcp.json`) pointing to `https://modelcontextprotocol.io/mcp`. For schema details, reference `https://github.com/modelcontextprotocol/modelcontextprotocol/tree/main/schema` which contains versioned schemas in JSON and TypeScript formats.
|
||||
|
||||
## Key Patterns
|
||||
|
||||
- Each server registers capabilities via `registerTools(server)`, `registerResources(server)`, `registerPrompts(server)` functions
|
||||
- Tool annotations: set `readOnlyHint`, `idempotentHint`, `destructiveHint` per MCP spec
|
||||
- Transport support: stdio (default), SSE (deprecated), Streamable HTTP
|
||||
- All PRs are reviewed against the [PR template](.github/pull_request_template.md) checklist -- ensure MCP docs are read, security best practices followed, and changes tested with an LLM client
|
||||
|
|
@ -6,9 +6,9 @@ We accept changes through [the standard GitHub flow model](https://docs.github.c
|
|||
|
||||
## Server Listings
|
||||
|
||||
The README no longer contains a list of third-party MCP servers — that list has been retired in favor of the [MCP Server Registry](https://github.com/modelcontextprotocol/registry). To make your server discoverable, follow the [quickstart guide](https://github.com/modelcontextprotocol/registry/blob/main/docs/modelcontextprotocol-io/quickstart.mdx) to publish it there.
|
||||
We are **no longer accepting PRs** to add server links to the README. Please publish your server to the [MCP Server Registry](https://github.com/modelcontextprotocol/registry) instead. Follow the [quickstart guide](https://github.com/modelcontextprotocol/registry/blob/main/docs/modelcontextprotocol-io/quickstart.mdx).
|
||||
|
||||
You can browse published servers at [https://registry.modelcontextprotocol.io/](https://registry.modelcontextprotocol.io/).
|
||||
You can browse published servers using the simple UI at [https://registry.modelcontextprotocol.io/](https://registry.modelcontextprotocol.io/).
|
||||
|
||||
## Server Implementations
|
||||
|
||||
|
|
|
|||
197
LICENSE
197
LICENSE
|
|
@ -1,193 +1,6 @@
|
|||
The MCP project is undergoing a licensing transition from the MIT License to the Apache License, Version 2.0 ("Apache-2.0"). All new code and specification contributions to the project are licensed under Apache-2.0. Documentation contributions (excluding specifications) are licensed under CC-BY-4.0.
|
||||
|
||||
Contributions for which relicensing consent has been obtained are licensed under Apache-2.0. Contributions made by authors who originally licensed their work under the MIT License and who have not yet granted explicit permission to relicense remain licensed under the MIT License.
|
||||
|
||||
No rights beyond those granted by the applicable original license are conveyed for such contributions.
|
||||
|
||||
---
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to the Licensor for inclusion in the Work by the copyright
|
||||
owner or by an individual or Legal Entity authorized to submit on behalf
|
||||
of the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
---
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024-2025 Model Context Protocol a Series of LF Projects, LLC.
|
||||
Copyright (c) 2025 Anthropic, PBC
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
@ -206,11 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
Creative Commons Attribution 4.0 International (CC-BY-4.0)
|
||||
|
||||
Documentation in this project (excluding specifications) is licensed under
|
||||
CC-BY-4.0. See https://creativecommons.org/licenses/by/4.0/legalcode for
|
||||
the full license text.
|
||||
|
|
|
|||
49
RELEASING.md
49
RELEASING.md
|
|
@ -1,49 +0,0 @@
|
|||
# Releasing
|
||||
|
||||
How the packages in this repository are published, and what to do when a publish fails.
|
||||
|
||||
## How publishing works
|
||||
|
||||
All packages publish exclusively from the [`release.yml`](.github/workflows/release.yml) GitHub Actions workflow, gated by the `release` environment (a required reviewer must approve each deployment). Releases are triggered deliberately by a maintainer via **workflow_dispatch** (Actions → Release → Run workflow, or `gh workflow run release.yml`) — there is no scheduled/automatic release.
|
||||
|
||||
**Authentication is OIDC trusted publishing on both registries — there are no registry tokens.**
|
||||
|
||||
- **npm** (TypeScript servers): each `@modelcontextprotocol/*` package is registered on npmjs.com with a [trusted publisher](https://docs.npmjs.com/trusted-publishers) bound to this repository, workflow filename `release.yml`, and environment `release` (the binding is case-sensitive). Packages publish with [provenance attestations](https://docs.npmjs.com/generating-provenance-statements).
|
||||
- **PyPI** (Python servers): published via [PyPI trusted publishing](https://docs.pypi.org/trusted-publishers/) using `pypa/gh-action-pypi-publish`.
|
||||
|
||||
A release run:
|
||||
|
||||
1. **Detects changed packages** since the last release tag — a package counts as changed if any `.py`, `.ts`, or `.md` file in its directory changed (READMEs ship inside the published artifacts).
|
||||
2. **Stamps versions and pushes the release tag** — versions are date-based (CalVer, e.g. `2026.7.4`).
|
||||
3. **Publishes each changed package as an independent matrix job** (`fail-fast: false` — one package's failure never blocks another). Each job: checkout at the release tag → install → double-publish guard → **run the package's tests** (plus `pyright` for Python) → build → publish. The guard differs by registry: the npm job aborts before tests if the version already exists; for PyPI the skip happens at the publish step itself (`skip-existing` on the upload action).
|
||||
4. **Creates the GitHub release** with generated notes.
|
||||
|
||||
## When a publish fails
|
||||
|
||||
A failed matrix leg means that one package didn't publish; everything that succeeded stays published.
|
||||
|
||||
**Preferred: re-run the failed jobs on the same run.**
|
||||
|
||||
```bash
|
||||
gh run rerun <run-id> --failed --repo modelcontextprotocol/servers
|
||||
```
|
||||
|
||||
- A re-run is still a `release.yml` run in the `release` environment, so it satisfies the trusted-publisher binding.
|
||||
- It re-runs only the failed legs, checked out at the original release tag — it publishes exactly the tagged code, and the double-publish guard keeps already-published packages safe.
|
||||
- It needs a fresh `release` environment approval, and the run must be complete first (approve or reject any pending deployments).
|
||||
- GitHub's re-run window is ~30 days from the original run, and re-runs execute the *original* workflow snapshot — workflow fixes on `main` don't apply to a re-run.
|
||||
|
||||
**Otherwise: let the next release pick it up.** If the re-run window has closed (or the fix required a workflow change), the failed version simply never exists on that registry — that's benign; npm and PyPI version histories don't need to match. The package publishes at the next version, provided it has a qualifying change (`.py`, `.ts`, or `.md`) since the last release tag.
|
||||
|
||||
**Never:**
|
||||
|
||||
- Publish manually with an npm token or from a laptop — there are no registry tokens, and manual publishes would break the provenance/trust chain.
|
||||
- Dispatch a fresh `release.yml` run expecting it to retry a failed version — versions are date-granular, so a same-day dispatch collides with the existing tag, and a later dispatch mints a *new* version. Neither retries the failed one.
|
||||
|
||||
## Environment approvals
|
||||
|
||||
The `release` environment's required-reviewer list is configured in the repository settings (Settings → Environments → `release`). Reviewer rights come only from that list — repository admin does not confer deployment approval.
|
||||
|
||||
---
|
||||
|
||||
Planned changes to this process — semver via changesets for the TypeScript packages, publishing triggered by manually-created GitHub Releases — are tracked in [#4463](https://github.com/modelcontextprotocol/servers/issues/4463). This document will be updated when that work merges.
|
||||
21
SECURITY.md
21
SECURITY.md
|
|
@ -1,21 +1,10 @@
|
|||
# Security Policy
|
||||
Thank you for helping us keep our MCP servers secure.
|
||||
|
||||
Thank you for helping keep the Model Context Protocol and its ecosystem secure.
|
||||
The **reference servers** in this repo are maintained by [Anthropic](https://www.anthropic.com/) as part of the Model Context Protocol project.
|
||||
|
||||
## Important Notice
|
||||
The security of our systems and user data is Anthropic’s top priority. We appreciate the work of security researchers acting in good faith in identifying and reporting potential vulnerabilities.
|
||||
|
||||
The servers in this repository are **reference implementations** intended to demonstrate
|
||||
MCP features and SDK usage. They serve as educational examples for developers building
|
||||
their own MCP servers, not as production-ready solutions.
|
||||
## Vulnerability Disclosure Program
|
||||
|
||||
This repository is **not** eligible for security vulnerability reporting. If you discover
|
||||
a vulnerability in an MCP SDK, please report it in the appropriate SDK repository.
|
||||
|
||||
## Reporting Security Issues in MCP SDKs
|
||||
|
||||
If you discover a security vulnerability in an MCP SDK, please report it through the
|
||||
[GitHub Security Advisory process](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability)
|
||||
in the relevant SDK repository.
|
||||
|
||||
Please **do not** report security vulnerabilities through public GitHub issues, discussions,
|
||||
or pull requests.
|
||||
Our Vulnerability Program guidelines are defined on our [HackerOne program page](https://hackerone.com/anthropic-vdp). We ask that any validated vulnerability in this functionality be reported through the [submission form](https://hackerone.com/anthropic-vdp/reports/new?type=team&report_type=vulnerability).
|
||||
|
|
|
|||
3567
package-lock.json
generated
3567
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -3,8 +3,8 @@
|
|||
"private": true,
|
||||
"version": "0.6.2",
|
||||
"description": "Model Context Protocol servers",
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
"author": "Model Context Protocol a Series of LF Projects, LLC.",
|
||||
"license": "MIT",
|
||||
"author": "Anthropic, PBC (https://anthropic.com)",
|
||||
"homepage": "https://modelcontextprotocol.io",
|
||||
"bugs": "https://github.com/modelcontextprotocol/servers/issues",
|
||||
"type": "module",
|
||||
|
|
@ -23,9 +23,5 @@
|
|||
"@modelcontextprotocol/server-memory": "*",
|
||||
"@modelcontextprotocol/server-filesystem": "*",
|
||||
"@modelcontextprotocol/server-sequential-thinking": "*"
|
||||
},
|
||||
"overrides": {
|
||||
"qs": ">=6.15.2",
|
||||
"hono": ">=4.12.21"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,9 +113,7 @@ def has_changes(path: Path, git_hash: GitHash) -> bool:
|
|||
)
|
||||
|
||||
changed_files = [Path(f) for f in output.stdout.splitlines()]
|
||||
# .md counts as a change: READMEs ship inside the published package
|
||||
# (npm tarball / PyPI long_description)
|
||||
relevant_files = [f for f in changed_files if f.suffix in [".py", ".ts", ".md"]]
|
||||
relevant_files = [f for f in changed_files if f.suffix in [".py", ".ts"]]
|
||||
return len(relevant_files) >= 1
|
||||
except subprocess.CalledProcessError:
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
- Watch mode: `npm run watch` - Watches for changes and rebuilds automatically
|
||||
- Run STDIO server: `npm run start:stdio` - Starts the MCP server using stdio transport
|
||||
- Run SSE server: `npm run start:sse` - Starts the MCP server with SSE transport
|
||||
- Run StreamableHttp server: `npm run start:streamableHttp` - Starts the MCP server with StreamableHttp transport
|
||||
- Run StreamableHttp server: `npm run start:stremableHttp` - Starts the MCP server with StreamableHttp transport
|
||||
- Prepare release: `npm run prepare` - Builds the project for publishing
|
||||
|
||||
## Code Style Guidelines
|
||||
|
|
|
|||
|
|
@ -31,27 +31,9 @@ Add to your `claude_desktop_config.json`:
|
|||
}
|
||||
```
|
||||
|
||||
On Windows, use `cmd /c` to launch `npx`:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"everything": {
|
||||
"command": "cmd",
|
||||
"args": [
|
||||
"/c",
|
||||
"npx",
|
||||
"-y",
|
||||
"@modelcontextprotocol/server-everything"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Usage with VS Code
|
||||
|
||||
For quick installation, use one of the one-click install buttons below...
|
||||
For quick installation, use of of the one-click install buttons below...
|
||||
|
||||
[](https://insiders.vscode.dev/redirect/mcp/install?name=everything&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40modelcontextprotocol%2Fserver-everything%22%5D%7D) [](https://insiders.vscode.dev/redirect/mcp/install?name=everything&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40modelcontextprotocol%2Fserver-everything%22%5D%7D&quality=insiders)
|
||||
|
||||
|
|
@ -80,19 +62,6 @@ Alternatively, you can add the configuration to a file called `.vscode/mcp.json`
|
|||
}
|
||||
```
|
||||
|
||||
On Windows, use:
|
||||
|
||||
```json
|
||||
{
|
||||
"servers": {
|
||||
"everything": {
|
||||
"command": "cmd",
|
||||
"args": ["/c", "npx", "-y", "@modelcontextprotocol/server-everything"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Running from source with [HTTP+SSE Transport](https://modelcontextprotocol.io/specification/2024-11-05/basic/transports#http-with-sse) (deprecated as of [2025-03-26](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports))
|
||||
|
||||
```shell
|
||||
|
|
@ -113,7 +82,7 @@ npm run start:streamableHttp
|
|||
### Install
|
||||
```shell
|
||||
npm install -g @modelcontextprotocol/server-everything@latest
|
||||
```
|
||||
````
|
||||
|
||||
### Run the default (stdio) server
|
||||
```shell
|
||||
|
|
@ -135,6 +104,3 @@ npx @modelcontextprotocol/server-everything sse
|
|||
npx @modelcontextprotocol/server-everything streamableHttp
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
This MCP server is licensed under the MIT License. This means you are free to use, modify, and distribute the software, subject to the terms and conditions of the MIT License. For more details, please see the LICENSE file in the project repository.
|
||||
|
|
|
|||
|
|
@ -1,179 +0,0 @@
|
|||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||
import { registerSimplePrompt } from '../prompts/simple.js';
|
||||
import { registerArgumentsPrompt } from '../prompts/args.js';
|
||||
import { registerPromptWithCompletions } from '../prompts/completions.js';
|
||||
import { registerEmbeddedResourcePrompt } from '../prompts/resource.js';
|
||||
|
||||
// Helper to capture registered prompt handlers
|
||||
function createMockServer() {
|
||||
const handlers: Map<string, Function> = new Map();
|
||||
const configs: Map<string, any> = new Map();
|
||||
|
||||
const mockServer = {
|
||||
registerPrompt: vi.fn((name: string, config: any, handler: Function) => {
|
||||
handlers.set(name, handler);
|
||||
configs.set(name, config);
|
||||
}),
|
||||
} as unknown as McpServer;
|
||||
|
||||
return { mockServer, handlers, configs };
|
||||
}
|
||||
|
||||
describe('Prompts', () => {
|
||||
describe('simple-prompt', () => {
|
||||
it('should return fixed message with no arguments', () => {
|
||||
const { mockServer, handlers } = createMockServer();
|
||||
registerSimplePrompt(mockServer);
|
||||
|
||||
const handler = handlers.get('simple-prompt')!;
|
||||
const result = handler();
|
||||
|
||||
expect(result).toEqual({
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: {
|
||||
type: 'text',
|
||||
text: 'This is a simple prompt without arguments.',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('args-prompt', () => {
|
||||
it('should include city in message', () => {
|
||||
const { mockServer, handlers } = createMockServer();
|
||||
registerArgumentsPrompt(mockServer);
|
||||
|
||||
const handler = handlers.get('args-prompt')!;
|
||||
const result = handler({ city: 'San Francisco' });
|
||||
|
||||
expect(result.messages[0].content.text).toBe("What's weather in San Francisco?");
|
||||
});
|
||||
|
||||
it('should include city and state in message', () => {
|
||||
const { mockServer, handlers } = createMockServer();
|
||||
registerArgumentsPrompt(mockServer);
|
||||
|
||||
const handler = handlers.get('args-prompt')!;
|
||||
const result = handler({ city: 'San Francisco', state: 'California' });
|
||||
|
||||
expect(result.messages[0].content.text).toBe(
|
||||
"What's weather in San Francisco, California?"
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle city only (optional state omitted)', () => {
|
||||
const { mockServer, handlers } = createMockServer();
|
||||
registerArgumentsPrompt(mockServer);
|
||||
|
||||
const handler = handlers.get('args-prompt')!;
|
||||
const result = handler({ city: 'New York' });
|
||||
|
||||
expect(result.messages[0].content.text).toBe("What's weather in New York?");
|
||||
expect(result.messages[0].content.text).not.toContain(',');
|
||||
expect(result.messages[0].role).toBe('user');
|
||||
expect(result.messages[0].content.type).toBe('text');
|
||||
});
|
||||
});
|
||||
|
||||
describe('completable-prompt', () => {
|
||||
it('should generate promotion message with department and name', () => {
|
||||
const { mockServer, handlers } = createMockServer();
|
||||
registerPromptWithCompletions(mockServer);
|
||||
|
||||
const handler = handlers.get('completable-prompt')!;
|
||||
const result = handler({ department: 'Engineering', name: 'Alice' });
|
||||
|
||||
expect(result.messages[0].content.text).toBe(
|
||||
'Please promote Alice to the head of the Engineering team.'
|
||||
);
|
||||
});
|
||||
|
||||
it('should work with different departments', () => {
|
||||
const { mockServer, handlers } = createMockServer();
|
||||
registerPromptWithCompletions(mockServer);
|
||||
|
||||
const handler = handlers.get('completable-prompt')!;
|
||||
|
||||
const salesResult = handler({ department: 'Sales', name: 'David' });
|
||||
expect(salesResult.messages[0].content.text).toContain('Sales');
|
||||
expect(salesResult.messages[0].content.text).toContain('David');
|
||||
expect(salesResult.messages[0].role).toBe('user');
|
||||
|
||||
const marketingResult = handler({ department: 'Marketing', name: 'Grace' });
|
||||
expect(marketingResult.messages[0].content.text).toContain('Marketing');
|
||||
expect(marketingResult.messages[0].content.text).toContain('Grace');
|
||||
});
|
||||
});
|
||||
|
||||
describe('resource-prompt', () => {
|
||||
it('should return text resource reference', () => {
|
||||
const { mockServer, handlers } = createMockServer();
|
||||
registerEmbeddedResourcePrompt(mockServer);
|
||||
|
||||
const handler = handlers.get('resource-prompt')!;
|
||||
const result = handler({ resourceType: 'Text', resourceId: '1' });
|
||||
|
||||
expect(result.messages).toHaveLength(2);
|
||||
expect(result.messages[0].content.text).toContain('Text');
|
||||
expect(result.messages[0].content.text).toContain('1');
|
||||
expect(result.messages[1].content.type).toBe('resource');
|
||||
expect(result.messages[1].content.resource.uri).toContain('text/1');
|
||||
});
|
||||
|
||||
it('should return blob resource reference', () => {
|
||||
const { mockServer, handlers } = createMockServer();
|
||||
registerEmbeddedResourcePrompt(mockServer);
|
||||
|
||||
const handler = handlers.get('resource-prompt')!;
|
||||
const result = handler({ resourceType: 'Blob', resourceId: '5' });
|
||||
|
||||
expect(result.messages[0].content.text).toContain('Blob');
|
||||
expect(result.messages[1].content.resource.uri).toContain('blob/5');
|
||||
});
|
||||
|
||||
it('should reject invalid resource type', () => {
|
||||
const { mockServer, handlers } = createMockServer();
|
||||
registerEmbeddedResourcePrompt(mockServer);
|
||||
|
||||
const handler = handlers.get('resource-prompt')!;
|
||||
expect(() => handler({ resourceType: 'Invalid', resourceId: '1' })).toThrow(
|
||||
'Invalid resourceType'
|
||||
);
|
||||
});
|
||||
|
||||
it('should reject invalid resource ID', () => {
|
||||
const { mockServer, handlers } = createMockServer();
|
||||
registerEmbeddedResourcePrompt(mockServer);
|
||||
|
||||
const handler = handlers.get('resource-prompt')!;
|
||||
expect(() => handler({ resourceType: 'Text', resourceId: '-1' })).toThrow(
|
||||
'Invalid resourceId'
|
||||
);
|
||||
expect(() => handler({ resourceType: 'Text', resourceId: '0' })).toThrow(
|
||||
'Invalid resourceId'
|
||||
);
|
||||
expect(() => handler({ resourceType: 'Text', resourceId: 'abc' })).toThrow(
|
||||
'Invalid resourceId'
|
||||
);
|
||||
});
|
||||
|
||||
it('should include both intro text and resource messages', () => {
|
||||
const { mockServer, handlers } = createMockServer();
|
||||
registerEmbeddedResourcePrompt(mockServer);
|
||||
|
||||
const handler = handlers.get('resource-prompt')!;
|
||||
const result = handler({ resourceType: 'Text', resourceId: '3' });
|
||||
|
||||
expect(result.messages).toHaveLength(2);
|
||||
expect(result.messages[0].role).toBe('user');
|
||||
expect(result.messages[0].content.type).toBe('text');
|
||||
expect(result.messages[1].role).toBe('user');
|
||||
expect(result.messages[1].content.type).toBe('resource');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,155 +0,0 @@
|
|||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||
|
||||
// Create mock server
|
||||
function createMockServer() {
|
||||
return {
|
||||
registerTool: vi.fn(),
|
||||
registerPrompt: vi.fn(),
|
||||
registerResource: vi.fn(),
|
||||
server: {
|
||||
getClientCapabilities: vi.fn(() => ({})),
|
||||
setRequestHandler: vi.fn(),
|
||||
},
|
||||
sendLoggingMessage: vi.fn(),
|
||||
sendResourceUpdated: vi.fn(),
|
||||
} as unknown as McpServer;
|
||||
}
|
||||
|
||||
describe('Registration Index Files', () => {
|
||||
describe('tools/index.ts', () => {
|
||||
it('should register all standard tools', async () => {
|
||||
const { registerTools } = await import('../tools/index.js');
|
||||
const mockServer = createMockServer();
|
||||
|
||||
registerTools(mockServer);
|
||||
|
||||
// Should register 12 standard tools (non-conditional)
|
||||
expect(mockServer.registerTool).toHaveBeenCalledTimes(12);
|
||||
|
||||
// Verify specific tools are registered
|
||||
const registeredTools = (mockServer.registerTool as any).mock.calls.map(
|
||||
(call: any[]) => call[0]
|
||||
);
|
||||
expect(registeredTools).toContain('echo');
|
||||
expect(registeredTools).toContain('get-sum');
|
||||
expect(registeredTools).toContain('get-env');
|
||||
expect(registeredTools).toContain('get-tiny-image');
|
||||
expect(registeredTools).toContain('get-structured-content');
|
||||
expect(registeredTools).toContain('get-annotated-message');
|
||||
expect(registeredTools).toContain('trigger-long-running-operation');
|
||||
expect(registeredTools).toContain('get-resource-links');
|
||||
expect(registeredTools).toContain('get-resource-reference');
|
||||
expect(registeredTools).toContain('gzip-file-as-resource');
|
||||
expect(registeredTools).toContain('toggle-simulated-logging');
|
||||
expect(registeredTools).toContain('toggle-subscriber-updates');
|
||||
});
|
||||
|
||||
it('should register conditional tools based on capabilities', async () => {
|
||||
const { registerConditionalTools } = await import('../tools/index.js');
|
||||
|
||||
// Server with all capabilities including experimental tasks API
|
||||
const mockServerWithCapabilities = {
|
||||
registerTool: vi.fn(),
|
||||
server: {
|
||||
getClientCapabilities: vi.fn(() => ({
|
||||
roots: {},
|
||||
elicitation: { url: {} },
|
||||
sampling: {},
|
||||
})),
|
||||
},
|
||||
experimental: {
|
||||
tasks: {
|
||||
registerToolTask: vi.fn(),
|
||||
},
|
||||
},
|
||||
} as unknown as McpServer;
|
||||
|
||||
registerConditionalTools(mockServerWithCapabilities);
|
||||
|
||||
// Should register 4 conditional tools via registerTool when all capabilities
|
||||
// are present. Task-based tools register via registerToolTask (counted separately),
|
||||
// so they are not included in this registerTool count.
|
||||
expect(mockServerWithCapabilities.registerTool).toHaveBeenCalledTimes(4);
|
||||
|
||||
const registeredTools = (
|
||||
mockServerWithCapabilities.registerTool as any
|
||||
).mock.calls.map((call: any[]) => call[0]);
|
||||
expect(registeredTools).toContain('get-roots-list');
|
||||
expect(registeredTools).toContain('trigger-elicitation-request');
|
||||
expect(registeredTools).toContain('trigger-url-elicitation');
|
||||
expect(registeredTools).toContain('trigger-sampling-request');
|
||||
|
||||
// Task-based tools are registered via experimental.tasks.registerToolTask
|
||||
expect(mockServerWithCapabilities.experimental.tasks.registerToolTask).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not register conditional tools when capabilities missing', async () => {
|
||||
const { registerConditionalTools } = await import('../tools/index.js');
|
||||
|
||||
const mockServerNoCapabilities = {
|
||||
registerTool: vi.fn(),
|
||||
server: {
|
||||
getClientCapabilities: vi.fn(() => ({})),
|
||||
},
|
||||
experimental: {
|
||||
tasks: {
|
||||
registerToolTask: vi.fn(),
|
||||
},
|
||||
},
|
||||
} as unknown as McpServer;
|
||||
|
||||
registerConditionalTools(mockServerNoCapabilities);
|
||||
|
||||
// Should not register any capability-gated tools when capabilities are missing
|
||||
expect(mockServerNoCapabilities.registerTool).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('prompts/index.ts', () => {
|
||||
it('should register all prompts', async () => {
|
||||
const { registerPrompts } = await import('../prompts/index.js');
|
||||
const mockServer = createMockServer();
|
||||
|
||||
registerPrompts(mockServer);
|
||||
|
||||
// Should register 4 prompts
|
||||
expect(mockServer.registerPrompt).toHaveBeenCalledTimes(4);
|
||||
|
||||
const registeredPrompts = (mockServer.registerPrompt as any).mock.calls.map(
|
||||
(call: any[]) => call[0]
|
||||
);
|
||||
expect(registeredPrompts).toContain('simple-prompt');
|
||||
expect(registeredPrompts).toContain('args-prompt');
|
||||
expect(registeredPrompts).toContain('completable-prompt');
|
||||
expect(registeredPrompts).toContain('resource-prompt');
|
||||
});
|
||||
});
|
||||
|
||||
describe('resources/index.ts', () => {
|
||||
it('should register resource templates', async () => {
|
||||
const { registerResources } = await import('../resources/index.js');
|
||||
const mockServer = createMockServer();
|
||||
|
||||
registerResources(mockServer);
|
||||
|
||||
// Should register at least the 2 resource templates (text and blob) plus file resources
|
||||
expect(mockServer.registerResource).toHaveBeenCalled();
|
||||
const registeredResources = (mockServer.registerResource as any).mock.calls.map(
|
||||
(call: any[]) => call[0]
|
||||
);
|
||||
expect(registeredResources).toContain('Dynamic Text Resource');
|
||||
expect(registeredResources).toContain('Dynamic Blob Resource');
|
||||
});
|
||||
|
||||
it('should read instructions from file', async () => {
|
||||
const { readInstructions } = await import('../resources/index.js');
|
||||
|
||||
const instructions = readInstructions();
|
||||
|
||||
// Should return a string (either content or error message)
|
||||
expect(typeof instructions).toBe('string');
|
||||
expect(instructions.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,327 +0,0 @@
|
|||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||
import {
|
||||
textResource,
|
||||
blobResource,
|
||||
textResourceUri,
|
||||
blobResourceUri,
|
||||
RESOURCE_TYPE_TEXT,
|
||||
RESOURCE_TYPE_BLOB,
|
||||
RESOURCE_TYPES,
|
||||
resourceTypeCompleter,
|
||||
resourceIdForPromptCompleter,
|
||||
resourceIdForResourceTemplateCompleter,
|
||||
registerResourceTemplates,
|
||||
} from '../resources/templates.js';
|
||||
import {
|
||||
getSessionResourceURI,
|
||||
registerSessionResource,
|
||||
} from '../resources/session.js';
|
||||
import { registerFileResources } from '../resources/files.js';
|
||||
import {
|
||||
setSubscriptionHandlers,
|
||||
beginSimulatedResourceUpdates,
|
||||
stopSimulatedResourceUpdates,
|
||||
} from '../resources/subscriptions.js';
|
||||
|
||||
describe('Resource Templates', () => {
|
||||
describe('Constants', () => {
|
||||
it('should include both types in RESOURCE_TYPES array', () => {
|
||||
expect(RESOURCE_TYPES).toContain(RESOURCE_TYPE_TEXT);
|
||||
expect(RESOURCE_TYPES).toContain(RESOURCE_TYPE_BLOB);
|
||||
expect(RESOURCE_TYPES).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('textResourceUri', () => {
|
||||
it('should create URL for text resource', () => {
|
||||
const uri = textResourceUri(1);
|
||||
expect(uri.toString()).toBe('demo://resource/dynamic/text/1');
|
||||
});
|
||||
|
||||
it('should handle different resource IDs', () => {
|
||||
expect(textResourceUri(5).toString()).toBe('demo://resource/dynamic/text/5');
|
||||
expect(textResourceUri(100).toString()).toBe('demo://resource/dynamic/text/100');
|
||||
});
|
||||
});
|
||||
|
||||
describe('blobResourceUri', () => {
|
||||
it('should create URL for blob resource', () => {
|
||||
const uri = blobResourceUri(1);
|
||||
expect(uri.toString()).toBe('demo://resource/dynamic/blob/1');
|
||||
});
|
||||
|
||||
it('should handle different resource IDs', () => {
|
||||
expect(blobResourceUri(5).toString()).toBe('demo://resource/dynamic/blob/5');
|
||||
expect(blobResourceUri(100).toString()).toBe('demo://resource/dynamic/blob/100');
|
||||
});
|
||||
});
|
||||
|
||||
describe('textResource', () => {
|
||||
it('should create text resource with correct structure', () => {
|
||||
const uri = textResourceUri(1);
|
||||
const resource = textResource(uri, 1);
|
||||
|
||||
expect(resource.uri).toBe(uri.toString());
|
||||
expect(resource.mimeType).toBe('text/plain');
|
||||
expect(resource.text).toContain('Resource 1');
|
||||
expect(resource.text).toContain('plaintext');
|
||||
});
|
||||
|
||||
it('should include timestamp in content', () => {
|
||||
const uri = textResourceUri(2);
|
||||
const resource = textResource(uri, 2);
|
||||
|
||||
// Timestamp format varies, just check it contains time-related content
|
||||
expect(resource.text).toMatch(/\d/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('blobResource', () => {
|
||||
it('should create blob resource with correct structure', () => {
|
||||
const uri = blobResourceUri(1);
|
||||
const resource = blobResource(uri, 1);
|
||||
|
||||
expect(resource.uri).toBe(uri.toString());
|
||||
expect(resource.mimeType).toBe('text/plain');
|
||||
expect(resource.blob).toBeDefined();
|
||||
});
|
||||
|
||||
it('should create valid base64 encoded content', () => {
|
||||
const uri = blobResourceUri(3);
|
||||
const resource = blobResource(uri, 3);
|
||||
|
||||
// Decode and verify content
|
||||
const decoded = Buffer.from(resource.blob, 'base64').toString();
|
||||
expect(decoded).toContain('Resource 3');
|
||||
expect(decoded).toContain('base64 blob');
|
||||
});
|
||||
});
|
||||
|
||||
describe('resourceTypeCompleter', () => {
|
||||
it('should be defined as a completable schema', () => {
|
||||
// The completer is a zod schema wrapped with completable
|
||||
expect(resourceTypeCompleter).toBeDefined();
|
||||
// It should have the zod parse method
|
||||
expect(typeof (resourceTypeCompleter as any).parse).toBe('function');
|
||||
});
|
||||
|
||||
it('should validate string resource types', () => {
|
||||
// Test that valid strings pass validation
|
||||
expect(() => (resourceTypeCompleter as any).parse('Text')).not.toThrow();
|
||||
expect(() => (resourceTypeCompleter as any).parse('Blob')).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('resourceIdForPromptCompleter', () => {
|
||||
it('should be defined as a completable schema', () => {
|
||||
expect(resourceIdForPromptCompleter).toBeDefined();
|
||||
expect(typeof (resourceIdForPromptCompleter as any).parse).toBe('function');
|
||||
});
|
||||
|
||||
it('should validate string IDs', () => {
|
||||
// Test that valid strings pass validation
|
||||
expect(() => (resourceIdForPromptCompleter as any).parse('1')).not.toThrow();
|
||||
expect(() => (resourceIdForPromptCompleter as any).parse('100')).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('resourceIdForResourceTemplateCompleter', () => {
|
||||
it('should validate positive integer IDs', () => {
|
||||
expect(resourceIdForResourceTemplateCompleter('1')).toEqual(['1']);
|
||||
expect(resourceIdForResourceTemplateCompleter('50')).toEqual(['50']);
|
||||
});
|
||||
|
||||
it('should reject invalid IDs', () => {
|
||||
expect(resourceIdForResourceTemplateCompleter('0')).toEqual([]);
|
||||
expect(resourceIdForResourceTemplateCompleter('-5')).toEqual([]);
|
||||
expect(resourceIdForResourceTemplateCompleter('not-a-number')).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('registerResourceTemplates', () => {
|
||||
it('should register text and blob resource templates', () => {
|
||||
const registeredResources: any[] = [];
|
||||
|
||||
const mockServer = {
|
||||
registerResource: vi.fn((...args) => {
|
||||
registeredResources.push(args);
|
||||
}),
|
||||
} as unknown as McpServer;
|
||||
|
||||
registerResourceTemplates(mockServer);
|
||||
|
||||
expect(mockServer.registerResource).toHaveBeenCalledTimes(2);
|
||||
|
||||
// Check text resource registration
|
||||
const textRegistration = registeredResources.find((r) =>
|
||||
r[0].includes('Text')
|
||||
);
|
||||
expect(textRegistration).toBeDefined();
|
||||
expect(textRegistration[1]).toBeInstanceOf(ResourceTemplate);
|
||||
|
||||
// Check blob resource registration
|
||||
const blobRegistration = registeredResources.find((r) =>
|
||||
r[0].includes('Blob')
|
||||
);
|
||||
expect(blobRegistration).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Session Resources', () => {
|
||||
describe('getSessionResourceURI', () => {
|
||||
it('should generate correct URI for resource name', () => {
|
||||
expect(getSessionResourceURI('test')).toBe('demo://resource/session/test');
|
||||
});
|
||||
|
||||
it('should handle various resource names', () => {
|
||||
expect(getSessionResourceURI('my-file')).toBe('demo://resource/session/my-file');
|
||||
expect(getSessionResourceURI('document_123')).toBe(
|
||||
'demo://resource/session/document_123'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('registerSessionResource', () => {
|
||||
it('should register text resource and return resource link', () => {
|
||||
const registrations: any[] = [];
|
||||
const mockServer = {
|
||||
registerResource: vi.fn((...args) => {
|
||||
registrations.push(args);
|
||||
}),
|
||||
} as unknown as McpServer;
|
||||
|
||||
const resource = {
|
||||
uri: 'demo://resource/session/test-file',
|
||||
name: 'test-file',
|
||||
mimeType: 'text/plain',
|
||||
description: 'A test file',
|
||||
};
|
||||
|
||||
const result = registerSessionResource(
|
||||
mockServer,
|
||||
resource,
|
||||
'text',
|
||||
'Hello, World!'
|
||||
);
|
||||
|
||||
expect(result.type).toBe('resource_link');
|
||||
expect(result.uri).toBe(resource.uri);
|
||||
expect(result.name).toBe(resource.name);
|
||||
|
||||
expect(mockServer.registerResource).toHaveBeenCalledWith(
|
||||
'test-file',
|
||||
'demo://resource/session/test-file',
|
||||
expect.objectContaining({
|
||||
mimeType: 'text/plain',
|
||||
description: 'A test file',
|
||||
}),
|
||||
expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it('should register blob resource correctly', () => {
|
||||
const mockServer = {
|
||||
registerResource: vi.fn(),
|
||||
} as unknown as McpServer;
|
||||
|
||||
const resource = {
|
||||
uri: 'demo://resource/session/binary-file',
|
||||
name: 'binary-file',
|
||||
mimeType: 'application/octet-stream',
|
||||
};
|
||||
|
||||
const blobContent = Buffer.from('binary data').toString('base64');
|
||||
const result = registerSessionResource(mockServer, resource, 'blob', blobContent);
|
||||
|
||||
expect(result.type).toBe('resource_link');
|
||||
expect(mockServer.registerResource).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return resource handler that provides correct content', async () => {
|
||||
let capturedHandler: Function | null = null;
|
||||
const mockServer = {
|
||||
registerResource: vi.fn((_name, _uri, _config, handler) => {
|
||||
capturedHandler = handler;
|
||||
}),
|
||||
} as unknown as McpServer;
|
||||
|
||||
const resource = {
|
||||
uri: 'demo://resource/session/content-test',
|
||||
name: 'content-test',
|
||||
mimeType: 'text/plain',
|
||||
};
|
||||
|
||||
registerSessionResource(mockServer, resource, 'text', 'Test content here');
|
||||
|
||||
expect(capturedHandler).not.toBeNull();
|
||||
|
||||
const handlerResult = await capturedHandler!(new URL(resource.uri));
|
||||
expect(handlerResult.contents).toHaveLength(1);
|
||||
expect(handlerResult.contents[0].text).toBe('Test content here');
|
||||
expect(handlerResult.contents[0].mimeType).toBe('text/plain');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('File Resources', () => {
|
||||
describe('registerFileResources', () => {
|
||||
it('should register file resources when docs directory exists', () => {
|
||||
const mockServer = {
|
||||
registerResource: vi.fn(),
|
||||
} as unknown as McpServer;
|
||||
|
||||
registerFileResources(mockServer);
|
||||
|
||||
// The docs folder exists in the everything server and contains files
|
||||
// so registerResource should have been called
|
||||
expect(mockServer.registerResource).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Subscriptions', () => {
|
||||
describe('setSubscriptionHandlers', () => {
|
||||
it('should set request handlers on server', () => {
|
||||
const mockServer = {
|
||||
server: {
|
||||
setRequestHandler: vi.fn(),
|
||||
},
|
||||
sendLoggingMessage: vi.fn(),
|
||||
} as unknown as McpServer;
|
||||
|
||||
setSubscriptionHandlers(mockServer);
|
||||
|
||||
// Should set both subscribe and unsubscribe handlers
|
||||
expect(mockServer.server.setRequestHandler).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('simulated resource updates lifecycle', () => {
|
||||
afterEach(() => {
|
||||
// Clean up any intervals
|
||||
stopSimulatedResourceUpdates('lifecycle-test-session');
|
||||
});
|
||||
|
||||
it('should start and stop updates without errors', () => {
|
||||
const mockServer = {
|
||||
server: {
|
||||
notification: vi.fn(),
|
||||
},
|
||||
} as unknown as McpServer;
|
||||
|
||||
// Start updates - should work for both defined and undefined sessionId
|
||||
beginSimulatedResourceUpdates(mockServer, 'lifecycle-test-session');
|
||||
beginSimulatedResourceUpdates(mockServer, undefined);
|
||||
|
||||
// Stop updates - should handle all cases gracefully
|
||||
stopSimulatedResourceUpdates('lifecycle-test-session');
|
||||
stopSimulatedResourceUpdates('non-existent-session');
|
||||
stopSimulatedResourceUpdates(undefined);
|
||||
|
||||
// If we got here without throwing, the lifecycle works correctly
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue