towow-mcp 0.2.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/dist/client.d.ts +121 -0
- package/dist/client.js +374 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +26 -0
- package/dist/config.js +89 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +923 -0
- package/dist/index.js.map +1 -0
- package/dist/sse.d.ts +26 -0
- package/dist/sse.js +116 -0
- package/dist/sse.js.map +1 -0
- package/package.json +38 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,923 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Towow MCP Server — canonical workflow adapter over /protocol APIs.
|
|
4
|
+
*
|
|
5
|
+
* Port of Python server.py — 30 tool definitions with identical names and parameters.
|
|
6
|
+
*/
|
|
7
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
import { TowowClient, AuthError, APIError } from "./client.js";
|
|
11
|
+
import { getSessionToken, saveSessionToken, clearSessionToken, getAgentId, getDisplayName, saveAgent, clearAgent, } from "./config.js";
|
|
12
|
+
import { consumeSseStream, SSEError, SSEAuthError } from "./sse.js";
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Instructions (identical to Python version)
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
const _INSTRUCTIONS = `\
|
|
17
|
+
Towow (通爻) is an agent collaboration protocol. It connects agents who can help \
|
|
18
|
+
each other — not by searching, but by mutual discovery and structured negotiation.
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
If the user hasn't registered yet, help them get started:
|
|
23
|
+
1. **Register**: \`towow_register\` with an invite code, email, password, name, and a profile.
|
|
24
|
+
2. **Build a good profile**: The profile is how other agents discover you. Include:
|
|
25
|
+
- Who you are (role, background, key skills)
|
|
26
|
+
- What you're working on (current projects, goals)
|
|
27
|
+
- What you need help with (specific needs, collaboration interests)
|
|
28
|
+
- What you can offer others (expertise, resources, connections)
|
|
29
|
+
A 3-5 paragraph profile works best. Vague profiles get poor matches.
|
|
30
|
+
3. **Discover collaborators**: Use \`towow_formulation_start\` to describe what you need, \
|
|
31
|
+
then \`towow_formulation_confirm\` to find matching agents.
|
|
32
|
+
4. **Invite & negotiate**: Send invitations to promising matches with \`towow_invite\`, \
|
|
33
|
+
then start a negotiation run with \`towow_negotiate\`.
|
|
34
|
+
|
|
35
|
+
## Typical Workflow
|
|
36
|
+
|
|
37
|
+
\`\`\`
|
|
38
|
+
Register/Login → Build Profile → Describe Need → Discover Matches
|
|
39
|
+
→ Review Nominations → Invite → Negotiate → Get Results
|
|
40
|
+
\`\`\`
|
|
41
|
+
|
|
42
|
+
### Formulation (Need Refinement)
|
|
43
|
+
When the user has a vague need, use \`towow_formulation_start\` to begin a structured \
|
|
44
|
+
conversation that refines it into a clear, matchable demand. The system will ask \
|
|
45
|
+
clarifying questions. Reply with \`towow_formulation_reply\` until the need is well-defined, \
|
|
46
|
+
then \`towow_formulation_confirm\` to trigger discovery.
|
|
47
|
+
|
|
48
|
+
### Quick Discovery
|
|
49
|
+
For a clear, specific need, skip formulation and use \`towow_discover\` directly.
|
|
50
|
+
|
|
51
|
+
### After Discovery
|
|
52
|
+
Results include nominations — agents who might be good collaborators. For each nomination:
|
|
53
|
+
- Review their profile and match reason
|
|
54
|
+
- If interested, create an invitation with \`towow_invite\`
|
|
55
|
+
- Once invitations are accepted, start negotiation with \`towow_negotiate\`
|
|
56
|
+
|
|
57
|
+
### Negotiation
|
|
58
|
+
A negotiation run brings multiple agents together to co-create a plan. Each participant \
|
|
59
|
+
contributes their perspective. The system synthesizes all contributions into actionable \
|
|
60
|
+
deliverables. Check progress with \`towow_run_status\` and get results with \`towow_result\`.
|
|
61
|
+
|
|
62
|
+
## Profile Standards
|
|
63
|
+
|
|
64
|
+
A good profile should include:
|
|
65
|
+
- **Identity**: Name, role, organization (if any)
|
|
66
|
+
- **Expertise**: What you know well, what you've built
|
|
67
|
+
- **Current focus**: What you're working on now
|
|
68
|
+
- **Needs**: What kind of help or collaboration you're looking for
|
|
69
|
+
- **Offerings**: What you can contribute to others
|
|
70
|
+
|
|
71
|
+
You can update the profile anytime with \`towow_update_profile\`.
|
|
72
|
+
|
|
73
|
+
## BYOK (Bring Your Own Key)
|
|
74
|
+
Most features (formulation, discovery) require an LLM. You can bind your own API key:
|
|
75
|
+
1. \`towow_byok_bind\` with your Anthropic API key
|
|
76
|
+
2. \`towow_byok_status\` to check if it's active
|
|
77
|
+
3. \`towow_byok_clear\` to remove it
|
|
78
|
+
|
|
79
|
+
Without BYOK, only pre-configured platform keys are used (if available).
|
|
80
|
+
|
|
81
|
+
## Tips
|
|
82
|
+
- Use \`towow_agents\` to see all your agents and which one is selected.
|
|
83
|
+
- Each account can have multiple agents for different contexts.
|
|
84
|
+
- Use \`towow_agent_delete\` to clean up agents you no longer need.
|
|
85
|
+
- Use \`towow_refresh_token\` if your session is about to expire.
|
|
86
|
+
- Invitations expire — respond promptly with \`towow_invitation_respond\`.
|
|
87
|
+
- After a run completes, use \`towow_feedback\` to rate the experience.
|
|
88
|
+
- Use \`towow_get_formulation\` to check formulation state without sending a message.
|
|
89
|
+
- Use \`towow_get_discovery\` to re-read previous discovery results.
|
|
90
|
+
`;
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
// Server
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
const server = new McpServer({ name: "towow", version: "0.2.0" }, { instructions: _INSTRUCTIONS });
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
// Helpers
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
function getClient() {
|
|
99
|
+
return new TowowClient();
|
|
100
|
+
}
|
|
101
|
+
function requireAuth() {
|
|
102
|
+
if (!getSessionToken()) {
|
|
103
|
+
throw new ToolError("Not logged in. Call towow_login or towow_register first.");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function requireAgentId() {
|
|
107
|
+
const agentId = getAgentId();
|
|
108
|
+
if (!agentId) {
|
|
109
|
+
throw new ToolError("No current agent selected. Call towow_agents or towow_agent_select first.");
|
|
110
|
+
}
|
|
111
|
+
return agentId;
|
|
112
|
+
}
|
|
113
|
+
function toJson(payload) {
|
|
114
|
+
return JSON.stringify(payload);
|
|
115
|
+
}
|
|
116
|
+
function selectedAgentPayload() {
|
|
117
|
+
return {
|
|
118
|
+
agent_id: getAgentId(),
|
|
119
|
+
display_name: getDisplayName(),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function pickAgentPayload(agent) {
|
|
123
|
+
return {
|
|
124
|
+
agent_id: agent.agent_id,
|
|
125
|
+
display_name: agent.display_name,
|
|
126
|
+
visibility: agent.visibility,
|
|
127
|
+
scoped_tags: agent.scoped_tags ?? [],
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
// ToolError maps to Python's ToolError — returns isError content to the LLM.
|
|
131
|
+
class ToolError extends Error {
|
|
132
|
+
constructor(message) {
|
|
133
|
+
super(message);
|
|
134
|
+
this.name = "ToolError";
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function ok(text) {
|
|
138
|
+
return { content: [{ type: "text", text }] };
|
|
139
|
+
}
|
|
140
|
+
function err(text) {
|
|
141
|
+
return { content: [{ type: "text", text }], isError: true };
|
|
142
|
+
}
|
|
143
|
+
function handleToolError(e) {
|
|
144
|
+
if (e instanceof ToolError)
|
|
145
|
+
return err(e.message);
|
|
146
|
+
if (e instanceof AuthError)
|
|
147
|
+
return err(e.message);
|
|
148
|
+
if (e instanceof APIError)
|
|
149
|
+
return err(`API error: ${e.detail}`);
|
|
150
|
+
if (e instanceof SSEError)
|
|
151
|
+
return err(`Formulation error: ${e.message}`);
|
|
152
|
+
if (e instanceof SSEAuthError)
|
|
153
|
+
return err(e.message);
|
|
154
|
+
if (e instanceof Error)
|
|
155
|
+
return err(e.message);
|
|
156
|
+
return err(String(e));
|
|
157
|
+
}
|
|
158
|
+
// ---------------------------------------------------------------------------
|
|
159
|
+
// Shared logic: canonical formulation confirm
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
async function canonicalFormulationConfirm(opts) {
|
|
162
|
+
requireAuth();
|
|
163
|
+
const client = getClient();
|
|
164
|
+
const confirmed = await client.confirmFormulation({
|
|
165
|
+
sessionId: opts.sessionId,
|
|
166
|
+
scopeTags: opts.scopeTags,
|
|
167
|
+
maxCandidates: opts.maxCandidates,
|
|
168
|
+
});
|
|
169
|
+
const queryId = confirmed.query_id;
|
|
170
|
+
const query = await client.getDiscoveryQuery(queryId);
|
|
171
|
+
return {
|
|
172
|
+
session_id: opts.sessionId,
|
|
173
|
+
demand_id: confirmed.demand_id,
|
|
174
|
+
query_id: queryId,
|
|
175
|
+
session: confirmed.session,
|
|
176
|
+
query,
|
|
177
|
+
selected_agent: selectedAgentPayload(),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
// ===========================================================================
|
|
181
|
+
// Tool definitions (30 tools — exact same names and params as Python version)
|
|
182
|
+
// ===========================================================================
|
|
183
|
+
// 1. towow_register
|
|
184
|
+
server.registerTool("towow_register", {
|
|
185
|
+
description: "Register a new Towow account and persist the returned session locally.",
|
|
186
|
+
inputSchema: z.object({
|
|
187
|
+
invite_code: z.string(),
|
|
188
|
+
email: z.string(),
|
|
189
|
+
password: z.string(),
|
|
190
|
+
name: z.string(),
|
|
191
|
+
profile_text: z.string(),
|
|
192
|
+
}),
|
|
193
|
+
}, async ({ invite_code, email, password, name, profile_text }) => {
|
|
194
|
+
try {
|
|
195
|
+
const client = getClient();
|
|
196
|
+
const result = await client.register(invite_code, email, password, name, profile_text);
|
|
197
|
+
const token = result.session_token;
|
|
198
|
+
if (token)
|
|
199
|
+
saveSessionToken(token);
|
|
200
|
+
const defaultAgentId = result.default_agent_id;
|
|
201
|
+
if (defaultAgentId)
|
|
202
|
+
saveAgent(defaultAgentId, result.name);
|
|
203
|
+
return ok(toJson({
|
|
204
|
+
account_id: result.account_id,
|
|
205
|
+
default_agent_id: defaultAgentId,
|
|
206
|
+
email: result.email,
|
|
207
|
+
name: result.name,
|
|
208
|
+
selected_agent: selectedAgentPayload(),
|
|
209
|
+
}));
|
|
210
|
+
}
|
|
211
|
+
catch (e) {
|
|
212
|
+
return handleToolError(e);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
// 2. towow_login
|
|
216
|
+
server.registerTool("towow_login", {
|
|
217
|
+
description: "Login to Towow and persist the returned session locally.",
|
|
218
|
+
inputSchema: z.object({
|
|
219
|
+
email: z.string(),
|
|
220
|
+
password: z.string(),
|
|
221
|
+
}),
|
|
222
|
+
}, async ({ email, password }) => {
|
|
223
|
+
try {
|
|
224
|
+
const client = getClient();
|
|
225
|
+
const result = await client.login(email, password);
|
|
226
|
+
const token = result.session_token;
|
|
227
|
+
if (token)
|
|
228
|
+
saveSessionToken(token);
|
|
229
|
+
const defaultAgentId = result.default_agent_id;
|
|
230
|
+
if (defaultAgentId)
|
|
231
|
+
saveAgent(defaultAgentId, result.name);
|
|
232
|
+
return ok(toJson({
|
|
233
|
+
account_id: result.account_id,
|
|
234
|
+
default_agent_id: defaultAgentId,
|
|
235
|
+
email: result.email,
|
|
236
|
+
name: result.name,
|
|
237
|
+
expires_at: result.expires_at,
|
|
238
|
+
selected_agent: selectedAgentPayload(),
|
|
239
|
+
}));
|
|
240
|
+
}
|
|
241
|
+
catch (e) {
|
|
242
|
+
return handleToolError(e);
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
// 3. towow_agents
|
|
246
|
+
server.registerTool("towow_agents", {
|
|
247
|
+
description: "List all agents owned by the current account and show the selected agent.",
|
|
248
|
+
inputSchema: z.object({}),
|
|
249
|
+
}, async () => {
|
|
250
|
+
try {
|
|
251
|
+
requireAuth();
|
|
252
|
+
const client = getClient();
|
|
253
|
+
const result = await client.listAgents();
|
|
254
|
+
const agents = result.agents ?? [];
|
|
255
|
+
return ok(toJson({
|
|
256
|
+
selected_agent: selectedAgentPayload(),
|
|
257
|
+
agents: agents.map(pickAgentPayload),
|
|
258
|
+
}));
|
|
259
|
+
}
|
|
260
|
+
catch (e) {
|
|
261
|
+
return handleToolError(e);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
// 4. towow_agent_create
|
|
265
|
+
server.registerTool("towow_agent_create", {
|
|
266
|
+
description: "Create a new agent under the current account.",
|
|
267
|
+
inputSchema: z.object({
|
|
268
|
+
display_name: z.string(),
|
|
269
|
+
profile_text: z.string(),
|
|
270
|
+
visibility: z.string().default("scoped"),
|
|
271
|
+
scoped_tags: z.array(z.string()).optional(),
|
|
272
|
+
}),
|
|
273
|
+
}, async ({ display_name, profile_text, visibility, scoped_tags }) => {
|
|
274
|
+
try {
|
|
275
|
+
requireAuth();
|
|
276
|
+
const client = getClient();
|
|
277
|
+
const agent = await client.createAgent({
|
|
278
|
+
displayName: display_name,
|
|
279
|
+
profileText: profile_text,
|
|
280
|
+
visibility,
|
|
281
|
+
scopedTags: scoped_tags,
|
|
282
|
+
});
|
|
283
|
+
return ok(toJson({
|
|
284
|
+
agent: pickAgentPayload(agent),
|
|
285
|
+
selected_agent: selectedAgentPayload(),
|
|
286
|
+
}));
|
|
287
|
+
}
|
|
288
|
+
catch (e) {
|
|
289
|
+
return handleToolError(e);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
// 5. towow_agent_select
|
|
293
|
+
server.registerTool("towow_agent_select", {
|
|
294
|
+
description: "Select which owned agent subsequent MCP actions should use.",
|
|
295
|
+
inputSchema: z.object({
|
|
296
|
+
agent_id: z.string(),
|
|
297
|
+
}),
|
|
298
|
+
}, async ({ agent_id }) => {
|
|
299
|
+
try {
|
|
300
|
+
requireAuth();
|
|
301
|
+
const client = getClient();
|
|
302
|
+
const agent = await client.getAgent(agent_id);
|
|
303
|
+
saveAgent(agent.agent_id, agent.display_name);
|
|
304
|
+
return ok(toJson({
|
|
305
|
+
selected_agent: selectedAgentPayload(),
|
|
306
|
+
agent: pickAgentPayload(agent),
|
|
307
|
+
}));
|
|
308
|
+
}
|
|
309
|
+
catch (e) {
|
|
310
|
+
return handleToolError(e);
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
// 6. towow_agent_set_visibility
|
|
314
|
+
server.registerTool("towow_agent_set_visibility", {
|
|
315
|
+
description: "Update the visibility policy for the selected agent or an explicitly supplied agent.",
|
|
316
|
+
inputSchema: z.object({
|
|
317
|
+
visibility: z.string(),
|
|
318
|
+
agent_id: z.string().optional(),
|
|
319
|
+
}),
|
|
320
|
+
}, async ({ visibility, agent_id }) => {
|
|
321
|
+
try {
|
|
322
|
+
requireAuth();
|
|
323
|
+
const selectedAgentId = agent_id ?? requireAgentId();
|
|
324
|
+
const client = getClient();
|
|
325
|
+
const updated = await client.setAgentVisibility(selectedAgentId, visibility);
|
|
326
|
+
if (selectedAgentId === getAgentId()) {
|
|
327
|
+
saveAgent(updated.agent_id, updated.display_name);
|
|
328
|
+
}
|
|
329
|
+
return ok(toJson({
|
|
330
|
+
agent: pickAgentPayload(updated),
|
|
331
|
+
selected_agent: selectedAgentPayload(),
|
|
332
|
+
}));
|
|
333
|
+
}
|
|
334
|
+
catch (e) {
|
|
335
|
+
return handleToolError(e);
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
// 7. towow_formulation_start
|
|
339
|
+
server.registerTool("towow_formulation_start", {
|
|
340
|
+
description: "Start a canonical formulation session for the selected agent.",
|
|
341
|
+
inputSchema: z.object({
|
|
342
|
+
raw_intent: z.string(),
|
|
343
|
+
scene_context: z.string().default("startup-hub"),
|
|
344
|
+
}),
|
|
345
|
+
}, async ({ raw_intent, scene_context }) => {
|
|
346
|
+
try {
|
|
347
|
+
requireAuth();
|
|
348
|
+
const agentId = requireAgentId();
|
|
349
|
+
const client = getClient();
|
|
350
|
+
const result = await client.createFormulation({
|
|
351
|
+
agentId,
|
|
352
|
+
rawIntent: raw_intent,
|
|
353
|
+
sceneContext: scene_context,
|
|
354
|
+
});
|
|
355
|
+
const session = result.session;
|
|
356
|
+
return ok(toJson({
|
|
357
|
+
session_id: session.session_id,
|
|
358
|
+
agent_id: session.agent_id,
|
|
359
|
+
reply: result.assistant_reply,
|
|
360
|
+
document: session.formulated_demand,
|
|
361
|
+
updates: result.updates,
|
|
362
|
+
selected_agent: selectedAgentPayload(),
|
|
363
|
+
}));
|
|
364
|
+
}
|
|
365
|
+
catch (e) {
|
|
366
|
+
return handleToolError(e);
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
// 8. towow_formulation_reply
|
|
370
|
+
server.registerTool("towow_formulation_reply", {
|
|
371
|
+
description: "Continue a formulation session and return the fully-consumed SSE result.",
|
|
372
|
+
inputSchema: z.object({
|
|
373
|
+
session_id: z.string(),
|
|
374
|
+
user_message: z.string(),
|
|
375
|
+
}),
|
|
376
|
+
}, async ({ session_id, user_message }) => {
|
|
377
|
+
try {
|
|
378
|
+
requireAuth();
|
|
379
|
+
const client = getClient();
|
|
380
|
+
const resp = await client.formulationReplyStream(session_id, user_message);
|
|
381
|
+
const { fullText, updates } = await consumeSseStream(resp);
|
|
382
|
+
const client2 = getClient();
|
|
383
|
+
const updatedSession = await client2.getFormulation(session_id);
|
|
384
|
+
return ok(toJson({
|
|
385
|
+
session_id,
|
|
386
|
+
reply: fullText,
|
|
387
|
+
document: updatedSession.formulated_demand ?? "",
|
|
388
|
+
updates,
|
|
389
|
+
selected_agent: selectedAgentPayload(),
|
|
390
|
+
}));
|
|
391
|
+
}
|
|
392
|
+
catch (e) {
|
|
393
|
+
return handleToolError(e);
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
// 9. towow_formulation_confirm
|
|
397
|
+
server.registerTool("towow_formulation_confirm", {
|
|
398
|
+
description: "Confirm a formulation session, execute discovery, and return the canonical query payload.",
|
|
399
|
+
inputSchema: z.object({
|
|
400
|
+
session_id: z.string(),
|
|
401
|
+
scope_tags: z.array(z.string()).optional(),
|
|
402
|
+
max_candidates: z.number().default(10),
|
|
403
|
+
}),
|
|
404
|
+
}, async ({ session_id, scope_tags, max_candidates }) => {
|
|
405
|
+
try {
|
|
406
|
+
const result = await canonicalFormulationConfirm({
|
|
407
|
+
sessionId: session_id,
|
|
408
|
+
scopeTags: scope_tags,
|
|
409
|
+
maxCandidates: max_candidates,
|
|
410
|
+
});
|
|
411
|
+
return ok(toJson(result));
|
|
412
|
+
}
|
|
413
|
+
catch (e) {
|
|
414
|
+
return handleToolError(e);
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
// 10. towow_discover
|
|
418
|
+
server.registerTool("towow_discover", {
|
|
419
|
+
description: "Run an ad-hoc discovery query for the selected agent.",
|
|
420
|
+
inputSchema: z.object({
|
|
421
|
+
demand_text: z.string(),
|
|
422
|
+
scope_tags: z.array(z.string()).optional(),
|
|
423
|
+
scene_context: z.string().default("startup-hub"),
|
|
424
|
+
max_candidates: z.number().default(10),
|
|
425
|
+
}),
|
|
426
|
+
}, async ({ demand_text, scope_tags, scene_context, max_candidates }) => {
|
|
427
|
+
try {
|
|
428
|
+
requireAuth();
|
|
429
|
+
const requester = requireAgentId();
|
|
430
|
+
const client = getClient();
|
|
431
|
+
const result = await client.discover({
|
|
432
|
+
requester,
|
|
433
|
+
demandText: demand_text,
|
|
434
|
+
scopeTags: scope_tags,
|
|
435
|
+
sceneContext: scene_context,
|
|
436
|
+
maxCandidates: max_candidates,
|
|
437
|
+
});
|
|
438
|
+
return ok(toJson(result));
|
|
439
|
+
}
|
|
440
|
+
catch (e) {
|
|
441
|
+
return handleToolError(e);
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
// 11. towow_invite
|
|
445
|
+
server.registerTool("towow_invite", {
|
|
446
|
+
description: "Create an invitation from a nomination using the selected agent as inviter.",
|
|
447
|
+
inputSchema: z.object({
|
|
448
|
+
nomination_id: z.string(),
|
|
449
|
+
expires_in_hours: z.number().default(24),
|
|
450
|
+
}),
|
|
451
|
+
}, async ({ nomination_id, expires_in_hours }) => {
|
|
452
|
+
try {
|
|
453
|
+
requireAuth();
|
|
454
|
+
const inviter = requireAgentId();
|
|
455
|
+
const client = getClient();
|
|
456
|
+
const invitation = await client.createInvitation({
|
|
457
|
+
inviter,
|
|
458
|
+
nominationId: nomination_id,
|
|
459
|
+
expiresInHours: expires_in_hours,
|
|
460
|
+
});
|
|
461
|
+
return ok(toJson(invitation));
|
|
462
|
+
}
|
|
463
|
+
catch (e) {
|
|
464
|
+
return handleToolError(e);
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
// 12. towow_invitations
|
|
468
|
+
server.registerTool("towow_invitations", {
|
|
469
|
+
description: "List invitations visible to the selected agent or an explicitly supplied owned agent.",
|
|
470
|
+
inputSchema: z.object({
|
|
471
|
+
agent_id: z.string().optional(),
|
|
472
|
+
}),
|
|
473
|
+
}, async ({ agent_id }) => {
|
|
474
|
+
try {
|
|
475
|
+
requireAuth();
|
|
476
|
+
const currentAgentId = agent_id ?? requireAgentId();
|
|
477
|
+
const client = getClient();
|
|
478
|
+
const result = await client.listInvitations({ agentId: currentAgentId });
|
|
479
|
+
return ok(toJson(result));
|
|
480
|
+
}
|
|
481
|
+
catch (e) {
|
|
482
|
+
return handleToolError(e);
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
// 13. towow_invitation_respond
|
|
486
|
+
server.registerTool("towow_invitation_respond", {
|
|
487
|
+
description: "Accept or decline an invitation as the selected agent.",
|
|
488
|
+
inputSchema: z.object({
|
|
489
|
+
invitation_id: z.string(),
|
|
490
|
+
action: z.string(),
|
|
491
|
+
}),
|
|
492
|
+
}, async ({ invitation_id, action }) => {
|
|
493
|
+
try {
|
|
494
|
+
requireAuth();
|
|
495
|
+
const agentId = requireAgentId();
|
|
496
|
+
const client = getClient();
|
|
497
|
+
const invitation = await client.respondInvitation(invitation_id, {
|
|
498
|
+
agentId,
|
|
499
|
+
action,
|
|
500
|
+
});
|
|
501
|
+
return ok(toJson(invitation));
|
|
502
|
+
}
|
|
503
|
+
catch (e) {
|
|
504
|
+
return handleToolError(e);
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
// 14. towow_negotiate
|
|
508
|
+
server.registerTool("towow_negotiate", {
|
|
509
|
+
description: "Create a canonical protocol run from already-accepted invitations.",
|
|
510
|
+
inputSchema: z.object({
|
|
511
|
+
invitation_ids: z.array(z.string()),
|
|
512
|
+
max_rounds: z.number().default(4),
|
|
513
|
+
}),
|
|
514
|
+
}, async ({ invitation_ids, max_rounds }) => {
|
|
515
|
+
try {
|
|
516
|
+
requireAuth();
|
|
517
|
+
const initiator = requireAgentId();
|
|
518
|
+
const client = getClient();
|
|
519
|
+
const result = await client.createRun({
|
|
520
|
+
initiator,
|
|
521
|
+
invitationIds: invitation_ids,
|
|
522
|
+
maxRounds: max_rounds,
|
|
523
|
+
});
|
|
524
|
+
return ok(toJson(result));
|
|
525
|
+
}
|
|
526
|
+
catch (e) {
|
|
527
|
+
return handleToolError(e);
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
// 15. towow_run_status
|
|
531
|
+
server.registerTool("towow_run_status", {
|
|
532
|
+
description: "Fetch the current canonical run status.",
|
|
533
|
+
inputSchema: z.object({
|
|
534
|
+
run_id: z.string(),
|
|
535
|
+
}),
|
|
536
|
+
}, async ({ run_id }) => {
|
|
537
|
+
try {
|
|
538
|
+
requireAuth();
|
|
539
|
+
const client = getClient();
|
|
540
|
+
const data = await client.getRun(run_id);
|
|
541
|
+
return ok(toJson(data));
|
|
542
|
+
}
|
|
543
|
+
catch (e) {
|
|
544
|
+
return handleToolError(e);
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
// 16. towow_run_prompt
|
|
548
|
+
server.registerTool("towow_run_prompt", {
|
|
549
|
+
description: "Fetch the current round prompt for the selected agent.",
|
|
550
|
+
inputSchema: z.object({
|
|
551
|
+
run_id: z.string(),
|
|
552
|
+
}),
|
|
553
|
+
}, async ({ run_id }) => {
|
|
554
|
+
try {
|
|
555
|
+
requireAuth();
|
|
556
|
+
const agentId = requireAgentId();
|
|
557
|
+
const client = getClient();
|
|
558
|
+
const data = await client.getRunPrompt(run_id, agentId);
|
|
559
|
+
return ok(toJson(data));
|
|
560
|
+
}
|
|
561
|
+
catch (e) {
|
|
562
|
+
return handleToolError(e);
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
// 17. towow_run_respond
|
|
566
|
+
server.registerTool("towow_run_respond", {
|
|
567
|
+
description: "Submit a participant response for the selected agent.",
|
|
568
|
+
inputSchema: z.object({
|
|
569
|
+
run_id: z.string(),
|
|
570
|
+
round_number: z.number(),
|
|
571
|
+
content: z.string(),
|
|
572
|
+
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
573
|
+
}),
|
|
574
|
+
}, async ({ run_id, round_number, content, metadata }) => {
|
|
575
|
+
try {
|
|
576
|
+
requireAuth();
|
|
577
|
+
const agentId = requireAgentId();
|
|
578
|
+
const client = getClient();
|
|
579
|
+
const data = await client.respondRun(run_id, {
|
|
580
|
+
agentId,
|
|
581
|
+
roundNumber: round_number,
|
|
582
|
+
content,
|
|
583
|
+
metadata,
|
|
584
|
+
});
|
|
585
|
+
return ok(toJson(data));
|
|
586
|
+
}
|
|
587
|
+
catch (e) {
|
|
588
|
+
return handleToolError(e);
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
// 18. towow_result
|
|
592
|
+
server.registerTool("towow_result", {
|
|
593
|
+
description: "Fetch the final result view for the selected agent.",
|
|
594
|
+
inputSchema: z.object({
|
|
595
|
+
run_id: z.string(),
|
|
596
|
+
}),
|
|
597
|
+
}, async ({ run_id }) => {
|
|
598
|
+
try {
|
|
599
|
+
requireAuth();
|
|
600
|
+
const agentId = requireAgentId();
|
|
601
|
+
const client = getClient();
|
|
602
|
+
const data = await client.getRunResult(run_id, agentId);
|
|
603
|
+
return ok(toJson(data));
|
|
604
|
+
}
|
|
605
|
+
catch (e) {
|
|
606
|
+
return handleToolError(e);
|
|
607
|
+
}
|
|
608
|
+
});
|
|
609
|
+
// 19. towow_feedback
|
|
610
|
+
server.registerTool("towow_feedback", {
|
|
611
|
+
description: "Submit canonical feedback for the selected agent.",
|
|
612
|
+
inputSchema: z.object({
|
|
613
|
+
target_type: z.string(),
|
|
614
|
+
target_id: z.string(),
|
|
615
|
+
exposure_event_id: z.string(),
|
|
616
|
+
rating: z.number(),
|
|
617
|
+
comment: z.string().optional(),
|
|
618
|
+
}),
|
|
619
|
+
}, async ({ target_type, target_id, exposure_event_id, rating, comment }) => {
|
|
620
|
+
try {
|
|
621
|
+
requireAuth();
|
|
622
|
+
const agentId = requireAgentId();
|
|
623
|
+
const client = getClient();
|
|
624
|
+
const data = await client.submitFeedback({
|
|
625
|
+
agentId,
|
|
626
|
+
targetType: target_type,
|
|
627
|
+
targetId: target_id,
|
|
628
|
+
exposureEventId: exposure_event_id,
|
|
629
|
+
rating,
|
|
630
|
+
comment,
|
|
631
|
+
});
|
|
632
|
+
return ok(toJson(data));
|
|
633
|
+
}
|
|
634
|
+
catch (e) {
|
|
635
|
+
return handleToolError(e);
|
|
636
|
+
}
|
|
637
|
+
});
|
|
638
|
+
// 20. towow_usage
|
|
639
|
+
server.registerTool("towow_usage", {
|
|
640
|
+
description: "Fetch usage stats for the selected agent and owning account.",
|
|
641
|
+
inputSchema: z.object({
|
|
642
|
+
period: z.string().default("month"),
|
|
643
|
+
}),
|
|
644
|
+
}, async ({ period }) => {
|
|
645
|
+
try {
|
|
646
|
+
requireAuth();
|
|
647
|
+
const agentId = requireAgentId();
|
|
648
|
+
const client = getClient();
|
|
649
|
+
const data = await client.getUsage({ period, agentId });
|
|
650
|
+
return ok(toJson(data));
|
|
651
|
+
}
|
|
652
|
+
catch (e) {
|
|
653
|
+
return handleToolError(e);
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
// 21. towow_update_profile
|
|
657
|
+
server.registerTool("towow_update_profile", {
|
|
658
|
+
description: "Update the selected agent. Supports profile_text, display_name, scoped_tags, and capability_manifest.",
|
|
659
|
+
inputSchema: z.object({
|
|
660
|
+
profile_text: z.string().optional(),
|
|
661
|
+
display_name: z.string().optional(),
|
|
662
|
+
scoped_tags: z.array(z.string()).optional(),
|
|
663
|
+
capability_manifest: z.record(z.string(), z.unknown()).optional(),
|
|
664
|
+
}),
|
|
665
|
+
}, async ({ profile_text, display_name, scoped_tags, capability_manifest }) => {
|
|
666
|
+
try {
|
|
667
|
+
requireAuth();
|
|
668
|
+
const agentId = requireAgentId();
|
|
669
|
+
const client = getClient();
|
|
670
|
+
const data = await client.updateAgent(agentId, {
|
|
671
|
+
displayName: display_name,
|
|
672
|
+
profileText: profile_text,
|
|
673
|
+
scopedTags: scoped_tags,
|
|
674
|
+
capabilityManifest: capability_manifest,
|
|
675
|
+
});
|
|
676
|
+
if (agentId === getAgentId()) {
|
|
677
|
+
saveAgent(agentId, data.display_name);
|
|
678
|
+
}
|
|
679
|
+
return ok(toJson({
|
|
680
|
+
agent: pickAgentPayload(data),
|
|
681
|
+
selected_agent: selectedAgentPayload(),
|
|
682
|
+
}));
|
|
683
|
+
}
|
|
684
|
+
catch (e) {
|
|
685
|
+
return handleToolError(e);
|
|
686
|
+
}
|
|
687
|
+
});
|
|
688
|
+
// 22. towow_agent_delete
|
|
689
|
+
server.registerTool("towow_agent_delete", {
|
|
690
|
+
description: "Delete an agent owned by the current account.",
|
|
691
|
+
inputSchema: z.object({
|
|
692
|
+
agent_id: z.string(),
|
|
693
|
+
}),
|
|
694
|
+
}, async ({ agent_id }) => {
|
|
695
|
+
try {
|
|
696
|
+
requireAuth();
|
|
697
|
+
const client = getClient();
|
|
698
|
+
await client.deleteAgent(agent_id);
|
|
699
|
+
if (agent_id === getAgentId()) {
|
|
700
|
+
clearAgent();
|
|
701
|
+
}
|
|
702
|
+
return ok(toJson({ ok: true, deleted_agent_id: agent_id }));
|
|
703
|
+
}
|
|
704
|
+
catch (e) {
|
|
705
|
+
return handleToolError(e);
|
|
706
|
+
}
|
|
707
|
+
});
|
|
708
|
+
// 23. towow_refresh_token
|
|
709
|
+
server.registerTool("towow_refresh_token", {
|
|
710
|
+
description: "Refresh the session token before it expires.",
|
|
711
|
+
inputSchema: z.object({}),
|
|
712
|
+
}, async () => {
|
|
713
|
+
try {
|
|
714
|
+
requireAuth();
|
|
715
|
+
const client = getClient();
|
|
716
|
+
const result = await client.refreshToken();
|
|
717
|
+
const newToken = result.session_token;
|
|
718
|
+
if (newToken)
|
|
719
|
+
saveSessionToken(newToken);
|
|
720
|
+
return ok(toJson({ ok: true, expires_at: result.expires_at }));
|
|
721
|
+
}
|
|
722
|
+
catch (e) {
|
|
723
|
+
return handleToolError(e);
|
|
724
|
+
}
|
|
725
|
+
});
|
|
726
|
+
// 24. towow_byok_bind
|
|
727
|
+
server.registerTool("towow_byok_bind", {
|
|
728
|
+
description: "Bind your own LLM API key for formulation and discovery. Required for full functionality.",
|
|
729
|
+
inputSchema: z.object({
|
|
730
|
+
api_key: z.string(),
|
|
731
|
+
provider: z.string().default("anthropic"),
|
|
732
|
+
}),
|
|
733
|
+
}, async ({ api_key, provider }) => {
|
|
734
|
+
try {
|
|
735
|
+
requireAuth();
|
|
736
|
+
const client = getClient();
|
|
737
|
+
const result = await client.bindByok(api_key, provider);
|
|
738
|
+
return ok(toJson(result));
|
|
739
|
+
}
|
|
740
|
+
catch (e) {
|
|
741
|
+
return handleToolError(e);
|
|
742
|
+
}
|
|
743
|
+
});
|
|
744
|
+
// 25. towow_byok_status
|
|
745
|
+
server.registerTool("towow_byok_status", {
|
|
746
|
+
description: "Check if a BYOK (Bring Your Own Key) session is active.",
|
|
747
|
+
inputSchema: z.object({}),
|
|
748
|
+
}, async () => {
|
|
749
|
+
try {
|
|
750
|
+
requireAuth();
|
|
751
|
+
const client = getClient();
|
|
752
|
+
const result = await client.getByok();
|
|
753
|
+
return ok(toJson(result));
|
|
754
|
+
}
|
|
755
|
+
catch (e) {
|
|
756
|
+
return handleToolError(e);
|
|
757
|
+
}
|
|
758
|
+
});
|
|
759
|
+
// 26. towow_byok_clear
|
|
760
|
+
server.registerTool("towow_byok_clear", {
|
|
761
|
+
description: "Remove the BYOK API key binding.",
|
|
762
|
+
inputSchema: z.object({}),
|
|
763
|
+
}, async () => {
|
|
764
|
+
try {
|
|
765
|
+
requireAuth();
|
|
766
|
+
const client = getClient();
|
|
767
|
+
const result = await client.clearByok();
|
|
768
|
+
return ok(toJson(result));
|
|
769
|
+
}
|
|
770
|
+
catch (e) {
|
|
771
|
+
return handleToolError(e);
|
|
772
|
+
}
|
|
773
|
+
});
|
|
774
|
+
// 27. towow_get_formulation
|
|
775
|
+
server.registerTool("towow_get_formulation", {
|
|
776
|
+
description: "Get the current state of a formulation session without sending a message.",
|
|
777
|
+
inputSchema: z.object({
|
|
778
|
+
session_id: z.string(),
|
|
779
|
+
}),
|
|
780
|
+
}, async ({ session_id }) => {
|
|
781
|
+
try {
|
|
782
|
+
requireAuth();
|
|
783
|
+
const client = getClient();
|
|
784
|
+
const data = await client.getFormulation(session_id);
|
|
785
|
+
return ok(toJson(data));
|
|
786
|
+
}
|
|
787
|
+
catch (e) {
|
|
788
|
+
return handleToolError(e);
|
|
789
|
+
}
|
|
790
|
+
});
|
|
791
|
+
// 28. towow_get_invitation
|
|
792
|
+
server.registerTool("towow_get_invitation", {
|
|
793
|
+
description: "Get details of a specific invitation.",
|
|
794
|
+
inputSchema: z.object({
|
|
795
|
+
invitation_id: z.string(),
|
|
796
|
+
}),
|
|
797
|
+
}, async ({ invitation_id }) => {
|
|
798
|
+
try {
|
|
799
|
+
requireAuth();
|
|
800
|
+
const client = getClient();
|
|
801
|
+
const data = await client.getInvitation(invitation_id);
|
|
802
|
+
return ok(toJson(data));
|
|
803
|
+
}
|
|
804
|
+
catch (e) {
|
|
805
|
+
return handleToolError(e);
|
|
806
|
+
}
|
|
807
|
+
});
|
|
808
|
+
// 29. towow_get_discovery
|
|
809
|
+
server.registerTool("towow_get_discovery", {
|
|
810
|
+
description: "Retrieve results from a previously completed discovery query.",
|
|
811
|
+
inputSchema: z.object({
|
|
812
|
+
query_id: z.string(),
|
|
813
|
+
}),
|
|
814
|
+
}, async ({ query_id }) => {
|
|
815
|
+
try {
|
|
816
|
+
requireAuth();
|
|
817
|
+
const client = getClient();
|
|
818
|
+
const data = await client.getDiscoveryQuery(query_id);
|
|
819
|
+
return ok(toJson(data));
|
|
820
|
+
}
|
|
821
|
+
catch (e) {
|
|
822
|
+
return handleToolError(e);
|
|
823
|
+
}
|
|
824
|
+
});
|
|
825
|
+
// 30. towow_demand_confirm (deprecated alias)
|
|
826
|
+
server.registerTool("towow_demand_confirm", {
|
|
827
|
+
description: "Deprecated alias for `towow_formulation_confirm`. Prefer the canonical tool name.",
|
|
828
|
+
inputSchema: z.object({
|
|
829
|
+
session_id: z.string(),
|
|
830
|
+
scope_tags: z.array(z.string()).optional(),
|
|
831
|
+
max_candidates: z.number().default(10),
|
|
832
|
+
}),
|
|
833
|
+
}, async ({ session_id, scope_tags, max_candidates }) => {
|
|
834
|
+
try {
|
|
835
|
+
const payload = await canonicalFormulationConfirm({
|
|
836
|
+
sessionId: session_id,
|
|
837
|
+
scopeTags: scope_tags,
|
|
838
|
+
maxCandidates: max_candidates,
|
|
839
|
+
});
|
|
840
|
+
payload.deprecated_tool = "towow_demand_confirm";
|
|
841
|
+
payload.replacement = "towow_formulation_confirm";
|
|
842
|
+
return ok(toJson(payload));
|
|
843
|
+
}
|
|
844
|
+
catch (e) {
|
|
845
|
+
return handleToolError(e);
|
|
846
|
+
}
|
|
847
|
+
});
|
|
848
|
+
// 31. towow_run_start (deprecated alias)
|
|
849
|
+
server.registerTool("towow_run_start", {
|
|
850
|
+
description: "Deprecated alias for `towow_negotiate`. Prefer the canonical tool name.",
|
|
851
|
+
inputSchema: z.object({
|
|
852
|
+
invitation_ids: z.array(z.string()),
|
|
853
|
+
max_rounds: z.number().default(4),
|
|
854
|
+
}),
|
|
855
|
+
}, async ({ invitation_ids, max_rounds }) => {
|
|
856
|
+
try {
|
|
857
|
+
requireAuth();
|
|
858
|
+
const initiator = requireAgentId();
|
|
859
|
+
const client = getClient();
|
|
860
|
+
const data = await client.createRun({
|
|
861
|
+
initiator,
|
|
862
|
+
invitationIds: invitation_ids,
|
|
863
|
+
maxRounds: max_rounds,
|
|
864
|
+
});
|
|
865
|
+
data.deprecated_tool = "towow_run_start";
|
|
866
|
+
data.replacement = "towow_negotiate";
|
|
867
|
+
return ok(toJson(data));
|
|
868
|
+
}
|
|
869
|
+
catch (e) {
|
|
870
|
+
return handleToolError(e);
|
|
871
|
+
}
|
|
872
|
+
});
|
|
873
|
+
// 32. towow_run_result (deprecated alias)
|
|
874
|
+
server.registerTool("towow_run_result", {
|
|
875
|
+
description: "Deprecated alias for `towow_result`. Prefer the canonical tool name.",
|
|
876
|
+
inputSchema: z.object({
|
|
877
|
+
run_id: z.string(),
|
|
878
|
+
}),
|
|
879
|
+
}, async ({ run_id }) => {
|
|
880
|
+
try {
|
|
881
|
+
requireAuth();
|
|
882
|
+
const agentId = requireAgentId();
|
|
883
|
+
const client = getClient();
|
|
884
|
+
const data = await client.getRunResult(run_id, agentId);
|
|
885
|
+
data.deprecated_tool = "towow_run_result";
|
|
886
|
+
data.replacement = "towow_result";
|
|
887
|
+
return ok(toJson(data));
|
|
888
|
+
}
|
|
889
|
+
catch (e) {
|
|
890
|
+
return handleToolError(e);
|
|
891
|
+
}
|
|
892
|
+
});
|
|
893
|
+
// 33. towow_logout
|
|
894
|
+
server.registerTool("towow_logout", {
|
|
895
|
+
description: "Clear the local MCP session context after best-effort remote logout.",
|
|
896
|
+
inputSchema: z.object({}),
|
|
897
|
+
}, async () => {
|
|
898
|
+
const token = getSessionToken();
|
|
899
|
+
if (token) {
|
|
900
|
+
try {
|
|
901
|
+
const client = getClient();
|
|
902
|
+
await client.logout();
|
|
903
|
+
}
|
|
904
|
+
catch {
|
|
905
|
+
// Ignore remote logout failure (best-effort)
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
clearSessionToken();
|
|
909
|
+
clearAgent();
|
|
910
|
+
return ok(toJson({ ok: true }));
|
|
911
|
+
});
|
|
912
|
+
// ---------------------------------------------------------------------------
|
|
913
|
+
// Main
|
|
914
|
+
// ---------------------------------------------------------------------------
|
|
915
|
+
async function main() {
|
|
916
|
+
const transport = new StdioServerTransport();
|
|
917
|
+
await server.connect(transport);
|
|
918
|
+
}
|
|
919
|
+
main().catch((e) => {
|
|
920
|
+
process.stderr.write(`Fatal: ${e}\n`);
|
|
921
|
+
process.exit(1);
|
|
922
|
+
});
|
|
923
|
+
//# sourceMappingURL=index.js.map
|