@projectsolo/solo-mission-mcp 0.22.1 → 0.23.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.
- package/README.md +4 -3
- package/dist/index.js +4 -21
- package/package.json +1 -1
- package/src/tools/tracks.test.ts +16 -5
- package/src/tools/tracks.ts +16 -24
package/README.md
CHANGED
|
@@ -160,7 +160,7 @@ Free off-chain missions need no wallet. To fund or refund a paid mission, point
|
|
|
160
160
|
|
|
161
161
|
## Tools Reference
|
|
162
162
|
|
|
163
|
-
|
|
163
|
+
33 tools in total.
|
|
164
164
|
|
|
165
165
|
### Agent (1 tool)
|
|
166
166
|
|
|
@@ -219,14 +219,15 @@ Free off-chain missions need no wallet. To fund or refund a paid mission, point
|
|
|
219
219
|
|
|
220
220
|
**Limitation:** The buffers are in-memory only. If the session ends (client closes, process crashes), unread buffered messages are lost and watches stop. To recover, call `get_messages` with a manual `since` timestamp and re-watch.
|
|
221
221
|
|
|
222
|
-
### Media tracks (
|
|
222
|
+
### Media tracks (3 tools, `media_review` missions)
|
|
223
223
|
|
|
224
224
|
| Tool | Description |
|
|
225
225
|
|---|---|
|
|
226
226
|
| `add_mission_track` | Upload an audio, image or video track, via `file_path` (use this for audio/video) or `file_base64` |
|
|
227
227
|
| `list_mission_tracks` | List a mission's tracks with `vote_counts` and `total_listen_seconds` |
|
|
228
228
|
| `get_track_ratings` | Per-participant star ratings and comments for one track |
|
|
229
|
-
|
|
229
|
+
|
|
230
|
+
Tracks can't be deleted or replaced. To change the media, cancel the mission and create a new one: `cancel_mission` for an off-chain or unfunded (`pending_funding`) mission, or `refund_solana_mission` with `action: 'cancel'` for a funded one before its `qualify_deadline`. If an upload fails partway, retry `add_mission_track`: an unconfirmed track is ignored (not listed, not counted toward the 20-track cap or completion).
|
|
230
231
|
|
|
231
232
|
### Solana (4 tools)
|
|
232
233
|
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
apiDelete,
|
|
4
3
|
apiGet,
|
|
5
4
|
apiPatch,
|
|
6
5
|
apiPost,
|
|
@@ -789,7 +788,7 @@ import { readFile } from "fs/promises";
|
|
|
789
788
|
var trackTools = [
|
|
790
789
|
{
|
|
791
790
|
name: "add_mission_track",
|
|
792
|
-
description: "Upload a media item (audio, image, or video) to a media_review mission. Provide the file EITHER via file_path (the MCP server reads it from local disk and uploads it directly \u2014 use this for audio/video, since inlining a multi-MB file as base64 in the tool call can exceed the calling agent's own tool-call or context limits, causing a silent client-side failure before any request reaches the API) OR inline as file_base64 (fine for small images). Exactly one of the two is required. For on-chain missions, call this BEFORE fund_solana_mission \u2014 uploads are blocked once the mission is active. For off-chain missions, call while the mission is active and before any participant is hired. The item becomes visible to hired participants once confirmed.",
|
|
791
|
+
description: "Upload a media item (audio, image, or video) to a media_review mission. Provide the file EITHER via file_path (the MCP server reads it from local disk and uploads it directly \u2014 use this for audio/video, since inlining a multi-MB file as base64 in the tool call can exceed the calling agent's own tool-call or context limits, causing a silent client-side failure before any request reaches the API) OR inline as file_base64 (fine for small images). Exactly one of the two is required. For on-chain missions, call this BEFORE fund_solana_mission \u2014 uploads are blocked once the mission is active. For off-chain missions, call while the mission is active and before any participant is hired. The item becomes visible to hired participants once confirmed. Tracks cannot be deleted or replaced, so check the file before uploading. To change the media, cancel the mission and create a new one: for an off-chain mission or an unfunded Solana mission (pending_funding), use cancel_mission; for a funded Solana mission, use refund_solana_mission with action 'cancel' before its qualify_deadline. If the upload fails partway, it is safe to call add_mission_track again: a track whose upload was never confirmed is ignored (not shown, not counted toward the 20-track cap, not required for completion).",
|
|
793
792
|
inputSchema: {
|
|
794
793
|
type: "object",
|
|
795
794
|
properties: {
|
|
@@ -836,18 +835,6 @@ var trackTools = [
|
|
|
836
835
|
},
|
|
837
836
|
required: ["mission_id", "track_id"]
|
|
838
837
|
}
|
|
839
|
-
},
|
|
840
|
-
{
|
|
841
|
-
name: "delete_mission_track",
|
|
842
|
-
description: "Delete a track from a mission. Only allowed if the track has received no ratings yet.",
|
|
843
|
-
inputSchema: {
|
|
844
|
-
type: "object",
|
|
845
|
-
properties: {
|
|
846
|
-
mission_id: { type: "string", description: "ID of the mission" },
|
|
847
|
-
track_id: { type: "string", description: "ID of the track to delete" }
|
|
848
|
-
},
|
|
849
|
-
required: ["mission_id", "track_id"]
|
|
850
|
-
}
|
|
851
838
|
}
|
|
852
839
|
];
|
|
853
840
|
async function handleTrackTool(name, args) {
|
|
@@ -881,9 +868,9 @@ async function handleTrackTool(name, args) {
|
|
|
881
868
|
});
|
|
882
869
|
if (!uploadRes.ok) {
|
|
883
870
|
const errText = await uploadRes.text().catch(() => "");
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
871
|
+
throw new Error(
|
|
872
|
+
`Media upload failed: ${uploadRes.status} ${errText}. The unconfirmed track is ignored by the platform (not listed, not counted toward the 20-track cap), so it is safe to retry add_mission_track.`
|
|
873
|
+
);
|
|
887
874
|
}
|
|
888
875
|
const confirmed = await apiPost(
|
|
889
876
|
`/agent/missions/${mission_id}/tracks/${urlRes.track_id}/confirm`,
|
|
@@ -899,10 +886,6 @@ async function handleTrackTool(name, args) {
|
|
|
899
886
|
const { mission_id, track_id } = args;
|
|
900
887
|
return apiGet(`/agent/missions/${mission_id}/tracks/${track_id}/ratings`);
|
|
901
888
|
}
|
|
902
|
-
case "delete_mission_track": {
|
|
903
|
-
const { mission_id, track_id } = args;
|
|
904
|
-
return apiDelete(`/agent/missions/${mission_id}/tracks/${track_id}`);
|
|
905
|
-
}
|
|
906
889
|
default:
|
|
907
890
|
throw new Error(`Unknown track tool: ${name}`);
|
|
908
891
|
}
|
package/package.json
CHANGED
package/src/tools/tracks.test.ts
CHANGED
|
@@ -22,7 +22,7 @@ vi.mock('../api/client.js', () => ({
|
|
|
22
22
|
apiDelete: (...args: unknown[]) => apiDelete(...args),
|
|
23
23
|
}));
|
|
24
24
|
|
|
25
|
-
import { handleTrackTool } from './tracks.js';
|
|
25
|
+
import { handleTrackTool, trackTools } from './tracks.js';
|
|
26
26
|
|
|
27
27
|
describe('add_mission_track', () => {
|
|
28
28
|
let tmpDir: string;
|
|
@@ -131,9 +131,8 @@ describe('add_mission_track', () => {
|
|
|
131
131
|
expect(fetchMock).not.toHaveBeenCalled();
|
|
132
132
|
});
|
|
133
133
|
|
|
134
|
-
it('
|
|
134
|
+
it('does not try to delete the pending track when the signed-URL PUT fails, and says a retry is safe', async () => {
|
|
135
135
|
mockHappyPathApi();
|
|
136
|
-
apiDelete.mockResolvedValue(undefined);
|
|
137
136
|
fetchMock.mockResolvedValue({ ok: false, status: 500, text: async () => 'boom' });
|
|
138
137
|
const filePath = join(tmpDir, 'song.mp3');
|
|
139
138
|
writeFileSync(filePath, 'bytes');
|
|
@@ -145,7 +144,19 @@ describe('add_mission_track', () => {
|
|
|
145
144
|
content_type: 'audio/mpeg',
|
|
146
145
|
file_path: filePath,
|
|
147
146
|
}),
|
|
148
|
-
).rejects.toThrow(/Media upload failed/);
|
|
149
|
-
|
|
147
|
+
).rejects.toThrow(/Media upload failed: 500 boom.*safe to retry add_mission_track/);
|
|
148
|
+
// The backend's DELETE track route is gone; an unconfirmed track is ignored server-side.
|
|
149
|
+
expect(apiDelete).not.toHaveBeenCalled();
|
|
150
|
+
// No confirm call for a failed upload.
|
|
151
|
+
expect(apiPost).toHaveBeenCalledTimes(1);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
describe('track tool set', () => {
|
|
156
|
+
it('no longer exposes delete_mission_track', async () => {
|
|
157
|
+
expect(trackTools.map((t) => t.name)).not.toContain('delete_mission_track');
|
|
158
|
+
await expect(handleTrackTool('delete_mission_track', { mission_id: 'm1', track_id: 't1' })).rejects.toThrow(
|
|
159
|
+
/Unknown track tool/,
|
|
160
|
+
);
|
|
150
161
|
});
|
|
151
162
|
});
|
package/src/tools/tracks.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
3
|
-
import { apiGet, apiPost
|
|
3
|
+
import { apiGet, apiPost } from '../api/client.js';
|
|
4
4
|
|
|
5
5
|
export const trackTools: Tool[] = [
|
|
6
6
|
{
|
|
@@ -13,7 +13,13 @@ export const trackTools: Tool[] = [
|
|
|
13
13
|
'small images). Exactly one of the two is required. ' +
|
|
14
14
|
'For on-chain missions, call this BEFORE fund_solana_mission — uploads are blocked once the mission is active. ' +
|
|
15
15
|
'For off-chain missions, call while the mission is active and before any participant is hired. ' +
|
|
16
|
-
'The item becomes visible to hired participants once confirmed.'
|
|
16
|
+
'The item becomes visible to hired participants once confirmed. ' +
|
|
17
|
+
'Tracks cannot be deleted or replaced, so check the file before uploading. To change the media, cancel the ' +
|
|
18
|
+
'mission and create a new one: for an off-chain mission or an unfunded Solana mission (pending_funding), use ' +
|
|
19
|
+
"cancel_mission; for a funded Solana mission, use refund_solana_mission with action 'cancel' before its " +
|
|
20
|
+
'qualify_deadline. ' +
|
|
21
|
+
'If the upload fails partway, it is safe to call add_mission_track again: a track whose upload was never ' +
|
|
22
|
+
'confirmed is ignored (not shown, not counted toward the 20-track cap, not required for completion).',
|
|
17
23
|
inputSchema: {
|
|
18
24
|
type: 'object',
|
|
19
25
|
properties: {
|
|
@@ -78,18 +84,6 @@ export const trackTools: Tool[] = [
|
|
|
78
84
|
required: ['mission_id', 'track_id'],
|
|
79
85
|
},
|
|
80
86
|
},
|
|
81
|
-
{
|
|
82
|
-
name: 'delete_mission_track',
|
|
83
|
-
description: 'Delete a track from a mission. Only allowed if the track has received no ratings yet.',
|
|
84
|
-
inputSchema: {
|
|
85
|
-
type: 'object',
|
|
86
|
-
properties: {
|
|
87
|
-
mission_id: { type: 'string', description: 'ID of the mission' },
|
|
88
|
-
track_id: { type: 'string', description: 'ID of the track to delete' },
|
|
89
|
-
},
|
|
90
|
-
required: ['mission_id', 'track_id'],
|
|
91
|
-
},
|
|
92
|
-
},
|
|
93
87
|
];
|
|
94
88
|
|
|
95
89
|
export async function handleTrackTool(name: string, args: Record<string, any>): Promise<unknown> {
|
|
@@ -125,8 +119,9 @@ export async function handleTrackTool(name: string, args: Record<string, any>):
|
|
|
125
119
|
);
|
|
126
120
|
|
|
127
121
|
// Step 2: Upload binary to the signed URL.
|
|
128
|
-
//
|
|
129
|
-
//
|
|
122
|
+
// A failed upload leaves the track doc pending (unconfirmed). The backend ignores
|
|
123
|
+
// pending tracks: they are not listed, not counted toward the 20-track cap and not
|
|
124
|
+
// required for completion. Tracks cannot be deleted, so there is nothing to clean up.
|
|
130
125
|
const uploadRes = await fetch(urlRes.upload_url, {
|
|
131
126
|
method: 'PUT',
|
|
132
127
|
headers: { 'Content-Type': content_type },
|
|
@@ -134,9 +129,11 @@ export async function handleTrackTool(name: string, args: Record<string, any>):
|
|
|
134
129
|
});
|
|
135
130
|
if (!uploadRes.ok) {
|
|
136
131
|
const errText = await uploadRes.text().catch(() => '');
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
132
|
+
throw new Error(
|
|
133
|
+
`Media upload failed: ${uploadRes.status} ${errText}. ` +
|
|
134
|
+
'The unconfirmed track is ignored by the platform (not listed, not counted toward the 20-track cap), ' +
|
|
135
|
+
'so it is safe to retry add_mission_track.',
|
|
136
|
+
);
|
|
140
137
|
}
|
|
141
138
|
|
|
142
139
|
// Step 3: Confirm upload and optionally set duration
|
|
@@ -158,11 +155,6 @@ export async function handleTrackTool(name: string, args: Record<string, any>):
|
|
|
158
155
|
return apiGet(`/agent/missions/${mission_id}/tracks/${track_id}/ratings`);
|
|
159
156
|
}
|
|
160
157
|
|
|
161
|
-
case 'delete_mission_track': {
|
|
162
|
-
const { mission_id, track_id } = args;
|
|
163
|
-
return apiDelete(`/agent/missions/${mission_id}/tracks/${track_id}`);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
158
|
default:
|
|
167
159
|
throw new Error(`Unknown track tool: ${name}`);
|
|
168
160
|
}
|