@ascendkit/cli 0.1.10

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 (48) hide show
  1. package/LICENSE +21 -0
  2. package/dist/api/client.d.ts +34 -0
  3. package/dist/api/client.js +155 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +1153 -0
  6. package/dist/commands/auth.d.ts +17 -0
  7. package/dist/commands/auth.js +29 -0
  8. package/dist/commands/content.d.ts +25 -0
  9. package/dist/commands/content.js +28 -0
  10. package/dist/commands/email.d.ts +37 -0
  11. package/dist/commands/email.js +39 -0
  12. package/dist/commands/journeys.d.ts +86 -0
  13. package/dist/commands/journeys.js +69 -0
  14. package/dist/commands/platform.d.ts +35 -0
  15. package/dist/commands/platform.js +517 -0
  16. package/dist/commands/surveys.d.ts +51 -0
  17. package/dist/commands/surveys.js +41 -0
  18. package/dist/commands/webhooks.d.ts +16 -0
  19. package/dist/commands/webhooks.js +28 -0
  20. package/dist/index.d.ts +2 -0
  21. package/dist/index.js +29 -0
  22. package/dist/mcp.d.ts +2 -0
  23. package/dist/mcp.js +40 -0
  24. package/dist/tools/auth.d.ts +3 -0
  25. package/dist/tools/auth.js +75 -0
  26. package/dist/tools/content.d.ts +3 -0
  27. package/dist/tools/content.js +64 -0
  28. package/dist/tools/email.d.ts +3 -0
  29. package/dist/tools/email.js +57 -0
  30. package/dist/tools/journeys.d.ts +3 -0
  31. package/dist/tools/journeys.js +302 -0
  32. package/dist/tools/platform.d.ts +3 -0
  33. package/dist/tools/platform.js +63 -0
  34. package/dist/tools/surveys.d.ts +3 -0
  35. package/dist/tools/surveys.js +212 -0
  36. package/dist/tools/webhooks.d.ts +3 -0
  37. package/dist/tools/webhooks.js +56 -0
  38. package/dist/types.d.ts +96 -0
  39. package/dist/types.js +4 -0
  40. package/dist/utils/credentials.d.ts +27 -0
  41. package/dist/utils/credentials.js +90 -0
  42. package/dist/utils/duration.d.ts +16 -0
  43. package/dist/utils/duration.js +47 -0
  44. package/dist/utils/journey-format.d.ts +112 -0
  45. package/dist/utils/journey-format.js +200 -0
  46. package/dist/utils/survey-format.d.ts +60 -0
  47. package/dist/utils/survey-format.js +164 -0
  48. package/package.json +37 -0
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Human-readable formatting for journey data.
3
+ * Used by MCP tools to present journey definitions and analytics conversationally.
4
+ */
5
+ interface JourneyData {
6
+ id: string;
7
+ name: string;
8
+ description?: string;
9
+ status: string;
10
+ entryEvent: string;
11
+ entryNode: string;
12
+ reEntryPolicy?: string;
13
+ nodes: Record<string, {
14
+ action?: {
15
+ type?: string;
16
+ };
17
+ terminal?: boolean;
18
+ }>;
19
+ transitions?: Array<{
20
+ from?: string;
21
+ from_?: string;
22
+ to?: string;
23
+ trigger?: {
24
+ type?: string;
25
+ event?: string;
26
+ delay?: string;
27
+ };
28
+ priority?: number;
29
+ }>;
30
+ stats?: {
31
+ totalEnrolled?: number;
32
+ currentlyActive?: number;
33
+ completed?: number;
34
+ };
35
+ version?: number;
36
+ warnings?: string[];
37
+ createdAt?: string;
38
+ updatedAt?: string;
39
+ }
40
+ interface AnalyticsData {
41
+ journey: {
42
+ id: string;
43
+ name: string;
44
+ status: string;
45
+ };
46
+ nodes: Array<{
47
+ name: string;
48
+ action: string;
49
+ terminal: boolean;
50
+ userCount: number;
51
+ medianTimeHours: number;
52
+ }>;
53
+ transitions: Array<{
54
+ from: string;
55
+ to: string;
56
+ type: string;
57
+ event?: string;
58
+ delay?: string;
59
+ userCount: number;
60
+ }>;
61
+ terminalDistribution: Array<{
62
+ node: string;
63
+ count: number;
64
+ }>;
65
+ totals: {
66
+ enrolled: number;
67
+ active: number;
68
+ completed: number;
69
+ failed: number;
70
+ exited?: number;
71
+ };
72
+ }
73
+ export declare function formatJourneyWithGuidance(journey: JourneyData): string;
74
+ export declare function formatJourneyList(data: {
75
+ journeys: JourneyData[];
76
+ total: number;
77
+ }): string;
78
+ interface NodeListItem {
79
+ name: string;
80
+ action: {
81
+ type?: string;
82
+ templateSlug?: string;
83
+ surveySlug?: string;
84
+ tagName?: string;
85
+ stageName?: string;
86
+ };
87
+ terminal: boolean;
88
+ isEntryNode: boolean;
89
+ outgoingTransitions: number;
90
+ incomingTransitions: number;
91
+ }
92
+ interface TransitionListItem {
93
+ name: string;
94
+ from: string;
95
+ to: string;
96
+ trigger: {
97
+ type?: string;
98
+ event?: string;
99
+ delay?: string;
100
+ };
101
+ priority?: number;
102
+ }
103
+ export declare function formatNodeList(data: {
104
+ nodes: NodeListItem[];
105
+ }): string;
106
+ export declare function formatSingleNode(data: JourneyData, action: string, nodeName: string): string;
107
+ export declare function formatTransitionList(data: {
108
+ transitions: TransitionListItem[];
109
+ }): string;
110
+ export declare function formatSingleTransition(data: JourneyData, action: string, transitionName: string): string;
111
+ export declare function formatJourneyAnalytics(data: AnalyticsData): string;
112
+ export {};
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Human-readable formatting for journey data.
3
+ * Used by MCP tools to present journey definitions and analytics conversationally.
4
+ */
5
+ const STATUS_ICONS = {
6
+ draft: "draft",
7
+ active: "active",
8
+ paused: "paused",
9
+ archived: "archived",
10
+ };
11
+ function nodeCount(journey) {
12
+ return Object.keys(journey.nodes || {}).length;
13
+ }
14
+ function transitionCount(journey) {
15
+ return (journey.transitions || []).length;
16
+ }
17
+ export function formatJourneyWithGuidance(journey) {
18
+ const nodes = nodeCount(journey);
19
+ const transitions = transitionCount(journey);
20
+ const stats = journey.stats;
21
+ const lines = [
22
+ `Journey: ${journey.name} (${journey.id})`,
23
+ `Status: ${STATUS_ICONS[journey.status] || journey.status} | Nodes: ${nodes} | Transitions: ${transitions} | Entry: ${journey.entryEvent}`,
24
+ ];
25
+ if (stats && stats.totalEnrolled) {
26
+ lines.push(`Stats: ${stats.totalEnrolled} enrolled, ${stats.currentlyActive ?? 0} active, ${stats.completed ?? 0} completed`);
27
+ }
28
+ if (journey.description) {
29
+ lines.push(`Description: ${journey.description}`);
30
+ }
31
+ // Graph summary
32
+ lines.push("");
33
+ lines.push("Nodes:");
34
+ for (const [name, node] of Object.entries(journey.nodes || {})) {
35
+ const action = node.action?.type || "none";
36
+ const terminal = node.terminal ? " (terminal)" : "";
37
+ const isEntry = name === journey.entryNode ? " [entry]" : "";
38
+ lines.push(` ${name}: ${action}${terminal}${isEntry}`);
39
+ }
40
+ if (journey.transitions && journey.transitions.length > 0) {
41
+ lines.push("");
42
+ lines.push("Transitions:");
43
+ for (const t of journey.transitions) {
44
+ const from = t.from || t.from_;
45
+ const trigger = t.trigger;
46
+ let desc;
47
+ if (trigger?.type === "timer") {
48
+ desc = `timer (${trigger.delay || "?"})`;
49
+ }
50
+ else {
51
+ desc = trigger?.event || "event";
52
+ }
53
+ lines.push(` ${from} → ${t.to} [${desc}]`);
54
+ }
55
+ }
56
+ // Warnings
57
+ if (journey.warnings && journey.warnings.length > 0) {
58
+ lines.push("");
59
+ lines.push("Warnings:");
60
+ for (const w of journey.warnings) {
61
+ lines.push(` ! ${w}`);
62
+ }
63
+ }
64
+ // Next steps
65
+ const hints = [];
66
+ if (journey.status === "draft") {
67
+ hints.push("Journey is in draft. Use journey_activate to start enrolling users.");
68
+ }
69
+ else if (journey.status === "active") {
70
+ hints.push("Journey is active and enrolling users. Use journey_analytics to see user flow.");
71
+ }
72
+ else if (journey.status === "paused") {
73
+ hints.push("Journey is paused. Actions are queued. Use journey_activate to resume.");
74
+ }
75
+ else if (journey.status === "archived") {
76
+ hints.push("Journey is archived. No further enrollment or transitions.");
77
+ }
78
+ if (hints.length > 0) {
79
+ lines.push("");
80
+ lines.push("Next steps:");
81
+ for (const hint of hints) {
82
+ lines.push(` → ${hint}`);
83
+ }
84
+ }
85
+ return lines.join("\n");
86
+ }
87
+ export function formatJourneyList(data) {
88
+ const { journeys, total } = data;
89
+ if (!journeys || journeys.length === 0) {
90
+ return "No journeys found. Use journey_create to design a user lifecycle journey.";
91
+ }
92
+ const lines = [`${total} journey(s):\n`];
93
+ for (const j of journeys) {
94
+ const nodes = nodeCount(j);
95
+ const stats = j.stats;
96
+ const enrolled = stats?.totalEnrolled ?? 0;
97
+ const active = stats?.currentlyActive ?? 0;
98
+ let readiness;
99
+ if (j.status === "draft") {
100
+ readiness = "draft";
101
+ }
102
+ else if (j.status === "active") {
103
+ readiness = `active — ${active} active / ${enrolled} enrolled`;
104
+ }
105
+ else {
106
+ readiness = j.status;
107
+ }
108
+ lines.push(` ${j.id} | ${j.name} — ${nodes} nodes — ${readiness}`);
109
+ }
110
+ return lines.join("\n");
111
+ }
112
+ function formatActionLabel(action) {
113
+ const type = action?.type || "none";
114
+ if (type === "send_email") {
115
+ const parts = [`send_email (${action.templateSlug || "?"})`];
116
+ if (action.surveySlug)
117
+ parts.push(`+ survey: ${action.surveySlug}`);
118
+ return parts.join(" ");
119
+ }
120
+ if (type === "tag_user")
121
+ return `tag_user (${action.tagName || "?"})`;
122
+ if (type === "advance_stage")
123
+ return `advance_stage (${action.stageName || "?"})`;
124
+ return type;
125
+ }
126
+ function formatTriggerLabel(trigger) {
127
+ if (trigger?.type === "timer")
128
+ return `after ${trigger.delay || "?"}`;
129
+ return `on ${trigger?.event || "event"}`;
130
+ }
131
+ export function formatNodeList(data) {
132
+ const nodes = data.nodes || [];
133
+ if (nodes.length === 0) {
134
+ return "No nodes. Use journey_add_node to add the first node.";
135
+ }
136
+ const lines = [`${nodes.length} node(s):\n`];
137
+ for (const n of nodes) {
138
+ const entry = n.isEntryNode ? " [entry]" : "";
139
+ const terminal = n.terminal ? " (terminal)" : "";
140
+ const action = formatActionLabel(n.action);
141
+ const edges = `${n.outgoingTransitions} out / ${n.incomingTransitions} in`;
142
+ lines.push(` ${n.name}: ${action}${terminal}${entry}`);
143
+ lines.push(` transitions: ${edges}`);
144
+ }
145
+ return lines.join("\n");
146
+ }
147
+ export function formatSingleNode(data, action, nodeName) {
148
+ return `${action} node '${nodeName}'.\n\n${formatJourneyWithGuidance(data)}`;
149
+ }
150
+ export function formatTransitionList(data) {
151
+ const transitions = data.transitions || [];
152
+ if (transitions.length === 0) {
153
+ return "No transitions. Use journey_add_transition to connect nodes.";
154
+ }
155
+ const lines = [`${transitions.length} transition(s):\n`];
156
+ for (const t of transitions) {
157
+ const trigger = formatTriggerLabel(t.trigger);
158
+ const priority = t.priority != null ? ` [priority: ${t.priority}]` : "";
159
+ lines.push(` ${t.name}: ${t.from} → ${t.to} — ${trigger}${priority}`);
160
+ }
161
+ return lines.join("\n");
162
+ }
163
+ export function formatSingleTransition(data, action, transitionName) {
164
+ return `${action} transition '${transitionName}'.\n\n${formatJourneyWithGuidance(data)}`;
165
+ }
166
+ export function formatJourneyAnalytics(data) {
167
+ const { journey, nodes, transitions, terminalDistribution, totals } = data;
168
+ const lines = [
169
+ `Analytics: ${journey.name} (${journey.id})`,
170
+ `Status: ${journey.status}`,
171
+ "",
172
+ `Totals: ${totals.enrolled} enrolled, ${totals.active} active, ${totals.completed} completed, ${totals.failed} failed`,
173
+ "",
174
+ ];
175
+ // Node breakdown
176
+ lines.push("Nodes:");
177
+ for (const n of nodes) {
178
+ const terminal = n.terminal ? " (terminal)" : "";
179
+ const time = n.medianTimeHours > 0 ? ` — median ${n.medianTimeHours}h` : "";
180
+ lines.push(` ${n.name}: ${n.userCount} active users${terminal}${time}`);
181
+ }
182
+ // Transition breakdown
183
+ if (transitions.length > 0) {
184
+ lines.push("");
185
+ lines.push("Transitions:");
186
+ for (const t of transitions) {
187
+ const trigger = t.type === "timer" ? `timer (${t.delay || "?"})` : (t.event || "event");
188
+ lines.push(` ${t.from} → ${t.to} [${trigger}]: ${t.userCount} users`);
189
+ }
190
+ }
191
+ // Terminal distribution
192
+ if (terminalDistribution.length > 0) {
193
+ lines.push("");
194
+ lines.push("Completed users ended at:");
195
+ for (const td of terminalDistribution) {
196
+ lines.push(` ${td.node}: ${td.count}`);
197
+ }
198
+ }
199
+ return lines.join("\n");
200
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Human-readable formatting for survey questions.
3
+ * Used by MCP tools to present question data in a conversational format.
4
+ */
5
+ interface QuestionData {
6
+ name: string;
7
+ type: string;
8
+ title: string;
9
+ isRequired: boolean;
10
+ position: number;
11
+ choices?: string[];
12
+ rateMin?: number;
13
+ rateMax?: number;
14
+ minRateDescription?: string;
15
+ maxRateDescription?: string;
16
+ inputType?: string;
17
+ visibleIf?: string;
18
+ requiredIf?: string;
19
+ }
20
+ export declare function formatQuestionList(questions: QuestionData[]): string;
21
+ export declare function formatSingleQuestion(q: QuestionData, action: string): string;
22
+ interface SurveyData {
23
+ id: string;
24
+ name: string;
25
+ type: string;
26
+ status: string;
27
+ definition?: {
28
+ pages?: Array<{
29
+ elements?: unknown[];
30
+ }>;
31
+ };
32
+ stats?: {
33
+ responseCount?: number;
34
+ completionRate?: number;
35
+ };
36
+ createdAt?: string;
37
+ updatedAt?: string;
38
+ }
39
+ /**
40
+ * Format a survey with contextual guidance and next-step suggestions.
41
+ * Used by create, get, and update tools to guide the developer through
42
+ * the survey lifecycle: create → add questions → activate → distribute.
43
+ */
44
+ export declare function formatSurveyWithGuidance(survey: SurveyData): string;
45
+ /**
46
+ * Format a survey list with readiness status.
47
+ */
48
+ export declare function formatSurveyList(surveyList: SurveyData[]): string;
49
+ /**
50
+ * Format distribution results with guidance.
51
+ */
52
+ export declare function formatDistributionResult(invitations: Array<{
53
+ id: string;
54
+ userId: string;
55
+ token: string;
56
+ status: string;
57
+ isNew?: boolean;
58
+ link?: string;
59
+ }>): string;
60
+ export {};
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Human-readable formatting for survey questions.
3
+ * Used by MCP tools to present question data in a conversational format.
4
+ */
5
+ const TYPE_LABELS = {
6
+ text: "Short text",
7
+ comment: "Long text",
8
+ radiogroup: "Single choice",
9
+ checkbox: "Multiple choice",
10
+ rating: "Rating scale",
11
+ boolean: "Yes/No",
12
+ dropdown: "Dropdown",
13
+ ranking: "Ranking",
14
+ };
15
+ function getTypeDisplay(q) {
16
+ if (q.type === "rating" && q.rateMin === 0 && q.rateMax === 10) {
17
+ return "NPS (0-10)";
18
+ }
19
+ if (q.type === "rating" && q.rateMin != null && q.rateMax != null) {
20
+ return `Rating (${q.rateMin}-${q.rateMax})`;
21
+ }
22
+ if (q.type === "text" && q.inputType === "date") {
23
+ return "Date";
24
+ }
25
+ return TYPE_LABELS[q.type] || q.type;
26
+ }
27
+ export function formatQuestionList(questions) {
28
+ if (!questions || questions.length === 0) {
29
+ return "This survey has no questions yet.";
30
+ }
31
+ const lines = [`${questions.length} question(s):\n`];
32
+ for (const q of questions) {
33
+ const num = q.position + 1;
34
+ const required = q.isRequired ? " (required)" : "";
35
+ const typeDisplay = getTypeDisplay(q);
36
+ lines.push(` ${num}. [${typeDisplay}]${required} ${q.title}`);
37
+ lines.push(` name: ${q.name}`);
38
+ if (q.choices && q.choices.length > 0) {
39
+ lines.push(` choices: ${q.choices.join(", ")}`);
40
+ }
41
+ if (q.minRateDescription || q.maxRateDescription) {
42
+ const parts = [];
43
+ if (q.minRateDescription)
44
+ parts.push(`min: "${q.minRateDescription}"`);
45
+ if (q.maxRateDescription)
46
+ parts.push(`max: "${q.maxRateDescription}"`);
47
+ lines.push(` labels: ${parts.join(", ")}`);
48
+ }
49
+ if (q.visibleIf) {
50
+ lines.push(` show if: ${q.visibleIf}`);
51
+ }
52
+ if (q.requiredIf) {
53
+ lines.push(` required if: ${q.requiredIf}`);
54
+ }
55
+ }
56
+ return lines.join("\n");
57
+ }
58
+ export function formatSingleQuestion(q, action) {
59
+ const formatted = formatQuestionList([{ ...q, position: 0 }]);
60
+ return `${action} question:\n\n${formatted}`;
61
+ }
62
+ function countQuestions(definition) {
63
+ if (!definition?.pages)
64
+ return 0;
65
+ return definition.pages.reduce((sum, page) => sum + (page.elements?.length ?? 0), 0);
66
+ }
67
+ /**
68
+ * Format a survey with contextual guidance and next-step suggestions.
69
+ * Used by create, get, and update tools to guide the developer through
70
+ * the survey lifecycle: create → add questions → activate → distribute.
71
+ */
72
+ export function formatSurveyWithGuidance(survey) {
73
+ const questionCount = countQuestions(survey.definition);
74
+ const responseCount = survey.stats?.responseCount ?? 0;
75
+ const typeLabel = survey.type === "nps"
76
+ ? "NPS"
77
+ : survey.type === "csat"
78
+ ? "CSAT"
79
+ : "Custom";
80
+ const lines = [
81
+ `Survey: ${survey.name} (${survey.id})`,
82
+ `Type: ${typeLabel} | Status: ${survey.status} | Questions: ${questionCount} | Responses: ${responseCount}`,
83
+ ];
84
+ // Next steps based on current state
85
+ const hints = [];
86
+ if (questionCount === 0) {
87
+ hints.push("This survey has no questions yet. Use survey_add_question to add questions, or survey_list_questions to see supported question types.");
88
+ }
89
+ else if (survey.status === "draft") {
90
+ hints.push(`Survey has ${questionCount} question(s) and is in draft. Use survey_update with status 'active' to activate it for distribution.`);
91
+ hints.push("Use survey_list_questions to review the questions before activating.");
92
+ }
93
+ else if (survey.status === "active" && responseCount === 0) {
94
+ hints.push("Survey is active and ready to distribute. Use survey_distribute with a list of user IDs to create personalized tracking links.");
95
+ }
96
+ else if (survey.status === "active" && responseCount > 0) {
97
+ hints.push("Survey is collecting responses. Use survey_analytics to see the distribution funnel and results.");
98
+ }
99
+ else if (survey.status === "paused") {
100
+ hints.push("Survey is paused. Use survey_update with status 'active' to resume collection.");
101
+ }
102
+ if (hints.length > 0) {
103
+ lines.push("");
104
+ lines.push("Next steps:");
105
+ for (const hint of hints) {
106
+ lines.push(` → ${hint}`);
107
+ }
108
+ }
109
+ return lines.join("\n");
110
+ }
111
+ /**
112
+ * Format a survey list with readiness status.
113
+ */
114
+ export function formatSurveyList(surveyList) {
115
+ if (!surveyList || surveyList.length === 0) {
116
+ return "No surveys found. Use survey_create to create one (try type 'nps' or 'csat' for quick presets).";
117
+ }
118
+ const lines = [`${surveyList.length} survey(s):\n`];
119
+ for (const s of surveyList) {
120
+ const qCount = countQuestions(s.definition);
121
+ const responses = s.stats?.responseCount ?? 0;
122
+ const typeLabel = s.type === "nps" ? "NPS" : s.type === "csat" ? "CSAT" : "Custom";
123
+ // Readiness indicator
124
+ let readiness;
125
+ if (qCount === 0) {
126
+ readiness = "needs questions";
127
+ }
128
+ else if (s.status === "draft") {
129
+ readiness = "draft — ready to activate";
130
+ }
131
+ else if (s.status === "active" && responses === 0) {
132
+ readiness = "active — ready to distribute";
133
+ }
134
+ else if (s.status === "active") {
135
+ readiness = `active — ${responses} response(s)`;
136
+ }
137
+ else {
138
+ readiness = s.status;
139
+ }
140
+ lines.push(` ${s.id} | ${s.name} [${typeLabel}] — ${qCount} questions — ${readiness}`);
141
+ }
142
+ return lines.join("\n");
143
+ }
144
+ /**
145
+ * Format distribution results with guidance.
146
+ */
147
+ export function formatDistributionResult(invitations) {
148
+ if (!invitations || invitations.length === 0) {
149
+ return "No invitations created.";
150
+ }
151
+ const newCount = invitations.filter((i) => i.isNew === true).length;
152
+ const existingCount = invitations.length - newCount;
153
+ const lines = [
154
+ `Created ${newCount} personalized survey link(s).`,
155
+ ];
156
+ if (existingCount > 0) {
157
+ lines.push(`${existingCount} user(s) already had invitations (skipped).`);
158
+ }
159
+ lines.push("");
160
+ lines.push("Each link is unique and trackable (sent → opened → submitted).");
161
+ lines.push("Include these links in email campaigns or journey-triggered messages.");
162
+ lines.push("Use survey_analytics to monitor the distribution funnel.");
163
+ return lines.join("\n");
164
+ }
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@ascendkit/cli",
3
+ "version": "0.1.10",
4
+ "description": "AscendKit CLI and MCP server",
5
+ "author": "ascendkit.dev",
6
+ "license": "MIT",
7
+ "homepage": "https://ascendkit.dev",
8
+ "publishConfig": {
9
+ "access": "public"
10
+ },
11
+ "keywords": ["ascendkit", "cli", "mcp", "model-context-protocol", "b2b", "saas"],
12
+ "type": "module",
13
+ "main": "dist/cli.js",
14
+ "files": [
15
+ "dist/",
16
+ "README.md",
17
+ "!dist/releases/**"
18
+ ],
19
+ "bin": {
20
+ "ascendkit": "dist/cli.js",
21
+ "ascendkit-mcp": "dist/mcp.js"
22
+ },
23
+ "scripts": {
24
+ "build": "tsc",
25
+ "dev": "tsc --watch",
26
+ "start": "node dist/cli.js",
27
+ "release:local": "./scripts/release-local.sh"
28
+ },
29
+ "dependencies": {
30
+ "@modelcontextprotocol/sdk": "^1.0.0",
31
+ "zod": "^3.23.0"
32
+ },
33
+ "devDependencies": {
34
+ "typescript": "^5.7.0",
35
+ "@types/node": "^22.0.0"
36
+ }
37
+ }