The Substack MCP Plus project includes an extensive testing suite that was crucial during development, especially when troubleshooting authentication issues and ensuring all formatting features work correctly.
tests/
├── unit/ # Unit tests for individual components
│ ├── test_block_builder.py # Tests for BlockBuilder
│ ├── test_markdown_converter.py # Tests for MarkdownConverter
│ └── test_html_converter.py # Tests for HTMLConverter
├── integration/ # Integration tests
│ ├── test_create_post_flow.py # End-to-end post creation
│ ├── test_auth_flow.py # Authentication workflows
│ └── test_formatting_integration.py # Formatting tests
└── debug/ # Debug utilities (not in repo)
# Clone the repository
git clone [repository-url]
cd substack-mcp-plus
# Run the automated setup
./setup.shThe setup script will:
- ✅ Check Python version (3.8+ required)
- ✅ Create virtual environment
- ✅ Install all dependencies
- ✅ Create .env file from template
- ✅ Run basic tests to verify installation
-
setup.sh- Automated environment setup./setup.sh # Sets up everything in one command -
health_check.py- System health verificationpython health_check.py
Checks:
- ✅ Python version
- ✅ Dependencies installed
- ✅ Project structure
- ✅ Environment configuration
- ✅ Claude Desktop setup
- ✅ Network connectivity
-
debug_auth.py- Authentication testingpython debug_auth.py
Features:
- Tests both auth methods
- Lists your drafts
- Optionally creates a test post
- Color-coded output
-
generate_test_content.py- Create test filespython generate_test_content.py
Generates:
- Basic formatting examples
- Complex nested content
- Edge cases
- Real-world blog posts
- Newsletter templates
pytestpytest --cov=src --cov-report=html
# View coverage report at htmlcov/index.html# Unit tests only
pytest tests/unit/
# Integration tests only
pytest tests/integration/
# Specific test file
pytest tests/unit/test_markdown_converter.py
# Specific test function
pytest tests/unit/test_markdown_converter.py::test_parse_bold_textpytest -v # Show test names
pytest -vv # Show detailed assertions
pytest -s # Show print statementsTests all block creation methods:
- Paragraph blocks
- Heading blocks (H1-H6)
- List blocks (ordered/unordered)
- Code blocks
- Image blocks
- Blockquote blocks
- Horizontal rule blocks
- Paywall blocks
Tests markdown parsing and conversion:
- Inline formatting (bold, italic, code)
- Links and images
- Headers
- Lists (including nested)
- Code blocks with language detection
- Blockquotes
- Edge cases (empty content, malformed markdown)
Tests HTML to Substack block conversion:
- Basic HTML tags
- Nested structures
- Attributes handling
- Entity decoding
End-to-end testing of post creation:
- Draft creation with various content types
- Publishing workflow
- Scheduling posts
- Error handling
Authentication testing:
- Email/password authentication
- Session token authentication
- Cookie handling
- Error scenarios
Comprehensive formatting tests:
- Mixed content types
- Complex nested structures
- Special characters
- Large content handling
During development, we created several helpful testing utilities:
Standalone authentication tester:
python debug_auth.py- Tests authentication methods
- Validates credentials
- Shows detailed error messages
Generates test posts with various content:
python generate_test_content.py- Creates posts with all formatting options
- Tests edge cases
- Useful for manual testing
System health checker:
python health_check.py- Verifies Python version
- Checks dependencies
- Tests API connectivity
The test suite achieves high coverage:
- BlockBuilder: 100%
- MarkdownConverter: 95%+
- HTMLConverter: 90%+
- Authentication handlers: 85%+
- Post handlers: 90%+
@pytest.fixture
def auth_handler():
"""Provide authenticated handler for tests"""
return AuthHandler()
def test_create_post(auth_handler):
# Use the fixture
client = auth_handler.authenticate()@mock.patch('src.handlers.auth_handler.SubstackApi')
def test_auth_without_network(mock_api):
# Test without hitting real API
mock_api.return_value.login.return_value = True@pytest.mark.parametrize("markdown,expected", [
("**bold**", [{"type": "text", "content": "bold", "marks": [{"type": "strong"}]}]),
("*italic*", [{"type": "text", "content": "italic", "marks": [{"type": "em"}]}]),
])
def test_inline_formatting(markdown, expected):
result = converter.parse_inline_formatting(markdown)
assert result == expected-
Use pytest's built-in debugger:
pytest --pdb # Drop into debugger on failure -
Capture print output:
pytest -s # Show all print statements -
Run specific test with extra verbosity:
pytest -vv tests/unit/test_markdown_converter.py::test_parse_bold_text
-
Generate detailed failure reports:
pytest --tb=long # Long traceback format pytest --tb=short # Short traceback format
- Test file naming: All test files start with
test_ - Test function naming: Descriptive names like
test_parse_bold_text_with_spaces - Arrange-Act-Assert: Clear test structure
- One assertion per test: When possible
- Mock external dependencies: Don't hit real APIs in unit tests
- Use fixtures: For common setup code
- Test edge cases: Empty inputs, special characters, large data
def test_captcha_error_handling():
"""Test handling of CAPTCHA challenges"""
# This was crucial during developmentdef test_empty_post_prevention():
"""Ensure posts never appear empty"""
# Helped identify the formatting issuesdef test_nested_formatting():
"""Test bold inside links, etc."""
# Found several parser bugsFor development, use pytest-watch:
pip install pytest-watch
ptw # Runs tests automatically on file changesWhen adding features:
- Write tests first (TDD approach)
- Test both success and failure cases
- Include edge cases
- Update this documentation
-
Install Claude Desktop
- Download from claude.ai/desktop
- Install and log in with your Claude account
-
Configure MCP Server
Find your Claude Desktop config file:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json - Linux:
~/.config/Claude/claude_desktop_config.json
Add our server configuration:
{ "mcpServers": { "substack": { "command": "python", "args": ["-m", "src.server"], "cwd": "/path/to/substack-mcp-plus", "env": { "SUBSTACK_PUBLICATION_URL": "https://yourpublication.substack.com", "SUBSTACK_EMAIL": "your-email@example.com", "SUBSTACK_PASSWORD": "your-password" } } } } - macOS:
-
Restart Claude Desktop (required after config changes)
# Kill any existing MCP processes
pkill -f substack-mcp-plus
# Clear any test drafts from your Substack account (optional)
# Log into Substack web UI and delete test drafts
# Verify authentication is set up
python debug_auth.pyTest the confirmation system and rich formatting
Test 1A - Basic Creation with Confirmation:
1. Say: "Create a new Substack draft with the title 'Test Post 1' and content 'This is a test'"
2. EXPECT: Confirmation warning appears
3. Say: "No"
4. VERIFY: Draft is NOT created
5. Repeat step 1, then say "Yes"
6. VERIFY: Draft is created successfully
Test 1B - Rich Formatting:
Create a draft with complex formatting including headers, bold/italic, lists, code blocks, blockquotes, and paywall markers.
No confirmation needed - read-only operation
- Test listing recent drafts
- Test listing with limit
Test partial updates and confirmation
- Update subtitle only
- Update title only
- Update multiple fields
- Verify confirmation shows only fields being changed
Read-only operation - no confirmation needed
- Read draft content with full formatting
Test duplication with confirmation
- Duplicate with default "Copy of" title
- Duplicate with custom title
Read-only - generates preview link
- Generate preview URL and verify it works
Test scheduling with confirmation
- Schedule for future date/time
- Verify confirmation shows formatted date and warning
DANGER: This sends emails! Use test account if possible
- Test confirmation shows subscriber count and "CANNOT be undone" warning
- Only actually publish if using test account
No confirmation needed
- Upload local image file
- Verify CDN URL is returned and works
Already has built-in confirmation
- Test deletion with confirmation parameter
Read-only - lists drafts with deletion instructions
Read-only operation
Read-only operation
Read-only operation
- All 14 tools appear in Claude Desktop
- Confirmation prompts work for all protected tools
- Saying "no" cancels operations
- Saying "yes" proceeds with operations
- Partial updates only change specified fields
- Rich text formatting is preserved
- Paywall markers work correctly
- Error messages are helpful
- Preview links are functional
- Image uploads return valid URLs
- No tools execute without appropriate confirmation
First, run the health check:
python health_check.pyThis will identify most common issues automatically!
# Solutions:
pip install -e .
# Ensure virtual environment is activated
source venv/bin/activate
# Force reinstall if needed
python -m pip install --force-reinstall -e .# Quick diagnosis
python debug_auth.py
# Solutions:
- Check .env has no extra spaces or quotes
- Try session token method (more reliable)
- Verify publication URL is correct
- Ensure account has publication access- Check config location is correct
- Verify Python path in config ("python" or "python3")
- Check absolute path to project in "cwd"
- Restart Claude Desktop (required after config changes)
- Get fresh tokens from browser (F12 → Application → Cookies)
- Copy new
substack.sidandsubstack.uid - Update
.envwith new values - Test with:
python debug_auth.py
macOS
- If
pythoncommand not found, usepython3 - May need to allow terminal access in Security settings
Windows
- Use
pythonnotpython3 - Run as Administrator if permission errors
- Use PowerShell, not Command Prompt
Linux
- May need
python3-venvpackage:sudo apt install python3-venv - Check firewall isn't blocking connections
After running generate_test_content.py:
| File | Description | Tests |
|---|---|---|
basic_formatting.md |
All basic markdown elements | Headers, lists, links, images |
nested_formatting.md |
Complex nested structures | Bold/italic in lists, quotes |
paywall_post.md |
Premium content example | Paywall marker handling |
complex_html.html |
Nested HTML elements | HTML to blocks conversion |
format_chaos.md |
Edge cases and special chars | Parser robustness |
technical_post.md |
Real blog post example | Code blocks, tables |
newsletter.md |
Newsletter format | Mixed content types |
The comprehensive test suite was instrumental in achieving a reliable, production-ready MCP server. It caught numerous edge cases and helped us iterate quickly during the development process.