@nordsym/apiclaw 2.5.8 → 2.5.10
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/dist/cli/commands/mcp-install.js +1 -1
- package/dist/cli/commands/mcp-install.js.map +1 -1
- package/dist/cli.js +3 -3
- package/dist/cli.js.map +1 -1
- package/dist/enterprise/env.d.ts +1 -1
- package/dist/enterprise/env.d.ts.map +1 -1
- package/dist/enterprise/env.js +1 -1
- package/dist/enterprise/env.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +412 -74
- package/dist/index.js.map +1 -1
- package/dist/proxy.js +1 -1
- package/dist/proxy.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* APIClaw - Agent-Native API Discovery MCP Server
|
|
4
4
|
*
|
|
5
5
|
* Tools:
|
|
6
6
|
* - discover_apis: Search for APIs by capability
|
|
@@ -20,7 +20,7 @@ import { hasRealCredentials } from './credentials.js';
|
|
|
20
20
|
import { getConnectedProviders } from './execute.js';
|
|
21
21
|
import { executeMetered } from './metered.js';
|
|
22
22
|
import { logAPICall } from './mcp-analytics.js';
|
|
23
|
-
import { isOpenAPI, executeOpenAPI, listOpenAPIs, getOpenAPIBaseUrl } from './open-apis.js';
|
|
23
|
+
import { isOpenAPI, executeOpenAPI, listOpenAPIs, getOpenAPIBaseUrl, getAPIClawTotalStats } from './open-apis.js';
|
|
24
24
|
import { getGateway, isGatewayEnabled } from './gateway-client.js';
|
|
25
25
|
import { requiresConfirmationAsync, createPendingAction, consumePendingAction, generatePreview, validateParams } from './confirmation.js';
|
|
26
26
|
import { executeCapability, listCapabilities, hasCapability } from './capability-router.js';
|
|
@@ -44,6 +44,128 @@ const anonymousRateLimits = new Map();
|
|
|
44
44
|
const ANONYMOUS_HOURLY_LIMIT = 5;
|
|
45
45
|
const ANONYMOUS_WEEKLY_LIMIT = 10;
|
|
46
46
|
const FREE_MONTHLY_LIMIT = 50;
|
|
47
|
+
const MAX_MCP_TOOL_RESULT_BYTES = 900_000;
|
|
48
|
+
const SOFT_TRANSPORT_LIMITS = {
|
|
49
|
+
maxDepth: 6,
|
|
50
|
+
maxArrayItems: 40,
|
|
51
|
+
maxObjectKeys: 50,
|
|
52
|
+
maxStringChars: 12_000,
|
|
53
|
+
};
|
|
54
|
+
const HARD_TRANSPORT_LIMITS = {
|
|
55
|
+
maxDepth: 4,
|
|
56
|
+
maxArrayItems: 12,
|
|
57
|
+
maxObjectKeys: 20,
|
|
58
|
+
maxStringChars: 3_000,
|
|
59
|
+
};
|
|
60
|
+
function measureUtf8Bytes(text) {
|
|
61
|
+
return Buffer.byteLength(text, 'utf8');
|
|
62
|
+
}
|
|
63
|
+
function truncateToolString(value, maxChars) {
|
|
64
|
+
if (value.length <= maxChars)
|
|
65
|
+
return value;
|
|
66
|
+
const omitted = value.length - maxChars;
|
|
67
|
+
return `${value.slice(0, maxChars)}\n...[truncated ${omitted} chars]`;
|
|
68
|
+
}
|
|
69
|
+
function summarizeOverflowValue(value) {
|
|
70
|
+
if (Array.isArray(value)) {
|
|
71
|
+
return `[Array(${value.length})]`;
|
|
72
|
+
}
|
|
73
|
+
if (value && typeof value === 'object') {
|
|
74
|
+
return `[Object keys=${Object.keys(value).length}]`;
|
|
75
|
+
}
|
|
76
|
+
return String(value);
|
|
77
|
+
}
|
|
78
|
+
function compactToolPayload(value, limits, depth = 0, seen = new WeakSet()) {
|
|
79
|
+
if (typeof value === 'string') {
|
|
80
|
+
return truncateToolString(value, limits.maxStringChars);
|
|
81
|
+
}
|
|
82
|
+
if (value === null || typeof value !== 'object') {
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
if (depth >= limits.maxDepth) {
|
|
86
|
+
return summarizeOverflowValue(value);
|
|
87
|
+
}
|
|
88
|
+
if (seen.has(value)) {
|
|
89
|
+
return '[Circular]';
|
|
90
|
+
}
|
|
91
|
+
seen.add(value);
|
|
92
|
+
if (Array.isArray(value)) {
|
|
93
|
+
const items = value
|
|
94
|
+
.slice(0, limits.maxArrayItems)
|
|
95
|
+
.map((item) => compactToolPayload(item, limits, depth + 1, seen));
|
|
96
|
+
if (value.length > limits.maxArrayItems) {
|
|
97
|
+
items.push(`[${value.length - limits.maxArrayItems} more items truncated]`);
|
|
98
|
+
}
|
|
99
|
+
return items;
|
|
100
|
+
}
|
|
101
|
+
const entries = Object.entries(value);
|
|
102
|
+
const output = {};
|
|
103
|
+
for (const [key, nested] of entries.slice(0, limits.maxObjectKeys)) {
|
|
104
|
+
output[key] = compactToolPayload(nested, limits, depth + 1, seen);
|
|
105
|
+
}
|
|
106
|
+
if (entries.length > limits.maxObjectKeys) {
|
|
107
|
+
output._truncated_keys = entries.length - limits.maxObjectKeys;
|
|
108
|
+
}
|
|
109
|
+
return output;
|
|
110
|
+
}
|
|
111
|
+
function wrapToolTransportMeta(payload, meta) {
|
|
112
|
+
if (payload && typeof payload === 'object' && !Array.isArray(payload)) {
|
|
113
|
+
return {
|
|
114
|
+
...payload,
|
|
115
|
+
_transport: meta,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
data: payload,
|
|
120
|
+
_transport: meta,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function safeJsonStringify(payload, options = {}) {
|
|
124
|
+
const pretty = options.pretty ?? true;
|
|
125
|
+
const stringify = (value, prettyPrint = pretty) => JSON.stringify(value, null, prettyPrint ? 2 : 0);
|
|
126
|
+
const initial = stringify(payload);
|
|
127
|
+
const initialBytes = measureUtf8Bytes(initial);
|
|
128
|
+
if (initialBytes <= MAX_MCP_TOOL_RESULT_BYTES) {
|
|
129
|
+
return initial;
|
|
130
|
+
}
|
|
131
|
+
const suggestion = options.hint ||
|
|
132
|
+
'Narrow the request, ask for a summary, paginate, or use compact=true when available.';
|
|
133
|
+
const softPayload = wrapToolTransportMeta(compactToolPayload(payload, SOFT_TRANSPORT_LIMITS), {
|
|
134
|
+
truncated: true,
|
|
135
|
+
reason: 'Tool result exceeded the MCP transport limit and was compacted automatically.',
|
|
136
|
+
original_bytes: initialBytes,
|
|
137
|
+
suggestion,
|
|
138
|
+
});
|
|
139
|
+
const softText = stringify(softPayload);
|
|
140
|
+
if (measureUtf8Bytes(softText) <= MAX_MCP_TOOL_RESULT_BYTES) {
|
|
141
|
+
return softText;
|
|
142
|
+
}
|
|
143
|
+
const hardPayload = wrapToolTransportMeta(compactToolPayload(payload, HARD_TRANSPORT_LIMITS), {
|
|
144
|
+
truncated: true,
|
|
145
|
+
reason: 'Tool result exceeded the MCP transport limit and was heavily compacted automatically.',
|
|
146
|
+
original_bytes: initialBytes,
|
|
147
|
+
suggestion,
|
|
148
|
+
});
|
|
149
|
+
const hardText = stringify(hardPayload);
|
|
150
|
+
if (measureUtf8Bytes(hardText) <= MAX_MCP_TOOL_RESULT_BYTES) {
|
|
151
|
+
return hardText;
|
|
152
|
+
}
|
|
153
|
+
return JSON.stringify({
|
|
154
|
+
status: 'partial',
|
|
155
|
+
preview: compactToolPayload(payload, {
|
|
156
|
+
maxDepth: 2,
|
|
157
|
+
maxArrayItems: 5,
|
|
158
|
+
maxObjectKeys: 10,
|
|
159
|
+
maxStringChars: 400,
|
|
160
|
+
}),
|
|
161
|
+
_transport: {
|
|
162
|
+
truncated: true,
|
|
163
|
+
reason: 'Tool result exceeded the MCP transport limit even after compaction.',
|
|
164
|
+
original_bytes: initialBytes,
|
|
165
|
+
suggestion,
|
|
166
|
+
},
|
|
167
|
+
}, null, 2);
|
|
168
|
+
}
|
|
47
169
|
/**
|
|
48
170
|
* Calculate minutes until next hour
|
|
49
171
|
*/
|
|
@@ -479,6 +601,76 @@ const SUGGESTED_CALL_RULES = [
|
|
|
479
601
|
intent: 'website screenshot',
|
|
480
602
|
},
|
|
481
603
|
},
|
|
604
|
+
{
|
|
605
|
+
match: /(scrape|scraper|scraping|crawl|crawler|browser.*automat|extract.*page|extract.*site|browserless|firecrawl|scrapingbee)/i,
|
|
606
|
+
suggestion: {
|
|
607
|
+
provider: 'firecrawl',
|
|
608
|
+
action: 'scrape',
|
|
609
|
+
description: 'Scrape a single URL to clean markdown via Firecrawl (APIClaw owns the key).',
|
|
610
|
+
example_params: { url: 'https://example.com', formats: ['markdown'] },
|
|
611
|
+
intent: 'web scraping / page extraction',
|
|
612
|
+
},
|
|
613
|
+
},
|
|
614
|
+
{
|
|
615
|
+
match: /(web[\s-]?search|search.*web|search.*google|search.*engine|serp|google.*results|brave.*search|search.*the.*internet)/i,
|
|
616
|
+
suggestion: {
|
|
617
|
+
provider: 'brave_search',
|
|
618
|
+
action: 'search',
|
|
619
|
+
description: 'Live web search via Brave (APIClaw owns the key).',
|
|
620
|
+
example_params: { q: 'apiclaw nordsym', count: 10 },
|
|
621
|
+
intent: 'web search',
|
|
622
|
+
},
|
|
623
|
+
},
|
|
624
|
+
{
|
|
625
|
+
match: /(\bllm\b|chat[\s-]?completion|chat.*model|gpt[\s-]?\d|claude[\s-]?\d|opus|sonnet|haiku|generate.*text|language.*model|reasoning.*model)/i,
|
|
626
|
+
suggestion: {
|
|
627
|
+
provider: 'openrouter',
|
|
628
|
+
action: 'chat',
|
|
629
|
+
description: 'Route to any of 800+ LLMs via OpenRouter; or use APIClaw advisor by passing model="auto" to /v1/chat/completions.',
|
|
630
|
+
example_params: { model: 'auto', messages: [{ role: 'user', content: 'Hello' }] },
|
|
631
|
+
intent: 'LLM chat / completion',
|
|
632
|
+
},
|
|
633
|
+
},
|
|
634
|
+
{
|
|
635
|
+
match: /(text[\s-]?to[\s-]?speech|\btts\b|generate.*speech|voice[\s-]?clone|elevenlabs|speech.*synthesis)/i,
|
|
636
|
+
suggestion: {
|
|
637
|
+
provider: 'elevenlabs',
|
|
638
|
+
action: 'text_to_speech',
|
|
639
|
+
description: 'Generate speech audio from text via ElevenLabs (APIClaw owns the key).',
|
|
640
|
+
example_params: { text: 'Hello from APIClaw.', voice_id: 'Rachel' },
|
|
641
|
+
intent: 'text-to-speech',
|
|
642
|
+
},
|
|
643
|
+
},
|
|
644
|
+
{
|
|
645
|
+
match: /(transcribe|speech[\s-]?to[\s-]?text|\bstt\b|deepgram|whisper|audio.*to.*text)/i,
|
|
646
|
+
suggestion: {
|
|
647
|
+
provider: 'deepgram',
|
|
648
|
+
action: 'transcribe',
|
|
649
|
+
description: 'Transcribe audio to text via Deepgram (APIClaw owns the key).',
|
|
650
|
+
example_params: { url: 'https://example.com/audio.mp3' },
|
|
651
|
+
intent: 'audio transcription',
|
|
652
|
+
},
|
|
653
|
+
},
|
|
654
|
+
{
|
|
655
|
+
match: /(generate.*image|image[\s-]?generation|text[\s-]?to[\s-]?image|stable[\s-]?diffusion|sdxl|flux|midjourney|dall[\s-]?e|create.*picture)/i,
|
|
656
|
+
suggestion: {
|
|
657
|
+
provider: 'replicate',
|
|
658
|
+
action: 'run',
|
|
659
|
+
description: 'Run any open-source image/video model via Replicate (APIClaw owns the key).',
|
|
660
|
+
example_params: { model: 'black-forest-labs/flux-schnell', input: { prompt: 'a lobster wearing a tiny hat' } },
|
|
661
|
+
intent: 'image / video generation',
|
|
662
|
+
},
|
|
663
|
+
},
|
|
664
|
+
{
|
|
665
|
+
match: /(run.*code|sandbox|execute.*python|execute.*javascript|code.*interpreter|e2b)/i,
|
|
666
|
+
suggestion: {
|
|
667
|
+
provider: 'e2b',
|
|
668
|
+
action: 'run_code',
|
|
669
|
+
description: 'Run code in an isolated cloud sandbox via E2B (APIClaw owns the key).',
|
|
670
|
+
example_params: { language: 'python', code: 'print(2+2)' },
|
|
671
|
+
intent: 'code execution sandbox',
|
|
672
|
+
},
|
|
673
|
+
},
|
|
482
674
|
];
|
|
483
675
|
function buildSuggestedCalls(query) {
|
|
484
676
|
if (!query)
|
|
@@ -528,7 +720,7 @@ const tools = [
|
|
|
528
720
|
},
|
|
529
721
|
{
|
|
530
722
|
name: 'discover_apis',
|
|
531
|
-
description: '
|
|
723
|
+
description: 'Find APIs by job-to-be-done. Use this when the user asks "what API can do X?", wants provider recommendations, or needs web search, scraping, email, SMS, speech, PDFs, browser automation, weather, finance, or other external capabilities.',
|
|
532
724
|
inputSchema: {
|
|
533
725
|
type: 'object',
|
|
534
726
|
properties: {
|
|
@@ -538,8 +730,12 @@ const tools = [
|
|
|
538
730
|
},
|
|
539
731
|
category: {
|
|
540
732
|
type: 'string',
|
|
541
|
-
description: '
|
|
542
|
-
|
|
733
|
+
description: 'Optional category filter. Categories are case-sensitive — call list_categories first to see exact names (e.g., "Utilities", "Analytics", "Development", "AI & ML", "Cloud", "Finance", "Communication", "Location", "Entertainment", "Security", "Health").',
|
|
734
|
+
},
|
|
735
|
+
callable_only: {
|
|
736
|
+
type: 'boolean',
|
|
737
|
+
description: 'If true, only return APIs APIClaw can execute right now (managed providers + keyless open APIs). If false (default) you also see discovery-only entries — useful when scoping integrations, but agents that just want to act should set this to true.',
|
|
738
|
+
default: false,
|
|
543
739
|
},
|
|
544
740
|
max_results: {
|
|
545
741
|
type: 'number',
|
|
@@ -564,7 +760,7 @@ const tools = [
|
|
|
564
760
|
},
|
|
565
761
|
{
|
|
566
762
|
name: 'get_api_details',
|
|
567
|
-
description: '
|
|
763
|
+
description: 'Inspect one provider after discovery. Good when the agent needs endpoint names, params, pricing, auth, or docs. Use compact=true to avoid oversized responses in Claude/Desktop.',
|
|
568
764
|
inputSchema: {
|
|
569
765
|
type: 'object',
|
|
570
766
|
properties: {
|
|
@@ -636,15 +832,22 @@ const tools = [
|
|
|
636
832
|
},
|
|
637
833
|
{
|
|
638
834
|
name: 'list_categories',
|
|
639
|
-
description: 'List
|
|
835
|
+
description: 'List API categories with total + callable counts. Lightweight by design — does NOT dump every API ID. Use discover_apis(query, category) to drill into a category.',
|
|
640
836
|
inputSchema: {
|
|
641
837
|
type: 'object',
|
|
642
|
-
properties: {
|
|
643
|
-
|
|
838
|
+
properties: {
|
|
839
|
+
with_api_ids: {
|
|
840
|
+
type: 'boolean',
|
|
841
|
+
description: 'If true, include the full API id list per category (large response, will auto-compact). Default: false.',
|
|
842
|
+
default: false,
|
|
843
|
+
},
|
|
844
|
+
},
|
|
845
|
+
required: [],
|
|
846
|
+
},
|
|
644
847
|
},
|
|
645
848
|
{
|
|
646
849
|
name: 'call_api',
|
|
647
|
-
description: `
|
|
850
|
+
description: `Primary execution tool. Use this to actually do the job through APIClaw: live web search, scraping, email, SMS, speech, LLM calls, invoices, screenshots, currency, weather, and other external API work. Requires free registration; if not registered, call register_owner first.
|
|
648
851
|
|
|
649
852
|
SINGLE CALL: Provide provider + action + params
|
|
650
853
|
CHAIN: Provide chain array to execute multiple APIs in sequence/parallel with cross-step references.
|
|
@@ -757,15 +960,26 @@ Example chain:
|
|
|
757
960
|
},
|
|
758
961
|
{
|
|
759
962
|
name: 'list_connected',
|
|
760
|
-
description: '
|
|
963
|
+
description: 'Summary of providers callable right now through APIClaw with no key paste. Defaults to a compact summary (managed providers + open-API counts). Pass verbose=true only if the agent explicitly needs the full open-API list. Use discover_apis(query) for narrow lookups instead of dumping the whole catalog.',
|
|
761
964
|
inputSchema: {
|
|
762
965
|
type: 'object',
|
|
763
|
-
properties: {
|
|
764
|
-
|
|
966
|
+
properties: {
|
|
967
|
+
verbose: {
|
|
968
|
+
type: 'boolean',
|
|
969
|
+
description: 'If true, also include the full keyless open-API list (large response, will auto-compact). Default: false.',
|
|
970
|
+
default: false,
|
|
971
|
+
},
|
|
972
|
+
category: {
|
|
973
|
+
type: 'string',
|
|
974
|
+
description: 'Optional category filter for the open-API list when verbose=true.',
|
|
975
|
+
},
|
|
976
|
+
},
|
|
977
|
+
required: [],
|
|
978
|
+
},
|
|
765
979
|
},
|
|
766
980
|
{
|
|
767
981
|
name: 'capability',
|
|
768
|
-
description: '
|
|
982
|
+
description: 'Best default when you know the job but not the provider. Execute by intent such as sms, email, search, tts, invoice, or llm, and APIClaw picks the provider plus fallback automatically.',
|
|
769
983
|
inputSchema: {
|
|
770
984
|
type: 'object',
|
|
771
985
|
properties: {
|
|
@@ -805,7 +1019,7 @@ Example chain:
|
|
|
805
1019
|
},
|
|
806
1020
|
{
|
|
807
1021
|
name: 'list_capabilities',
|
|
808
|
-
description: 'List
|
|
1022
|
+
description: 'List high-level jobs APIClaw can do, such as sms, email, search, tts, invoice, or llm, and which providers back them.',
|
|
809
1023
|
inputSchema: {
|
|
810
1024
|
type: 'object',
|
|
811
1025
|
properties: {}
|
|
@@ -958,8 +1172,8 @@ Example chain:
|
|
|
958
1172
|
];
|
|
959
1173
|
// Create server
|
|
960
1174
|
const server = new Server({
|
|
961
|
-
name: '
|
|
962
|
-
version: '0.1.0',
|
|
1175
|
+
name: 'apiclaw',
|
|
1176
|
+
version: process.env.npm_package_version || '0.1.0',
|
|
963
1177
|
}, {
|
|
964
1178
|
capabilities: {
|
|
965
1179
|
tools: {},
|
|
@@ -1011,12 +1225,25 @@ Docs: https://apiclaw.cloud
|
|
|
1011
1225
|
case 'discover_apis': {
|
|
1012
1226
|
const query = args?.query;
|
|
1013
1227
|
const category = args?.category;
|
|
1014
|
-
const
|
|
1228
|
+
const requestedMax = args?.max_results || 5;
|
|
1229
|
+
const callableOnly = args?.callable_only === true;
|
|
1015
1230
|
const region = args?.region;
|
|
1016
1231
|
const subagentId = args?.subagent_id;
|
|
1017
1232
|
const aiBackend = args?.ai_backend;
|
|
1018
1233
|
const startTime = Date.now();
|
|
1019
|
-
|
|
1234
|
+
// When callable_only is set, over-fetch then filter so the agent still
|
|
1235
|
+
// gets the requested count of useful entries.
|
|
1236
|
+
const fetchMax = callableOnly ? Math.max(requestedMax * 6, 30) : requestedMax;
|
|
1237
|
+
const rawResults = discoverAPIs(query, { category, maxResults: fetchMax, region });
|
|
1238
|
+
const filteredResults = callableOnly
|
|
1239
|
+
? rawResults.filter((r) => r.provider.callable === true)
|
|
1240
|
+
: rawResults;
|
|
1241
|
+
// Stable callable-first ordering (preserve relevance order within each bucket).
|
|
1242
|
+
const results = [...filteredResults].sort((a, b) => {
|
|
1243
|
+
const aCall = a.provider.callable === true ? 0 : 1;
|
|
1244
|
+
const bCall = b.provider.callable === true ? 0 : 1;
|
|
1245
|
+
return aCall - bCall;
|
|
1246
|
+
}).slice(0, requestedMax);
|
|
1020
1247
|
const responseTimeMs = Date.now() - startTime;
|
|
1021
1248
|
trackSearch(query, results.length, responseTimeMs);
|
|
1022
1249
|
// Suggested-call enrichment.
|
|
@@ -1113,23 +1340,33 @@ Docs: https://apiclaw.cloud
|
|
|
1113
1340
|
content: [
|
|
1114
1341
|
{
|
|
1115
1342
|
type: 'text',
|
|
1116
|
-
text:
|
|
1343
|
+
text: safeJsonStringify({
|
|
1117
1344
|
status: 'no_results',
|
|
1118
1345
|
message: `No APIs found matching "${query}". Try broader terms or check available categories with list_categories.`,
|
|
1119
1346
|
available_categories: getCategories()
|
|
1120
|
-
}
|
|
1347
|
+
})
|
|
1121
1348
|
}
|
|
1122
1349
|
]
|
|
1123
1350
|
};
|
|
1124
1351
|
}
|
|
1352
|
+
const callableCount = results.filter((r) => r.provider.callable === true).length;
|
|
1353
|
+
const discoveryOnlyCount = results.length - callableCount;
|
|
1125
1354
|
return {
|
|
1126
1355
|
content: [
|
|
1127
1356
|
{
|
|
1128
1357
|
type: 'text',
|
|
1129
|
-
text:
|
|
1358
|
+
text: safeJsonStringify({
|
|
1130
1359
|
status: 'success',
|
|
1131
1360
|
query,
|
|
1132
1361
|
results_count: results.length,
|
|
1362
|
+
callable_count: callableCount,
|
|
1363
|
+
discovery_only_count: discoveryOnlyCount,
|
|
1364
|
+
...(callableCount === 0 && !callableOnly
|
|
1365
|
+
? {
|
|
1366
|
+
no_callable_match: true,
|
|
1367
|
+
no_callable_match_hint: 'No directly-callable provider matched. Try call_api({provider:"generic", action:"request", params:{url, method, ...}}) for any keyless public endpoint, or refine the query, or call list_connected to see what APIClaw can execute right now.',
|
|
1368
|
+
}
|
|
1369
|
+
: {}),
|
|
1133
1370
|
...(suggestedCalls.length > 0
|
|
1134
1371
|
? {
|
|
1135
1372
|
suggested_calls: suggestedCalls,
|
|
@@ -1158,7 +1395,9 @@ Docs: https://apiclaw.cloud
|
|
|
1158
1395
|
: { tool: null, endpoint: null, hint: 'Discovery-only. See docsUrl for integration.' },
|
|
1159
1396
|
};
|
|
1160
1397
|
})
|
|
1161
|
-
},
|
|
1398
|
+
}, {
|
|
1399
|
+
hint: 'Lower max_results or inspect one provider at a time if you need the full discovery payload.',
|
|
1400
|
+
})
|
|
1162
1401
|
}
|
|
1163
1402
|
]
|
|
1164
1403
|
};
|
|
@@ -1172,11 +1411,11 @@ Docs: https://apiclaw.cloud
|
|
|
1172
1411
|
content: [
|
|
1173
1412
|
{
|
|
1174
1413
|
type: 'text',
|
|
1175
|
-
text:
|
|
1414
|
+
text: safeJsonStringify({
|
|
1176
1415
|
status: 'error',
|
|
1177
1416
|
message: `API not found: ${apiId}`,
|
|
1178
1417
|
hint: 'Try discover_apis to search, or list_connected for direct-call APIs',
|
|
1179
|
-
}
|
|
1418
|
+
})
|
|
1180
1419
|
}
|
|
1181
1420
|
]
|
|
1182
1421
|
};
|
|
@@ -1187,7 +1426,10 @@ Docs: https://apiclaw.cloud
|
|
|
1187
1426
|
content: [
|
|
1188
1427
|
{
|
|
1189
1428
|
type: 'text',
|
|
1190
|
-
text:
|
|
1429
|
+
text: safeJsonStringify({ status: 'ok', ...api }, {
|
|
1430
|
+
pretty: false,
|
|
1431
|
+
hint: 'Retry with compact=true or inspect a single endpoint if you need less metadata.',
|
|
1432
|
+
})
|
|
1191
1433
|
}
|
|
1192
1434
|
]
|
|
1193
1435
|
};
|
|
@@ -1196,10 +1438,12 @@ Docs: https://apiclaw.cloud
|
|
|
1196
1438
|
content: [
|
|
1197
1439
|
{
|
|
1198
1440
|
type: 'text',
|
|
1199
|
-
text:
|
|
1441
|
+
text: safeJsonStringify({
|
|
1200
1442
|
status: 'success',
|
|
1201
1443
|
api
|
|
1202
|
-
},
|
|
1444
|
+
}, {
|
|
1445
|
+
hint: 'Retry get_api_details({ api_id, compact: true }) for a smaller provider spec.',
|
|
1446
|
+
})
|
|
1203
1447
|
}
|
|
1204
1448
|
]
|
|
1205
1449
|
};
|
|
@@ -1293,23 +1537,50 @@ Docs: https://apiclaw.cloud
|
|
|
1293
1537
|
};
|
|
1294
1538
|
}
|
|
1295
1539
|
case 'list_categories': {
|
|
1540
|
+
const withApiIds = args?.with_api_ids === true;
|
|
1296
1541
|
const categories = getCategories();
|
|
1297
|
-
const
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1542
|
+
const allAPIs = getAllAPIs();
|
|
1543
|
+
const summary = categories
|
|
1544
|
+
.map((cat) => {
|
|
1545
|
+
const inCat = allAPIs.filter((a) => a.category === cat);
|
|
1546
|
+
const callable = inCat.filter((a) => {
|
|
1547
|
+
const anyA = a;
|
|
1548
|
+
return anyA.callable === true;
|
|
1549
|
+
}).length;
|
|
1550
|
+
const entry = {
|
|
1551
|
+
category: cat,
|
|
1552
|
+
total: inCat.length,
|
|
1553
|
+
callable,
|
|
1554
|
+
};
|
|
1555
|
+
if (withApiIds) {
|
|
1556
|
+
entry.api_ids = inCat.map((a) => a.id);
|
|
1557
|
+
}
|
|
1558
|
+
return entry;
|
|
1559
|
+
})
|
|
1560
|
+
.sort((a, b) => b.total - a.total);
|
|
1561
|
+
const totalAPIs = allAPIs.length;
|
|
1562
|
+
const totalCallable = allAPIs.filter((a) => {
|
|
1563
|
+
const anyA = a;
|
|
1564
|
+
return anyA.callable === true;
|
|
1565
|
+
}).length;
|
|
1303
1566
|
return {
|
|
1304
1567
|
content: [
|
|
1305
1568
|
{
|
|
1306
1569
|
type: 'text',
|
|
1307
|
-
text:
|
|
1570
|
+
text: safeJsonStringify({
|
|
1308
1571
|
status: 'success',
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1572
|
+
totals: {
|
|
1573
|
+
categories: categories.length,
|
|
1574
|
+
apis_indexed: totalAPIs,
|
|
1575
|
+
apis_callable: totalCallable,
|
|
1576
|
+
},
|
|
1577
|
+
hint: 'Use discover_apis({ query, category }) to find APIs in a specific category. Pass with_api_ids=true here only if you really need every id.',
|
|
1578
|
+
categories: summary,
|
|
1579
|
+
}, {
|
|
1580
|
+
hint: 'Use discover_apis({query, category}) for a narrow slice instead of every API id.',
|
|
1581
|
+
}),
|
|
1582
|
+
},
|
|
1583
|
+
],
|
|
1313
1584
|
};
|
|
1314
1585
|
}
|
|
1315
1586
|
case 'call_api': {
|
|
@@ -1390,7 +1661,7 @@ Docs: https://apiclaw.cloud
|
|
|
1390
1661
|
return {
|
|
1391
1662
|
content: [{
|
|
1392
1663
|
type: 'text',
|
|
1393
|
-
text:
|
|
1664
|
+
text: safeJsonStringify({
|
|
1394
1665
|
status: chainResult.success ? 'success' : 'error',
|
|
1395
1666
|
mode: 'chain',
|
|
1396
1667
|
chainId: chainResult.chainId,
|
|
@@ -1417,7 +1688,9 @@ Docs: https://apiclaw.cloud
|
|
|
1417
1688
|
canResume: chainResult.canResume,
|
|
1418
1689
|
resumeToken: chainResult.resumeToken,
|
|
1419
1690
|
} : {}),
|
|
1420
|
-
},
|
|
1691
|
+
}, {
|
|
1692
|
+
hint: 'Inspect one step at a time or reduce step outputs if the chain result is too large.',
|
|
1693
|
+
})
|
|
1421
1694
|
}],
|
|
1422
1695
|
isError: !chainResult.success
|
|
1423
1696
|
};
|
|
@@ -1426,11 +1699,11 @@ Docs: https://apiclaw.cloud
|
|
|
1426
1699
|
return {
|
|
1427
1700
|
content: [{
|
|
1428
1701
|
type: 'text',
|
|
1429
|
-
text:
|
|
1702
|
+
text: safeJsonStringify({
|
|
1430
1703
|
status: 'error',
|
|
1431
1704
|
mode: 'chain',
|
|
1432
1705
|
error: error instanceof Error ? error.message : String(error),
|
|
1433
|
-
}
|
|
1706
|
+
})
|
|
1434
1707
|
}],
|
|
1435
1708
|
isError: true
|
|
1436
1709
|
};
|
|
@@ -1446,7 +1719,9 @@ Docs: https://apiclaw.cloud
|
|
|
1446
1719
|
return {
|
|
1447
1720
|
content: [{
|
|
1448
1721
|
type: 'text',
|
|
1449
|
-
text:
|
|
1722
|
+
text: safeJsonStringify(dryRunResult, {
|
|
1723
|
+
hint: 'Use smaller params or one step at a time for a shorter preview.',
|
|
1724
|
+
})
|
|
1450
1725
|
}]
|
|
1451
1726
|
};
|
|
1452
1727
|
}
|
|
@@ -1533,13 +1808,15 @@ Docs: https://apiclaw.cloud
|
|
|
1533
1808
|
return {
|
|
1534
1809
|
content: [{
|
|
1535
1810
|
type: 'text',
|
|
1536
|
-
text:
|
|
1811
|
+
text: safeJsonStringify({
|
|
1537
1812
|
status: result.success ? 'success' : 'error',
|
|
1538
1813
|
provider: result.provider,
|
|
1539
1814
|
action: result.action,
|
|
1540
1815
|
confirmed: true,
|
|
1541
1816
|
...(result.success ? { data: result.data } : { error: result.error }),
|
|
1542
|
-
},
|
|
1817
|
+
}, {
|
|
1818
|
+
hint: 'Ask for a summary or narrower params if the confirmed result is very large.',
|
|
1819
|
+
})
|
|
1543
1820
|
}],
|
|
1544
1821
|
isError: !result.success
|
|
1545
1822
|
};
|
|
@@ -1719,7 +1996,7 @@ Docs: https://apiclaw.cloud
|
|
|
1719
1996
|
content: [
|
|
1720
1997
|
{
|
|
1721
1998
|
type: 'text',
|
|
1722
|
-
text:
|
|
1999
|
+
text: safeJsonStringify({
|
|
1723
2000
|
status: 'success',
|
|
1724
2001
|
message: 'Linked APIClaw to your workspace and ran the call.',
|
|
1725
2002
|
provider: retry.provider,
|
|
@@ -1727,7 +2004,9 @@ Docs: https://apiclaw.cloud
|
|
|
1727
2004
|
type: apiType,
|
|
1728
2005
|
data: retry.data,
|
|
1729
2006
|
...(retry.cost !== undefined ? { cost_sek: retry.cost } : {}),
|
|
1730
|
-
},
|
|
2007
|
+
}, {
|
|
2008
|
+
hint: 'Retry with narrower params or ask for a summary if the linked-call result is too large.',
|
|
2009
|
+
}),
|
|
1731
2010
|
},
|
|
1732
2011
|
],
|
|
1733
2012
|
};
|
|
@@ -1773,34 +2052,85 @@ Docs: https://apiclaw.cloud
|
|
|
1773
2052
|
content: [
|
|
1774
2053
|
{
|
|
1775
2054
|
type: 'text',
|
|
1776
|
-
text:
|
|
2055
|
+
text: safeJsonStringify(responseData, {
|
|
2056
|
+
hint: 'Narrow params, paginate, or ask for a summarized result if the full dataset is too large.',
|
|
2057
|
+
})
|
|
1777
2058
|
}
|
|
1778
2059
|
],
|
|
1779
2060
|
isError: !result.success
|
|
1780
2061
|
};
|
|
1781
2062
|
}
|
|
1782
2063
|
case 'list_connected': {
|
|
2064
|
+
const verbose = args?.verbose === true;
|
|
2065
|
+
const filterCategory = typeof args?.category === 'string' ? args.category : undefined;
|
|
1783
2066
|
const directProviders = getConnectedProviders();
|
|
1784
2067
|
const openProviders = listOpenAPIs();
|
|
2068
|
+
const stats = getAPIClawTotalStats();
|
|
2069
|
+
// Cheap top-N "what kind of open APIs are there" rollup so the agent
|
|
2070
|
+
// gets a useful narrative without 9k entries.
|
|
2071
|
+
const allAPIs = getAllAPIs();
|
|
2072
|
+
const openCategoryCounts = {};
|
|
2073
|
+
for (const a of allAPIs) {
|
|
2074
|
+
const anyA = a;
|
|
2075
|
+
if (anyA.callable === true && anyA.category) {
|
|
2076
|
+
openCategoryCounts[anyA.category] = (openCategoryCounts[anyA.category] || 0) + 1;
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
const topOpenCategories = Object.entries(openCategoryCounts)
|
|
2080
|
+
.sort((a, b) => b[1] - a[1])
|
|
2081
|
+
.slice(0, 12)
|
|
2082
|
+
.map(([category, count]) => ({ category, callable: count }));
|
|
2083
|
+
const summary = {
|
|
2084
|
+
status: 'success',
|
|
2085
|
+
message: 'APIClaw can execute these RIGHT NOW — no key paste, no integration code.',
|
|
2086
|
+
counts: {
|
|
2087
|
+
managed_providers: directProviders.length,
|
|
2088
|
+
open_callable_providers: openProviders.length,
|
|
2089
|
+
total_callable_apis: stats.total_callable,
|
|
2090
|
+
indexed_for_discovery: stats.tier1_discovery_indexed,
|
|
2091
|
+
},
|
|
2092
|
+
managed_providers: {
|
|
2093
|
+
description: 'APIClaw owns the keys. Free tier: 25 calls/month across the whole platform, then pay-as-you-go (provider cost + 15%).',
|
|
2094
|
+
providers: directProviders,
|
|
2095
|
+
},
|
|
2096
|
+
open_apis_summary: {
|
|
2097
|
+
description: 'Keyless public APIs proxied via call_api({provider, action, params}). Free.',
|
|
2098
|
+
total_providers: openProviders.length,
|
|
2099
|
+
top_categories: topOpenCategories,
|
|
2100
|
+
generic_passthrough: {
|
|
2101
|
+
hint: 'Use call_api({provider:"generic", action:"request", params:{url, method, headers, query, body}}) to hit any keyless public endpoint not curated below.',
|
|
2102
|
+
},
|
|
2103
|
+
},
|
|
2104
|
+
usage: 'discover_apis(query) for narrow search. call_api(provider, action, params) to execute. Set verbose=true to also see the full open-API list.',
|
|
2105
|
+
};
|
|
2106
|
+
if (verbose) {
|
|
2107
|
+
const filtered = filterCategory
|
|
2108
|
+
? allAPIs.filter((a) => {
|
|
2109
|
+
const anyA = a;
|
|
2110
|
+
return anyA.callable === true && anyA.category === filterCategory;
|
|
2111
|
+
}).map((a) => ({
|
|
2112
|
+
provider: a.id,
|
|
2113
|
+
name: a.name,
|
|
2114
|
+
category: a.category,
|
|
2115
|
+
}))
|
|
2116
|
+
: openProviders;
|
|
2117
|
+
summary.open_apis_full = {
|
|
2118
|
+
description: filterCategory
|
|
2119
|
+
? `Open APIs in category "${filterCategory}".`
|
|
2120
|
+
: 'Full keyless open-API list (auto-compacted if oversized; prefer discover_apis for narrow lookups).',
|
|
2121
|
+
count: filtered.length,
|
|
2122
|
+
providers: filtered,
|
|
2123
|
+
};
|
|
2124
|
+
}
|
|
1785
2125
|
return {
|
|
1786
2126
|
content: [
|
|
1787
2127
|
{
|
|
1788
2128
|
type: 'text',
|
|
1789
|
-
text:
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
providers: directProviders,
|
|
1795
|
-
},
|
|
1796
|
-
open_apis: {
|
|
1797
|
-
description: 'Free, open APIs (no auth required)',
|
|
1798
|
-
providers: openProviders,
|
|
1799
|
-
},
|
|
1800
|
-
usage: 'Use call_api with provider, action, and params to execute calls.'
|
|
1801
|
-
}, null, 2)
|
|
1802
|
-
}
|
|
1803
|
-
]
|
|
2129
|
+
text: safeJsonStringify(summary, {
|
|
2130
|
+
hint: 'Use discover_apis(query) or capability() for a narrower slice instead of listing everything.',
|
|
2131
|
+
}),
|
|
2132
|
+
},
|
|
2133
|
+
],
|
|
1804
2134
|
};
|
|
1805
2135
|
}
|
|
1806
2136
|
case 'capability': {
|
|
@@ -1837,12 +2167,12 @@ Docs: https://apiclaw.cloud
|
|
|
1837
2167
|
return {
|
|
1838
2168
|
content: [{
|
|
1839
2169
|
type: 'text',
|
|
1840
|
-
text:
|
|
2170
|
+
text: safeJsonStringify({
|
|
1841
2171
|
status: 'error',
|
|
1842
2172
|
error: `Unknown capability: ${capabilityId}`,
|
|
1843
2173
|
available_capabilities: available.map(c => c.id),
|
|
1844
2174
|
hint: 'Use list_capabilities to see all available capabilities.'
|
|
1845
|
-
}
|
|
2175
|
+
})
|
|
1846
2176
|
}],
|
|
1847
2177
|
isError: true
|
|
1848
2178
|
};
|
|
@@ -1852,7 +2182,7 @@ Docs: https://apiclaw.cloud
|
|
|
1852
2182
|
return {
|
|
1853
2183
|
content: [{
|
|
1854
2184
|
type: 'text',
|
|
1855
|
-
text:
|
|
2185
|
+
text: safeJsonStringify({
|
|
1856
2186
|
status: result.success ? 'success' : 'error',
|
|
1857
2187
|
capability: result.capability,
|
|
1858
2188
|
action: result.action,
|
|
@@ -1862,7 +2192,9 @@ Docs: https://apiclaw.cloud
|
|
|
1862
2192
|
...(result.success ? { data: result.data } : { error: result.error }),
|
|
1863
2193
|
...(result.cost !== undefined ? { cost: result.cost, currency: result.currency } : {}),
|
|
1864
2194
|
latency_ms: result.latencyMs,
|
|
1865
|
-
},
|
|
2195
|
+
}, {
|
|
2196
|
+
hint: 'Use more specific params or one capability call at a time for a smaller payload.',
|
|
2197
|
+
})
|
|
1866
2198
|
}],
|
|
1867
2199
|
isError: !result.success
|
|
1868
2200
|
};
|
|
@@ -1872,12 +2204,14 @@ Docs: https://apiclaw.cloud
|
|
|
1872
2204
|
return {
|
|
1873
2205
|
content: [{
|
|
1874
2206
|
type: 'text',
|
|
1875
|
-
text:
|
|
2207
|
+
text: safeJsonStringify({
|
|
1876
2208
|
status: 'success',
|
|
1877
2209
|
message: 'Available capabilities - use capability() to execute',
|
|
1878
2210
|
capabilities,
|
|
1879
2211
|
usage: 'capability("sms", "send", {to: "+46...", message: "Hello"})'
|
|
1880
|
-
},
|
|
2212
|
+
}, {
|
|
2213
|
+
hint: 'If this list is too broad, ask for a specific capability like search, sms, email, tts, invoice, or llm.',
|
|
2214
|
+
})
|
|
1881
2215
|
}]
|
|
1882
2216
|
};
|
|
1883
2217
|
}
|
|
@@ -2457,7 +2791,7 @@ Docs: https://apiclaw.cloud
|
|
|
2457
2791
|
return {
|
|
2458
2792
|
content: [{
|
|
2459
2793
|
type: 'text',
|
|
2460
|
-
text:
|
|
2794
|
+
text: safeJsonStringify({
|
|
2461
2795
|
status: 'success',
|
|
2462
2796
|
chain: {
|
|
2463
2797
|
chainId: chainStatus.chainId,
|
|
@@ -2475,7 +2809,9 @@ Docs: https://apiclaw.cloud
|
|
|
2475
2809
|
}
|
|
2476
2810
|
} : {})
|
|
2477
2811
|
}
|
|
2478
|
-
},
|
|
2812
|
+
}, {
|
|
2813
|
+
hint: 'Inspect one chain step or ask for a shorter status summary if needed.',
|
|
2814
|
+
})
|
|
2479
2815
|
}]
|
|
2480
2816
|
};
|
|
2481
2817
|
}
|
|
@@ -2531,7 +2867,7 @@ Docs: https://apiclaw.cloud
|
|
|
2531
2867
|
return {
|
|
2532
2868
|
content: [{
|
|
2533
2869
|
type: 'text',
|
|
2534
|
-
text:
|
|
2870
|
+
text: safeJsonStringify({
|
|
2535
2871
|
status: result.success ? 'success' : 'error',
|
|
2536
2872
|
mode: 'chain_resumed',
|
|
2537
2873
|
chainId: result.chainId,
|
|
@@ -2550,7 +2886,9 @@ Docs: https://apiclaw.cloud
|
|
|
2550
2886
|
canResume: result.canResume,
|
|
2551
2887
|
resumeToken: result.resumeToken,
|
|
2552
2888
|
} : {}),
|
|
2553
|
-
},
|
|
2889
|
+
}, {
|
|
2890
|
+
hint: 'Inspect one resumed step at a time or reduce step outputs if the trace is too large.',
|
|
2891
|
+
})
|
|
2554
2892
|
}],
|
|
2555
2893
|
isError: !result.success
|
|
2556
2894
|
};
|