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.
@@ -0,0 +1,6 @@
1
+ # Required: your Sentor API key
2
+ # Get yours at https://dashboard.sentor.app/settings?tab=api-access
3
+ SENTOR_API_KEY=your_api_key_here
4
+
5
+ # Optional: override API base URL (defaults to production)
6
+ # SENTOR_BASE_URL=https://sentor.app/api
@@ -0,0 +1,10 @@
1
+ .claude-flow/
2
+ __pycache__/
3
+ *.pyc
4
+ *.pyo
5
+ .env
6
+ dist/
7
+ build/
8
+ *.egg-info/
9
+ .venv/
10
+ venv/
@@ -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,3 @@
1
+ """Sentor MCP Server — entity-based sentiment analysis for AI assistants."""
2
+
3
+ __version__ = "1.0.0"
@@ -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)