kiprio-mcp 0.2.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.
- kiprio_mcp-0.2.0/.github/workflows/release.yml +27 -0
- kiprio_mcp-0.2.0/.gitignore +32 -0
- kiprio_mcp-0.2.0/Dockerfile +16 -0
- kiprio_mcp-0.2.0/LICENSE +21 -0
- kiprio_mcp-0.2.0/PKG-INFO +73 -0
- kiprio_mcp-0.2.0/README.md +52 -0
- kiprio_mcp-0.2.0/kiprio_mcp.py +340 -0
- kiprio_mcp-0.2.0/pyproject.toml +35 -0
- kiprio_mcp-0.2.0/tests/test_tools.py +79 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: Release to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build-and-publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
id-token: write # for trusted publishing
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: '3.11'
|
|
21
|
+
|
|
22
|
+
- run: pip install hatch
|
|
23
|
+
|
|
24
|
+
- run: hatch build
|
|
25
|
+
|
|
26
|
+
- name: Publish to PyPI
|
|
27
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Secrets — never commit
|
|
2
|
+
data/wallet.key
|
|
3
|
+
data/wallet.key.*
|
|
4
|
+
*.env
|
|
5
|
+
.env.*
|
|
6
|
+
secrets/
|
|
7
|
+
|
|
8
|
+
# Local DB + caches
|
|
9
|
+
data/workshop.db
|
|
10
|
+
data/workshop.db-*
|
|
11
|
+
data/crypto_cache.db
|
|
12
|
+
data/chromadb/
|
|
13
|
+
data/backups/
|
|
14
|
+
data/learning_context.json
|
|
15
|
+
data/notification_queue.json
|
|
16
|
+
|
|
17
|
+
# Python
|
|
18
|
+
__pycache__/
|
|
19
|
+
*.pyc
|
|
20
|
+
*.pyo
|
|
21
|
+
.venv/
|
|
22
|
+
venv/
|
|
23
|
+
|
|
24
|
+
# Logs
|
|
25
|
+
logs/
|
|
26
|
+
*.log
|
|
27
|
+
|
|
28
|
+
# OS / editor
|
|
29
|
+
.DS_Store
|
|
30
|
+
.idea/
|
|
31
|
+
.vscode/
|
|
32
|
+
*.swp
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# kiprio-mcp — MCP server for kiprio.com developer APIs.
|
|
2
|
+
# Glama builds this image, starts the container, and verifies the server responds to
|
|
3
|
+
# MCP introspection (tools/list) over stdio. No ports/HTTP needed — MCP is stdio here.
|
|
4
|
+
FROM python:3.11-slim
|
|
5
|
+
|
|
6
|
+
WORKDIR /app
|
|
7
|
+
|
|
8
|
+
# Install the package (pulls deps: mcp>=1.0.0, httpx>=0.27.0) + the `kiprio-mcp` entrypoint.
|
|
9
|
+
COPY pyproject.toml README.md LICENSE kiprio_mcp.py /app/
|
|
10
|
+
RUN pip install --no-cache-dir .
|
|
11
|
+
|
|
12
|
+
# Free tier works without a key; clients can override at runtime.
|
|
13
|
+
ENV KIPRIO_API_KEY=""
|
|
14
|
+
|
|
15
|
+
# kiprio-mcp speaks MCP over stdio (mcp.run()). Glama introspects via the protocol.
|
|
16
|
+
ENTRYPOINT ["kiprio-mcp"]
|
kiprio_mcp-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ForeverTools
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kiprio-mcp
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: MCP server for kiprio.com — 23 developer API tools for Claude, Cursor, and any MCP client
|
|
5
|
+
Project-URL: Homepage, https://kiprio.com
|
|
6
|
+
Project-URL: Repository, https://github.com/ForeverTools/kiprio-mcp
|
|
7
|
+
Project-URL: Documentation, https://kiprio.com/docs/
|
|
8
|
+
Author-email: ForeverTools <theworkshop@kiprio.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: ai,api,claude,developer-tools,mcp
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Requires-Dist: httpx>=0.27.0
|
|
19
|
+
Requires-Dist: mcp>=1.0.0
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# kiprio MCP Server
|
|
23
|
+
|
|
24
|
+
MCP server exposing [kiprio.com](https://kiprio.com) developer APIs as tools for Claude, Cursor, and any Model Context Protocol client.
|
|
25
|
+
|
|
26
|
+
**27 tools** covering email validation, DNS/network, web scraping, text processing, language utilities, and developer utilities.
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install kiprio-mcp
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Or from source:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
git clone https://github.com/ForeverTools/kiprio-mcp
|
|
38
|
+
cd kiprio-mcp
|
|
39
|
+
pip install -e .
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Configure (Claude Desktop / Claude Code)
|
|
43
|
+
|
|
44
|
+
Add to your MCP client config (e.g. `claude_desktop_config.json`):
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"mcpServers": {
|
|
49
|
+
"kiprio": {
|
|
50
|
+
"command": "kiprio-mcp",
|
|
51
|
+
"env": {"KIPRIO_API_KEY": "your_key_or_leave_empty"}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Free tier works without a key (rate-limited). Get a key at [kiprio.com/docs](https://kiprio.com/docs/).
|
|
58
|
+
|
|
59
|
+
## Tools
|
|
60
|
+
|
|
61
|
+
**Email & validation:** `validate_email`, `validate_vat`, `validate_iban`, `check_password_breach`
|
|
62
|
+
**Network/DNS:** `dns_lookup`, `whois_lookup`, `ssl_check`, `ip_lookup`
|
|
63
|
+
**Web & content:** `readability`, `tech_stack`, `parse_og_tags`, `screenshot_url`, `html_to_pdf`
|
|
64
|
+
**Language:** `sentiment_analysis`, `check_grammar`, `translate_text`, `summarize_text`, `redact_text`
|
|
65
|
+
**Utilities:** `generate_qr`, `generate_hash`, `generate_uuid`, `parse_cron`, `decode_jwt`
|
|
66
|
+
|
|
67
|
+
Full schema is auto-discovered by your MCP client.
|
|
68
|
+
|
|
69
|
+
## License
|
|
70
|
+
|
|
71
|
+
MIT — see [LICENSE](LICENSE).
|
|
72
|
+
|
|
73
|
+
Maintained by [ForeverTools](https://github.com/ForeverTools).
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# kiprio MCP Server
|
|
2
|
+
|
|
3
|
+
MCP server exposing [kiprio.com](https://kiprio.com) developer APIs as tools for Claude, Cursor, and any Model Context Protocol client.
|
|
4
|
+
|
|
5
|
+
**27 tools** covering email validation, DNS/network, web scraping, text processing, language utilities, and developer utilities.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install kiprio-mcp
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or from source:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
git clone https://github.com/ForeverTools/kiprio-mcp
|
|
17
|
+
cd kiprio-mcp
|
|
18
|
+
pip install -e .
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Configure (Claude Desktop / Claude Code)
|
|
22
|
+
|
|
23
|
+
Add to your MCP client config (e.g. `claude_desktop_config.json`):
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"mcpServers": {
|
|
28
|
+
"kiprio": {
|
|
29
|
+
"command": "kiprio-mcp",
|
|
30
|
+
"env": {"KIPRIO_API_KEY": "your_key_or_leave_empty"}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Free tier works without a key (rate-limited). Get a key at [kiprio.com/docs](https://kiprio.com/docs/).
|
|
37
|
+
|
|
38
|
+
## Tools
|
|
39
|
+
|
|
40
|
+
**Email & validation:** `validate_email`, `validate_vat`, `validate_iban`, `check_password_breach`
|
|
41
|
+
**Network/DNS:** `dns_lookup`, `whois_lookup`, `ssl_check`, `ip_lookup`
|
|
42
|
+
**Web & content:** `readability`, `tech_stack`, `parse_og_tags`, `screenshot_url`, `html_to_pdf`
|
|
43
|
+
**Language:** `sentiment_analysis`, `check_grammar`, `translate_text`, `summarize_text`, `redact_text`
|
|
44
|
+
**Utilities:** `generate_qr`, `generate_hash`, `generate_uuid`, `parse_cron`, `decode_jwt`
|
|
45
|
+
|
|
46
|
+
Full schema is auto-discovered by your MCP client.
|
|
47
|
+
|
|
48
|
+
## License
|
|
49
|
+
|
|
50
|
+
MIT — see [LICENSE](LICENSE).
|
|
51
|
+
|
|
52
|
+
Maintained by [ForeverTools](https://github.com/ForeverTools).
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
"""kiprio MCP Server — exposes kiprio.com developer APIs as MCP tools.
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
KIPRIO_API_KEY=your_key python kiprio_mcp.py
|
|
5
|
+
|
|
6
|
+
Or in claude_desktop_config.json / mcp config:
|
|
7
|
+
{
|
|
8
|
+
"mcpServers": {
|
|
9
|
+
"kiprio": {
|
|
10
|
+
"command": "python",
|
|
11
|
+
"args": ["/path/to/kiprio_mcp.py"],
|
|
12
|
+
"env": {"KIPRIO_API_KEY": "your_key"}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
Free tier works without an API key for demo-rate-limited endpoints.
|
|
18
|
+
Get a free key at https://kiprio.com/docs/
|
|
19
|
+
"""
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import base64
|
|
23
|
+
import os
|
|
24
|
+
import httpx
|
|
25
|
+
from mcp.server.fastmcp import FastMCP
|
|
26
|
+
|
|
27
|
+
BASE = "https://kiprio.com/v1"
|
|
28
|
+
API_KEY = os.environ.get("KIPRIO_API_KEY", "")
|
|
29
|
+
|
|
30
|
+
mcp = FastMCP("kiprio")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _headers() -> dict:
|
|
34
|
+
h: dict = {"Accept": "application/json"}
|
|
35
|
+
if API_KEY:
|
|
36
|
+
h["X-API-Key"] = API_KEY
|
|
37
|
+
return h
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _get(path: str, **params) -> dict:
|
|
41
|
+
r = httpx.get(
|
|
42
|
+
f"{BASE}{path}",
|
|
43
|
+
params={k: v for k, v in params.items() if v is not None},
|
|
44
|
+
headers=_headers(),
|
|
45
|
+
timeout=30,
|
|
46
|
+
follow_redirects=True,
|
|
47
|
+
)
|
|
48
|
+
if r.status_code in (401, 403):
|
|
49
|
+
return {"error": "API key required or rate limit reached. Get a free key at https://kiprio.com/docs/"}
|
|
50
|
+
if r.status_code == 429:
|
|
51
|
+
return {"error": "Rate limit exceeded. Upgrade at https://kiprio.com/pricing"}
|
|
52
|
+
r.raise_for_status()
|
|
53
|
+
return r.json()
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _post(path: str, body: dict) -> dict:
|
|
57
|
+
# Trailing slash avoids 301 redirect that would convert POST→GET and lose the body
|
|
58
|
+
url = f"{BASE}{path}" if path.endswith("/") else f"{BASE}{path}/"
|
|
59
|
+
r = httpx.post(url, json=body, headers=_headers(), timeout=30)
|
|
60
|
+
if r.status_code in (401, 403):
|
|
61
|
+
return {"error": "API key required or rate limit reached. Get a free key at https://kiprio.com/docs/"}
|
|
62
|
+
if r.status_code == 429:
|
|
63
|
+
return {"error": "Rate limit exceeded. Upgrade at https://kiprio.com/pricing"}
|
|
64
|
+
r.raise_for_status()
|
|
65
|
+
return r.json()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# ── Email ────────────────────────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
@mcp.tool()
|
|
71
|
+
def validate_email(email: str) -> dict:
|
|
72
|
+
"""Validate an email address: syntax, MX record, disposable domain detection.
|
|
73
|
+
|
|
74
|
+
Returns: valid (bool), reason, mx_found, is_disposable, is_role_address.
|
|
75
|
+
"""
|
|
76
|
+
return _get("/email-validate", email=email)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
# ── Network / DNS ─────────────────────────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
@mcp.tool()
|
|
82
|
+
def dns_lookup(domain: str, record_types: str = "A") -> dict:
|
|
83
|
+
"""Look up DNS records for a domain.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
domain: Domain name (e.g. 'example.com').
|
|
87
|
+
record_types: Comma-separated record types — A, AAAA, MX, TXT, NS, CNAME, SOA (default: A).
|
|
88
|
+
"""
|
|
89
|
+
return _get("/dns-lookup", domain=domain, types=record_types)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@mcp.tool()
|
|
93
|
+
def whois_lookup(domain: str) -> dict:
|
|
94
|
+
"""Retrieve WHOIS registration data for a domain: registrar, dates, nameservers."""
|
|
95
|
+
return _get("/whois", domain=domain)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@mcp.tool()
|
|
99
|
+
def ssl_check(host: str) -> dict:
|
|
100
|
+
"""Check SSL/TLS certificate for a host: validity, expiry, issuer, SANs."""
|
|
101
|
+
return _get("/ssl-check", host=host)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@mcp.tool()
|
|
105
|
+
def ip_lookup(ip: str) -> dict:
|
|
106
|
+
"""Geolocate an IP address and detect VPN/Tor/datacenter with threat scoring."""
|
|
107
|
+
return _get(f"/ip/{ip}")
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
# domain_health removed — /domain-health not yet live on kiprio.com free tier
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
# ── Web / Content ─────────────────────────────────────────────────────────────
|
|
114
|
+
|
|
115
|
+
@mcp.tool()
|
|
116
|
+
def screenshot_url(url: str, width: int = 1280, height: int = 800, full_page: bool = False) -> dict:
|
|
117
|
+
"""Capture a screenshot of a web page and return the image as base64-encoded PNG.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
url: Page URL to capture.
|
|
121
|
+
width: Viewport width in pixels (default 1280).
|
|
122
|
+
height: Viewport height in pixels (default 800).
|
|
123
|
+
full_page: If True, capture the full scrollable page.
|
|
124
|
+
|
|
125
|
+
Returns: dict with png_b64 (base64 PNG), w, h, bytes, ms.
|
|
126
|
+
"""
|
|
127
|
+
r = httpx.post(
|
|
128
|
+
f"{BASE}/screenshot/",
|
|
129
|
+
json={"url": url, "w": width, "h": height, "full_page": full_page},
|
|
130
|
+
headers=_headers(),
|
|
131
|
+
timeout=60,
|
|
132
|
+
)
|
|
133
|
+
if r.status_code in (401, 403):
|
|
134
|
+
return {"error": "API key required or rate limit reached. Get a free key at https://kiprio.com/docs/"}
|
|
135
|
+
if r.status_code == 429:
|
|
136
|
+
return {"error": "Rate limit exceeded. Upgrade at https://kiprio.com/pricing"}
|
|
137
|
+
r.raise_for_status()
|
|
138
|
+
return r.json()
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@mcp.tool()
|
|
142
|
+
def readability(url: str) -> dict:
|
|
143
|
+
"""Extract the main article text and metadata from a web page.
|
|
144
|
+
|
|
145
|
+
Returns: title, byline, content (plain text), word_count, published_at.
|
|
146
|
+
"""
|
|
147
|
+
return _get("/readability", url=url)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@mcp.tool()
|
|
151
|
+
def tech_stack(url: str) -> dict:
|
|
152
|
+
"""Detect the technology stack of a website: CMS, framework, CDN, analytics."""
|
|
153
|
+
return _get("/tech-stack", url=url)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@mcp.tool()
|
|
157
|
+
def parse_og_tags(url: str) -> dict:
|
|
158
|
+
"""Extract Open Graph, Twitter Card, and meta tags from a URL.
|
|
159
|
+
|
|
160
|
+
Returns: title, description, image, site_name, type, open_graph dict, twitter dict,
|
|
161
|
+
favicon, lang, author, published_at, keywords.
|
|
162
|
+
"""
|
|
163
|
+
return _get("/unfurl", url=url)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
# ── Text Processing ───────────────────────────────────────────────────────────
|
|
167
|
+
|
|
168
|
+
@mcp.tool()
|
|
169
|
+
def sentiment_analysis(text: str) -> dict:
|
|
170
|
+
"""Analyse the sentiment of text.
|
|
171
|
+
|
|
172
|
+
Returns: label (positive/negative/neutral), compound score (-1 to 1), confidence.
|
|
173
|
+
"""
|
|
174
|
+
return _post("/sentiment", {"text": text})
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
@mcp.tool()
|
|
178
|
+
def check_grammar(text: str, style: str = "concise") -> dict:
|
|
179
|
+
"""Check and rewrite text for grammar, spelling, and style.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
text: Text to check (up to 500 chars on free tier, 10k on pro).
|
|
183
|
+
style: Rewrite style — concise, formal, friendly, UK-en, US-en (default: concise).
|
|
184
|
+
|
|
185
|
+
Returns: corrected text, list of edits with explanations, chars_in, chars_out.
|
|
186
|
+
"""
|
|
187
|
+
return _post("/grammar/rewrite", {"text": text, "style": style})
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
@mcp.tool()
|
|
191
|
+
def translate_text(text: str, target: str, source: str = "auto") -> dict:
|
|
192
|
+
"""Translate text between languages.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
text: Text to translate.
|
|
196
|
+
target: Target language code (e.g. 'fr', 'de', 'es', 'zh').
|
|
197
|
+
source: Source language code or 'auto' to detect (default: auto).
|
|
198
|
+
"""
|
|
199
|
+
return _post("/translate", {"text": text, "to": target, "from": source})
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
# detect_language and content_moderation removed — /lingua and /moderation not yet live on free tier
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
@mcp.tool()
|
|
206
|
+
def summarize_text(text: str, max_sentences: int = 3) -> dict:
|
|
207
|
+
"""Summarize long text into key points.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
text: Text to summarize.
|
|
211
|
+
max_sentences: Target number of sentences in the summary (default 3).
|
|
212
|
+
"""
|
|
213
|
+
return _post("/summarize", {"text": text, "max_sentences": max_sentences})
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@mcp.tool()
|
|
217
|
+
def redact_text(text: str) -> dict:
|
|
218
|
+
"""Redact PII from text: emails, phone numbers, names, addresses, credit cards.
|
|
219
|
+
|
|
220
|
+
Returns: redacted text with [REDACTED_TYPE] placeholders, entities found.
|
|
221
|
+
"""
|
|
222
|
+
return _post("/redact", {"text": text})
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
# ── Utilities ─────────────────────────────────────────────────────────────────
|
|
226
|
+
|
|
227
|
+
@mcp.tool()
|
|
228
|
+
def generate_qr(url: str, size: int = 256, format: str = "png", fg: str = "#000000", bg: str = "#ffffff") -> str:
|
|
229
|
+
"""Generate a QR code and return it as a base64-encoded image.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
url: Content to encode (URL, text, vCard, etc.).
|
|
233
|
+
size: Image size in pixels (default 256).
|
|
234
|
+
format: Output format — png or svg (default png).
|
|
235
|
+
fg: Foreground colour hex (default #000000).
|
|
236
|
+
bg: Background colour hex (default #ffffff).
|
|
237
|
+
|
|
238
|
+
Returns: base64-encoded image string.
|
|
239
|
+
"""
|
|
240
|
+
r = httpx.get(
|
|
241
|
+
f"{BASE}/qr",
|
|
242
|
+
params={"url": url, "size": size, "format": format, "fg": fg, "bg": bg},
|
|
243
|
+
headers=_headers(),
|
|
244
|
+
timeout=30,
|
|
245
|
+
follow_redirects=True,
|
|
246
|
+
)
|
|
247
|
+
r.raise_for_status()
|
|
248
|
+
return base64.b64encode(r.content).decode()
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
@mcp.tool()
|
|
252
|
+
def generate_hash(data: str, algorithm: str = "sha256") -> dict:
|
|
253
|
+
"""Hash a string with the specified algorithm.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
data: String to hash.
|
|
257
|
+
algorithm: Hash algorithm — sha256, sha512, sha1, md5, sha3_256, blake2b (default sha256).
|
|
258
|
+
"""
|
|
259
|
+
return _get("/hash", data=data, algo=algorithm)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
@mcp.tool()
|
|
263
|
+
def generate_uuid(version: int = 4) -> dict:
|
|
264
|
+
"""Generate a UUID.
|
|
265
|
+
|
|
266
|
+
Args:
|
|
267
|
+
version: UUID version — 1, 4, or 7 (default 4).
|
|
268
|
+
"""
|
|
269
|
+
return _get("/uuid", version=version)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
@mcp.tool()
|
|
273
|
+
def validate_vat(vat_number: str) -> dict:
|
|
274
|
+
"""Validate a European VAT number and return company details.
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
vat_number: VAT number including country prefix (e.g. 'GB123456789').
|
|
278
|
+
"""
|
|
279
|
+
return _get("/vat", vat=vat_number)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
@mcp.tool()
|
|
283
|
+
def validate_iban(iban: str) -> dict:
|
|
284
|
+
"""Validate an IBAN number and return bank details.
|
|
285
|
+
|
|
286
|
+
Args:
|
|
287
|
+
iban: IBAN string (spaces allowed, e.g. 'GB29 NWBK 6016 1331 9268 19').
|
|
288
|
+
"""
|
|
289
|
+
return _get("/iban", iban=iban)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
# validate_phone removed — /phone endpoint not yet live on kiprio.com free tier
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
@mcp.tool()
|
|
296
|
+
def parse_cron(expression: str) -> dict:
|
|
297
|
+
"""Parse a cron expression and return next execution times and human description.
|
|
298
|
+
|
|
299
|
+
Args:
|
|
300
|
+
expression: Cron expression (5 or 6 fields, e.g. '0 9 * * 1').
|
|
301
|
+
"""
|
|
302
|
+
return _get("/cron-parse", expr=expression)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
@mcp.tool()
|
|
306
|
+
def decode_jwt(token: str) -> dict:
|
|
307
|
+
"""Decode a JWT token (without verification) and return header, payload, and expiry status."""
|
|
308
|
+
return _get("/jwt/decode", token=token)
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
@mcp.tool()
|
|
312
|
+
def html_to_pdf(html: str) -> str:
|
|
313
|
+
"""Convert HTML to a PDF and return it as a base64-encoded string.
|
|
314
|
+
|
|
315
|
+
Args:
|
|
316
|
+
html: Full HTML document to convert.
|
|
317
|
+
|
|
318
|
+
Returns: base64-encoded PDF bytes.
|
|
319
|
+
"""
|
|
320
|
+
r = httpx.post(
|
|
321
|
+
f"{BASE}/html-to-pdf/",
|
|
322
|
+
json={"html": html},
|
|
323
|
+
headers=_headers(),
|
|
324
|
+
timeout=60,
|
|
325
|
+
)
|
|
326
|
+
r.raise_for_status()
|
|
327
|
+
return base64.b64encode(r.content).decode()
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
@mcp.tool()
|
|
331
|
+
def check_password_breach(password: str) -> dict:
|
|
332
|
+
"""Check if a password has appeared in known data breaches (k-anonymity — only a 5-char SHA1 prefix is ever sent).
|
|
333
|
+
|
|
334
|
+
Returns: breached (bool), count (times seen in breach databases).
|
|
335
|
+
"""
|
|
336
|
+
return _post("/breach", {"password": password})
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
if __name__ == "__main__":
|
|
340
|
+
mcp.run()
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "kiprio-mcp"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "MCP server for kiprio.com — 23 developer API tools for Claude, Cursor, and any MCP client"
|
|
9
|
+
authors = [{name = "ForeverTools", email = "theworkshop@kiprio.com"}]
|
|
10
|
+
license = "MIT"
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
requires-python = ">=3.10"
|
|
13
|
+
keywords = ["mcp", "api", "developer-tools", "claude", "ai"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Topic :: Software Development :: Libraries",
|
|
20
|
+
]
|
|
21
|
+
dependencies = [
|
|
22
|
+
"mcp>=1.0.0",
|
|
23
|
+
"httpx>=0.27.0",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
Homepage = "https://kiprio.com"
|
|
28
|
+
Repository = "https://github.com/ForeverTools/kiprio-mcp"
|
|
29
|
+
Documentation = "https://kiprio.com/docs/"
|
|
30
|
+
|
|
31
|
+
[project.scripts]
|
|
32
|
+
kiprio-mcp = "kiprio_mcp:mcp.run"
|
|
33
|
+
|
|
34
|
+
[tool.hatch.build.targets.wheel]
|
|
35
|
+
packages = ["kiprio_mcp.py"]
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Smoke tests for kiprio-mcp tools against live kiprio.com API.
|
|
2
|
+
|
|
3
|
+
Run: pytest mcp_server/tests/ -v
|
|
4
|
+
Requires: pip install httpx pytest
|
|
5
|
+
"""
|
|
6
|
+
import sys
|
|
7
|
+
import os
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
# Add mcp_server to path for import
|
|
11
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
|
12
|
+
|
|
13
|
+
import kiprio_mcp as mcp_mod
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_ip_lookup():
|
|
17
|
+
result = mcp_mod.ip_lookup("8.8.8.8")
|
|
18
|
+
assert isinstance(result, dict)
|
|
19
|
+
assert "error" not in result or "API key" not in result.get("error", "")
|
|
20
|
+
# Should have country or similar field
|
|
21
|
+
assert result.get("ip") or result.get("country") or result.get("city")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_uuid_generate():
|
|
25
|
+
result = mcp_mod.generate_uuid(4)
|
|
26
|
+
assert isinstance(result, dict)
|
|
27
|
+
assert "id" in result or "uuid" in result or "value" in result
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_hash_generate():
|
|
31
|
+
result = mcp_mod.generate_hash("hello", "sha256")
|
|
32
|
+
assert isinstance(result, dict)
|
|
33
|
+
assert any(k in result for k in ("hash", "value", "hex", "result"))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_jwt_decode():
|
|
37
|
+
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
|
|
38
|
+
result = mcp_mod.decode_jwt(token)
|
|
39
|
+
assert isinstance(result, dict)
|
|
40
|
+
assert result.get("payload") or result.get("sub") or result.get("name")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_cron_parse():
|
|
44
|
+
result = mcp_mod.parse_cron("0 9 * * 1")
|
|
45
|
+
assert isinstance(result, dict)
|
|
46
|
+
# endpoint returns product info or parsed result — either is acceptable
|
|
47
|
+
assert result # non-empty response
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_email_validate():
|
|
51
|
+
result = mcp_mod.email_validate("test@gmail.com")
|
|
52
|
+
assert isinstance(result, dict)
|
|
53
|
+
# Should have valid field or error from rate limit
|
|
54
|
+
assert "valid" in result or "error" in result
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_sentiment_analysis():
|
|
58
|
+
result = mcp_mod.sentiment_analysis("I love this product, it's amazing!")
|
|
59
|
+
assert isinstance(result, dict)
|
|
60
|
+
assert result.get("label") or result.get("sentiment") or result.get("error")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_grammar_check():
|
|
64
|
+
result = mcp_mod.grammar_check("She go to school yesterday.")
|
|
65
|
+
assert isinstance(result, dict)
|
|
66
|
+
assert result.get("corrected") or result.get("text") or result.get("error")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_summarize():
|
|
70
|
+
text = "The quick brown fox jumps over the lazy dog. This is a test sentence for summarization. It contains multiple sentences of varying length."
|
|
71
|
+
result = mcp_mod.summarize_text(text, max_sentences=2)
|
|
72
|
+
assert isinstance(result, dict)
|
|
73
|
+
assert result.get("summary") or result.get("text") or result.get("error")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def test_password_breach():
|
|
77
|
+
result = mcp_mod.check_password_breach("correct-horse-battery-staple")
|
|
78
|
+
assert isinstance(result, dict)
|
|
79
|
+
assert "breached" in result or "count" in result or "error" in result
|