I recently built a Bluesky MCP server using Python. Here are the key lessons that I learned.
Use Pytest to test the MCP tool:
@pytest.mark.asyncio
async def test_create_and_delete_post():
"""Test creating a post and then deleting it."""
# Create client session
async with client_session(mcp._mcp_server) as client:
# Create a post with a unique identifier to avoid duplicate posts
unique_id = str(uuid.uuid4())[:8]
test_text = f"Test post from Bluesky MCP test suite - {unique_id}"
create_params = {"text": test_text}
# Call the send_post tool
result = await client.call_tool("send_post", create_params)
⭐ During development with Claude Code Opus v4 I noticed that it was putting debug prints into the code to help itself troubleshoot! It was then removing the debug prints once it figured out the problem. I didn’t notice the behavior with earlier versions of Claude, I love this improvement!
I like to keep my CLAUDE.md file brief. I also use Context7 MCP to load docs about packages, and by adding a Required Context section to this file Claude knows to load the context from Context7 before starting to work. Here’s is what I used for my CLAUDE.md file:
# CLAUDE.md
## Primary Directives
1. Design from broad structures to specific details.
2. Keep design simple but not simpler.
3. Check with user often.
## Secondary Directives
- Test often.
- Follow Git Conventions and Style Guide.
## Required Context
- context7 gwbischof/python-mcp
- context7 marshalx/atproto
- @<https://github.com/MarshalX/atproto/blob/main/packages/atproto_client/client/client.py>
## Commands
- Lint: `uv run ruff check .`- Tests: `uv run pytest`- Format: `uv run black .`## Style Guide- Format: Black
- Imports: isort (stdlib → third-party → first-party)
- Naming: snake_case, PascalCase, UPPER_SNAKE_CASE
- Errors: Specific exceptions with context
Modern syntax: CLAUDE.md supports importing files with @path/to/file
and loads from ~/.claude/CLAUDE.md
globally.
This video was really helpful for me to understand CLAUDE.md better:
Taking Claude to the Next Level
Traditional Python package distribution creates some friction for installing MCP servers. UVX eliminates the setup hassle because it allows installation from a GitHub repo:
<aside> <img src="/icons/skull_gray.svg" alt="/icons/skull_gray.svg" width="40px" />
Pin MCP versions in your config file. Without version pinning, MCPs auto-update on restart, creating a security risk where maintainers could inject malicious code that executes when you restart your Agent.
</aside>
{
"mcpServers": {
"bluesky-social": {
"command": "uvx",
"args": ["--from", "git+https://github.com/gwbischof/[email protected]", "bluesky-social-mcp"],
"env": {
"BLUESKY_IDENTIFIER": "your-handle.bsky.social",
"BLUESKY_APP_PASSWORD": "your-app-password"
}
}
}
}
UVX allows users to install your MCP with only 2 steps: