# Directory Structure
```
├── .gitignore
├── .python-version
├── Dockerfile
├── LICENSE
├── pyproject.toml
├── README.md
├── smithery.yaml
├── src
│ └── huggingface
│ ├── __init__.py
│ └── server.py
└── uv.lock
```
# Files
--------------------------------------------------------------------------------
/.python-version:
--------------------------------------------------------------------------------
```
3.13
```
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
# Python-generated files
__pycache__/
*.py[oc]
build/
dist/
wheels/
*.egg-info
# Virtual environments
.venv
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
# 🤗 Hugging Face MCP Server 🤗
[](https://smithery.ai/server/@shreyaskarnik/huggingface-mcp-server)
A Model Context Protocol (MCP) server that provides read-only access to the Hugging Face Hub APIs. This server allows LLMs like Claude to interact with Hugging Face's models, datasets, spaces, papers, and collections.
## Components
### Resources
The server exposes popular Hugging Face resources:
- Custom `hf://` URI scheme for accessing resources
- Models with `hf://model/{model_id}` URIs
- Datasets with `hf://dataset/{dataset_id}` URIs
- Spaces with `hf://space/{space_id}` URIs
- All resources have descriptive names and JSON content type
### Prompts
The server provides two prompt templates:
- `compare-models`: Generates a comparison between multiple Hugging Face models
- Required `model_ids` argument (comma-separated model IDs)
- Retrieves model details and formats them for comparison
- `summarize-paper`: Summarizes a research paper from Hugging Face
- Required `arxiv_id` argument for paper identification
- Optional `detail_level` argument (brief/detailed) to control summary depth
- Combines paper metadata with implementation details
### Tools
The server implements several tool categories:
- **Model Tools**
- `search-models`: Search models with filters for query, author, tags, and limit
- `get-model-info`: Get detailed information about a specific model
- **Dataset Tools**
- `search-datasets`: Search datasets with filters
- `get-dataset-info`: Get detailed information about a specific dataset
- **Space Tools**
- `search-spaces`: Search Spaces with filters including SDK type
- `get-space-info`: Get detailed information about a specific Space
- **Paper Tools**
- `get-paper-info`: Get information about a paper and its implementations
- `get-daily-papers`: Get the list of curated daily papers
- **Collection Tools**
- `search-collections`: Search collections with various filters
- `get-collection-info`: Get detailed information about a specific collection
## Configuration
The server does not require configuration, but supports optional Hugging Face authentication:
- Set `HF_TOKEN` environment variable with your Hugging Face API token for:
- Higher API rate limits
- Access to private repositories (if authorized)
- Improved reliability for high-volume requests
## Quickstart
### Install
#### Installing via Smithery
To install huggingface-mcp-server for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@shreyaskarnik/huggingface-mcp-server):
```bash
npx -y @smithery/cli install @shreyaskarnik/huggingface-mcp-server --client claude
```
#### Claude Desktop
On MacOS: `~/Library/Application\ Support/Claude/claude_desktop_config.json`
On Windows: `%APPDATA%/Claude/claude_desktop_config.json`
<details>
<summary>Development/Unpublished Servers Configuration</summary>
```json
"mcpServers": {
"huggingface": {
"command": "uv",
"args": [
"--directory",
"/absolute/path/to/huggingface-mcp-server",
"run",
"huggingface_mcp_server.py"
],
"env": {
"HF_TOKEN": "your_token_here" // Optional
}
}
}
```
</details>
## Development
### Building and Publishing
To prepare the package for distribution:
1. Sync dependencies and update lockfile:
```bash
uv sync
```
1. Build package distributions:
```bash
uv build
```
This will create source and wheel distributions in the `dist/` directory.
1. Publish to PyPI:
```bash
uv publish
```
Note: You'll need to set PyPI credentials via environment variables or command flags:
- Token: `--token` or `UV_PUBLISH_TOKEN`
- Or username/password: `--username`/`UV_PUBLISH_USERNAME` and `--password`/`UV_PUBLISH_PASSWORD`
### Debugging
Since MCP servers run over stdio, debugging can be challenging. For the best debugging
experience, we strongly recommend using the [MCP Inspector](https://github.com/modelcontextprotocol/inspector).
You can launch the MCP Inspector via [`npm`](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) with this command:
```bash
npx @modelcontextprotocol/inspector uv --directory /path/to/huggingface-mcp-server run huggingface_mcp_server.py
```
Upon launching, the Inspector will display a URL that you can access in your browser to begin debugging.
## Example Prompts for Claude
When using this server with Claude, try these example prompts:
- "Search for BERT models on Hugging Face with less than 100 million parameters"
- "Find the most popular datasets for text classification on Hugging Face"
- "What are today's featured AI research papers on Hugging Face?"
- "Summarize the paper with arXiv ID 2307.09288 using the Hugging Face MCP server"
- "Compare the Llama-3-8B and Mistral-7B models from Hugging Face"
- "Show me the most popular Gradio spaces for image generation"
- "Find collections created by TheBloke that include Mixtral models"
## Troubleshooting
If you encounter issues with the server:
1. Check server logs in Claude Desktop:
- macOS: `~/Library/Logs/Claude/mcp-server-huggingface.log`
- Windows: `%APPDATA%\Claude\logs\mcp-server-huggingface.log`
2. For API rate limiting errors, consider adding a Hugging Face API token
3. Make sure your machine has internet connectivity to reach the Hugging Face API
4. If a particular tool is failing, try accessing the same data through the Hugging Face website to verify it exists
```
--------------------------------------------------------------------------------
/src/huggingface/__init__.py:
--------------------------------------------------------------------------------
```python
from . import server
import asyncio
def main():
"""Main entry point for the package."""
asyncio.run(server.main())
# Optionally expose other important items at package level
__all__ = ['main', 'server']
```
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
```toml
[project]
name = "huggingface"
version = "0.1.0"
description = "Hugging Face MCP Server"
readme = "README.md"
requires-python = ">=3.13"
dependencies = ["huggingface-hub>=0.29.3", "mcp>=1.4.1"]
[[project.authors]]
name = "Shreyas Karnik"
email = "[email protected]"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project.scripts]
huggingface = "huggingface:main"
```
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
# Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile
FROM python:3.11-slim
# Set working directory
WORKDIR /app
# Copy necessary files
COPY pyproject.toml ./
COPY README.md ./
COPY src ./src
COPY uv.lock ./
# Upgrade pip and install build tools and the package
RUN pip install --upgrade pip \
&& pip install hatchling \
&& pip install . --ignore-requires-python --no-build-isolation
CMD ["huggingface"]
```
--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------
```yaml
# Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml
startCommand:
type: stdio
configSchema:
# JSON Schema defining the configuration options for the MCP.
type: object
properties:
hfToken:
type: string
default: ""
description: Optional Hugging Face API Token. Leave empty if not provided.
commandFunction:
# A JS function that produces the CLI command based on the given config to start the MCP on stdio.
|-
(config) => ({
command: 'huggingface',
args: [],
env: {
HF_TOKEN: config.hfToken || ""
}
})
exampleConfig:
hfToken: your_hf_token_here
```
--------------------------------------------------------------------------------
/src/huggingface/server.py:
--------------------------------------------------------------------------------
```python
"""
🤗 Hugging Face MCP Server 🤗
This server provides Model Context Protocol (MCP) access to the Hugging Face API,
allowing models like Claude to interact with models, datasets, spaces, and other
Hugging Face resources in a read-only manner.
"""
import asyncio
import json
from typing import Any, Dict, Optional
from urllib.parse import quote_plus
import httpx
import mcp.server.stdio
import mcp.types as types
from huggingface_hub import HfApi
from mcp.server import NotificationOptions, Server
from mcp.server.models import InitializationOptions
from pydantic import AnyUrl
# Initialize server
server = Server("huggingface")
# Initialize Hugging Face API client
hf_api = HfApi()
# Base URL for the Hugging Face API
HF_API_BASE = "https://huggingface.co/api"
# Initialize HTTP client for making requests
http_client = httpx.AsyncClient(timeout=30.0)
# Helper Functions
async def make_hf_request(
endpoint: str, params: Optional[Dict[str, Any]] = None
) -> Dict:
"""Make a request to the Hugging Face API with proper error handling."""
url = f"{HF_API_BASE}/{endpoint}"
try:
response = await http_client.get(url, params=params)
response.raise_for_status()
return response.json()
except Exception as e:
return {"error": str(e)}
# Tool Handlers
@server.list_tools()
async def handle_list_tools() -> list[types.Tool]:
"""
List available tools for interacting with the Hugging Face Hub.
Each tool specifies its arguments using JSON Schema validation.
"""
return [
# Model Tools
types.Tool(
name="search-models",
description="Search for models on Hugging Face Hub",
inputSchema={
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Search term (e.g., 'bert', 'gpt')",
},
"author": {
"type": "string",
"description": "Filter by author/organization (e.g., 'huggingface', 'google')",
},
"tags": {
"type": "string",
"description": "Filter by tags (e.g., 'text-classification', 'translation')",
},
"limit": {
"type": "integer",
"description": "Maximum number of results to return",
},
},
},
),
types.Tool(
name="get-model-info",
description="Get detailed information about a specific model",
inputSchema={
"type": "object",
"properties": {
"model_id": {
"type": "string",
"description": "The ID of the model (e.g., 'google/bert-base-uncased')",
},
},
"required": ["model_id"],
},
),
# Dataset Tools
types.Tool(
name="search-datasets",
description="Search for datasets on Hugging Face Hub",
inputSchema={
"type": "object",
"properties": {
"query": {"type": "string", "description": "Search term"},
"author": {
"type": "string",
"description": "Filter by author/organization",
},
"tags": {"type": "string", "description": "Filter by tags"},
"limit": {
"type": "integer",
"description": "Maximum number of results to return",
},
},
},
),
types.Tool(
name="get-dataset-info",
description="Get detailed information about a specific dataset",
inputSchema={
"type": "object",
"properties": {
"dataset_id": {
"type": "string",
"description": "The ID of the dataset (e.g., 'squad')",
},
},
"required": ["dataset_id"],
},
),
# Space Tools
types.Tool(
name="search-spaces",
description="Search for Spaces on Hugging Face Hub",
inputSchema={
"type": "object",
"properties": {
"query": {"type": "string", "description": "Search term"},
"author": {
"type": "string",
"description": "Filter by author/organization",
},
"tags": {"type": "string", "description": "Filter by tags"},
"sdk": {
"type": "string",
"description": "Filter by SDK (e.g., 'streamlit', 'gradio', 'docker')",
},
"limit": {
"type": "integer",
"description": "Maximum number of results to return",
},
},
},
),
types.Tool(
name="get-space-info",
description="Get detailed information about a specific Space",
inputSchema={
"type": "object",
"properties": {
"space_id": {
"type": "string",
"description": "The ID of the Space (e.g., 'huggingface/diffusers-demo')",
},
},
"required": ["space_id"],
},
),
# Papers Tools
types.Tool(
name="get-paper-info",
description="Get information about a specific paper on Hugging Face",
inputSchema={
"type": "object",
"properties": {
"arxiv_id": {
"type": "string",
"description": "The arXiv ID of the paper (e.g., '1810.04805')",
},
},
"required": ["arxiv_id"],
},
),
types.Tool(
name="get-daily-papers",
description="Get the list of daily papers curated by Hugging Face",
inputSchema={
"type": "object",
"properties": {},
},
),
# Collections Tools
types.Tool(
name="search-collections",
description="Search for collections on Hugging Face Hub",
inputSchema={
"type": "object",
"properties": {
"owner": {"type": "string", "description": "Filter by owner"},
"item": {
"type": "string",
"description": "Filter by item (e.g., 'models/teknium/OpenHermes-2.5-Mistral-7B')",
},
"query": {
"type": "string",
"description": "Search term for titles and descriptions",
},
"limit": {
"type": "integer",
"description": "Maximum number of results to return",
},
},
},
),
types.Tool(
name="get-collection-info",
description="Get detailed information about a specific collection",
inputSchema={
"type": "object",
"properties": {
"namespace": {
"type": "string",
"description": "The namespace of the collection (user or organization)",
},
"collection_id": {
"type": "string",
"description": "The ID part of the collection",
},
},
"required": ["namespace", "collection_id"],
},
),
]
@server.call_tool()
async def handle_call_tool(
name: str, arguments: dict | None
) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
"""
Handle tool execution requests for Hugging Face API.
"""
if not arguments:
arguments = {}
if name == "search-models":
query = arguments.get("query")
author = arguments.get("author")
tags = arguments.get("tags")
limit = arguments.get("limit", 10)
params = {"limit": limit}
if query:
params["search"] = query
if author:
params["author"] = author
if tags:
params["filter"] = tags
data = await make_hf_request("models", params)
if "error" in data:
return [
types.TextContent(
type="text", text=f"Error searching models: {data['error']}"
)
]
# Format the results
results = []
for model in data:
model_info = {
"id": model.get("id", ""),
"name": model.get("modelId", ""),
"author": model.get("author", ""),
"tags": model.get("tags", []),
"downloads": model.get("downloads", 0),
"likes": model.get("likes", 0),
"lastModified": model.get("lastModified", ""),
}
results.append(model_info)
return [types.TextContent(type="text", text=json.dumps(results, indent=2))]
elif name == "get-model-info":
model_id = arguments.get("model_id")
if not model_id:
return [types.TextContent(type="text", text="Error: model_id is required")]
data = await make_hf_request(f"models/{quote_plus(model_id)}")
if "error" in data:
return [
types.TextContent(
type="text",
text=f"Error retrieving model information: {data['error']}",
)
]
# Format the result
model_info = {
"id": data.get("id", ""),
"name": data.get("modelId", ""),
"author": data.get("author", ""),
"tags": data.get("tags", []),
"pipeline_tag": data.get("pipeline_tag", ""),
"downloads": data.get("downloads", 0),
"likes": data.get("likes", 0),
"lastModified": data.get("lastModified", ""),
"description": data.get("description", "No description available"),
}
# Add model card if available
if "card" in data and data["card"]:
model_info["model_card"] = (
data["card"].get("data", {}).get("text", "No model card available")
)
return [types.TextContent(type="text", text=json.dumps(model_info, indent=2))]
elif name == "search-datasets":
query = arguments.get("query")
author = arguments.get("author")
tags = arguments.get("tags")
limit = arguments.get("limit", 10)
params = {"limit": limit}
if query:
params["search"] = query
if author:
params["author"] = author
if tags:
params["filter"] = tags
data = await make_hf_request("datasets", params)
if "error" in data:
return [
types.TextContent(
type="text", text=f"Error searching datasets: {data['error']}"
)
]
# Format the results
results = []
for dataset in data:
dataset_info = {
"id": dataset.get("id", ""),
"name": dataset.get("datasetId", ""),
"author": dataset.get("author", ""),
"tags": dataset.get("tags", []),
"downloads": dataset.get("downloads", 0),
"likes": dataset.get("likes", 0),
"lastModified": dataset.get("lastModified", ""),
}
results.append(dataset_info)
return [types.TextContent(type="text", text=json.dumps(results, indent=2))]
elif name == "get-dataset-info":
dataset_id = arguments.get("dataset_id")
if not dataset_id:
return [
types.TextContent(type="text", text="Error: dataset_id is required")
]
data = await make_hf_request(f"datasets/{quote_plus(dataset_id)}")
if "error" in data:
return [
types.TextContent(
type="text",
text=f"Error retrieving dataset information: {data['error']}",
)
]
# Format the result
dataset_info = {
"id": data.get("id", ""),
"name": data.get("datasetId", ""),
"author": data.get("author", ""),
"tags": data.get("tags", []),
"downloads": data.get("downloads", 0),
"likes": data.get("likes", 0),
"lastModified": data.get("lastModified", ""),
"description": data.get("description", "No description available"),
}
# Add dataset card if available
if "card" in data and data["card"]:
dataset_info["dataset_card"] = (
data["card"].get("data", {}).get("text", "No dataset card available")
)
return [types.TextContent(type="text", text=json.dumps(dataset_info, indent=2))]
elif name == "search-spaces":
query = arguments.get("query")
author = arguments.get("author")
tags = arguments.get("tags")
sdk = arguments.get("sdk")
limit = arguments.get("limit", 10)
params = {"limit": limit}
if query:
params["search"] = query
if author:
params["author"] = author
if tags:
params["filter"] = tags
if sdk:
params["filter"] = params.get("filter", "") + f" sdk:{sdk}"
data = await make_hf_request("spaces", params)
if "error" in data:
return [
types.TextContent(
type="text", text=f"Error searching spaces: {data['error']}"
)
]
# Format the results
results = []
for space in data:
space_info = {
"id": space.get("id", ""),
"name": space.get("spaceId", ""),
"author": space.get("author", ""),
"sdk": space.get("sdk", ""),
"tags": space.get("tags", []),
"likes": space.get("likes", 0),
"lastModified": space.get("lastModified", ""),
}
results.append(space_info)
return [types.TextContent(type="text", text=json.dumps(results, indent=2))]
elif name == "get-space-info":
space_id = arguments.get("space_id")
if not space_id:
return [types.TextContent(type="text", text="Error: space_id is required")]
data = await make_hf_request(f"spaces/{quote_plus(space_id)}")
if "error" in data:
return [
types.TextContent(
type="text",
text=f"Error retrieving space information: {data['error']}",
)
]
# Format the result
space_info = {
"id": data.get("id", ""),
"name": data.get("spaceId", ""),
"author": data.get("author", ""),
"sdk": data.get("sdk", ""),
"tags": data.get("tags", []),
"likes": data.get("likes", 0),
"lastModified": data.get("lastModified", ""),
"description": data.get("description", "No description available"),
"url": f"https://huggingface.co/spaces/{space_id}",
}
return [types.TextContent(type="text", text=json.dumps(space_info, indent=2))]
elif name == "get-paper-info":
arxiv_id = arguments.get("arxiv_id")
if not arxiv_id:
return [types.TextContent(type="text", text="Error: arxiv_id is required")]
data = await make_hf_request(f"papers/{arxiv_id}")
if "error" in data:
return [
types.TextContent(
type="text",
text=f"Error retrieving paper information: {data['error']}",
)
]
# Format the result
paper_info = {
"arxiv_id": data.get("arxivId", ""),
"title": data.get("title", ""),
"authors": data.get("authors", []),
"summary": data.get("summary", "No summary available"),
"url": f"https://huggingface.co/papers/{arxiv_id}",
}
# Get implementations
implementations = await make_hf_request(f"arxiv/{arxiv_id}/repos")
if "error" not in implementations:
paper_info["implementations"] = implementations
return [types.TextContent(type="text", text=json.dumps(paper_info, indent=2))]
elif name == "get-daily-papers":
data = await make_hf_request("daily_papers")
if "error" in data:
return [
types.TextContent(
type="text", text=f"Error retrieving daily papers: {data['error']}"
)
]
# Format the results
results = []
for paper in data:
paper_info = {
"arxiv_id": paper.get("paper", {}).get("arxivId", ""),
"title": paper.get("paper", {}).get("title", ""),
"authors": paper.get("paper", {}).get("authors", []),
"summary": paper.get("paper", {}).get("summary", "")[:200] + "..."
if len(paper.get("paper", {}).get("summary", "")) > 200
else paper.get("paper", {}).get("summary", ""),
}
results.append(paper_info)
return [types.TextContent(type="text", text=json.dumps(results, indent=2))]
elif name == "search-collections":
owner = arguments.get("owner")
item = arguments.get("item")
query = arguments.get("query")
limit = arguments.get("limit", 10)
params = {"limit": limit}
if owner:
params["owner"] = owner
if item:
params["item"] = item
if query:
params["q"] = query
data = await make_hf_request("collections", params)
if "error" in data:
return [
types.TextContent(
type="text", text=f"Error searching collections: {data['error']}"
)
]
# Format the results
results = []
for collection in data:
collection_info = {
"id": collection.get("id", ""),
"title": collection.get("title", ""),
"owner": collection.get("owner", {}).get("name", ""),
"description": collection.get(
"description", "No description available"
),
"items_count": collection.get("itemsCount", 0),
"upvotes": collection.get("upvotes", 0),
"last_modified": collection.get("lastModified", ""),
}
results.append(collection_info)
return [types.TextContent(type="text", text=json.dumps(results, indent=2))]
elif name == "get-collection-info":
namespace = arguments.get("namespace")
collection_id = arguments.get("collection_id")
if not namespace or not collection_id:
return [
types.TextContent(
type="text", text="Error: namespace and collection_id are required"
)
]
# Extract the slug from the collection_id if it contains a dash
slug = collection_id.split("-")[0] if "-" in collection_id else collection_id
endpoint = f"collections/{namespace}/{slug}-{collection_id}"
data = await make_hf_request(endpoint)
if "error" in data:
return [
types.TextContent(
type="text",
text=f"Error retrieving collection information: {data['error']}",
)
]
# Format the result
collection_info = {
"id": data.get("id", ""),
"title": data.get("title", ""),
"owner": data.get("owner", {}).get("name", ""),
"description": data.get("description", "No description available"),
"upvotes": data.get("upvotes", 0),
"last_modified": data.get("lastModified", ""),
"items": [],
}
# Add items
for item in data.get("items", []):
item_info = {
"type": item.get("item", {}).get("type", ""),
"id": item.get("item", {}).get("id", ""),
"note": item.get("note", ""),
}
collection_info["items"].append(item_info)
return [
types.TextContent(type="text", text=json.dumps(collection_info, indent=2))
]
else:
return [types.TextContent(type="text", text=f"Unknown tool: {name}")]
# Resource Handlers - Define popular models, datasets, and spaces as resources
@server.list_resources()
async def handle_list_resources() -> list[types.Resource]:
"""
List available Hugging Face resources.
This provides direct access to popular models, datasets, and spaces.
"""
resources = []
# Popular models
popular_models = [
(
"meta-llama/Llama-3-8B-Instruct",
"Llama 3 8B Instruct",
"Meta's Llama 3 8B Instruct model",
),
(
"mistralai/Mistral-7B-Instruct-v0.2",
"Mistral 7B Instruct v0.2",
"Mistral AI's 7B instruction-following model",
),
(
"openchat/openchat-3.5-0106",
"OpenChat 3.5",
"Open-source chatbot based on Mistral 7B",
),
(
"stabilityai/stable-diffusion-xl-base-1.0",
"Stable Diffusion XL 1.0",
"SDXL text-to-image model",
),
]
for model_id, name, description in popular_models:
resources.append(
types.Resource(
uri=AnyUrl(f"hf://model/{model_id}"),
name=name,
description=description,
mimeType="application/json",
)
)
# Popular datasets
popular_datasets = [
(
"databricks/databricks-dolly-15k",
"Databricks Dolly 15k",
"15k instruction-following examples",
),
("squad", "SQuAD", "Stanford Question Answering Dataset"),
("glue", "GLUE", "General Language Understanding Evaluation benchmark"),
(
"openai/summarize_from_feedback",
"Summarize From Feedback",
"OpenAI summarization dataset",
),
]
for dataset_id, name, description in popular_datasets:
resources.append(
types.Resource(
uri=AnyUrl(f"hf://dataset/{dataset_id}"),
name=name,
description=description,
mimeType="application/json",
)
)
# Popular spaces
popular_spaces = [
(
"huggingface/diffusers-demo",
"Diffusers Demo",
"Demo of Stable Diffusion models",
),
("gradio/chatbot-demo", "Chatbot Demo", "Demo of a Gradio chatbot interface"),
(
"prompthero/midjourney-v4-diffusion",
"Midjourney v4 Diffusion",
"Replica of Midjourney v4",
),
("stabilityai/stablevicuna", "StableVicuna", "Fine-tuned Vicuna with RLHF"),
]
for space_id, name, description in popular_spaces:
resources.append(
types.Resource(
uri=AnyUrl(f"hf://space/{space_id}"),
name=name,
description=description,
mimeType="application/json",
)
)
return resources
@server.read_resource()
async def handle_read_resource(uri: AnyUrl) -> str:
"""
Read a specific Hugging Face resource by its URI.
"""
if uri.scheme != "hf":
raise ValueError(f"Unsupported URI scheme: {uri.scheme}")
if not uri.path:
raise ValueError("Invalid Hugging Face resource URI")
parts = uri.path.lstrip("/").split("/", 1)
if len(parts) != 2:
raise ValueError("Invalid Hugging Face resource URI format")
resource_type, resource_id = parts
if resource_type == "model":
data = await make_hf_request(f"models/{quote_plus(resource_id)}")
elif resource_type == "dataset":
data = await make_hf_request(f"datasets/{quote_plus(resource_id)}")
elif resource_type == "space":
data = await make_hf_request(f"spaces/{quote_plus(resource_id)}")
else:
raise ValueError(f"Unsupported resource type: {resource_type}")
if "error" in data:
raise ValueError(f"Error retrieving resource: {data['error']}")
return json.dumps(data, indent=2)
# Prompt Handlers
@server.list_prompts()
async def handle_list_prompts() -> list[types.Prompt]:
"""
List available prompts for Hugging Face integration.
"""
return [
types.Prompt(
name="compare-models",
description="Compare multiple Hugging Face models",
arguments=[
types.PromptArgument(
name="model_ids",
description="Comma-separated list of model IDs to compare",
required=True,
)
],
),
types.Prompt(
name="summarize-paper",
description="Summarize an AI research paper from arXiv",
arguments=[
types.PromptArgument(
name="arxiv_id",
description="arXiv ID of the paper to summarize",
required=True,
),
types.PromptArgument(
name="detail_level",
description="Level of detail in the summary (brief/detailed/eli5)",
required=False,
),
],
),
]
@server.get_prompt()
async def handle_get_prompt(
name: str, arguments: dict[str, str] | None
) -> types.GetPromptResult:
"""
Generate a prompt related to Hugging Face resources.
"""
if not arguments:
arguments = {}
if name == "compare-models":
model_ids = arguments.get("model_ids", "")
if not model_ids:
raise ValueError("model_ids argument is required")
model_list = [model_id.strip() for model_id in model_ids.split(",")]
models_data = []
for model_id in model_list:
data = await make_hf_request(f"models/{quote_plus(model_id)}")
if "error" not in data:
models_data.append(data)
model_details = []
for data in models_data:
details = {
"id": data.get("id", ""),
"author": data.get("author", ""),
"downloads": data.get("downloads", 0),
"tags": data.get("tags", []),
"description": data.get("description", "No description available"),
}
model_details.append(details)
return types.GetPromptResult(
description=f"Comparing models: {model_ids}",
messages=[
types.PromptMessage(
role="user",
content=types.TextContent(
type="text",
text="I'd like you to compare these Hugging Face models and help me understand their differences, strengths, and suitable use cases:\n\n"
+ json.dumps(model_details, indent=2)
+ "\n\nPlease structure your comparison with sections on architecture, performance, use cases, and limitations.",
),
)
],
)
elif name == "summarize-paper":
arxiv_id = arguments.get("arxiv_id", "")
if not arxiv_id:
raise ValueError("arxiv_id argument is required")
detail_level = arguments.get("detail_level", "detailed")
paper_data = await make_hf_request(f"papers/{arxiv_id}")
if "error" in paper_data:
raise ValueError(f"Error retrieving paper: {paper_data['error']}")
# Get implementations
implementations = await make_hf_request(f"arxiv/{arxiv_id}/repos")
return types.GetPromptResult(
description=f"Summarizing paper: {paper_data.get('title', arxiv_id)}",
messages=[
types.PromptMessage(
role="user",
content=types.TextContent(
type="text",
text=f"Please provide a {'detailed' if detail_level == 'detailed' else 'brief'} summary of this AI research paper:\n\n"
+ f"Title: {paper_data.get('title', 'Unknown')}\n"
+ f"Authors: {', '.join(paper_data.get('authors', []))}\n"
+ f"Abstract: {paper_data.get('summary', 'No abstract available')}\n\n"
+ (
f"Implementations on Hugging Face: {json.dumps(implementations, indent=2)}\n\n"
if "error" not in implementations
else ""
)
+ f"Please {'cover all key aspects including methodology, results, and implications' if detail_level == 'detailed' else 'provide a concise overview of the main contributions'}.",
),
)
],
)
else:
raise ValueError(f"Unknown prompt: {name}")
async def main():
# Run the server using stdin/stdout streams
async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
await server.run(
read_stream,
write_stream,
InitializationOptions(
server_name="huggingface",
server_version="0.1.0",
capabilities=server.get_capabilities(
notification_options=NotificationOptions(),
experimental_capabilities={},
),
),
)
if __name__ == "__main__":
asyncio.run(main())
```