auxilo-mcp 0.9.1 → 0.9.2
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 +130 -149
- package/bin/auxilo-cli.js +230 -68
- package/lib/installer.js +5 -1
- package/lib/review.js +170 -3
- package/mcp-server.js +243 -146
- package/package.json +11 -2
- package/scripts/extract-local.js +119 -0
- package/scripts/runner.js +10 -8
package/mcp-server.js
CHANGED
|
@@ -10,14 +10,19 @@ const {
|
|
|
10
10
|
ListToolsRequestSchema,
|
|
11
11
|
} = require('@modelcontextprotocol/sdk/types.js');
|
|
12
12
|
|
|
13
|
+
// Review-seamless: pure selection + chunking helpers shared with the CLI so the
|
|
14
|
+
// MCP dry run and the confirmed run use the SAME logic (lib/review.js ships in
|
|
15
|
+
// the npm package alongside this file).
|
|
16
|
+
const reviewLib = require('./lib/review.js');
|
|
17
|
+
|
|
13
18
|
// Credential file reading — auto-configure base URL and API key
|
|
14
19
|
const CRED_PATH = path.join(os.homedir(), '.auxilo', 'credentials.json');
|
|
15
20
|
let credentials = {};
|
|
16
21
|
try {
|
|
17
22
|
credentials = JSON.parse(fs.readFileSync(CRED_PATH, 'utf8'));
|
|
18
23
|
} catch { /* no credentials file — unauthenticated mode */ }
|
|
19
|
-
// LW-17: default was a long-dead
|
|
20
|
-
// credentials.json pointed every tool call at it.
|
|
24
|
+
// LW-17: default was a long-dead sandbox URL on a retired host, so fresh installs
|
|
25
|
+
// without credentials.json pointed every tool call at it.
|
|
21
26
|
const AUXILO_BASE = credentials.base_url || 'https://auxilo.io';
|
|
22
27
|
|
|
23
28
|
function baseHeaders(extra = {}) {
|
|
@@ -55,8 +60,107 @@ function fenceUnlockResult(data) {
|
|
|
55
60
|
};
|
|
56
61
|
}
|
|
57
62
|
|
|
63
|
+
// AUD19-5: decide how the unlock handler presents a payment-required response.
|
|
64
|
+
// 402 = x402-first challenge (server >= AUD19-5: accepts[] merged with the
|
|
65
|
+
// options envelope, served cold). 401-with-options = legacy servers that only
|
|
66
|
+
// returned the options block — same meaning, no accepts[]. Returns null when
|
|
67
|
+
// the response is not a payment challenge (caller falls through to
|
|
68
|
+
// fenceUnlockResult). Pure (no I/O) — unit-tested.
|
|
69
|
+
function unlockPaymentRequired(status, data, http_endpoint) {
|
|
70
|
+
const isChallenge = status === 402 || (status === 401 && data && data.options);
|
|
71
|
+
if (!isChallenge) return null;
|
|
72
|
+
const price = data.accepts?.[0]?.maxAmountRequired
|
|
73
|
+
? `$${(Number(data.accepts[0].maxAmountRequired) / 1_000_000).toFixed(4)}`
|
|
74
|
+
: (data.options?.x402_payment?.price_usd != null
|
|
75
|
+
? `$${Number(data.options.x402_payment.price_usd).toFixed(4)}` : 'dynamic');
|
|
76
|
+
return {
|
|
77
|
+
status: 'payment_required',
|
|
78
|
+
cost: `${price} USDC on Base (set by contributor)`,
|
|
79
|
+
how_to_pay: 'Pass an x402 payment via the x_payment argument, or configure an API key with unlock credits (npx auxilo setup).',
|
|
80
|
+
http_endpoint,
|
|
81
|
+
payment_details: data,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// AUD19-7 (DASH/ROUTE): shape the status-and-routing response for
|
|
86
|
+
// auxilo_withdraw from a GET /account/earnings body. This tool reports
|
|
87
|
+
// balances and the payout path; it NEVER moves funds and never calls
|
|
88
|
+
// POST /withdraw. Pure (no I/O) — unit-tested.
|
|
89
|
+
function shapeWithdrawStatus(e) {
|
|
90
|
+
return {
|
|
91
|
+
payout_model: 'Earnings from on-chain-settled sales are paid to your linked wallet at sale time. Only the legacy accrued balance below is withdrawable.',
|
|
92
|
+
legacy_pending_balance_usd: e.pending_balance,
|
|
93
|
+
held_pending_terms_acceptance_usd: e.unassented_pending,
|
|
94
|
+
payouts_paused: e.payouts_paused,
|
|
95
|
+
how_to_withdraw_legacy_balance: e.payouts_paused
|
|
96
|
+
? 'Withdrawals of the legacy balance are paused during the settlement migration. Your balance is recorded and payable under the Terms; watch https://auxilo.io/status for the reopen.'
|
|
97
|
+
: 'Withdraw from your dashboard: https://auxilo.io/dashboard (bank payout via Stripe; $0.50 minimum, $0.25 fee).',
|
|
98
|
+
get_paid_at_sale_time: 'auxilo_accept_terms -> auxilo_verify_wallet -> auxilo_link_wallet',
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// AUD19-7: the verify-wallet surface legitimately needs only the wallet-
|
|
103
|
+
// verification flow. Forwarding the raw args object let a caller mint OTHER
|
|
104
|
+
// challenge types (/wallet/challenge honors action:'withdrawal') through an
|
|
105
|
+
// undocumented passthrough — closed: only wallet + signature ever travel, so
|
|
106
|
+
// the server's default (verification) action always applies. Pure — unit-tested.
|
|
107
|
+
function verifyWalletRequestBody(args) {
|
|
108
|
+
return {
|
|
109
|
+
wallet: args.wallet,
|
|
110
|
+
...(args.signature && { signature: args.signature }),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Review-seamless: build the approve_clean dry-run plan from a summary payload.
|
|
115
|
+
// Pure (no I/O) so the dry-run shape is unit-testable; the selection itself is
|
|
116
|
+
// reviewLib.selectForBulkApprove, the SAME function the CLI uses.
|
|
117
|
+
function planApproveClean(summary, opts = {}) {
|
|
118
|
+
const minQuality = Number.isFinite(opts.min_quality) ? opts.min_quality : reviewLib.DEFAULT_QUALITY_THRESHOLD;
|
|
119
|
+
const sel = reviewLib.selectForBulkApprove((summary && summary.items) || [], { mode: 'clean', minQuality });
|
|
120
|
+
const brief = (r) => ({ id: r.id, title: r.title, category: r.category, quality: r.quality });
|
|
121
|
+
return {
|
|
122
|
+
dry_run: true,
|
|
123
|
+
min_quality: minQuality,
|
|
124
|
+
pending_count: summary ? summary.pending_count : 0,
|
|
125
|
+
would_approve_count: sel.selected.length,
|
|
126
|
+
would_approve: sel.selected.map(brief),
|
|
127
|
+
excluded_flagged_count: sel.excluded_flagged.length,
|
|
128
|
+
excluded_flagged: sel.excluded_flagged.map((r) => ({ ...brief(r), flags: r.flags })),
|
|
129
|
+
excluded_low_quality_count: sel.excluded_low_quality.length,
|
|
130
|
+
excluded_unscored_count: sel.excluded_unscored.length,
|
|
131
|
+
next_step: sel.selected.length > 0
|
|
132
|
+
? `Show the operator this list and count (${sel.selected.length}). Only after their explicit confirmation, call auxilo_review again with {action:"approve_clean", dry_run:false, confirm:true, expected_count:${sel.selected.length}}. Approved items become PUBLIC immediately.`
|
|
133
|
+
: 'Nothing qualifies at this threshold. No follow-up call needed.',
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Review-seamless: POST one confirmed decision batch through the counted bulk
|
|
138
|
+
// endpoint, chunked at the server cap. Used by approve/reject/approve_clean.
|
|
139
|
+
async function postBulkChunks(headers, decisions) {
|
|
140
|
+
const totals = { approved: 0, rejected: 0, idempotent: 0, failed: 0, results: [] };
|
|
141
|
+
for (const chunk of reviewLib.chunkDecisions(decisions)) {
|
|
142
|
+
const resp = await fetch(`${AUXILO_BASE}/account/pending/bulk`, {
|
|
143
|
+
method: 'POST',
|
|
144
|
+
headers,
|
|
145
|
+
body: JSON.stringify({ decisions: chunk, confirm_count: chunk.length }),
|
|
146
|
+
});
|
|
147
|
+
const data = await resp.json();
|
|
148
|
+
if (!resp.ok) {
|
|
149
|
+
totals.error = data.error || `HTTP ${resp.status}`;
|
|
150
|
+
totals.error_code = data.code;
|
|
151
|
+
return totals;
|
|
152
|
+
}
|
|
153
|
+
totals.approved += data.approved || 0;
|
|
154
|
+
totals.rejected += data.rejected || 0;
|
|
155
|
+
totals.idempotent += data.idempotent || 0;
|
|
156
|
+
totals.failed += data.failed || 0;
|
|
157
|
+
if (Array.isArray(data.results)) totals.results.push(...data.results);
|
|
158
|
+
}
|
|
159
|
+
return totals;
|
|
160
|
+
}
|
|
161
|
+
|
|
58
162
|
const server = new Server(
|
|
59
|
-
{ name: 'auxilo', version: '0.9.
|
|
163
|
+
{ name: 'auxilo', version: '0.9.2' },
|
|
60
164
|
{
|
|
61
165
|
capabilities: { tools: {} },
|
|
62
166
|
instructions: `You are connected to Auxilo, a knowledge marketplace where AI agents buy and sell operational learnings.
|
|
@@ -130,7 +234,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
130
234
|
},
|
|
131
235
|
{
|
|
132
236
|
name: 'auxilo_contribute',
|
|
133
|
-
description: 'Submit operational knowledge you discovered during this session to the Auxilo marketplace. WHEN TO USE: Call this AFTER you solve a non-trivial problem that required debugging, experimentation, or combining information from multiple sources. Call it when you discover undocumented behavior, a workaround, or a subtle edge case. Do NOT call it for trivial lookups or standard documentation answers. QUALITY GATE: Self-assess on Specificity, Actionability, Novelty, Completeness (1-5 each). Only submit if total >= 14/20, no dimension below 3. DEDUP: Search auxilo_knowledge first to avoid duplicates. SENSITIVITY (mandatory self-screen): never include secrets, credentials, API keys, PII, private filesystem paths, or proprietary/client business content — generalize to placeholders or omit; this is a PUBLIC marketplace. PRICING: Leave unlock_price unset to let the dynamic pricing engine calculate automatically (recommended). If setting manually: $0.05-$0.10 common techniques, $0.10-$1.00 specific solutions, $1.00-$10.00 novel discoveries, $10.00-$50.00 breakthroughs. Minimum $0.05, maximum $50.00. Free to submit — you earn 70% when others unlock.',
|
|
237
|
+
description: 'Submit operational knowledge you discovered during this session to the Auxilo marketplace. WHEN TO USE: Call this AFTER you solve a non-trivial problem that required debugging, experimentation, or combining information from multiple sources. Call it when you discover undocumented behavior, a workaround, or a subtle edge case. Do NOT call it for trivial lookups or standard documentation answers. QUALITY GATE: Self-assess on Specificity, Actionability, Novelty, Completeness (1-5 each) and ALWAYS include your scores in quality_self_assessment — a submission WITHOUT it is held for manual review instead of publishing seamlessly. Only submit if total >= 14/20, no dimension below 3 (the server quarantines below-floor submissions for review). DEDUP: Search auxilo_knowledge first to avoid duplicates. SENSITIVITY (mandatory self-screen): never include secrets, credentials, API keys, PII, private filesystem paths, or proprietary/client business content — generalize to placeholders or omit; this is a PUBLIC marketplace. PRICING: Leave unlock_price unset to let the dynamic pricing engine calculate automatically (recommended). If setting manually: $0.05-$0.10 common techniques, $0.10-$1.00 specific solutions, $1.00-$10.00 novel discoveries, $10.00-$50.00 breakthroughs. Minimum $0.05, maximum $50.00. Free to submit — you earn 70% when others unlock. If the result is status pending_review, follow its how_to_review instructions (self-approval via `auxilo review`, the dashboard queue, or GET /account/pending).',
|
|
134
238
|
inputSchema: {
|
|
135
239
|
type: 'object',
|
|
136
240
|
properties: {
|
|
@@ -140,12 +244,25 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
140
244
|
tags: { type: 'array', items: { type: 'string' }, description: 'Relevant keywords' },
|
|
141
245
|
task_context: { type: 'string', description: 'What task were you performing?' },
|
|
142
246
|
outcome: { type: 'string', enum: ['success', 'partial', 'failure', 'workaround'] },
|
|
247
|
+
quality_self_assessment: {
|
|
248
|
+
type: 'object',
|
|
249
|
+
description: 'Your quality self-assessment. ALWAYS include this — without it the submission is held for manual review. Score each dimension 1-5; total MUST equal their sum (server-verified). Submissions below the floor (total < 14 or any dimension < 3) are quarantined for review rather than published.',
|
|
250
|
+
properties: {
|
|
251
|
+
specificity: { type: 'integer', minimum: 1, maximum: 5, description: 'How precise and detailed? (1-5)' },
|
|
252
|
+
actionability: { type: 'integer', minimum: 1, maximum: 5, description: 'Can another agent directly use this? (1-5)' },
|
|
253
|
+
novelty: { type: 'integer', minimum: 1, maximum: 5, description: 'Non-obvious / would an LLM get it wrong? (1-5)' },
|
|
254
|
+
completeness: { type: 'integer', minimum: 1, maximum: 5, description: 'Full context, reproduction steps, caveats? (1-5)' },
|
|
255
|
+
total: { type: 'integer', minimum: 4, maximum: 20, description: 'Sum of the four dimensions (server rejects mismatched totals)' },
|
|
256
|
+
reasoning: { type: 'string', description: 'Optional: one-line justification for your scores' },
|
|
257
|
+
},
|
|
258
|
+
required: ['specificity', 'actionability', 'novelty', 'completeness', 'total'],
|
|
259
|
+
},
|
|
143
260
|
contributor_wallet: { type: 'string', description: 'Your Base wallet (0x...) for revenue share' },
|
|
144
261
|
unlock_price: { type: 'number', description: 'Price in USD to unlock this learning (min $0.05, default auto-calculated). Set higher for deep, high-value knowledge.' },
|
|
145
262
|
contributor_agent: { type: 'string', description: 'Optional: identify yourself' },
|
|
146
263
|
related_skills: { type: 'array', items: { type: 'string' }, description: 'Optional: related Auxilo skill IDs' },
|
|
147
264
|
},
|
|
148
|
-
required: ['title', 'body', 'category', 'tags', 'task_context', 'outcome'],
|
|
265
|
+
required: ['title', 'body', 'category', 'tags', 'task_context', 'outcome', 'quality_self_assessment'],
|
|
149
266
|
},
|
|
150
267
|
},
|
|
151
268
|
{
|
|
@@ -166,7 +283,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
166
283
|
},
|
|
167
284
|
{
|
|
168
285
|
name: 'auxilo_unlock',
|
|
169
|
-
description: 'Unlock full learning content by ID. Price is set by the contributor (min $0.05 USDC). 70% goes to the contributor who shared this knowledge. Check unlock_price_usd in search results to see the cost before unlocking.',
|
|
286
|
+
description: 'Unlock full learning content by ID. Price is set by the contributor (min $0.05 USDC). 70% of the amount you pay goes to the contributor who shared this knowledge. Check unlock_price_usd in search results to see the cost before unlocking.',
|
|
170
287
|
inputSchema: {
|
|
171
288
|
type: 'object',
|
|
172
289
|
properties: {
|
|
@@ -203,20 +320,18 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
203
320
|
},
|
|
204
321
|
{
|
|
205
322
|
name: 'auxilo_withdraw',
|
|
206
|
-
description: '
|
|
323
|
+
description: 'Check how your earnings are paid out, and how to receive any legacy accrued balance. HOW PAYOUT WORKS NOW: earnings from sales settled on-chain are paid directly to your linked, verified wallet at the moment of sale — there is no balance to withdraw for those, because Auxilo never holds your share. LEGACY BALANCE: earnings accrued before direct settlement sit in your account\'s pending balance; withdraw them from your dashboard at https://auxilo.io/dashboard (bank payout via Stripe; $0.50 minimum, $0.25 flat fee), where you sign in with the email on your account. This tool reports your current balances and payout status; it does not move funds. To be paid at sale time going forward: auxilo_accept_terms, then auxilo_verify_wallet, then auxilo_link_wallet.',
|
|
207
324
|
inputSchema: {
|
|
208
325
|
type: 'object',
|
|
209
326
|
properties: {
|
|
210
|
-
|
|
211
|
-
signature: { type: 'string', description: 'Signature of the withdrawal payload' },
|
|
212
|
-
timestamp: { type: 'number', description: 'Unix timestamp in milliseconds (must be within 5 mins of server time)' }
|
|
327
|
+
session_token: { type: 'string', description: 'Optional JWT session token from /auth/verify. If omitted, your configured API key authenticates the account.' }
|
|
213
328
|
},
|
|
214
|
-
required: [
|
|
329
|
+
required: []
|
|
215
330
|
}
|
|
216
331
|
},
|
|
217
332
|
{
|
|
218
333
|
name: 'auxilo_settlements',
|
|
219
|
-
description: 'Check settlement history and processing status for
|
|
334
|
+
description: 'Check settlement history and processing status for a given wallet. Lists on-chain router settlements (per-sale receipts paid to your wallet at sale time), not only withdrawal history. Free.',
|
|
220
335
|
inputSchema: {
|
|
221
336
|
type: 'object',
|
|
222
337
|
properties: {
|
|
@@ -238,11 +353,12 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
238
353
|
},
|
|
239
354
|
{
|
|
240
355
|
name: 'auxilo_link_wallet',
|
|
241
|
-
description: 'Link a verified wallet address to your Auxilo account. Authenticates with your configured API key automatically, or pass a session_token (JWT from magic link login). The wallet must have been previously verified via auxilo_verify_wallet. One wallet per account. Required to
|
|
356
|
+
description: 'Link a verified wallet address to your Auxilo account. Authenticates with your configured API key automatically, or pass a session_token (JWT from magic link login). The wallet must have been previously verified via auxilo_verify_wallet. One wallet per account. Required to be paid at sale time on the on-chain settlement rail (and to receive any legacy balance on the USDC rail if it reopens). TWO-STEP FLOW (linking requires FRESH proof you control the wallet, bound to your account): (1) call with just the wallet — the server returns 401 link_signature_required with an EIP-712 challenge payload (single-use, 5-minute expiry, the account id is bound into the signed action string); (2) sign that exact typed-data payload with the wallet\'s private key and call again with the signature. A signature for another account\'s challenge will not verify. NOTE: you must first accept the current Terms via auxilo_accept_terms — linking a payout wallet triggers the §5.10 payment-collection agency and returns 403 TERMS_NOT_ACCEPTED until acceptance is on file. If the link adopts previously wallet-only submissions into your account, the response lists their ids (adopted_learning_ids).',
|
|
242
357
|
inputSchema: {
|
|
243
358
|
type: 'object',
|
|
244
359
|
properties: {
|
|
245
360
|
wallet: { type: 'string', description: 'Verified wallet address (0x...) to link to your account' },
|
|
361
|
+
signature: { type: 'string', description: 'EIP-712 signature of the link challenge, produced by the wallet\'s key. Omit on the first call to receive the challenge payload to sign.' },
|
|
246
362
|
session_token: { type: 'string', description: 'Optional JWT session token from /auth/verify. If omitted, your configured API key authenticates the account.' },
|
|
247
363
|
},
|
|
248
364
|
required: ['wallet'],
|
|
@@ -250,7 +366,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
250
366
|
},
|
|
251
367
|
{
|
|
252
368
|
name: 'auxilo_account_earnings',
|
|
253
|
-
description: 'View earnings for your authenticated Auxilo account. Authenticates with your configured API key automatically, or pass a session_token (JWT). Returns total gross, contributor share, pending balance, total withdrawn,
|
|
369
|
+
description: 'View earnings for your authenticated Auxilo account. Authenticates with your configured API key automatically, or pass a session_token (JWT). Returns total gross, contributor share, pending balance, total withdrawn, whether withdrawal is available (can_withdraw), and held_pending_assent — undisbursable receipts recorded before you accepted the current Terms, released to your withdrawable balance when you accept via auxilo_accept_terms. Earnings from on-chain-settled sales are paid to your wallet at sale time and appear in settlement history, not in pending balance. Free.',
|
|
254
370
|
inputSchema: {
|
|
255
371
|
type: 'object',
|
|
256
372
|
properties: {
|
|
@@ -273,56 +389,23 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
273
389
|
},
|
|
274
390
|
},
|
|
275
391
|
{
|
|
276
|
-
name: '
|
|
277
|
-
description: '
|
|
392
|
+
name: 'auxilo_review',
|
|
393
|
+
description: 'Review YOUR OWN pending-review learnings (from background extraction) so they can be approved to the public marketplace or rejected to stay private. Account-scoped: only the authenticated account\'s own pending items are ever visible or affected. ACTIONS: "list" returns the triage summary (counts + compact rows with quality score and platform screen verdicts: injection, content sensitivity, near-duplicate). "approve" / "reject" apply explicit decisions to the ids you pass (the operator must have named or confirmed these items). "approve_clean" selects every item that passed ALL platform screens AND has quality >= min_quality (default 14/20); it is DRY-RUN BY DEFAULT and returns exactly what WOULD be approved. CONSENT CONTRACT: nothing goes public without the contributor\'s explicit approval. So before executing approve_clean you MUST show the operator the dry-run list and count and get their confirmation, then call again with dry_run:false, confirm:true, and expected_count set to the dry-run count. The server also enforces a counted-confirmation gate on every bulk call. Requires your configured API key (or session_token).',
|
|
278
394
|
inputSchema: {
|
|
279
395
|
type: 'object',
|
|
280
396
|
properties: {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
description: 'Extract structured data from any public URL — title, description, headings, links, images, and meta tags. Costs $0.001 USDC via x402 or API key.',
|
|
290
|
-
inputSchema: {
|
|
291
|
-
type: 'object',
|
|
292
|
-
properties: {
|
|
293
|
-
url: { type: 'string', description: 'Public URL to extract structured data from' },
|
|
294
|
-
x_payment: { type: 'string', description: 'x402 payment header' },
|
|
295
|
-
},
|
|
296
|
-
required: ['url'],
|
|
297
|
-
},
|
|
298
|
-
},
|
|
299
|
-
{
|
|
300
|
-
name: 'renderly_readable',
|
|
301
|
-
description: 'Get plain readable text from any public URL — no markdown formatting, no HTML, just the content. Costs $0.0005 USDC via x402 or API key.',
|
|
302
|
-
inputSchema: {
|
|
303
|
-
type: 'object',
|
|
304
|
-
properties: {
|
|
305
|
-
url: { type: 'string', description: 'Public URL to extract readable text from' },
|
|
306
|
-
x_payment: { type: 'string', description: 'x402 payment header' },
|
|
397
|
+
action: { type: 'string', enum: ['list', 'approve', 'reject', 'approve_clean'], description: 'What to do. Start with "list".' },
|
|
398
|
+
ids: { type: 'array', items: { type: 'string' }, description: 'Learning ids for action approve/reject. These must be items the operator explicitly chose.' },
|
|
399
|
+
reason: { type: 'string', description: 'Optional rejection reason (action reject; max 500 chars).' },
|
|
400
|
+
dry_run: { type: 'boolean', description: 'approve_clean only. Default TRUE: report what would be approved without changing anything. Set false only together with confirm:true and expected_count after the operator confirmed the dry-run list.' },
|
|
401
|
+
confirm: { type: 'boolean', description: 'approve_clean only. Must be exactly true to execute. Never set this without the operator\'s explicit go-ahead on the dry-run output.' },
|
|
402
|
+
expected_count: { type: 'number', description: 'approve_clean execute only. The count from the dry run, echoed back. If the live selection differs (queue changed), nothing is approved and a fresh dry run is returned.' },
|
|
403
|
+
min_quality: { type: 'number', description: 'approve_clean quality threshold 0-20 (default 14). 0 includes unscored items.' },
|
|
404
|
+
session_token: { type: 'string', description: 'Optional JWT session token from /auth/verify. If omitted, your configured API key authenticates the account.' },
|
|
307
405
|
},
|
|
308
|
-
required: ['
|
|
406
|
+
required: ['action'],
|
|
309
407
|
},
|
|
310
408
|
},
|
|
311
|
-
{
|
|
312
|
-
name: 'renderly_llms_txt',
|
|
313
|
-
description: 'Get the LLM-readable service description for Renderly — what it does, endpoints, and usage. Free.',
|
|
314
|
-
inputSchema: { type: 'object', properties: {} },
|
|
315
|
-
},
|
|
316
|
-
{
|
|
317
|
-
name: 'renderly_health',
|
|
318
|
-
description: 'Check Renderly service health status. Free.',
|
|
319
|
-
inputSchema: { type: 'object', properties: {} },
|
|
320
|
-
},
|
|
321
|
-
{
|
|
322
|
-
name: 'renderly_pricing',
|
|
323
|
-
description: 'Get Renderly pricing information for all endpoints. Free.',
|
|
324
|
-
inputSchema: { type: 'object', properties: {} },
|
|
325
|
-
},
|
|
326
409
|
{
|
|
327
410
|
name: 'get_stats',
|
|
328
411
|
description: 'Get Auxilo registry statistics — catalog size, skill types, and query volume. Free.',
|
|
@@ -389,6 +472,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
389
472
|
tags: args.tags,
|
|
390
473
|
task_context: args.task_context,
|
|
391
474
|
outcome: args.outcome,
|
|
475
|
+
// AUD19-4: pass the quality self-assessment through — without it the
|
|
476
|
+
// server can never seamless-publish (qualityPresent is false) and every
|
|
477
|
+
// MCP contribution lands pending_review with 'awaiting_quality'.
|
|
478
|
+
...(args.quality_self_assessment && { quality_self_assessment: args.quality_self_assessment }),
|
|
392
479
|
...(args.contributor_wallet && { contributor_wallet: args.contributor_wallet }),
|
|
393
480
|
unlock_price: args.unlock_price,
|
|
394
481
|
contributor_agent: args.contributor_agent,
|
|
@@ -421,10 +508,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
421
508
|
|
|
422
509
|
const resp = await fetch(`${AUXILO_BASE}/knowledge/${args.id}`, { headers });
|
|
423
510
|
const data = await resp.json();
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
}
|
|
511
|
+
// AUD19-5: handle BOTH the 402-first challenge (server >= the fix,
|
|
512
|
+
// accepts[] served cold) and the legacy 401-with-options shape from
|
|
513
|
+
// older live servers — same meaning, no accepts[].
|
|
514
|
+
const challenge = unlockPaymentRequired(resp.status, data, `${AUXILO_BASE}/knowledge/${args.id}`);
|
|
515
|
+
if (challenge) return text(challenge);
|
|
428
516
|
// LW-3(a): fence the contributor body so the LLM treats it as data, not instructions.
|
|
429
517
|
return text(fenceUnlockResult(data));
|
|
430
518
|
}
|
|
@@ -440,33 +528,36 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
440
528
|
|
|
441
529
|
case 'auxilo_verify_wallet': {
|
|
442
530
|
const url = args.signature ? `${AUXILO_BASE}/wallet/verify` : `${AUXILO_BASE}/wallet/challenge`;
|
|
531
|
+
// AUD19-7: whitelist the request body — only wallet + signature travel.
|
|
532
|
+
// (Forwarding raw args let a caller mint a withdrawal-action challenge
|
|
533
|
+
// through an undocumented passthrough; the MCP surface only needs the
|
|
534
|
+
// default verification action.)
|
|
443
535
|
const resp = await fetch(url, {
|
|
444
536
|
method: 'POST',
|
|
445
537
|
headers: baseHeaders(),
|
|
446
|
-
body: JSON.stringify(args)
|
|
538
|
+
body: JSON.stringify(verifyWalletRequestBody(args))
|
|
447
539
|
});
|
|
448
540
|
return text(await resp.json());
|
|
449
541
|
}
|
|
450
542
|
|
|
451
543
|
case 'auxilo_withdraw': {
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
544
|
+
// AUD19-7 (DASH/ROUTE): honest status-and-routing tool. Reads
|
|
545
|
+
// GET /account/earnings and reports balances + the real payout path.
|
|
546
|
+
// It never attempts a custodial withdrawal (no POST /withdraw) — new
|
|
547
|
+
// earnings settle to the linked wallet at sale time; the legacy
|
|
548
|
+
// balance exits via the dashboard (Stripe) rail.
|
|
549
|
+
const resp = await fetch(`${AUXILO_BASE}/account/earnings`, {
|
|
550
|
+
headers: baseHeaders(args.session_token ? { 'Authorization': `Bearer ${args.session_token}` } : {}),
|
|
456
551
|
});
|
|
457
|
-
const
|
|
458
|
-
|
|
459
|
-
// migration and returns 503 withdraw_paused_noncustodial_migration.
|
|
460
|
-
// Surface that as a plain, human-legible note instead of a raw error
|
|
461
|
-
// blob so the agent doesn't treat it as a transient failure to retry.
|
|
462
|
-
if (resp.status === 503 && data && data.code === 'withdraw_paused_noncustodial_migration') {
|
|
552
|
+
const e = await resp.json();
|
|
553
|
+
if (resp.status === 401) {
|
|
463
554
|
return text({
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
server_response: data,
|
|
555
|
+
error: 'Not authenticated. Run npx auxilo setup to create credentials, or pass session_token. Balances are account-scoped.',
|
|
556
|
+
server_response: e,
|
|
467
557
|
});
|
|
468
558
|
}
|
|
469
|
-
return text(
|
|
559
|
+
if (!resp.ok) return text(e);
|
|
560
|
+
return text(shapeWithdrawStatus(e));
|
|
470
561
|
}
|
|
471
562
|
|
|
472
563
|
case 'auxilo_settlements': {
|
|
@@ -489,9 +580,25 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
489
580
|
headers: baseHeaders(
|
|
490
581
|
args.session_token ? { 'Authorization': `Bearer ${args.session_token}` } : {}
|
|
491
582
|
),
|
|
492
|
-
|
|
583
|
+
// HIGH-1: pass the link signature through when present (step 2 of the
|
|
584
|
+
// two-step link flow — see the tool description).
|
|
585
|
+
body: JSON.stringify({
|
|
586
|
+
wallet: args.wallet,
|
|
587
|
+
...(args.signature && { signature: args.signature }),
|
|
588
|
+
}),
|
|
493
589
|
});
|
|
494
|
-
|
|
590
|
+
const data = await resp.json();
|
|
591
|
+
// HIGH-1: the challenge response is expected, not an error — surface it
|
|
592
|
+
// as the next step so the agent signs and calls again instead of
|
|
593
|
+
// treating the 401 as a failure.
|
|
594
|
+
if (resp.status === 401 && data && data.code === 'link_signature_required') {
|
|
595
|
+
return text({
|
|
596
|
+
status: 'signature_required',
|
|
597
|
+
message: 'Expected first-step response: sign the eip712 payload below with the wallet\'s private key (EIP-712 typed data — the account id is bound into the action string), then call auxilo_link_wallet again with the same wallet plus the signature. The challenge is single-use and expires in 5 minutes.',
|
|
598
|
+
server_response: data,
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
return text(data);
|
|
495
602
|
}
|
|
496
603
|
|
|
497
604
|
case 'auxilo_account_earnings': {
|
|
@@ -531,82 +638,72 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
531
638
|
return text(await resp.json());
|
|
532
639
|
}
|
|
533
640
|
|
|
534
|
-
case '
|
|
535
|
-
|
|
536
|
-
|
|
641
|
+
case 'auxilo_review': {
|
|
642
|
+
// Same auth idiom as the other account tools: session JWT when
|
|
643
|
+
// provided, else the configured API key via baseHeaders().
|
|
644
|
+
const headers = baseHeaders(
|
|
645
|
+
args.session_token ? { 'Authorization': `Bearer ${args.session_token}` } : {}
|
|
646
|
+
);
|
|
537
647
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
body: JSON.stringify({ url: args.url }),
|
|
542
|
-
});
|
|
543
|
-
const data = await resp.json();
|
|
544
|
-
if (resp.status === 402) {
|
|
545
|
-
return text({
|
|
546
|
-
status: 'payment_required',
|
|
547
|
-
cost: '$0.001 USDC on Base',
|
|
548
|
-
http_endpoint: `${AUXILO_BASE}/renderly/markdown`,
|
|
549
|
-
payment_details: data,
|
|
550
|
-
});
|
|
648
|
+
if (args.action === 'list') {
|
|
649
|
+
const resp = await fetch(`${AUXILO_BASE}/account/pending/summary`, { headers });
|
|
650
|
+
return text(await resp.json());
|
|
551
651
|
}
|
|
552
|
-
return text(data);
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
case 'renderly_extract': {
|
|
556
|
-
const headers = baseHeaders();
|
|
557
|
-
if (args.x_payment) headers['X-Payment'] = args.x_payment;
|
|
558
652
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
cost: '$0.001 USDC on Base',
|
|
569
|
-
http_endpoint: `${AUXILO_BASE}/renderly/extract`,
|
|
570
|
-
payment_details: data,
|
|
571
|
-
});
|
|
653
|
+
if (args.action === 'approve' || args.action === 'reject') {
|
|
654
|
+
if (!Array.isArray(args.ids) || args.ids.length === 0) {
|
|
655
|
+
return text({ error: `action "${args.action}" requires a non-empty ids array. Run {action:"list"} first and pass the ids the operator chose.` });
|
|
656
|
+
}
|
|
657
|
+
const decisions = args.ids.map((id) => (args.action === 'reject' && args.reason)
|
|
658
|
+
? { id, decision: 'reject', reason: args.reason }
|
|
659
|
+
: { id, decision: args.action });
|
|
660
|
+
const totals = await postBulkChunks(headers, decisions);
|
|
661
|
+
return text({ action: args.action, submitted: decisions.length, ...totals });
|
|
572
662
|
}
|
|
573
|
-
return text(data);
|
|
574
|
-
}
|
|
575
663
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
664
|
+
if (args.action === 'approve_clean') {
|
|
665
|
+
const summaryResp = await fetch(`${AUXILO_BASE}/account/pending/summary`, { headers });
|
|
666
|
+
const summary = await summaryResp.json();
|
|
667
|
+
if (!summaryResp.ok) return text(summary);
|
|
579
668
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
669
|
+
const plan = planApproveClean(summary, args);
|
|
670
|
+
|
|
671
|
+
// DRY RUN unless the operator-confirmed execute contract is complete:
|
|
672
|
+
// dry_run explicitly false AND confirm exactly true.
|
|
673
|
+
if (args.dry_run !== false || args.confirm !== true) {
|
|
674
|
+
return text(plan);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// Counted confirmation: the execute call must echo the dry-run count.
|
|
678
|
+
if (!Number.isInteger(args.expected_count)) {
|
|
679
|
+
return text({
|
|
680
|
+
error: 'expected_count is required to execute approve_clean: echo the would_approve_count from the dry run. This is the counted-confirmation gate.',
|
|
681
|
+
...plan,
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
if (args.expected_count !== plan.would_approve_count) {
|
|
685
|
+
return text({
|
|
686
|
+
error: `Selection changed since the dry run (expected ${args.expected_count}, now ${plan.would_approve_count}). Nothing was approved. Re-run the dry run and re-confirm with the operator.`,
|
|
687
|
+
...plan,
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
if (plan.would_approve_count === 0) {
|
|
691
|
+
return text({ action: 'approve_clean', approved: 0, message: 'Nothing qualifies at this threshold.' });
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
const decisions = plan.would_approve.map((r) => ({ id: r.id, decision: 'approve' }));
|
|
695
|
+
const totals = await postBulkChunks(headers, decisions);
|
|
587
696
|
return text({
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
697
|
+
action: 'approve_clean',
|
|
698
|
+
executed: true,
|
|
699
|
+
min_quality: plan.min_quality,
|
|
700
|
+
submitted: decisions.length,
|
|
701
|
+
...totals,
|
|
702
|
+
note: 'Approved items are now PUBLIC on the marketplace. Screen-flagged and below-threshold items remain pending for individual review.',
|
|
592
703
|
});
|
|
593
704
|
}
|
|
594
|
-
return text(data);
|
|
595
|
-
}
|
|
596
705
|
|
|
597
|
-
|
|
598
|
-
const resp = await fetch(`${AUXILO_BASE}/renderly/llms.txt`, { headers: baseHeaders() });
|
|
599
|
-
return text(await resp.json());
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
case 'renderly_health': {
|
|
603
|
-
const resp = await fetch(`${AUXILO_BASE}/renderly/health`, { headers: baseHeaders() });
|
|
604
|
-
return text(await resp.json());
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
case 'renderly_pricing': {
|
|
608
|
-
const resp = await fetch(`${AUXILO_BASE}/renderly/pricing`, { headers: baseHeaders() });
|
|
609
|
-
return text(await resp.json());
|
|
706
|
+
return text({ error: `Unknown action: ${args.action}. Use list, approve, reject, or approve_clean.` });
|
|
610
707
|
}
|
|
611
708
|
|
|
612
709
|
case 'get_stats': {
|
|
@@ -634,7 +731,7 @@ function text(obj) {
|
|
|
634
731
|
// LW-3(a): export the pure helpers so they can be unit-tested without starting
|
|
635
732
|
// the stdio transport. When this file is required (not run directly), stop here
|
|
636
733
|
// before the CLI dispatch and MCP startup below.
|
|
637
|
-
module.exports = { fenceUnlockResult, UNTRUSTED_CONTENT_ADVISORY, baseHeaders };
|
|
734
|
+
module.exports = { fenceUnlockResult, UNTRUSTED_CONTENT_ADVISORY, baseHeaders, planApproveClean, unlockPaymentRequired, shapeWithdrawStatus, verifyWalletRequestBody };
|
|
638
735
|
if (require.main !== module) {
|
|
639
736
|
return;
|
|
640
737
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "auxilo-mcp",
|
|
3
|
-
"version": "0.9.
|
|
4
|
-
"
|
|
3
|
+
"version": "0.9.2",
|
|
4
|
+
"mcpName": "io.github.silent-architects/auxilo",
|
|
5
|
+
"description": "MCP server for Auxilo. Your agent stops solving the same problem twice: auto-extracted learnings, free self-unlocks, and earnings when other agents unlock yours.",
|
|
5
6
|
"main": "mcp-server.js",
|
|
6
7
|
"bin": {
|
|
7
8
|
"auxilo-mcp": "mcp-server.js",
|
|
@@ -15,6 +16,7 @@
|
|
|
15
16
|
"lib/sensitivity-filter.js",
|
|
16
17
|
"scripts/runner.js",
|
|
17
18
|
"scripts/capture-core.js",
|
|
19
|
+
"scripts/extract-local.js",
|
|
18
20
|
"scripts/sources/",
|
|
19
21
|
"scripts/hooks/auxilo-extract.sh",
|
|
20
22
|
"README.md",
|
|
@@ -26,9 +28,16 @@
|
|
|
26
28
|
},
|
|
27
29
|
"keywords": [
|
|
28
30
|
"mcp",
|
|
31
|
+
"mcp-server",
|
|
29
32
|
"model-context-protocol",
|
|
33
|
+
"claude",
|
|
34
|
+
"claude-code",
|
|
35
|
+
"cursor",
|
|
36
|
+
"windsurf",
|
|
30
37
|
"ai-agents",
|
|
38
|
+
"agent-learning",
|
|
31
39
|
"agent-discovery",
|
|
40
|
+
"knowledge-sharing",
|
|
32
41
|
"knowledge-marketplace",
|
|
33
42
|
"x402",
|
|
34
43
|
"auxilo"
|