@runhuman/mcp-server 1.0.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/.env.example ADDED
@@ -0,0 +1,12 @@
1
+ # RunHuman API Configuration
2
+
3
+ # API Base URL
4
+ # For local development, use http://localhost:3000
5
+ # For production, use your deployed API URL
6
+ RUNHUMAN_API_URL=http://localhost:3000
7
+
8
+ # API Key
9
+ # Get this from the API dashboard at http://localhost:3000/app.html
10
+ # Or use the default test key below for local development
11
+ # Format: qa_live_xxxxxxxxxxxxxxxxxxxxx
12
+ RUNHUMAN_API_KEY=qa_live_test_key_for_demo_purposes_only_12345
package/README.md ADDED
@@ -0,0 +1,206 @@
1
+ # RunHuman MCP Server
2
+
3
+ A Model Context Protocol (MCP) server that allows AI agents to interact with the RunHuman QA testing service.
4
+
5
+ ## Overview
6
+
7
+ This MCP server provides tools for creating and managing human QA jobs through the RunHuman API. AI agents can use this server to:
8
+
9
+ - Create new QA jobs with custom schemas
10
+ - Check the status of running jobs
11
+ - Retrieve completed job results
12
+
13
+ ## Installation
14
+
15
+ ### For Claude Desktop (Recommended)
16
+
17
+ 1. Get your API key at: https://qa-experiment.fly.dev/app.html
18
+
19
+ 2. Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on Mac):
20
+
21
+ ```json
22
+ {
23
+ "mcpServers": {
24
+ "runhuman": {
25
+ "command": "npx",
26
+ "args": ["-y", "@runhuman/mcp-server", "--api-key=qa_live_xxxxxxxxxxxxx"]
27
+ }
28
+ }
29
+ }
30
+ ```
31
+
32
+ 3. Restart Claude Desktop
33
+
34
+ That's it! The server will be automatically downloaded and run by Claude.
35
+
36
+ ### For Development
37
+
38
+ From the monorepo root:
39
+
40
+ ```bash
41
+ npm install
42
+ npm run build --workspace=@runhuman/mcp-server
43
+
44
+ # Run with API key
45
+ node packages/mcp-server/dist/index.js --api-key=qa_live_xxxxx
46
+ ```
47
+
48
+ ### Available Tools
49
+
50
+ #### `create_job`
51
+ Create a new QA job with human testers.
52
+
53
+ **Parameters:**
54
+ - `url` (string): The URL to test
55
+ - `description` (string): Instructions for the human tester describing what to test
56
+ - `schema` (object): Expected result schema that the tester response will be extracted into
57
+
58
+ #### `get_job_status`
59
+ Get the current status of a QA job.
60
+
61
+ **Parameters:**
62
+ - `jobId` (string): The ID of the job to check
63
+
64
+ #### `get_job_result`
65
+ Get the results of a completed QA job.
66
+
67
+ **Parameters:**
68
+ - `jobId` (string): The ID of the completed job
69
+
70
+ ## Configuration
71
+
72
+ The MCP server needs to be configured with your RunHuman API credentials.
73
+
74
+ ### 1. Get an API Key
75
+
76
+ **Option A: Via Dashboard**
77
+ 1. Start the API server: `npm run dev --workspace=@runhuman/api`
78
+ 2. Open http://localhost:3000/app.html
79
+ 3. Go to "API Keys" tab
80
+ 4. Click "Create API Key"
81
+ 5. Copy the key (starts with `qa_live_`)
82
+
83
+ **Option B: Use Default Test Key**
84
+ - For local development, you can use: `qa_live_test_key_123`
85
+ - This key exists in `packages/api/data/api-keys.json`
86
+
87
+ ### 2. Configure Environment Variables
88
+
89
+ Create a `.env` file in the MCP server directory:
90
+
91
+ ```bash
92
+ # For local development
93
+ RUNHUMAN_API_URL=http://localhost:3000
94
+ RUNHUMAN_API_KEY=qa_live_test_key_123
95
+
96
+ # For production
97
+ RUNHUMAN_API_URL=https://api.runhuman.com
98
+ RUNHUMAN_API_KEY=qa_live_xxxxxxxxxxxxxxxxxxxxx
99
+ ```
100
+
101
+ **Important:** Never commit `.env` files to git! They're already in `.gitignore`.
102
+
103
+ ### 3. Verify Configuration
104
+
105
+ Test your API key works:
106
+
107
+ ```bash
108
+ curl http://localhost:3000/api/jobs \
109
+ -H "Authorization: Bearer qa_live_test_key_123" \
110
+ -H "Content-Type: application/json" \
111
+ -d '{"url":"https://example.com","description":"test","outputSchema":{}}'
112
+ ```
113
+
114
+ Should return a job ID if authentication works.
115
+
116
+ For more details, see [docs/API-AUTHENTICATION.md](docs/API-AUTHENTICATION.md)
117
+
118
+ ## Testing
119
+
120
+ The MCP server includes automated tests to verify it's working correctly:
121
+
122
+ ```bash
123
+ # Build first
124
+ npm run build --workspace=@runhuman/mcp-server
125
+
126
+ # Run simple automated test
127
+ npm run test --workspace=@runhuman/mcp-server
128
+
129
+ # Or use the MCP Inspector (interactive testing)
130
+ npm run test:inspector --workspace=@runhuman/mcp-server
131
+ ```
132
+
133
+ The test script will:
134
+ 1. ✅ Initialize a connection to the MCP server
135
+ 2. ✅ List all available tools (create_job, get_job_status, get_job_result)
136
+ 3. ✅ Test calling the create_job tool
137
+
138
+ ### Expected Test Output
139
+
140
+ ```
141
+ ✅ Server initialized successfully
142
+ ✅ Tools listed: create_job, get_job_status, get_job_result
143
+ ✅ create_job tool called successfully
144
+ ```
145
+
146
+ ## Development
147
+
148
+ ```bash
149
+ # Watch mode (auto-rebuild on changes)
150
+ npm run dev --workspace=@runhuman/mcp-server
151
+
152
+ # Build
153
+ npm run build --workspace=@runhuman/mcp-server
154
+
155
+ # Test after building
156
+ npm run test --workspace=@runhuman/mcp-server
157
+ ```
158
+
159
+ ## Integration with Claude Desktop
160
+
161
+ To use this MCP server with Claude Desktop, add it to your configuration:
162
+
163
+ ```json
164
+ {
165
+ "mcpServers": {
166
+ "runhuman": {
167
+ "command": "node",
168
+ "args": ["/path/to/qa-experiment/packages/mcp-server/dist/index.js"]
169
+ }
170
+ }
171
+ }
172
+ ```
173
+
174
+ ## Example Usage
175
+
176
+ Once connected to an AI agent (like Claude), the agent can use these tools naturally:
177
+
178
+ **User:** "Can someone test my checkout page at https://myapp.com/checkout?"
179
+
180
+ **Agent uses create_job:**
181
+ ```
182
+ ✅ Job created successfully!
183
+ Job ID: job_abc123
184
+ Status: pending
185
+ ...
186
+ ```
187
+
188
+ **Agent polls get_job_status until complete, then calls get_job_result:**
189
+ ```
190
+ ✅ Test completed!
191
+ Results Summary:
192
+ - Checkout Flow: ✅ Working
193
+ - Payment Processing: ✅ Successful
194
+ ...
195
+ ```
196
+
197
+ ## Developer Documentation
198
+
199
+ For developers working on this MCP server:
200
+ - [docs/HOW-AGENTS-USE-MCP.md](docs/HOW-AGENTS-USE-MCP.md) - How AI agents discover and use MCP servers
201
+ - [docs/TOOL-RESPONSE-BEST-PRACTICES.md](docs/TOOL-RESPONSE-BEST-PRACTICES.md) - Best practices for tool responses
202
+
203
+ ## Learn More
204
+
205
+ - [Model Context Protocol Documentation](https://modelcontextprotocol.io/)
206
+ - [RunHuman API Documentation](../api/README.md)
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * RunHuman MCP Server
4
+ *
5
+ * This MCP server provides tools for AI agents to interact with the RunHuman QA testing service.
6
+ * It allows agents to:
7
+ * - Create QA jobs
8
+ * - Check job status
9
+ * - Retrieve job results
10
+ *
11
+ * @see https://modelcontextprotocol.io/
12
+ */
13
+ export {};
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;GAUG"}
package/dist/index.js ADDED
@@ -0,0 +1,424 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * RunHuman MCP Server
4
+ *
5
+ * This MCP server provides tools for AI agents to interact with the RunHuman QA testing service.
6
+ * It allows agents to:
7
+ * - Create QA jobs
8
+ * - Check job status
9
+ * - Retrieve job results
10
+ *
11
+ * @see https://modelcontextprotocol.io/
12
+ */
13
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
14
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
15
+ import { CallToolRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
16
+ import * as dotenv from 'dotenv';
17
+ // Load environment variables (optional - for standalone testing)
18
+ dotenv.config();
19
+ // Configuration
20
+ // Priority: CLI args > env vars > defaults
21
+ // Usage: node dist/index.js --api-key=qa_live_xxx [--api-url=https://...]
22
+ const args = process.argv.slice(2);
23
+ const apiKeyArg = args.find(arg => arg.startsWith('--api-key='))?.split('=')[1];
24
+ const apiUrlArg = args.find(arg => arg.startsWith('--api-url='))?.split('=')[1];
25
+ const API_URL = apiUrlArg || process.env.RUNHUMAN_API_URL || 'https://qa-experiment.fly.dev';
26
+ const API_KEY = apiKeyArg || process.env.RUNHUMAN_API_KEY;
27
+ if (!API_KEY) {
28
+ console.error('❌ Error: API key is required');
29
+ console.error('');
30
+ console.error('For Claude Desktop, add to your config:');
31
+ console.error('{');
32
+ console.error(' "mcpServers": {');
33
+ console.error(' "runhuman": {');
34
+ console.error(' "command": "npx",');
35
+ console.error(' "args": ["-y", "@runhuman/mcp-server", "--api-key=qa_live_xxxxx"]');
36
+ console.error(' }');
37
+ console.error(' }');
38
+ console.error('}');
39
+ console.error('');
40
+ console.error('Get your API key at: https://qa-experiment.fly.dev/app.html');
41
+ process.exit(1);
42
+ }
43
+ console.error(`🔗 Connected to RunHuman API at: ${API_URL}`);
44
+ console.error(`🔑 Using API key: ${API_KEY.substring(0, 12)}...`);
45
+ /**
46
+ * Create and configure the MCP server
47
+ */
48
+ const server = new Server({
49
+ name: 'runhuman-mcp-server',
50
+ version: '1.0.0',
51
+ }, {
52
+ capabilities: {
53
+ tools: {},
54
+ prompts: {},
55
+ },
56
+ });
57
+ /**
58
+ * List available prompts (documentation for the agent)
59
+ */
60
+ server.setRequestHandler(ListPromptsRequestSchema, async () => {
61
+ return {
62
+ prompts: [
63
+ {
64
+ name: 'explain_runhuman',
65
+ description: 'Get an explanation of how to use RunHuman for QA testing',
66
+ },
67
+ ],
68
+ };
69
+ });
70
+ /**
71
+ * List available tools
72
+ */
73
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
74
+ return {
75
+ tools: [
76
+ {
77
+ name: 'create_job',
78
+ description: `Create a new QA job with real human testers. This sends your test to a human who will manually test your application and describe what they find in natural language. The response is then extracted into structured data matching your schema using GPT-4o.
79
+
80
+ Use this when you need human verification of:
81
+ - UI/UX functionality that's hard to automate
82
+ - Visual issues, accessibility problems
83
+ - Complex user flows (login, checkout, forms)
84
+ - Cross-browser compatibility
85
+ - Real user experience feedback
86
+
87
+ Returns a jobId that you can use with get_job_status and get_job_result.
88
+
89
+ Example workflow:
90
+ 1. create_job → get jobId
91
+ 2. get_job_status → poll until status is "completed"
92
+ 3. get_job_result → retrieve structured results`,
93
+ inputSchema: {
94
+ type: 'object',
95
+ properties: {
96
+ url: {
97
+ type: 'string',
98
+ description: 'The URL to test (must be publicly accessible). Example: "https://myapp.com/checkout"',
99
+ },
100
+ description: {
101
+ type: 'string',
102
+ description: 'Clear instructions for the human tester. Be specific about what to test and how. Example: "Test the checkout flow: Add a product to cart, proceed to checkout, fill in shipping info, and verify the order summary shows correct totals before submitting."',
103
+ },
104
+ schema: {
105
+ type: 'object',
106
+ description: 'JSON Schema defining the structure you want extracted from the tester\'s response. Example: { "type": "object", "properties": { "checkoutWorks": { "type": "boolean" }, "totalIsCorrect": { "type": "boolean" }, "issues": { "type": "array", "items": { "type": "string" } } } }',
107
+ },
108
+ },
109
+ required: ['url', 'description', 'schema'],
110
+ },
111
+ },
112
+ {
113
+ name: 'get_job_status',
114
+ description: `Check the current status of a QA job. Jobs progress through states: pending → claimed → in_progress → completed (or failed/timeout).
115
+
116
+ Use this to poll for completion before fetching results. Typical job completion time is 2-10 minutes depending on test complexity.
117
+
118
+ Returns: { status: "pending" | "claimed" | "in_progress" | "completed" | "failed" | "timeout", message: "..." }`,
119
+ inputSchema: {
120
+ type: 'object',
121
+ properties: {
122
+ jobId: {
123
+ type: 'string',
124
+ description: 'The job ID returned from create_job. Example: "550e8400-e29b-41d4-a716-446655440000"',
125
+ },
126
+ },
127
+ required: ['jobId'],
128
+ },
129
+ },
130
+ {
131
+ name: 'get_job_result',
132
+ description: `Get the structured results of a completed QA job. Only call this after get_job_status shows status="completed".
133
+
134
+ Returns the tester's response extracted into your specified schema, plus metadata about timing and the raw tester response.
135
+
136
+ If the job isn't complete yet, returns an error. If extraction failed, includes the raw response so you can see what the tester said.`,
137
+ inputSchema: {
138
+ type: 'object',
139
+ properties: {
140
+ jobId: {
141
+ type: 'string',
142
+ description: 'The job ID of a completed job. Example: "550e8400-e29b-41d4-a716-446655440000"',
143
+ },
144
+ },
145
+ required: ['jobId'],
146
+ },
147
+ },
148
+ ],
149
+ };
150
+ });
151
+ /**
152
+ * Handle tool calls
153
+ */
154
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
155
+ const { name, arguments: args } = request.params;
156
+ if (!args) {
157
+ throw new Error('Missing arguments');
158
+ }
159
+ switch (name) {
160
+ case 'create_job':
161
+ try {
162
+ // Call RunHuman API to create job
163
+ const response = await fetch(`${API_URL}/api/jobs`, {
164
+ method: 'POST',
165
+ headers: {
166
+ 'Authorization': `Bearer ${API_KEY}`,
167
+ 'Content-Type': 'application/json'
168
+ },
169
+ body: JSON.stringify({
170
+ url: args.url,
171
+ description: args.description,
172
+ outputSchema: args.schema
173
+ })
174
+ });
175
+ if (!response.ok) {
176
+ const error = await response.json().catch(() => ({ error: response.statusText }));
177
+ return {
178
+ content: [{
179
+ type: 'text',
180
+ text: `❌ Failed to create job
181
+
182
+ Error: ${error.error || error.message || response.statusText}
183
+ Status: ${response.status}
184
+
185
+ Please check:
186
+ - Your RUNHUMAN_API_KEY is valid
187
+ - The API server is running at ${API_URL}
188
+ - Your API key has permission to create jobs`
189
+ }],
190
+ isError: true
191
+ };
192
+ }
193
+ const data = await response.json();
194
+ return {
195
+ content: [
196
+ {
197
+ type: 'text',
198
+ text: `✅ Job created successfully!
199
+
200
+ Job ID: ${data.jobId}
201
+ Status: ${data.status}
202
+ URL: ${args.url}
203
+
204
+ Your test has been queued and will be picked up by a human tester shortly.
205
+
206
+ Next steps:
207
+ - Use get_job_status(jobId: "${data.jobId}") to check progress
208
+ - Typical completion time: 2-10 minutes`,
209
+ },
210
+ ],
211
+ };
212
+ }
213
+ catch (error) {
214
+ return {
215
+ content: [{
216
+ type: 'text',
217
+ text: `❌ Error creating job
218
+
219
+ ${error instanceof Error ? error.message : 'Unknown error'}
220
+
221
+ Please check:
222
+ - The API server is running at ${API_URL}
223
+ - Your network connection
224
+ - Your .env file configuration`
225
+ }],
226
+ isError: true
227
+ };
228
+ }
229
+ case 'get_job_status':
230
+ try {
231
+ const response = await fetch(`${API_URL}/api/jobs/${args.jobId}`, {
232
+ headers: {
233
+ 'Authorization': `Bearer ${API_KEY}`
234
+ }
235
+ });
236
+ if (!response.ok) {
237
+ if (response.status === 404) {
238
+ return {
239
+ content: [{
240
+ type: 'text',
241
+ text: `❌ Job not found
242
+
243
+ Job ID: ${args.jobId}
244
+
245
+ The job does not exist or you don't have permission to access it.`
246
+ }],
247
+ isError: true
248
+ };
249
+ }
250
+ return {
251
+ content: [{
252
+ type: 'text',
253
+ text: `❌ Failed to get job status
254
+
255
+ Status: ${response.status}
256
+ Error: ${response.statusText}`
257
+ }],
258
+ isError: true
259
+ };
260
+ }
261
+ const job = await response.json();
262
+ // Format timeline
263
+ const createdAt = new Date(job.createdAt);
264
+ const now = new Date();
265
+ const elapsed = Math.floor((now.getTime() - createdAt.getTime()) / 1000 / 60);
266
+ const statusEmoji = {
267
+ pending: '⏳',
268
+ claimed: '👤',
269
+ in_progress: '🔄',
270
+ completed: '✅',
271
+ failed: '❌',
272
+ timeout: '⏰'
273
+ };
274
+ const emoji = statusEmoji[job.status] || '📊';
275
+ let message = `${emoji} Job Status: ${job.status}
276
+
277
+ Job ID: ${job.id}
278
+ URL: ${job.url}
279
+
280
+ Timeline:
281
+ - Created: ${elapsed} minute${elapsed !== 1 ? 's' : ''} ago`;
282
+ if (job.claimedAt) {
283
+ const claimedElapsed = Math.floor((now.getTime() - new Date(job.claimedAt).getTime()) / 1000 / 60);
284
+ message += `\n- Claimed: ${claimedElapsed} minute${claimedElapsed !== 1 ? 's' : ''} ago`;
285
+ }
286
+ if (job.status === 'pending') {
287
+ message += '\n\nWaiting for a tester to claim this job...';
288
+ }
289
+ else if (job.status === 'claimed' || job.status === 'in_progress') {
290
+ message += '\n\nThe tester is working on your test. Estimated completion: 2-10 minutes';
291
+ }
292
+ else if (job.status === 'completed') {
293
+ message += '\n\n✅ Job is complete! Use get_job_result to retrieve the results.';
294
+ }
295
+ else if (job.status === 'failed') {
296
+ message += '\n\n❌ Job failed. Check the error with get_job_result.';
297
+ }
298
+ else if (job.status === 'timeout') {
299
+ message += '\n\n⏰ Job timed out. The tester did not complete in time.';
300
+ }
301
+ return {
302
+ content: [{
303
+ type: 'text',
304
+ text: message
305
+ }]
306
+ };
307
+ }
308
+ catch (error) {
309
+ return {
310
+ content: [{
311
+ type: 'text',
312
+ text: `❌ Error checking job status
313
+
314
+ ${error instanceof Error ? error.message : 'Unknown error'}`
315
+ }],
316
+ isError: true
317
+ };
318
+ }
319
+ case 'get_job_result':
320
+ try {
321
+ const response = await fetch(`${API_URL}/api/jobs/${args.jobId}`, {
322
+ headers: {
323
+ 'Authorization': `Bearer ${API_KEY}`
324
+ }
325
+ });
326
+ if (!response.ok) {
327
+ if (response.status === 404) {
328
+ return {
329
+ content: [{
330
+ type: 'text',
331
+ text: `❌ Job not found
332
+
333
+ Job ID: ${args.jobId}
334
+
335
+ The job does not exist or you don't have permission to access it.`
336
+ }],
337
+ isError: true
338
+ };
339
+ }
340
+ return {
341
+ content: [{
342
+ type: 'text',
343
+ text: `❌ Failed to get job result
344
+
345
+ Status: ${response.status}
346
+ Error: ${response.statusText}`
347
+ }],
348
+ isError: true
349
+ };
350
+ }
351
+ const job = await response.json();
352
+ if (job.status !== 'completed') {
353
+ return {
354
+ content: [{
355
+ type: 'text',
356
+ text: `⏳ Job not yet completed
357
+
358
+ Job ID: ${job.id}
359
+ Current status: ${job.status}
360
+
361
+ ${job.status === 'pending' ? 'Waiting for a tester to claim this job...' :
362
+ job.status === 'claimed' || job.status === 'in_progress' ? 'The tester is working on your test...' :
363
+ job.status === 'failed' ? '❌ Job failed. Error: ' + (job.error || 'Unknown error') :
364
+ job.status === 'timeout' ? '⏰ Job timed out.' :
365
+ 'Use get_job_status to check current status.'}`
366
+ }]
367
+ };
368
+ }
369
+ // Job is completed, format results
370
+ let message = `✅ Test completed!
371
+
372
+ Job ID: ${job.id}
373
+ URL: ${job.url}
374
+
375
+ **Tester Response:**
376
+ "${job.testerResponse || 'No response recorded'}"
377
+
378
+ **Extracted Data:**`;
379
+ const contents = [
380
+ {
381
+ type: 'text',
382
+ text: message
383
+ },
384
+ {
385
+ type: 'text',
386
+ text: JSON.stringify(job.result || {}, null, 2)
387
+ }
388
+ ];
389
+ if (job.error) {
390
+ contents.push({
391
+ type: 'text',
392
+ text: `\n⚠️ Extraction Warnings:\n${job.error}`
393
+ });
394
+ }
395
+ return { content: contents };
396
+ }
397
+ catch (error) {
398
+ return {
399
+ content: [{
400
+ type: 'text',
401
+ text: `❌ Error getting job result
402
+
403
+ ${error instanceof Error ? error.message : 'Unknown error'}`
404
+ }],
405
+ isError: true
406
+ };
407
+ }
408
+ default:
409
+ throw new Error(`Unknown tool: ${name}`);
410
+ }
411
+ });
412
+ /**
413
+ * Start the server
414
+ */
415
+ async function main() {
416
+ const transport = new StdioServerTransport();
417
+ await server.connect(transport);
418
+ console.error('RunHuman MCP server running on stdio');
419
+ }
420
+ main().catch((error) => {
421
+ console.error('Server error:', error);
422
+ process.exit(1);
423
+ });
424
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@runhuman/mcp-server",
3
+ "version": "1.0.0",
4
+ "description": "Model Context Protocol (MCP) server for RunHuman - Human-powered QA testing for AI agents",
5
+ "main": "dist/index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "runhuman-mcp": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md",
13
+ ".env.example"
14
+ ],
15
+ "scripts": {
16
+ "build": "tsc",
17
+ "dev": "tsx watch src/index.ts",
18
+ "start": "node dist/index.js",
19
+ "prepublishOnly": "npm run build",
20
+ "test": "node test-simple.cjs",
21
+ "test:all": "node test-all-tools.cjs",
22
+ "test:inspector": "npx @modelcontextprotocol/inspector node dist/index.js"
23
+ },
24
+ "keywords": [
25
+ "mcp",
26
+ "model-context-protocol",
27
+ "qa",
28
+ "testing",
29
+ "human-in-the-loop",
30
+ "ai-agent",
31
+ "claude",
32
+ "anthropic",
33
+ "qa-testing",
34
+ "manual-testing"
35
+ ],
36
+ "author": "RunHuman <hey@runhuman.com>",
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/yueranyuan/qa-experiment.git",
40
+ "directory": "packages/mcp-server"
41
+ },
42
+ "homepage": "https://runhuman.com",
43
+ "bugs": {
44
+ "url": "https://github.com/yueranyuan/qa-experiment/issues"
45
+ },
46
+ "license": "ISC",
47
+ "engines": {
48
+ "node": ">=18.0.0"
49
+ },
50
+ "dependencies": {
51
+ "@modelcontextprotocol/sdk": "latest",
52
+ "dotenv": "^17.2.3"
53
+ },
54
+ "devDependencies": {
55
+ "@types/node": "^20.11.17",
56
+ "tsx": "^4.7.1",
57
+ "typescript": "^5.3.3"
58
+ }
59
+ }