@solworks/poll-mcp 0.1.22 → 0.1.26
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/dist/index.js
CHANGED
|
@@ -26,7 +26,7 @@ function jsonPropToZod(prop) {
|
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
export function createServer(config, clientOverride) {
|
|
29
|
-
const server = new McpServer({ name: 'poll-fun', version: PKG_VERSION }, { capabilities: { tools: {}, resources: {
|
|
29
|
+
const server = new McpServer({ name: 'poll-fun', version: PKG_VERSION }, { capabilities: { tools: {}, resources: { listChanged: true }, prompts: {} } });
|
|
30
30
|
const client = clientOverride ??
|
|
31
31
|
new PollClient({
|
|
32
32
|
apiUrl: config?.apiUrl ?? 'https://api.poll.fun',
|
package/dist/tools/index.js
CHANGED
|
@@ -568,6 +568,10 @@ export const TOOL_DEFINITIONS = {
|
|
|
568
568
|
client.getBet(betAddress),
|
|
569
569
|
client.getAccount(),
|
|
570
570
|
]);
|
|
571
|
+
const status = (bet.status ?? '').toLowerCase();
|
|
572
|
+
if (status !== 'resolving') {
|
|
573
|
+
return errorResult(`Cannot vote: voting has not been initiated for this bet (status: ${bet.status}). Use initiate_vote first.`);
|
|
574
|
+
}
|
|
571
575
|
const alreadyVoted = Array.isArray(bet.votes) && bet.votes.some((v) => v.user === account.uuid || v.userUuid === account.uuid);
|
|
572
576
|
if (alreadyVoted) {
|
|
573
577
|
return errorResult('You have already voted on this bet');
|
|
@@ -747,6 +751,10 @@ export const TOOL_DEFINITIONS = {
|
|
|
747
751
|
if (!betAddress)
|
|
748
752
|
return errorResult('Missing required parameter: betAddress');
|
|
749
753
|
try {
|
|
754
|
+
const alreadyFavourited = await client.isBetFavourited(betAddress);
|
|
755
|
+
if (alreadyFavourited) {
|
|
756
|
+
return textResult(`Bet is already in your favourites: ${betAddress}`);
|
|
757
|
+
}
|
|
750
758
|
await client.favouriteBet(betAddress);
|
|
751
759
|
return textResult(`Bet added to favourites: ${betAddress}`);
|
|
752
760
|
}
|
|
@@ -771,6 +779,10 @@ export const TOOL_DEFINITIONS = {
|
|
|
771
779
|
if (!betAddress)
|
|
772
780
|
return errorResult('Missing required parameter: betAddress');
|
|
773
781
|
try {
|
|
782
|
+
const isFavourited = await client.isBetFavourited(betAddress);
|
|
783
|
+
if (!isFavourited) {
|
|
784
|
+
return textResult(`Bet is not in your favourites: ${betAddress}`);
|
|
785
|
+
}
|
|
774
786
|
await client.unfavouriteBet(betAddress);
|
|
775
787
|
return textResult(`Bet removed from favourites: ${betAddress}`);
|
|
776
788
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solworks/poll-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.26",
|
|
4
4
|
"description": "MCP server for Poll.fun. See documentation at https://dev.poll.fun",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
21
|
-
"@solworks/poll-api-client": "
|
|
21
|
+
"@solworks/poll-api-client": "^0.1.17",
|
|
22
22
|
"zod": "^3.24.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
@@ -140,6 +140,17 @@ describe('MCP Server integration', () => {
|
|
|
140
140
|
expect(prompts.length).toBe(3);
|
|
141
141
|
});
|
|
142
142
|
|
|
143
|
+
it('can read a resource and re-read returns fresh data', async () => {
|
|
144
|
+
const result1 = await mcpClient.readResource({ uri: 'poll://user/balances' });
|
|
145
|
+
expect(result1.contents[0].text).toContain('XP');
|
|
146
|
+
|
|
147
|
+
const result2 = await mcpClient.readResource({ uri: 'poll://user/balances' });
|
|
148
|
+
expect(result2.contents[0].text).toContain('XP');
|
|
149
|
+
|
|
150
|
+
// Verify handler was called twice (no server-side caching)
|
|
151
|
+
expect(client.getXpBalance).toHaveBeenCalledTimes(2);
|
|
152
|
+
});
|
|
153
|
+
|
|
143
154
|
it('handles API errors gracefully', async () => {
|
|
144
155
|
const errorClient = mockClient({
|
|
145
156
|
getAccount: vi.fn().mockRejectedValue(new Error('API Error 401: Unauthorized')),
|
|
@@ -106,6 +106,41 @@ describe('Bet tool handlers', () => {
|
|
|
106
106
|
expect(result.content[0].text).toContain('Missing required parameter: betAddress');
|
|
107
107
|
});
|
|
108
108
|
|
|
109
|
+
it('vote returns error when voting not initiated', async () => {
|
|
110
|
+
const client = mockClient({
|
|
111
|
+
getBet: vi.fn().mockResolvedValue({
|
|
112
|
+
status: 'Pending',
|
|
113
|
+
votes: [],
|
|
114
|
+
}),
|
|
115
|
+
getAccount: vi.fn().mockResolvedValue({ uuid: 'user-1' }),
|
|
116
|
+
});
|
|
117
|
+
const result = await TOOL_DEFINITIONS.vote.handler(
|
|
118
|
+
{ betAddress: 'addr1', optionIndex: 0 },
|
|
119
|
+
client
|
|
120
|
+
);
|
|
121
|
+
expect(result.isError).toBe(true);
|
|
122
|
+
expect(result.content[0].text).toContain('voting has not been initiated');
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('vote succeeds when bet is in Resolving status', async () => {
|
|
126
|
+
const voteFn = vi.fn().mockResolvedValue({});
|
|
127
|
+
const client = mockClient({
|
|
128
|
+
getBet: vi.fn().mockResolvedValue({
|
|
129
|
+
status: 'Resolving',
|
|
130
|
+
votes: [],
|
|
131
|
+
}),
|
|
132
|
+
getAccount: vi.fn().mockResolvedValue({ uuid: 'user-1' }),
|
|
133
|
+
vote: voteFn,
|
|
134
|
+
});
|
|
135
|
+
const result = await TOOL_DEFINITIONS.vote.handler(
|
|
136
|
+
{ betAddress: 'addr1', optionIndex: 0 },
|
|
137
|
+
client
|
|
138
|
+
);
|
|
139
|
+
expect(result.isError).toBeUndefined();
|
|
140
|
+
expect(result.content[0].text).toContain('Vote cast successfully');
|
|
141
|
+
expect(voteFn).toHaveBeenCalledWith({ marketPubkey: 'addr1', outcome: 'for' });
|
|
142
|
+
});
|
|
143
|
+
|
|
109
144
|
it('create_bet handles API error', async () => {
|
|
110
145
|
const client = mockClient({
|
|
111
146
|
createBet: vi.fn().mockRejectedValue(new Error('API Error 400: Invalid question')),
|
|
@@ -23,7 +23,10 @@ describe('Social tool handlers', () => {
|
|
|
23
23
|
|
|
24
24
|
it('favourite_bet calls client', async () => {
|
|
25
25
|
const favouriteFn = vi.fn().mockResolvedValue({});
|
|
26
|
-
const client = mockClient({
|
|
26
|
+
const client = mockClient({
|
|
27
|
+
isBetFavourited: vi.fn().mockResolvedValue(false),
|
|
28
|
+
favouriteBet: favouriteFn,
|
|
29
|
+
});
|
|
27
30
|
const result = await TOOL_DEFINITIONS.favourite_bet.handler(
|
|
28
31
|
{ betAddress: 'addr-fav' },
|
|
29
32
|
client
|
|
@@ -33,6 +36,51 @@ describe('Social tool handlers', () => {
|
|
|
33
36
|
expect(favouriteFn).toHaveBeenCalledWith('addr-fav');
|
|
34
37
|
});
|
|
35
38
|
|
|
39
|
+
it('favourite_bet returns already-favourited message', async () => {
|
|
40
|
+
const favouriteFn = vi.fn();
|
|
41
|
+
const client = mockClient({
|
|
42
|
+
isBetFavourited: vi.fn().mockResolvedValue(true),
|
|
43
|
+
favouriteBet: favouriteFn,
|
|
44
|
+
});
|
|
45
|
+
const result = await TOOL_DEFINITIONS.favourite_bet.handler(
|
|
46
|
+
{ betAddress: 'addr-fav' },
|
|
47
|
+
client
|
|
48
|
+
);
|
|
49
|
+
expect(result.isError).toBeUndefined();
|
|
50
|
+
expect(result.content[0].text).toContain('Bet is already in your favourites: addr-fav');
|
|
51
|
+
expect(favouriteFn).not.toHaveBeenCalled();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('unfavourite_bet removes favourite', async () => {
|
|
55
|
+
const unfavouriteFn = vi.fn().mockResolvedValue({});
|
|
56
|
+
const client = mockClient({
|
|
57
|
+
isBetFavourited: vi.fn().mockResolvedValue(true),
|
|
58
|
+
unfavouriteBet: unfavouriteFn,
|
|
59
|
+
});
|
|
60
|
+
const result = await TOOL_DEFINITIONS.unfavourite_bet.handler(
|
|
61
|
+
{ betAddress: 'addr-fav' },
|
|
62
|
+
client
|
|
63
|
+
);
|
|
64
|
+
expect(result.isError).toBeUndefined();
|
|
65
|
+
expect(result.content[0].text).toContain('Bet removed from favourites: addr-fav');
|
|
66
|
+
expect(unfavouriteFn).toHaveBeenCalledWith('addr-fav');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('unfavourite_bet returns not-favourited message', async () => {
|
|
70
|
+
const unfavouriteFn = vi.fn();
|
|
71
|
+
const client = mockClient({
|
|
72
|
+
isBetFavourited: vi.fn().mockResolvedValue(false),
|
|
73
|
+
unfavouriteBet: unfavouriteFn,
|
|
74
|
+
});
|
|
75
|
+
const result = await TOOL_DEFINITIONS.unfavourite_bet.handler(
|
|
76
|
+
{ betAddress: 'addr-fav' },
|
|
77
|
+
client
|
|
78
|
+
);
|
|
79
|
+
expect(result.isError).toBeUndefined();
|
|
80
|
+
expect(result.content[0].text).toContain('Bet is not in your favourites: addr-fav');
|
|
81
|
+
expect(unfavouriteFn).not.toHaveBeenCalled();
|
|
82
|
+
});
|
|
83
|
+
|
|
36
84
|
it('send_friend_request calls client', async () => {
|
|
37
85
|
const sendFn = vi.fn().mockResolvedValue({});
|
|
38
86
|
const client = mockClient({ sendFriendRequest: sendFn });
|