@honkio/mcp 1.4.0 → 1.6.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 +193 -65
- package/dist/auth.d.ts +40 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +98 -0
- package/dist/auth.js.map +1 -0
- package/dist/client.d.ts +43 -5
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +97 -22
- package/dist/client.js.map +1 -1
- package/dist/http.d.ts +51 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +248 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +0 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -46
- package/dist/index.js.map +1 -1
- package/dist/log.d.ts +10 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +10 -0
- package/dist/log.js.map +1 -0
- package/dist/prompts.d.ts +1 -1
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +23 -23
- package/dist/prompts.js.map +1 -1
- package/dist/rateLimit.d.ts +50 -0
- package/dist/rateLimit.d.ts.map +1 -0
- package/dist/rateLimit.js +85 -0
- package/dist/rateLimit.js.map +1 -0
- package/dist/resources.d.ts +1 -1
- package/dist/resources.d.ts.map +1 -1
- package/dist/resources.js +4 -4
- package/dist/resources.js.map +1 -1
- package/dist/server.d.ts +7 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +46 -0
- package/dist/server.js.map +1 -0
- package/dist/toolMeta.d.ts +20 -0
- package/dist/toolMeta.d.ts.map +1 -0
- package/dist/toolMeta.js +81 -0
- package/dist/toolMeta.js.map +1 -0
- package/dist/tools.d.ts +10 -1
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +344 -209
- package/dist/tools.js.map +1 -1
- package/package.json +16 -7
package/dist/http.js
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* HonkIO MCP server, hosted Streamable HTTP transport: what mcp.honkio.ca runs.
|
|
4
|
+
*
|
|
5
|
+
* POST /mcp the MCP endpoint (Origin check, then API-key gate, then MCP)
|
|
6
|
+
* GET /healthz liveness for Railway
|
|
7
|
+
*
|
|
8
|
+
* Every request builds a fresh McpServer around a HonkioClient carrying the
|
|
9
|
+
* caller's key, so nothing is shared between callers and the process holds no
|
|
10
|
+
* session state. The verifier cache is the only thing that outlives a request.
|
|
11
|
+
*/
|
|
12
|
+
import { createServer } from 'node:http';
|
|
13
|
+
import { pathToFileURL } from 'node:url';
|
|
14
|
+
import { createMcpHandler } from '@modelcontextprotocol/server';
|
|
15
|
+
import { toNodeHandler } from '@modelcontextprotocol/node';
|
|
16
|
+
import { HonkioClient } from './client.js';
|
|
17
|
+
import { buildServer, PACKAGE_VERSION } from './server.js';
|
|
18
|
+
import { extractApiKey, KeyVerifier } from './auth.js';
|
|
19
|
+
import { createLogger } from './log.js';
|
|
20
|
+
import { clientIp, RateLimiter } from './rateLimit.js';
|
|
21
|
+
const MAX_BODY_BYTES = 1_000_000;
|
|
22
|
+
export const DEFAULT_REQUESTS_PER_MINUTE = 300;
|
|
23
|
+
export const DEFAULT_FAILURES_PER_MINUTE = 10;
|
|
24
|
+
/** Keep in step with api/src/services/oauth/scopes.ts. */
|
|
25
|
+
export const SCOPES = [
|
|
26
|
+
'messages:read', 'messages:send', 'numbers:read', 'numbers:manage', 'compliance:read', 'compliance:write',
|
|
27
|
+
'verify', 'webhooks:read', 'webhooks:manage', 'account:read', 'account:manage',
|
|
28
|
+
];
|
|
29
|
+
const DEFAULT_SCOPE = SCOPES.filter((s) => s !== 'account:manage').join(' ');
|
|
30
|
+
export const DEFAULT_PUBLIC_URL = 'https://mcp.honkio.ca/mcp';
|
|
31
|
+
function sendJson(res, status, body, headers = {}) {
|
|
32
|
+
res.writeHead(status, { 'Content-Type': 'application/json', ...headers });
|
|
33
|
+
res.end(JSON.stringify(body));
|
|
34
|
+
}
|
|
35
|
+
class PayloadTooLarge extends Error {
|
|
36
|
+
}
|
|
37
|
+
function readBody(req, limit) {
|
|
38
|
+
return new Promise((resolve, reject) => {
|
|
39
|
+
const chunks = [];
|
|
40
|
+
let size = 0;
|
|
41
|
+
req.on('data', (chunk) => {
|
|
42
|
+
size += chunk.length;
|
|
43
|
+
if (size > limit) {
|
|
44
|
+
reject(new PayloadTooLarge());
|
|
45
|
+
req.destroy();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
chunks.push(chunk);
|
|
49
|
+
});
|
|
50
|
+
req.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
|
|
51
|
+
req.on('error', reject);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
function rpcShape(body) {
|
|
55
|
+
if (!body || typeof body !== 'object' || Array.isArray(body))
|
|
56
|
+
return {};
|
|
57
|
+
const { method, params } = body;
|
|
58
|
+
const name = params?.name ?? params?.uri;
|
|
59
|
+
return {
|
|
60
|
+
method: typeof method === 'string' ? method : undefined,
|
|
61
|
+
name: typeof name === 'string' ? name : undefined,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export function createHttpServer(opts) {
|
|
65
|
+
const logger = opts.logger ?? createLogger();
|
|
66
|
+
const verifier = opts.verifier ?? new KeyVerifier({ apiBaseUrl: opts.apiBaseUrl, serviceKey: opts.serviceKey });
|
|
67
|
+
const publicUrl = opts.publicUrl ?? DEFAULT_PUBLIC_URL;
|
|
68
|
+
const resourceMetadata = {
|
|
69
|
+
resource: publicUrl,
|
|
70
|
+
authorization_servers: [opts.apiBaseUrl],
|
|
71
|
+
bearer_methods_supported: ['header'],
|
|
72
|
+
scopes_supported: [...SCOPES],
|
|
73
|
+
resource_documentation: 'https://honkio.ca/docs#mcp',
|
|
74
|
+
};
|
|
75
|
+
/** The metadata URL on the origin the caller actually used, so a test host and the public host both resolve. */
|
|
76
|
+
const metadataUrlFor = (req) => {
|
|
77
|
+
const proto = typeof req.headers['x-forwarded-proto'] === 'string' ? req.headers['x-forwarded-proto'] : 'http';
|
|
78
|
+
return `${proto}://${req.headers.host ?? 'localhost'}/.well-known/oauth-protected-resource/mcp`;
|
|
79
|
+
};
|
|
80
|
+
const challenge = (req, error) => `Bearer realm="honkio"${error ? `, error="${error}"` : ''}, resource_metadata="${metadataUrlFor(req)}", scope="${DEFAULT_SCOPE}"`;
|
|
81
|
+
const allowedOrigins = new Set(opts.allowedOrigins ?? []);
|
|
82
|
+
const requestsPerMinute = opts.rateLimit?.requestsPerMinute ?? DEFAULT_REQUESTS_PER_MINUTE;
|
|
83
|
+
const failuresPerMinute = opts.rateLimit?.failuresPerMinute ?? DEFAULT_FAILURES_PER_MINUTE;
|
|
84
|
+
const requests = new RateLimiter({ limit: requestsPerMinute });
|
|
85
|
+
const failures = new RateLimiter({ limit: failuresPerMinute });
|
|
86
|
+
const trustedProxies = opts.trustedProxies ?? 1;
|
|
87
|
+
const handler = createMcpHandler(({ authInfo }) => {
|
|
88
|
+
if (!authInfo)
|
|
89
|
+
throw new Error('MCP handler reached without authInfo; the gate must run first');
|
|
90
|
+
const extra = (authInfo.extra ?? {});
|
|
91
|
+
return buildServer(new HonkioClient({
|
|
92
|
+
apiKey: authInfo.token,
|
|
93
|
+
baseUrl: opts.apiBaseUrl,
|
|
94
|
+
accountId: extra.accountId,
|
|
95
|
+
serviceKey: extra.credential === 'token' ? opts.serviceKey : undefined,
|
|
96
|
+
}));
|
|
97
|
+
}, {
|
|
98
|
+
// Default responseMode ('auto'): a single JSON body unless a handler emits a
|
|
99
|
+
// notification first, which none of ours do. Forcing 'json' logs a warning on boot.
|
|
100
|
+
onerror: (err) => logger.error('mcp handler error', { error: err.message }),
|
|
101
|
+
});
|
|
102
|
+
const mcp = toNodeHandler(handler, {
|
|
103
|
+
onerror: (err) => logger.error('mcp adapter error', { error: err.message, stack: err.stack }),
|
|
104
|
+
});
|
|
105
|
+
/**
|
|
106
|
+
* A refusal before MCP is reached. Logged with its reason so abuse and
|
|
107
|
+
* misconfiguration are visible. When an address is given, the refusal also
|
|
108
|
+
* counts toward that address's failure budget.
|
|
109
|
+
*/
|
|
110
|
+
function reject(res, status, reason, message, headers = {}, ip) {
|
|
111
|
+
if (ip && status !== 429)
|
|
112
|
+
failures.hit(ip);
|
|
113
|
+
logger.info('mcp rejected', { status, reason, ...(ip ? { ip, forwardedFor: res.req.headers['x-forwarded-for'] } : {}) });
|
|
114
|
+
sendJson(res, status, { error: reason, message }, headers);
|
|
115
|
+
}
|
|
116
|
+
async function route(req, res) {
|
|
117
|
+
const started = Date.now();
|
|
118
|
+
const path = (req.url ?? '/').split('?')[0];
|
|
119
|
+
if (path === '/healthz' && req.method === 'GET') {
|
|
120
|
+
return sendJson(res, 200, { ok: true, version: PACKAGE_VERSION });
|
|
121
|
+
}
|
|
122
|
+
if ((path === '/.well-known/oauth-protected-resource' || path === '/.well-known/oauth-protected-resource/mcp') && req.method === 'GET') {
|
|
123
|
+
return sendJson(res, 200, resourceMetadata, { 'Access-Control-Allow-Origin': '*', 'Cache-Control': 'public, max-age=300' });
|
|
124
|
+
}
|
|
125
|
+
if (path !== '/mcp') {
|
|
126
|
+
return sendJson(res, 404, { error: 'not_found', message: 'The MCP endpoint is POST /mcp.' });
|
|
127
|
+
}
|
|
128
|
+
if (req.method !== 'POST') {
|
|
129
|
+
return reject(res, 405, 'method_not_allowed', 'The MCP endpoint accepts POST only.', { Allow: 'POST' });
|
|
130
|
+
}
|
|
131
|
+
const ip = clientIp(req, trustedProxies);
|
|
132
|
+
const budget = requests.hit(ip);
|
|
133
|
+
if (!budget.allowed) {
|
|
134
|
+
return reject(res, 429, 'rate_limited', `More than ${requestsPerMinute} requests a minute from one address. Retry in ${budget.retryAfterSec} s.`, { 'Retry-After': String(budget.retryAfterSec) }, ip);
|
|
135
|
+
}
|
|
136
|
+
const lockout = failures.blocked(ip);
|
|
137
|
+
if (!lockout.allowed) {
|
|
138
|
+
return reject(res, 429, 'too_many_failures', `More than ${failuresPerMinute} refused requests a minute from one address. Retry in ${lockout.retryAfterSec} s.`, { 'Retry-After': String(lockout.retryAfterSec) }, ip);
|
|
139
|
+
}
|
|
140
|
+
const origin = req.headers.origin;
|
|
141
|
+
if (typeof origin === 'string' && !allowedOrigins.has(origin)) {
|
|
142
|
+
return reject(res, 403, 'origin_forbidden', `Origin ${origin} is not allowed to call this endpoint.`, {}, ip);
|
|
143
|
+
}
|
|
144
|
+
const key = extractApiKey(req.headers);
|
|
145
|
+
if (!key) {
|
|
146
|
+
return reject(res, 401, 'unauthorized', 'Send your HonkIO API key as "Authorization: Bearer mk_live_..." (or mk_test_...).', { 'WWW-Authenticate': challenge(req) }, ip);
|
|
147
|
+
}
|
|
148
|
+
const verdict = await verifier.verify(key);
|
|
149
|
+
if (verdict.status === 'invalid') {
|
|
150
|
+
return reject(res, 401, 'unauthorized', 'Invalid or revoked API key.', { 'WWW-Authenticate': challenge(req, 'invalid_token') }, ip);
|
|
151
|
+
}
|
|
152
|
+
if (verdict.status === 'unavailable') {
|
|
153
|
+
return reject(res, 503, 'api_unavailable', 'The HonkIO API could not verify the key. Retry shortly.');
|
|
154
|
+
}
|
|
155
|
+
let raw;
|
|
156
|
+
try {
|
|
157
|
+
raw = await readBody(req, MAX_BODY_BYTES);
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
if (err instanceof PayloadTooLarge) {
|
|
161
|
+
return reject(res, 413, 'payload_too_large', 'The request body exceeds 1 MB.', {}, ip);
|
|
162
|
+
}
|
|
163
|
+
throw err;
|
|
164
|
+
}
|
|
165
|
+
let body;
|
|
166
|
+
try {
|
|
167
|
+
body = JSON.parse(raw);
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
return reject(res, 400, 'invalid_json', 'The request body must be a JSON-RPC message.', {}, ip);
|
|
171
|
+
}
|
|
172
|
+
const { method: mcpMethod, name: mcpName } = rpcShape(body);
|
|
173
|
+
res.on('finish', () => {
|
|
174
|
+
logger.info('mcp request', {
|
|
175
|
+
mcpMethod,
|
|
176
|
+
mcpName,
|
|
177
|
+
protocolVersion: req.headers['mcp-protocol-version'],
|
|
178
|
+
account: verdict.accountId,
|
|
179
|
+
mode: verdict.isTestMode ? 'test' : 'live',
|
|
180
|
+
status: res.statusCode,
|
|
181
|
+
ms: Date.now() - started,
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
req.auth = verdict.authInfo;
|
|
185
|
+
await mcp(req, res, body);
|
|
186
|
+
}
|
|
187
|
+
const server = createServer((req, res) => {
|
|
188
|
+
route(req, res).catch((err) => {
|
|
189
|
+
logger.error('unhandled request error', { error: err instanceof Error ? err.message : String(err) });
|
|
190
|
+
if (!res.headersSent)
|
|
191
|
+
sendJson(res, 500, { error: 'internal_error' });
|
|
192
|
+
else
|
|
193
|
+
res.end();
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
server.requestTimeout = 30_000;
|
|
197
|
+
return {
|
|
198
|
+
server,
|
|
199
|
+
close: async () => {
|
|
200
|
+
await handler.close();
|
|
201
|
+
await new Promise((resolve, reject) => server.close((err) => (err ? reject(err) : resolve())));
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
export async function startHttpServer(opts) {
|
|
206
|
+
const handle = createHttpServer(opts);
|
|
207
|
+
const host = opts.host ?? '0.0.0.0';
|
|
208
|
+
await new Promise((resolve) => handle.server.listen(opts.port, host, resolve));
|
|
209
|
+
const { port } = handle.server.address();
|
|
210
|
+
const urlHost = host === '0.0.0.0' ? '127.0.0.1' : host;
|
|
211
|
+
return { url: `http://${urlHost}:${port}`, port, close: handle.close };
|
|
212
|
+
}
|
|
213
|
+
function main() {
|
|
214
|
+
const logger = createLogger();
|
|
215
|
+
const port = Number(process.env['PORT'] ?? 8080);
|
|
216
|
+
const apiBaseUrl = process.env['API_URL'] ?? process.env['HONKIO_API_URL'] ?? 'https://api.honkio.ca';
|
|
217
|
+
const allowedOrigins = (process.env['MCP_ALLOWED_ORIGINS'] ?? '')
|
|
218
|
+
.split(',')
|
|
219
|
+
.map((s) => s.trim())
|
|
220
|
+
.filter(Boolean);
|
|
221
|
+
const positive = (raw, fallback) => {
|
|
222
|
+
const n = Number(raw);
|
|
223
|
+
return Number.isInteger(n) && n > 0 ? n : fallback;
|
|
224
|
+
};
|
|
225
|
+
const rateLimit = {
|
|
226
|
+
requestsPerMinute: positive(process.env['MCP_RATE_LIMIT_PER_MINUTE'], DEFAULT_REQUESTS_PER_MINUTE),
|
|
227
|
+
failuresPerMinute: positive(process.env['MCP_AUTH_FAILURES_PER_MINUTE'], DEFAULT_FAILURES_PER_MINUTE),
|
|
228
|
+
};
|
|
229
|
+
const trustedEnv = Number(process.env['MCP_TRUSTED_PROXIES']);
|
|
230
|
+
const trustedProxies = Number.isInteger(trustedEnv) && trustedEnv >= 0 ? trustedEnv : 1;
|
|
231
|
+
const publicUrl = process.env['MCP_PUBLIC_URL'] ?? DEFAULT_PUBLIC_URL;
|
|
232
|
+
const serviceKey = process.env['MCP_SERVICE_KEY'] || undefined;
|
|
233
|
+
void startHttpServer({ apiBaseUrl, allowedOrigins, logger, port, rateLimit, trustedProxies, publicUrl, serviceKey }).then((running) => {
|
|
234
|
+
logger.info('listening', { port: running.port, apiBaseUrl, allowedOrigins, rateLimit, trustedProxies, publicUrl, oauth: Boolean(serviceKey), version: PACKAGE_VERSION });
|
|
235
|
+
const shutdown = (signal) => {
|
|
236
|
+
logger.info('shutting down', { signal });
|
|
237
|
+
const force = setTimeout(() => process.exit(1), 10_000);
|
|
238
|
+
force.unref();
|
|
239
|
+
void running.close().then(() => process.exit(0));
|
|
240
|
+
};
|
|
241
|
+
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
|
242
|
+
process.on('SIGINT', () => shutdown('SIGINT'));
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
246
|
+
main();
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=http.js.map
|
package/dist/http.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* HonkIO MCP Server
|
|
4
|
-
*
|
|
5
|
-
* Exposes the HonkIO SMS API as an MCP server for AI coding agents.
|
|
6
|
-
* Supports stdio transport (Claude Desktop, Cursor, VS Code, etc.)
|
|
7
|
-
*
|
|
8
|
-
* Usage:
|
|
9
|
-
* HONKIO_API_KEY=mk_live_... node dist/index.js
|
|
10
|
-
*/
|
|
11
2
|
export {};
|
|
12
3
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
CHANGED
|
@@ -1,56 +1,29 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* HonkIO MCP
|
|
3
|
+
* HonkIO MCP server, stdio transport: what `npx -y @honkio/mcp` runs inside
|
|
4
|
+
* Claude Code, Claude Desktop, Cursor and VS Code.
|
|
4
5
|
*
|
|
5
|
-
*
|
|
6
|
-
* Supports stdio transport (Claude Desktop, Cursor, VS Code, etc.)
|
|
6
|
+
* HONKIO_API_KEY=mk_test_... node dist/index.js
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
* HONKIO_API_KEY=mk_live_... node dist/index.js
|
|
8
|
+
* Diagnostics go to stderr; stdout is the protocol channel and must stay clean.
|
|
10
9
|
*/
|
|
11
|
-
import {
|
|
12
|
-
import { fileURLToPath } from 'node:url';
|
|
13
|
-
import { dirname, resolve } from 'node:path';
|
|
14
|
-
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
15
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
10
|
+
import { serveStdio } from '@modelcontextprotocol/server/stdio';
|
|
16
11
|
import { HonkioClient } from './client.js';
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
try {
|
|
23
|
-
const pkg = JSON.parse(readFileSync(resolve(__dirname, '../package.json'), 'utf8'));
|
|
24
|
-
return pkg.version ?? 'unknown';
|
|
25
|
-
}
|
|
26
|
-
catch {
|
|
27
|
-
return 'unknown';
|
|
28
|
-
}
|
|
29
|
-
})();
|
|
30
|
-
function getEnv(key) {
|
|
31
|
-
const val = process.env[key];
|
|
32
|
-
if (!val) {
|
|
33
|
-
console.error(`[honkio-mcp] Missing required environment variable: ${key}`);
|
|
34
|
-
process.exit(1);
|
|
35
|
-
}
|
|
36
|
-
return val;
|
|
12
|
+
import { buildServer } from './server.js';
|
|
13
|
+
const apiKey = process.env['HONKIO_API_KEY'];
|
|
14
|
+
if (!apiKey) {
|
|
15
|
+
console.error('[honkio-mcp] Missing required environment variable: HONKIO_API_KEY');
|
|
16
|
+
process.exit(1);
|
|
37
17
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
18
|
+
// HONKIO_API_URL is the pre-1.5 name; still honoured so existing local overrides keep working.
|
|
19
|
+
const baseUrl = process.env['API_URL'] ?? process.env['HONKIO_API_URL'] ?? 'https://api.honkio.ca';
|
|
20
|
+
const client = new HonkioClient({ apiKey, baseUrl });
|
|
21
|
+
const handle = serveStdio(() => buildServer(client), {
|
|
22
|
+
onerror: (err) => console.error('[honkio-mcp]', err.message),
|
|
23
|
+
});
|
|
24
|
+
for (const signal of ['SIGINT', 'SIGTERM']) {
|
|
25
|
+
process.on(signal, () => {
|
|
26
|
+
void handle.close().finally(() => process.exit(0));
|
|
45
27
|
});
|
|
46
|
-
registerTools(server, client);
|
|
47
|
-
registerResources(server, client);
|
|
48
|
-
registerPrompts(server);
|
|
49
|
-
const transport = new StdioServerTransport();
|
|
50
|
-
await server.connect(transport);
|
|
51
28
|
}
|
|
52
|
-
main().catch((err) => {
|
|
53
|
-
console.error('[honkio-mcp] Fatal error:', err);
|
|
54
|
-
process.exit(1);
|
|
55
|
-
});
|
|
56
29
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;AAC7C,IAAI,CAAC,MAAM,EAAE,CAAC;IACZ,OAAO,CAAC,KAAK,CAAC,oEAAoE,CAAC,CAAC;IACpF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AACD,+FAA+F;AAC/F,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,uBAAuB,CAAC;AACnG,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;AAErD,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE;IACnD,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,GAAG,CAAC,OAAO,CAAC;CAC7D,CAAC,CAAC;AAEH,KAAK,MAAM,MAAM,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAU,EAAE,CAAC;IACpD,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;QACtB,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/log.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** One JSON object per line on stdout. Used by the HTTP entry only; stdio must keep stdout for the protocol. */
|
|
2
|
+
export type LogFields = Record<string, unknown>;
|
|
3
|
+
export interface Logger {
|
|
4
|
+
info(msg: string, fields?: LogFields): void;
|
|
5
|
+
error(msg: string, fields?: LogFields): void;
|
|
6
|
+
}
|
|
7
|
+
export declare function createLogger(stream?: {
|
|
8
|
+
write(chunk: string): unknown;
|
|
9
|
+
}): Logger;
|
|
10
|
+
//# sourceMappingURL=log.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAAA,gHAAgH;AAChH,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEhD,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IAC5C,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;CAC9C;AAED,wBAAgB,YAAY,CAAC,MAAM,GAAE;IAAE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAA;CAAmB,GAAG,MAAM,CAQ/F"}
|
package/dist/log.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function createLogger(stream = process.stdout) {
|
|
2
|
+
const emit = (level, msg, fields) => {
|
|
3
|
+
stream.write(`${JSON.stringify({ ts: new Date().toISOString(), level, msg, ...fields })}\n`);
|
|
4
|
+
};
|
|
5
|
+
return {
|
|
6
|
+
info: (msg, fields) => emit('info', msg, fields),
|
|
7
|
+
error: (msg, fields) => emit('error', msg, fields),
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=log.js.map
|
package/dist/log.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.js","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAQA,MAAM,UAAU,YAAY,CAAC,SAA4C,OAAO,CAAC,MAAM;IACrF,MAAM,IAAI,GAAG,CAAC,KAAuB,EAAE,GAAW,EAAE,MAAkB,EAAE,EAAE;QACxE,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC;IAC/F,CAAC,CAAC;IACF,OAAO;QACL,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC;QAChD,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC;KACnD,CAAC;AACJ,CAAC"}
|
package/dist/prompts.d.ts
CHANGED
package/dist/prompts.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAGzD,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAiHvD"}
|
package/dist/prompts.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export function registerPrompts(server) {
|
|
3
|
-
server.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
server.registerPrompt('send_compliant_sms', { description: 'Walk through the full compliant SMS send workflow: check CASL consent and send the message. (CRTC DNCL checking is coming soon and not yet enforced.)', argsSchema: z.object({
|
|
4
|
+
phone_number: z.string().describe('Recipient phone number in E.164 format (+1 and ten digits). Use the real recipient: 555-XXXX exchanges are reserved and a send to one is refused.'),
|
|
5
|
+
from_number: z.string().describe('Your sending phone number in E.164 format.'),
|
|
6
|
+
message: z.string().describe('The message body you want to send.'),
|
|
7
|
+
}) }, ({ phone_number, from_number, message }) => ({
|
|
8
8
|
messages: [{
|
|
9
9
|
role: 'user',
|
|
10
10
|
content: {
|
|
@@ -26,9 +26,9 @@ Show me the result of each step.`,
|
|
|
26
26
|
},
|
|
27
27
|
}],
|
|
28
28
|
}));
|
|
29
|
-
server.
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
server.registerPrompt('provision_canadian_number', { description: 'Search for and provision a Canadian phone number, optionally filtered by area code.', argsSchema: z.object({
|
|
30
|
+
area_code: z.string().optional().describe('Preferred area code (e.g. "416" for Toronto, "604" for Vancouver). Leave blank to search all available Canadian numbers.'),
|
|
31
|
+
}) }, ({ area_code }) => ({
|
|
32
32
|
messages: [{
|
|
33
33
|
role: 'user',
|
|
34
34
|
content: {
|
|
@@ -44,9 +44,9 @@ Please:
|
|
|
44
44
|
},
|
|
45
45
|
}],
|
|
46
46
|
}));
|
|
47
|
-
server.
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
server.registerPrompt('setup_webhooks', { description: 'Set up webhook endpoints to receive HonkIO event notifications.', argsSchema: z.object({
|
|
48
|
+
url: z.string().describe('Your HTTPS endpoint URL that will receive webhook events.'),
|
|
49
|
+
}) }, ({ url }) => ({
|
|
50
50
|
messages: [{
|
|
51
51
|
role: 'user',
|
|
52
52
|
content: {
|
|
@@ -61,14 +61,14 @@ Please:
|
|
|
61
61
|
- message.received (to handle inbound SMS replies)
|
|
62
62
|
- opt_out.recorded (to sync opt-outs to your CRM)
|
|
63
63
|
- account.sending_paused and account.delivery_warning (to hear about a pause or a failure spike before customers do)
|
|
64
|
-
3. Display the signing_secret from the response
|
|
64
|
+
3. Display the signing_secret from the response: remind me to store it securely as it won't be shown again. Explain that I should use it to verify the X-HonkIO-Signature header on incoming webhook requests.
|
|
65
65
|
4. If webhooks already exist, show me the existing ones and ask if I want to create an additional one or update an existing one.`,
|
|
66
66
|
},
|
|
67
67
|
}],
|
|
68
68
|
}));
|
|
69
|
-
server.
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
server.registerPrompt('compliance_audit', { description: 'Run a compliance check on a phone number: consent status and opt-out status. (CRTC DNCL status is coming soon.)', argsSchema: z.object({
|
|
70
|
+
phone_number: z.string().describe('Phone number to audit in E.164 format.'),
|
|
71
|
+
}) }, ({ phone_number }) => ({
|
|
72
72
|
messages: [{
|
|
73
73
|
role: 'user',
|
|
74
74
|
content: {
|
|
@@ -76,8 +76,8 @@ Please:
|
|
|
76
76
|
text: `Please run a CASL compliance audit for the phone number: ${phone_number}
|
|
77
77
|
|
|
78
78
|
Use these tools and summarize the results:
|
|
79
|
-
1. \`check_consent
|
|
80
|
-
2. \`list_opt_outs\` with phone_number filter
|
|
79
|
+
1. \`check_consent\`: What is the consent status? Is it ACTIVE, EXPIRED, or missing?
|
|
80
|
+
2. \`list_opt_outs\` with phone_number filter: Has this number opted out from any of my sending numbers?
|
|
81
81
|
(CRTC DNCL checking is coming soon and not yet available, so skip the DNCL lookup for now.)
|
|
82
82
|
|
|
83
83
|
Summarize with:
|
|
@@ -87,10 +87,10 @@ Summarize with:
|
|
|
87
87
|
},
|
|
88
88
|
}],
|
|
89
89
|
}));
|
|
90
|
-
server.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
server.registerPrompt('handle_erasure_request', { description: 'Process a PIPEDA right-to-erasure request for a subscriber.', argsSchema: z.object({
|
|
91
|
+
phone_number: z.string().describe('Phone number to erase data for in E.164 format.'),
|
|
92
|
+
reason: z.string().optional().describe('Reason provided by the subscriber for the erasure request.'),
|
|
93
|
+
}) }, ({ phone_number, reason }) => ({
|
|
94
94
|
messages: [{
|
|
95
95
|
role: 'user',
|
|
96
96
|
content: {
|
|
@@ -101,10 +101,10 @@ Summarize with:
|
|
|
101
101
|
${reason ? `**Reason provided:** ${reason}` : ''}
|
|
102
102
|
|
|
103
103
|
Please:
|
|
104
|
-
1. Confirm with me before proceeding
|
|
104
|
+
1. Confirm with me before proceeding: this action is irreversible.
|
|
105
105
|
2. If confirmed, use \`request_erasure\` to erase all personal data for this number.
|
|
106
106
|
3. Show me the result including the erased_at timestamp.
|
|
107
|
-
4. Explain what was erased (message bodies, consent records,
|
|
107
|
+
4. Explain what was erased (message bodies, consent records, verifications, contacts, non-DENY contact-list entries, and webhook dead letters referencing the number) and what is retained: message metadata, opt-out records (kept as required CASL compliance evidence, never erased), any DENY-list block on the number (kept so the number stays blocked), and an immutable audit log entry.`,
|
|
108
108
|
},
|
|
109
109
|
}],
|
|
110
110
|
}));
|
package/dist/prompts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,UAAU,eAAe,CAAC,MAAiB;IAC/C,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-address rate limiting for the hosted endpoint. Fixed one-minute
|
|
3
|
+
* windows in memory: the service runs as one replica, and the API behind it
|
|
4
|
+
* has its own per-key limits, so this only has to stop floods and key
|
|
5
|
+
* spraying from reaching the process and the API.
|
|
6
|
+
*/
|
|
7
|
+
import type { IncomingMessage } from 'node:http';
|
|
8
|
+
/**
|
|
9
|
+
* The caller's address as seen through `trustedProxies` hops. Each trusted
|
|
10
|
+
* proxy appends its own address to X-Forwarded-For (Railway's edge appends
|
|
11
|
+
* one, verified 2026-09-05: a request arrives as "<client>, <edge>"), so the
|
|
12
|
+
* client is the entry that many places left of the right end. Anything a
|
|
13
|
+
* client writes into the header itself lands further left and is ignored.
|
|
14
|
+
* With fewer entries than hops (a local run with no proxy) the leftmost
|
|
15
|
+
* entry is used; with no header at all, the socket address.
|
|
16
|
+
*/
|
|
17
|
+
export declare function clientIp(req: Pick<IncomingMessage, 'headers' | 'socket'>, trustedProxies?: number): string;
|
|
18
|
+
export interface RateLimiterOptions {
|
|
19
|
+
/** Events allowed per key per window. */
|
|
20
|
+
limit: number;
|
|
21
|
+
/** Window length. Default one minute. */
|
|
22
|
+
windowMs?: number;
|
|
23
|
+
/** Cap on tracked keys; the oldest is dropped past it. Default 10,000. */
|
|
24
|
+
maxEntries?: number;
|
|
25
|
+
/** Clock, injectable for tests. */
|
|
26
|
+
now?: () => number;
|
|
27
|
+
}
|
|
28
|
+
export interface RateLimitVerdict {
|
|
29
|
+
allowed: boolean;
|
|
30
|
+
/** Seconds until the window resets; what to send as Retry-After. */
|
|
31
|
+
retryAfterSec: number;
|
|
32
|
+
}
|
|
33
|
+
export declare class RateLimiter {
|
|
34
|
+
private readonly buckets;
|
|
35
|
+
private readonly limit;
|
|
36
|
+
private readonly windowMs;
|
|
37
|
+
private readonly maxEntries;
|
|
38
|
+
private readonly now;
|
|
39
|
+
private lastPrune;
|
|
40
|
+
constructor(opts: RateLimiterOptions);
|
|
41
|
+
get size(): number;
|
|
42
|
+
/** Counts one event for the key and says whether it stayed within the limit. */
|
|
43
|
+
hit(key: string): RateLimitVerdict;
|
|
44
|
+
/** Whether the key is already at the limit, without counting anything. */
|
|
45
|
+
blocked(key: string): RateLimitVerdict;
|
|
46
|
+
private verdict;
|
|
47
|
+
private bucket;
|
|
48
|
+
private prune;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=rateLimit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rateLimit.d.ts","sourceRoot":"","sources":["../src/rateLimit.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEjD;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,SAAS,GAAG,QAAQ,CAAC,EAAE,cAAc,SAAI,GAAG,MAAM,CAWrG;AAED,MAAM,WAAW,kBAAkB;IACjC,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mCAAmC;IACnC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,oEAAoE;IACpE,aAAa,EAAE,MAAM,CAAC;CACvB;AAID,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;IACrD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,SAAS,CAAS;gBAEd,IAAI,EAAE,kBAAkB;IAQpC,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,gFAAgF;IAChF,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB;IAOlC,0EAA0E;IAC1E,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB;IAOtC,OAAO,CAAC,OAAO;IAKf,OAAO,CAAC,MAAM;IAkBd,OAAO,CAAC,KAAK;CAMd"}
|