kntor-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 +179 -0
- package/bin/kntor-mcp.mjs +170 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# Kntor MCP Server
|
|
2
|
+
|
|
3
|
+
Model Context Protocol (MCP) server for Kntor.io Healthcare ERP. Allows AI agents (Claude, n8n, WhatsApp bots) to interact with the ERP system.
|
|
4
|
+
|
|
5
|
+
## Claude Desktop Setup
|
|
6
|
+
|
|
7
|
+
### Option 1: Using npx (Recommended)
|
|
8
|
+
|
|
9
|
+
Add to your Claude Desktop config (`%APPDATA%\Claude\claude_desktop_config.json` on Windows, `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"mcpServers": {
|
|
14
|
+
"kntor-erp": {
|
|
15
|
+
"command": "npx",
|
|
16
|
+
"args": ["-y", "kntor-mcp"],
|
|
17
|
+
"env": {
|
|
18
|
+
"KNTOR_API_KEY": "kntor_your_api_key_here"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Option 2: Using local installation
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"mcpServers": {
|
|
30
|
+
"kntor-erp": {
|
|
31
|
+
"command": "node",
|
|
32
|
+
"args": ["C:\\path\\to\\kntor-mcp-server\\bin\\kntor-mcp.mjs"],
|
|
33
|
+
"env": {
|
|
34
|
+
"KNTOR_API_KEY": "kntor_your_api_key_here"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
After saving the config, restart Claude Desktop.
|
|
42
|
+
|
|
43
|
+
## Server Development
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# Install dependencies
|
|
47
|
+
pnpm install
|
|
48
|
+
|
|
49
|
+
# Set up secrets
|
|
50
|
+
wrangler secret put SUPABASE_URL
|
|
51
|
+
wrangler secret put SUPABASE_ANON_KEY
|
|
52
|
+
wrangler secret put SUPABASE_SERVICE_ROLE_KEY
|
|
53
|
+
|
|
54
|
+
# Run locally
|
|
55
|
+
pnpm run dev
|
|
56
|
+
|
|
57
|
+
# Deploy
|
|
58
|
+
pnpm run deploy
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Authentication
|
|
62
|
+
|
|
63
|
+
The server uses a 3-layer authentication model:
|
|
64
|
+
|
|
65
|
+
1. **API Key** (`x-api-key` header) - Identifies the brand/tenant
|
|
66
|
+
2. **JWT** (in tool calls) - Identifies the user, preserves `auth.uid()`
|
|
67
|
+
3. **RBAC** - Verifies permissions before executing
|
|
68
|
+
|
|
69
|
+
### Getting an API Key
|
|
70
|
+
|
|
71
|
+
API keys are created by brand admins in the Kntor.io dashboard:
|
|
72
|
+
1. Go to Settings → MCP Integration
|
|
73
|
+
2. Click "Create API Key"
|
|
74
|
+
3. Save the key (shown only once)
|
|
75
|
+
|
|
76
|
+
## Available Tools
|
|
77
|
+
|
|
78
|
+
### `get_availability`
|
|
79
|
+
Check professional availability for appointment scheduling.
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"method": "tools/call",
|
|
84
|
+
"params": {
|
|
85
|
+
"name": "get_availability",
|
|
86
|
+
"arguments": {
|
|
87
|
+
"jwt": "eyJhbGciOiJS...",
|
|
88
|
+
"date": "2025-01-20",
|
|
89
|
+
"professional_id": "uuid-optional",
|
|
90
|
+
"profession_type": "psychologist"
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### `schedule_appointment`
|
|
97
|
+
Schedule a new appointment.
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"method": "tools/call",
|
|
102
|
+
"params": {
|
|
103
|
+
"name": "schedule_appointment",
|
|
104
|
+
"arguments": {
|
|
105
|
+
"jwt": "eyJhbGciOiJS...",
|
|
106
|
+
"patient_id": "patient-uuid",
|
|
107
|
+
"professional_id": "prof-uuid",
|
|
108
|
+
"appointment_date": "2025-01-20",
|
|
109
|
+
"start_time": "10:00",
|
|
110
|
+
"duration_minutes": 60
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## API Endpoints
|
|
117
|
+
|
|
118
|
+
| Endpoint | Method | Description |
|
|
119
|
+
|----------|--------|-------------|
|
|
120
|
+
| `/` | GET | Health check |
|
|
121
|
+
| `/health` | GET | Health check |
|
|
122
|
+
| `/mcp` | POST | MCP JSON-RPC endpoint |
|
|
123
|
+
| `/messages` | POST | MCP JSON-RPC endpoint (alias) |
|
|
124
|
+
|
|
125
|
+
## Example Request
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
curl -X POST https://mcp.kntor.io/mcp \
|
|
129
|
+
-H "Content-Type: application/json" \
|
|
130
|
+
-H "x-api-key: kntor_your_api_key_here" \
|
|
131
|
+
-d '{
|
|
132
|
+
"jsonrpc": "2.0",
|
|
133
|
+
"id": 1,
|
|
134
|
+
"method": "tools/list"
|
|
135
|
+
}'
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Pricing Tiers
|
|
139
|
+
|
|
140
|
+
| Tier | Monthly Limit | Price |
|
|
141
|
+
|------|--------------|-------|
|
|
142
|
+
| free | 100 calls | $0 |
|
|
143
|
+
| starter | 1,000 calls | $29/mo |
|
|
144
|
+
| pro | 10,000 calls | $99/mo |
|
|
145
|
+
| enterprise | Unlimited | Custom |
|
|
146
|
+
|
|
147
|
+
## Development
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
# Type check
|
|
151
|
+
pnpm run typecheck
|
|
152
|
+
|
|
153
|
+
# Run locally
|
|
154
|
+
pnpm run dev
|
|
155
|
+
|
|
156
|
+
# Deploy to production
|
|
157
|
+
pnpm run deploy
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Architecture
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
mcp-server/
|
|
164
|
+
├── src/
|
|
165
|
+
│ ├── index.ts # Worker entry point
|
|
166
|
+
│ ├── types.ts # TypeScript types
|
|
167
|
+
│ ├── auth/
|
|
168
|
+
│ │ ├── api-key.ts # API key validation
|
|
169
|
+
│ │ └── jwt.ts # JWT validation + user client
|
|
170
|
+
│ ├── tools/
|
|
171
|
+
│ │ ├── index.ts # Tool registry
|
|
172
|
+
│ │ ├── get-availability.ts
|
|
173
|
+
│ │ └── schedule-appointment.ts
|
|
174
|
+
│ └── utils/
|
|
175
|
+
│ ├── supabase.ts # Supabase client factory
|
|
176
|
+
│ └── metering.ts # Usage logging
|
|
177
|
+
├── wrangler.toml # Cloudflare config
|
|
178
|
+
└── package.json
|
|
179
|
+
```
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Kntor MCP Client
|
|
4
|
+
*
|
|
5
|
+
* Stdio proxy that connects Claude Desktop to Kntor.io ERP.
|
|
6
|
+
* Handles MCP protocol communication over stdin/stdout.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* KNTOR_API_KEY=kntor_xxx npx kntor-mcp
|
|
10
|
+
*
|
|
11
|
+
* Configuration for Claude Desktop (claude_desktop_config.json):
|
|
12
|
+
* {
|
|
13
|
+
* "mcpServers": {
|
|
14
|
+
* "kntor-erp": {
|
|
15
|
+
* "command": "npx",
|
|
16
|
+
* "args": ["-y", "kntor-mcp"],
|
|
17
|
+
* "env": {
|
|
18
|
+
* "KNTOR_API_KEY": "kntor_your_api_key_here"
|
|
19
|
+
* }
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const SERVER_URL = process.env.KNTOR_MCP_URL || 'https://mcp.kntor.io/mcp';
|
|
26
|
+
const API_KEY = process.env.KNTOR_API_KEY;
|
|
27
|
+
|
|
28
|
+
// Validate API key
|
|
29
|
+
if (!API_KEY) {
|
|
30
|
+
const error = {
|
|
31
|
+
jsonrpc: '2.0',
|
|
32
|
+
id: null,
|
|
33
|
+
error: {
|
|
34
|
+
code: -32600,
|
|
35
|
+
message: 'KNTOR_API_KEY environment variable is required. Get your API key at https://kntor.io/settings/mcp'
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
console.log(JSON.stringify(error));
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!API_KEY.startsWith('kntor_')) {
|
|
43
|
+
const error = {
|
|
44
|
+
jsonrpc: '2.0',
|
|
45
|
+
id: null,
|
|
46
|
+
error: {
|
|
47
|
+
code: -32600,
|
|
48
|
+
message: 'Invalid API key format. Key should start with "kntor_"'
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
console.log(JSON.stringify(error));
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Buffer for incomplete JSON messages
|
|
56
|
+
let buffer = '';
|
|
57
|
+
|
|
58
|
+
// Process stdin data
|
|
59
|
+
process.stdin.setEncoding('utf8');
|
|
60
|
+
process.stdin.on('data', async (chunk) => {
|
|
61
|
+
buffer += chunk;
|
|
62
|
+
|
|
63
|
+
// Process complete lines
|
|
64
|
+
const lines = buffer.split('\n');
|
|
65
|
+
buffer = lines.pop() || ''; // Keep incomplete line in buffer
|
|
66
|
+
|
|
67
|
+
for (const line of lines) {
|
|
68
|
+
if (!line.trim()) continue;
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
const message = JSON.parse(line);
|
|
72
|
+
await handleMessage(message);
|
|
73
|
+
} catch (parseError) {
|
|
74
|
+
// Log parse errors to stderr (won't interfere with protocol)
|
|
75
|
+
console.error('[kntor-mcp] Parse error:', parseError.message);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Handle a single MCP message
|
|
81
|
+
async function handleMessage(message) {
|
|
82
|
+
// Preserve the request id (use 0 as fallback, not null - Claude Desktop requires string/number)
|
|
83
|
+
const requestId = message.id !== undefined ? message.id : 0;
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
console.error(`[kntor-mcp] Sending: ${message.method}`);
|
|
87
|
+
|
|
88
|
+
const response = await fetch(SERVER_URL, {
|
|
89
|
+
method: 'POST',
|
|
90
|
+
headers: {
|
|
91
|
+
'Content-Type': 'application/json',
|
|
92
|
+
'x-api-key': API_KEY
|
|
93
|
+
},
|
|
94
|
+
body: JSON.stringify(message)
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const responseText = await response.text();
|
|
98
|
+
console.error(`[kntor-mcp] Response (${response.status}): ${responseText.substring(0, 200)}`);
|
|
99
|
+
|
|
100
|
+
if (!response.ok) {
|
|
101
|
+
let errorData;
|
|
102
|
+
try {
|
|
103
|
+
errorData = JSON.parse(responseText);
|
|
104
|
+
} catch {
|
|
105
|
+
errorData = { message: responseText };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const errorResponse = {
|
|
109
|
+
jsonrpc: '2.0',
|
|
110
|
+
id: requestId,
|
|
111
|
+
error: {
|
|
112
|
+
code: -32000,
|
|
113
|
+
message: `Server error (${response.status}): ${errorData.error?.message || errorData.message || responseText}`
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
console.log(JSON.stringify(errorResponse));
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
let result;
|
|
121
|
+
try {
|
|
122
|
+
result = JSON.parse(responseText);
|
|
123
|
+
} catch {
|
|
124
|
+
const errorResponse = {
|
|
125
|
+
jsonrpc: '2.0',
|
|
126
|
+
id: requestId,
|
|
127
|
+
error: {
|
|
128
|
+
code: -32603,
|
|
129
|
+
message: `Invalid JSON response: ${responseText.substring(0, 100)}`
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
console.log(JSON.stringify(errorResponse));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Ensure the response has the correct id
|
|
137
|
+
if (result.id === null || result.id === undefined) {
|
|
138
|
+
result.id = requestId;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
console.log(JSON.stringify(result));
|
|
142
|
+
|
|
143
|
+
} catch (networkError) {
|
|
144
|
+
console.error(`[kntor-mcp] Network error: ${networkError.message}`);
|
|
145
|
+
const errorResponse = {
|
|
146
|
+
jsonrpc: '2.0',
|
|
147
|
+
id: requestId,
|
|
148
|
+
error: {
|
|
149
|
+
code: -32603,
|
|
150
|
+
message: `Network error: ${networkError.message}`
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
console.log(JSON.stringify(errorResponse));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Handle stdin close
|
|
158
|
+
process.stdin.on('end', () => {
|
|
159
|
+
process.exit(0);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Handle process signals
|
|
163
|
+
process.on('SIGINT', () => process.exit(0));
|
|
164
|
+
process.on('SIGTERM', () => process.exit(0));
|
|
165
|
+
|
|
166
|
+
// Keep process alive
|
|
167
|
+
process.stdin.resume();
|
|
168
|
+
|
|
169
|
+
// Log startup to stderr (doesn't interfere with protocol)
|
|
170
|
+
console.error('[kntor-mcp] Connected to Kntor.io ERP');
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "kntor-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP client for Kntor.io ERP - Connect AI agents to your business management system",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"kntor-mcp": "./bin/kntor-mcp.mjs"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"dev": "wrangler dev",
|
|
15
|
+
"deploy": "wrangler deploy",
|
|
16
|
+
"typecheck": "tsc --noEmit"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"mcp",
|
|
20
|
+
"model-context-protocol",
|
|
21
|
+
"kntor",
|
|
22
|
+
"healthcare",
|
|
23
|
+
"erp",
|
|
24
|
+
"ai",
|
|
25
|
+
"claude"
|
|
26
|
+
],
|
|
27
|
+
"author": "Kntor.io",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/edgargomero/kntor-mcp-server"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=18.0.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@cloudflare/workers-types": "^4.20241127.0",
|
|
38
|
+
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
39
|
+
"typescript": "^5.8.3",
|
|
40
|
+
"wrangler": "^4.59.2",
|
|
41
|
+
"zod": "^3.22.4"
|
|
42
|
+
}
|
|
43
|
+
}
|