katyayani-core-mcp 1.0.0
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.
- package/README.md +56 -0
- package/dist/api.js +66 -0
- package/dist/docs.js +180 -0
- package/dist/index.js +112 -0
- package/package.json +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# katyayani-core-mcp
|
|
2
|
+
|
|
3
|
+
MCP server for **Katyayani Core** (Customer Engagement Platform). Add it to Claude / Cursor / any MCP
|
|
4
|
+
client and ask anything about the platform — live data **and** how the SDKs work.
|
|
5
|
+
|
|
6
|
+
## What you can ask
|
|
7
|
+
|
|
8
|
+
- **Live data** (needs a token): "How many installs from google-play last 30 days?", "Top referrers",
|
|
9
|
+
"Revenue by source", "Show the acquisition funnel", "Find user 6261414316".
|
|
10
|
+
- **How-to / docs** (no token needed): "How do I add referral sharing?", "How to set user attributes?",
|
|
11
|
+
"What does trackAttribution do?", "How to install the Flutter SDK?", "Does the RN SDK auto-ask push permission?"
|
|
12
|
+
|
|
13
|
+
## Tools
|
|
14
|
+
|
|
15
|
+
| Tool | Purpose |
|
|
16
|
+
|------|---------|
|
|
17
|
+
| `kc_docs` | Platform + SDK knowledge (install, identify/attributes, events, attribution, referral, push). Works with **no token**. |
|
|
18
|
+
| `kc_get` | Read live dashboard data (users, attribution, revenue, referrals, funnels…). Needs `KC_TOKEN`. |
|
|
19
|
+
| `kc_action` | Write actions (create smart link, send message…). Needs `KC_TOKEN`. |
|
|
20
|
+
|
|
21
|
+
## Install (Claude Code)
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
claude mcp add katyayani-core \
|
|
25
|
+
-e KC_TOKEN=<shared-token> \
|
|
26
|
+
-e KC_TENANT_ID=<project-tenant-id> \
|
|
27
|
+
-- npx -y katyayani-core-mcp
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
- **`KC_TOKEN`** — the shared access token (ask whoever gave you this MCP). Omit it and only `kc_docs` works.
|
|
31
|
+
- **`KC_TENANT_ID`** *(optional)* — scopes all data to one project. Omit → the API's default project.
|
|
32
|
+
- **`KC_API_BASE`** *(optional)* — defaults to `https://8.231.80.238.nip.io`.
|
|
33
|
+
|
|
34
|
+
### Cursor / other clients (`mcp.json`)
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"mcpServers": {
|
|
39
|
+
"katyayani-core": {
|
|
40
|
+
"command": "npx",
|
|
41
|
+
"args": ["-y", "katyayani-core-mcp"],
|
|
42
|
+
"env": { "KC_TOKEN": "<shared-token>", "KC_TENANT_ID": "<tenant-id>" }
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Security
|
|
49
|
+
|
|
50
|
+
`KC_TOKEN` is a full-access dashboard token — share it only with people you trust. It is **never** baked
|
|
51
|
+
into this package; it is read from your MCP config at runtime. `kc_docs` needs no token, so you can share
|
|
52
|
+
the docs experience openly.
|
|
53
|
+
|
|
54
|
+
## License
|
|
55
|
+
|
|
56
|
+
MIT
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Thin authenticated HTTP layer for the Katyayani Core dashboard API.
|
|
2
|
+
// Auth = a single shared token sent as `Authorization: Bearer <token>`.
|
|
3
|
+
// The default is the platform's static dashboard key; override via KC_TOKEN.
|
|
4
|
+
// KC_TENANT_ID (optional) scopes every call to one project via the x-tenant-id header.
|
|
5
|
+
export const API_BASE = (process.env.KC_API_BASE || 'https://8.231.80.238.nip.io').replace(/\/$/, '');
|
|
6
|
+
// Shared access token — provided by whoever shares this MCP, via the KC_TOKEN env.
|
|
7
|
+
// Intentionally NOT hardcoded so the secret never ships inside the published package.
|
|
8
|
+
const TOKEN = process.env.KC_TOKEN || '';
|
|
9
|
+
export const TENANT_ID = process.env.KC_TENANT_ID || '';
|
|
10
|
+
const NO_TOKEN_MSG = 'No access token set. Live data (kc_get / kc_action) needs the shared KC_TOKEN env var — ' +
|
|
11
|
+
'ask whoever shared this MCP for the token, then set KC_TOKEN in your MCP config. ' +
|
|
12
|
+
'Docs (kc_docs) work without a token.';
|
|
13
|
+
function headers(extra = {}) {
|
|
14
|
+
const h = { authorization: `Bearer ${TOKEN}`, ...extra };
|
|
15
|
+
if (TENANT_ID)
|
|
16
|
+
h['x-tenant-id'] = TENANT_ID;
|
|
17
|
+
return h;
|
|
18
|
+
}
|
|
19
|
+
function normalizePath(path) {
|
|
20
|
+
let p = path.trim();
|
|
21
|
+
if (!p.startsWith('/'))
|
|
22
|
+
p = '/' + p;
|
|
23
|
+
// Be forgiving: allow callers to pass "attribution/overview" or "/v1/..." etc.
|
|
24
|
+
if (!p.startsWith('/v1/') && !p.startsWith('/health') && p !== '/') {
|
|
25
|
+
p = '/v1' + p;
|
|
26
|
+
}
|
|
27
|
+
return p;
|
|
28
|
+
}
|
|
29
|
+
async function parse(res) {
|
|
30
|
+
const text = await res.text();
|
|
31
|
+
try {
|
|
32
|
+
return JSON.parse(text);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return { _status: res.status, _nonJson: text.slice(0, 2000) };
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export async function apiGet(path, params) {
|
|
39
|
+
if (!TOKEN)
|
|
40
|
+
return { error: NO_TOKEN_MSG };
|
|
41
|
+
const p = normalizePath(path);
|
|
42
|
+
let qs = '';
|
|
43
|
+
if (params && Object.keys(params).length) {
|
|
44
|
+
const usp = new URLSearchParams();
|
|
45
|
+
for (const [k, v] of Object.entries(params)) {
|
|
46
|
+
if (v !== undefined && v !== null && v !== '')
|
|
47
|
+
usp.set(k, String(v));
|
|
48
|
+
}
|
|
49
|
+
const s = usp.toString();
|
|
50
|
+
if (s)
|
|
51
|
+
qs = '?' + s;
|
|
52
|
+
}
|
|
53
|
+
const res = await fetch(`${API_BASE}${p}${qs}`, { headers: headers() });
|
|
54
|
+
return parse(res);
|
|
55
|
+
}
|
|
56
|
+
export async function apiSend(method, path, body) {
|
|
57
|
+
if (!TOKEN)
|
|
58
|
+
return { error: NO_TOKEN_MSG };
|
|
59
|
+
const p = normalizePath(path);
|
|
60
|
+
const res = await fetch(`${API_BASE}${p}`, {
|
|
61
|
+
method: method.toUpperCase(),
|
|
62
|
+
headers: headers({ 'content-type': 'application/json' }),
|
|
63
|
+
body: body != null ? JSON.stringify(body) : undefined,
|
|
64
|
+
});
|
|
65
|
+
return parse(res);
|
|
66
|
+
}
|
package/dist/docs.js
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// Bundled Katyayani Core knowledge base. `kc_docs` returns the whole thing (or
|
|
2
|
+
// the sections matching a topic) so anyone can ask how the platform / SDKs work.
|
|
3
|
+
const SECTIONS = [
|
|
4
|
+
{
|
|
5
|
+
title: 'Overview — what Katyayani Core is',
|
|
6
|
+
keywords: ['overview', 'what', 'platform', 'intro', 'about', 'architecture'],
|
|
7
|
+
body: `Katyayani Core is a multi-tenant Customer Engagement Platform (Netcore / Linkrunner-style MMP).
|
|
8
|
+
It ingests events from client SDKs (Flutter, React Native, Web), stitches user identities, tracks
|
|
9
|
+
install attribution + revenue, sends push notifications, and shows everything in a dashboard.
|
|
10
|
+
|
|
11
|
+
- API base URL: https://8.231.80.238.nip.io
|
|
12
|
+
- Dashboard: same host (Next.js). Events/analytics, Attribution, Users, Push, Segments, Workflows.
|
|
13
|
+
- Each project = a "tenant"/app with its own SDK API key (nc_live_...). A key maps to a tenant.
|
|
14
|
+
- Data stores: Postgres (users, links, config), ClickHouse (events, attribution funnel, revenue).`,
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
title: 'SDK install — Flutter, React Native, Web',
|
|
18
|
+
keywords: ['install', 'setup', 'sdk', 'init', 'flutter', 'react native', 'rn', 'web', 'add'],
|
|
19
|
+
body: `All three SDKs talk to the same backend and share the same core API (init, identify, track,
|
|
20
|
+
attribution, capturePayment, referral).
|
|
21
|
+
|
|
22
|
+
FLUTTER (package "katyayani_core" on pub.dev, latest 1.10.0):
|
|
23
|
+
dependencies: katyayani_core: ^1.10.0
|
|
24
|
+
await KatyayaniCore.init(
|
|
25
|
+
siteId: 'nc_live_...', // your SDK API key
|
|
26
|
+
apiBase: 'https://8.231.80.238.nip.io',
|
|
27
|
+
enablePush: false, // set true ONLY if Firebase is configured (see Push section)
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
REACT NATIVE (package "katyayani-core-react-native" on npm, latest 1.3.0):
|
|
31
|
+
npm i katyayani-core-react-native
|
|
32
|
+
await KatyayaniCore.init({ siteId: 'nc_live_...', apiBase: 'https://8.231.80.238.nip.io', enablePush: true });
|
|
33
|
+
|
|
34
|
+
WEB (self-hosted script, global nc(...)):
|
|
35
|
+
<script src="https://8.231.80.238.nip.io/nc.min.js"></script>
|
|
36
|
+
nc('init', { siteId: 'nc_live_...', apiBase: 'https://8.231.80.238.nip.io' });`,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
title: 'Identify & user attributes (traits)',
|
|
40
|
+
keywords: ['identify', 'user', 'attribute', 'attributes', 'traits', 'profile', 'phone', 'email', 'set user'],
|
|
41
|
+
body: `Set user attributes with identify() — there is NO separate setUserAttributes method.
|
|
42
|
+
|
|
43
|
+
KatyayaniCore.identify('+916261414316', traits: {
|
|
44
|
+
'phone': '+916261414316', // recognized → phone column (needed for Users search)
|
|
45
|
+
'email': 'ram@x.com', // recognized → email column
|
|
46
|
+
'name': 'Ram Kumar', // auto-split into firstName/lastName
|
|
47
|
+
'city': 'Indore', // any other key → customAttributes (JSON)
|
|
48
|
+
'plan': 'premium',
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
Recognized keys → dedicated columns: phone, email, name, firstName, lastName, $timezone, $locale.
|
|
52
|
+
Everything else → customAttributes.
|
|
53
|
+
|
|
54
|
+
MERGE BEHAVIOR (important):
|
|
55
|
+
- Standard fields (phone/email/name...) are PATCHED — fields you don't send stay unchanged.
|
|
56
|
+
- customAttributes are REPLACED as a whole IF you send any custom key. Sending {plan:'gold'} alone
|
|
57
|
+
wipes other custom attributes. To update one custom attribute safely, resend all custom attributes,
|
|
58
|
+
OR ask the platform team to enable server-side jsonb merge.
|
|
59
|
+
- Sending only standard fields (no custom keys) leaves customAttributes untouched.
|
|
60
|
+
|
|
61
|
+
You MUST call identify() to create a profile — init() alone keeps the user anonymous.
|
|
62
|
+
Use a stable, readable userId (e.g. '+916261414316'), not a random UUID.`,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
title: 'Events & ecommerce tracking',
|
|
66
|
+
keywords: ['event', 'track', 'ecommerce', 'purchase', 'screen', 'analytics', 'ga4'],
|
|
67
|
+
body: `Track custom events and GA4-aligned ecommerce events.
|
|
68
|
+
|
|
69
|
+
KatyayaniCore.track('button_clicked', properties: {'screen': 'home'});
|
|
70
|
+
KatyayaniCore.screenView('ProductPage');
|
|
71
|
+
|
|
72
|
+
Ecommerce convenience methods (GA4 payload {currency, value, transaction_id, items:[...]}):
|
|
73
|
+
viewItem, addToCart, removeFromCart, viewCart, beginCheckout, addPaymentInfo, purchase, refund.
|
|
74
|
+
e.g. KatyayaniCore.purchase({'currency':'INR','value':1499,'transaction_id':'ORD1','items':[...]});
|
|
75
|
+
|
|
76
|
+
track() = product analytics (Analytics/Events pages). For revenue attribution use capturePayment().`,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
title: 'Attribution & sources (Meta / Google / organic / referral)',
|
|
80
|
+
keywords: ['attribution', 'source', 'meta', 'google', 'ads', 'organic', 'utm', 'install', 'trackattribution', 'funnel', 'campaign', 'smart link'],
|
|
81
|
+
body: `WHICH source a user came from (meta / google / organic / referral) is detected AUTOMATICALLY at
|
|
82
|
+
install time (resolveInstall runs on init). You never pass the source manually. It is derived from:
|
|
83
|
+
1. A Katyayani Core smart link click (/l/<code>) — the link's configured source wins.
|
|
84
|
+
2. Play Store install referrer UTM (utm_source=google, etc.).
|
|
85
|
+
3. Click→install fingerprint match (iOS / no referrer).
|
|
86
|
+
4. Nothing matched → 'organic'.
|
|
87
|
+
|
|
88
|
+
To make Meta/Google show up (not 'organic'), drive installs through a KC SMART LINK per channel
|
|
89
|
+
(Dashboard → Attribution → Links, source=meta/google) OR a Play Store URL tagged with utm_source.
|
|
90
|
+
|
|
91
|
+
FUNNEL milestones are MANUAL — the SDK can't know when "signup" happens:
|
|
92
|
+
KatyayaniCore.trackAttribution('signup'); // ties this conversion to the user's acquired source
|
|
93
|
+
KatyayaniCore.capturePayment(1499, orderId: 'ORD1'); // ties revenue to source
|
|
94
|
+
|
|
95
|
+
Funnel: click → install → signup (trackAttribution) → purchase (capturePayment). The built-in funnel
|
|
96
|
+
only counts type 'signup' as the signup step; other trackAttribution types are stored as raw events.
|
|
97
|
+
|
|
98
|
+
Manual vs auto:
|
|
99
|
+
AUTO → source detection, referral tracking, install/click counts.
|
|
100
|
+
MANUAL→ trackAttribution('signup'), capturePayment(amount), putting a KC smart link in your ads.`,
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
title: 'Referral sharing (referral attribution)',
|
|
104
|
+
keywords: ['referral', 'refer', 'share', 'generatereferrallink', 'onreferral', 'confirmreferral', 'referrer', 'referee', 'invite'],
|
|
105
|
+
body: `Referral flow (app shares a link → new user installs → code auto-links → backend tracks who
|
|
106
|
+
referred whom). Available in all 3 SDKs (Flutter 1.10.0, RN 1.3.0, Web).
|
|
107
|
+
|
|
108
|
+
1) SHARE side (referrer):
|
|
109
|
+
final url = await KatyayaniCore.generateReferralLink(
|
|
110
|
+
myReferralCode, // your app's own code (app-provided)
|
|
111
|
+
referrerId: currentUserId, // sharer's userId
|
|
112
|
+
deepLinkPath: '/referral',
|
|
113
|
+
androidStoreUrl: 'https://play.google.com/store/apps/details?id=com.mymali', // REQUIRED
|
|
114
|
+
);
|
|
115
|
+
Share.share('Join with my code: $url');
|
|
116
|
+
|
|
117
|
+
2) INSTALL side (new user) — auto:
|
|
118
|
+
KatyayaniCore.onReferral((code, params) {
|
|
119
|
+
referralField.text = code; // auto-fill; params['referrerId'] = who referred
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
3) After signup (authoritative):
|
|
123
|
+
KatyayaniCore.confirmReferral(code, referrerId: params['referrerId']);
|
|
124
|
+
|
|
125
|
+
The SDK DELIVERS the code + referrerId to your app via onReferral; YOUR app decides which field to
|
|
126
|
+
fill and how to reward. Reward crediting lives in your backend. Backend records referrer↔referee in
|
|
127
|
+
the referrals table; report at GET /v1/attribution/referrals.
|
|
128
|
+
androidStoreUrl (Play details URL) must be passed once so the install-referrer flow works.
|
|
129
|
+
|
|
130
|
+
Web commands: nc('referral.generate', opts, cb); nc('referral.on', cb); nc('referral.confirm', code, opts); nc('referral.getCode', cb).`,
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
title: 'Push notifications',
|
|
134
|
+
keywords: ['push', 'notification', 'fcm', 'firebase', 'permission', 'enablepush', 'sticky', 'timer'],
|
|
135
|
+
body: `Push uses Firebase (FCM). enablePush defaults to TRUE in the Flutter SDK.
|
|
136
|
+
|
|
137
|
+
- No Firebase in the project? Set enablePush: false — otherwise init() can throw (no default Firebase
|
|
138
|
+
app) and the WHOLE SDK fails to initialize (events/attribution/referral stop too).
|
|
139
|
+
- With enablePush:true the SDK registers the FCM token on startup WITHOUT asking permission
|
|
140
|
+
(getToken works without display permission). It does NOT auto-request notification permission.
|
|
141
|
+
- To actually show notifications, call KatyayaniCore.requestPushPermission() at a good moment
|
|
142
|
+
(needed for Android 13+ POST_NOTIFICATIONS and iOS). Add POST_NOTIFICATIONS to AndroidManifest.
|
|
143
|
+
- Rich push (sticky / timer / custom sound) is Flutter-only. RN has FCM topics + host-rendered nudges.
|
|
144
|
+
- Per-project FCM service account is configured in Dashboard → Settings → App Management.`,
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
title: 'Dashboard data & API endpoints',
|
|
148
|
+
keywords: ['dashboard', 'api', 'endpoint', 'data', 'report', 'query', 'revenue', 'users', 'segments', 'workflows'],
|
|
149
|
+
body: `Read data via kc_get (GET) and mutate via kc_action (POST/PATCH/DELETE). Common paths:
|
|
150
|
+
|
|
151
|
+
Attribution: /v1/attribution/overview?days=30 (per-source clicks/installs/revenue/ROAS)
|
|
152
|
+
/v1/attribution/funnel?days=30
|
|
153
|
+
/v1/attribution/timeseries?days=30
|
|
154
|
+
/v1/attribution/referrals?limit=100 (top referrers)
|
|
155
|
+
/v1/attribution/links (smart links)
|
|
156
|
+
Users: /v1/users?page=1&limit=20&search=... ; /v1/users/:id ; /v1/users/:id/activity
|
|
157
|
+
Analytics: /v1/analytics/overview?days=30 ; /v1/analytics/revenue ; /v1/analytics/events/summary
|
|
158
|
+
/v1/analytics/retention ; /v1/analytics/rfm ; /v1/analytics/geo ; /v1/analytics/devices
|
|
159
|
+
Apps/config: /v1/apps ; /v1/settings/api-keys ; /v1/segments ; /v1/workflows ; /v1/nudges
|
|
160
|
+
Actions: POST /v1/attribution/links (create smart link),
|
|
161
|
+
POST /v1/attribution/referral/generate (needs SDK key, not dashboard token),
|
|
162
|
+
POST /v1/transactional/email|sms|whatsapp, POST /v1/segments.
|
|
163
|
+
|
|
164
|
+
Auth is a single shared Bearer token; KC_TENANT_ID scopes to one project.`,
|
|
165
|
+
},
|
|
166
|
+
];
|
|
167
|
+
const TOC = SECTIONS.map((s, i) => ` ${i + 1}. ${s.title}`).join('\n');
|
|
168
|
+
export function getDocs(topic) {
|
|
169
|
+
const header = `# Katyayani Core — Knowledge Base\n\nSections:\n${TOC}\n\n(Call kc_docs with a topic keyword to narrow, or no topic for everything.)\n\n`;
|
|
170
|
+
if (topic && topic.trim()) {
|
|
171
|
+
const q = topic.toLowerCase();
|
|
172
|
+
const matched = SECTIONS.filter((s) => s.title.toLowerCase().includes(q) ||
|
|
173
|
+
s.keywords.some((k) => q.includes(k) || k.includes(q)));
|
|
174
|
+
if (matched.length) {
|
|
175
|
+
return matched.map((s) => `## ${s.title}\n\n${s.body}`).join('\n\n---\n\n');
|
|
176
|
+
}
|
|
177
|
+
// No match → fall through to full docs so the caller still gets an answer.
|
|
178
|
+
}
|
|
179
|
+
return header + SECTIONS.map((s) => `## ${s.title}\n\n${s.body}`).join('\n\n---\n\n');
|
|
180
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
+
import { apiGet, apiSend, API_BASE, TENANT_ID } from './api.js';
|
|
6
|
+
import { getDocs } from './docs.js';
|
|
7
|
+
const ENDPOINT_CATALOG = `
|
|
8
|
+
Common GET paths (pass as "path", optional "params"):
|
|
9
|
+
Attribution:
|
|
10
|
+
/v1/attribution/overview params: {days} → per-source clicks/installs/users/orders/revenue/ROAS
|
|
11
|
+
/v1/attribution/funnel params: {days} → click→install→signup→purchase counts
|
|
12
|
+
/v1/attribution/timeseries params: {days} → daily revenue/installs by source
|
|
13
|
+
/v1/attribution/referrals params: {limit} → top referrers + totals (who referred whom)
|
|
14
|
+
/v1/attribution/links → all smart links
|
|
15
|
+
/v1/attribution/links/:id/stats params:{days}
|
|
16
|
+
Users:
|
|
17
|
+
/v1/users params: {page,limit,search}
|
|
18
|
+
/v1/users/:id → single profile (traits, customAttributes)
|
|
19
|
+
/v1/users/:id/activity → recent events
|
|
20
|
+
Analytics:
|
|
21
|
+
/v1/analytics/overview params: {days}
|
|
22
|
+
/v1/analytics/revenue params: {days}
|
|
23
|
+
/v1/analytics/events/summary params: {days}
|
|
24
|
+
/v1/analytics/retention | /rfm | /geo | /devices | /realtime | /fatigue
|
|
25
|
+
Config:
|
|
26
|
+
/v1/apps → projects + SDK keys
|
|
27
|
+
/v1/segments | /v1/workflows | /v1/nudges | /v1/settings/api-keys
|
|
28
|
+
`;
|
|
29
|
+
const tools = [
|
|
30
|
+
{
|
|
31
|
+
name: 'kc_docs',
|
|
32
|
+
description: 'Explain how Katyayani Core (the Customer Engagement Platform) and its SDKs work. Covers: platform overview, SDK install (Flutter/React Native/Web), identify & user attributes, events/ecommerce, attribution & sources (Meta/Google/organic/referral), the referral system, push notifications, and the dashboard/API. Use for any "how do I / what is / how does X work" question. Pass an optional topic keyword to focus.',
|
|
33
|
+
inputSchema: {
|
|
34
|
+
type: 'object',
|
|
35
|
+
properties: {
|
|
36
|
+
topic: {
|
|
37
|
+
type: 'string',
|
|
38
|
+
description: 'Optional keyword to focus the docs, e.g. "referral", "attribution", "user attributes", "push", "install", "events". Omit to get everything.',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: 'kc_get',
|
|
45
|
+
description: 'Read LIVE data from the Katyayani Core dashboard API (GET). Use for any question about real numbers: users, events, attribution by source, revenue/ROAS, funnels, referrals, segments, workflows, etc. Provide the API "path" and optional "params". ' +
|
|
46
|
+
ENDPOINT_CATALOG,
|
|
47
|
+
inputSchema: {
|
|
48
|
+
type: 'object',
|
|
49
|
+
properties: {
|
|
50
|
+
path: {
|
|
51
|
+
type: 'string',
|
|
52
|
+
description: 'API path, e.g. "/v1/attribution/overview" or "/v1/users". "/v1" is added if you omit it.',
|
|
53
|
+
},
|
|
54
|
+
params: {
|
|
55
|
+
type: 'object',
|
|
56
|
+
description: 'Optional query params, e.g. {"days": 30} or {"search": "6261", "limit": 20}.',
|
|
57
|
+
additionalProperties: true,
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
required: ['path'],
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: 'kc_action',
|
|
65
|
+
description: 'Perform a WRITE action on Katyayani Core (POST/PATCH/DELETE) — e.g. create a smart link (POST /v1/attribution/links), create a segment, send a transactional message. This MUTATES data; use only when the user explicitly asks to create/update/send something. Prefer kc_get for reads.',
|
|
66
|
+
inputSchema: {
|
|
67
|
+
type: 'object',
|
|
68
|
+
properties: {
|
|
69
|
+
method: { type: 'string', enum: ['POST', 'PATCH', 'DELETE'], description: 'HTTP method (default POST).' },
|
|
70
|
+
path: { type: 'string', description: 'API path, e.g. "/v1/attribution/links".' },
|
|
71
|
+
body: { type: 'object', description: 'JSON body for the request.', additionalProperties: true },
|
|
72
|
+
},
|
|
73
|
+
required: ['path'],
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
];
|
|
77
|
+
const server = new Server({ name: 'katyayani-core', version: '1.0.0' }, { capabilities: { tools: {} } });
|
|
78
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));
|
|
79
|
+
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
80
|
+
const { name } = req.params;
|
|
81
|
+
const args = (req.params.arguments || {});
|
|
82
|
+
try {
|
|
83
|
+
if (name === 'kc_docs') {
|
|
84
|
+
return { content: [{ type: 'text', text: getDocs(args.topic) }] };
|
|
85
|
+
}
|
|
86
|
+
if (name === 'kc_get') {
|
|
87
|
+
if (!args.path)
|
|
88
|
+
throw new Error('path is required');
|
|
89
|
+
const data = await apiGet(String(args.path), args.params);
|
|
90
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
91
|
+
}
|
|
92
|
+
if (name === 'kc_action') {
|
|
93
|
+
if (!args.path)
|
|
94
|
+
throw new Error('path is required');
|
|
95
|
+
const data = await apiSend(String(args.method || 'POST'), String(args.path), args.body);
|
|
96
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
97
|
+
}
|
|
98
|
+
return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true };
|
|
99
|
+
}
|
|
100
|
+
catch (e) {
|
|
101
|
+
return { content: [{ type: 'text', text: `Error: ${e?.message || String(e)}` }], isError: true };
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
async function main() {
|
|
105
|
+
const transport = new StdioServerTransport();
|
|
106
|
+
await server.connect(transport);
|
|
107
|
+
console.error(`katyayani-core MCP running → ${API_BASE}${TENANT_ID ? ` (tenant ${TENANT_ID})` : ''}`);
|
|
108
|
+
}
|
|
109
|
+
main().catch((e) => {
|
|
110
|
+
console.error('katyayani-core MCP failed to start:', e);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "katyayani-core-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for Katyayani Core (Customer Engagement Platform). Ask anything about your CEP — users, events, attribution (Meta/Google/organic/referral), revenue/ROAS, referrals, funnels — plus full SDK & platform how-to docs.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"katyayani-core-mcp": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"start": "node dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"mcp",
|
|
18
|
+
"model-context-protocol",
|
|
19
|
+
"katyayani-core",
|
|
20
|
+
"analytics",
|
|
21
|
+
"attribution",
|
|
22
|
+
"claude"
|
|
23
|
+
],
|
|
24
|
+
"author": "Katyayani Organics",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@modelcontextprotocol/sdk": "^1.12.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^22.10.0",
|
|
31
|
+
"typescript": "^5.7.0"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=18"
|
|
35
|
+
}
|
|
36
|
+
}
|