@quidli/connect-mcp 0.6.3 → 0.6.5
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 +8 -2
- package/dist/mount.d.ts +4 -1
- package/dist/mount.js +34 -2
- package/dist/tools/register.js +89 -19
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://www.npmjs.com/package/@quidli/connect-mcp)
|
|
5
5
|
[](./LICENSE)
|
|
6
6
|
|
|
7
|
-
Open payments for multiplayer agents, powered by
|
|
7
|
+
Open payments for multiplayer agents, powered by onchain reputation. Resolve a social handle to a wallet, check its onchain reputation, and send tokens — from Cursor, Claude Desktop, Claude Code, or any MCP-compatible client.
|
|
8
8
|
|
|
9
9
|
Multiplayer means parties who don't already know each other — an agent paying a person, or another agent, with no prior relationship and no account to fall back on. Connect is how it verifies who it's dealing with before sending anything.
|
|
10
10
|
|
|
@@ -52,6 +52,8 @@ Then ask your agent:
|
|
|
52
52
|
|
|
53
53
|
Resolves across GitHub, X, Telegram, Discord, LinkedIn, Farcaster, email and phone — and generates a wallet for people who have never used Quidli.
|
|
54
54
|
|
|
55
|
+
Then score what you find: `connect_scores_batch` returns onchain reputation for a batch of identities.
|
|
56
|
+
|
|
55
57
|
Lookup, scores, and price run without a key under a shared anonymous quota. Add a [Connect API key](https://connect.quid.li) for higher limits, your own profile (`connect_me`), and Smart Send.
|
|
56
58
|
|
|
57
59
|
---
|
|
@@ -371,8 +373,12 @@ mistake.
|
|
|
371
373
|
| `connect_scores_by_account` | Scores for one linked account |
|
|
372
374
|
| `connect_scores_by_username` | Scores by Connect username |
|
|
373
375
|
| `connect_me` | API key owner profile, scores, and linked accounts — **API key only** |
|
|
374
|
-
| `connect_drop` | Smart Send (EVM batch or Solana SOL/SPL) — **API key only** |
|
|
376
|
+
| `connect_drop` | Smart Send (EVM batch or Solana SOL/SPL); accepts `trustFilter` to gate recipients by trust graph — **API key only** |
|
|
375
377
|
| `connect_drop_balance` | Smart Send wallet balances on a chain — **API key only** |
|
|
378
|
+
| `connect_trust_create` | Create an on-chain trust attestation (EAS on Base) — **API key only** |
|
|
379
|
+
| `connect_trust_check` | Check whether identities sit in a trust graph at depth 1 |
|
|
380
|
+
| `connect_trust_graph` | List outgoing/incoming trust edges for an identity |
|
|
381
|
+
| `connect_trust_revoke` | Revoke active trust attestations by target — **API key only** |
|
|
376
382
|
|
|
377
383
|
|
|
378
384
|
Ask your client to use these tools when you need Connect data or actions.
|
package/dist/mount.d.ts
CHANGED
|
@@ -8,5 +8,8 @@ export interface ConnectMcpMountOptions {
|
|
|
8
8
|
}
|
|
9
9
|
export declare function resolveEmbeddedApiBaseUrl(port: number, env?: NodeJS.ProcessEnv): string;
|
|
10
10
|
export declare function isConnectMcpHost(hostname: string, env?: NodeJS.ProcessEnv): boolean;
|
|
11
|
-
|
|
11
|
+
type NormalizableRequest = Pick<Request, 'headers'> & {
|
|
12
|
+
rawHeaders?: string[];
|
|
13
|
+
};
|
|
14
|
+
export declare function normalizeAcceptHeader(req: NormalizableRequest): void;
|
|
12
15
|
export declare function mountConnectMcpHttp(router: IRouter, options: ConnectMcpMountOptions): void;
|
package/dist/mount.js
CHANGED
|
@@ -51,12 +51,44 @@ export function isConnectMcpHost(hostname, env = process.env) {
|
|
|
51
51
|
// wildcards). A client that genuinely accepts neither is left alone and still
|
|
52
52
|
// gets its 406.
|
|
53
53
|
const CANONICAL_ACCEPT = 'application/json, text/event-stream';
|
|
54
|
+
// The SDK's StreamableHTTPServerTransport (>= 1.26) is a wrapper that converts the
|
|
55
|
+
// Node request into a web-standard Request using @hono/node-server. That converter
|
|
56
|
+
// rebuilds the header set from `req.rawHeaders` and never reads `req.headers`, so
|
|
57
|
+
// writing only `req.headers.accept` is invisible to the transport and the 406 still
|
|
58
|
+
// fires. Write both: `headers` for anything else in the stack, `rawHeaders` for the
|
|
59
|
+
// SDK. Duplicate Accept entries are collapsed, because `new Headers()` would comma
|
|
60
|
+
// join them back into a value we did not intend.
|
|
61
|
+
function setAcceptHeader(req, value) {
|
|
62
|
+
req.headers.accept = value;
|
|
63
|
+
const raw = req.rawHeaders;
|
|
64
|
+
if (!Array.isArray(raw)) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const rebuilt = [];
|
|
68
|
+
let written = false;
|
|
69
|
+
for (let i = 0; i + 1 < raw.length; i += 2) {
|
|
70
|
+
const key = raw[i];
|
|
71
|
+
if (key.toLowerCase() === 'accept') {
|
|
72
|
+
if (!written) {
|
|
73
|
+
rebuilt.push(key, value);
|
|
74
|
+
written = true;
|
|
75
|
+
}
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
rebuilt.push(key, raw[i + 1]);
|
|
79
|
+
}
|
|
80
|
+
if (!written) {
|
|
81
|
+
rebuilt.push('Accept', value);
|
|
82
|
+
}
|
|
83
|
+
raw.length = 0;
|
|
84
|
+
raw.push(...rebuilt);
|
|
85
|
+
}
|
|
54
86
|
export function normalizeAcceptHeader(req) {
|
|
55
87
|
const raw = req.headers.accept;
|
|
56
88
|
const value = Array.isArray(raw) ? raw.join(',') : (raw ?? '');
|
|
57
89
|
// No Accept header means "anything" per RFC 9110.
|
|
58
90
|
if (!value.trim()) {
|
|
59
|
-
req
|
|
91
|
+
setAcceptHeader(req, CANONICAL_ACCEPT);
|
|
60
92
|
return;
|
|
61
93
|
}
|
|
62
94
|
const types = value
|
|
@@ -65,7 +97,7 @@ export function normalizeAcceptHeader(req) {
|
|
|
65
97
|
.filter(Boolean);
|
|
66
98
|
const permits = (type) => types.includes(type) || types.includes('*/*') || types.includes(`${type.split('/')[0]}/*`);
|
|
67
99
|
if (permits('application/json') && permits('text/event-stream')) {
|
|
68
|
-
req
|
|
100
|
+
setAcceptHeader(req, CANONICAL_ACCEPT);
|
|
69
101
|
}
|
|
70
102
|
}
|
|
71
103
|
export function mountConnectMcpHttp(router, options) {
|
package/dist/tools/register.js
CHANGED
|
@@ -34,39 +34,84 @@ const SPENDS_FUNDS = {
|
|
|
34
34
|
openWorldHint: true,
|
|
35
35
|
};
|
|
36
36
|
export function registerTools(server, client) {
|
|
37
|
-
server.
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
server.registerTool('connect_get_price', {
|
|
38
|
+
title: 'Get x402 Prices',
|
|
39
|
+
description: 'Get public x402 list prices for lookup and scores (reference only; live paywall amounts are in 402 responses).',
|
|
40
|
+
inputSchema: {},
|
|
41
|
+
annotations: { title: 'Get x402 Prices', ...READ_ONLY },
|
|
42
|
+
}, async () => client.request({ method: 'GET', path: '/price', authenticated: false }));
|
|
43
|
+
server.registerTool('connect_get_chains', {
|
|
44
|
+
title: 'List Supported Chains',
|
|
45
|
+
description: 'List product chains with per-feature compatibility. lookup is true for EVM catalog chains and Solana; drop is true for Smart Send EVM chains and Solana (chainId 1399811149 for connect_drop / connect_drop_balance).',
|
|
46
|
+
inputSchema: {},
|
|
47
|
+
annotations: { title: 'List Supported Chains', ...READ_ONLY },
|
|
48
|
+
}, async () => client.request({ method: 'GET', path: '/chains', authenticated: false }));
|
|
49
|
+
server.registerTool('connect_lookup', {
|
|
50
|
+
title: 'Resolve Handle to Wallet',
|
|
51
|
+
description: 'Resolve social identities to EVM and Solana wallet addresses. Resolving a recipient who has no wallet provisions one for them, so this is read-only for the caller but not for the recipient. If status is processing, retry the same payload.',
|
|
52
|
+
inputSchema: lookupInputSchema,
|
|
53
|
+
annotations: { title: 'Resolve Handle to Wallet', ...READ_ONLY },
|
|
54
|
+
}, async ({ recipients }) => client.request({
|
|
40
55
|
method: 'POST',
|
|
41
56
|
path: '/lookup',
|
|
42
57
|
body: { recipients },
|
|
43
58
|
}));
|
|
44
|
-
server.
|
|
59
|
+
server.registerTool('connect_lookup_exposed', {
|
|
60
|
+
title: 'List Exposed Accounts',
|
|
61
|
+
description: 'List platforms a recipient has exposed on Connect, with enriched profile, scores, and wallet addresses. Recipient may be a social account, an exposed wallet (EVM/Solana/smart wallet), or a Connect username. May require x402 payment when the profile owner charges for lookups.',
|
|
62
|
+
inputSchema: lookupExposedInputSchema,
|
|
63
|
+
annotations: { title: 'List Exposed Accounts', ...READ_ONLY },
|
|
64
|
+
}, async ({ recipient }) => client.request({
|
|
45
65
|
method: 'POST',
|
|
46
66
|
path: '/lookup/exposed',
|
|
47
67
|
body: { recipient },
|
|
48
68
|
}));
|
|
49
|
-
server.
|
|
69
|
+
server.registerTool('connect_scores_batch', {
|
|
70
|
+
title: 'Score Identities (Batch)',
|
|
71
|
+
description: 'Batch scores for linked accounts or Connect usernames. One result per request user. Optional filter sets passedFilter (quidli 0–100, neynar/lens 0–1, ethos 0–2800).',
|
|
72
|
+
inputSchema: scoresBatchInputSchema,
|
|
73
|
+
annotations: { title: 'Score Identities (Batch)', ...READ_ONLY },
|
|
74
|
+
}, async ({ users, filter }) => client.request({
|
|
50
75
|
method: 'POST',
|
|
51
76
|
path: '/scores',
|
|
52
77
|
body: { users, ...(filter ? { filter } : {}) },
|
|
53
78
|
}));
|
|
54
|
-
server.
|
|
79
|
+
server.registerTool('connect_scores_by_account', {
|
|
80
|
+
title: 'Score a Linked Account',
|
|
81
|
+
description: 'Scores for a linked social account or wallet.',
|
|
82
|
+
inputSchema: scoresByAccountInputSchema,
|
|
83
|
+
annotations: { title: 'Score a Linked Account', ...READ_ONLY },
|
|
84
|
+
}, async ({ platform, identifier }) => client.request({
|
|
55
85
|
method: 'GET',
|
|
56
86
|
path: `/scores/${encodeURIComponent(platform)}/${encodeURIComponent(identifier)}`,
|
|
57
87
|
}));
|
|
58
|
-
server.
|
|
88
|
+
server.registerTool('connect_scores_by_username', {
|
|
89
|
+
title: 'Score a Connect Username',
|
|
90
|
+
description: 'Scores by Connect public username.',
|
|
91
|
+
inputSchema: scoresByUsernameInputSchema,
|
|
92
|
+
annotations: { title: 'Score a Connect Username', ...READ_ONLY },
|
|
93
|
+
}, async ({ username }) => client.request({
|
|
59
94
|
method: 'GET',
|
|
60
95
|
path: `/scores/u/${encodeURIComponent(username.trim())}`,
|
|
61
96
|
}));
|
|
62
|
-
server.
|
|
97
|
+
server.registerTool('connect_me', {
|
|
98
|
+
title: 'Get My Connect Profile',
|
|
99
|
+
description: 'Get the Connect profile, scores, and all linked accounts for the API key owner. Use to identify which user the key belongs to.',
|
|
100
|
+
inputSchema: {},
|
|
101
|
+
annotations: { title: 'Get My Connect Profile', ...READ_ONLY },
|
|
102
|
+
}, async () => client.request({
|
|
63
103
|
method: 'GET',
|
|
64
104
|
path: '/account/me',
|
|
65
105
|
}));
|
|
66
|
-
server.
|
|
67
|
-
|
|
68
|
-
'
|
|
69
|
-
|
|
106
|
+
server.registerTool('connect_drop', {
|
|
107
|
+
title: 'Send Tokens (Smart Send)',
|
|
108
|
+
description: 'Execute a Smart Send from the API key owner Connect embedded wallet. EVM: batch native or ERC-20 (need native gas plus the token). Solana (chainId 1399811149): SOL or SPL from the Solana embedded wallet; packs up to 20 native or 10 SPL recipients per transaction. Social recipient types: email, phone, telegram, discord, farcaster, twitter, github (id or username). linkedin and slack are not on /drop — use connect_lookup first, then type wallet. After connect_lookup, EVM payouts use ethWalletAddress (never solWalletAddress); Solana payouts use solWalletAddress (never ethWalletAddress). Social types on /drop resolve server-side; for type wallet, pass the resolved payout address for the target chain. Omit tokenContract or set it to null for the native token; pass an ERC-20 contract or SPL mint otherwise — do not use the zero address. Amounts are smallest-unit integer strings (ETH 18 decimals, SOL 9, USDC usually 6). ' +
|
|
109
|
+
'Solana native (tokenContract null): no ATA. Sending to a recipient without an existing funded account requires amount ≥ 890880 lamports (rent-exempt minimum for a system account); that SOL stays with the recipient. Below that the tx fails. Sender also pays a ~5000-lamport fee. ' +
|
|
110
|
+
'Solana SPL: tokens sit in Associated Token Accounts (ATA), not on the wallet pubkey. Recipients need not already hold the token — the API prepends CreateIdempotent. The sender (not the recipient) pays ~2039280 lamports (~0.002039 SOL) rent per newly created dest ATA, plus tx fees, on top of the token amount (which can be as small as 1 unit). A 400 "Insufficient funds" on SPL is often missing SOL for ATA rent, not missing USDC. Token-2022 is not supported; USDC mint is EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v. ' +
|
|
111
|
+
'Always call connect_drop_balance first. Returns 201 when submitted or 202 when recipients still processing — retry with the same idempotencyKey.',
|
|
112
|
+
inputSchema: dropInputSchema,
|
|
113
|
+
annotations: { title: 'Send Tokens (Smart Send)', ...SPENDS_FUNDS },
|
|
114
|
+
}, async ({ ignoreFailedRecipients, ...body }) => client.request({
|
|
70
115
|
method: 'POST',
|
|
71
116
|
path: '/drop',
|
|
72
117
|
body,
|
|
@@ -75,32 +120,57 @@ export function registerTools(server, client) {
|
|
|
75
120
|
? { ignoreFailedRecipients: 'true' }
|
|
76
121
|
: undefined,
|
|
77
122
|
}));
|
|
78
|
-
server.
|
|
79
|
-
|
|
80
|
-
|
|
123
|
+
server.registerTool('connect_drop_balance', {
|
|
124
|
+
title: 'Check Smart Send Balance',
|
|
125
|
+
description: 'Get native and token balances for the API key owner Smart Send embedded wallet on a chain. Always call before connect_drop. Zero balances are omitted, so a missing token means balance 0. ' +
|
|
126
|
+
'EVM: confirm native gas plus the ERC-20 being sent. ' +
|
|
127
|
+
'Solana (chainId 1399811149): SOL in this response is spendable lamports on the wallet pubkey (rent locked in existing token accounts is not included). Native SOL drop to a recipient without an existing funded account: amount itself must be ≥ 890880 lamports and sender SOL must cover amount + ~5000 lamports fee. SPL drop: token balance ≥ total amount, and SOL ≥ tx fee + ~2039280 lamports (~0.002039 SOL) per recipient that may need a new Associated Token Account — even when sending USDC. Insufficient SOL for ATA rent fails before the token transfer.',
|
|
128
|
+
inputSchema: dropBalanceInputSchema,
|
|
129
|
+
annotations: { title: 'Check Smart Send Balance', ...READ_ONLY },
|
|
130
|
+
}, async ({ chainId }) => client.request({
|
|
81
131
|
method: 'GET',
|
|
82
132
|
path: '/drop/balance',
|
|
83
133
|
requireApiKey: true,
|
|
84
134
|
query: { chainId: String(chainId) },
|
|
85
135
|
}));
|
|
86
|
-
server.
|
|
136
|
+
server.registerTool('connect_trust_create', {
|
|
137
|
+
title: 'Create Trust Attestation',
|
|
138
|
+
description: 'Create a unidirectional trust attestation on Base (EAS) from the API key owner embedded wallet to a wallet or social identity. Identical active attestations are returned without a new transaction. Requires CONNECT_API_KEY, Smart Send attestation signer enrolled, and ETH on Base for gas.',
|
|
139
|
+
inputSchema: trustCreateInputSchema,
|
|
140
|
+
annotations: { title: 'Create Trust Attestation', ...SPENDS_FUNDS },
|
|
141
|
+
}, async (body) => client.request({
|
|
87
142
|
method: 'POST',
|
|
88
143
|
path: '/trust',
|
|
89
144
|
body,
|
|
90
145
|
requireApiKey: true,
|
|
91
146
|
}));
|
|
92
|
-
server.
|
|
147
|
+
server.registerTool('connect_trust_revoke', {
|
|
148
|
+
title: 'Revoke Trust Attestation',
|
|
149
|
+
description: 'Revoke active trust attestations from the API key owner to a target. Optional context limits which attestations are revoked. Requires CONNECT_API_KEY and ETH on Base.',
|
|
150
|
+
inputSchema: trustRevokeInputSchema,
|
|
151
|
+
annotations: { title: 'Revoke Trust Attestation', ...SPENDS_FUNDS },
|
|
152
|
+
}, async (body) => client.request({
|
|
93
153
|
method: 'POST',
|
|
94
154
|
path: '/trust/revoke',
|
|
95
155
|
body,
|
|
96
156
|
requireApiKey: true,
|
|
97
157
|
}));
|
|
98
|
-
server.
|
|
158
|
+
server.registerTool('connect_trust_check', {
|
|
159
|
+
title: 'Check Trust',
|
|
160
|
+
description: 'Check whether identities sit in a trust graph at depth 1. Omit context to match any context.',
|
|
161
|
+
inputSchema: trustCheckInputSchema,
|
|
162
|
+
annotations: { title: 'Check Trust', ...READ_ONLY },
|
|
163
|
+
}, async (body) => client.request({
|
|
99
164
|
method: 'POST',
|
|
100
165
|
path: '/trust/check',
|
|
101
166
|
body,
|
|
102
167
|
}));
|
|
103
|
-
server.
|
|
168
|
+
server.registerTool('connect_trust_graph', {
|
|
169
|
+
title: 'List Trust Edges',
|
|
170
|
+
description: 'List outgoing or incoming trust edges for an identity at depth 1.',
|
|
171
|
+
inputSchema: trustGraphInputSchema,
|
|
172
|
+
annotations: { title: 'List Trust Edges', ...READ_ONLY },
|
|
173
|
+
}, async ({ platform, identifier, context, direction }) => client.request({
|
|
104
174
|
method: 'GET',
|
|
105
175
|
path: `/trust/graph/${encodeURIComponent(platform)}/${encodeURIComponent(identifier)}`,
|
|
106
176
|
query: {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quidli/connect-mcp",
|
|
3
|
-
"version": "0.6.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.6.5",
|
|
4
|
+
"description": "Onchain reputation and identity for AI agents: score onchain reputation across social accounts, resolve handles to EVM and Solana wallet addresses, and send USDC from any MCP client.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
7
7
|
"mcp-server",
|