Skip to main content

Model Context Protocol server for local HWPX document automation.

Project description

๐Ÿ“„ hwpx-mcp-server

ํ•œ๊ธ€(HWPX) ๋ฌธ์„œ๋ฅผ AI๋กœ ์ž๋™ํ™”ํ•˜๋Š” MCP ์„œ๋ฒ„

ํ•œ๊ธ€ ์›Œ๋“œํ”„๋กœ์„ธ์„œ ์—†์ด ยท ์ˆœ์ˆ˜ ํŒŒ์ด์ฌ ยท ํฌ๋กœ์Šค ํ”Œ๋žซํผ

PyPI Python License Tests


hwpx-mcp-server๋Š” Model Context Protocol(MCP) ํ‘œ์ค€์„ ๋”ฐ๋ฅด๋Š” ์„œ๋ฒ„๋กœ, python-hwpx ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ธฐ๋ฐ˜์œผ๋กœ HWPX ๋ฌธ์„œ์˜ ์—ด๋žŒ ยท ๊ฒ€์ƒ‰ ยท ํŽธ์ง‘ ยท ์ €์žฅ์„ AI ํด๋ผ์ด์–ธํŠธ์—์„œ ์ง์ ‘ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

Note โ€” ์ด ์„œ๋ฒ„๋Š” Open XML ๊ธฐ๋ฐ˜ .hwpx ํฌ๋งท์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ๋ ˆ๊ฑฐ์‹œ ๋ฐ”์ด๋„ˆ๋ฆฌ .hwp ํฌ๋งท์€ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.


Why?

ํ•œ๊ตญ์˜ ๊ณต๊ณต๊ธฐ๊ด€ยทํ•™๊ตยท๊ธฐ์—…์—์„œ ๋งค์ผ ์ˆ˜๋งŒ ๊ฑด์˜ ํ•œ๊ธ€ ๋ฌธ์„œ๊ฐ€ ์˜ค๊ฐ‘๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์ด ๋ฌธ์„œ๋ฅผ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์œผ๋กœ ๋‹ค๋ฃจ๋ ค๋ฉด? ํ•œ๊ธ€ ์›Œ๋“œํ”„๋กœ์„ธ์„œ๊ฐ€ ์„ค์น˜๋œ Windows์—์„œ๋งŒ, COM ์ž๋™ํ™”๋กœ๋งŒ ๊ฐ€๋Šฅํ–ˆ์Šต๋‹ˆ๋‹ค.

hwpx-mcp-server๋Š” ์ด ์ œ์•ฝ์„ ์—†์•ฑ๋‹ˆ๋‹ค.

  • โœ… OS ๋ฌด๊ด€ โ€” Windows, macOS, Linux ์–ด๋””์„œ๋“  ๋™์ž‘
  • โœ… ํ•œ๊ธ€ ์›Œ๋“œํ”„๋กœ์„ธ์„œ ๋ถˆํ•„์š” โ€” ์ˆœ์ˆ˜ ํŒŒ์ด์ฌ์œผ๋กœ HWPX๋ฅผ ์ง์ ‘ ํŒŒ์‹ฑ
  • โœ… AI ๋„ค์ดํ‹ฐ๋ธŒ โ€” Claude Desktop, Gemini CLI ๋“ฑ MCP ํด๋ผ์ด์–ธํŠธ์™€ ๋ฐ”๋กœ ์—ฐ๊ฒฐ

Quick Start

1. ์„ค์น˜ & ์‹คํ–‰

uv๋งŒ ์žˆ์œผ๋ฉด ํ•œ ์ค„๋กœ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.

uvx hwpx-mcp-server

python-hwpx >= 1.9 ์ด์ƒ์ด ํ•„์š”ํ•˜๋ฉฐ, ์ฒซ ์‹คํ–‰ ์‹œ ์ž๋™ ์„ค์น˜๋ฉ๋‹ˆ๋‹ค.

2. MCP ํด๋ผ์ด์–ธํŠธ ์„ค์ •

Claude Desktop

claude_desktop_config.json์— ์ถ”๊ฐ€:

{
  "mcpServers": {
    "hwpx": {
      "command": "uvx",
      "args": ["hwpx-mcp-server"]
    }
  }
}
Gemini CLI

~/.gemini/settings.json์— ์ถ”๊ฐ€:

{
  "mcpServers": {
    "hwpx": {
      "command": "uvx",
      "args": ["hwpx-mcp-server"]
    }
  }
}
VS Code (Copilot Chat)

.vscode/mcp.json์— ์ถ”๊ฐ€:

{
  "servers": {
    "hwpx": {
      "command": "uvx",
      "args": ["hwpx-mcp-server"]
    }
  }
}
Cursor / Windsurf

๊ฐ ์—๋””ํ„ฐ์˜ MCP ์„ค์ • ํŒŒ์ผ์— ๋™์ผํ•œ JSON ๋ธ”๋ก์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”:

{
  "mcpServers": {
    "hwpx": {
      "command": "uvx",
      "args": ["hwpx-mcp-server"]
    }
  }
}

Features

๐Ÿ“– ์ฝ๊ธฐ & ํƒ์ƒ‰

๋„๊ตฌ ์„ค๋ช…
open_info ๋ฌธ์„œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ยท ๋ฌธ๋‹จ ์ˆ˜ ยท ํ—ค๋” ๊ฐœ์ˆ˜ ์š”์•ฝ
read_text ํŽ˜์ด์ง€๋„ค์ด์…˜ ๊ธฐ๋ฐ˜ ํ…์ŠคํŠธ ์ถ”์ถœ (๊ธฐ๋ณธ 200๋ฌธ๋‹จ ๋‹จ์œ„)
read_paragraphs ํŠน์ • ๋ฌธ๋‹จ๋งŒ ์„ ํƒ์ ์œผ๋กœ ์ถ”์ถœ
text_extract_report ์ฃผ์„ ํฌํ•จ ์ „์ฒด ํ…์ŠคํŠธ ๋ฆฌํฌํŠธ
find ํ‚ค์›Œ๋“œ ๊ฒ€์ƒ‰ + ์ „ํ›„ ์ปจํ…์ŠคํŠธ ์Šค๋‹ˆํŽซ ๋ฐ˜ํ™˜
find_runs_by_style ์Šคํƒ€์ผ ๊ธฐ๋ฐ˜ ๋Ÿฐ(run) ๊ฒ€์ƒ‰
list_sections / list_headers ์„น์…˜ ยท ํ—ค๋” ๊ตฌ์กฐ ํƒ์ƒ‰

โœ๏ธ ํŽธ์ง‘

๋„๊ตฌ ์„ค๋ช…
replace_text_in_runs ์Šคํƒ€์ผ ๋ณด์กด ํ…์ŠคํŠธ ์น˜ํ™˜ (dryRun ์ง€์›)
add_paragraph / insert_paragraphs_bulk ๋ฌธ๋‹จ ์ถ”๊ฐ€ (๋‹จ์ผ ยท ๋ฒŒํฌ)
add_table / set_table_cell_text / replace_table_region ํ‘œ ์ƒ์„ฑ ยท ์…€ ํŽธ์ง‘ ยท ์˜์—ญ ์น˜ํ™˜
get_table_cell_map / split_table_cell ๋ณ‘ํ•ฉ ์…€ ๋งคํ•‘ ยท ๋ถ„ํ• 
add_memo / add_memo_with_anchor / remove_memo ๋ฉ”๋ชจ ๊ด€๋ฆฌ
add_shape / add_control ๊ฐœ์ฒด ยท ์ปจํŠธ๋กค ์ถ”๊ฐ€

๐ŸŽจ ์Šคํƒ€์ผ๋ง

๋„๊ตฌ ์„ค๋ช…
ensure_run_style ์Šคํƒ€์ผ ์กด์žฌ ํ™•์ธ ยท ์ƒ์„ฑ
apply_style_to_text_ranges ๋‹จ์–ด ๋‹จ์œ„ ์Šคํƒ€์ผ ์ ์šฉ
apply_style_to_paragraphs ๋ฌธ๋‹จ ๋‹จ์œ„ ์Šคํƒ€์ผ ์ ์šฉ
list_styles_and_bullets ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์Šคํƒ€์ผ ยท ๊ธ€๋จธ๋ฆฌํ‘œ ๋ชฉ๋ก

๐Ÿ’พ ํŒŒ์ผ ๊ด€๋ฆฌ

๋„๊ตฌ ์„ค๋ช…
save / save_as ์ €์žฅ (์ž๋™ .bak ๋ฐฑ์—… ์˜ต์…˜)
make_blank ์ƒˆ ๋นˆ ๋ฌธ์„œ ์ƒ์„ฑ
validate_structure / lint_text_conventions ๊ตฌ์กฐ ๊ฒ€์ฆ ยท ํ…์ŠคํŠธ ๋ฆฐํŠธ

๐Ÿ”ฌ OPC ํŒจํ‚ค์ง€ ๋‚ด๋ถ€ (์ฝ๊ธฐ ์ „์šฉ)

๋„๊ตฌ ์„ค๋ช…
package_parts ํŒจํ‚ค์ง€ ๋‚ด ๋ชจ๋“  ํŒŒํŠธ ๊ฒฝ๋กœ ๋ชฉ๋ก
package_get_text / package_get_xml ํŒŒํŠธ๋ฅผ ํ…์ŠคํŠธ ๋˜๋Š” XML๋กœ ์กฐํšŒ
object_find_by_tag / object_find_by_attr XML ์š”์†Œ ๊ฒ€์ƒ‰

Configuration

ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ์„œ๋ฒ„ ๋™์ž‘์„ ์กฐ์ •ํ•ฉ๋‹ˆ๋‹ค.

๋ณ€์ˆ˜ ์„ค๋ช… ๊ธฐ๋ณธ๊ฐ’
HWPX_MCP_PAGING_PARA_LIMIT read_text๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ตœ๋Œ€ ๋ฌธ๋‹จ ์ˆ˜ 200
HWPX_MCP_AUTOBACKUP 1์ด๋ฉด ์ €์žฅ ์ „ .bak ๋ฐฑ์—… ์ƒ์„ฑ 0
LOG_LEVEL stderr JSONL ๋กœ๊ทธ ๋ ˆ๋ฒจ INFO
HWPX_MCP_HARDENING 1์ด๋ฉด 3๋‹จ๊ณ„ ํŽธ์ง‘ ํŒŒ์ดํ”„๋ผ์ธ + ๊ฒ€์ƒ‰/์ปจํ…์ŠคํŠธ ๋„๊ตฌ ํ™œ์„ฑํ™” 0

ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ํฌํ•จํ•œ ์ „์ฒด ์„ค์ • ์˜ˆ์‹œ:

{
  "mcpServers": {
    "hwpx": {
      "command": "uvx",
      "args": ["hwpx-mcp-server"],
      "env": {
        "HWPX_MCP_PAGING_PARA_LIMIT": "200",
        "HWPX_MCP_AUTOBACKUP": "1",
        "LOG_LEVEL": "INFO"
      }
    }
  }
}

Advanced

๐Ÿ“ ๋ฌธ์„œ ๋กœ์ผ€์ดํ„ฐ (Document Locator)

๋ชจ๋“  ๋„๊ตฌ๋Š” ๋ฌธ์„œ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” discriminated union ๋กœ์ผ€์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

๋กœ์ปฌ ํŒŒ์ผ (๊ธฐ๋ณธ๊ฐ’ โ€” ๊ธฐ์กด ์Šคํ‚ค๋งˆ์™€ ๋™์ผ):

{ "path": "sample.hwpx" }

HTTP ๋ฐฑ์—”๋“œ ์—ฐ๊ณ„:

{ "type": "uri", "uri": "reports/weekly.hwpx", "backend": "http" }

์‚ฌ์ „ ๋“ฑ๋ก๋œ ํ•ธ๋“ค (ํ•˜๋“œ๋‹ ๋ชจ๋“œ์—์„œ ํ›„์† ํŽธ์ง‘ ์‹œ):

{ "type": "handle", "handleId": "doc-1234" }
๐Ÿ” ํ•˜๋“œ๋‹ ํŽธ์ง‘ ํŒŒ์ดํ”„๋ผ์ธ

HWPX_MCP_HARDENING=1๋กœ ํ™œ์„ฑํ™”ํ•˜๋ฉด ๋ชจ๋“  ํŽธ์ง‘์ด Plan โ†’ Preview โ†’ Apply 3๋‹จ๊ณ„๋ฅผ ๊ฑฐ์นฉ๋‹ˆ๋‹ค.

๋‹จ๊ณ„ ๋„๊ตฌ ์„ค๋ช…
1. ๊ณ„ํš hwpx.plan_edit ๋ณ€๊ฒฝ ๋Œ€์ƒ๊ณผ ์˜๋„๋ฅผ ์„ค๋ช…ํ•˜๋ฉด planId์™€ ์ž‘์—… ์š”์•ฝ์„ ๋ฐ˜ํ™˜
2. ๊ฒ€ํ†  hwpx.preview_edit diff, ๋ชจํ˜ธ์„ฑ ๊ฒฝ๊ณ , ์•ˆ์ „ ์ ์ˆ˜๋ฅผ ํฌํ•จํ•œ ๋ฏธ๋ฆฌ๋ณด๊ธฐ
3. ์ ์šฉ hwpx.apply_edit confirm: true ๋ช…์‹œ ์‹œ ์‹ค์ œ ๋ฌธ์„œ ๋ณ€๊ฒฝ ์ˆ˜ํ–‰

์ถ”๊ฐ€ ์ง€์› ๋„๊ตฌ:

  • hwpx.search โ€” ์ •๊ทœ์‹/ํ‚ค์›Œ๋“œ ๊ธฐ๋ฐ˜ ๋ฌธ์„œ ๊ฒ€์ƒ‰
  • hwpx.get_context โ€” ํŠน์ • ๋ฌธ๋‹จ ์ฃผ๋ณ€ ์ œํ•œ ์ฐฝ(window) ์ถ”์ถœ

์—๋Ÿฌ ์ฝ”๋“œ: PREVIEW_REQUIRED ยท AMBIGUOUS_TARGET ยท UNSAFE_WILDCARD ยท IDEMPOTENT_REPLAY

๐Ÿ“ ํ‘œ ํŽธ์ง‘ ๊ณ ๊ธ‰ ์˜ต์…˜

get_table_cell_map์œผ๋กœ ์ „์ฒด ๊ฒฉ์ž๋ฅผ ์ง๋ ฌํ™”ํ•˜์—ฌ ๊ฐ ์œ„์น˜์˜ ์•ต์ปค ์…€, ๋ณ‘ํ•ฉ ๋ฒ”์œ„๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

{
  "name": "set_table_cell_text",
  "arguments": {
    "path": "sample.hwpx",
    "tableIndex": 0,
    "row": 1, "col": 1,
    "text": "๋…ผ๋ฆฌ ์ขŒํ‘œ ํŽธ์ง‘",
    "logical": true,
    "splitMerged": true,
    "autoFit": true
  }
}
  • logical: true โ€” ๋…ผ๋ฆฌ ์ขŒํ‘œ๊ณ„ ์‚ฌ์šฉ
  • splitMerged: true โ€” ์“ฐ๊ธฐ ์ „ ๋ณ‘ํ•ฉ ์˜์—ญ ์ž๋™ ๋ถ„ํ• 
  • autoFit: true โ€” ์…€ ๋‚ด์šฉ์— ๋งž์ถฐ ์—ด ๋„ˆ๋น„ ์žฌ๊ณ„์‚ฐ
๐Ÿ” ๊ฒ€์ƒ‰ ์ปจํ…์ŠคํŠธ ๊ธธ์ด ์กฐ์ ˆ

find ๋„๊ตฌ๋Š” ๊ธฐ๋ณธ ์ „ํ›„ 80์ž์˜ ์ปจํ…์ŠคํŠธ ์Šค๋‹ˆํŽซ์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. contextRadius ์ธ์ˆ˜๋กœ ์กฐ์ • ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค:

{
  "name": "find",
  "arguments": {
    "path": "sample.hwpx",
    "query": "HWPX",
    "contextRadius": 200
  }
}

Testing

# ์˜์กด์„ฑ ์„ค์น˜
python -m pip install -e ".[test]"

# ์ „์ฒด ํ…Œ์ŠคํŠธ ์‹คํ–‰
python -m pytest

์—”๋“œํˆฌ์—”๋“œ ํ…Œ์ŠคํŠธ๊ฐ€ ์„œ๋ฒ„์˜ ๋Œ€๋ถ€๋ถ„์˜ ๋„๊ตฌ๋ฅผ ์‹ค์ œ ํ˜ธ์ถœํ•˜์—ฌ ํ…์ŠคํŠธ ยท ํ‘œ ยท ๋ฉ”๋ชจ ํŽธ์ง‘, OPC ํŒจํ‚ค์ง€ ์ฝ๊ธฐ, ์ž๋™ ๋ฐฑ์—… ๋“ฑ์„ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค.


Architecture

hwpx-mcp-server
โ”œโ”€โ”€ src/
โ”‚   โ””โ”€โ”€ hwpx_mcp_server/
โ”‚       โ”œโ”€โ”€ server.py          # MCP ์„œ๋ฒ„ ์ง„์ž…์ 
โ”‚       โ”œโ”€โ”€ tools/             # ๋„๊ตฌ ํ•ธ๋“ค๋Ÿฌ
โ”‚       โ””โ”€โ”€ schema/            # JSON ์Šคํ‚ค๋งˆ ๋นŒ๋” (draft-07, $ref ์ œ๊ฑฐ)
โ”œโ”€โ”€ tests/
โ”‚   โ””โ”€โ”€ test_mcp_end_to_end.py # E2E ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ
โ”œโ”€โ”€ docs/                      # ๋ฌธ์„œ
โ””โ”€โ”€ pyproject.toml
  • ์ˆœ์ˆ˜ ํŒŒ์ด์ฌ: python-hwpx >= 1.9 ยท mcp ยท anyio ยท pydantic
  • ๊ฒฝ๋กœ ํ•ด์„: ์„œ๋ฒ„ ์‹คํ–‰ ๋””๋ ‰ํ„ฐ๋ฆฌ ๊ธฐ์ค€, ๋ณ„๋„ ์„ค์ • ๋ถˆํ•„์š”
  • ์•ˆ์ „ ์žฅ์น˜: ํŒŒ๊ดด์  ์ž‘์—…์— dryRun ํ”Œ๋ž˜๊ทธ ์šฐ์„  ์ œ๊ณต ยท ์ž๋™ .bak ๋ฐฑ์—…
  • ์Šคํ‚ค๋งˆ: draft-07 ํ˜ธํ™˜ Sanitizer๋ฅผ ๊ฑฐ์ณ $ref / anyOf ์—†์ด ํ‰ํƒ„ํ™”

Comparison

hwpx-mcp-server hwp-mcp
ํฌ๋งท .hwpx (Open XML) .hwp (๋ฐ”์ด๋„ˆ๋ฆฌ)
OS Windows ยท macOS ยท Linux Windows only
ํ•œ๊ธ€ ํ•„์š” โŒ ๋ถˆํ•„์š” โœ… ํ•„์ˆ˜ (COM ์ž๋™ํ™”)
๋ฐฉ์‹ ์ˆœ์ˆ˜ ํŒŒ์ด์ฌ ํŒŒ์‹ฑ ํ•œ๊ธ€ ํ”„๋กœ์„ธ์Šค ์ œ์–ด
ํŽธ์ง‘ ๋„๊ตฌ ์ˆ˜ 30+ ~10

Roadmap

  • Streamable HTTP transport ์ง€์›
  • MCP Resources ๋…ธ์ถœ (๋ฌธ์„œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ URI)
  • MCP Prompts ํ…œํ”Œ๋ฆฟ (๋ฌธ์„œ ์š”์•ฝ, ํ‘œ ์ถ”์ถœ ๋“ฑ)
  • Docker ์ด๋ฏธ์ง€ ๋ฐฐํฌ
  • GitHub Actions CI/CD ํŒŒ์ดํ”„๋ผ์ธ
  • MCP Registry ๋“ฑ๋ก

Contributing

๊ธฐ์—ฌ๋ฅผ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค! ๋ฒ„๊ทธ ๋ฆฌํฌํŠธ, ๊ธฐ๋Šฅ ์š”์ฒญ, PR ๋ชจ๋‘ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

  1. Fork โ†’ Branch (feat/amazing-feature) โ†’ Commit โ†’ PR
  2. ํ…Œ์ŠคํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ๊ธฐ์กด ํ…Œ์ŠคํŠธ๊ฐ€ ํ†ต๊ณผํ•˜๋Š”์ง€ ํ™•์ธํ•ด์ฃผ์„ธ์š”
  3. ํ•œ๊ธ€/์˜๋ฌธ ๋ชจ๋‘ OK

License

MIT ยฉ ๊ณ ๊ทœํ˜„ (Kyuhyun Koh)


Author

๊ณ ๊ทœํ˜„ โ€” ๊ด‘๊ต๊ณ ๋“ฑํ•™๊ต ์ •๋ณดยท์ปดํ“จํ„ฐ ๊ต์‚ฌ

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

hwpx_mcp_server-2.0.0.tar.gz (89.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

hwpx_mcp_server-2.0.0-py3-none-any.whl (76.1 kB view details)

Uploaded Python 3

File details

Details for the file hwpx_mcp_server-2.0.0.tar.gz.

File metadata

  • Download URL: hwpx_mcp_server-2.0.0.tar.gz
  • Upload date:
  • Size: 89.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.7

File hashes

Hashes for hwpx_mcp_server-2.0.0.tar.gz
Algorithm Hash digest
SHA256 a17ed56a4d12e1af673d34a7b88e0a15be12bc2222fcd428656a6b066ccd58ef
MD5 d65a8d8139f1d023660e86a5f62a0f05
BLAKE2b-256 9acdfcf654cbb8e9d7ae8cc811a7f7f958d24be172edbd90c1f418051044db7d

See more details on using hashes here.

File details

Details for the file hwpx_mcp_server-2.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for hwpx_mcp_server-2.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2a1cafa56012064c1e222a148e477e578d9da8a0114fd8a8dd59745285379cdf
MD5 5200ae17f62121ceb8a294cb127aad53
BLAKE2b-256 3f70f5f67ca6adcc2e8df81943418da3906a04fe2dce3bb64123ecce528dab68

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page