sentor-mcp 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sentor_mcp-1.0.0/.env.example +6 -0
- sentor_mcp-1.0.0/.gitignore +10 -0
- sentor_mcp-1.0.0/Dockerfile +14 -0
- sentor_mcp-1.0.0/PKG-INFO +175 -0
- sentor_mcp-1.0.0/README.md +151 -0
- sentor_mcp-1.0.0/pyproject.toml +39 -0
- sentor_mcp-1.0.0/sentor_mcp/__init__.py +3 -0
- sentor_mcp-1.0.0/sentor_mcp/server.py +173 -0
- sentor_mcp-1.0.0/sentor_mcp/server_http.py +8 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
FROM python:3.12-slim
|
|
2
|
+
|
|
3
|
+
WORKDIR /app
|
|
4
|
+
|
|
5
|
+
COPY pyproject.toml .
|
|
6
|
+
COPY sentor_mcp/ ./sentor_mcp/
|
|
7
|
+
|
|
8
|
+
RUN pip install --no-cache-dir -e .
|
|
9
|
+
|
|
10
|
+
ENV SENTOR_BASE_URL=https://sentor.app/api
|
|
11
|
+
|
|
12
|
+
EXPOSE 8080
|
|
13
|
+
|
|
14
|
+
CMD ["python", "-m", "sentor_mcp.server_http"]
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sentor-mcp
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Sentor AI MCP Server — entity-based sentiment analysis tools for Claude, Cursor, and any MCP-compatible AI assistant
|
|
5
|
+
Project-URL: Homepage, https://sentor.app
|
|
6
|
+
Project-URL: Documentation, https://sentor.app/docs/integrations/mcp
|
|
7
|
+
Project-URL: Repository, https://github.com/NIKX-Tech/sentor-mcp
|
|
8
|
+
Project-URL: Get API Key, https://dashboard.sentor.app/settings?tab=api-access
|
|
9
|
+
Author-email: "NIKX Technologies B.V." <sentor@nikx.one>
|
|
10
|
+
License: MIT
|
|
11
|
+
Keywords: ai,claude,mcp,nlp,sentiment-analysis,sentor
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Requires-Dist: httpx>=0.27.0
|
|
22
|
+
Requires-Dist: mcp>=1.0.0
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# Sentor MCP Server
|
|
26
|
+
|
|
27
|
+
Connect Sentor's entity-based sentiment analysis to Claude, Cursor, Windsurf, and any MCP-compatible AI assistant.
|
|
28
|
+
|
|
29
|
+
## What It Does
|
|
30
|
+
|
|
31
|
+
Once installed, your AI assistant gains four tools:
|
|
32
|
+
|
|
33
|
+
| Tool | What it does |
|
|
34
|
+
|------|-------------|
|
|
35
|
+
| `analyze_sentiment` | Score sentiment toward specific entities in any text |
|
|
36
|
+
| `cluster_documents` | Group 5+ documents by topic automatically (BERTopic) |
|
|
37
|
+
| `name_topic` | Generate a 3-5 word label for each cluster using LLM |
|
|
38
|
+
| `health_check` | Verify Sentor API is reachable |
|
|
39
|
+
|
|
40
|
+
**Example prompt after setup:** *"Analyze these 50 customer reviews for sentiment toward our checkout flow and shipping speed, then cluster them by topic."*
|
|
41
|
+
|
|
42
|
+
## Requirements
|
|
43
|
+
|
|
44
|
+
- Python 3.10+
|
|
45
|
+
- A Sentor API key → [Get one free at dashboard.sentor.app](https://dashboard.sentor.app/settings?tab=api-access)
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install sentor-mcp
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Claude Desktop Setup
|
|
54
|
+
|
|
55
|
+
Add to your `claude_desktop_config.json`:
|
|
56
|
+
|
|
57
|
+
**macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
58
|
+
**Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"mcpServers": {
|
|
63
|
+
"sentor": {
|
|
64
|
+
"command": "sentor-mcp",
|
|
65
|
+
"env": {
|
|
66
|
+
"SENTOR_API_KEY": "your_api_key_here"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Restart Claude Desktop. You'll see the Sentor tools available in the tool selector.
|
|
74
|
+
|
|
75
|
+
## Cursor / Windsurf Setup
|
|
76
|
+
|
|
77
|
+
Add to your MCP config file (`.cursor/mcp.json` or equivalent):
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"mcpServers": {
|
|
82
|
+
"sentor": {
|
|
83
|
+
"command": "sentor-mcp",
|
|
84
|
+
"env": {
|
|
85
|
+
"SENTOR_API_KEY": "your_api_key_here"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Usage Examples
|
|
93
|
+
|
|
94
|
+
Once connected, use natural language:
|
|
95
|
+
|
|
96
|
+
**Sentiment analysis:**
|
|
97
|
+
> "Use Sentor to analyze the sentiment of these reviews toward Apple and iPhone: [paste reviews]"
|
|
98
|
+
|
|
99
|
+
**Clustering:**
|
|
100
|
+
> "I have 200 customer support tickets. Use Sentor to cluster them by topic and name each cluster."
|
|
101
|
+
|
|
102
|
+
**Full pipeline:**
|
|
103
|
+
> "Analyze sentiment in these 100 reviews for 'delivery' and 'packaging' entities, then cluster them and name each cluster."
|
|
104
|
+
|
|
105
|
+
## Tool Reference
|
|
106
|
+
|
|
107
|
+
### `analyze_sentiment(docs, language="en")`
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
docs = [
|
|
111
|
+
{
|
|
112
|
+
"doc_id": "review_1",
|
|
113
|
+
"doc": "The delivery was fast but the packaging was terrible.",
|
|
114
|
+
"entities": ["delivery", "packaging"]
|
|
115
|
+
}
|
|
116
|
+
]
|
|
117
|
+
# Returns: predicted_label, probabilities, per-sentence details
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### `cluster_documents(documents, language="en")`
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
documents = [
|
|
124
|
+
{"doc_id": "r1", "text": "Great product quality", "entities": ["product"]},
|
|
125
|
+
# ... at least 5 documents required
|
|
126
|
+
]
|
|
127
|
+
# Returns: clusters with cluster_id, documents, top_words
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### `name_topic(cluster_id, documents, top_words, entities, language="en")`
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
# Pass the cluster data from cluster_documents response:
|
|
134
|
+
name_topic(
|
|
135
|
+
cluster_id=0,
|
|
136
|
+
documents=cluster["documents"],
|
|
137
|
+
top_words=cluster["top_words"],
|
|
138
|
+
entities=["BrandName"] # exclude your brand from the topic label
|
|
139
|
+
)
|
|
140
|
+
# Returns: topic_name (e.g. "Shipping Delay Complaints")
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### `health_check()`
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
# Returns: { "status": "healthy", "version": "...", "llm_status": "available" }
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Rate Limits
|
|
150
|
+
|
|
151
|
+
| Plan | Per Minute | Per Month |
|
|
152
|
+
|------|-----------|-----------|
|
|
153
|
+
| Free | 3 | 300 |
|
|
154
|
+
| Starter | 60 | 3,000 |
|
|
155
|
+
| Growth | 200 | 15,000 |
|
|
156
|
+
| Business | 500 | 60,000 |
|
|
157
|
+
| Enterprise | 10,000 | Unlimited |
|
|
158
|
+
|
|
159
|
+
## Remote Deployment (HTTP/SSE)
|
|
160
|
+
|
|
161
|
+
To run as a hosted server:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
docker build -t sentor-mcp .
|
|
165
|
+
docker run -e SENTOR_API_KEY=your_key -p 8080:8080 sentor-mcp
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
The server exposes SSE at `http://localhost:8080/sse` and can be connected to AI tools that support remote MCP servers.
|
|
169
|
+
|
|
170
|
+
## Links
|
|
171
|
+
|
|
172
|
+
- [Sentor Dashboard](https://dashboard.sentor.app)
|
|
173
|
+
- [API Documentation](https://sentor.app/docs)
|
|
174
|
+
- [MCP Integration Guide](https://sentor.app/docs/integrations/mcp)
|
|
175
|
+
- [Support](mailto:sentor@nikx.one)
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Sentor MCP Server
|
|
2
|
+
|
|
3
|
+
Connect Sentor's entity-based sentiment analysis to Claude, Cursor, Windsurf, and any MCP-compatible AI assistant.
|
|
4
|
+
|
|
5
|
+
## What It Does
|
|
6
|
+
|
|
7
|
+
Once installed, your AI assistant gains four tools:
|
|
8
|
+
|
|
9
|
+
| Tool | What it does |
|
|
10
|
+
|------|-------------|
|
|
11
|
+
| `analyze_sentiment` | Score sentiment toward specific entities in any text |
|
|
12
|
+
| `cluster_documents` | Group 5+ documents by topic automatically (BERTopic) |
|
|
13
|
+
| `name_topic` | Generate a 3-5 word label for each cluster using LLM |
|
|
14
|
+
| `health_check` | Verify Sentor API is reachable |
|
|
15
|
+
|
|
16
|
+
**Example prompt after setup:** *"Analyze these 50 customer reviews for sentiment toward our checkout flow and shipping speed, then cluster them by topic."*
|
|
17
|
+
|
|
18
|
+
## Requirements
|
|
19
|
+
|
|
20
|
+
- Python 3.10+
|
|
21
|
+
- A Sentor API key → [Get one free at dashboard.sentor.app](https://dashboard.sentor.app/settings?tab=api-access)
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install sentor-mcp
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Claude Desktop Setup
|
|
30
|
+
|
|
31
|
+
Add to your `claude_desktop_config.json`:
|
|
32
|
+
|
|
33
|
+
**macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
34
|
+
**Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"mcpServers": {
|
|
39
|
+
"sentor": {
|
|
40
|
+
"command": "sentor-mcp",
|
|
41
|
+
"env": {
|
|
42
|
+
"SENTOR_API_KEY": "your_api_key_here"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Restart Claude Desktop. You'll see the Sentor tools available in the tool selector.
|
|
50
|
+
|
|
51
|
+
## Cursor / Windsurf Setup
|
|
52
|
+
|
|
53
|
+
Add to your MCP config file (`.cursor/mcp.json` or equivalent):
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"mcpServers": {
|
|
58
|
+
"sentor": {
|
|
59
|
+
"command": "sentor-mcp",
|
|
60
|
+
"env": {
|
|
61
|
+
"SENTOR_API_KEY": "your_api_key_here"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Usage Examples
|
|
69
|
+
|
|
70
|
+
Once connected, use natural language:
|
|
71
|
+
|
|
72
|
+
**Sentiment analysis:**
|
|
73
|
+
> "Use Sentor to analyze the sentiment of these reviews toward Apple and iPhone: [paste reviews]"
|
|
74
|
+
|
|
75
|
+
**Clustering:**
|
|
76
|
+
> "I have 200 customer support tickets. Use Sentor to cluster them by topic and name each cluster."
|
|
77
|
+
|
|
78
|
+
**Full pipeline:**
|
|
79
|
+
> "Analyze sentiment in these 100 reviews for 'delivery' and 'packaging' entities, then cluster them and name each cluster."
|
|
80
|
+
|
|
81
|
+
## Tool Reference
|
|
82
|
+
|
|
83
|
+
### `analyze_sentiment(docs, language="en")`
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
docs = [
|
|
87
|
+
{
|
|
88
|
+
"doc_id": "review_1",
|
|
89
|
+
"doc": "The delivery was fast but the packaging was terrible.",
|
|
90
|
+
"entities": ["delivery", "packaging"]
|
|
91
|
+
}
|
|
92
|
+
]
|
|
93
|
+
# Returns: predicted_label, probabilities, per-sentence details
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### `cluster_documents(documents, language="en")`
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
documents = [
|
|
100
|
+
{"doc_id": "r1", "text": "Great product quality", "entities": ["product"]},
|
|
101
|
+
# ... at least 5 documents required
|
|
102
|
+
]
|
|
103
|
+
# Returns: clusters with cluster_id, documents, top_words
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### `name_topic(cluster_id, documents, top_words, entities, language="en")`
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
# Pass the cluster data from cluster_documents response:
|
|
110
|
+
name_topic(
|
|
111
|
+
cluster_id=0,
|
|
112
|
+
documents=cluster["documents"],
|
|
113
|
+
top_words=cluster["top_words"],
|
|
114
|
+
entities=["BrandName"] # exclude your brand from the topic label
|
|
115
|
+
)
|
|
116
|
+
# Returns: topic_name (e.g. "Shipping Delay Complaints")
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### `health_check()`
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
# Returns: { "status": "healthy", "version": "...", "llm_status": "available" }
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Rate Limits
|
|
126
|
+
|
|
127
|
+
| Plan | Per Minute | Per Month |
|
|
128
|
+
|------|-----------|-----------|
|
|
129
|
+
| Free | 3 | 300 |
|
|
130
|
+
| Starter | 60 | 3,000 |
|
|
131
|
+
| Growth | 200 | 15,000 |
|
|
132
|
+
| Business | 500 | 60,000 |
|
|
133
|
+
| Enterprise | 10,000 | Unlimited |
|
|
134
|
+
|
|
135
|
+
## Remote Deployment (HTTP/SSE)
|
|
136
|
+
|
|
137
|
+
To run as a hosted server:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
docker build -t sentor-mcp .
|
|
141
|
+
docker run -e SENTOR_API_KEY=your_key -p 8080:8080 sentor-mcp
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The server exposes SSE at `http://localhost:8080/sse` and can be connected to AI tools that support remote MCP servers.
|
|
145
|
+
|
|
146
|
+
## Links
|
|
147
|
+
|
|
148
|
+
- [Sentor Dashboard](https://dashboard.sentor.app)
|
|
149
|
+
- [API Documentation](https://sentor.app/docs)
|
|
150
|
+
- [MCP Integration Guide](https://sentor.app/docs/integrations/mcp)
|
|
151
|
+
- [Support](mailto:sentor@nikx.one)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "sentor-mcp"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Sentor AI MCP Server — entity-based sentiment analysis tools for Claude, Cursor, and any MCP-compatible AI assistant"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
authors = [{ name = "NIKX Technologies B.V.", email = "sentor@nikx.one" }]
|
|
12
|
+
keywords = ["mcp", "sentiment-analysis", "nlp", "ai", "claude", "sentor"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 5 - Production/Stable",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.10",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
22
|
+
]
|
|
23
|
+
requires-python = ">=3.10"
|
|
24
|
+
dependencies = [
|
|
25
|
+
"mcp>=1.0.0",
|
|
26
|
+
"httpx>=0.27.0",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://sentor.app"
|
|
31
|
+
Documentation = "https://sentor.app/docs/integrations/mcp"
|
|
32
|
+
Repository = "https://github.com/NIKX-Tech/sentor-mcp"
|
|
33
|
+
"Get API Key" = "https://dashboard.sentor.app/settings?tab=api-access"
|
|
34
|
+
|
|
35
|
+
[project.scripts]
|
|
36
|
+
sentor-mcp = "sentor_mcp.server:main"
|
|
37
|
+
|
|
38
|
+
[tool.hatch.build.targets.wheel]
|
|
39
|
+
packages = ["sentor_mcp"]
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""Sentor MCP Server — exposes sentiment analysis, clustering, and topic naming as MCP tools."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import httpx
|
|
5
|
+
from mcp.server.fastmcp import FastMCP
|
|
6
|
+
|
|
7
|
+
SENTOR_BASE_URL = os.getenv("SENTOR_BASE_URL", "https://sentor.app/api")
|
|
8
|
+
SENTOR_API_KEY = os.getenv("SENTOR_API_KEY", "")
|
|
9
|
+
|
|
10
|
+
mcp = FastMCP(
|
|
11
|
+
"Sentor AI",
|
|
12
|
+
instructions=(
|
|
13
|
+
"You have access to Sentor's entity-based sentiment analysis API. "
|
|
14
|
+
"Use analyze_sentiment to score sentiment toward specific entities in text. "
|
|
15
|
+
"Use cluster_documents to group documents by topic (min 5 docs). "
|
|
16
|
+
"Use name_topic after clustering to label each cluster with a descriptive name. "
|
|
17
|
+
"Use health_check to verify the service is available. "
|
|
18
|
+
"All tools require a valid SENTOR_API_KEY set in the environment."
|
|
19
|
+
),
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _headers() -> dict:
|
|
24
|
+
if not SENTOR_API_KEY:
|
|
25
|
+
raise ValueError(
|
|
26
|
+
"SENTOR_API_KEY environment variable is not set. "
|
|
27
|
+
"Get your key at https://dashboard.sentor.app/settings?tab=api-access"
|
|
28
|
+
)
|
|
29
|
+
return {"x-api-key": SENTOR_API_KEY, "Content-Type": "application/json"}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@mcp.tool()
|
|
33
|
+
def analyze_sentiment(
|
|
34
|
+
docs: list[dict],
|
|
35
|
+
language: str = "en",
|
|
36
|
+
) -> dict:
|
|
37
|
+
"""Analyze entity-based sentiment in one or more documents.
|
|
38
|
+
|
|
39
|
+
Each item in docs must be:
|
|
40
|
+
{ "doc_id": "unique-id", "doc": "text to analyze", "entities": ["entity1", "entity2"] }
|
|
41
|
+
|
|
42
|
+
Entities are the specific subjects you want sentiment scored for (e.g. a brand, product,
|
|
43
|
+
feature, or person). The model returns per-document sentiment (negative/neutral/positive)
|
|
44
|
+
plus per-sentence breakdowns.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
docs: List of document objects with doc_id, doc, and entities fields.
|
|
48
|
+
language: Language code — "en" (English) or "nl" (Dutch). Defaults to "en".
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
Dict with "results" list. Each result has doc_id, predicted_label, probabilities,
|
|
52
|
+
and sentence-level details.
|
|
53
|
+
"""
|
|
54
|
+
with httpx.Client(timeout=30) as client:
|
|
55
|
+
response = client.post(
|
|
56
|
+
f"{SENTOR_BASE_URL}/predicts",
|
|
57
|
+
params={"language": language},
|
|
58
|
+
json={"docs": docs},
|
|
59
|
+
headers=_headers(),
|
|
60
|
+
)
|
|
61
|
+
response.raise_for_status()
|
|
62
|
+
return response.json()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@mcp.tool()
|
|
66
|
+
def cluster_documents(
|
|
67
|
+
documents: list[dict],
|
|
68
|
+
language: str = "en",
|
|
69
|
+
) -> dict:
|
|
70
|
+
"""Group documents into thematic clusters using BERTopic + HDBSCAN.
|
|
71
|
+
|
|
72
|
+
Requires at least 5 documents. The algorithm automatically discovers the natural
|
|
73
|
+
number of clusters — you do not need to specify how many. Cluster -1 contains outliers
|
|
74
|
+
that did not fit any topic.
|
|
75
|
+
|
|
76
|
+
Each item in documents must be:
|
|
77
|
+
{ "doc_id": "unique-id", "text": "document text", "entities": ["optional", "entities"] }
|
|
78
|
+
|
|
79
|
+
After clustering, pass each cluster's top_words and documents to name_topic to get
|
|
80
|
+
a human-readable label.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
documents: List of document objects with doc_id, text, and optional entities fields.
|
|
84
|
+
language: Language code — "en" or "nl". Defaults to "en".
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
Dict with "clusters" list (each with cluster_id, document_count, documents, top_words),
|
|
88
|
+
total_documents, total_clusters, and outliers_count.
|
|
89
|
+
"""
|
|
90
|
+
if len(documents) < 5:
|
|
91
|
+
return {
|
|
92
|
+
"error": f"Clustering requires at least 5 documents, got {len(documents)}.",
|
|
93
|
+
"hint": "Add more documents or use analyze_sentiment for smaller sets.",
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
with httpx.Client(timeout=120) as client:
|
|
97
|
+
response = client.post(
|
|
98
|
+
f"{SENTOR_BASE_URL}/predicts/cluster",
|
|
99
|
+
params={"language": language},
|
|
100
|
+
json={"documents": documents},
|
|
101
|
+
headers=_headers(),
|
|
102
|
+
)
|
|
103
|
+
response.raise_for_status()
|
|
104
|
+
return response.json()
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@mcp.tool()
|
|
108
|
+
def name_topic(
|
|
109
|
+
cluster_id: int,
|
|
110
|
+
documents: list[dict],
|
|
111
|
+
top_words: list[str] | None = None,
|
|
112
|
+
entities: list[str] | None = None,
|
|
113
|
+
language: str = "en",
|
|
114
|
+
) -> dict:
|
|
115
|
+
"""Generate a short descriptive name for a document cluster using an LLM.
|
|
116
|
+
|
|
117
|
+
Call this after cluster_documents. Pass the cluster's documents and top_words
|
|
118
|
+
from the clustering response for best results. The model produces a 3-5 word
|
|
119
|
+
label (e.g. "Shipping Delay Complaints", "Product Quality Praise").
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
cluster_id: The cluster ID from the clustering response (e.g. 0, 1, 2).
|
|
123
|
+
documents: The documents list from that cluster's clustering response entry.
|
|
124
|
+
top_words: The top_words list from that cluster's clustering response entry (recommended).
|
|
125
|
+
entities: Entity names to exclude from the topic label (e.g. your brand name).
|
|
126
|
+
language: Language code — "en" or "nl". Defaults to "en".
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
Dict with cluster_id, topic_name (the generated label), document_count,
|
|
130
|
+
and generation_method ("LLM" or "Fallback").
|
|
131
|
+
"""
|
|
132
|
+
with httpx.Client(timeout=30) as client:
|
|
133
|
+
response = client.post(
|
|
134
|
+
f"{SENTOR_BASE_URL}/predicts/topic-name",
|
|
135
|
+
json={
|
|
136
|
+
"cluster_id": cluster_id,
|
|
137
|
+
"documents": documents,
|
|
138
|
+
"top_words": top_words or [],
|
|
139
|
+
"entities": entities or [],
|
|
140
|
+
"language": language,
|
|
141
|
+
},
|
|
142
|
+
headers=_headers(),
|
|
143
|
+
)
|
|
144
|
+
response.raise_for_status()
|
|
145
|
+
return response.json()
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@mcp.tool()
|
|
149
|
+
def health_check() -> dict:
|
|
150
|
+
"""Check Sentor API availability and model status.
|
|
151
|
+
|
|
152
|
+
Returns the API status, version, and whether the LLM provider (used for
|
|
153
|
+
topic naming and report generation) is reachable.
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
Dict with status ("healthy" or "degraded"), version, llm_provider, and llm_status.
|
|
157
|
+
"""
|
|
158
|
+
with httpx.Client(timeout=10) as client:
|
|
159
|
+
response = client.get(
|
|
160
|
+
f"{SENTOR_BASE_URL}/predicts/health",
|
|
161
|
+
headers=_headers(),
|
|
162
|
+
)
|
|
163
|
+
response.raise_for_status()
|
|
164
|
+
return response.json()
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def main() -> None:
|
|
168
|
+
"""Entry point for stdio transport (Claude Desktop, Cursor, Windsurf)."""
|
|
169
|
+
mcp.run(transport="stdio")
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
if __name__ == "__main__":
|
|
173
|
+
main()
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""HTTP/SSE transport for Sentor MCP — for remote deployment (Claude.ai web, hosted environments)."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from sentor_mcp.server import mcp
|
|
5
|
+
|
|
6
|
+
if __name__ == "__main__":
|
|
7
|
+
port = int(os.getenv("PORT", "8080"))
|
|
8
|
+
mcp.run(transport="sse", host="0.0.0.0", port=port)
|