Convert markdown to Google Docs with formatting support
Project description
md-to-gdocs
Convert markdown files to Google Docs with formatting support. Both CLI tool and Python library.
Features
- ✅ CLI and Library: Use as command-line tool or Python library
- ✅ Rich Formatting: Headers, bold, italic, links, strikethrough, inline code
- ✅ Lists: Bullet lists, numbered lists
- ✅ Advanced Features: Tables, code blocks, blockquotes, horizontal rules
- ✅ Flexible Operations: Create, append, insert at specific positions
- ✅ Dry Run Mode: Preview changes before applying
- ✅ JSON Output: Automation-friendly output format
Installation
Local Development
cd /path/to/markdown-to-google-doc
uv sync
From PyPI
pip install md-to-gdocs
# or with uv
uv pip install md-to-gdocs
Prerequisites
Google Authentication
You need a google_token.json file with Google OAuth2 credentials.
Token File Locations (checked in order):
./google_token.json(current directory - highest priority)~/.claude/google_token.json(Claude directory)- Custom path via
--tokenflag
How to Generate Token:
Option 1: Using Claude Code
/google-auth # Creates ~/.claude/google_token.json
Option 2: Manual Setup (for standalone use)
- Go to Google Cloud Console
- Create a project or select existing one
- Enable Google Docs API
- Create OAuth 2.0 credentials (Desktop app)
- Download credentials and save as
google_token.json - Place in current directory or
~/.claude/
Required Scope: https://www.googleapis.com/auth/documents
CLI Usage
Create New Document
# Basic usage (title derived from filename)
md-to-gdocs create README.md
# With custom title
md-to-gdocs create README.md --title "My Documentation"
# With custom token path
md-to-gdocs create README.md --token /path/to/google_token.json
# Dry run (preview without creating)
md-to-gdocs create README.md --dry-run
# JSON output (for scripting)
md-to-gdocs create README.md --output-json
Append to Existing Document
# Append markdown to end of document
md-to-gdocs append 1abc234def567 changelog.md
# With dry run
md-to-gdocs append 1abc234def567 changelog.md --dry-run
Insert at Specific Index
# Insert at character index 1234
md-to-gdocs update-index 1abc234def567 section.md 1234
# Use --dry-run to preview
md-to-gdocs update-index 1abc234def567 section.md 1234 --dry-run
Find Text (Get Character Indices)
# Find text and get indices (JSON output by default)
md-to-gdocs find 1abc234def567 "Section Header"
# Human-readable output
md-to-gdocs find 1abc234def567 "Section Header" --no-output-json
Python Library Usage
Create Document
from md_to_gdocs import create_document
markdown_content = """
# Main Header
This is **bold** and *italic* text.
- List item 1
- List item 2
"""
result = create_document(
markdown_content=markdown_content,
title="My Documentation"
)
if result.success:
print(f"Created: {result.doc_url}")
print(f"Doc ID: {result.doc_id}")
else:
print(f"Error: {result.error}")
Append to Document
from md_to_gdocs import append_to_document
result = append_to_document(
doc_id="1abc234def567",
markdown_content="## New Section\n\nNew content here."
)
if result.success:
print("Content appended successfully")
Insert at Index
from md_to_gdocs import update_at_index
result = update_at_index(
doc_id="1abc234def567",
markdown_content="## Inserted Section",
start_index=1234
)
Find Text
from md_to_gdocs import find_text
locations = find_text(
doc_id="1abc234def567",
search_text="Section Header"
)
for loc in locations:
print(f"Found at: {loc.index} - {loc.end_index}")
print(f"Context: {loc.context}")
Dry Run Mode
result = create_document(
markdown_content="# Test",
title="Test Doc",
dry_run=True
)
if result.dry_run:
print(f"Would execute {len(result.requests)} API requests")
print("Requests:", result.requests)
Supported Markdown Syntax
Headers
# H1 Header
## H2 Header
### H3 Header
#### H4 Header
##### H5 Header
###### H6 Header
Inline Formatting
**bold text**
*italic text*
***bold and italic***
~~strikethrough text~~
`inline code`
[link text](https://example.com)
Lists
# Bullet lists
- Bullet item 1
- Bullet item 2
* Also works with asterisks
* Another item
# Numbered lists
1. First item
2. Second item
3. Third item
Code Blocks
```python
def hello():
print("Hello, world!")
```
```javascript
const greeting = "Hello, world!";
console.log(greeting);
```
Tables
| Header 1 | Header 2 | Header 3 |
|----------|----------|----------|
| Cell 1 | Cell 2 | Cell 3 |
| Cell 4 | Cell 5 | Cell 6 |
Blockquotes
> This is a blockquote
> It can span multiple lines
Horizontal Rules
---
***
___
Paragraphs
Regular text paragraphs are preserved. Blank lines create spacing.
Finding Document IDs
Google Doc IDs are in the URL:
https://docs.google.com/document/d/1abc234def567/edit
^^^^^^^^^^^^^^
This is the doc_id
Workflow: Replace Section
To replace content between markers:
-
Add HTML comments as markers in your Google Doc:
<!-- START_SECTION --> Old content here <!-- END_SECTION -->
-
Find the marker positions:
md-to-gdocs find 1abc234def567 "<!-- START_SECTION -->" # Returns: index: 100, end_index: 125 md-to-gdocs find 1abc234def567 "<!-- END_SECTION -->" # Returns: index: 500, end_index: 523
-
Use the indices to insert new content (Phase 2 feature)
Error Handling
Authentication Errors
Error: Token file not found. Searched:
- ./google_token.json
- ~/.claude/google_token.json
Solution:
- Place
google_token.jsonin current directory or~/.claude/ - Or use
--tokenflag to specify custom path - Generate token using
/google-auth(Claude Code) or Google Cloud Console
Document Not Found
Error: Document 1abc234def567 not found
Solution: Check the document ID. Ensure you have access to the document.
Invalid Token
Error: Invalid credentials. Please run /google-auth skill to re-authenticate.
Solution: Token may be expired. Re-run /google-auth.
Development
Setup
# Clone and install dependencies
cd /Users/kprice/repos/misc/kj/markdown-to-google-doc
uv sync
# Install dev dependencies
uv sync --dev
Run Tests
# Run all tests with coverage
uv run pytest
# Run specific test file
uv run pytest tests/test_parsers.py
# Run with verbose output
uv run pytest -v
Code Quality
# Format code
uv run black src/ tests/
# Type checking
uv run mypy src/
Version Management
When making ANY code changes, ALWAYS update versions in BOTH locations:
pyproject.toml(line 3):version = "X.Y.Z"src/md_to_gdocs/__init__.py(line 13):__version__ = "X.Y.Z"
Semantic Versioning Guidelines:
- MAJOR (X.0.0): Breaking changes
- MINOR (0.X.0): New features (backward compatible)
- PATCH (0.0.X): Bug fixes (backward compatible)
After version bump:
uv sync # Updates uv.lock
Project Structure
markdown-to-google-doc/
├── src/md_to_gdocs/
│ ├── __init__.py # Public API exports
│ ├── cli.py # Typer CLI commands
│ ├── core.py # Core library functions
│ ├── auth.py # Google OAuth handling
│ ├── api.py # Google Docs API operations
│ ├── models.py # Data classes
│ ├── exceptions.py # Custom exceptions
│ └── parsers/
│ ├── __init__.py
│ └── custom.py # Markdown parser
└── tests/
├── conftest.py # Pytest fixtures
├── test_parsers.py # Parser tests
├── test_api.py # API tests
├── test_core.py # Core tests
└── test_cli.py # CLI tests
Roadmap
Phase 1 (Completed ✅)
- ✅ Markdown parsing (headers, bold, italic, lists, links)
- ✅ Advanced features (tables, code blocks, blockquotes, horizontal rules, strikethrough)
- ✅ CLI commands (create, append, update-index, find)
- ✅ Python library API
- ✅ Dry run mode
- ✅ Comprehensive tests
Phase 2 (Next)
- ⏳ Section management (replace, delete between markers)
- ⏳ Index-based section operations
- ⏳ Enhanced text search
Phase 3 (Future)
- ⏳ CI/CD pipeline
- ⏳ PyPI publication
- ⏳ Alternative parser options (mistune)
Claude Code Integration
Want to use this as a Claude Code skill for automated documentation workflows?
See SKILL_CREATION.md for complete instructions on:
- Converting this library into a reusable Claude skill
- Usage patterns and workflow examples
- Integration with
/google-authand other skills - Automated documentation publishing workflows
Contributing
This is currently a personal project. Contributions welcome after PyPI publication.
License
MIT License - see LICENSE file for details
Author
KJ Price
Acknowledgments
- Parser design inspired by ai-book project
- Google Docs API patterns from gdocs_editor.py
- Built with: Typer, Rich, Google API Python Client
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 md_to_gdocs-0.2.1.tar.gz.
File metadata
- Download URL: md_to_gdocs-0.2.1.tar.gz
- Upload date:
- Size: 32.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.5.28
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
65175393aa5af08b6822297461074530b7f3fdbd0b0865d4a4f6dd7a85a9343b
|
|
| MD5 |
a6ed05303651d5b304c021c1554531af
|
|
| BLAKE2b-256 |
92b201dd4401a1944990e27d893b82e891e70927457cee6e45cc1004fe358c19
|
File details
Details for the file md_to_gdocs-0.2.1-py3-none-any.whl.
File metadata
- Download URL: md_to_gdocs-0.2.1-py3-none-any.whl
- Upload date:
- Size: 19.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.5.28
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d9e8045650687c24e933db7a9cc897c0e25229302bdcfe5f659b23f40e20986c
|
|
| MD5 |
14396f9b6f5685fca3bca6c013378937
|
|
| BLAKE2b-256 |
ab090ec7679c1a7534626cf9335e4474ffc69b2ba3a555166449be1f911ec01c
|