@nordsym/apiclaw 1.0.0 → 1.1.1

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.
Files changed (161) hide show
  1. package/AGENTS.md +74 -0
  2. package/HEARTBEAT.md +4 -0
  3. package/IDENTITY.md +22 -0
  4. package/README.md +193 -202
  5. package/SOUL.md +36 -0
  6. package/STATUS.md +237 -0
  7. package/TOOLS.md +36 -0
  8. package/USER.md +17 -0
  9. package/{backend/convex → convex}/_generated/api.d.ts +12 -6
  10. package/convex/analytics.ts +90 -0
  11. package/convex/credits.ts +211 -0
  12. package/convex/http.ts +578 -0
  13. package/convex/providers.ts +516 -0
  14. package/convex/purchases.ts +183 -0
  15. package/convex/ratelimit.ts +104 -0
  16. package/convex/schema.ts +220 -0
  17. package/convex/telemetry.ts +81 -0
  18. package/convex.json +3 -0
  19. package/dist/credentials.d.ts +19 -0
  20. package/dist/credentials.d.ts.map +1 -0
  21. package/dist/credentials.js +158 -0
  22. package/dist/credentials.js.map +1 -0
  23. package/dist/credits.d.ts +14 -11
  24. package/dist/credits.d.ts.map +1 -1
  25. package/dist/credits.js +151 -99
  26. package/dist/credits.js.map +1 -1
  27. package/dist/discovery.d.ts +7 -16
  28. package/dist/discovery.d.ts.map +1 -1
  29. package/dist/discovery.js +33 -40
  30. package/dist/discovery.js.map +1 -1
  31. package/dist/execute.d.ts +19 -0
  32. package/dist/execute.d.ts.map +1 -0
  33. package/dist/execute.js +285 -0
  34. package/dist/execute.js.map +1 -0
  35. package/dist/index.js +175 -31
  36. package/dist/index.js.map +1 -1
  37. package/dist/proxy.d.ts +6 -0
  38. package/dist/proxy.d.ts.map +1 -0
  39. package/dist/proxy.js +19 -0
  40. package/dist/proxy.js.map +1 -0
  41. package/dist/registry/apis.json +95362 -202
  42. package/dist/registry/apis_expanded.json +100853 -0
  43. package/dist/stripe.d.ts +68 -0
  44. package/dist/stripe.d.ts.map +1 -0
  45. package/dist/stripe.js +196 -0
  46. package/dist/stripe.js.map +1 -0
  47. package/dist/telemetry.d.ts +28 -0
  48. package/dist/telemetry.d.ts.map +1 -0
  49. package/dist/telemetry.js +50 -0
  50. package/dist/telemetry.js.map +1 -0
  51. package/dist/test.d.ts +3 -2
  52. package/dist/test.d.ts.map +1 -1
  53. package/dist/test.js +105 -75
  54. package/dist/test.js.map +1 -1
  55. package/dist/types.d.ts +0 -28
  56. package/dist/types.d.ts.map +1 -1
  57. package/dist/webhook.d.ts +2 -0
  58. package/dist/webhook.d.ts.map +1 -0
  59. package/dist/webhook.js +90 -0
  60. package/dist/webhook.js.map +1 -0
  61. package/landing/DESIGN.md +343 -0
  62. package/landing/package-lock.json +1196 -7
  63. package/landing/package.json +5 -1
  64. package/landing/public/android-chrome-192x192.png +0 -0
  65. package/landing/public/android-chrome-512x512.png +0 -0
  66. package/landing/public/apple-touch-icon.png +0 -0
  67. package/landing/public/demo.gif +0 -0
  68. package/landing/public/demo.mp4 +0 -0
  69. package/landing/public/favicon-16x16.png +0 -0
  70. package/landing/public/favicon-32x32.png +0 -0
  71. package/landing/public/favicon.ico +0 -0
  72. package/landing/public/favicon.svg +3 -0
  73. package/landing/public/icon.svg +47 -0
  74. package/landing/public/logo-mono.svg +37 -0
  75. package/landing/public/logo-simple.svg +45 -0
  76. package/landing/public/logo.svg +84 -0
  77. package/landing/public/og-template.html +184 -0
  78. package/landing/public/site.webmanifest +31 -0
  79. package/landing/scripts/generate-assets.js +284 -0
  80. package/landing/scripts/generate-pngs.js +48 -0
  81. package/landing/scripts/generate-stats.js +42 -0
  82. package/landing/src/app/admin/page.tsx +348 -0
  83. package/landing/src/app/api/auth/magic-link/route.ts +73 -0
  84. package/landing/src/app/api/auth/session/route.ts +38 -0
  85. package/landing/src/app/api/auth/verify/route.ts +43 -0
  86. package/landing/src/app/api/og/route.tsx +84 -0
  87. package/landing/src/app/globals.css +439 -100
  88. package/landing/src/app/layout.tsx +37 -7
  89. package/landing/src/app/page.tsx +627 -552
  90. package/landing/src/app/providers/dashboard/login/page.tsx +176 -0
  91. package/landing/src/app/providers/dashboard/page.tsx +589 -0
  92. package/landing/src/app/providers/dashboard/verify/page.tsx +106 -0
  93. package/landing/src/app/providers/layout.tsx +14 -0
  94. package/landing/src/app/providers/page.tsx +402 -0
  95. package/landing/src/app/providers/register/page.tsx +670 -0
  96. package/landing/src/components/ProviderDashboard.tsx +794 -0
  97. package/landing/src/hooks/useDashboardData.ts +99 -0
  98. package/landing/src/lib/apis.json +116054 -0
  99. package/landing/src/lib/convex-client.ts +106 -0
  100. package/landing/src/lib/mock-data.ts +285 -0
  101. package/landing/src/lib/stats.json +6 -0
  102. package/landing/tailwind.config.ts +12 -11
  103. package/landing/tsconfig.tsbuildinfo +1 -0
  104. package/package.json +21 -20
  105. package/scripts/SYMBOT-FIX.md +238 -0
  106. package/scripts/demo-simulation.py +177 -0
  107. package/scripts/expand-more.py +502 -0
  108. package/scripts/expand-registry.py +434 -0
  109. package/scripts/history-sanitizer.ts +272 -0
  110. package/scripts/mass-scrape.py +1308 -0
  111. package/scripts/sync-and-deploy.sh +36 -0
  112. package/src/credentials.ts +177 -0
  113. package/src/credits.ts +190 -122
  114. package/src/discovery.ts +45 -58
  115. package/src/execute.ts +350 -0
  116. package/src/index.ts +184 -32
  117. package/src/proxy.ts +24 -0
  118. package/src/registry/apis.json +95362 -202
  119. package/src/registry/apis_expanded.json +100853 -0
  120. package/src/stripe.ts +243 -0
  121. package/src/telemetry.ts +71 -0
  122. package/src/test.ts +127 -89
  123. package/src/types.ts +0 -34
  124. package/src/webhook.ts +107 -0
  125. package/.github/ISSUE_TEMPLATE/add-api.yml +0 -123
  126. package/BRIEFING.md +0 -30
  127. package/backend/convex/apiKeys.ts +0 -75
  128. package/backend/convex/purchases.ts +0 -74
  129. package/backend/convex/schema.ts +0 -45
  130. package/backend/convex/transactions.ts +0 -57
  131. package/backend/convex/users.ts +0 -94
  132. package/backend/package-lock.json +0 -521
  133. package/backend/package.json +0 -15
  134. package/dist/registry/parse_apis.py +0 -146
  135. package/dist/revenuecat.d.ts +0 -61
  136. package/dist/revenuecat.d.ts.map +0 -1
  137. package/dist/revenuecat.js +0 -166
  138. package/dist/revenuecat.js.map +0 -1
  139. package/dist/webhooks/revenuecat.d.ts +0 -48
  140. package/dist/webhooks/revenuecat.d.ts.map +0 -1
  141. package/dist/webhooks/revenuecat.js +0 -119
  142. package/dist/webhooks/revenuecat.js.map +0 -1
  143. package/docs/revenuecat-setup.md +0 -89
  144. package/landing/src/app/api/keys/route.ts +0 -71
  145. package/landing/src/app/api/log/route.ts +0 -37
  146. package/landing/src/app/api/stats/route.ts +0 -37
  147. package/landing/src/app/page.tsx.bak +0 -567
  148. package/landing/src/components/AddKeyModal.tsx +0 -159
  149. package/newsletter-template.html +0 -71
  150. package/outreach/OUTREACH-SYSTEM.md +0 -211
  151. package/outreach/email-template.html +0 -179
  152. package/outreach/targets.md +0 -133
  153. package/src/registry/parse_apis.py +0 -146
  154. package/src/revenuecat.ts +0 -239
  155. package/src/webhooks/revenuecat.ts +0 -187
  156. /package/{backend/convex → convex}/README.md +0 -0
  157. /package/{backend/convex → convex}/_generated/api.js +0 -0
  158. /package/{backend/convex → convex}/_generated/dataModel.d.ts +0 -0
  159. /package/{backend/convex → convex}/_generated/server.d.ts +0 -0
  160. /package/{backend/convex → convex}/_generated/server.js +0 -0
  161. /package/{backend/convex → convex}/tsconfig.json +0 -0
package/src/index.ts CHANGED
@@ -18,38 +18,56 @@ import {
18
18
  Tool,
19
19
  } from '@modelcontextprotocol/sdk/types.js';
20
20
 
21
- import { discoverAPIs, getAPIDetails, getCategories, getAllAPIs, getAPICount } from './discovery.js';
21
+ import { discoverAPIs, getAPIDetails, getCategories, getAllAPIs } from './discovery.js';
22
+ import { trackStartup, trackSearch, trackExecute, trackDiscovery } from './telemetry.js';
22
23
  import {
23
24
  getAgentCredits,
24
25
  addCredits,
25
26
  purchaseAPIAccess,
26
27
  getBalanceSummary,
27
- getAgentPurchases
28
+ getAgentPurchases,
29
+ getProvidersWithRealCredentials
28
30
  } from './credits.js';
31
+ import { hasRealCredentials } from './credentials.js';
32
+ import { executeAPICall, getConnectedProviders } from './execute.js';
29
33
 
30
34
  // Default agent ID for MVP (in production, this would come from auth)
31
35
  const DEFAULT_AGENT_ID = 'agent_default';
32
36
 
33
37
  // Tool definitions
34
38
  const tools: Tool[] = [
39
+ {
40
+ name: 'apiclaw_help',
41
+ description: 'Get help and see available commands. Start here if you are new to APIClaw.',
42
+ inputSchema: {
43
+ type: 'object',
44
+ properties: {},
45
+ required: []
46
+ }
47
+ },
35
48
  {
36
49
  name: 'discover_apis',
37
- description: 'Search 1400+ APIs based on what you need to do. Describe your use case naturally.',
50
+ description: 'Search for APIs based on what you need to do. Describe your use case naturally.',
38
51
  inputSchema: {
39
52
  type: 'object',
40
53
  properties: {
41
54
  query: {
42
55
  type: 'string',
43
- description: 'Natural language query describing what you need (e.g., "send SMS", "weather data", "payment processing", "text to speech")'
56
+ description: 'Natural language query describing what you need (e.g., "send SMS to Sweden", "search the web", "generate speech from text")'
44
57
  },
45
58
  category: {
46
59
  type: 'string',
47
- description: 'Filter by category (use list_categories to see all)'
60
+ description: 'Filter by category: communication, search, ai',
61
+ enum: ['communication', 'search', 'ai']
48
62
  },
49
63
  max_results: {
50
64
  type: 'number',
51
- description: 'Maximum number of results to return (default: 10)',
52
- default: 10
65
+ description: 'Maximum number of results to return (default: 5)',
66
+ default: 5
67
+ },
68
+ region: {
69
+ type: 'string',
70
+ description: 'Filter by region (e.g., "SE", "EU", "global")'
53
71
  }
54
72
  },
55
73
  required: ['query']
@@ -129,6 +147,36 @@ const tools: Tool[] = [
129
147
  type: 'object',
130
148
  properties: {}
131
149
  }
150
+ },
151
+ {
152
+ name: 'call_api',
153
+ description: 'Execute an API call through APIClaw Instant Connect. No API keys needed - we handle authentication.',
154
+ inputSchema: {
155
+ type: 'object',
156
+ properties: {
157
+ provider: {
158
+ type: 'string',
159
+ description: 'Provider ID (e.g., "46elks", "brave_search", "resend", "openrouter", "elevenlabs", "twilio")'
160
+ },
161
+ action: {
162
+ type: 'string',
163
+ description: 'Action to perform (e.g., "send_sms", "search", "send_email", "chat", "text_to_speech")'
164
+ },
165
+ params: {
166
+ type: 'object',
167
+ description: 'Parameters for the action. Varies by provider/action.'
168
+ }
169
+ },
170
+ required: ['provider', 'action', 'params']
171
+ }
172
+ },
173
+ {
174
+ name: 'list_connected',
175
+ description: 'List all APIs available for Instant Connect (no API key needed).',
176
+ inputSchema: {
177
+ type: 'object',
178
+ properties: {}
179
+ }
132
180
  }
133
181
  ];
134
182
 
@@ -156,12 +204,51 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
156
204
 
157
205
  try {
158
206
  switch (name) {
207
+ case 'apiclaw_help': {
208
+ const helpText = `
209
+ 🦞 APIClaw — The API Layer for AI Agents
210
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
211
+
212
+ DISCOVER APIs:
213
+ discover_apis({ query: "send SMS to Sweden" })
214
+ discover_apis({ query: "search the web", max_results: 10 })
215
+ discover_apis({ query: "text to speech", category: "ai" })
216
+
217
+ GET DETAILS:
218
+ get_api_details({ api_id: "46elks" })
219
+
220
+ INSTANT CONNECT (6 APIs, no key needed):
221
+ get_connected_providers()
222
+ call_api({ provider: "brave_search", endpoint: "search", params: { q: "AI agents" } })
223
+
224
+ Available instant-connect providers:
225
+ • brave_search — Web search
226
+ • 46elks — SMS (Sweden)
227
+ • twilio — SMS (Global)
228
+ • resend — Email
229
+ • openrouter — LLM routing
230
+ • elevenlabs — Text-to-speech
231
+
232
+ BROWSE:
233
+ list_categories()
234
+ list_all_apis({ category: "communication", limit: 20 })
235
+
236
+ Docs: https://apiclaw.nordsym.com
237
+ `;
238
+ return {
239
+ content: [{ type: 'text', text: helpText }]
240
+ };
241
+ }
242
+
159
243
  case 'discover_apis': {
160
244
  const query = args?.query as string;
161
245
  const category = args?.category as string | undefined;
162
- const maxResults = (args?.max_results as number) || 10;
246
+ const maxResults = (args?.max_results as number) || 5;
247
+ const region = args?.region as string | undefined;
163
248
 
164
- const results = discoverAPIs(query, { category, maxResults });
249
+ const startTime = Date.now();
250
+ const results = discoverAPIs(query, { category, maxResults, region });
251
+ trackSearch(query, results.length, Date.now() - startTime);
165
252
 
166
253
  if (results.length === 0) {
167
254
  return {
@@ -171,8 +258,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
171
258
  text: JSON.stringify({
172
259
  status: 'no_results',
173
260
  message: `No APIs found matching "${query}". Try broader terms or check available categories with list_categories.`,
174
- total_apis: getAPICount(),
175
- available_categories: getCategories().slice(0, 20)
261
+ available_categories: getCategories()
176
262
  }, null, 2)
177
263
  }
178
264
  ]
@@ -187,17 +273,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
187
273
  status: 'success',
188
274
  query,
189
275
  results_count: results.length,
190
- total_apis: getAPICount(),
191
276
  results: results.map(r => ({
192
- id: r.api.id,
193
- name: r.api.name,
194
- description: r.api.description,
195
- category: r.api.category,
196
- auth: r.api.auth,
197
- https: r.api.https,
198
- link: r.api.link,
199
- pricing: r.api.pricing,
200
- keywords: r.api.keywords,
277
+ id: r.provider.id,
278
+ name: r.provider.name,
279
+ description: r.provider.description,
280
+ category: r.provider.category,
281
+ capabilities: r.provider.capabilities,
282
+ pricing_model: r.provider.pricing.model,
283
+ has_free_tier: r.provider.pricing.free_tier,
284
+ agent_success_rate: r.provider.agent_success_rate,
201
285
  relevance_score: r.relevance_score,
202
286
  match_reasons: r.match_reasons
203
287
  }))
@@ -274,12 +358,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
274
358
  provider: apiId,
275
359
  amount_paid_usd: amountUsd,
276
360
  credits_received: result.purchase!.credits_purchased,
277
- status: result.purchase!.status
361
+ status: result.purchase!.status,
362
+ real_credentials: hasRealCredentials(apiId)
278
363
  },
279
364
  credentials: result.purchase!.credentials,
280
365
  access: {
281
- link: api?.link,
282
- auth: api?.auth
366
+ base_url: api?.base_url,
367
+ docs_url: api?.docs_url,
368
+ auth_type: api?.auth_type
283
369
  }
284
370
  }, null, 2)
285
371
  }
@@ -301,11 +387,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
301
387
  balance_usd: summary.credits.balance_usd,
302
388
  currency: summary.credits.currency,
303
389
  total_spent_usd: summary.total_spent_usd,
390
+ real_credential_providers: summary.real_credentials_available,
304
391
  active_purchases: summary.active_purchases.map(p => ({
305
392
  id: p.id,
306
393
  provider: p.provider_id,
307
- credits_remaining: p.credits_purchased, // Would track actual usage in production
308
- status: p.status
394
+ credits_remaining: p.credits_purchased,
395
+ status: p.status,
396
+ real_credentials: hasRealCredentials(p.provider_id)
309
397
  }))
310
398
  }, null, 2)
311
399
  }
@@ -335,10 +423,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
335
423
 
336
424
  case 'list_categories': {
337
425
  const categories = getCategories();
338
- const categoryCounts: Record<string, number> = {};
426
+ const apisByCategory: Record<string, string[]> = {};
339
427
 
340
428
  for (const cat of categories) {
341
- categoryCounts[cat] = getAllAPIs().filter(a => a.category === cat).length;
429
+ apisByCategory[cat] = getAllAPIs()
430
+ .filter(a => a.category === cat)
431
+ .map(a => a.id);
342
432
  }
343
433
 
344
434
  return {
@@ -347,9 +437,49 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
347
437
  type: 'text',
348
438
  text: JSON.stringify({
349
439
  status: 'success',
350
- total_apis: getAPICount(),
351
- category_count: categories.length,
352
- categories: categoryCounts
440
+ categories: apisByCategory
441
+ }, null, 2)
442
+ }
443
+ ]
444
+ };
445
+ }
446
+
447
+ case 'call_api': {
448
+ const provider = args?.provider as string;
449
+ const action = args?.action as string;
450
+ const params = (args?.params as Record<string, any>) || {};
451
+
452
+ const result = await executeAPICall(provider, action, params);
453
+
454
+ return {
455
+ content: [
456
+ {
457
+ type: 'text',
458
+ text: JSON.stringify({
459
+ status: result.success ? 'success' : 'error',
460
+ provider: result.provider,
461
+ action: result.action,
462
+ ...(result.success ? { data: result.data } : { error: result.error }),
463
+ ...(result.cost !== undefined ? { cost_sek: result.cost } : {})
464
+ }, null, 2)
465
+ }
466
+ ],
467
+ isError: !result.success
468
+ };
469
+ }
470
+
471
+ case 'list_connected': {
472
+ const connected = getConnectedProviders();
473
+
474
+ return {
475
+ content: [
476
+ {
477
+ type: 'text',
478
+ text: JSON.stringify({
479
+ status: 'success',
480
+ message: 'These APIs are available for Instant Connect - no API key needed!',
481
+ connected_providers: connected,
482
+ usage: 'Use call_api with provider, action, and params to execute calls.'
353
483
  }, null, 2)
354
484
  }
355
485
  ]
@@ -390,7 +520,29 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
390
520
  async function main() {
391
521
  const transport = new StdioServerTransport();
392
522
  await server.connect(transport);
393
- console.error('APIvault MCP server running on stdio');
523
+ trackStartup();
524
+
525
+ // Welcome message with onboarding
526
+ console.error(`
527
+ 🦞 APIClaw v1.1.1 — The API Layer for AI Agents
528
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
529
+
530
+ ✓ 10,001 APIs indexed
531
+ ✓ 446 categories
532
+ ✓ 6 instant-connect providers ready
533
+
534
+ Quick Start:
535
+ discover_apis("send SMS to Sweden")
536
+ discover_apis("search the web")
537
+ discover_apis("generate speech from text")
538
+
539
+ Instant Connect (no API key needed):
540
+ get_connected_providers()
541
+ call_api({ provider: "brave_search", ... })
542
+
543
+ Docs: https://apiclaw.nordsym.com
544
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
545
+ `);
394
546
  }
395
547
 
396
548
  main().catch(console.error);
package/src/proxy.ts ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * APIClaw Proxy - Fallback to hosted API when no local credentials
3
+ */
4
+
5
+ const PROXY_BASE = "https://adventurous-avocet-799.convex.site/proxy";
6
+
7
+ export async function callProxy(provider: string, params: any): Promise<any> {
8
+ const url = `${PROXY_BASE}/${provider}`;
9
+
10
+ const response = await fetch(url, {
11
+ method: "POST",
12
+ headers: { "Content-Type": "application/json" },
13
+ body: JSON.stringify(params),
14
+ });
15
+
16
+ if (!response.ok) {
17
+ const errorData = await response.json().catch(() => ({ error: "Proxy request failed" })) as { error?: string };
18
+ throw new Error(errorData.error || `Proxy error: ${response.status}`);
19
+ }
20
+
21
+ return response.json();
22
+ }
23
+
24
+ export const PROXY_PROVIDERS = ["openrouter", "brave_search", "resend", "elevenlabs", "46elks", "twilio"];