@runhuman/mcp-server 2.0.3 → 2.0.4

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.
@@ -0,0 +1,132 @@
1
+ /**
2
+ * create_job Tool - Creates a QA job for human testing
3
+ */
4
+ export const createJobToolDefinition = {
5
+ name: 'create_job',
6
+ description: `⚠️ IMPORTANT: This ONLY creates and queues a job. It does NOT perform the test or return results. You MUST follow up with wait_for_result.
7
+
8
+ Creates a QA job that will be performed by a REAL HUMAN tester (not AI). The human will manually test your application, describe findings in natural language, and GPT-4o will extract structured data from their response.
9
+
10
+ Use this when you need human verification of:
11
+ - UI/UX functionality that's hard to automate
12
+ - Visual issues, accessibility problems
13
+ - Complex user flows (login, checkout, forms)
14
+ - Cross-browser compatibility
15
+ - Real user experience feedback
16
+
17
+ ⚠️ REQUIRED WORKFLOW (do NOT skip steps):
18
+ 1. create_job → Returns jobId (job is now QUEUED, not complete!)
19
+ 2. wait_for_result → Checks status, waits, and retrieves results (takes 2-10 min total)
20
+ 3. If not complete, call wait_for_result again with longer wait time
21
+
22
+ DO NOT treat job creation as completion. You MUST wait for and retrieve results.`,
23
+ inputSchema: {
24
+ type: 'object',
25
+ properties: {
26
+ url: {
27
+ type: 'string',
28
+ description: 'The URL to test (must be publicly accessible). Example: "https://myapp.com/checkout"',
29
+ },
30
+ description: {
31
+ type: 'string',
32
+ 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."',
33
+ },
34
+ schema: {
35
+ type: 'object',
36
+ 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" } } } }',
37
+ },
38
+ targetDurationMinutes: {
39
+ type: 'number',
40
+ description: 'Time limit in minutes for the tester to complete the test after claiming. Default: 5 minutes. Range: 1-60.',
41
+ },
42
+ allowDurationExtension: {
43
+ type: 'boolean',
44
+ description: 'Allow test duration to exceed targetDurationMinutes if the tester needs more time. Default: true.',
45
+ },
46
+ maxExtensionMinutes: {
47
+ type: ['number', 'boolean'],
48
+ description: 'Maximum additional minutes allowed for extension. Set to false for unlimited. Default: false (unlimited). Range: 1-60 if number.',
49
+ },
50
+ },
51
+ required: ['url', 'description', 'schema'],
52
+ },
53
+ };
54
+ export async function handleCreateJob(args, config) {
55
+ const { apiUrl, apiKey } = config;
56
+ try {
57
+ const response = await fetch(`${apiUrl}/api/jobs`, {
58
+ method: 'POST',
59
+ headers: {
60
+ 'Authorization': `Bearer ${apiKey}`,
61
+ 'Content-Type': 'application/json',
62
+ },
63
+ body: JSON.stringify({
64
+ url: args.url,
65
+ description: args.description,
66
+ outputSchema: args.schema,
67
+ targetDurationMinutes: args.targetDurationMinutes,
68
+ allowDurationExtension: args.allowDurationExtension,
69
+ maxExtensionMinutes: args.maxExtensionMinutes,
70
+ }),
71
+ });
72
+ if (!response.ok) {
73
+ const error = await response.json().catch(() => ({ error: response.statusText }));
74
+ return {
75
+ content: [{
76
+ type: 'text',
77
+ text: `❌ Failed to create job
78
+
79
+ Error: ${error.error || error.message || response.statusText}
80
+ Status: ${response.status}
81
+
82
+ Please check:
83
+ - Your RUNHUMAN_API_KEY is valid
84
+ - The API server is running at ${apiUrl}
85
+ - Your API key has permission to create jobs`,
86
+ }],
87
+ isError: true,
88
+ };
89
+ }
90
+ const data = await response.json();
91
+ const duration = args.targetDurationMinutes || 5;
92
+ return {
93
+ content: [{
94
+ type: 'text',
95
+ text: `⏳ Job QUEUED (not complete yet!)
96
+
97
+ Job ID: ${data.jobId}
98
+ Status: ${data.status}
99
+ URL: ${args.url}
100
+ Time limit: ${duration} minutes
101
+
102
+ The test has been queued for a HUMAN tester. It is NOT complete yet.
103
+
104
+ ⚠️ REQUIRED NEXT STEPS:
105
+ 1. Call wait_for_result(jobId: "${data.jobId}", waitSeconds: 30)
106
+ 2. If not complete, call wait_for_result again with waitSeconds: 45
107
+ 3. Continue with increasing wait times (60, 90, etc.) until complete
108
+
109
+ Typical completion time: 2-10 minutes total.
110
+
111
+ DO NOT consider this task done until you have retrieved the results!`,
112
+ }],
113
+ };
114
+ }
115
+ catch (error) {
116
+ return {
117
+ content: [{
118
+ type: 'text',
119
+ text: `❌ Error creating job
120
+
121
+ ${error instanceof Error ? error.message : 'Unknown error'}
122
+
123
+ Please check:
124
+ - The API server is running at ${apiUrl}
125
+ - Your network connection
126
+ - Your .env file configuration`,
127
+ }],
128
+ isError: true,
129
+ };
130
+ }
131
+ }
132
+ //# sourceMappingURL=create-job.tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-job.tool.js","sourceRoot":"","sources":["../../src/tools/create-job.tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * MCP Tools - Barrel export
3
+ */
4
+ export { createJobToolDefinition, handleCreateJob } from './create-job.tool.js';
5
+ export { waitForResultToolDefinition, handleWaitForResult } from './wait-for-result.tool.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAChF,OAAO,EAAE,2BAA2B,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * MCP Tools - Barrel export
3
+ */
4
+ export { createJobToolDefinition, handleCreateJob } from './create-job.tool.js';
5
+ export { waitForResultToolDefinition, handleWaitForResult } from './wait-for-result.tool.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAChF,OAAO,EAAE,2BAA2B,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * wait_for_result Tool - Check status and retrieve results for a QA job
3
+ */
4
+ import type { Tool } from '@modelcontextprotocol/sdk/types.js';
5
+ import type { McpServerConfig, WaitForResultArgs } from '../types.js';
6
+ export declare const waitForResultToolDefinition: Tool;
7
+ export declare function handleWaitForResult(args: WaitForResultArgs, config: McpServerConfig): Promise<{
8
+ content: Array<{
9
+ type: 'text';
10
+ text: string;
11
+ }>;
12
+ isError?: boolean;
13
+ }>;
14
+ //# sourceMappingURL=wait-for-result.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wait-for-result.tool.d.ts","sourceRoot":"","sources":["../../src/tools/wait-for-result.tool.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAe,MAAM,aAAa,CAAC;AAEnF,eAAO,MAAM,2BAA2B,EAAE,IAmCzC,CAAC;AAwEF,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,iBAAiB,EACvB,MAAM,EAAE,eAAe,GACtB,OAAO,CAAC;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAqGhF"}
@@ -0,0 +1,196 @@
1
+ /**
2
+ * wait_for_result Tool - Check status and retrieve results for a QA job
3
+ */
4
+ export const waitForResultToolDefinition = {
5
+ name: 'wait_for_result',
6
+ description: `Check status, wait, and retrieve results for a QA job in a single call.
7
+
8
+ This tool combines status checking, waiting, and result retrieval into one convenient function:
9
+ - Checks status BEFORE waiting (returns immediately if already complete)
10
+ - Polls status every 5 seconds during the wait period
11
+ - Returns immediately when job completes (no need to wait full duration)
12
+ - Returns results if complete, otherwise suggests calling again with longer wait
13
+
14
+ Jobs progress through states: pending → claimed → in_progress → completed (or failed/timeout)
15
+ Typical completion time: 2-10 minutes total
16
+
17
+ Use this tool repeatedly with increasing wait times until you get results:
18
+ - First call: wait_for_result(jobId, { waitSeconds: 30 })
19
+ - If not complete: wait_for_result(jobId, { waitSeconds: 45 })
20
+ - If still not complete: wait_for_result(jobId, { waitSeconds: 60 })
21
+
22
+ Returns immediately if job is already complete (no waiting needed).`,
23
+ inputSchema: {
24
+ type: 'object',
25
+ properties: {
26
+ jobId: {
27
+ type: 'string',
28
+ description: 'The job ID returned from create_job. Example: "550e8400-e29b-41d4-a716-446655440000"',
29
+ },
30
+ waitSeconds: {
31
+ type: 'number',
32
+ description: 'How long to wait (in seconds) before checking status again. Default: 30. Range: 1-300. Increase this on subsequent calls (30 → 45 → 60) for better efficiency.',
33
+ minimum: 1,
34
+ maximum: 300,
35
+ },
36
+ },
37
+ required: ['jobId'],
38
+ },
39
+ };
40
+ async function checkJobStatus(jobId, config) {
41
+ const response = await fetch(`${config.apiUrl}/api/job/${jobId}`, {
42
+ headers: {
43
+ 'Authorization': `Bearer ${config.apiKey}`,
44
+ },
45
+ });
46
+ if (!response.ok) {
47
+ if (response.status === 404) {
48
+ throw new Error(`Job not found: ${jobId}`);
49
+ }
50
+ throw new Error(`Failed to get job status: ${response.status} ${response.statusText}`);
51
+ }
52
+ return await response.json();
53
+ }
54
+ function formatCompletedResult(job) {
55
+ return {
56
+ content: [
57
+ {
58
+ type: 'text',
59
+ text: `✅ Test completed!
60
+
61
+ Job ID: ${job.id}
62
+
63
+ **Test Results:**`,
64
+ },
65
+ {
66
+ type: 'text',
67
+ text: JSON.stringify({
68
+ result: job.result || {},
69
+ testerResponse: job.testerResponse,
70
+ testerAlias: job.testerAlias,
71
+ testerAvatarUrl: job.testerAvatarUrl,
72
+ testerColor: job.testerColor,
73
+ testerData: job.testerData,
74
+ }, null, 2),
75
+ },
76
+ ],
77
+ };
78
+ }
79
+ function formatFailedResult(job) {
80
+ return {
81
+ content: [{
82
+ type: 'text',
83
+ text: `❌ Job failed
84
+
85
+ Job ID: ${job.id}
86
+ Error: ${job.error || 'Unknown error'}`,
87
+ }],
88
+ isError: true,
89
+ };
90
+ }
91
+ function formatTimeoutResult(job) {
92
+ return {
93
+ content: [{
94
+ type: 'text',
95
+ text: `⏰ Job timed out
96
+
97
+ Job ID: ${job.id}
98
+
99
+ The tester did not complete the test in time.`,
100
+ }],
101
+ isError: true,
102
+ };
103
+ }
104
+ export async function handleWaitForResult(args, config) {
105
+ try {
106
+ const waitSeconds = Math.min(Math.max(args.waitSeconds || 30, 1), 300);
107
+ const pollIntervalSeconds = 5; // Poll every 5 seconds
108
+ // Check status BEFORE waiting
109
+ console.error(`[wait_for_result] Checking initial status for job ${args.jobId}...`);
110
+ let job = await checkJobStatus(args.jobId, config);
111
+ // If already in terminal state, return immediately
112
+ if (job.status === 'completed') {
113
+ console.error(`[wait_for_result] Job already completed`);
114
+ return formatCompletedResult(job);
115
+ }
116
+ if (job.status === 'failed') {
117
+ console.error(`[wait_for_result] Job already failed`);
118
+ return formatFailedResult(job);
119
+ }
120
+ if (job.status === 'timeout') {
121
+ console.error(`[wait_for_result] Job already timed out`);
122
+ return formatTimeoutResult(job);
123
+ }
124
+ // Job not complete yet, poll periodically during the wait period
125
+ console.error(`[wait_for_result] Job status: ${job.status}. Polling every ${pollIntervalSeconds}s for up to ${waitSeconds}s...`);
126
+ const startTime = Date.now();
127
+ const maxWaitMs = waitSeconds * 1000;
128
+ let elapsedSeconds = 0;
129
+ while (true) {
130
+ // Wait for the poll interval
131
+ await new Promise(resolve => setTimeout(resolve, pollIntervalSeconds * 1000));
132
+ elapsedSeconds += pollIntervalSeconds;
133
+ // Check if we've exceeded the max wait time
134
+ const elapsed = Date.now() - startTime;
135
+ if (elapsed >= maxWaitMs) {
136
+ console.error(`[wait_for_result] Reached max wait time of ${waitSeconds}s`);
137
+ break;
138
+ }
139
+ // Check status
140
+ console.error(`[wait_for_result] Polling status (${elapsedSeconds}s elapsed)...`);
141
+ job = await checkJobStatus(args.jobId, config);
142
+ // Check if complete now
143
+ if (job.status === 'completed') {
144
+ console.error(`[wait_for_result] Job completed after ${elapsedSeconds}s`);
145
+ return formatCompletedResult(job);
146
+ }
147
+ if (job.status === 'failed') {
148
+ console.error(`[wait_for_result] Job failed after ${elapsedSeconds}s`);
149
+ return formatFailedResult(job);
150
+ }
151
+ if (job.status === 'timeout') {
152
+ console.error(`[wait_for_result] Job timed out after ${elapsedSeconds}s`);
153
+ return formatTimeoutResult(job);
154
+ }
155
+ console.error(`[wait_for_result] Job still ${job.status}, continuing to poll...`);
156
+ }
157
+ // Reached max wait time without completion
158
+ const statusEmoji = {
159
+ pending: '⏳',
160
+ claimed: '👤',
161
+ in_progress: '🔄',
162
+ };
163
+ const emoji = statusEmoji[job.status] || '📊';
164
+ const nextWait = Math.min(waitSeconds + 15, 120);
165
+ const statusMessage = job.status === 'pending'
166
+ ? 'Waiting for a tester to claim this job...'
167
+ : 'The tester is working on your test...';
168
+ return {
169
+ content: [{
170
+ type: 'text',
171
+ text: `${emoji} Job Status: ${job.status}
172
+
173
+ Job ID: ${job.id}
174
+
175
+ ${statusMessage}
176
+
177
+ ⏰ Waited ${waitSeconds}s (polling every ${pollIntervalSeconds}s), job not complete yet.
178
+
179
+ 💡 Suggestion: Call wait_for_result again with waitSeconds: ${nextWait}
180
+ Typical completion time: 2-10 minutes total.`,
181
+ }],
182
+ };
183
+ }
184
+ catch (error) {
185
+ return {
186
+ content: [{
187
+ type: 'text',
188
+ text: `❌ Error waiting for result
189
+
190
+ ${error instanceof Error ? error.message : 'Unknown error'}`,
191
+ }],
192
+ isError: true,
193
+ };
194
+ }
195
+ }
196
+ //# sourceMappingURL=wait-for-result.tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wait-for-result.tool.js","sourceRoot":"","sources":["../../src/tools/wait-for-result.tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * MCP Server Configuration Types
3
+ */
4
+ export interface McpServerConfig {
5
+ /** Base URL of the RunHuman API */
6
+ apiUrl: string;
7
+ /** API key for authentication */
8
+ apiKey: string;
9
+ }
10
+ export interface CreateJobArgs {
11
+ url: string;
12
+ description: string;
13
+ schema: Record<string, unknown>;
14
+ targetDurationMinutes?: number;
15
+ allowDurationExtension?: boolean;
16
+ maxExtensionMinutes?: number | boolean;
17
+ }
18
+ export interface WaitForResultArgs {
19
+ jobId: string;
20
+ waitSeconds?: number;
21
+ }
22
+ export interface JobResponse {
23
+ id: string;
24
+ status: string;
25
+ result?: unknown;
26
+ error?: string;
27
+ testerResponse?: string;
28
+ testerAlias?: string;
29
+ testerAvatarUrl?: string;
30
+ testerColor?: string;
31
+ testerData?: {
32
+ testDurationSeconds: number;
33
+ consoleMessages: Array<{
34
+ type: string;
35
+ message: string;
36
+ timestamp: string;
37
+ }>;
38
+ networkRequests: Array<{
39
+ url: string;
40
+ method: string;
41
+ status?: number;
42
+ timestamp: string;
43
+ }>;
44
+ clicks: Array<{
45
+ x: number;
46
+ y: number;
47
+ timestamp: string;
48
+ element?: string;
49
+ }>;
50
+ screenshots: string[];
51
+ videoUrl?: string;
52
+ };
53
+ }
54
+ export interface CreateJobResponse {
55
+ jobId: string;
56
+ status: string;
57
+ }
58
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,eAAe;IAC9B,mCAAmC;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,mBAAmB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACxC;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE;QACX,mBAAmB,EAAE,MAAM,CAAC;QAC5B,eAAe,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAC7E,eAAe,EAAE,KAAK,CAAC;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAC5F,MAAM,EAAE,KAAK,CAAC;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAC7E,WAAW,EAAE,MAAM,EAAE,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB"}
package/dist/types.js ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * MCP Server Configuration Types
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
package/package.json CHANGED
@@ -1,62 +1,78 @@
1
- {
2
- "name": "@runhuman/mcp-server",
3
- "version": "2.0.3",
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:routes": "node test-route-fix.cjs",
23
- "test:endpoints": "node test-api-endpoints.cjs",
24
- "test:ci": "npm run test:endpoints && npm run test:routes",
25
- "test:inspector": "npx @modelcontextprotocol/inspector node dist/index.js"
26
- },
27
- "keywords": [
28
- "mcp",
29
- "model-context-protocol",
30
- "qa",
31
- "testing",
32
- "human-in-the-loop",
33
- "ai-agent",
34
- "claude",
35
- "anthropic",
36
- "qa-testing",
37
- "manual-testing"
38
- ],
39
- "author": "RunHuman <hey@runhuman.com>",
40
- "repository": {
41
- "type": "git",
42
- "url": "git+https://github.com/yueranyuan/qa-experiment.git",
43
- "directory": "packages/mcp-server"
44
- },
45
- "homepage": "https://runhuman.com",
46
- "bugs": {
47
- "url": "https://github.com/yueranyuan/qa-experiment/issues"
48
- },
49
- "license": "ISC",
50
- "engines": {
51
- "node": ">=18.0.0"
52
- },
53
- "dependencies": {
54
- "@modelcontextprotocol/sdk": "latest",
55
- "dotenv": "^17.2.3"
56
- },
57
- "devDependencies": {
58
- "@types/node": "^20.11.17",
59
- "tsx": "^4.7.1",
60
- "typescript": "^5.3.3"
61
- }
62
- }
1
+ {
2
+ "name": "@runhuman/mcp-server",
3
+ "version": "2.0.4",
4
+ "description": "Model Context Protocol (MCP) server for RunHuman - Human-powered QA testing for AI agents",
5
+ "main": "dist/lib.js",
6
+ "type": "module",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/lib.js",
10
+ "require": "./dist/lib.js",
11
+ "types": "./dist/lib.d.ts"
12
+ },
13
+ "./factory": {
14
+ "import": "./dist/mcp-server-factory.js",
15
+ "types": "./dist/mcp-server-factory.d.ts"
16
+ },
17
+ "./types": {
18
+ "import": "./dist/types.js",
19
+ "types": "./dist/types.d.ts"
20
+ }
21
+ },
22
+ "bin": {
23
+ "runhuman-mcp": "dist/index.js"
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "README.md",
28
+ ".env.example"
29
+ ],
30
+ "scripts": {
31
+ "build": "tsc",
32
+ "dev": "tsx watch src/index.ts",
33
+ "start": "node dist/index.js",
34
+ "prepublishOnly": "npm run build",
35
+ "type-check": "tsc",
36
+ "test": "node test-simple.cjs",
37
+ "test:all": "node test-all-tools.cjs",
38
+ "test:routes": "node test-route-fix.cjs",
39
+ "test:endpoints": "node test-api-endpoints.cjs",
40
+ "test:ci": "npm run test:endpoints && npm run test:routes",
41
+ "test:inspector": "npx @modelcontextprotocol/inspector node dist/index.js"
42
+ },
43
+ "keywords": [
44
+ "mcp",
45
+ "model-context-protocol",
46
+ "qa",
47
+ "testing",
48
+ "human-in-the-loop",
49
+ "ai-agent",
50
+ "claude",
51
+ "anthropic",
52
+ "qa-testing",
53
+ "manual-testing"
54
+ ],
55
+ "author": "RunHuman <hey@runhuman.com>",
56
+ "repository": {
57
+ "type": "git",
58
+ "url": "git+https://github.com/yueranyuan/qa-experiment.git",
59
+ "directory": "packages/mcp-server"
60
+ },
61
+ "homepage": "https://runhuman.com",
62
+ "bugs": {
63
+ "url": "https://github.com/yueranyuan/qa-experiment/issues"
64
+ },
65
+ "license": "ISC",
66
+ "engines": {
67
+ "node": ">=18.0.0"
68
+ },
69
+ "dependencies": {
70
+ "@modelcontextprotocol/sdk": "latest",
71
+ "dotenv": "^17.2.3"
72
+ },
73
+ "devDependencies": {
74
+ "@types/node": "^20.11.17",
75
+ "tsx": "^4.7.1",
76
+ "typescript": "^5.3.3"
77
+ }
78
+ }