chekk 0.5.4 → 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.
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Chekk MCP Server
4
+ *
5
+ * Makes every project deployed on chekk.dev available as agent-callable
6
+ * tools via the Model Context Protocol.
7
+ *
8
+ * Usage (Claude Desktop / Cursor / any MCP client):
9
+ * npx chekk-mcp
10
+ *
11
+ * Tools exposed:
12
+ * - chekk_search Search Chekk's project index by keyword, framework, etc.
13
+ * - chekk_discover Browse trending, popular, and featured projects.
14
+ * - chekk_get_manifest Get the full agent manifest for a specific project.
15
+ * - chekk_call Call an action on a Chekk-deployed project (proxied).
16
+ */
17
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,448 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Chekk MCP Server
4
+ *
5
+ * Makes every project deployed on chekk.dev available as agent-callable
6
+ * tools via the Model Context Protocol.
7
+ *
8
+ * Usage (Claude Desktop / Cursor / any MCP client):
9
+ * npx chekk-mcp
10
+ *
11
+ * Tools exposed:
12
+ * - chekk_search Search Chekk's project index by keyword, framework, etc.
13
+ * - chekk_discover Browse trending, popular, and featured projects.
14
+ * - chekk_get_manifest Get the full agent manifest for a specific project.
15
+ * - chekk_call Call an action on a Chekk-deployed project (proxied).
16
+ */
17
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
18
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
19
+ import { z } from "zod";
20
+ const CHEKK_API = process.env.CHEKK_API_URL || "https://chekk.dev/api/v1";
21
+ // ---------------------------------------------------------------------------
22
+ // Helpers
23
+ // ---------------------------------------------------------------------------
24
+ const AGENT_ID = process.env.CHEKK_AGENT_ID || "chekk-mcp-server/0.2.0";
25
+ async function chekkFetch(path, init) {
26
+ const url = path.startsWith("http") ? path : `${CHEKK_API}${path}`;
27
+ return fetch(url, {
28
+ ...init,
29
+ headers: {
30
+ "User-Agent": AGENT_ID,
31
+ "X-Agent-Id": AGENT_ID,
32
+ Accept: "application/json",
33
+ ...(init?.headers || {}),
34
+ },
35
+ });
36
+ }
37
+ /** Fire-and-forget call logging — never blocks the tool response. */
38
+ function logCall(data) {
39
+ chekkFetch("/agents/log", {
40
+ method: "POST",
41
+ headers: { "Content-Type": "application/json" },
42
+ body: JSON.stringify({ ...data, agent_id: AGENT_ID }),
43
+ }).catch(() => { });
44
+ }
45
+ function formatProject(item) {
46
+ const requires = item.requires || {};
47
+ return {
48
+ project: item.slug || `${item.github_owner}/${item.github_repo}`,
49
+ title: item.title || item.github_repo || item.repo,
50
+ description: item.description,
51
+ framework: item.framework,
52
+ actions_count: item.actions_count,
53
+ stars: item.star_count ?? item.stars ?? null,
54
+ is_healthy: item.is_healthy ?? null,
55
+ requires_auth: requires.auth || false,
56
+ requires_payment: requires.payment || false,
57
+ manifest_url: item.well_known,
58
+ chat_url: item.chat,
59
+ };
60
+ }
61
+ // ---------------------------------------------------------------------------
62
+ // Server
63
+ // ---------------------------------------------------------------------------
64
+ const server = new McpServer({
65
+ name: "chekk",
66
+ version: "0.2.0",
67
+ });
68
+ // ---------------------------------------------------------------------------
69
+ // Tool 1: chekk_search
70
+ // ---------------------------------------------------------------------------
71
+ server.registerTool("chekk_search", {
72
+ title: "Search Chekk Tools",
73
+ description: "Search Chekk's index of deployed software that agents can call. " +
74
+ "Returns projects with their available actions, payment requirements, " +
75
+ "health status, and entry points. Use this when you know what kind " +
76
+ "of tool you need (e.g. 'markdown to PDF', 'language detection', 'web scraper').",
77
+ inputSchema: {
78
+ query: z
79
+ .string()
80
+ .optional()
81
+ .describe("Keyword search across project names, descriptions, and owners"),
82
+ framework: z
83
+ .string()
84
+ .optional()
85
+ .describe("Filter by framework: nextjs, react, fastapi, express, flask, django, go, rust, etc."),
86
+ has_payment: z
87
+ .boolean()
88
+ .optional()
89
+ .describe("Filter to projects that accept USDC payments"),
90
+ has_auth: z
91
+ .boolean()
92
+ .optional()
93
+ .describe("Filter to projects that require authentication"),
94
+ min_actions: z
95
+ .number()
96
+ .int()
97
+ .min(0)
98
+ .optional()
99
+ .describe("Minimum number of callable actions"),
100
+ limit: z
101
+ .number()
102
+ .int()
103
+ .min(1)
104
+ .max(50)
105
+ .optional()
106
+ .describe("Max results to return (default 20)"),
107
+ },
108
+ }, async (args) => {
109
+ const params = new URLSearchParams();
110
+ if (args.query)
111
+ params.set("q", args.query);
112
+ if (args.framework)
113
+ params.set("framework", args.framework);
114
+ if (args.has_payment !== undefined)
115
+ params.set("has_payment", String(args.has_payment));
116
+ if (args.has_auth !== undefined)
117
+ params.set("has_auth", String(args.has_auth));
118
+ if (args.min_actions !== undefined)
119
+ params.set("min_actions", String(args.min_actions));
120
+ params.set("limit", String(args.limit || 20));
121
+ const t0 = Date.now();
122
+ const res = await chekkFetch(`/agents/search?${params}`);
123
+ logCall({ tool: "search", query: args.query, status_code: res.status, response_time_ms: Date.now() - t0 });
124
+ if (!res.ok) {
125
+ return {
126
+ content: [
127
+ {
128
+ type: "text",
129
+ text: `Error searching Chekk: HTTP ${res.status} — ${await res.text()}`,
130
+ },
131
+ ],
132
+ isError: true,
133
+ };
134
+ }
135
+ const data = await res.json();
136
+ const items = (data.items || []).map(formatProject);
137
+ return {
138
+ content: [
139
+ {
140
+ type: "text",
141
+ text: JSON.stringify({
142
+ total: data.count,
143
+ results: items,
144
+ hint: "Use chekk_get_manifest with a project slug to see all available actions, or chekk_call to invoke an action directly.",
145
+ }, null, 2),
146
+ },
147
+ ],
148
+ };
149
+ });
150
+ // ---------------------------------------------------------------------------
151
+ // Tool 2: chekk_discover
152
+ // ---------------------------------------------------------------------------
153
+ server.registerTool("chekk_discover", {
154
+ title: "Discover Chekk Projects",
155
+ description: "Browse Chekk's curated project feed. Use this when you want to " +
156
+ "explore what's available — trending projects, staff picks, most " +
157
+ "popular tools, or recently deployed software. Good for general " +
158
+ "discovery when you don't have a specific keyword in mind.",
159
+ inputSchema: {
160
+ sort: z
161
+ .enum(["trending", "popular", "recent", "rising", "staff_picks"])
162
+ .default("trending")
163
+ .describe("Sort order: trending (hot this week), popular (all-time stars), " +
164
+ "recent (newest), rising (breakout last 3 days), staff_picks (curated)"),
165
+ limit: z
166
+ .number()
167
+ .int()
168
+ .min(1)
169
+ .max(50)
170
+ .optional()
171
+ .describe("Max results to return (default 20)"),
172
+ },
173
+ }, async (args) => {
174
+ const limit = args.limit || 20;
175
+ const t0 = Date.now();
176
+ const res = await chekkFetch(`/deployments/recent?limit=${limit}&sort=${args.sort}`);
177
+ logCall({ tool: "discover", query: args.sort, status_code: res.status, response_time_ms: Date.now() - t0 });
178
+ if (!res.ok) {
179
+ return {
180
+ content: [
181
+ {
182
+ type: "text",
183
+ text: `Error fetching Chekk feed: HTTP ${res.status} — ${await res.text()}`,
184
+ },
185
+ ],
186
+ isError: true,
187
+ };
188
+ }
189
+ const data = await res.json();
190
+ const deployments = Array.isArray(data) ? data : data.items || data.deployments || [];
191
+ const items = deployments.map((d) => ({
192
+ project: `${d.github_owner}/${d.github_repo}`,
193
+ title: d.title || d.github_repo,
194
+ description: d.description,
195
+ framework: d.framework,
196
+ stars: d.star_count ?? 0,
197
+ views: d.view_count ?? 0,
198
+ is_healthy: d.is_healthy ?? null,
199
+ is_featured: d.is_featured ?? null,
200
+ deployed_url: d.deployed_url,
201
+ manifest_url: `https://chekk.dev/${d.github_owner}/${d.github_repo}/.well-known/agent.json`,
202
+ }));
203
+ return {
204
+ content: [
205
+ {
206
+ type: "text",
207
+ text: JSON.stringify({
208
+ sort: args.sort,
209
+ count: items.length,
210
+ results: items,
211
+ hint: "Use chekk_search to find specific tools, or chekk_get_manifest to see a project's callable actions.",
212
+ }, null, 2),
213
+ },
214
+ ],
215
+ };
216
+ });
217
+ // ---------------------------------------------------------------------------
218
+ // Tool 3: chekk_get_manifest
219
+ // ---------------------------------------------------------------------------
220
+ server.registerTool("chekk_get_manifest", {
221
+ title: "Get Project Manifest",
222
+ description: "Get the full agent manifest for a Chekk-deployed project. " +
223
+ "Returns all callable actions with their HTTP methods, paths, " +
224
+ "required inputs, payment prices, and auth requirements. " +
225
+ "Use this to understand what a specific project can do before calling it.",
226
+ inputSchema: {
227
+ project: z
228
+ .string()
229
+ .describe('Project slug in "owner/repo" format, e.g. "ifihan/nigerian-markets-api"'),
230
+ },
231
+ }, async (args) => {
232
+ const [owner, repo] = args.project.split("/");
233
+ if (!owner || !repo) {
234
+ return {
235
+ content: [
236
+ {
237
+ type: "text",
238
+ text: 'Invalid project slug. Use "owner/repo" format.',
239
+ },
240
+ ],
241
+ isError: true,
242
+ };
243
+ }
244
+ const t0 = Date.now();
245
+ const res = await chekkFetch(`/agent/${owner}/${repo}/manifest`);
246
+ logCall({ tool: "get_manifest", query: args.project, status_code: res.status, response_time_ms: Date.now() - t0 });
247
+ if (!res.ok) {
248
+ if (res.status === 404) {
249
+ return {
250
+ content: [
251
+ {
252
+ type: "text",
253
+ text: `Project "${args.project}" not found or has no agent manifest. ` +
254
+ `Try chekk_search to find available projects.`,
255
+ },
256
+ ],
257
+ isError: true,
258
+ };
259
+ }
260
+ return {
261
+ content: [
262
+ {
263
+ type: "text",
264
+ text: `Error fetching manifest: HTTP ${res.status}`,
265
+ },
266
+ ],
267
+ isError: true,
268
+ };
269
+ }
270
+ const manifest = await res.json();
271
+ const actions = (manifest.actions || []).map((a) => ({
272
+ id: a.id,
273
+ method: a.method,
274
+ path: a.path,
275
+ description: a.description,
276
+ side_effect: a.side_effect,
277
+ inputs: a.inputs,
278
+ }));
279
+ const summary = {
280
+ project: manifest.slug,
281
+ description: manifest.description,
282
+ framework: manifest.framework,
283
+ deployed_url: manifest.deployed_url,
284
+ actions_count: actions.length,
285
+ actions,
286
+ requires_auth: manifest.requires?.auth || false,
287
+ requires_payment: manifest.requires?.payment || false,
288
+ payment_info: manifest.payment || null,
289
+ session: manifest.session || null,
290
+ hint: "Use chekk_call with the project slug, method, path, and body to invoke an action.",
291
+ };
292
+ return {
293
+ content: [
294
+ {
295
+ type: "text",
296
+ text: JSON.stringify(summary, null, 2),
297
+ },
298
+ ],
299
+ };
300
+ });
301
+ // ---------------------------------------------------------------------------
302
+ // Tool 4: chekk_call
303
+ // ---------------------------------------------------------------------------
304
+ server.registerTool("chekk_call", {
305
+ title: "Call a Chekk Tool",
306
+ description: "Execute an action on a Chekk-deployed project. " +
307
+ "Proxies your request through Chekk's agent gateway, which handles " +
308
+ "rate limiting, payment enforcement (402 challenges), and routing. " +
309
+ "If the action requires payment, you'll receive a 402 response with " +
310
+ "instructions to pay USDC on Base.",
311
+ inputSchema: {
312
+ project: z
313
+ .string()
314
+ .describe('Project slug in "owner/repo" format, e.g. "ifihan/nigerian-markets-api"'),
315
+ method: z
316
+ .enum(["GET", "POST", "PUT", "PATCH", "DELETE"])
317
+ .default("POST")
318
+ .describe("HTTP method for the action"),
319
+ path: z
320
+ .string()
321
+ .describe('API path to call, e.g. "/api/states"'),
322
+ body: z
323
+ .record(z.unknown())
324
+ .optional()
325
+ .describe("Request body (JSON object) for POST/PUT/PATCH"),
326
+ query: z
327
+ .record(z.string())
328
+ .optional()
329
+ .describe("Query parameters as key-value pairs"),
330
+ payment_receipt: z
331
+ .string()
332
+ .optional()
333
+ .describe("Payment receipt ID from a previous 402 challenge (after paying USDC)"),
334
+ },
335
+ }, async (args) => {
336
+ const [owner, repo] = args.project.split("/");
337
+ if (!owner || !repo) {
338
+ return {
339
+ content: [
340
+ {
341
+ type: "text",
342
+ text: 'Invalid project slug. Use "owner/repo" format.',
343
+ },
344
+ ],
345
+ isError: true,
346
+ };
347
+ }
348
+ const headers = {
349
+ "Content-Type": "application/json",
350
+ };
351
+ if (args.payment_receipt) {
352
+ headers["X-Payment-Receipt"] = args.payment_receipt;
353
+ }
354
+ const callBody = {
355
+ method: args.method,
356
+ path: args.path,
357
+ };
358
+ if (args.body)
359
+ callBody.body = args.body;
360
+ if (args.query)
361
+ callBody.query = args.query;
362
+ const t0 = Date.now();
363
+ const res = await chekkFetch(`/agent/${owner}/${repo}/call`, {
364
+ method: "POST",
365
+ headers,
366
+ body: JSON.stringify(callBody),
367
+ });
368
+ const elapsed = Date.now() - t0;
369
+ const data = await res.json();
370
+ logCall({ tool: "call", query: args.project, method: args.method, path: args.path, status_code: res.status, response_time_ms: elapsed });
371
+ if (res.status === 402) {
372
+ return {
373
+ content: [
374
+ {
375
+ type: "text",
376
+ text: JSON.stringify({
377
+ status: "payment_required",
378
+ message: data.message,
379
+ receipt_id: data.receipt_id,
380
+ challenge: data.challenge,
381
+ hint: data.hint,
382
+ next_step: "Pay the USDC amount to the specified address on Base, " +
383
+ "then call chekk_call again with payment_receipt set to the receipt_id.",
384
+ }, null, 2),
385
+ },
386
+ ],
387
+ };
388
+ }
389
+ if (res.status === 429) {
390
+ return {
391
+ content: [
392
+ {
393
+ type: "text",
394
+ text: JSON.stringify({
395
+ status: "rate_limited",
396
+ message: data.message,
397
+ retry_after_seconds: data.retry_after_seconds,
398
+ scope: data.scope,
399
+ }, null, 2),
400
+ },
401
+ ],
402
+ isError: true,
403
+ };
404
+ }
405
+ if (!res.ok) {
406
+ return {
407
+ content: [
408
+ {
409
+ type: "text",
410
+ text: JSON.stringify({
411
+ status: "error",
412
+ http_status: res.status,
413
+ error: data,
414
+ }, null, 2),
415
+ },
416
+ ],
417
+ isError: true,
418
+ };
419
+ }
420
+ return {
421
+ content: [
422
+ {
423
+ type: "text",
424
+ text: JSON.stringify({
425
+ status: "success",
426
+ upstream_status: data.upstream_status,
427
+ result: data.upstream_body,
428
+ paid: data.paid || false,
429
+ receipt_id: data.receipt_id || null,
430
+ response_time_ms: data.upstream_seconds
431
+ ? Math.round(data.upstream_seconds * 1000)
432
+ : null,
433
+ }, null, 2),
434
+ },
435
+ ],
436
+ };
437
+ });
438
+ // ---------------------------------------------------------------------------
439
+ // Start
440
+ // ---------------------------------------------------------------------------
441
+ async function main() {
442
+ const transport = new StdioServerTransport();
443
+ await server.connect(transport);
444
+ }
445
+ main().catch((err) => {
446
+ console.error("Chekk MCP server failed to start:", err);
447
+ process.exit(1);
448
+ });
package/package.json CHANGED
@@ -1,44 +1,28 @@
1
1
  {
2
2
  "name": "chekk",
3
- "version": "0.5.4",
4
- "description": "See how you prompt. Chekk analyzes your AI coding workflow, tells you what kind of engineer you are, and shows what your habits actually cost.",
3
+ "version": "1.0.0",
4
+ "description": "MCP server that exposes every Chekk-deployed project as agent-callable tools. Use any deployed API as an agent tool.",
5
+ "type": "module",
5
6
  "bin": {
6
- "chekk": "./bin/chekk.js"
7
+ "chekk": "./dist/index.js",
8
+ "chekk-mcp": "./dist/index.js"
7
9
  },
8
- "type": "module",
9
- "files": [
10
- "bin/",
11
- "src/"
12
- ],
10
+ "main": "./dist/index.js",
13
11
  "scripts": {
14
- "start": "node bin/chekk.js"
12
+ "build": "tsc",
13
+ "start": "node dist/index.js",
14
+ "dev": "tsx src/index.ts"
15
15
  },
16
- "keywords": [
17
- "ai",
18
- "coding",
19
- "claude",
20
- "cursor",
21
- "copilot",
22
- "developer",
23
- "analysis",
24
- "prompt",
25
- "engineering",
26
- "vibe-coding",
27
- "workflow"
28
- ],
29
- "author": "Chekk <timi@chekk.dev>",
16
+ "files": ["dist"],
17
+ "keywords": ["mcp", "chekk", "agent", "tools", "llm"],
30
18
  "license": "MIT",
31
- "repository": {
32
- "type": "git",
33
- "url": "https://github.com/omarionnn/chekk"
34
- },
35
- "homepage": "https://chekk.dev",
36
- "engines": {
37
- "node": ">=18.0.0"
38
- },
39
19
  "dependencies": {
40
- "chalk": "^5.3.0",
41
- "commander": "^12.1.0",
42
- "ora": "^8.0.1"
20
+ "@modelcontextprotocol/sdk": "^1.12.0",
21
+ "zod": "^3.24.0"
22
+ },
23
+ "devDependencies": {
24
+ "typescript": "^5.7.0",
25
+ "tsx": "^4.19.0",
26
+ "@types/node": "^22.0.0"
43
27
  }
44
28
  }
package/bin/chekk.js DELETED
@@ -1,62 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { execSync, spawn } from 'child_process';
4
- import { Command } from 'commander';
5
- import { run } from '../src/index.js';
6
-
7
- const LOCAL_VERSION = '0.5.4';
8
-
9
- // ── Auto-update check ──
10
- // If running from a cached npx install, check if there's a newer version
11
- // and re-exec with @latest to ensure users always get the freshest build.
12
- async function checkForUpdate() {
13
- try {
14
- const latest = execSync('npm view chekk version 2>/dev/null', {
15
- encoding: 'utf-8',
16
- timeout: 3000,
17
- }).trim();
18
-
19
- if (latest && latest !== LOCAL_VERSION && isNewer(latest, LOCAL_VERSION)) {
20
- // Re-run with the latest version
21
- const args = process.argv.slice(2);
22
- const child = spawn('npx', ['--yes', `chekk@${latest}`, ...args], {
23
- stdio: 'inherit',
24
- shell: true,
25
- });
26
- child.on('exit', (code) => process.exit(code || 0));
27
- return true; // signal that we're handing off
28
- }
29
- } catch {
30
- // Network down or timeout — just run the local version
31
- }
32
- return false;
33
- }
34
-
35
- function isNewer(remote, local) {
36
- const r = remote.split('.').map(Number);
37
- const l = local.split('.').map(Number);
38
- for (let i = 0; i < 3; i++) {
39
- if ((r[i] || 0) > (l[i] || 0)) return true;
40
- if ((r[i] || 0) < (l[i] || 0)) return false;
41
- }
42
- return false;
43
- }
44
-
45
- const handedOff = await checkForUpdate();
46
- if (!handedOff) {
47
- const program = new Command();
48
-
49
- program
50
- .name('chekk')
51
- .description('The prompt engineering capability score. See how you prompt.')
52
- .version(LOCAL_VERSION)
53
- .option('--offline', 'Skip AI prose generation, show data-driven output')
54
- .option('--verbose', 'Show detailed per-project and per-metric breakdowns')
55
- .option('--json', 'Output raw metrics as JSON')
56
- .option('--no-upload', 'Skip the claim prompt')
57
- .action(async (options) => {
58
- await run(options);
59
- });
60
-
61
- program.parse();
62
- }