Skip to content

Commit 24f402e

Browse files
authored
Merge pull request #44 from appwrite/hide-healthz-access-logs
Hide health checks and publish Docker Hub images
2 parents 8b72426 + c02a422 commit 24f402e

4 files changed

Lines changed: 94 additions & 3 deletions

File tree

.github/workflows/production.yml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,17 @@ concurrency:
99
group: ${{ github.workflow }}
1010
cancel-in-progress: false
1111

12+
permissions:
13+
contents: read
14+
packages: write
15+
1216
env:
1317
ENVIRONMENT: production
1418
PROJECT: mcp
1519
DECLARATIVE_OWNER: appwrite-labs
1620
DECLARATIVE_REPOSITORY: assets-applications
1721
REGISTRY_GITHUB: ghcr.io
22+
REGISTRY_DOCKERHUB: docker.io
1823
IMAGE_NAME: appwrite/mcp
1924
TAG: ${{ github.event.release.tag_name || github.sha }}
2025

@@ -32,12 +37,21 @@ jobs:
3237
username: ${{ github.actor }}
3338
password: ${{ secrets.GITHUB_TOKEN }}
3439

40+
- name: Login to Docker Hub
41+
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
42+
with:
43+
registry: ${{ env.REGISTRY_DOCKERHUB }}
44+
username: ${{ secrets.DOCKERHUB_USERNAME }}
45+
password: ${{ secrets.DOCKERHUB_TOKEN }}
46+
3547
- name: Build and push
3648
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
3749
with:
3850
context: .
3951
push: true
40-
tags: ${{ env.REGISTRY_GITHUB }}/${{ env.IMAGE_NAME }}:${{ env.TAG }}
52+
tags: |
53+
${{ env.REGISTRY_GITHUB }}/${{ env.IMAGE_NAME }}:${{ env.TAG }}
54+
${{ env.REGISTRY_DOCKERHUB }}/${{ env.IMAGE_NAME }}:${{ env.TAG }}
4155
4256
deploy:
4357
needs: build

.github/workflows/staging.yml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,17 @@ concurrency:
1010
group: ${{ github.workflow }}
1111
cancel-in-progress: false
1212

13+
permissions:
14+
contents: read
15+
packages: write
16+
1317
env:
1418
ENVIRONMENT: staging
1519
PROJECT: mcp
1620
DECLARATIVE_OWNER: appwrite-labs
1721
DECLARATIVE_REPOSITORY: assets-applications
1822
REGISTRY_GITHUB: ghcr.io
23+
REGISTRY_DOCKERHUB: docker.io
1924
IMAGE_NAME: appwrite/mcp
2025
TAG: ${{ github.sha }}
2126

@@ -33,12 +38,21 @@ jobs:
3338
username: ${{ github.actor }}
3439
password: ${{ secrets.GITHUB_TOKEN }}
3540

41+
- name: Login to Docker Hub
42+
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
43+
with:
44+
registry: ${{ env.REGISTRY_DOCKERHUB }}
45+
username: ${{ secrets.DOCKERHUB_USERNAME }}
46+
password: ${{ secrets.DOCKERHUB_TOKEN }}
47+
3648
- name: Build and push
3749
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
3850
with:
3951
context: .
4052
push: true
41-
tags: ${{ env.REGISTRY_GITHUB }}/${{ env.IMAGE_NAME }}:${{ env.TAG }}
53+
tags: |
54+
${{ env.REGISTRY_GITHUB }}/${{ env.IMAGE_NAME }}:${{ env.TAG }}
55+
${{ env.REGISTRY_DOCKERHUB }}/${{ env.IMAGE_NAME }}:${{ env.TAG }}
4256
4357
deploy:
4458
needs: build

src/mcp_server_appwrite/http_app.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
from __future__ import annotations
1616

1717
import contextlib
18+
import copy
1819
import json
20+
import logging
1921
import sys
2022

2123
from mcp.server.auth.middleware.auth_context import AuthContextMiddleware
@@ -49,6 +51,19 @@
4951
}
5052

5153

54+
class HealthzAccessLogFilter(logging.Filter):
55+
"""Drop noisy load-balancer health probes from uvicorn access logs."""
56+
57+
def filter(self, record: logging.LogRecord) -> bool:
58+
args = record.args
59+
if isinstance(args, tuple) and len(args) >= 3:
60+
method = args[1]
61+
path = str(args[2]).split("?", 1)[0]
62+
return not (method == "GET" and path == "/healthz")
63+
64+
return "GET /healthz HTTP/" not in record.getMessage()
65+
66+
5267
def _log(message: str) -> None:
5368
print(f"[appwrite-mcp][http] {message}", file=sys.stderr, flush=True)
5469

@@ -166,7 +181,14 @@ async def lifespan(app: Starlette):
166181

167182
def run_http(*, host: str = "0.0.0.0", port: int = 8000) -> None:
168183
import uvicorn
184+
from uvicorn.config import LOGGING_CONFIG
169185

170186
app = build_app()
187+
log_config = copy.deepcopy(LOGGING_CONFIG)
188+
log_config.setdefault("filters", {})["hide_healthz"] = {
189+
"()": "mcp_server_appwrite.http_app.HealthzAccessLogFilter"
190+
}
191+
log_config["handlers"]["access"]["filters"] = ["hide_healthz"]
192+
171193
_log(f"Serving on http://{host}:{port} (resource path: /mcp)")
172-
uvicorn.run(app, host=host, port=port)
194+
uvicorn.run(app, host=host, port=port, log_config=log_config)

tests/unit/test_http_app.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import logging
2+
import unittest
3+
4+
from mcp_server_appwrite.http_app import HealthzAccessLogFilter
5+
6+
7+
class HealthzAccessLogFilterTests(unittest.TestCase):
8+
def setUp(self):
9+
self.filter = HealthzAccessLogFilter()
10+
11+
def _record(self, args):
12+
return logging.LogRecord(
13+
name="uvicorn.access",
14+
level=logging.INFO,
15+
pathname=__file__,
16+
lineno=1,
17+
msg='%s - "%s %s HTTP/%s" %d',
18+
args=args,
19+
exc_info=None,
20+
)
21+
22+
def test_filters_healthz_access_logs(self):
23+
record = self._record(("127.0.0.1:12345", "GET", "/healthz", "1.1", 200))
24+
25+
self.assertFalse(self.filter.filter(record))
26+
27+
def test_filters_healthz_access_logs_with_query_string(self):
28+
record = self._record(
29+
("127.0.0.1:12345", "GET", "/healthz?ready=1", "1.1", 200)
30+
)
31+
32+
self.assertFalse(self.filter.filter(record))
33+
34+
def test_keeps_non_healthz_access_logs(self):
35+
record = self._record(("127.0.0.1:12345", "GET", "/mcp", "1.1", 401))
36+
37+
self.assertTrue(self.filter.filter(record))
38+
39+
40+
if __name__ == "__main__":
41+
unittest.main()

0 commit comments

Comments
 (0)