verifiedworkflows-mcp-server 0.1.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 +44 -0
- package/index.js +245 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# @verifiedworkflows/mcp-server
|
|
2
|
+
|
|
3
|
+
MCP server for Verified Workflows — human-in-the-loop review as AI agent tools.
|
|
4
|
+
|
|
5
|
+
## Quick start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
export VW_API_KEY="vw_live_xxx"
|
|
9
|
+
npx @verifiedworkflows/mcp-server
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Claude Desktop
|
|
13
|
+
|
|
14
|
+
Add to `~/.claude/claude_desktop_config.json`:
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"mcpServers": {
|
|
19
|
+
"verifiedworkflows": {
|
|
20
|
+
"command": "npx",
|
|
21
|
+
"args": ["-y", "@verifiedworkflows/mcp-server"],
|
|
22
|
+
"env": {
|
|
23
|
+
"VW_API_KEY": "vw_live_xxx"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Cursor / Windsurf / Continue
|
|
31
|
+
|
|
32
|
+
Same config pattern — add the `verifiedworkflows` server to your MCP settings.
|
|
33
|
+
|
|
34
|
+
## Tools
|
|
35
|
+
|
|
36
|
+
| Tool | Description |
|
|
37
|
+
|------|-------------|
|
|
38
|
+
| `submit_review` | Submit AI output for certified human review |
|
|
39
|
+
| `get_review_status` | Check task status |
|
|
40
|
+
| `get_review_result` | Get verified result with corrections and HMAC signature |
|
|
41
|
+
|
|
42
|
+
## License
|
|
43
|
+
|
|
44
|
+
MIT
|
package/index.js
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Verified Workflows MCP Server
|
|
5
|
+
* ==============================
|
|
6
|
+
* Exposes human-in-the-loop review as MCP tools.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* export VW_API_KEY="vw_live_xxx"
|
|
10
|
+
* npx @verifiedworkflows/mcp-server
|
|
11
|
+
*
|
|
12
|
+
* Or in Claude Desktop config:
|
|
13
|
+
* "mcpServers": {
|
|
14
|
+
* "verifiedworkflows": {
|
|
15
|
+
* "command": "npx",
|
|
16
|
+
* "args": ["-y", "@verifiedworkflows/mcp-server"],
|
|
17
|
+
* "env": { "VW_API_KEY": "vw_live_xxx" }
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const API_KEY = process.env.VW_API_KEY || "";
|
|
23
|
+
const BASE_URL = process.env.VW_BASE_URL || "https://api.verifiedworkflows.com";
|
|
24
|
+
|
|
25
|
+
const TOOLS = [
|
|
26
|
+
{
|
|
27
|
+
name: "submit_review",
|
|
28
|
+
description:
|
|
29
|
+
"Submit AI-generated content for certified human review. " +
|
|
30
|
+
"Use this BEFORE sending AI output to users. Returns a task ID — " +
|
|
31
|
+
"the verified result arrives via webhook (typically <24h).",
|
|
32
|
+
inputSchema: {
|
|
33
|
+
type: "object",
|
|
34
|
+
required: ["content"],
|
|
35
|
+
properties: {
|
|
36
|
+
content: { type: "string", description: "The AI-generated content to review" },
|
|
37
|
+
review_type: {
|
|
38
|
+
type: "string",
|
|
39
|
+
enum: ["text-qa", "medical", "code-review", "translation", "fact-check", "decision-approval", "audio-audit", "data-extraction"],
|
|
40
|
+
default: "text-qa",
|
|
41
|
+
description: "Type of review needed",
|
|
42
|
+
},
|
|
43
|
+
priority: {
|
|
44
|
+
type: "string",
|
|
45
|
+
enum: ["standard", "express"],
|
|
46
|
+
default: "standard",
|
|
47
|
+
description: "Standard (24h) or Express (1-4h, 3x cost)",
|
|
48
|
+
},
|
|
49
|
+
instructions: { type: "string", default: "", description: "What the reviewer should check" },
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: "get_review_status",
|
|
55
|
+
description: "Check the status of a submitted review task.",
|
|
56
|
+
inputSchema: {
|
|
57
|
+
type: "object",
|
|
58
|
+
required: ["task_id"],
|
|
59
|
+
properties: {
|
|
60
|
+
task_id: { type: "string", description: "Task ID (e.g. tsk_live_f8a29)" },
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: "get_review_result",
|
|
66
|
+
description:
|
|
67
|
+
"Get the verified result of a completed review, including corrections, " +
|
|
68
|
+
"reviewer info, and HMAC signature. Only returns data if completed.",
|
|
69
|
+
inputSchema: {
|
|
70
|
+
type: "object",
|
|
71
|
+
required: ["task_id"],
|
|
72
|
+
properties: {
|
|
73
|
+
task_id: { type: "string", description: "Task ID (e.g. tsk_live_f8a29)" },
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
async function apiCall(method, path, body) {
|
|
80
|
+
const opts = {
|
|
81
|
+
method,
|
|
82
|
+
headers: {
|
|
83
|
+
Authorization: `Bearer ${API_KEY}`,
|
|
84
|
+
"Content-Type": "application/json",
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
if (body) opts.body = JSON.stringify(body);
|
|
88
|
+
|
|
89
|
+
const resp = await fetch(`${BASE_URL}${path}`, opts);
|
|
90
|
+
const text = await resp.text();
|
|
91
|
+
|
|
92
|
+
if (!resp.ok) {
|
|
93
|
+
throw new Error(`API error ${resp.status}: ${text}`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return text ? JSON.parse(text) : {};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function handleTool(name, args) {
|
|
100
|
+
if (!API_KEY) {
|
|
101
|
+
return {
|
|
102
|
+
content: [{
|
|
103
|
+
type: "text",
|
|
104
|
+
text: "Error: VW_API_KEY not set. Get your key at https://verifiedworkflows.com and set: export VW_API_KEY=vw_live_xxx",
|
|
105
|
+
}],
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
if (name === "submit_review") {
|
|
111
|
+
const { content, review_type = "text-qa", priority = "standard", instructions = "" } = args;
|
|
112
|
+
const result = await apiCall("POST", "/v1/tasks", {
|
|
113
|
+
type: review_type,
|
|
114
|
+
priority,
|
|
115
|
+
title: content.length > 60 ? content.slice(0, 60) + "..." : content,
|
|
116
|
+
instructions: instructions || `Review this ${review_type} output for accuracy and safety.`,
|
|
117
|
+
payload: { content },
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
content: [{
|
|
122
|
+
type: "text",
|
|
123
|
+
text: JSON.stringify({
|
|
124
|
+
task_id: result.id,
|
|
125
|
+
status: result.status || "pending",
|
|
126
|
+
estimated_turnaround: priority === "express" ? "1-4h" : "24h",
|
|
127
|
+
message: `Submitted for review. Task ID: ${result.id}.`,
|
|
128
|
+
}, null, 2),
|
|
129
|
+
}],
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (name === "get_review_status") {
|
|
134
|
+
const result = await apiCall("GET", `/v1/tasks/${args.task_id}`);
|
|
135
|
+
return {
|
|
136
|
+
content: [{
|
|
137
|
+
type: "text",
|
|
138
|
+
text: JSON.stringify({
|
|
139
|
+
task_id: result.id,
|
|
140
|
+
status: result.status,
|
|
141
|
+
age_sec: result.age_sec,
|
|
142
|
+
type: result.type,
|
|
143
|
+
has_result: result.result !== null,
|
|
144
|
+
}, null, 2),
|
|
145
|
+
}],
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (name === "get_review_result") {
|
|
150
|
+
const result = await apiCall("GET", `/v1/tasks/${args.task_id}`);
|
|
151
|
+
|
|
152
|
+
if (result.status !== "completed") {
|
|
153
|
+
return {
|
|
154
|
+
content: [{
|
|
155
|
+
type: "text",
|
|
156
|
+
text: `Task ${args.task_id} status: ${result.status}. Not completed yet.`,
|
|
157
|
+
}],
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const review = result.result || {};
|
|
162
|
+
return {
|
|
163
|
+
content: [{
|
|
164
|
+
type: "text",
|
|
165
|
+
text: JSON.stringify({
|
|
166
|
+
task_id: result.id,
|
|
167
|
+
verified_body: review.body,
|
|
168
|
+
corrections: review.corrections || [],
|
|
169
|
+
reviewer_id: review.reviewer_id,
|
|
170
|
+
hmac_signature: review.hmac_signature,
|
|
171
|
+
}, null, 2),
|
|
172
|
+
}],
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return { content: [{ type: "text", text: `Unknown tool: ${name}` }] };
|
|
177
|
+
} catch (e) {
|
|
178
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async function main() {
|
|
183
|
+
process.stderr.write(
|
|
184
|
+
API_KEY
|
|
185
|
+
? "[verifiedworkflows] MCP server started\n"
|
|
186
|
+
: "[verifiedworkflows] WARNING: VW_API_KEY not set\n"
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
process.stdin.setEncoding("utf8");
|
|
190
|
+
|
|
191
|
+
let buffer = "";
|
|
192
|
+
|
|
193
|
+
process.stdin.on("data", (chunk) => {
|
|
194
|
+
buffer += chunk;
|
|
195
|
+
|
|
196
|
+
let newlineIdx;
|
|
197
|
+
while ((newlineIdx = buffer.indexOf("\n")) !== -1) {
|
|
198
|
+
const line = buffer.slice(0, newlineIdx);
|
|
199
|
+
buffer = buffer.slice(newlineIdx + 1);
|
|
200
|
+
|
|
201
|
+
if (!line.trim()) continue;
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
const msg = JSON.parse(line);
|
|
205
|
+
handleLine(msg);
|
|
206
|
+
} catch (e) {
|
|
207
|
+
// ignore parse errors
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
process.stdin.on("end", () => {
|
|
213
|
+
process.exit(0);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
async function handleLine(msg) {
|
|
217
|
+
const { id, method, params } = msg;
|
|
218
|
+
|
|
219
|
+
if (method === "initialize") {
|
|
220
|
+
send(id, {
|
|
221
|
+
protocolVersion: "2024-11-05",
|
|
222
|
+
capabilities: { tools: {} },
|
|
223
|
+
serverInfo: { name: "verifiedworkflows", version: "0.1.0" },
|
|
224
|
+
});
|
|
225
|
+
} else if (method === "notifications/initialized") {
|
|
226
|
+
// no response needed
|
|
227
|
+
} else if (method === "tools/list") {
|
|
228
|
+
send(id, { tools: TOOLS });
|
|
229
|
+
} else if (method === "tools/call") {
|
|
230
|
+
const result = await handleTool(params.name, params.arguments || {});
|
|
231
|
+
send(id, { content: result.content, isError: false });
|
|
232
|
+
} else if (method === "ping") {
|
|
233
|
+
send(id, {});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function send(id, result) {
|
|
238
|
+
process.stdout.write(JSON.stringify({ jsonrpc: "2.0", id, result }) + "\n");
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
main().catch((e) => {
|
|
243
|
+
process.stderr.write(`[verifiedworkflows] Fatal: ${e.message}\n`);
|
|
244
|
+
process.exit(1);
|
|
245
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "verifiedworkflows-mcp-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for Verified Workflows — human-in-the-loop review as AI agent tools",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://verifiedworkflows.com",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/verifiedworkflows/mcp-server.git"
|
|
10
|
+
},
|
|
11
|
+
"bin": {
|
|
12
|
+
"verifiedworkflows-mcp": "index.js",
|
|
13
|
+
"vw-mcp": "index.js"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"index.js",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=18"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"mcp",
|
|
24
|
+
"model-context-protocol",
|
|
25
|
+
"human-in-the-loop",
|
|
26
|
+
"ai-review",
|
|
27
|
+
"claude",
|
|
28
|
+
"cursor",
|
|
29
|
+
"windsurf"
|
|
30
|
+
],
|
|
31
|
+
"author": {
|
|
32
|
+
"name": "Verified Workflows",
|
|
33
|
+
"email": "hello@verifiedworkflows.com"
|
|
34
|
+
}
|
|
35
|
+
}
|