A Python client library for Yuque OpenAPI
Project description
Yuque Python SDK
A modern, type-safe Python client library for the Yuque OpenAPI.
Features
- Modern Python: Built with Python 3.10+ type hints and Pydantic v2
- Async/Sync Support: Both synchronous and asynchronous interfaces
- Comprehensive Coverage: Full support for all Yuque API endpoints
- Type Safety: Full type annotations with Pydantic models
- Error Handling: Detailed exception hierarchy for API errors
- Pagination: Automatic pagination handling for list endpoints
- Smart Caching: Built-in caching with Redis and memory backends, TTL policies
- Dual Access: Access resources by ID or by path (group/book/slug)
Installation
# Using uv (recommended)
uv add yuque
# Using pip
pip install yuque
MCP Server Support
To use this library as an MCP (Model Context Protocol) server, install with MCP extras:
# Using uv
uv add yuque[mcp]
# Using pip
pip install yuque[mcp]
This will install additional dependencies:
mcp>=0.9.0- MCP Python SDK for building serversfastapi>=0.100.0- FastAPI framework (optional, for HTTP transport)uvicorn>=0.22.0- ASGI server (optional, for HTTP transport)
🤖 MCP Server Usage
This library can be used as an MCP (Model Context Protocol) server, allowing AI assistants like Claude Desktop to interact with Yuque directly.
What is MCP?
MCP (Model Context Protocol) is a protocol that enables AI assistants to interact with external tools and services. By running this library as an MCP server, you can:
- ✨ Let Claude Desktop access your Yuque documents and repositories
- 🔍 Search and retrieve Yuque content through natural language
- ✏️ Create and update documents via AI assistance
- 👥 Manage groups and team members
- 📊 Access comprehensive Yuque statistics
Installation
Install with MCP support:
# Using uv
uv add yuque[mcp]
# Using pip
pip install yuque[mcp]
This installs additional dependencies:
mcp>=0.9.0- MCP Python SDKfastapi>=0.100.0- FastAPI framework (optional, for HTTP transport)uvicorn>=0.22.0- ASGI server (optional, for HTTP transport)
Configuration
Set your Yuque API token as an environment variable:
# macOS/Linux
export YUQUE_TOKEN='your-api-token-here'
# Windows (Command Prompt)
set YUQUE_TOKEN=your-api-token-here
# Windows (PowerShell)
$env:YUQUE_TOKEN='your-api-token-here'
To get your API token:
- Log in to Yuque
- Go to Settings → Developer Settings
- Create a new Personal Access Token
- Copy and save the token securely
Usage Methods
1️⃣ Claude Desktop Integration
Add the MCP server to your Claude Desktop configuration:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
Add this configuration:
{
"mcpServers": {
"yuque": {
"command": "uvx",
"args": ["yuque[mcp]"],
"env": {
"YUQUE_TOKEN": "your-api-token-here"
}
}
}
}
Or if installed via pip:
{
"mcpServers": {
"yuque": {
"command": "yuque-mcp",
"env": {
"YUQUE_TOKEN": "your-api-token-here"
}
}
}
}
After updating the config:
- Restart Claude Desktop completely
- The Yuque tools will appear in Claude's available tools
- Start using natural language to interact with Yuque!
Example prompts for Claude:
- "Search for documents about API design in my Yuque"
- "Get the table of contents for my team's documentation repository"
- "Create a new document titled 'Meeting Notes' in the Engineering knowledge base"
- "Show me all the repositories in my team group"
2️⃣ Command Line Usage
Run the MCP server directly from command line:
# Start with STDIO transport (default, for MCP clients)
yuque-mcp
# Start with SSE transport (for HTTP-based clients)
yuque-mcp --transport sse
# Start with HTTP transport
yuque-mcp --transport http
# Enable debug mode
yuque-mcp --debug
# Set custom log level
yuque-mcp --log-level DEBUG
# Custom server name
yuque-mcp --name "my-yuque-server"
3️⃣ Programmatic Usage
Use the MCP server in your Python applications:
import asyncio
from yuque.mcp import MCPServer
async def main():
# Initialize the server
server = MCPServer(
token="your-api-token", # Or use YUQUE_TOKEN env var
name="my-yuque-server",
debug=False,
log_level="INFO"
)
# Run the server
await server.start_async(transport="stdio")
# Run the server
asyncio.run(main())
Or use it as a context manager:
from yuque.mcp import MCPServer
async def run_server():
async with MCPServer(token="your-token") as server:
# Server is running
await server.start_async()
🛠️ Available Tools
The MCP server provides 27 comprehensive tools for interacting with Yuque:
👤 User Tools (3)
| Tool | Description |
|---|---|
yuque_get_current_user |
Get current authenticated user's information |
yuque_get_user |
Get a specific user by ID |
yuque_get_user_groups |
Get groups/teams a user belongs to |
📄 Document Tools (9)
| Tool | Description |
|---|---|
yuque_get_doc |
Get document by ID |
yuque_get_docs_by_repo |
List documents in a repository |
yuque_get_docs_by_path |
List documents by group/book path |
yuque_create_doc |
Create a new document |
yuque_create_doc_by_path |
Create document by path |
yuque_update_doc |
Update an existing document |
yuque_update_doc_by_repo |
Update document in repository |
yuque_delete_doc |
Delete a document |
yuque_get_doc_version |
Get a specific document version |
📚 Repository Tools (7)
| Tool | Description |
|---|---|
yuque_get_repo |
Get repository by ID |
yuque_get_repo_by_path |
Get repository by path |
yuque_list_repos |
List all accessible repositories |
yuque_get_user_repos |
Get user's repositories |
yuque_get_group_repos |
Get group's repositories |
yuque_get_repo_toc |
Get table of contents |
yuque_get_repo_toc_by_path |
Get TOC by path |
👥 Group Tools (7)
| Tool | Description |
|---|---|
yuque_get_group |
Get group information |
yuque_get_group_repos |
Get repositories in a group |
yuque_get_group_members |
Get group members |
yuque_add_group_member |
Add member to group |
yuque_update_group_member |
Update member role |
yuque_remove_group_member |
Remove member from group |
yuque_get_group_statistics |
Get group statistics |
🔍 Search Tools (1)
| Tool | Description |
|---|---|
yuque_search |
Search across Yuque (docs, repos, users, groups) |
💡 Examples
Searching for Documents
# In Claude Desktop, simply ask:
"Search for documents about Python in my Yuque workspace"
# The tool will be called with:
yuque_search(keyword="Python", type="doc", page=1)
Creating a Document
# Natural language request to Claude:
"Create a new document titled 'API Guide' in the engineering/api-docs repository with markdown content"
# Results in:
yuque_create_doc_by_path(
group_login="engineering",
book_slug="api-docs",
title="API Guide",
body="# API Guide\n\nWelcome to the API documentation...",
format="markdown"
)
Managing Team Members
# Request:
"Add user with ID 12345 to the 'engineering' team as a member"
# Tool call:
yuque_add_group_member(
login="engineering",
user_id=12345,
role=1 # 0=Admin, 1=Member, 2=Read-only
)
🔧 Troubleshooting
Common Issues
❌ "YUQUE_TOKEN environment variable is required"
Solution: Make sure you've set the environment variable:
export YUQUE_TOKEN='your-token-here'
❌ "Authentication failed"
Possible causes:
- Invalid or expired API token
- Token lacks necessary permissions
- Network connectivity issues
Solution: Generate a new token from Yuque settings and ensure it has the required permissions.
❌ "Claude Desktop doesn't see the tools"
Solutions:
- Verify the config file location is correct
- Restart Claude Desktop completely (not just refresh)
- Check the JSON syntax in the config file
- Ensure
yuque-mcpis in your PATH or use full path
❌ "Permission denied"
Cause: Your token doesn't have access to the resource.
Solution: Check that:
- You have access to the group/repository
- Your token has appropriate scopes
- You're not trying to access private content without permission
❌ "Rate limit exceeded"
Solution: Yuque API has rate limits. Wait a moment and try again.
Debug Mode
Enable debug mode for verbose logging:
# Command line
yuque-mcp --debug
# Or in Claude Desktop config
{
"mcpServers": {
"yuque": {
"command": "yuque-mcp",
"args": ["--debug"],
"env": {
"YUQUE_TOKEN": "your-token"
}
}
}
}
Getting Help
If you encounter issues:
- Check the logs: Debug mode shows detailed error messages
- Verify token permissions: Ensure your token has the necessary scopes
- Test the CLI: Run
yuque-mcp --debugto see if the server starts correctly - Open an issue: GitHub Issues
📚 Best Practices
-
Token Security
- Never commit tokens to version control
- Use environment variables or secure secret management
- Rotate tokens periodically
-
Error Handling
- Always check the
successfield in responses - Handle rate limits gracefully with retries
- Validate user inputs before creating/updating content
- Always check the
-
Performance
- Use pagination for large result sets
- Cache frequently accessed data
- Limit concurrent requests to avoid rate limiting
-
Content Management
- Use meaningful document titles and slugs
- Organize content with clear hierarchy
- Set appropriate visibility levels (public/private)
Quick Start
Synchronous Usage
from yuque import YuqueClient
# Initialize the client with your API token
client = YuqueClient(token="your-api-token")
# Get current user
user = client.user.get_me()
print(f"Hello, {user.name}!")
# List your repositories
repos = client.repo.list()
for repo in repos:
print(f"- {repo['name']} ({repo['slug']})")
# Get a specific document
doc = client.doc.get(doc_id=12345)
print(f"Document: {doc['title']}")
Asynchronous Usage
import asyncio
from yuque import YuqueClient
async def main():
async with YuqueClient(token="your-api-token") as client:
# Get current user
user = await client.user.get_me_async()
print(f"Hello, {user.name}!")
# List repositories
repos = await client.repo.list_async()
for repo in repos:
print(f"- {repo.name}")
asyncio.run(main())
Using as Context Manager
from yuque import YuqueClient
# Synchronous context manager
with YuqueClient(token="your-token") as client:
user = client.user.get_me()
print(user.name)
# Asynchronous context manager
import asyncio
async def async_example():
async with YuqueClient(token="your-token") as client:
user = await client.user.get_me_async()
print(user.name)
asyncio.run(async_example())
API Reference
User API
# Get current authenticated user
user = client.user.get_me()
# Get user by ID
user = client.user.get_by_id(user_id=123)
# Get groups for a user
groups = client.user.get_groups(user_id=123)
Group API
# Get group by login
group = client.group.get(login="my-group")
# Get group by ID (dual access)
group = client.group.get_by_id(group_id=12345)
# List repositories in a group
repos = client.group.get_repos(login="my-group")
# List group members
members = client.group.get_members(login="my-group")
# Add a member to group
client.group.add_member(login="my-group", user_id=123, role=1)
# Update member role
client.group.update_member(login="my-group", user_id=123, role=0)
# Remove a member
client.group.remove_member(login="my-group", user_id=123)
# Get group statistics
stats = client.group.get_statistics(login="my-group")
# Get member statistics with time range filter
member_stats = client.group.get_member_stats(
login="my-group",
range="30d", # "7d", "30d", "90d"
page=1,
limit=20,
)
# Get book statistics
book_stats = client.group.get_book_stats(
login="my-group",
range="30d",
)
# Get document statistics
doc_stats = client.group.get_doc_stats(
login="my-group",
range="30d",
)
Repository (Book) API
# Get repository by ID
repo = client.repo.get(book_id=123)
# Get repository by path (dual access)
repo = client.repo.get_by_path(group_login="my-group", book_slug="my-repo")
# List all repositories for authenticated user
repos = client.repo.list()
# List user's repositories
repos = client.repo.get_user_repos(login="username")
# List group's repositories
repos = client.repo.get_group_repos(login="my-group")
# Get table of contents
toc = client.repo.get_toc(book_id=123)
toc = client.repo.get_toc_by_path(group_login="my-group", book_slug="my-repo")
# Create a new repository (knowledge base)
repo = client.repo.create_repo(
owner_login="my-group",
owner_type="Group", # or "User"
name="New Knowledge Base",
slug="new-kb",
description="Description here",
public=0, # 0=private, 1=public, 2=public to internet
)
# Update an existing repository
repo = client.repo.update_repo(
book_id=123,
name="Updated Name",
description="New description",
public=1,
)
# Delete a repository
result = client.repo.delete_repo(book_id=123)
Document API
# Get document by ID
doc = client.doc.get(doc_id=12345)
# Get document by path (dual access)
doc = client.doc.get_doc_by_path(
namespace="my-group/my-book", # group_login/book_slug
slug="intro",
raw=False, # True for raw markdown
)
# List documents in a repository
docs = client.doc.get_by_repo(book_id=123)
# List documents by path
docs = client.doc.get_by_path(group_login="my-group", book_slug="my-repo")
# Create a document
doc = client.doc.create(
book_id=123,
title="My New Document",
body="# Hello World\n\nThis is my new document.",
format="markdown",
)
# Create document by path
doc = client.doc.create_by_path(
group_login="my-group",
book_slug="my-repo",
title="New Doc",
body="Content...",
)
# Update a document by ID
doc = client.doc.update(
doc_id=12345,
title="Updated Title",
body="Updated content...",
)
# Update document by path (dual access)
doc = client.doc.update_by_path(
namespace="my-group/my-book",
slug="intro",
title="Updated Title",
body="New content",
)
# Delete a document by ID
client.doc.delete(doc_id=12345)
# Delete document by path (dual access)
client.doc.delete_by_path(namespace="my-group/my-book", slug="old-doc")
# Get document version
version = client.doc.get_version(version_id=1)
Search API
# Search across Yuque
results = client.search.search(keyword="python", type="doc")
for result in results:
print(f"- {result.title}: {result.url}")
Error Handling
The library provides detailed exceptions for different API error scenarios:
from yuque import YuqueClient
from yuque.exceptions import (
YuqueError,
AuthenticationError,
PermissionDeniedError,
NotFoundError,
InvalidArgumentError,
ValidationError,
RateLimitError,
ServerError,
NetworkError,
)
try:
client = YuqueClient(token="your-token")
user = client.user.get_me()
except AuthenticationError:
print("Invalid API token")
except PermissionDeniedError:
print("You don't have permission")
except NotFoundError:
print("Resource not found")
except RateLimitError as e:
print(f"Rate limited. Retry after {e.retry_after} seconds")
except YuqueError as e:
print(f"API error: {e}")
Configuration
from yuque import YuqueClient
# Custom timeout
client = YuqueClient(token="your-token", timeout=60.0)
# Custom max retries
client = YuqueClient(token="your-token", max_retries=5)
Caching
The SDK provides built-in caching support with multiple backends and automatic TTL policies.
Basic Usage
from yuque import YuqueClient
from yuque.cache import CacheManager
# Create a cache manager with default memory backend
cache = CacheManager(enabled=True)
# Pass to client
with YuqueClient(token="your-token", cache=cache) as client:
# First call fetches from API
user = client.user.get_me()
# Second call returns cached data (faster!)
user = client.user.get_me()
# Check cache statistics
stats = cache.stats
print(f"Hits: {stats.hits}, Misses: {stats.misses}")
Cache Backends
Memory Backend (Default)
from yuque.cache import CacheManager, MemoryCacheBackend
# Memory backend - good for single-process applications
backend = MemoryCacheBackend(max_size=1000)
cache = CacheManager(backend=backend, enabled=True)
Redis Backend (Distributed)
from yuque.cache import AsyncCacheManager, RedisCacheBackend
# Redis backend - good for distributed applications
redis_backend = RedisCacheBackend(
url="redis://localhost:6379/0",
key_prefix="yuque:"
)
cache = AsyncCacheManager(backend=redis_backend, enabled=True)
# Use with async client
async with YuqueClient(token="your-token", cache=cache) as client:
user = await client.user.get_me_async()
Note: Redis backend requires
redispackage:pip install redis
TTL Policies
Default TTL (time-to-live) policies:
| Endpoint Pattern | TTL |
|---|---|
/user |
24 hours |
/repos |
12 hours |
/repos/*/docs |
6 hours |
/docs/ |
3 hours |
/search |
1 hour |
/groups |
12 hours |
Customize TTL policies:
from yuque.cache import CacheManager, TTL_POLICIES
custom_policies = {
**TTL_POLICIES,
"/docs/": 60 * 60, # 1 hour for documents
"/search": 30 * 60, # 30 minutes for search
}
cache = CacheManager(ttl_policies=custom_policies, default_ttl=1800)
Cache Statistics
# Get cache statistics
stats = cache.stats
print(f"Hit rate: {stats.hit_rate:.2%}")
print(f"Total requests: {stats.total_requests}")
print(f"Avg hit latency: {stats.avg_hit_latency_ms:.2f}ms")
# Get detailed stats as dictionary
stats_dict = cache.get_stats_dict()
print(stats_dict)
# Reset statistics
cache.reset_stats()
Disable Caching
# Disable cache temporarily
cache.enabled = False
# Or create client without cache
client = YuqueClient(token="your-token") # No caching
# Clear all cached data
cache.clear()
Migration Guide
Upgrading from v1.x to v2.x
Version 2.0 introduces caching, dual access patterns, and new APIs. Here's how to migrate:
New Features (Optional)
Caching - Enable for better performance:
# Before (v1.x)
client = YuqueClient(token="your-token")
# After (v2.x) - with caching
from yuque.cache import CacheManager
cache = CacheManager(enabled=True)
client = YuqueClient(token="your-token", cache=cache)
Dual Access - Access resources by path:
# Before (v1.x) - only by ID
doc = client.doc.get(doc_id=12345)
# After (v2.x) - also by path
doc = client.doc.get_doc_by_path("my-group/my-book", "intro")
New Repository APIs
# Create repository
repo = client.repo.create_repo(
owner_login="my-group",
owner_type="Group",
name="New KB",
)
# Update repository
client.repo.update_repo(book_id=123, name="Updated Name")
# Delete repository
client.repo.delete_repo(book_id=123)
New Group Statistics APIs
# Member statistics
stats = client.group.get_member_stats("my-group", range="30d")
# Book statistics
stats = client.group.get_book_stats("my-group", range="30d")
# Document statistics
stats = client.group.get_doc_stats("my-group", range="30d")
Async Methods
All new methods have async equivalents:
# Sync
repo = client.repo.create_repo(...)
# Async
repo = await client.repo.create_repo_async(...)
Breaking Changes
None. All v1.x APIs remain fully compatible with v2.x.
Getting Your API Token
- Log in to Yuque
- Go to Developer Settings
- Create a new Personal Access Token
- Copy the token and use it in your application
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
Links
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file yuque_sdk-0.1.2.tar.gz.
File metadata
- Download URL: yuque_sdk-0.1.2.tar.gz
- Upload date:
- Size: 245.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a66fab27c6114ddf2d4323510d6a4fba6062afeb32e96ca817eeb45b87e7cfe6
|
|
| MD5 |
3f2d45290246ca5bd5fc29c083d56a5e
|
|
| BLAKE2b-256 |
79a52b367817a71ea9421420660737ac23e338082107728ceae51e61f97db9c7
|
File details
Details for the file yuque_sdk-0.1.2-py3-none-any.whl.
File metadata
- Download URL: yuque_sdk-0.1.2-py3-none-any.whl
- Upload date:
- Size: 61.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b23388506dbb6e53470cca39f5666a87a5da14a835fe5172cce10a46dab9da79
|
|
| MD5 |
ef4e709146b7e81a36ca6b19cf2d08d5
|
|
| BLAKE2b-256 |
a381bbd76f980a8b3729f825654459d4c31b4db059516fb689e4d1f371ada5cc
|