Skip to content

Commit 05aaafa

Browse files
committed
feat(analytics): add mcp server.
1 parent 80c9a7c commit 05aaafa

6 files changed

Lines changed: 102 additions & 23 deletions

File tree

README.md

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ _Cloud‑native survey engine with end‑to‑end observability._
3333
- **Monitoring** | Prometheus + Grafana (Latency, Error Rate, Traffic, Saturation)
3434
- **Unit/Integration Test** | Vitest + JaCoCo + Codecov + TestContainers
3535
- **Stress tests** with k6 (nightly via GitHub Actions)
36-
- **LLMs** | analytics service exposes an AI chat endpoint to talk about the surveys.
36+
- **MCP** | analytics service with MCP connected to an LLM (feature available for premium users).
3737
- **Delivery** GitHub Actions · Docker · Caddy reverse‑proxy
3838

3939

@@ -48,16 +48,10 @@ _Cloud‑native survey engine with end‑to‑end observability._
4848
.github/workflows
4949
├── dependabot.yml
5050
└── workflows
51-
├── cd-keycloak.yml
52-
├── cd-monitoring.yml
53-
├── cd-prod.yml
54-
├── cd-staging.yml
55-
├── ci-analytics.yml
56-
├── ci-backend.yml
57-
├── ci-backoffice.yml
58-
├── ci-code-vulnerabilities.yml
59-
├── ci-coverage.yml
60-
├── ci-frontend.yml
51+
├── cd
52+
├── ...
53+
├── ci
54+
├── ...
6155
├── nightly-stress.yml
6256
├── release-changelog.yml
6357
└── security.yml

services/analytics/README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ Runtime dependencies are defined in `pyproject.toml`. To generate a
3838
`requirements.txt` file, install `pip-tools` and run:
3939

4040
```bash
41-
pip install pip-tools
42-
pip-compile --output-file=requirements.txt pyproject.toml
41+
uv pip install pip-tools
42+
```
43+
44+
```bash
45+
uv pip compile pyproject.toml -o requirements.txt
4346
```

services/analytics/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ dependencies = [
1616
"httpx>=0.27",
1717
"python-logstash>=0.4.8",
1818
"sentry-sdk>=2",
19+
"mcp>=1.11",
1920
]
2021

2122
[project.optional-dependencies]
Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
#
2-
# This file is autogenerated by pip-compile with Python 3.12
3-
# by the following command:
4-
#
5-
# pip-compile --output-file=requirements.txt pyproject.toml
6-
#
1+
# This file was autogenerated by uv via the following command:
2+
# uv pip compile pyproject.toml -o requirements.txt
73
annotated-types==0.7.0
84
# via pydantic
95
anyio==4.9.0
106
# via
117
# httpx
8+
# mcp
9+
# sse-starlette
1210
# starlette
11+
attrs==25.3.0
12+
# via
13+
# jsonschema
14+
# referencing
1315
certifi==2025.4.26
1416
# via
1517
# httpcore
@@ -26,35 +28,70 @@ h11==0.16.0
2628
httpcore==1.0.9
2729
# via httpx
2830
httpx==0.28.1
29-
# via analytics (pyproject.toml)
31+
# via
32+
# analytics (pyproject.toml)
33+
# mcp
34+
httpx-sse==0.4.1
35+
# via mcp
3036
idna==3.10
3137
# via
3238
# anyio
3339
# httpx
40+
jsonschema==4.24.0
41+
# via mcp
42+
jsonschema-specifications==2025.4.1
43+
# via jsonschema
44+
mcp==1.11.0
45+
# via analytics (pyproject.toml)
3446
pydantic==2.11.5
3547
# via
3648
# analytics (pyproject.toml)
3749
# fastapi
50+
# mcp
51+
# pydantic-settings
3852
pydantic-core==2.33.2
3953
# via pydantic
54+
pydantic-settings==2.10.1
55+
# via mcp
56+
python-dotenv==1.1.1
57+
# via pydantic-settings
4058
python-logstash==0.4.8
4159
# via analytics (pyproject.toml)
60+
python-multipart==0.0.20
61+
# via mcp
62+
referencing==0.36.2
63+
# via
64+
# jsonschema
65+
# jsonschema-specifications
66+
rpds-py==0.26.0
67+
# via
68+
# jsonschema
69+
# referencing
4270
sentry-sdk==2.30.0
4371
# via analytics (pyproject.toml)
4472
sniffio==1.3.1
4573
# via anyio
74+
sse-starlette==2.4.1
75+
# via mcp
4676
starlette==0.46.2
47-
# via fastapi
77+
# via
78+
# fastapi
79+
# mcp
4880
typing-extensions==4.14.0
4981
# via
5082
# anyio
5183
# fastapi
5284
# pydantic
5385
# pydantic-core
86+
# referencing
5487
# typing-inspection
5588
typing-inspection==0.4.1
56-
# via pydantic
89+
# via
90+
# pydantic
91+
# pydantic-settings
5792
urllib3==2.4.0
5893
# via sentry-sdk
5994
uvicorn==0.34.3
60-
# via analytics (pyproject.toml)
95+
# via
96+
# analytics (pyproject.toml)
97+
# mcp
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
"""Minimal MCP server exposing a summary tool."""
2+
3+
import os
4+
5+
from mcp.server import FastMCP
6+
7+
from analytics.exceptions import AnalyticException
8+
from analytics.logger import get_logger
9+
from analytics.ollama import OllamaClient
10+
11+
logger = get_logger("mcp")
12+
13+
OLLAMA_URL: str = os.getenv("OLLAMA_URL", "http://localhost:11434")
14+
OLLAMA_MODEL: str = "tinyllama"
15+
ollama_client = OllamaClient(OLLAMA_URL, OLLAMA_MODEL, logger=logger)
16+
17+
server = FastMCP(name="analytics")
18+
19+
20+
@server.tool(name="summarize")
21+
def summarize(question: str) -> str:
22+
"""Return a summary for ``question`` using Ollama."""
23+
try:
24+
return ollama_client.summarize(question)
25+
except AnalyticException as exc: # noqa: BLE001
26+
logger.error("Failed to summarize", exc_info=exc)
27+
return f"Received question: {question}"
28+
29+
30+
if __name__ == "__main__": # pragma: no cover
31+
server.run("sse")
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import anyio
2+
3+
from analytics import mcp_server
4+
5+
6+
def test_summarize_tool(monkeypatch):
7+
def fake_summary(prompt: str) -> str:
8+
return f"echo:{prompt}"
9+
10+
monkeypatch.setattr(mcp_server.ollama_client, "summarize", fake_summary)
11+
result = anyio.run(mcp_server.server.call_tool, "summarize", {"question": "hi"})
12+
content, data = result
13+
assert content[0].text == "echo:hi"

0 commit comments

Comments
 (0)