@quidli/connect-mcp 0.5.7 → 0.5.9
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 +72 -0
- package/dist/index-http.js +0 -0
- package/dist/index.js +0 -0
- package/dist/tools/register.js +28 -10
- package/package.json +15 -13
package/README.md
CHANGED
|
@@ -153,6 +153,17 @@ Without a key, `initialize` / `tools/list` always succeed. Anonymous `tools/call
|
|
|
153
153
|
|
|
154
154
|
---
|
|
155
155
|
|
|
156
|
+
### Grok
|
|
157
|
+
|
|
158
|
+
1. Go to [grok.com/connectors](https://grok.com/connectors) and select **Custom**.
|
|
159
|
+
2. Enter the server URL: `https://mcp.connect.quid.li`
|
|
160
|
+
|
|
161
|
+
This gives Grok access to the anonymous-tier tools -- lookup, scores, agent, and price -- under the shared quota.
|
|
162
|
+
|
|
163
|
+
**Current limitation:** Grok's custom connector UI does not currently support attaching an API key or custom header. That means `connect_me`, `connect_drop`, and `connect_drop_balance` (all **API key only**) are not usable through Grok yet -- calls to them will return an unauthorized error. This isn't a `connect-mcp` limitation; it's Grok's custom-connector setup not exposing an auth field at this time.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
156
167
|
## Smithery
|
|
157
168
|
|
|
158
169
|
If you already use [Smithery](https://smithery.ai/servers/quidli/connect), it will handle auth and sessions for you. Requires a Smithery account.
|
|
@@ -286,6 +297,67 @@ Credentials are optional in local mode. Without either variable, lookup/scores s
|
|
|
286
297
|
|
|
287
298
|
---
|
|
288
299
|
|
|
300
|
+
## Troubleshooting
|
|
301
|
+
|
|
302
|
+
### `npx` hangs with no output when run from a background service
|
|
303
|
+
|
|
304
|
+
If you're launching `@quidli/connect-mcp` via `npx` from inside a background
|
|
305
|
+
process manager -- a macOS `launchd` agent, a systemd unit, a supervisor, or
|
|
306
|
+
any client config running without a normal interactive shell -- it can hang
|
|
307
|
+
**indefinitely with zero output and no error**. This isn't a crash; `npx` is
|
|
308
|
+
stuck resolving `PATH` in an environment that doesn't provide the same shell
|
|
309
|
+
initialization an interactive terminal gets.
|
|
310
|
+
|
|
311
|
+
**Workaround:** if you have [Bun](https://bun.sh) installed, use `bunx`
|
|
312
|
+
instead of `npx`. It performs the same job -- running the package without a
|
|
313
|
+
local install -- but doesn't hit this hang in restricted-`PATH` environments.
|
|
314
|
+
|
|
315
|
+
```json
|
|
316
|
+
{
|
|
317
|
+
"mcpServers": {
|
|
318
|
+
"quidli-connect": {
|
|
319
|
+
"command": "bunx",
|
|
320
|
+
"args": ["-y", "@quidli/connect-mcp"],
|
|
321
|
+
"env": {
|
|
322
|
+
"CONNECT_API_KEY": "your-api-key"
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
If you're spawning the process yourself (rather than through a client's MCP
|
|
330
|
+
config) and want to be resilient to `bunx` not being on `PATH` either,
|
|
331
|
+
resolve it by absolute path first:
|
|
332
|
+
|
|
333
|
+
```ts
|
|
334
|
+
import { spawn } from "node:child_process";
|
|
335
|
+
import { existsSync } from "node:fs";
|
|
336
|
+
import { homedir } from "node:os";
|
|
337
|
+
import { join } from "node:path";
|
|
338
|
+
|
|
339
|
+
const BUNX_PATH = (() => {
|
|
340
|
+
const candidate = join(homedir(), ".bun", "bin", "bunx");
|
|
341
|
+
return existsSync(candidate) ? candidate : "bunx";
|
|
342
|
+
})();
|
|
343
|
+
|
|
344
|
+
const proc = spawn(BUNX_PATH, ["-y", "@quidli/connect-mcp"], {
|
|
345
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
346
|
+
});
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
This isn't a general claim that Bun is better than npm -- it's a targeted fix
|
|
350
|
+
for one specific, reproducible failure mode when `npx` is spawned outside an
|
|
351
|
+
interactive shell.
|
|
352
|
+
|
|
353
|
+
### Wrong environment variable name for the API key
|
|
354
|
+
|
|
355
|
+
Double-check you're setting `CONNECT_API_KEY`, not `API_KEY` -- the server
|
|
356
|
+
only reads `CONNECT_API_KEY`, and a mismatch here fails silently: your calls
|
|
357
|
+
will run in anonymous/unauthenticated mode instead of erroring, which can
|
|
358
|
+
look like a working-but-oddly-limited connection rather than a config
|
|
359
|
+
mistake.
|
|
360
|
+
|
|
289
361
|
## Tools
|
|
290
362
|
|
|
291
363
|
|
package/dist/index-http.js
CHANGED
|
File without changes
|
package/dist/index.js
CHANGED
|
File without changes
|
package/dist/tools/register.js
CHANGED
|
@@ -11,37 +11,55 @@ export const CONNECT_MCP_TOOL_NAMES = [
|
|
|
11
11
|
'connect_drop',
|
|
12
12
|
'connect_drop_balance',
|
|
13
13
|
];
|
|
14
|
+
/**
|
|
15
|
+
* Tool annotations. `readOnlyHint` is load-bearing, not decorative: clients
|
|
16
|
+
* auto-register Connect tools by it rather than by a hand-maintained allowlist,
|
|
17
|
+
* so an unannotated tool is not offered to a model at all. A tool that can move
|
|
18
|
+
* funds must never carry READ_ONLY.
|
|
19
|
+
*/
|
|
20
|
+
const READ_ONLY = {
|
|
21
|
+
readOnlyHint: true,
|
|
22
|
+
destructiveHint: false,
|
|
23
|
+
idempotentHint: true,
|
|
24
|
+
openWorldHint: true,
|
|
25
|
+
};
|
|
26
|
+
const SPENDS_FUNDS = {
|
|
27
|
+
readOnlyHint: false,
|
|
28
|
+
destructiveHint: true,
|
|
29
|
+
idempotentHint: true,
|
|
30
|
+
openWorldHint: true,
|
|
31
|
+
};
|
|
14
32
|
export function registerTools(server, client) {
|
|
15
|
-
server.tool('connect_get_price', 'Get public x402 list prices for lookup and scores (reference only; live paywall amounts are in 402 responses).', {}, async () => client.request({ method: 'GET', path: '/price', authenticated: false }));
|
|
16
|
-
server.tool('connect_get_chains', 'List product chains with per-feature compatibility. lookup is true for EVM catalog chains and Solana; drop is true only for Smart Send EVM chains (use those chainIds for connect_drop / connect_drop_balance).', {}, async () => client.request({ method: 'GET', path: '/chains', authenticated: false }));
|
|
17
|
-
server.tool('connect_lookup', 'Resolve social identities to EVM and Solana wallet addresses. If status is processing, retry the same payload.', lookupInputSchema, async ({ recipients }) => client.request({
|
|
33
|
+
server.tool('connect_get_price', 'Get public x402 list prices for lookup and scores (reference only; live paywall amounts are in 402 responses).', {}, READ_ONLY, async () => client.request({ method: 'GET', path: '/price', authenticated: false }));
|
|
34
|
+
server.tool('connect_get_chains', 'List product chains with per-feature compatibility. lookup is true for EVM catalog chains and Solana; drop is true only for Smart Send EVM chains (use those chainIds for connect_drop / connect_drop_balance).', {}, READ_ONLY, async () => client.request({ method: 'GET', path: '/chains', authenticated: false }));
|
|
35
|
+
server.tool('connect_lookup', '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.', lookupInputSchema, READ_ONLY, async ({ recipients }) => client.request({
|
|
18
36
|
method: 'POST',
|
|
19
37
|
path: '/lookup',
|
|
20
38
|
body: { recipients },
|
|
21
39
|
}));
|
|
22
|
-
server.tool('connect_lookup_exposed', '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.', lookupExposedInputSchema, async ({ recipient }) => client.request({
|
|
40
|
+
server.tool('connect_lookup_exposed', '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.', lookupExposedInputSchema, READ_ONLY, async ({ recipient }) => client.request({
|
|
23
41
|
method: 'POST',
|
|
24
42
|
path: '/lookup/exposed',
|
|
25
43
|
body: { recipient },
|
|
26
44
|
}));
|
|
27
|
-
server.tool('connect_scores_batch', 'Batch scores for linked accounts or Connect usernames. Optional filter excludes users below minScore (quidli 0–100, neynar/lens 0–1, ethos 0–2800).', scoresBatchInputSchema, async ({ users, filter }) => client.request({
|
|
45
|
+
server.tool('connect_scores_batch', 'Batch scores for linked accounts or Connect usernames. Optional filter excludes users below minScore (quidli 0–100, neynar/lens 0–1, ethos 0–2800).', scoresBatchInputSchema, READ_ONLY, async ({ users, filter }) => client.request({
|
|
28
46
|
method: 'POST',
|
|
29
47
|
path: '/scores',
|
|
30
48
|
body: { users, ...(filter ? { filter } : {}) },
|
|
31
49
|
}));
|
|
32
|
-
server.tool('connect_scores_by_account', 'Scores for a linked social account or wallet.', scoresByAccountInputSchema, async ({ platform, identifier }) => client.request({
|
|
50
|
+
server.tool('connect_scores_by_account', 'Scores for a linked social account or wallet.', scoresByAccountInputSchema, READ_ONLY, async ({ platform, identifier }) => client.request({
|
|
33
51
|
method: 'GET',
|
|
34
52
|
path: `/scores/${encodeURIComponent(platform)}/${encodeURIComponent(identifier)}`,
|
|
35
53
|
}));
|
|
36
|
-
server.tool('connect_scores_by_username', 'Scores by Connect public username.', scoresByUsernameInputSchema, async ({ username }) => client.request({
|
|
54
|
+
server.tool('connect_scores_by_username', 'Scores by Connect public username.', scoresByUsernameInputSchema, READ_ONLY, async ({ username }) => client.request({
|
|
37
55
|
method: 'GET',
|
|
38
56
|
path: `/scores/u/${encodeURIComponent(username.trim())}`,
|
|
39
57
|
}));
|
|
40
|
-
server.tool('connect_me', 'Get the Connect profile, scores, and all linked accounts for the API key owner. Use to identify which user the key belongs to.', {}, async () => client.request({
|
|
58
|
+
server.tool('connect_me', 'Get the Connect profile, scores, and all linked accounts for the API key owner. Use to identify which user the key belongs to.', {}, READ_ONLY, async () => client.request({
|
|
41
59
|
method: 'GET',
|
|
42
60
|
path: '/account/me',
|
|
43
61
|
}));
|
|
44
|
-
server.tool('connect_drop', 'Execute a Smart Send (batch native or ERC-20 transfer). Omit tokenContract or set it to null for the chain native token (ETH on 1/8453/10/42161/480, POL on 137, AVAX on 43114); pass the ERC-20 contract address otherwise — do not use the zero address. Amounts are smallest-unit integer strings; use connect_drop_balance decimals (native ETH = 18, USDC usually 6). Returns 201 when submitted or 202 when recipients still processing — retry with the same idempotencyKey.', dropInputSchema, async ({ ignoreFailedRecipients, ...body }) => client.request({
|
|
62
|
+
server.tool('connect_drop', 'Execute a Smart Send (batch native or ERC-20 transfer). Omit tokenContract or set it to null for the chain native token (ETH on 1/8453/10/42161/480, POL on 137, AVAX on 43114); pass the ERC-20 contract address otherwise — do not use the zero address. Amounts are smallest-unit integer strings; use connect_drop_balance decimals (native ETH = 18, USDC usually 6). Returns 201 when submitted or 202 when recipients still processing — retry with the same idempotencyKey.', dropInputSchema, SPENDS_FUNDS, async ({ ignoreFailedRecipients, ...body }) => client.request({
|
|
45
63
|
method: 'POST',
|
|
46
64
|
path: '/drop',
|
|
47
65
|
body,
|
|
@@ -50,7 +68,7 @@ export function registerTools(server, client) {
|
|
|
50
68
|
? { ignoreFailedRecipients: 'true' }
|
|
51
69
|
: undefined,
|
|
52
70
|
}));
|
|
53
|
-
server.tool('connect_drop_balance', 'Get native and ERC-20 balances for the API key owner Smart Send embedded wallet on a chain. Use before connect_drop to verify gas and token funds.', dropBalanceInputSchema, async ({ chainId }) => client.request({
|
|
71
|
+
server.tool('connect_drop_balance', 'Get native and ERC-20 balances for the API key owner Smart Send embedded wallet on a chain. Use before connect_drop to verify gas and token funds.', dropBalanceInputSchema, READ_ONLY, async ({ chainId }) => client.request({
|
|
54
72
|
method: 'GET',
|
|
55
73
|
path: '/drop/balance',
|
|
56
74
|
requireApiKey: true,
|
package/package.json
CHANGED
|
@@ -1,28 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quidli/connect-mcp",
|
|
3
|
-
"version": "0.5.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.5.9",
|
|
4
|
+
"description": "MCP server for agent identity and reputation: resolve social handles to wallet addresses, score onchain reputation, and send USDC from any MCP client.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
7
7
|
"mcp-server",
|
|
8
8
|
"model-context-protocol",
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"web3-agent",
|
|
12
|
-
"agent-payments",
|
|
13
|
-
"multiplayer-agents",
|
|
9
|
+
"reputation",
|
|
10
|
+
"onchain-reputation",
|
|
14
11
|
"identity",
|
|
15
12
|
"identity-resolution",
|
|
16
|
-
"reputation",
|
|
17
|
-
"payments",
|
|
18
13
|
"wallet",
|
|
14
|
+
"wallet-lookup",
|
|
15
|
+
"agent",
|
|
16
|
+
"ai",
|
|
17
|
+
"agent-payments",
|
|
18
|
+
"payments",
|
|
19
19
|
"usdc",
|
|
20
|
-
"
|
|
20
|
+
"crypto",
|
|
21
|
+
"web3-agent",
|
|
22
|
+
"multiplayer-agents",
|
|
21
23
|
"x402",
|
|
22
24
|
"ethereum",
|
|
23
25
|
"solana",
|
|
24
26
|
"base",
|
|
25
|
-
"
|
|
27
|
+
"farcaster",
|
|
26
28
|
"claude",
|
|
27
29
|
"cursor",
|
|
28
30
|
"claude-desktop"
|
|
@@ -53,8 +55,8 @@
|
|
|
53
55
|
"./http": "./dist/http.js"
|
|
54
56
|
},
|
|
55
57
|
"scripts": {
|
|
56
|
-
"build": "tsc",
|
|
57
|
-
"prepublishOnly": "npm run build
|
|
58
|
+
"build": "tsc && chmod +x dist/index.js dist/index-http.js",
|
|
59
|
+
"prepublishOnly": "npm run build",
|
|
58
60
|
"start": "node dist/index.js",
|
|
59
61
|
"start:http": "node dist/index-http.js",
|
|
60
62
|
"test": "vitest run",
|