@chorus-research/mcp 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.
Files changed (46) hide show
  1. package/README.md +126 -0
  2. package/dist/client.d.ts +162 -0
  3. package/dist/client.d.ts.map +1 -0
  4. package/dist/client.js +86 -0
  5. package/dist/client.js.map +1 -0
  6. package/dist/index.d.ts +3 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +48 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/prompts/design-survey.d.ts +3 -0
  11. package/dist/prompts/design-survey.d.ts.map +1 -0
  12. package/dist/prompts/design-survey.js +98 -0
  13. package/dist/prompts/design-survey.js.map +1 -0
  14. package/dist/prompts/launch-checklist.d.ts +3 -0
  15. package/dist/prompts/launch-checklist.d.ts.map +1 -0
  16. package/dist/prompts/launch-checklist.js +74 -0
  17. package/dist/prompts/launch-checklist.js.map +1 -0
  18. package/dist/resources/audiences.d.ts +4 -0
  19. package/dist/resources/audiences.d.ts.map +1 -0
  20. package/dist/resources/audiences.js +13 -0
  21. package/dist/resources/audiences.js.map +1 -0
  22. package/dist/resources/pricing.d.ts +4 -0
  23. package/dist/resources/pricing.d.ts.map +1 -0
  24. package/dist/resources/pricing.js +13 -0
  25. package/dist/resources/pricing.js.map +1 -0
  26. package/dist/tools/audiences.d.ts +4 -0
  27. package/dist/tools/audiences.d.ts.map +1 -0
  28. package/dist/tools/audiences.js +27 -0
  29. package/dist/tools/audiences.js.map +1 -0
  30. package/dist/tools/launch.d.ts +4 -0
  31. package/dist/tools/launch.d.ts.map +1 -0
  32. package/dist/tools/launch.js +87 -0
  33. package/dist/tools/launch.js.map +1 -0
  34. package/dist/tools/pricing.d.ts +4 -0
  35. package/dist/tools/pricing.d.ts.map +1 -0
  36. package/dist/tools/pricing.js +38 -0
  37. package/dist/tools/pricing.js.map +1 -0
  38. package/dist/tools/responses.d.ts +4 -0
  39. package/dist/tools/responses.d.ts.map +1 -0
  40. package/dist/tools/responses.js +40 -0
  41. package/dist/tools/responses.js.map +1 -0
  42. package/dist/tools/surveys.d.ts +4 -0
  43. package/dist/tools/surveys.d.ts.map +1 -0
  44. package/dist/tools/surveys.js +74 -0
  45. package/dist/tools/surveys.js.map +1 -0
  46. package/package.json +42 -0
package/README.md ADDED
@@ -0,0 +1,126 @@
1
+ # @chorus-research/mcp
2
+
3
+ MCP server for the [Chorus Research](https://chorusresearch.com) survey platform — create surveys, launch them to targeted audiences, and collect responses, all from your AI agent.
4
+
5
+ Built on the [Model Context Protocol](https://modelcontextprotocol.io).
6
+
7
+ ## Quick Start
8
+
9
+ ### 1. Get an API key
10
+
11
+ Sign up at [chorusresearch.com](https://chorusresearch.com) and generate an API key from your dashboard.
12
+
13
+ ### 2. Add to your MCP client
14
+
15
+ **Claude Code (`.mcp.json`)**:
16
+
17
+ ```json
18
+ {
19
+ "mcpServers": {
20
+ "chorus-research": {
21
+ "command": "npx",
22
+ "args": ["@chorus-research/mcp"],
23
+ "env": {
24
+ "CHORUS_API_KEY": "your-api-key"
25
+ }
26
+ }
27
+ }
28
+ }
29
+ ```
30
+
31
+ **Claude Desktop (`claude_desktop_config.json`)**:
32
+
33
+ ```json
34
+ {
35
+ "mcpServers": {
36
+ "chorus-research": {
37
+ "command": "npx",
38
+ "args": ["@chorus-research/mcp"],
39
+ "env": {
40
+ "CHORUS_API_KEY": "your-api-key"
41
+ }
42
+ }
43
+ }
44
+ }
45
+ ```
46
+
47
+ ## Tools
48
+
49
+ The server exposes 10 tools across surveys, audiences, pricing, launch, and responses.
50
+
51
+ ### Surveys
52
+
53
+ | Tool | Description | Key Parameters |
54
+ |------|-------------|----------------|
55
+ | `list_surveys` | List surveys with optional filtering and pagination | `status?`, `page?`, `pageSize?`, `sort?`, `order?` |
56
+ | `get_survey` | Get full survey details including the JSON definition | `surveyId` |
57
+ | `create_survey` | Create a new survey draft | `name`, `survey` (JSON), `targetCompletes`, `description?` |
58
+
59
+ ### Audiences
60
+
61
+ | Tool | Description | Key Parameters |
62
+ |------|-------------|----------------|
63
+ | `list_audiences` | List all available audience segments with costs and targeting criteria | _(none)_ |
64
+ | `get_audience` | Get details for a specific audience segment | `audienceId` |
65
+
66
+ ### Pricing
67
+
68
+ | Tool | Description | Key Parameters |
69
+ |------|-------------|----------------|
70
+ | `estimate_cost` | Calculate launch cost (costPerResponse x targetCompletes) | `audienceId`, `targetCompletes`, `projectId?` |
71
+ | `get_pricing_config` | Get current cost-per-response and currency | _(none)_ |
72
+
73
+ ### Launch (two-step)
74
+
75
+ | Tool | Description | Key Parameters |
76
+ |------|-------------|----------------|
77
+ | `launch_survey` | Launch a survey to collect responses | See below |
78
+ | `get_survey_status` | Real-time fielding status (completes, progress %, ETA) | `surveyId` |
79
+
80
+ **`launch_survey` is a two-step process** to prevent accidental charges:
81
+
82
+ 1. **Estimate** — call with `surveyId`, `audienceId`, and `targetCompletes`. Returns a cost estimate and a `confirmToken` (valid 5 minutes).
83
+ 2. **Confirm** — call again with `surveyId` and the `confirmToken` to execute the launch and charge payment.
84
+
85
+ ```
86
+ # Step 1: Get estimate
87
+ launch_survey(surveyId, audienceId="genpop", targetCompletes=100)
88
+ # → { step: "estimate", estimatedCost: 100, confirmToken: "tok_...", expiresAt: "..." }
89
+
90
+ # Step 2: Confirm (triggers real charges)
91
+ launch_survey(surveyId, confirmToken="tok_...")
92
+ # → { step: "confirmed", success: true, amountCharged: 100 }
93
+ ```
94
+
95
+ ### Responses
96
+
97
+ | Tool | Description | Key Parameters |
98
+ |------|-------------|----------------|
99
+ | `get_responses` | Export response data with pagination | `surveyId`, `page?`, `pageSize?`, `status?`, `includeDuplicates?` |
100
+
101
+ Response metadata includes device info (userAgent, screenSize, timezone), geographic data (city, state, country), and provider demographics when available. Duplicates are excluded by default.
102
+
103
+ ## Resources
104
+
105
+ | URI | Name | Description |
106
+ |-----|------|-------------|
107
+ | `chorus://audiences` | Audience Catalog | Full catalog of audience segments with targeting, costs, and fielding times |
108
+ | `chorus://pricing/config` | Pricing Configuration | Current cost-per-response and currency |
109
+
110
+ ## Prompts
111
+
112
+ | Name | Description | Arguments |
113
+ |------|-------------|-----------|
114
+ | `design_survey` | Guides survey creation with best practices | `topic` (required), `audience?`, `numQuestions?` |
115
+ | `launch_checklist` | Pre-launch validation checklist with cost review | `surveyId` (required) |
116
+
117
+ ## Configuration
118
+
119
+ | Environment Variable | Description | Default |
120
+ |---------------------|-------------|---------|
121
+ | `CHORUS_API_KEY` | API key for authentication (required) | _(none)_ |
122
+ | `CHORUS_API_BASE_URL` | Base URL for the Chorus Research API | `https://api.chorusresearch.io` |
123
+
124
+ ## Requirements
125
+
126
+ - Node.js >= 20
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Typed HTTP client wrapping the Chorus Research Public API.
3
+ *
4
+ * Reads CHORUS_API_KEY and CHORUS_API_BASE_URL from the environment.
5
+ */
6
+ export interface Audience {
7
+ id: string;
8
+ name: string;
9
+ description: string;
10
+ costPerResponse: number;
11
+ minTargetCompletes: number;
12
+ maxTargetCompletes: number;
13
+ averageLengthOfInterview: number;
14
+ targeting: {
15
+ age_min: number;
16
+ age_max: number;
17
+ country: string;
18
+ };
19
+ estimatedFieldingTime: Record<string, string>;
20
+ }
21
+ export interface Survey {
22
+ id: string;
23
+ name: string;
24
+ survey: Record<string, unknown>;
25
+ targetCompletes: number;
26
+ description?: string;
27
+ status: "draft" | "live" | "complete" | "cancelled";
28
+ createdAt: string;
29
+ updatedAt: string;
30
+ }
31
+ export interface Pagination {
32
+ page: number;
33
+ pageSize: number;
34
+ total: number;
35
+ totalPages: number;
36
+ }
37
+ export interface PricingEstimate {
38
+ audienceId: string;
39
+ targetCompletes: number;
40
+ baseCostPerComplete: number;
41
+ subtotal: number;
42
+ total: number;
43
+ currency: string;
44
+ }
45
+ export interface PricingConfig {
46
+ costPerResponse: number;
47
+ currency: string;
48
+ }
49
+ export interface LaunchEstimate {
50
+ estimatedCost: number;
51
+ breakdown: PricingEstimate;
52
+ confirmToken: string;
53
+ expiresAt: string;
54
+ }
55
+ export interface LaunchConfirmation {
56
+ success: boolean;
57
+ publishedSurveyId: string;
58
+ workflowId: string;
59
+ paymentIntentId: string;
60
+ amountCharged: number;
61
+ currency: string;
62
+ targetCompletes: number;
63
+ }
64
+ export interface SurveyStatus {
65
+ projectId: string;
66
+ publishedSurveyId: string | null;
67
+ status: "draft" | "live" | "complete" | "cancelled";
68
+ completes: number;
69
+ target: number;
70
+ progress: number;
71
+ lastUpdated: string;
72
+ estimatedCompletion: string | null;
73
+ message?: string;
74
+ }
75
+ export interface SurveyResponse {
76
+ id: string;
77
+ publishedSurveyId: string;
78
+ responseData: Record<string, unknown>;
79
+ metadata: {
80
+ rdud?: string;
81
+ completedAt?: string;
82
+ supplier?: string;
83
+ demographics?: Record<string, unknown>;
84
+ start_time?: string;
85
+ end_time?: string;
86
+ generated_at?: string;
87
+ userAgent?: string;
88
+ language?: string;
89
+ screenSize?: string;
90
+ timezone?: string;
91
+ city?: string;
92
+ state?: string;
93
+ region?: string;
94
+ country?: string;
95
+ };
96
+ status: "complete" | "partial" | "incomplete";
97
+ supplier: string | null;
98
+ createdAt: string;
99
+ completedAt: string;
100
+ }
101
+ export interface ApiError {
102
+ error: string;
103
+ message: string;
104
+ code: string;
105
+ }
106
+ export declare class ChorusApiClient {
107
+ private baseUrl;
108
+ private apiKey;
109
+ constructor(opts?: {
110
+ baseUrl?: string;
111
+ apiKey?: string;
112
+ });
113
+ private headers;
114
+ private request;
115
+ listAudiences(): Promise<{
116
+ audiences: Audience[];
117
+ count: number;
118
+ }>;
119
+ getAudience(id: string): Promise<Audience>;
120
+ estimateCost(params: {
121
+ audienceId: string;
122
+ targetCompletes: number;
123
+ projectId?: string;
124
+ }): Promise<PricingEstimate>;
125
+ getPricingConfig(): Promise<PricingConfig>;
126
+ createSurvey(params: {
127
+ name: string;
128
+ survey: Record<string, unknown>;
129
+ targetCompletes: number;
130
+ description?: string;
131
+ }): Promise<Survey>;
132
+ listSurveys(params?: {
133
+ status?: string;
134
+ page?: number;
135
+ pageSize?: number;
136
+ sort?: string;
137
+ order?: string;
138
+ }): Promise<{
139
+ surveys: Survey[];
140
+ pagination: Pagination;
141
+ }>;
142
+ getSurvey(id: string): Promise<Survey>;
143
+ launchEstimate(surveyId: string, params: {
144
+ audienceId: string;
145
+ targetCompletes: number;
146
+ }): Promise<LaunchEstimate>;
147
+ launchConfirm(surveyId: string, params: {
148
+ confirmToken: string;
149
+ paymentMethodId?: string;
150
+ }): Promise<LaunchConfirmation>;
151
+ getSurveyStatus(surveyId: string): Promise<SurveyStatus>;
152
+ getResponses(surveyId: string, params?: {
153
+ page?: number;
154
+ pageSize?: number;
155
+ status?: string;
156
+ includeDuplicates?: boolean;
157
+ }): Promise<{
158
+ responses: SurveyResponse[];
159
+ pagination: Pagination;
160
+ }>;
161
+ }
162
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/client.js ADDED
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Typed HTTP client wrapping the Chorus Research Public API.
3
+ *
4
+ * Reads CHORUS_API_KEY and CHORUS_API_BASE_URL from the environment.
5
+ */
6
+ // ── Client ────────────────────────────────────────────────────────
7
+ export class ChorusApiClient {
8
+ baseUrl;
9
+ apiKey;
10
+ constructor(opts) {
11
+ this.baseUrl = (opts?.baseUrl ??
12
+ process.env.CHORUS_API_BASE_URL ??
13
+ "https://api.chorusresearch.io").replace(/\/$/, "");
14
+ this.apiKey = opts?.apiKey ?? process.env.CHORUS_API_KEY ?? "";
15
+ }
16
+ // ── Internals ──────────────────────────────────────────────────
17
+ headers() {
18
+ const h = {
19
+ "Content-Type": "application/json",
20
+ Accept: "application/json",
21
+ };
22
+ if (this.apiKey) {
23
+ h["Authorization"] = `Bearer ${this.apiKey}`;
24
+ }
25
+ return h;
26
+ }
27
+ async request(method, path, body, query) {
28
+ const url = new URL(`${this.baseUrl}${path}`);
29
+ if (query) {
30
+ for (const [k, v] of Object.entries(query)) {
31
+ if (v !== undefined)
32
+ url.searchParams.set(k, String(v));
33
+ }
34
+ }
35
+ const res = await fetch(url.toString(), {
36
+ method,
37
+ headers: this.headers(),
38
+ body: body ? JSON.stringify(body) : undefined,
39
+ });
40
+ const json = await res.json();
41
+ if (!res.ok) {
42
+ const err = json;
43
+ throw new Error(`API ${res.status}: ${err.message ?? err.error ?? "Unknown error"} (${err.code ?? "NO_CODE"})`);
44
+ }
45
+ return json;
46
+ }
47
+ // ── Audiences ──────────────────────────────────────────────────
48
+ async listAudiences() {
49
+ return this.request("GET", "/audiences");
50
+ }
51
+ async getAudience(id) {
52
+ return this.request("GET", `/audiences/${encodeURIComponent(id)}`);
53
+ }
54
+ // ── Pricing ────────────────────────────────────────────────────
55
+ async estimateCost(params) {
56
+ return this.request("POST", "/pricing/estimate", params);
57
+ }
58
+ async getPricingConfig() {
59
+ return this.request("GET", "/pricing/config");
60
+ }
61
+ // ── Surveys ────────────────────────────────────────────────────
62
+ async createSurvey(params) {
63
+ return this.request("POST", "/surveys", params);
64
+ }
65
+ async listSurveys(params) {
66
+ return this.request("GET", "/surveys", undefined, params);
67
+ }
68
+ async getSurvey(id) {
69
+ return this.request("GET", `/surveys/${encodeURIComponent(id)}`);
70
+ }
71
+ // ── Launch ─────────────────────────────────────────────────────
72
+ async launchEstimate(surveyId, params) {
73
+ return this.request("POST", `/surveys/${encodeURIComponent(surveyId)}/launch`, params);
74
+ }
75
+ async launchConfirm(surveyId, params) {
76
+ return this.request("POST", `/surveys/${encodeURIComponent(surveyId)}/launch/confirm`, params);
77
+ }
78
+ async getSurveyStatus(surveyId) {
79
+ return this.request("GET", `/surveys/${encodeURIComponent(surveyId)}/status`);
80
+ }
81
+ // ── Responses ──────────────────────────────────────────────────
82
+ async getResponses(surveyId, params) {
83
+ return this.request("GET", `/surveys/${encodeURIComponent(surveyId)}/responses`, undefined, params);
84
+ }
85
+ }
86
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env node
2
+ import { FastMCP } from "fastmcp";
3
+ import { ChorusApiClient } from "./client.js";
4
+ import { registerSurveyTools } from "./tools/surveys.js";
5
+ import { registerAudienceTools } from "./tools/audiences.js";
6
+ import { registerPricingTools } from "./tools/pricing.js";
7
+ import { registerLaunchTools } from "./tools/launch.js";
8
+ import { registerResponseTools } from "./tools/responses.js";
9
+ import { registerAudienceResources } from "./resources/audiences.js";
10
+ import { registerPricingResources } from "./resources/pricing.js";
11
+ import { registerDesignSurveyPrompt } from "./prompts/design-survey.js";
12
+ import { registerLaunchChecklistPrompt } from "./prompts/launch-checklist.js";
13
+ const server = new FastMCP({
14
+ name: "Chorus Research",
15
+ version: "0.1.0",
16
+ instructions: `Chorus Research MCP — programmatic survey creation, launch, and response collection.
17
+
18
+ ## Quick Start
19
+ 1. Use \`list_audiences\` to see available audience segments
20
+ 2. Use \`get_pricing_config\` or \`estimate_cost\` to understand pricing
21
+ 3. Use \`create_survey\` to create a survey draft with a JSON definition
22
+ 4. Use \`launch_survey\` (two-step) to launch and collect responses
23
+ 5. Use \`get_responses\` to export collected data
24
+
25
+ ## Key Concepts
26
+ - **Audiences**: Pre-defined respondent segments (e.g., "genpop" = US adults 18+)
27
+ - **Two-step launch**: Launching requires an estimate step then a confirm step to prevent accidental charges
28
+ - **Fielding**: After launch, responses are collected automatically from the target audience
29
+
30
+ ## Authentication
31
+ Set the CHORUS_API_KEY environment variable with your API key.`,
32
+ });
33
+ const api = new ChorusApiClient();
34
+ // Register tools
35
+ registerSurveyTools(server, api);
36
+ registerAudienceTools(server, api);
37
+ registerPricingTools(server, api);
38
+ registerLaunchTools(server, api);
39
+ registerResponseTools(server, api);
40
+ // Register resources
41
+ registerAudienceResources(server, api);
42
+ registerPricingResources(server, api);
43
+ // Register prompts
44
+ registerDesignSurveyPrompt(server);
45
+ registerLaunchChecklistPrompt(server);
46
+ // Start on stdio (consumer MCP clients)
47
+ server.start({ transportType: "stdio" });
48
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,EAAE,6BAA6B,EAAE,MAAM,+BAA+B,CAAC;AAE9E,MAAM,MAAM,GAAG,IAAI,OAAO,CAAC;IACzB,IAAI,EAAE,iBAAiB;IACvB,OAAO,EAAE,OAAO;IAChB,YAAY,EAAE;;;;;;;;;;;;;;;+DAe+C;CAC9D,CAAC,CAAC;AAEH,MAAM,GAAG,GAAG,IAAI,eAAe,EAAE,CAAC;AAElC,iBAAiB;AACjB,mBAAmB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AACjC,qBAAqB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AACnC,oBAAoB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAClC,mBAAmB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AACjC,qBAAqB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAEnC,qBAAqB;AACrB,yBAAyB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AACvC,wBAAwB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAEtC,mBAAmB;AACnB,0BAA0B,CAAC,MAAM,CAAC,CAAC;AACnC,6BAA6B,CAAC,MAAM,CAAC,CAAC;AAEtC,wCAAwC;AACxC,MAAM,CAAC,KAAK,CAAC,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { FastMCP } from "fastmcp";
2
+ export declare function registerDesignSurveyPrompt(server: FastMCP): void;
3
+ //# sourceMappingURL=design-survey.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"design-survey.d.ts","sourceRoot":"","sources":["../../src/prompts/design-survey.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvC,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,OAAO,QAmGzD"}
@@ -0,0 +1,98 @@
1
+ export function registerDesignSurveyPrompt(server) {
2
+ server.addPrompt({
3
+ name: "design_survey",
4
+ description: "Guide an AI agent through survey creation best practices for the Chorus Research platform. Produces a valid SurveyJS JSON definition.",
5
+ arguments: [
6
+ {
7
+ name: "topic",
8
+ description: "The research topic or objective for the survey",
9
+ required: true,
10
+ },
11
+ {
12
+ name: "audience",
13
+ description: 'Target audience description (e.g., "US adults 18+", "parents with young children")',
14
+ required: false,
15
+ },
16
+ {
17
+ name: "numQuestions",
18
+ description: "Approximate number of questions desired (default: 10)",
19
+ required: false,
20
+ },
21
+ ],
22
+ async load({ topic, audience, numQuestions }) {
23
+ const count = numQuestions || "10";
24
+ const aud = audience || "general population (US adults 18+)";
25
+ return `You are designing a survey for the Chorus Research platform. Follow these best practices:
26
+
27
+ ## Research Objective
28
+ ${topic}
29
+
30
+ ## Target Audience
31
+ ${aud}
32
+
33
+ ## Guidelines
34
+
35
+ ### Survey Structure
36
+ - Target approximately ${count} questions
37
+ - Keep the survey under 15 minutes to avoid LOI cost multipliers (>15 min = 20% cost increase)
38
+ - Group related questions onto the same page
39
+ - Start with easy, engaging questions before sensitive topics
40
+ - Use a logical flow — general to specific
41
+ - Survey questions are limited to 10, server will reject more.
42
+ - There must be at least 1 question for a survey to launch
43
+
44
+ ### Question Types Available
45
+ - **text**: Short open-ended (use inputType: "text", "email", "number", "date")
46
+ - **comment**: Long open-ended (multi-line textarea)
47
+ - **radiogroup**: Single-select from a list
48
+ - **checkbox**: Multi-select from a list
49
+ - **dropdown**: Single-select dropdown (good for long lists)
50
+ - **tagbox**: Multi-select searchable dropdown
51
+ - **boolean**: Yes/No toggle
52
+ - **rating**: Numeric or star scale
53
+ - **ranking**: Drag-and-drop ordering
54
+
55
+ ### Best Practices
56
+ - Every input question should have \`"isRequired": true\` (auto-enforced at publish)
57
+ - Use clear, unbiased question wording
58
+ - Avoid double-barreled questions (asking two things at once)
59
+ - Include "Prefer not to answer" or "Other" options for sensitive questions
60
+ - Use \`visibleIf\` for conditional logic (e.g., show follow-up only if parent answer matches)
61
+ - Keep choice lists to 7±2 items when possible
62
+ - Use consistent scale directions (e.g., always low-to-high)
63
+
64
+ ### Survey JSON Format
65
+ The output must be a valid JSON object with this structure:
66
+ \`\`\`json
67
+ {
68
+ "title": "Survey Title",
69
+ "description": "Brief description",
70
+ "pages": [
71
+ {
72
+ "name": "page1",
73
+ "title": "Page Title",
74
+ "elements": [
75
+ {
76
+ "type": "radiogroup",
77
+ "name": "q1_unique_name",
78
+ "title": "Question text here?",
79
+ "isRequired": true,
80
+ "choices": ["Option 1", "Option 2", "Option 3"]
81
+ }
82
+ ]
83
+ }
84
+ ]
85
+ }
86
+ \`\`\`
87
+
88
+ ### Constraints
89
+ - Maximum 10 pages, 50 elements per page
90
+ - Maximum 5 MB total survey definition
91
+ - All question \`name\` fields must be unique within the survey
92
+ - URLs in questions must be HTTP/HTTPS
93
+
94
+ Please create the complete SurveyJS JSON for this survey, then use the \`create_survey\` tool to save it as a draft.`;
95
+ },
96
+ });
97
+ }
98
+ //# sourceMappingURL=design-survey.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"design-survey.js","sourceRoot":"","sources":["../../src/prompts/design-survey.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,0BAA0B,CAAC,MAAe;IACxD,MAAM,CAAC,SAAS,CAAC;QACf,IAAI,EAAE,eAAe;QACrB,WAAW,EACT,uIAAuI;QACzI,SAAS,EAAE;YACT;gBACE,IAAI,EAAE,OAAO;gBACb,WAAW,EAAE,gDAAgD;gBAC7D,QAAQ,EAAE,IAAI;aACf;YACD;gBACE,IAAI,EAAE,UAAU;gBAChB,WAAW,EACT,oFAAoF;gBACtF,QAAQ,EAAE,KAAK;aAChB;YACD;gBACE,IAAI,EAAE,cAAc;gBACpB,WAAW,EAAE,uDAAuD;gBACpE,QAAQ,EAAE,KAAK;aAChB;SACF;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE;YAC1C,MAAM,KAAK,GAAG,YAAY,IAAI,IAAI,CAAC;YACnC,MAAM,GAAG,GAAG,QAAQ,IAAI,oCAAoC,CAAC;YAE7D,OAAO;;;EAGX,KAAK;;;EAGL,GAAG;;;;;yBAKoB,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qHA0DuF,CAAC;QAClH,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { FastMCP } from "fastmcp";
2
+ export declare function registerLaunchChecklistPrompt(server: FastMCP): void;
3
+ //# sourceMappingURL=launch-checklist.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launch-checklist.d.ts","sourceRoot":"","sources":["../../src/prompts/launch-checklist.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvC,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,OAAO,QAyE5D"}
@@ -0,0 +1,74 @@
1
+ export function registerLaunchChecklistPrompt(server) {
2
+ server.addPrompt({
3
+ name: "launch_checklist",
4
+ description: "Pre-launch validation checklist. Guides an AI agent through verifying a survey is ready to launch, estimating costs, and confirming with the user before charging.",
5
+ arguments: [
6
+ {
7
+ name: "surveyId",
8
+ description: "The UUID of the survey to validate for launch",
9
+ required: true,
10
+ },
11
+ ],
12
+ async load({ surveyId }) {
13
+ return `You are preparing to launch survey \`${surveyId}\` on the Chorus Research platform. Follow this checklist carefully:
14
+
15
+ ## Pre-Launch Checklist
16
+
17
+ ### 1. Retrieve & Review Survey
18
+ - Use \`get_survey\` to fetch the full survey definition
19
+ - Verify the survey is in **draft** status
20
+ - Check that it has a descriptive name
21
+ - Review all questions for clarity and completeness
22
+
23
+ ### 2. Validate Survey Quality
24
+ - [ ] All questions have clear, unbiased wording
25
+ - [ ] Required questions are marked \`isRequired: true\`
26
+ - [ ] Conditional logic (\`visibleIf\`) is correct
27
+ - [ ] No duplicate question names
28
+ - [ ] Reasonable number of pages/questions
29
+ - [ ] Estimated completion time is reasonable (aim for <15 minutes)
30
+ - [ ] Surveys are 10 questions or less
31
+
32
+ ### 3. Choose Audience
33
+ - Use \`list_audiences\` to see available segments
34
+ - Select the most appropriate audience for the research objective
35
+ - Note the audience's \`minTargetCompletes\` and \`maxTargetCompletes\`
36
+
37
+ ### 4. Estimate Cost
38
+ - Use \`estimate_cost\` with the chosen audience and target completes
39
+ - Review the cost breakdown:
40
+ - Base cost per complete ($1.00 for General Population)
41
+ - Total cost (baseCostPerComplete × targetCompletes)
42
+ - If cost seems high, consider reducing target completes
43
+
44
+ ### 5. Present Summary to User
45
+ Show the user:
46
+ - Survey name and question count
47
+ - Target audience
48
+ - Target completes
49
+ - **Total estimated cost (prominently displayed)**
50
+ - Estimated fielding time
51
+
52
+ ### 6. Get Explicit Confirmation
53
+ **CRITICAL**: You MUST get explicit user confirmation before proceeding.
54
+ Say something like: "This will charge approximately $X to your account. Shall I proceed with the launch?"
55
+
56
+ ### 7. Execute Launch (Two-Step)
57
+ Only after user confirms:
58
+ 1. Call \`launch_survey\` with \`surveyId\`, \`audienceId\`, and \`targetCompletes\` → get estimate + \`confirmToken\`
59
+ 2. Call \`launch_survey\` again with \`surveyId\` and \`confirmToken\` → execute launch
60
+
61
+ ### 8. Verify Launch
62
+ - Confirm the launch response shows \`success: true\`
63
+ - Note the \`workflowId\` for status tracking
64
+ - Use \`get_survey_status\` to verify the survey is now **live**
65
+
66
+ ## Important Reminders
67
+ - The confirmToken expires in **5 minutes** — don't delay between steps
68
+ - Launch tokens are single-use (replay attack prevention)
69
+ - Once launched, the survey CANNOT be edited
70
+ - Charges are processed immediately upon confirmation`;
71
+ },
72
+ });
73
+ }
74
+ //# sourceMappingURL=launch-checklist.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launch-checklist.js","sourceRoot":"","sources":["../../src/prompts/launch-checklist.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,6BAA6B,CAAC,MAAe;IAC3D,MAAM,CAAC,SAAS,CAAC;QACf,IAAI,EAAE,kBAAkB;QACxB,WAAW,EACT,oKAAoK;QACtK,SAAS,EAAE;YACT;gBACE,IAAI,EAAE,UAAU;gBAChB,WAAW,EAAE,+CAA+C;gBAC5D,QAAQ,EAAE,IAAI;aACf;SACF;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE;YACrB,OAAO,wCAAwC,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sDAyDP,CAAC;QACnD,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { FastMCP } from "fastmcp";
2
+ import type { ChorusApiClient } from "../client.js";
3
+ export declare function registerAudienceResources(server: FastMCP, api: ChorusApiClient): void;
4
+ //# sourceMappingURL=audiences.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audiences.d.ts","sourceRoot":"","sources":["../../src/resources/audiences.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,OAAO,EACf,GAAG,EAAE,eAAe,QAarB"}
@@ -0,0 +1,13 @@
1
+ export function registerAudienceResources(server, api) {
2
+ server.addResource({
3
+ uri: "chorus://audiences",
4
+ name: "Audience Catalog",
5
+ description: "Full catalog of available audience segments with targeting criteria, costs, and estimated fielding times.",
6
+ mimeType: "application/json",
7
+ async load() {
8
+ const result = await api.listAudiences();
9
+ return { text: JSON.stringify(result, null, 2) };
10
+ },
11
+ });
12
+ }
13
+ //# sourceMappingURL=audiences.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audiences.js","sourceRoot":"","sources":["../../src/resources/audiences.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,yBAAyB,CACvC,MAAe,EACf,GAAoB;IAEpB,MAAM,CAAC,WAAW,CAAC;QACjB,GAAG,EAAE,oBAAoB;QACzB,IAAI,EAAE,kBAAkB;QACxB,WAAW,EACT,2GAA2G;QAC7G,QAAQ,EAAE,kBAAkB;QAC5B,KAAK,CAAC,IAAI;YACR,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,aAAa,EAAE,CAAC;YACzC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;QACnD,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { FastMCP } from "fastmcp";
2
+ import type { ChorusApiClient } from "../client.js";
3
+ export declare function registerPricingResources(server: FastMCP, api: ChorusApiClient): void;
4
+ //# sourceMappingURL=pricing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pricing.d.ts","sourceRoot":"","sources":["../../src/resources/pricing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,OAAO,EACf,GAAG,EAAE,eAAe,QAarB"}
@@ -0,0 +1,13 @@
1
+ export function registerPricingResources(server, api) {
2
+ server.addResource({
3
+ uri: "chorus://pricing/config",
4
+ name: "Pricing Configuration",
5
+ description: "Current pricing configuration including cost per response and currency.",
6
+ mimeType: "application/json",
7
+ async load() {
8
+ const config = await api.getPricingConfig();
9
+ return { text: JSON.stringify(config, null, 2) };
10
+ },
11
+ });
12
+ }
13
+ //# sourceMappingURL=pricing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pricing.js","sourceRoot":"","sources":["../../src/resources/pricing.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,wBAAwB,CACtC,MAAe,EACf,GAAoB;IAEpB,MAAM,CAAC,WAAW,CAAC;QACjB,GAAG,EAAE,yBAAyB;QAC9B,IAAI,EAAE,uBAAuB;QAC7B,WAAW,EACT,yEAAyE;QAC3E,QAAQ,EAAE,kBAAkB;QAC5B,KAAK,CAAC,IAAI;YACR,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,gBAAgB,EAAE,CAAC;YAC5C,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;QACnD,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { FastMCP } from "fastmcp";
2
+ import type { ChorusApiClient } from "../client.js";
3
+ export declare function registerAudienceTools(server: FastMCP, api: ChorusApiClient): void;
4
+ //# sourceMappingURL=audiences.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audiences.d.ts","sourceRoot":"","sources":["../../src/tools/audiences.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,eAAe,QA2B1E"}
@@ -0,0 +1,27 @@
1
+ import { z } from "zod";
2
+ export function registerAudienceTools(server, api) {
3
+ server.addTool({
4
+ name: "list_audiences",
5
+ description: "List all available audience segments for survey targeting. Each audience includes cost per response, targeting criteria, and estimated fielding times.",
6
+ annotations: { readOnlyHint: true, destructiveHint: false },
7
+ execute: async () => {
8
+ const result = await api.listAudiences();
9
+ return JSON.stringify(result, null, 2);
10
+ },
11
+ });
12
+ server.addTool({
13
+ name: "get_audience",
14
+ description: "Get detailed information about a specific audience segment including targeting criteria, cost, and fielding time estimates.",
15
+ annotations: { readOnlyHint: true, destructiveHint: false },
16
+ parameters: z.object({
17
+ audienceId: z
18
+ .string()
19
+ .describe('Audience identifier (e.g., "genpop")'),
20
+ }),
21
+ execute: async ({ audienceId }) => {
22
+ const audience = await api.getAudience(audienceId);
23
+ return JSON.stringify(audience, null, 2);
24
+ },
25
+ });
26
+ }
27
+ //# sourceMappingURL=audiences.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audiences.js","sourceRoot":"","sources":["../../src/tools/audiences.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,UAAU,qBAAqB,CAAC,MAAe,EAAE,GAAoB;IACzE,MAAM,CAAC,OAAO,CAAC;QACb,IAAI,EAAE,gBAAgB;QACtB,WAAW,EACT,wJAAwJ;QAC1J,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE;QAC3D,OAAO,EAAE,KAAK,IAAI,EAAE;YAClB,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,aAAa,EAAE,CAAC;YACzC,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACzC,CAAC;KACF,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,CAAC;QACb,IAAI,EAAE,cAAc;QACpB,WAAW,EACT,6HAA6H;QAC/H,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE;QAC3D,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC;YACnB,UAAU,EAAE,CAAC;iBACV,MAAM,EAAE;iBACR,QAAQ,CAAC,sCAAsC,CAAC;SACpD,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;YAChC,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;YACnD,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAC3C,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { FastMCP } from "fastmcp";
2
+ import type { ChorusApiClient } from "../client.js";
3
+ export declare function registerLaunchTools(server: FastMCP, api: ChorusApiClient): void;
4
+ //# sourceMappingURL=launch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launch.d.ts","sourceRoot":"","sources":["../../src/tools/launch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAGvC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,eAAe,QAwGxE"}
@@ -0,0 +1,87 @@
1
+ import { UserError } from "fastmcp";
2
+ import { z } from "zod";
3
+ export function registerLaunchTools(server, api) {
4
+ server.addTool({
5
+ name: "launch_survey",
6
+ description: `Launch a survey to collect responses. This is a TWO-STEP process to prevent accidental charges:
7
+
8
+ Step 1 (estimate): Call with surveyId, audienceId, and targetCompletes. Returns a cost estimate and a confirmToken (valid 5 minutes).
9
+
10
+ Step 2 (confirm): Call again with surveyId and the confirmToken from step 1 to execute the launch and charge payment.
11
+
12
+ IMPORTANT: Step 2 triggers real charges and starts a Temporal workflow for fielding. Always show the cost estimate to the user and get explicit confirmation before proceeding to step 2.`,
13
+ annotations: {
14
+ readOnlyHint: false,
15
+ destructiveHint: false,
16
+ idempotentHint: false,
17
+ },
18
+ parameters: z.object({
19
+ surveyId: z.string().uuid().describe("Survey ID to launch"),
20
+ // Step 1 params
21
+ audienceId: z
22
+ .string()
23
+ .optional()
24
+ .describe('Step 1: Audience segment ID (e.g., "genpop"). Required for estimate step.'),
25
+ targetCompletes: z
26
+ .number()
27
+ .int()
28
+ .min(0)
29
+ .max(5000)
30
+ .optional()
31
+ .describe("Step 1: Number of completed responses desired (0-5000, raise min for production). Required for estimate step."),
32
+ // Step 2 params
33
+ confirmToken: z
34
+ .string()
35
+ .optional()
36
+ .describe("Step 2: Confirmation token from the estimate step. Provide this to execute the launch."),
37
+ paymentMethodId: z
38
+ .string()
39
+ .optional()
40
+ .describe("Step 2: Optional Stripe payment method ID."),
41
+ }),
42
+ execute: async (args) => {
43
+ const { surveyId, audienceId, targetCompletes, confirmToken, paymentMethodId } = args;
44
+ // Step 2: Confirm launch
45
+ if (confirmToken) {
46
+ const result = await api.launchConfirm(surveyId, {
47
+ confirmToken,
48
+ paymentMethodId,
49
+ });
50
+ return JSON.stringify({
51
+ step: "confirmed",
52
+ message: `Survey launched successfully. Workflow ${result.workflowId} is now fielding ${result.targetCompletes} responses. Amount charged: $${result.amountCharged} ${result.currency}.`,
53
+ ...result,
54
+ }, null, 2);
55
+ }
56
+ // Step 1: Get estimate
57
+ if (!audienceId || !targetCompletes) {
58
+ throw new UserError("For step 1 (estimate), both audienceId and targetCompletes are required. For step 2 (confirm), provide the confirmToken from step 1.");
59
+ }
60
+ const estimate = await api.launchEstimate(surveyId, {
61
+ audienceId,
62
+ targetCompletes,
63
+ });
64
+ return JSON.stringify({
65
+ step: "estimate",
66
+ message: `Estimated cost: $${estimate.estimatedCost} USD. The confirmToken expires at ${estimate.expiresAt}. Call launch_survey again with the confirmToken to execute the launch.`,
67
+ ...estimate,
68
+ }, null, 2);
69
+ },
70
+ });
71
+ server.addTool({
72
+ name: "get_survey_status",
73
+ description: "Get the real-time fielding status of a survey including completion count, progress percentage, and estimated completion time. Results are cached for 30 seconds.",
74
+ annotations: { readOnlyHint: true, destructiveHint: false },
75
+ parameters: z.object({
76
+ surveyId: z
77
+ .string()
78
+ .uuid()
79
+ .describe("Survey ID to check status for"),
80
+ }),
81
+ execute: async ({ surveyId }) => {
82
+ const status = await api.getSurveyStatus(surveyId);
83
+ return JSON.stringify(status, null, 2);
84
+ },
85
+ });
86
+ }
87
+ //# sourceMappingURL=launch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launch.js","sourceRoot":"","sources":["../../src/tools/launch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { FastMCP } from "fastmcp";
2
+ import type { ChorusApiClient } from "../client.js";
3
+ export declare function registerPricingTools(server: FastMCP, api: ChorusApiClient): void;
4
+ //# sourceMappingURL=pricing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pricing.d.ts","sourceRoot":"","sources":["../../src/tools/pricing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,eAAe,QAsCzE"}
@@ -0,0 +1,38 @@
1
+ import { z } from "zod";
2
+ export function registerPricingTools(server, api) {
3
+ server.addTool({
4
+ name: "estimate_cost",
5
+ description: "Calculate the cost to launch a survey to a specific audience. Returns cost breakdown: baseCostPerComplete × targetCompletes. Use this before launching to understand pricing.",
6
+ annotations: { readOnlyHint: true, destructiveHint: false },
7
+ parameters: z.object({
8
+ audienceId: z
9
+ .string()
10
+ .describe('Audience segment ID (e.g., "genpop")'),
11
+ targetCompletes: z
12
+ .number()
13
+ .int()
14
+ .min(0)
15
+ .max(5000)
16
+ .describe("Number of completed responses desired (0-5000, raise min for production)"),
17
+ projectId: z
18
+ .string()
19
+ .uuid()
20
+ .optional()
21
+ .describe("Optional survey project ID for LOI auto-calculation"),
22
+ }),
23
+ execute: async (args) => {
24
+ const estimate = await api.estimateCost(args);
25
+ return JSON.stringify(estimate, null, 2);
26
+ },
27
+ });
28
+ server.addTool({
29
+ name: "get_pricing_config",
30
+ description: "Get the current pricing configuration including cost per response and currency. Useful for understanding how costs are calculated.",
31
+ annotations: { readOnlyHint: true, destructiveHint: false },
32
+ execute: async () => {
33
+ const config = await api.getPricingConfig();
34
+ return JSON.stringify(config, null, 2);
35
+ },
36
+ });
37
+ }
38
+ //# sourceMappingURL=pricing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pricing.js","sourceRoot":"","sources":["../../src/tools/pricing.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,UAAU,oBAAoB,CAAC,MAAe,EAAE,GAAoB;IACxE,MAAM,CAAC,OAAO,CAAC;QACb,IAAI,EAAE,eAAe;QACrB,WAAW,EACT,+KAA+K;QACjL,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE;QAC3D,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC;YACnB,UAAU,EAAE,CAAC;iBACV,MAAM,EAAE;iBACR,QAAQ,CAAC,sCAAsC,CAAC;YACnD,eAAe,EAAE,CAAC;iBACf,MAAM,EAAE;iBACR,GAAG,EAAE;iBACL,GAAG,CAAC,CAAC,CAAC;iBACN,GAAG,CAAC,IAAI,CAAC;iBACT,QAAQ,CAAC,0EAA0E,CAAC;YACvF,SAAS,EAAE,CAAC;iBACT,MAAM,EAAE;iBACR,IAAI,EAAE;iBACN,QAAQ,EAAE;iBACV,QAAQ,CAAC,qDAAqD,CAAC;SACnE,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YACtB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;YAC9C,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAC3C,CAAC;KACF,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,CAAC;QACb,IAAI,EAAE,oBAAoB;QAC1B,WAAW,EACT,oIAAoI;QACtI,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE;QAC3D,OAAO,EAAE,KAAK,IAAI,EAAE;YAClB,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,gBAAgB,EAAE,CAAC;YAC5C,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACzC,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { FastMCP } from "fastmcp";
2
+ import type { ChorusApiClient } from "../client.js";
3
+ export declare function registerResponseTools(server: FastMCP, api: ChorusApiClient): void;
4
+ //# sourceMappingURL=responses.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"responses.d.ts","sourceRoot":"","sources":["../../src/tools/responses.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,eAAe,QAwC1E"}
@@ -0,0 +1,40 @@
1
+ import { z } from "zod";
2
+ export function registerResponseTools(server, api) {
3
+ server.addTool({
4
+ name: "get_responses",
5
+ description: "Export survey response data with pagination. Returns individual responses including answer data, metadata, and completion status. Only returns responses from published (launched) surveys. Default filter is 'complete' responses only. Duplicate respondents are excluded by default; set includeDuplicates=true to include them. Each response includes a supplier field indicating the panel source. Metadata includes device info (userAgent, screenSize, timezone, language), geographic data (city, state, country), and provider demographics when available.",
6
+ annotations: { readOnlyHint: true, destructiveHint: false },
7
+ parameters: z.object({
8
+ surveyId: z
9
+ .string()
10
+ .uuid()
11
+ .describe("Survey ID to export responses for"),
12
+ page: z
13
+ .number()
14
+ .int()
15
+ .positive()
16
+ .optional()
17
+ .describe("Page number (default 1)"),
18
+ pageSize: z
19
+ .number()
20
+ .int()
21
+ .min(1)
22
+ .max(1000)
23
+ .optional()
24
+ .describe("Results per page (default 100, max 1000)"),
25
+ status: z
26
+ .enum(["complete", "partial", "incomplete"])
27
+ .optional()
28
+ .describe("Filter by response status (default: complete)"),
29
+ includeDuplicates: z
30
+ .boolean()
31
+ .optional()
32
+ .describe("Include duplicate/flagged responses (default: false). By default, responses flagged as duplicates are excluded for clean data export."),
33
+ }),
34
+ execute: async ({ surveyId, ...params }) => {
35
+ const result = await api.getResponses(surveyId, params);
36
+ return JSON.stringify(result, null, 2);
37
+ },
38
+ });
39
+ }
40
+ //# sourceMappingURL=responses.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"responses.js","sourceRoot":"","sources":["../../src/tools/responses.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,UAAU,qBAAqB,CAAC,MAAe,EAAE,GAAoB;IACzE,MAAM,CAAC,OAAO,CAAC;QACb,IAAI,EAAE,eAAe;QACrB,WAAW,EACT,uiBAAuiB;QACziB,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE;QAC3D,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC;YACnB,QAAQ,EAAE,CAAC;iBACR,MAAM,EAAE;iBACR,IAAI,EAAE;iBACN,QAAQ,CAAC,mCAAmC,CAAC;YAChD,IAAI,EAAE,CAAC;iBACJ,MAAM,EAAE;iBACR,GAAG,EAAE;iBACL,QAAQ,EAAE;iBACV,QAAQ,EAAE;iBACV,QAAQ,CAAC,yBAAyB,CAAC;YACtC,QAAQ,EAAE,CAAC;iBACR,MAAM,EAAE;iBACR,GAAG,EAAE;iBACL,GAAG,CAAC,CAAC,CAAC;iBACN,GAAG,CAAC,IAAI,CAAC;iBACT,QAAQ,EAAE;iBACV,QAAQ,CAAC,0CAA0C,CAAC;YACvD,MAAM,EAAE,CAAC;iBACN,IAAI,CAAC,CAAC,UAAU,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;iBAC3C,QAAQ,EAAE;iBACV,QAAQ,CAAC,+CAA+C,CAAC;YAC5D,iBAAiB,EAAE,CAAC;iBACjB,OAAO,EAAE;iBACT,QAAQ,EAAE;iBACV,QAAQ,CACP,uIAAuI,CACxI;SACJ,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,GAAG,MAAM,EAAE,EAAE,EAAE;YACzC,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YACxD,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACzC,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { FastMCP } from "fastmcp";
2
+ import type { ChorusApiClient } from "../client.js";
3
+ export declare function registerSurveyTools(server: FastMCP, api: ChorusApiClient): void;
4
+ //# sourceMappingURL=surveys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"surveys.d.ts","sourceRoot":"","sources":["../../src/tools/surveys.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,eAAe,QA8ExE"}
@@ -0,0 +1,74 @@
1
+ import { z } from "zod";
2
+ export function registerSurveyTools(server, api) {
3
+ server.addTool({
4
+ name: "list_surveys",
5
+ description: "List surveys with optional filtering by status and pagination. Returns survey metadata (id, name, status, dates) but not the full JSON.",
6
+ annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true },
7
+ parameters: z.object({
8
+ status: z
9
+ .enum(["draft", "live", "complete", "cancelled"])
10
+ .optional()
11
+ .describe("Filter by survey status"),
12
+ page: z.number().int().positive().optional().describe("Page number (default 1)"),
13
+ pageSize: z
14
+ .number()
15
+ .int()
16
+ .min(1)
17
+ .max(100)
18
+ .optional()
19
+ .describe("Results per page (default 20, max 100)"),
20
+ sort: z
21
+ .enum(["createdAt", "updatedAt", "name"])
22
+ .optional()
23
+ .describe("Sort field"),
24
+ order: z.enum(["asc", "desc"]).optional().describe("Sort order"),
25
+ }),
26
+ execute: async (args) => {
27
+ const result = await api.listSurveys(args);
28
+ return JSON.stringify(result, null, 2);
29
+ },
30
+ });
31
+ server.addTool({
32
+ name: "get_survey",
33
+ description: "Get full details of a specific survey including the complete JSON definition, status, and metadata.",
34
+ annotations: { readOnlyHint: true, destructiveHint: false },
35
+ parameters: z.object({
36
+ surveyId: z.string().uuid().describe("Survey ID (UUID)"),
37
+ }),
38
+ execute: async ({ surveyId }) => {
39
+ const survey = await api.getSurvey(surveyId);
40
+ return JSON.stringify(survey, null, 2);
41
+ },
42
+ });
43
+ server.addTool({
44
+ name: "create_survey",
45
+ description: "Create a new survey draft. Provide a name, the JSON definition, and a target number of completed responses. The survey will be created in 'draft' status.",
46
+ annotations: { readOnlyHint: false, destructiveHint: false },
47
+ parameters: z.object({
48
+ name: z
49
+ .string()
50
+ .min(1)
51
+ .max(255)
52
+ .describe("Survey name (1-255 characters)"),
53
+ survey: z
54
+ .record(z.unknown())
55
+ .describe("JSON definition. Must include `pages` array with question elements."),
56
+ targetCompletes: z
57
+ .number()
58
+ .int()
59
+ .min(0)
60
+ .max(5000)
61
+ .describe("Target number of completed responses (0-5000, raise min for production)"),
62
+ description: z
63
+ .string()
64
+ .max(1000)
65
+ .optional()
66
+ .describe("Optional survey description (max 1000 chars)"),
67
+ }),
68
+ execute: async (args) => {
69
+ const survey = await api.createSurvey(args);
70
+ return JSON.stringify(survey, null, 2);
71
+ },
72
+ });
73
+ }
74
+ //# sourceMappingURL=surveys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"surveys.js","sourceRoot":"","sources":["../../src/tools/surveys.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,UAAU,mBAAmB,CAAC,MAAe,EAAE,GAAoB;IACvE,MAAM,CAAC,OAAO,CAAC;QACb,IAAI,EAAE,cAAc;QACpB,WAAW,EACT,yIAAyI;QAC3I,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE;QAChF,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC;YACnB,MAAM,EAAE,CAAC;iBACN,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;iBAChD,QAAQ,EAAE;iBACV,QAAQ,CAAC,yBAAyB,CAAC;YACtC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,yBAAyB,CAAC;YAChF,QAAQ,EAAE,CAAC;iBACR,MAAM,EAAE;iBACR,GAAG,EAAE;iBACL,GAAG,CAAC,CAAC,CAAC;iBACN,GAAG,CAAC,GAAG,CAAC;iBACR,QAAQ,EAAE;iBACV,QAAQ,CAAC,wCAAwC,CAAC;YACrD,IAAI,EAAE,CAAC;iBACJ,IAAI,CAAC,CAAC,WAAW,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;iBACxC,QAAQ,EAAE;iBACV,QAAQ,CAAC,YAAY,CAAC;YACzB,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC;SACjE,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YACtB,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YAC3C,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACzC,CAAC;KACF,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,CAAC;QACb,IAAI,EAAE,YAAY;QAClB,WAAW,EACT,qGAAqG;QACvG,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE;QAC3D,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC;YACnB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,kBAAkB,CAAC;SACzD,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;YAC9B,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;YAC7C,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACzC,CAAC;KACF,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,CAAC;QACb,IAAI,EAAE,eAAe;QACrB,WAAW,EACT,2JAA2J;QAC7J,WAAW,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE;QAC5D,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC;YACnB,IAAI,EAAE,CAAC;iBACJ,MAAM,EAAE;iBACR,GAAG,CAAC,CAAC,CAAC;iBACN,GAAG,CAAC,GAAG,CAAC;iBACR,QAAQ,CAAC,gCAAgC,CAAC;YAC7C,MAAM,EAAE,CAAC;iBACN,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;iBACnB,QAAQ,CACP,qEAAqE,CACtE;YACH,eAAe,EAAE,CAAC;iBACf,MAAM,EAAE;iBACR,GAAG,EAAE;iBACL,GAAG,CAAC,CAAC,CAAC;iBACN,GAAG,CAAC,IAAI,CAAC;iBACT,QAAQ,CAAC,yEAAyE,CAAC;YACtF,WAAW,EAAE,CAAC;iBACX,MAAM,EAAE;iBACR,GAAG,CAAC,IAAI,CAAC;iBACT,QAAQ,EAAE;iBACV,QAAQ,CAAC,8CAA8C,CAAC;SAC5D,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YACtB,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;YAC5C,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACzC,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@chorus-research/mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for the Chorus Research survey platform",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "chorus-research-mcp": "dist/index.js",
9
+ "chorus-mcp": "dist/index.js"
10
+ },
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "start": "node dist/index.js",
14
+ "dev": "tsx src/index.ts",
15
+ "inspect": "npx fastmcp inspect src/index.ts",
16
+ "test": "vitest run",
17
+ "test:watch": "vitest"
18
+ },
19
+ "dependencies": {
20
+ "fastmcp": "^3.34.0",
21
+ "zod": "^3.23.8"
22
+ },
23
+ "devDependencies": {
24
+ "@types/node": "^22.10.0",
25
+ "tsx": "^4.19.0",
26
+ "typescript": "^5.7.0",
27
+ "vitest": "^3.0.0"
28
+ },
29
+ "engines": {
30
+ "node": ">=20"
31
+ },
32
+ "files": [
33
+ "dist"
34
+ ],
35
+ "keywords": [
36
+ "mcp",
37
+ "survey",
38
+ "research",
39
+ "chorus"
40
+ ],
41
+ "license": "Proprietary"
42
+ }