🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@solinkify/mcp

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@solinkify/mcp - npm Package Compare versions

Comparing version
0.2.4
to
0.3.0
+24
dist/spend.d.ts
import { PublicKey } from '@solana/web3.js';
import type { Connection } from '@solana/web3.js';
export interface SpendAuthorityState {
address: string;
owner: string;
agent: string;
token_mint: string;
per_tx_cap_usd: number;
daily_cap_usd: number;
spent_today_usd: number;
remaining_today_usd: number;
revoked: boolean;
}
/** PDA `[spend_authority, owner, agent]` (contract spend.rs). */
export declare function spendAuthorityPda(owner: PublicKey, agent: PublicKey): PublicKey;
/**
* Fetch + decode a SpendAuthority account, or null when the owner has not
* created one. Layout (spend.rs): disc(8) ++ owner(32) ++ agent(32) ++
* token_mint(32) ++ per_tx_cap(u64) ++ daily_cap(u64) ++ spent_today(u64) ++
* day_index(u64) ++ revoked(u8) ++ bump(u8). All whitelisted stablecoins have
* 6 decimals, so base units convert to USD with 1e6.
*/
export declare function fetchSpendAuthority(connection: Connection, owner: PublicKey, agent: PublicKey): Promise<SpendAuthorityState | null>;
//# sourceMappingURL=spend.d.ts.map
{"version":3,"file":"spend.d.ts","sourceRoot":"","sources":["../src/spend.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGlD,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,iEAAiE;AACjE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,GAAG,SAAS,CAK/E;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,SAAS,EAChB,KAAK,EAAE,SAAS,GACf,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAsBrC"}
// ADR-008 SpendAuthority client helpers (owner mode).
//
// A SpendAuthority PDA per (owner, agent) lets the agent pay merchants from
// the OWNER's wallet under program-enforced caps: per-tx cap, daily ceiling,
// and revoke as an instant kill switch. The agent never holds the owner's
// funds or keys — it only signs pay_spl_delegated transactions.
import { PublicKey } from '@solana/web3.js';
import { PROGRAM_ID } from './context.js';
/** PDA `[spend_authority, owner, agent]` (contract spend.rs). */
export function spendAuthorityPda(owner, agent) {
return PublicKey.findProgramAddressSync([Buffer.from('spend_authority'), owner.toBuffer(), agent.toBuffer()], new PublicKey(PROGRAM_ID))[0];
}
/**
* Fetch + decode a SpendAuthority account, or null when the owner has not
* created one. Layout (spend.rs): disc(8) ++ owner(32) ++ agent(32) ++
* token_mint(32) ++ per_tx_cap(u64) ++ daily_cap(u64) ++ spent_today(u64) ++
* day_index(u64) ++ revoked(u8) ++ bump(u8). All whitelisted stablecoins have
* 6 decimals, so base units convert to USD with 1e6.
*/
export async function fetchSpendAuthority(connection, owner, agent) {
const pda = spendAuthorityPda(owner, agent);
const info = await connection.getAccountInfo(pda);
if (!info || info.data.length < 138)
return null;
const d = info.data;
const dailyCap = Number(d.readBigUInt64LE(112)) / 1e6;
// spent_today only counts when its day_index is the CURRENT unix day —
// after a rollover the program resets it on the next spend.
const today = Math.floor(Date.now() / 1000 / 86_400);
const spentToday = Number(d.readBigUInt64LE(128)) === today ? Number(d.readBigUInt64LE(120)) / 1e6 : 0;
return {
address: pda.toBase58(),
owner: new PublicKey(d.subarray(8, 40)).toBase58(),
agent: new PublicKey(d.subarray(40, 72)).toBase58(),
token_mint: new PublicKey(d.subarray(72, 104)).toBase58(),
per_tx_cap_usd: Number(d.readBigUInt64LE(104)) / 1e6,
daily_cap_usd: dailyCap,
spent_today_usd: spentToday,
remaining_today_usd: Math.max(0, dailyCap - spentToday),
revoked: d[136] !== 0,
};
}
+7
-0

@@ -10,2 +10,9 @@ export interface McpConfig {

dailyCapUsd: number;
/**
* ADR-008 owner mode: base58 pubkey of the wallet whose funds pay_checkout
* draws from via the on-chain SpendAuthority (per-tx/daily caps + revoke
* kill switch enforced by the program). The agent wallet only signs — it
* never holds the owner's funds. Unset = spend from the agent wallet.
*/
ownerWallet: string | undefined;
}

@@ -12,0 +19,0 @@ export declare function loadConfig(): McpConfig;

+1
-1

@@ -1,1 +0,1 @@

{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,uDAAuD;IACvD,aAAa,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAC;CACrB;AAqBD,wBAAgB,UAAU,IAAI,SAAS,CAWtC;AAED,wBAAgB,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAQjE"}
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,uDAAuD;IACvD,aAAa,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC;AAmBD,wBAAgB,UAAU,IAAI,SAAS,CAYtC;AAED,wBAAgB,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAQjE"}

@@ -6,3 +6,2 @@ // Environment-driven configuration. Devnet defaults; every value can be

USDT: 'EJwZgeZrdC8TXTQbQBoL6bfuAnFUUy1PVCMB4DYPzVaS',
PYUSD: 'CXk2AMBfi3TwaEL2468s6zP8xq9NxTXjp9gjMgzeUynM',
};

@@ -12,3 +11,2 @@ const MAINNET_MINTS = {

USDT: 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB',
PYUSD: '2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo',
};

@@ -31,2 +29,3 @@ function num(name, fallback) {

dailyCapUsd: num('SOLINKIFY_DAILY_CAP_USD', 10),
ownerWallet: process.env['SOLINKIFY_OWNER_WALLET']?.trim() || undefined,
};

@@ -33,0 +32,0 @@ }

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
export declare const SERVER_VERSION = "0.2.4";
export declare const SERVER_VERSION = "0.3.0";
/** Build the Solinkify MCP server with all pillar tools registered. */
export declare function createServer(): McpServer;
//# sourceMappingURL=server.d.ts.map

@@ -11,3 +11,3 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';

import { registerSocialTools } from './tools/social.js';
export const SERVER_VERSION = '0.2.4';
export const SERVER_VERSION = '0.3.0';
/** Build the Solinkify MCP server with all pillar tools registered. */

@@ -14,0 +14,0 @@ export function createServer() {

@@ -70,4 +70,4 @@ import { createPrivateKey, sign as edSign } from 'node:crypto';

.describe('The dataset id returned by datahub_search (a uuid), not its title. Buying the same dataset twice pays twice; use datahub_download to re-fetch something already owned.'),
// PYUSD is whitelisted on-chain but is a Token-2022 mint that pay_spl
// (classic SPL only) cannot settle yet — pre-mainnet contract item.
// PYUSD was dropped from the whitelist (Token-2022 — pay_spl settles
// classic SPL only).
token: z

@@ -74,0 +74,0 @@ .enum(['USDC', 'USDT'])

@@ -76,3 +76,3 @@ import { z } from 'zod';

token: z
.enum(['USDC', 'USDT', 'PYUSD'])
.enum(['USDC', 'USDT'])
.default('USDC')

@@ -93,3 +93,3 @@ .describe('Stablecoin whose pre-paid balance to read. Balances are tracked per token, so USDC and USDT are separate pots. Defaults to USDC.'),

token: z
.enum(['USDC', 'USDT', 'PYUSD'])
.enum(['USDC', 'USDT'])
.default('USDC')

@@ -96,0 +96,0 @@ .describe('Stablecoin to deposit. Must match the token the target endpoints price in, since balances do not convert between tokens. Defaults to USDC.'),

@@ -1,1 +0,1 @@

{"version":3,"file":"pay.d.ts","sourceRoot":"","sources":["../../src/tools/pay.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAiDjD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,GAAG,IAAI,CAyE1E"}
{"version":3,"file":"pay.d.ts","sourceRoot":"","sources":["../../src/tools/pay.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAiDjD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,GAAG,IAAI,CA+E1E"}

@@ -36,3 +36,3 @@ import { z } from 'zod';

server.registerTool('pay_checkout', {
description: 'Pay a pending Solinkify Pay checkout session from the agent wallet. Settles on Solana, the merchant receives 99% instantly, and the order is marked paid. Moves money and is not reversible, so confirm the amount with pay_get_session first. Sessions that are already paid or expired are refused.',
description: 'Pay a pending Solinkify Pay checkout session. Settles on Solana, the merchant receives 99% instantly, and the order is marked paid. Spends from the agent wallet — or, when SOLINKIFY_OWNER_WALLET is configured, from the owner wallet under its on-chain SpendAuthority caps (ADR-008); check spend_authority_status first in that mode. Moves money and is not reversible, so confirm the amount with pay_get_session first. Sessions that are already paid or expired are refused.',
inputSchema: {

@@ -59,2 +59,5 @@ session: z

token_mint: s.token_mint,
// ADR-008 owner mode: draw from the owner's ATA via pay_spl_delegated
// (the contract enforces the SpendAuthority caps + kill switch).
owner_wallet: ctx.config.ownerWallet,
}),

@@ -75,2 +78,5 @@ });

paid_usd: amountUsd,
funds_source: ctx.config.ownerWallet
? `owner wallet ${ctx.config.ownerWallet} (delegated, on-chain caps)`
: 'agent wallet',
route: gw.route_path ?? null,

@@ -77,0 +83,0 @@ signature,

@@ -1,1 +0,1 @@

{"version":3,"file":"wallet.d.ts","sourceRoot":"","sources":["../../src/tools/wallet.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAGjD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,GAAG,IAAI,CAqD7E"}
{"version":3,"file":"wallet.d.ts","sourceRoot":"","sources":["../../src/tools/wallet.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAIjD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,GAAG,IAAI,CAqH7E"}

@@ -0,4 +1,6 @@

import { z } from 'zod';
import { PublicKey, LAMPORTS_PER_SOL } from '@solana/web3.js';
import { getPrepaidBalance } from '@solinkify/gate-sdk';
import { textResult } from '../payments.js';
import { fetchSpendAuthority, spendAuthorityPda } from '../spend.js';
export function registerWalletTools(server, ctx) {

@@ -30,2 +32,16 @@ server.registerTool('wallet_status', {

}
// ADR-008 owner mode: surface the on-chain allowance next to the local caps.
let ownerMode = null;
if (ctx.config.ownerWallet) {
try {
const sa = await fetchSpendAuthority(ctx.connection, new PublicKey(ctx.config.ownerWallet), owner);
ownerMode = {
owner_wallet: ctx.config.ownerWallet,
spend_authority: sa ?? 'not created on-chain — the owner must call create_spend_authority',
};
}
catch (e) {
ownerMode = { owner_wallet: ctx.config.ownerWallet, error: String(e) };
}
}
return textResult({

@@ -43,4 +59,38 @@ address: owner.toBase58(),

},
// pay_checkout draws from the owner's funds when owner mode is on.
owner_mode: ownerMode,
});
});
server.registerTool('spend_authority_status', {
description: "Show the on-chain SpendAuthority (ADR-008) that lets this agent pay merchants from an owner's wallet: per-payment cap, daily ceiling, what is spent/left today, and whether it has been revoked. All of it is enforced by the Solana program, not by this server. Read-only. Defaults to the SOLINKIFY_OWNER_WALLET the server is configured with.",
inputSchema: {
owner: z
.string()
.optional()
.describe('Base58 wallet address of the funds owner to inspect. Omit to use the configured SOLINKIFY_OWNER_WALLET.'),
},
}, async ({ owner }) => {
const ownerStr = owner ?? ctx.config.ownerWallet;
if (!ownerStr) {
return textResult('Owner mode is off: no SOLINKIFY_OWNER_WALLET configured and no owner argument given. ' +
'Payments spend from the agent wallet itself.');
}
const ownerPk = new PublicKey(ownerStr);
const agentPk = ctx.wallet.publicKey;
const sa = await fetchSpendAuthority(ctx.connection, ownerPk, agentPk);
if (!sa) {
return textResult({
owner: ownerStr,
agent: agentPk.toBase58(),
spend_authority_pda: spendAuthorityPda(ownerPk, agentPk).toBase58(),
status: 'not created — the owner must call create_spend_authority (per-tx cap, daily cap) on-chain before delegated payments work',
});
}
return textResult({
...sa,
status: sa.revoked
? 'REVOKED — the kill switch is on; every delegated payment is rejected until the owner re-arms it with update_spend_authority'
: 'active — pay_checkout can spend from the owner within these caps',
});
});
}

@@ -23,5 +23,5 @@ import fs from 'node:fs';

}
throw new Error('This tool spends from a wallet, so one must be configured: set SOLINKIFY_WALLET_PATH ' +
throw new Error('This tool needs the agent wallet, so one must be configured: set SOLINKIFY_WALLET_PATH ' +
'(path to a JSON keypair from `solana-keygen new`) or SOLINKIFY_WALLET_BS58 (base58 ' +
'secret key). Read-only tools work without it.');
'secret key). Discovery and search tools work without it.');
}
{
"name": "@solinkify/mcp",
"version": "0.2.4",
"version": "0.3.0",
"description": "Solinkify MCP server — let AI agents pay for gated content (x402), buy and rate datasets, and settle store checkouts on Solana, with hard spending caps.",

@@ -5,0 +5,0 @@ "type": "module",

@@ -39,3 +39,3 @@ # @solinkify/mcp

funded with a little SOL for fees and the stablecoin you want to spend
(USDC/USDT/PYUSD).
(USDC/USDT).

@@ -63,2 +63,3 @@ It is optional at startup: the server connects and lists its tools with no wallet

| `wallet_status` | Address, SOL, stablecoin balances, prepaid balance, remaining budget |
| `spend_authority_status` | On-chain SpendAuthority (owner mode): caps, spent/left today, revoked |
| `gate_get_price` | Preview an x402 paywall without paying |

@@ -74,3 +75,3 @@ | `gate_fetch` | Fetch gated content, auto-paying (prepaid preferred, escrow otherwise) |

| `pay_get_session` | Inspect a checkout session (accepts the full checkout URL) |
| `pay_checkout` | Pay a pending checkout session; the merchant's order is marked paid |
| `pay_checkout` | Pay a pending checkout session; the merchant's order is marked paid. With `SOLINKIFY_OWNER_WALLET`: spends the owner's funds under on-chain caps |
| `social_get_blink` | Inspect a Blink product link (title, price, seller) without paying |

@@ -93,5 +94,21 @@ | `social_buy_blink` | Buy from a Blink link; returns the receipt + download link |

on-chain prices; `withdraw_prepaid` pulls the remainder back at any time — the
kill switch. Delegated on-chain spend authority (allowance + revoke enforced
by the program itself) is on the pre-mainnet contract roadmap.
kill switch.
### Owner mode (on-chain SpendAuthority)
Set `SOLINKIFY_OWNER_WALLET` to the base58 address of a funds owner and
`pay_checkout` stops spending from the agent keypair: it settles through
`pay_spl_delegated`, drawing from the **owner's** token account under a
SpendAuthority the owner created on-chain — per-payment cap, daily ceiling,
and revoke-as-kill-switch all enforced **by the Solana program**, not by this
server. The agent wallet only signs and pays tx fees; it never holds the
owner's funds or keys. Inspect the allowance with `spend_authority_status`.
The owner grants (and can instantly revoke) the allowance with the contract's
`create_spend_authority` / `update_spend_authority` / `revoke_spend_authority`
instructions, keyed to the agent's public key. One SpendAuthority covers one
stablecoin mint. The local `SOLINKIFY_MAX_PAYMENT_USD` / `SOLINKIFY_DAILY_CAP_USD`
caps still apply on top (defense in depth). Other paying tools (gate, DataHub,
Blinks) still spend from the agent wallet.
Refusals come back as readable messages the agent can relay to the user. The

@@ -112,2 +129,3 @@ server also self-identifies as an AI agent via User-Agent (override with

| `SOLINKIFY_DAILY_CAP_USD` | `10` | Daily budget |
| `SOLINKIFY_OWNER_WALLET` | — (off) | Owner mode: `pay_checkout` spends the owner's funds via the on-chain SpendAuthority (ADR-008) |

@@ -114,0 +132,0 @@ ## QA