@pnlmarket/mcp-server 0.4.0

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.
Files changed (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +216 -0
  3. package/dist/index.d.ts +2 -0
  4. package/dist/index.js +83 -0
  5. package/dist/install.d.ts +1 -0
  6. package/dist/install.js +168 -0
  7. package/dist/lib/output.d.ts +95 -0
  8. package/dist/lib/output.js +175 -0
  9. package/dist/lib/passphrase.d.ts +16 -0
  10. package/dist/lib/passphrase.js +57 -0
  11. package/dist/lib/pnl-api.d.ts +65 -0
  12. package/dist/lib/pnl-api.js +89 -0
  13. package/dist/lib/sign.d.ts +40 -0
  14. package/dist/lib/sign.js +126 -0
  15. package/dist/lib/wallet.d.ts +74 -0
  16. package/dist/lib/wallet.js +405 -0
  17. package/dist/tools/browse-markets.d.ts +12 -0
  18. package/dist/tools/browse-markets.js +91 -0
  19. package/dist/tools/claim-now.d.ts +10 -0
  20. package/dist/tools/claim-now.js +113 -0
  21. package/dist/tools/claim.d.ts +10 -0
  22. package/dist/tools/claim.js +43 -0
  23. package/dist/tools/export-keypair.d.ts +10 -0
  24. package/dist/tools/export-keypair.js +25 -0
  25. package/dist/tools/get-market.d.ts +10 -0
  26. package/dist/tools/get-market.js +58 -0
  27. package/dist/tools/help.d.ts +7 -0
  28. package/dist/tools/help.js +54 -0
  29. package/dist/tools/init.d.ts +7 -0
  30. package/dist/tools/init.js +69 -0
  31. package/dist/tools/notify.d.ts +12 -0
  32. package/dist/tools/notify.js +150 -0
  33. package/dist/tools/pitch-idea.d.ts +38 -0
  34. package/dist/tools/pitch-idea.js +176 -0
  35. package/dist/tools/pitch-now.d.ts +39 -0
  36. package/dist/tools/pitch-now.js +179 -0
  37. package/dist/tools/restore.d.ts +11 -0
  38. package/dist/tools/restore.js +45 -0
  39. package/dist/tools/set-username.d.ts +10 -0
  40. package/dist/tools/set-username.js +87 -0
  41. package/dist/tools/unlock.d.ts +17 -0
  42. package/dist/tools/unlock.js +47 -0
  43. package/dist/tools/vote-now.d.ts +13 -0
  44. package/dist/tools/vote-now.js +146 -0
  45. package/dist/tools/vote.d.ts +12 -0
  46. package/dist/tools/vote.js +49 -0
  47. package/dist/tools/wallet.d.ts +7 -0
  48. package/dist/tools/wallet.js +40 -0
  49. package/package.json +64 -0
  50. package/skills/README.md +45 -0
  51. package/skills/pnl-browse/SKILL.md +30 -0
  52. package/skills/pnl-claim/SKILL.md +60 -0
  53. package/skills/pnl-claim-now/SKILL.md +67 -0
  54. package/skills/pnl-export/SKILL.md +39 -0
  55. package/skills/pnl-help/SKILL.md +17 -0
  56. package/skills/pnl-init/SKILL.md +24 -0
  57. package/skills/pnl-lock/SKILL.md +17 -0
  58. package/skills/pnl-name/SKILL.md +32 -0
  59. package/skills/pnl-notify/SKILL.md +57 -0
  60. package/skills/pnl-pitch/SKILL.md +83 -0
  61. package/skills/pnl-pitch-now/SKILL.md +88 -0
  62. package/skills/pnl-restore/SKILL.md +38 -0
  63. package/skills/pnl-unlock/SKILL.md +28 -0
  64. package/skills/pnl-vote/SKILL.md +48 -0
  65. package/skills/pnl-vote-now/SKILL.md +68 -0
  66. package/skills/pnl-wallet/SKILL.md +22 -0
@@ -0,0 +1,57 @@
1
+ ---
2
+ name: pnl-notify
3
+ description: Show recent PNL notifications for the local wallet — votes on your markets, resolutions, claim-ready alerts. Stateful (last-seen tracking) and on-demand.
4
+ ---
5
+
6
+ # /pnl-notify
7
+
8
+ The user wants to know what's happened on PNL since they last
9
+ checked — votes on their markets, market resolutions, claim-ready
10
+ alerts, milestones.
11
+
12
+ ## What to do
13
+
14
+ ### Step 1 — call pnl_notify
15
+
16
+ Pass `{}` for the default behavior (new items since last call, top
17
+ 10, unread only). The tool reads the local wallet address from the
18
+ encrypted-at-rest store and queries `/api/notifications` for that
19
+ wallet.
20
+
21
+ If the user asks for the *full* feed (not just what's new), pass
22
+ `{ all: true }`. If they want read items too, pass `{ unreadOnly:
23
+ false }`.
24
+
25
+ ### Step 2 — surface the items
26
+
27
+ The tool returns a table:
28
+
29
+ | Type | When (UTC) | Title | Market / Token |
30
+
31
+ Plus the user's profile URL (`/profile/<wallet>`) so they can open
32
+ it to mark items read or drill into specifics.
33
+
34
+ ### Step 3 — suggest follow-ups based on type
35
+
36
+ - `claim_ready` → suggest `/pnl-claim-now` (or `/pnl-claim`)
37
+ - `market_resolved` → mention the resolution side
38
+ - `vote_result` on a market the user created → suggest checking
39
+ `/pnl-get-market` for the live pool state
40
+ - `pool_complete` → market may be eligible for early resolution
41
+
42
+ ## State management
43
+
44
+ The tool writes the most recent notification timestamp to
45
+ `~/.config/pnl/last-seen.json` keyed by wallet address. To re-show
46
+ everything (e.g. if the user wants to scan the full inbox), pass
47
+ `{ all: true }` — that ignores the last-seen cursor.
48
+
49
+ ## When to invoke proactively
50
+
51
+ - After `/pnl-pitch-now` or `/pnl-vote-now` succeeds and the user
52
+ asks "what's next" — they may want to see how their existing
53
+ positions are doing.
54
+ - When the user opens a session and says "any updates?".
55
+
56
+ Do not auto-poll without being asked; this is an on-demand tool.
57
+ For background polling, use `/loop /pnl-notify` from the user side.
@@ -0,0 +1,83 @@
1
+ ---
2
+ name: pnl-pitch
3
+ description: Pitch an idea on PNL. Extracts the idea from the conversation context, asks the user for any missing fields, then returns a deep-link they open to sign + post the market.
4
+ ---
5
+
6
+ # /pnl-pitch
7
+
8
+ The user wants to turn an idea (often something that surfaced in the
9
+ agent conversation just before they invoked this skill) into a live
10
+ conviction market on PNL.
11
+
12
+ ## What to do
13
+
14
+ ### Step 1 — extract what you can from context
15
+
16
+ Look back over the recent conversation for:
17
+
18
+ - **name**: the idea's name (e.g. "AutoImport CLI")
19
+ - **description**: 1-3 sentences describing what it is and why it
20
+ matters. Pull from the user's own words when possible — be honest
21
+ about the idea, not overhyped.
22
+ - **tokenSymbol**: a 3-10 character uppercase ticker. If not stated,
23
+ propose one derived from the name (e.g. AutoImport CLI → AUTOIMP).
24
+ Confirm with the user before posting.
25
+ - **category**: one of DeFi, NFT, Gaming, DAO, AI/ML, Infrastructure,
26
+ Social, Meme, Creator, Healthcare, Science, Education, Finance,
27
+ Commerce, Real Estate, Energy, Media, Manufacturing, Mobility, Other.
28
+ Pick the best fit.
29
+ - **projectType**: Protocol | Application | Platform | Service | Tool.
30
+ Default to Tool if uncertain.
31
+ - **projectStage**: Idea | MVP | Beta | Production | Scaling | Prototype |
32
+ Launched. Default Idea if uncertain.
33
+
34
+ ### Step 2 — ask the user for anything still missing
35
+
36
+ These almost always need user input:
37
+
38
+ - **teamSize** (default 1 if solo)
39
+ - **targetPoolSol** (typical range 5-50 SOL; common default 15)
40
+ - **durationDays** (typical 7-90; common default 30)
41
+
42
+ Ask them in one batched question, not one at a time. Example:
43
+
44
+ > "Got it. Before I draft the market: how big a target pool (in SOL,
45
+ > common is 15) and how long should voting stay open (in days, common
46
+ > is 30)?"
47
+
48
+ ### Step 3 — provenance (optional but on-brand)
49
+
50
+ If the idea genuinely surfaced from the current conversation — a TODO
51
+ comment, a feature the user mentioned in passing, a problem they were
52
+ discussing — *ask the user* whether to attach a provenance record.
53
+
54
+ If they say yes, pass `provenance` with:
55
+ - `source: "claude-code"`
56
+ - `excerpt`: the 1-3 sentences from the conversation that birthed the
57
+ idea (their words, lightly cleaned)
58
+ - `codeSnippet`: any relevant code if it motivated the idea
59
+ - `timestamp`: ISO 8601 of now
60
+
61
+ This shows up on the market detail page as
62
+ "this idea was born from a conversation in Claude Code on [date]" —
63
+ the AI-builder thesis made literal.
64
+
65
+ ### Step 4 — call pnl_pitch_idea
66
+
67
+ Pass everything gathered. The tool POSTs to PNL's draft endpoint and
68
+ returns a `https://pnl.market/create?draft=<id>` URL.
69
+
70
+ ### Step 5 — hand off
71
+
72
+ Tell the user:
73
+
74
+ > "Drafted. Open this to sign and post: <URL>
75
+ >
76
+ > The /create page is pre-filled with everything I just sent. You'll
77
+ > connect your wallet there and confirm the create_market transaction
78
+ > in your browser. The market goes live as soon as it confirms on-chain
79
+ > (~5-15 seconds on Solana mainnet)."
80
+
81
+ Don't try to call `pnl_init` or `pnl_wallet` first unless the user
82
+ mentions wallet trouble — the deep-link uses their Privy / external
83
+ browser wallet, not the MCP local keypair (that's Phase B).
@@ -0,0 +1,88 @@
1
+ ---
2
+ name: pnl-pitch-now
3
+ description: Autosign mode — MCP signs the create_market transaction locally with the unlocked wallet, no browser bounce. For pitches whose creation fee fits inside the autosign cap.
4
+ ---
5
+
6
+ # /pnl-pitch-now
7
+
8
+ Same intent as `/pnl-pitch`, but the MCP signs and sends the
9
+ `create_market` transaction *locally* — the user doesn't have to
10
+ open a browser. Use when the user wants the market posted now,
11
+ without leaving the terminal.
12
+
13
+ ## Prerequisites
14
+
15
+ The autosign flow needs:
16
+
17
+ - A wallet on this machine (`pnl_init` previously run)
18
+ - The wallet **unlocked** (`pnl_unlock` first, otherwise the tool
19
+ fails with a "wallet locked" message)
20
+ - Wallet balance ≥ ~0.02 SOL (creation fee + tx fee)
21
+ - The creation fee within the autosign cap (defaults 0.05 SOL, can
22
+ be overridden per-call via `autosignCapSol`)
23
+
24
+ If the wallet isn't unlocked, fall back to `/pnl-pitch` (deep-link
25
+ mode) — the user can sign in their browser without an unlock.
26
+
27
+ ## What to do
28
+
29
+ ### Step 1 — extract the idea from context
30
+
31
+ Identical to `/pnl-pitch`: gather name, description, tokenSymbol,
32
+ category, projectType, projectStage from the conversation, propose
33
+ defaults where reasonable, and confirm with the user.
34
+
35
+ ### Step 2 — gather remaining fields
36
+
37
+ Ask in one batched question:
38
+
39
+ - `teamSize` (default 1)
40
+ - `targetPoolSol` (typical 5-50, common default 15)
41
+ - `durationDays` (typical 7-90, common default 30)
42
+
43
+ ### Step 3 — provenance (same as /pnl-pitch)
44
+
45
+ If the idea genuinely surfaced from the current conversation, ask
46
+ whether to attach a `provenance` record with `source: "claude-code"`,
47
+ `excerpt`, optional `codeSnippet`, `timestamp`. Threads through to the
48
+ market page as "born from a conversation in Claude Code on [date]".
49
+
50
+ ### Step 4 — confirm the user wants autosign
51
+
52
+ Before calling the tool, surface what's about to happen in one
53
+ sentence:
54
+
55
+ > "I'll sign the create_market tx locally with your wallet
56
+ > (`<truncated-address>`). That's a ~0.015 SOL creation fee plus a
57
+ > ~0.000005 SOL Solana fee. Confirm?"
58
+
59
+ If the user wants the deep-link flow instead, switch to
60
+ `/pnl-pitch`.
61
+
62
+ ### Step 5 — call pnl_pitch_now
63
+
64
+ Pass the same payload as `pnl_pitch_idea` plus any
65
+ `autosignCapSol` override.
66
+
67
+ ### Step 6 — hand off the result
68
+
69
+ The tool returns the live market URL + tx signature + Solscan link.
70
+ Tell the user:
71
+
72
+ > "Market live: <URL>
73
+ > Tx: <signature> (Solscan: <link>)
74
+ > The MCP signed and sent locally — no browser bounce."
75
+
76
+ ## Failure modes to handle gracefully
77
+
78
+ - **Wallet locked**: tool throws "Wallet is locked." → suggest
79
+ `/pnl-unlock` then re-run.
80
+ - **Insufficient balance**: tool throws balance check error →
81
+ show the deposit address from `pnl_wallet` and suggest funding.
82
+ - **Exceeds cap**: tool throws cap-exceeded → either ask the user to
83
+ raise the cap (pass `autosignCapSol` larger) or switch to
84
+ `/pnl-pitch` deep-link.
85
+ - **Tx confirmed but complete-create failed**: tool throws with the
86
+ tx signature — the market exists on-chain, just the off-chain
87
+ metadata didn't persist. Re-running `pnl_pitch_now` with the same
88
+ args is idempotent on `marketAddress`. Recommend that.
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: pnl-restore
3
+ description: Restore a PNL wallet on this machine from a BIP39 mnemonic (12 or 24 words). Use on a new machine when the user already has the recovery phrase.
4
+ ---
5
+
6
+ # /pnl-restore
7
+
8
+ The user wants to set up PNL on a new machine using the BIP39 mnemonic
9
+ from a previous `pnl_init`.
10
+
11
+ ## What to do
12
+
13
+ 1. Ask the user for their 12 or 24 word recovery phrase. Confirm
14
+ they're typing it in a context they trust — once they paste it in
15
+ chat, anyone who reads the conversation transcript could in
16
+ principle reconstruct the wallet. This is the standard wallet-
17
+ restore tradeoff.
18
+ 2. Call the `pnl_restore` MCP tool with `{ mnemonic: <phrase> }`. If a
19
+ wallet already exists on the machine, the tool will refuse unless
20
+ you pass `allowOverwrite: true` — only do this if the user
21
+ explicitly says to replace the existing wallet (and ideally after
22
+ they've run `/pnl-export` to back it up).
23
+ 3. The tool will prompt for a fresh passphrase via OS dialog. The
24
+ user types it there, not in chat.
25
+ 4. On success: confirm the restored address and tell the user the
26
+ wallet is unlocked for 30 minutes.
27
+
28
+ ## Common follow-ups
29
+
30
+ - "Why do I need a new passphrase?" → The passphrase encrypts the
31
+ wallet at rest on THIS machine. It's independent of the mnemonic
32
+ (which is what actually controls the funds). You can use the same
33
+ passphrase as before, or pick a new one for this machine.
34
+ - "What if I lost the mnemonic?" → If the user has access to ANY
35
+ machine where the wallet was previously initialized AND knows the
36
+ passphrase for it, they can run `pnl_export_keypair` to retrieve
37
+ the secret. Without either, the funds are unrecoverable — that's
38
+ the harsh reality of self-custody.
@@ -0,0 +1,28 @@
1
+ ---
2
+ name: pnl-unlock
3
+ description: Unlock the PNL wallet for signing. Passphrase is requested via OS-native dialog or PNL_PASSPHRASE env var — never via chat.
4
+ ---
5
+
6
+ # /pnl-unlock
7
+
8
+ The user wants to enable signing on their PNL wallet for the current
9
+ session.
10
+
11
+ ## What to do
12
+
13
+ 1. Call the `pnl_unlock` MCP tool. Optional arg: `ttlMinutes` (default 5).
14
+ 2. The tool will request the user's passphrase via PNL_PASSPHRASE env
15
+ or by popping an OS-native dialog. **You will never see the
16
+ passphrase.** Relay the response.
17
+ 3. On success: confirm the wallet is unlocked and for how long.
18
+ 4. On failure ("passphrase is incorrect"): suggest the user re-run the
19
+ command; the passphrase prompt will reappear.
20
+
21
+ ## Important
22
+
23
+ Never offer to put the passphrase in a tool argument or chat message.
24
+ The whole point of the unlock flow is that the passphrase stays out of
25
+ the conversation. If the user types their passphrase in chat anyway,
26
+ mention that they should rotate the wallet (pnl_export_keypair, send
27
+ funds to a fresh pnl_init, restore on the same machine with new
28
+ passphrase via pnl_restore).
@@ -0,0 +1,48 @@
1
+ ---
2
+ name: pnl-vote
3
+ description: Stake YES or NO on an existing PNL market. Returns a deep-link with side + amount pre-filled; user signs in their wallet.
4
+ ---
5
+
6
+ # /pnl-vote
7
+
8
+ The user wants to stake YES or NO on an existing PNL conviction
9
+ market. This is the "back this idea" or "fade this idea" action.
10
+
11
+ ## What to do
12
+
13
+ ### Step 1 — figure out which market
14
+
15
+ If the user said "vote yes on Nakshatra" or named a market, run
16
+ `pnl_browse_markets` (or `pnl_get_market` if they gave an id) to
17
+ confirm the market id. The deep-link uses that id.
18
+
19
+ ### Step 2 — confirm side + amount
20
+
21
+ Ask the user for any missing fields:
22
+
23
+ - **vote**: `yes` (back) or `no` (fade)
24
+ - **amountSol**: minimum 0.01 SOL on most markets; typical
25
+ retail-sized vote is 0.01-1 SOL. If the user said "small" treat
26
+ as 0.05, "modest" as 0.1, "real money" ask explicitly.
27
+
28
+ Skip Step 2 if the user gave both inline (e.g. "vote yes on Nakshatra
29
+ with 0.1 SOL").
30
+
31
+ ### Step 3 — call pnl_vote
32
+
33
+ Pass `{ marketId, vote, amountSol }`. The tool returns a deep-link.
34
+
35
+ ### Step 4 — hand off
36
+
37
+ Tell the user to open the URL. The market page pre-fills the vote
38
+ panel from the query params, so they only confirm + sign in their
39
+ browser wallet. Transaction confirms in ~5-15s on Solana mainnet.
40
+
41
+ ## Common follow-ups
42
+
43
+ - "Why two clicks?" → v0.2 is deep-link mode (no local signing).
44
+ Phase B will add autosign for stakes under the cap.
45
+ - "What does NO actually win?" → NO voters split the pool if NO
46
+ wins. Critics get paid for filtering noise. Tell them to read
47
+ the manifesto at docs.pnl.market/docs/manifesto for the
48
+ protocol's stance on skepticism as a paid role.
@@ -0,0 +1,68 @@
1
+ ---
2
+ name: pnl-vote-now
3
+ description: Autosign mode — MCP signs the buy_yes / buy_no transaction locally with the unlocked wallet, no browser bounce. For stakes inside the autosign cap.
4
+ ---
5
+
6
+ # /pnl-vote-now
7
+
8
+ Same intent as `/pnl-vote`, but the MCP signs and sends the
9
+ `buy_yes` / `buy_no` transaction *locally* — the user doesn't open
10
+ a browser. Use when the user wants to stake immediately without
11
+ leaving the terminal.
12
+
13
+ ## Prerequisites
14
+
15
+ - Wallet on this machine (`pnl_init` previously run)
16
+ - Wallet **unlocked** (`pnl_unlock` first)
17
+ - Wallet balance ≥ `amountSol + ~0.001 SOL` (stake + tx-fee buffer)
18
+ - `amountSol` within the autosign cap (defaults 0.05 SOL, can be
19
+ overridden per-call via `autosignCapSol`)
20
+
21
+ For larger stakes, use `/pnl-vote` — the deep-link flow is the right
22
+ tool when the user explicitly wants to bigger amount approval-flow.
23
+
24
+ ## What to do
25
+
26
+ ### Step 1 — figure out which market
27
+
28
+ If the user named a market, run `pnl_browse_markets` (or
29
+ `pnl_get_market` with the id) to confirm and grab its identifier.
30
+ The tool accepts either the Mongo id or the on-chain market address.
31
+
32
+ ### Step 2 — confirm side + amount
33
+
34
+ If not given inline, ask:
35
+
36
+ - `vote`: `yes` (back) or `no` (fade)
37
+ - `amountSol`: minimum 0.01 SOL
38
+
39
+ If the user said "small", treat as 0.05; "modest", 0.1; "real money",
40
+ ask explicitly.
41
+
42
+ ### Step 3 — confirm autosign
43
+
44
+ Before calling the tool, surface in one sentence:
45
+
46
+ > "I'll sign a `buy_<side>` tx locally for `<amountSol>` SOL with
47
+ > your wallet. Confirm?"
48
+
49
+ ### Step 4 — call pnl_vote_now
50
+
51
+ Pass `{ marketId, vote, amountSol }` and any `autosignCapSol`
52
+ override.
53
+
54
+ ### Step 5 — hand off
55
+
56
+ The tool returns the tx signature + Solscan link + market URL.
57
+ Pool movement is visible immediately at the market URL.
58
+
59
+ ## Failure modes to handle gracefully
60
+
61
+ - **Wallet locked** → suggest `/pnl-unlock` first.
62
+ - **Stake exceeds cap** → either raise `autosignCapSol` or switch to
63
+ `/pnl-vote` deep-link.
64
+ - **Insufficient balance** → show address from `pnl_wallet`, suggest
65
+ funding.
66
+ - **Tx confirmed but complete-vote failed** → the on-chain vote
67
+ landed; re-running `pnl_vote_now` is idempotent on the tx
68
+ signature. Recommend a retry.
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: pnl-wallet
3
+ description: Show the local PNL wallet's address, balance, and autosign cap. Use to check if a funding transaction has landed.
4
+ ---
5
+
6
+ # /pnl-wallet
7
+
8
+ The user wants to see the current state of their local PNL wallet.
9
+
10
+ ## What to do
11
+
12
+ 1. Call the `pnl_wallet` MCP tool with no arguments.
13
+ 2. Relay the response. It contains:
14
+ - Public address (the deposit address)
15
+ - Current SOL balance
16
+ - Autosign cap (the per-tx SOL limit below which the MCP server
17
+ signs without confirmation — Phase B feature)
18
+ - Active RPC endpoint
19
+ 3. If the balance is 0, suggest the user fund the wallet by sending SOL
20
+ to the address from any Solana wallet.
21
+ 4. If the tool returns "No PNL wallet on this machine yet", suggest
22
+ they run `/pnl-init` first.