@episoda/mcp 0.1.11 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,8 +1,13 @@
1
1
  #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
2
3
  var __getOwnPropNames = Object.getOwnPropertyNames;
3
4
  var __esm = (fn, res) => function __init() {
4
5
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
6
  };
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
6
11
 
7
12
  // src/runtime-config.ts
8
13
  import * as fs from "fs";
@@ -11,14 +16,15 @@ import * as path from "path";
11
16
  async function resolveRuntimeConfig() {
12
17
  const envConfig = readEnvConfig();
13
18
  let fileConfig = null;
14
- if (!envConfig.sessionToken || !envConfig.projectId || !envConfig.workspaceId || !envConfig.apiUrl) {
19
+ if (!envConfig.sessionToken || !envConfig.projectId || !envConfig.workspaceId || !envConfig.apiUrl || !envConfig.machineUuid) {
15
20
  fileConfig = loadLocalConfig();
16
21
  }
17
22
  const resolved = {
18
23
  apiUrl: envConfig.apiUrl || fileConfig?.api_url || DEFAULT_API_URL,
19
24
  sessionToken: envConfig.sessionToken || fileConfig?.access_token || "",
20
25
  projectId: envConfig.projectId || fileConfig?.project_id || "",
21
- workspaceId: envConfig.workspaceId || fileConfig?.workspace_id || ""
26
+ workspaceId: envConfig.workspaceId || fileConfig?.workspace_id || "",
27
+ machineUuid: envConfig.machineUuid || fileConfig?.machine_uuid || fileConfig?.device_id || void 0
22
28
  };
23
29
  const missing = [];
24
30
  if (!resolved.sessionToken) missing.push("EPISODA_SESSION_TOKEN");
@@ -43,6 +49,9 @@ async function hydrateRuntimeConfig() {
43
49
  if (!normalizeEnv(process.env.EPISODA_WORKSPACE_ID)) {
44
50
  process.env.EPISODA_WORKSPACE_ID = resolved.workspaceId;
45
51
  }
52
+ if (resolved.machineUuid && !normalizeEnv(process.env.EPISODA_MACHINE_UUID)) {
53
+ process.env.EPISODA_MACHINE_UUID = resolved.machineUuid;
54
+ }
46
55
  return resolved;
47
56
  }
48
57
  var DEFAULT_API_URL, DEFAULT_CONFIG_FILE, normalizeEnv, readEnvConfig, buildMissingMessage, getConfigPath, loadLocalConfig;
@@ -60,7 +69,8 @@ var init_runtime_config = __esm({
60
69
  apiUrl: normalizeEnv(process.env.EPISODA_API_URL),
61
70
  sessionToken: normalizeEnv(process.env.EPISODA_SESSION_TOKEN),
62
71
  projectId: normalizeEnv(process.env.EPISODA_PROJECT_ID),
63
- workspaceId: normalizeEnv(process.env.EPISODA_WORKSPACE_ID)
72
+ workspaceId: normalizeEnv(process.env.EPISODA_WORKSPACE_ID),
73
+ machineUuid: normalizeEnv(process.env.EPISODA_MACHINE_UUID)
64
74
  });
65
75
  buildMissingMessage = (missing, apiUrl) => {
66
76
  return [
@@ -92,57 +102,140 @@ var init_runtime_config = __esm({
92
102
  }
93
103
  });
94
104
 
95
- // src/workflow-server.ts
96
- var workflow_server_exports = {};
97
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
98
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
99
- import { z } from "zod";
100
- async function apiRequest(method, path2, body) {
101
- const url = `${EPISODA_API_URL}${path2}`;
105
+ // src/request-executors.ts
106
+ function buildMcpHeaders(runtime) {
107
+ const headers = {
108
+ "Content-Type": "application/json",
109
+ "Authorization": `Bearer ${runtime.sessionToken}`
110
+ };
111
+ if (runtime.projectId) {
112
+ headers["x-project-id"] = runtime.projectId;
113
+ }
114
+ if (runtime.workspaceId) {
115
+ headers["x-workspace-id"] = runtime.workspaceId;
116
+ }
117
+ if (runtime.machineUuid) {
118
+ headers["x-machine-uuid"] = runtime.machineUuid;
119
+ }
120
+ return headers;
121
+ }
122
+ async function apiRequest(runtime, method, path2, body, fetchImpl = fetch) {
123
+ const url = `${runtime.apiUrl}${path2}`;
102
124
  const options = {
103
125
  method,
104
- headers: {
105
- "Content-Type": "application/json",
106
- "Authorization": `Bearer ${EPISODA_SESSION_TOKEN}`
107
- }
126
+ headers: buildMcpHeaders(runtime)
108
127
  };
109
128
  if (body && method !== "GET") {
110
129
  options.body = JSON.stringify(body);
111
130
  }
112
- if (EPISODA_PROJECT_ID) {
113
- options.headers["x-project-id"] = EPISODA_PROJECT_ID;
114
- }
115
- if (EPISODA_WORKSPACE_ID) {
116
- options.headers["x-workspace-id"] = EPISODA_WORKSPACE_ID;
117
- }
118
- const response = await fetch(url, options);
131
+ const response = await fetchImpl(url, options);
119
132
  if (!response.ok) {
120
133
  const text = await response.text();
121
134
  throw new Error(`API error ${response.status}: ${text}`);
122
135
  }
123
136
  return response.json();
124
137
  }
125
- async function main() {
138
+ async function executeTransitionModule(runtime, args, fetchImpl = fetch) {
139
+ const result = await apiRequest(
140
+ runtime,
141
+ "POST",
142
+ `/api/modules/${args.module_uid}/transition`,
143
+ { targetState: args.target_state },
144
+ fetchImpl
145
+ );
146
+ if (!result.success) {
147
+ return { content: [{ type: "text", text: `Error: ${result.error}` }], isError: true };
148
+ }
149
+ return {
150
+ content: [{
151
+ type: "text",
152
+ text: `Module ${args.module_uid} transitioned to ${args.target_state}`
153
+ }]
154
+ };
155
+ }
156
+ async function executeGitCommandRequest(runtime, args, fetchImpl = fetch) {
157
+ const response = await fetchImpl(`${runtime.apiUrl}/api/dev/${args.target}/exec`, {
158
+ method: "POST",
159
+ headers: buildMcpHeaders(runtime),
160
+ body: JSON.stringify({
161
+ command: args.command,
162
+ cwd: args.cwd,
163
+ timeout: args.timeout ?? 3e4
164
+ })
165
+ });
166
+ if (!response.ok) {
167
+ return {
168
+ success: false,
169
+ error: `HTTP ${response.status}: ${response.statusText}`
170
+ };
171
+ }
172
+ return response.json();
173
+ }
174
+ var init_request_executors = __esm({
175
+ "src/request-executors.ts"() {
176
+ "use strict";
177
+ }
178
+ });
179
+
180
+ // src/workflow-server.ts
181
+ var workflow_server_exports = {};
182
+ __export(workflow_server_exports, {
183
+ handleTransitionModule: () => handleTransitionModule,
184
+ startServer: () => startServer
185
+ });
186
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
187
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
188
+ import { z } from "zod";
189
+ async function apiRequest2(method, path2, body) {
190
+ return apiRequest(
191
+ {
192
+ apiUrl: EPISODA_API_URL,
193
+ sessionToken: EPISODA_SESSION_TOKEN,
194
+ projectId: EPISODA_PROJECT_ID,
195
+ workspaceId: EPISODA_WORKSPACE_ID,
196
+ machineUuid: EPISODA_MACHINE_UUID
197
+ },
198
+ method,
199
+ path2,
200
+ body
201
+ );
202
+ }
203
+ async function handleTransitionModule(args) {
204
+ return executeTransitionModule(
205
+ {
206
+ apiUrl: EPISODA_API_URL,
207
+ sessionToken: EPISODA_SESSION_TOKEN,
208
+ projectId: EPISODA_PROJECT_ID,
209
+ workspaceId: EPISODA_WORKSPACE_ID,
210
+ machineUuid: EPISODA_MACHINE_UUID
211
+ },
212
+ args
213
+ );
214
+ }
215
+ async function startServer() {
126
216
  const runtimeConfig = await hydrateRuntimeConfig();
127
217
  EPISODA_API_URL = runtimeConfig.apiUrl;
128
218
  EPISODA_SESSION_TOKEN = runtimeConfig.sessionToken;
129
219
  EPISODA_PROJECT_ID = runtimeConfig.projectId;
130
220
  EPISODA_WORKSPACE_ID = runtimeConfig.workspaceId;
221
+ EPISODA_MACHINE_UUID = runtimeConfig.machineUuid || "";
131
222
  const transport = new StdioServerTransport();
132
223
  await server.connect(transport);
133
224
  console.error("[episoda-workflow] MCP server started");
134
225
  console.error(`[episoda-workflow] API URL: ${EPISODA_API_URL}`);
135
226
  console.error(`[episoda-workflow] Token: ${EPISODA_SESSION_TOKEN ? "****" : "NOT SET"}`);
136
227
  }
137
- var EPISODA_API_URL, EPISODA_SESSION_TOKEN, EPISODA_PROJECT_ID, EPISODA_WORKSPACE_ID, server;
228
+ var EPISODA_API_URL, EPISODA_SESSION_TOKEN, EPISODA_PROJECT_ID, EPISODA_WORKSPACE_ID, EPISODA_MACHINE_UUID, server;
138
229
  var init_workflow_server = __esm({
139
230
  "src/workflow-server.ts"() {
140
231
  "use strict";
141
232
  init_runtime_config();
233
+ init_request_executors();
142
234
  EPISODA_API_URL = process.env.EPISODA_API_URL || "https://episoda.dev";
143
235
  EPISODA_SESSION_TOKEN = process.env.EPISODA_SESSION_TOKEN || "";
144
236
  EPISODA_PROJECT_ID = process.env.EPISODA_PROJECT_ID || "";
145
237
  EPISODA_WORKSPACE_ID = process.env.EPISODA_WORKSPACE_ID || "";
238
+ EPISODA_MACHINE_UUID = process.env.EPISODA_MACHINE_UUID || "";
146
239
  server = new McpServer({
147
240
  name: "episoda-workflow",
148
241
  version: "1.0.0"
@@ -173,7 +266,7 @@ var init_workflow_server = __esm({
173
266
  }
174
267
  },
175
268
  async (args) => {
176
- const result = await apiRequest("POST", "/api/tasks", {
269
+ const result = await apiRequest2("POST", "/api/tasks", {
177
270
  module_id: args.module_uid,
178
271
  // API expects module_id (accepts both UUID and UID)
179
272
  title: args.title,
@@ -201,7 +294,7 @@ var init_workflow_server = __esm({
201
294
  }
202
295
  },
203
296
  async (args) => {
204
- const result = await apiRequest("PATCH", `/api/tasks/${args.task_uid}`, {
297
+ const result = await apiRequest2("PATCH", `/api/tasks/${args.task_uid}`, {
205
298
  state: args.state
206
299
  });
207
300
  if (!result.success) {
@@ -229,7 +322,7 @@ var init_workflow_server = __esm({
229
322
  const updates = {};
230
323
  if (args.title) updates.title = args.title;
231
324
  if (args.description) updates.description_md = args.description;
232
- const result = await apiRequest("PATCH", `/api/tasks/${args.task_uid}`, updates);
325
+ const result = await apiRequest2("PATCH", `/api/tasks/${args.task_uid}`, updates);
233
326
  if (!result.success) {
234
327
  return { content: [{ type: "text", text: `Error: ${result.error}` }], isError: true };
235
328
  }
@@ -250,7 +343,7 @@ var init_workflow_server = __esm({
250
343
  }
251
344
  },
252
345
  async (args) => {
253
- const result = await apiRequest("GET", `/api/tasks/${args.task_uid}`);
346
+ const result = await apiRequest2("GET", `/api/tasks/${args.task_uid}`);
254
347
  if (!result.success || !result.task) {
255
348
  return { content: [{ type: "text", text: `Error: ${result.error || "Task not found"}` }], isError: true };
256
349
  }
@@ -278,7 +371,7 @@ ${task.description_md || "_No description_"}`
278
371
  }
279
372
  },
280
373
  async (args) => {
281
- const result = await apiRequest("GET", `/api/tasks?module_id=${args.module_uid}`);
374
+ const result = await apiRequest2("GET", `/api/tasks?module_id=${args.module_uid}`);
282
375
  if (!result.success || !result.tasks) {
283
376
  return { content: [{ type: "text", text: `Error: ${result.error || "Failed to fetch tasks"}` }], isError: true };
284
377
  }
@@ -309,7 +402,7 @@ ${taskList}`
309
402
  }
310
403
  },
311
404
  async (args) => {
312
- const result = await apiRequest("DELETE", `/api/tasks/${args.task_uid}`);
405
+ const result = await apiRequest2("DELETE", `/api/tasks/${args.task_uid}`);
313
406
  if (!result.success) {
314
407
  return { content: [{ type: "text", text: `Error: ${result.error}` }], isError: true };
315
408
  }
@@ -341,7 +434,7 @@ Reduces latency from ~1.5s (6 calls) to ~300ms (1 call).`,
341
434
  async (args) => {
342
435
  try {
343
436
  const lookupPromises = args.task_uids.map(
344
- (uid) => apiRequest("GET", `/api/tasks/${uid}`).then((r) => r.success && r.task ? { uid, id: r.task.id } : null).catch(() => null)
437
+ (uid) => apiRequest2("GET", `/api/tasks/${uid}`).then((r) => r.success && r.task ? { uid, id: r.task.id } : null).catch(() => null)
345
438
  );
346
439
  const lookupResults = await Promise.all(lookupPromises);
347
440
  const validTasks = lookupResults.filter((t) => t !== null);
@@ -360,7 +453,7 @@ Not found: ${args.task_uids.join(", ")}`
360
453
  id: t.id,
361
454
  state: args.state
362
455
  }));
363
- const result = await apiRequest("PATCH", "/api/tasks/batch", {
456
+ const result = await apiRequest2("PATCH", "/api/tasks/batch", {
364
457
  updates
365
458
  });
366
459
  if (!result.success) {
@@ -420,7 +513,7 @@ EFFICIENCY: Single call instead of multiple create_task calls.`,
420
513
  },
421
514
  async (args) => {
422
515
  try {
423
- const result = await apiRequest("POST", "/api/tasks/bulk", {
516
+ const result = await apiRequest2("POST", "/api/tasks/bulk", {
424
517
  module_id: args.module_uid,
425
518
  tasks: args.tasks.map((t) => ({
426
519
  title: t.title,
@@ -477,7 +570,7 @@ EFFICIENCY: Single MCP call instead of multiple get_task_details calls.`,
477
570
  async (args) => {
478
571
  try {
479
572
  const fetchPromises = args.task_uids.map(
480
- (uid) => apiRequest("GET", `/api/tasks/${uid}`).then((r) => r.success && r.task ? { found: true, task: r.task } : { found: false, uid }).catch(() => ({ found: false, uid }))
573
+ (uid) => apiRequest2("GET", `/api/tasks/${uid}`).then((r) => r.success && r.task ? { found: true, task: r.task } : { found: false, uid }).catch(() => ({ found: false, uid }))
481
574
  );
482
575
  const results = await Promise.all(fetchPromises);
483
576
  const foundTasks = [];
@@ -536,7 +629,7 @@ ${notFoundUids.length > 0 ? `**Not found:** ${notFoundUids.join(", ")}` : ""}`
536
629
  }
537
630
  },
538
631
  async (args) => {
539
- const result = await apiRequest("POST", "/api/modules", {
632
+ const result = await apiRequest2("POST", "/api/modules", {
540
633
  title: args.title,
541
634
  description_md: args.description
542
635
  });
@@ -565,7 +658,7 @@ ${notFoundUids.length > 0 ? `**Not found:** ${notFoundUids.join(", ")}` : ""}`
565
658
  const updates = {};
566
659
  if (args.title) updates.title = args.title;
567
660
  if (args.description) updates.description_md = args.description;
568
- const result = await apiRequest("PATCH", `/api/modules/${args.module_uid}`, updates);
661
+ const result = await apiRequest2("PATCH", `/api/modules/${args.module_uid}`, updates);
569
662
  if (!result.success) {
570
663
  return { content: [{ type: "text", text: `Error: ${result.error}` }], isError: true };
571
664
  }
@@ -586,7 +679,7 @@ ${notFoundUids.length > 0 ? `**Not found:** ${notFoundUids.join(", ")}` : ""}`
586
679
  }
587
680
  },
588
681
  async (args) => {
589
- const result = await apiRequest("PATCH", `/api/modules/${args.module_uid}`, {
682
+ const result = await apiRequest2("PATCH", `/api/modules/${args.module_uid}`, {
590
683
  state: "review"
591
684
  });
592
685
  if (!result.success) {
@@ -609,7 +702,7 @@ ${notFoundUids.length > 0 ? `**Not found:** ${notFoundUids.join(", ")}` : ""}`
609
702
  }
610
703
  },
611
704
  async (args) => {
612
- const result = await apiRequest("PATCH", `/api/modules/${args.module_uid}`, {
705
+ const result = await apiRequest2("PATCH", `/api/modules/${args.module_uid}`, {
613
706
  state: "done"
614
707
  });
615
708
  if (!result.success) {
@@ -632,11 +725,19 @@ ${notFoundUids.length > 0 ? `**Not found:** ${notFoundUids.join(", ")}` : ""}`
632
725
  }
633
726
  },
634
727
  async (args) => {
635
- const result = await apiRequest("GET", `/api/modules/${args.module_uid}`);
636
- if (!result.success || !result.module) {
728
+ const MAX_INITIAL_PROMPT_CHARS = 2e3;
729
+ const result = await apiRequest2("GET", `/api/modules/${args.module_uid}`);
730
+ const mod = result.moduleRecord || result.module;
731
+ if (!result.success || !mod) {
637
732
  return { content: [{ type: "text", text: `Error: ${result.error || "Module not found"}` }], isError: true };
638
733
  }
639
- const mod = result.module;
734
+ const initialPrompt = mod.initial_prompt?.trim();
735
+ const initialPromptSection = initialPrompt ? `
736
+
737
+ ### Original Request
738
+ \`\`\`text
739
+ ${initialPrompt.slice(0, MAX_INITIAL_PROMPT_CHARS)}${initialPrompt.length > MAX_INITIAL_PROMPT_CHARS ? "\n\n[Truncated for output size]" : ""}
740
+ \`\`\`` : "";
640
741
  return {
641
742
  content: [{
642
743
  type: "text",
@@ -645,7 +746,7 @@ ${notFoundUids.length > 0 ? `**Not found:** ${notFoundUids.join(", ")}` : ""}`
645
746
  **State:** ${mod.state}
646
747
  **Branch:** ${mod.branch_name || "_Not set_"}
647
748
 
648
- ${mod.description_md || "_No description_"}`
749
+ ${mod.description_md || "_No description_"}${initialPromptSection}`
649
750
  }]
650
751
  };
651
752
  }
@@ -659,7 +760,7 @@ ${mod.description_md || "_No description_"}`
659
760
  }
660
761
  },
661
762
  async (args) => {
662
- const result = await apiRequest("DELETE", `/api/modules/${args.module_uid}`);
763
+ const result = await apiRequest2("DELETE", `/api/modules/${args.module_uid}`);
663
764
  if (!result.success) {
664
765
  return { content: [{ type: "text", text: `Error: ${result.error}` }], isError: true };
665
766
  }
@@ -692,7 +793,7 @@ USE THIS WHEN:
692
793
  if (args.limit) params.set("limit", String(args.limit));
693
794
  if (args.include_task_counts) params.set("includeTaskCounts", "true");
694
795
  const query = params.toString();
695
- const result = await apiRequest("GET", `/api/modules${query ? `?${query}` : ""}`);
796
+ const result = await apiRequest2("GET", `/api/modules${query ? `?${query}` : ""}`);
696
797
  if (!result.success || !result.modules) {
697
798
  return { content: [{ type: "text", text: `Error: ${result.error || "Failed to fetch modules"}` }], isError: true };
698
799
  }
@@ -725,22 +826,7 @@ Use this instead of request_review or mark_done for full control.`,
725
826
  target_state: z.enum(["ready", "doing", "review", "done"]).describe("Target state for the module")
726
827
  }
727
828
  },
728
- async (args) => {
729
- const result = await apiRequest(
730
- "POST",
731
- `/api/modules/${args.module_uid}/transition`,
732
- { targetState: args.target_state }
733
- );
734
- if (!result.success) {
735
- return { content: [{ type: "text", text: `Error: ${result.error}` }], isError: true };
736
- }
737
- return {
738
- content: [{
739
- type: "text",
740
- text: `Module ${args.module_uid} transitioned to ${args.target_state}`
741
- }]
742
- };
743
- }
829
+ handleTransitionModule
744
830
  );
745
831
  server.registerTool(
746
832
  "archive_module",
@@ -751,7 +837,7 @@ Use this instead of request_review or mark_done for full control.`,
751
837
  }
752
838
  },
753
839
  async (args) => {
754
- const result = await apiRequest(
840
+ const result = await apiRequest2(
755
841
  "POST",
756
842
  `/api/modules/${args.module_uid}/archive`,
757
843
  {}
@@ -776,7 +862,7 @@ Use this instead of request_review or mark_done for full control.`,
776
862
  }
777
863
  },
778
864
  async (args) => {
779
- const result = await apiRequest("GET", `/api/knowledge/${args.knowledge_id}`);
865
+ const result = await apiRequest2("GET", `/api/knowledge/${args.knowledge_id}`);
780
866
  if (!result.success || !result.knowledge) {
781
867
  return { content: [{ type: "text", text: `Error: ${result.error || "Knowledge not found"}` }], isError: true };
782
868
  }
@@ -819,7 +905,7 @@ DO NOT USE WHEN:
819
905
  }
820
906
  },
821
907
  async (args) => {
822
- const result = await apiRequest(
908
+ const result = await apiRequest2(
823
909
  "POST",
824
910
  "/api/search/semantic",
825
911
  {
@@ -880,7 +966,7 @@ DO NOT USE WHEN:
880
966
  }
881
967
  },
882
968
  async (args) => {
883
- const result = await apiRequest(
969
+ const result = await apiRequest2(
884
970
  "POST",
885
971
  "/api/search/text",
886
972
  {
@@ -940,13 +1026,13 @@ This is the recommended default search for most queries.`,
940
1026
  },
941
1027
  async (args) => {
942
1028
  const [semanticResult, textResult] = await Promise.all([
943
- apiRequest("POST", "/api/search/semantic", {
1029
+ apiRequest2("POST", "/api/search/semantic", {
944
1030
  query: args.query,
945
1031
  types: ["knowledge"],
946
1032
  limit: 50,
947
1033
  threshold: 0.4
948
1034
  }),
949
- apiRequest("POST", "/api/search/text", {
1035
+ apiRequest2("POST", "/api/search/text", {
950
1036
  query: args.query,
951
1037
  types: ["knowledge"],
952
1038
  limit: 50
@@ -1019,7 +1105,7 @@ USE THIS WHEN:
1019
1105
  }
1020
1106
  },
1021
1107
  async (args) => {
1022
- const result = await apiRequest(
1108
+ const result = await apiRequest2(
1023
1109
  "POST",
1024
1110
  "/api/search/conversations",
1025
1111
  {
@@ -1064,7 +1150,7 @@ ${list}`
1064
1150
  }
1065
1151
  },
1066
1152
  async (args) => {
1067
- const result = await apiRequest(
1153
+ const result = await apiRequest2(
1068
1154
  "GET",
1069
1155
  `/api/modules/${args.module_uid}/knowledge`
1070
1156
  );
@@ -1095,7 +1181,7 @@ ${list}`
1095
1181
  }
1096
1182
  },
1097
1183
  async (args) => {
1098
- const result = await apiRequest(
1184
+ const result = await apiRequest2(
1099
1185
  "POST",
1100
1186
  `/api/modules/${args.module_uid}/knowledge/link`,
1101
1187
  { knowledge_id: args.knowledge_id }
@@ -1131,7 +1217,7 @@ All documentation MUST be created in the knowledge base, not as markdown files.`
1131
1217
  }
1132
1218
  },
1133
1219
  async (args) => {
1134
- const result = await apiRequest("POST", "/api/knowledge", {
1220
+ const result = await apiRequest2("POST", "/api/knowledge", {
1135
1221
  title: args.title,
1136
1222
  domain: args.domain,
1137
1223
  doc_type: args.doc_type,
@@ -1170,7 +1256,7 @@ All documentation MUST be created in the knowledge base, not as markdown files.`
1170
1256
  if (args.domain) updates.domain = args.domain;
1171
1257
  if (args.doc_type) updates.doc_type = args.doc_type;
1172
1258
  if (args.significance !== void 0) updates.significance = args.significance;
1173
- const result = await apiRequest("PATCH", `/api/knowledge/${args.knowledge_id}`, updates);
1259
+ const result = await apiRequest2("PATCH", `/api/knowledge/${args.knowledge_id}`, updates);
1174
1260
  if (!result.success) {
1175
1261
  return { content: [{ type: "text", text: `Error: ${result.error}` }], isError: true };
1176
1262
  }
@@ -1191,7 +1277,7 @@ All documentation MUST be created in the knowledge base, not as markdown files.`
1191
1277
  }
1192
1278
  },
1193
1279
  async (args) => {
1194
- const result = await apiRequest("DELETE", `/api/knowledge/${args.knowledge_id}`);
1280
+ const result = await apiRequest2("DELETE", `/api/knowledge/${args.knowledge_id}`);
1195
1281
  if (!result.success) {
1196
1282
  return { content: [{ type: "text", text: `Error: ${result.error}` }], isError: true };
1197
1283
  }
@@ -1212,7 +1298,7 @@ All documentation MUST be created in the knowledge base, not as markdown files.`
1212
1298
  }
1213
1299
  },
1214
1300
  async (args) => {
1215
- const result = await apiRequest(
1301
+ const result = await apiRequest2(
1216
1302
  "GET",
1217
1303
  `/api/agent/context/module/${args.module_uid}`
1218
1304
  );
@@ -1234,7 +1320,7 @@ All documentation MUST be created in the knowledge base, not as markdown files.`
1234
1320
  inputSchema: {}
1235
1321
  },
1236
1322
  async () => {
1237
- const result = await apiRequest(
1323
+ const result = await apiRequest2(
1238
1324
  "GET",
1239
1325
  "/api/agent/context/system"
1240
1326
  );
@@ -1249,15 +1335,20 @@ All documentation MUST be created in the knowledge base, not as markdown files.`
1249
1335
  };
1250
1336
  }
1251
1337
  );
1252
- main().catch((error) => {
1253
- console.error("[episoda-workflow] Fatal error:", error);
1254
- process.exit(1);
1255
- });
1338
+ if (process.env.EPISODA_MCP_NO_AUTOSTART !== "1") {
1339
+ startServer().catch((error) => {
1340
+ console.error("[episoda-workflow] Fatal error:", error);
1341
+ process.exit(1);
1342
+ });
1343
+ }
1256
1344
  }
1257
1345
  });
1258
1346
 
1259
1347
  // src/git-server.ts
1260
1348
  var git_server_exports = {};
1349
+ __export(git_server_exports, {
1350
+ startServer: () => startServer2
1351
+ });
1261
1352
  import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
1262
1353
  import { StdioServerTransport as StdioServerTransport2 } from "@modelcontextprotocol/sdk/server/stdio.js";
1263
1354
  import { z as z2 } from "zod";
@@ -1270,28 +1361,17 @@ async function execCommand(command, options = {}) {
1270
1361
  error: "Module target missing. Provide moduleUid in the tool call or set MODULE_UID/DEV_ENVIRONMENT_ID."
1271
1362
  };
1272
1363
  }
1273
- const url = `${EPISODA_API_URL2}/api/dev/${target}/exec`;
1274
- const headers = {
1275
- "Content-Type": "application/json",
1276
- "Authorization": `Bearer ${EPISODA_SESSION_TOKEN2}`
1277
- };
1278
- if (EPISODA_PROJECT_ID2) {
1279
- headers["x-project-id"] = EPISODA_PROJECT_ID2;
1280
- }
1281
- if (EPISODA_WORKSPACE_ID2) {
1282
- headers["x-workspace-id"] = EPISODA_WORKSPACE_ID2;
1283
- }
1284
- const response = await fetch(url, {
1285
- method: "POST",
1286
- headers,
1287
- body: JSON.stringify({ command, cwd, timeout })
1288
- });
1289
- if (!response.ok) {
1290
- const text = await response.text();
1291
- return { success: false, error: `API error ${response.status}: ${text}` };
1292
- }
1293
- const result = await response.json();
1294
- return result;
1364
+ return executeGitCommandRequest(
1365
+ {
1366
+ apiUrl: EPISODA_API_URL2,
1367
+ sessionToken: EPISODA_SESSION_TOKEN2,
1368
+ projectId: EPISODA_PROJECT_ID2,
1369
+ workspaceId: EPISODA_WORKSPACE_ID2,
1370
+ // EP1376: Intentional parity with workflow/dev MCP servers.
1371
+ machineUuid: EPISODA_MACHINE_UUID2
1372
+ },
1373
+ { target, command, cwd, timeout }
1374
+ );
1295
1375
  }
1296
1376
  function formatGitOutput(result, errorPrefix = "Git error") {
1297
1377
  if (!result.success || !result.data) {
@@ -1313,12 +1393,13 @@ ${errorOutput}` }],
1313
1393
  content: [{ type: "text", text: stdout || "(no output)" }]
1314
1394
  };
1315
1395
  }
1316
- async function main2() {
1396
+ async function startServer2() {
1317
1397
  const runtimeConfig = await hydrateRuntimeConfig();
1318
1398
  EPISODA_API_URL2 = runtimeConfig.apiUrl;
1319
1399
  EPISODA_SESSION_TOKEN2 = runtimeConfig.sessionToken;
1320
1400
  EPISODA_PROJECT_ID2 = runtimeConfig.projectId;
1321
1401
  EPISODA_WORKSPACE_ID2 = runtimeConfig.workspaceId;
1402
+ EPISODA_MACHINE_UUID2 = runtimeConfig.machineUuid || "";
1322
1403
  if (!MODULE_UID && !DEV_ENVIRONMENT_ID) {
1323
1404
  console.warn("[episoda-git] Warning: MODULE_UID/DEV_ENVIRONMENT_ID not set. Provide moduleUid per tool call.");
1324
1405
  }
@@ -1330,17 +1411,19 @@ async function main2() {
1330
1411
  console.error(`[episoda-git] Dev Target: ${devTarget}`);
1331
1412
  console.error(`[episoda-git] Token: ${EPISODA_SESSION_TOKEN2 ? "****" : "NOT SET"}`);
1332
1413
  }
1333
- var EPISODA_API_URL2, EPISODA_SESSION_TOKEN2, DEV_ENVIRONMENT_ID, MODULE_UID, EPISODA_PROJECT_ID2, EPISODA_WORKSPACE_ID2, targetSchema, server2;
1414
+ var EPISODA_API_URL2, EPISODA_SESSION_TOKEN2, DEV_ENVIRONMENT_ID, MODULE_UID, EPISODA_PROJECT_ID2, EPISODA_WORKSPACE_ID2, EPISODA_MACHINE_UUID2, targetSchema, server2;
1334
1415
  var init_git_server = __esm({
1335
1416
  "src/git-server.ts"() {
1336
1417
  "use strict";
1337
1418
  init_runtime_config();
1419
+ init_request_executors();
1338
1420
  EPISODA_API_URL2 = process.env.EPISODA_API_URL || "https://episoda.dev";
1339
1421
  EPISODA_SESSION_TOKEN2 = process.env.EPISODA_SESSION_TOKEN || "";
1340
1422
  DEV_ENVIRONMENT_ID = process.env.DEV_ENVIRONMENT_ID || "";
1341
1423
  MODULE_UID = process.env.MODULE_UID || "";
1342
1424
  EPISODA_PROJECT_ID2 = process.env.EPISODA_PROJECT_ID || "";
1343
1425
  EPISODA_WORKSPACE_ID2 = process.env.EPISODA_WORKSPACE_ID || "";
1426
+ EPISODA_MACHINE_UUID2 = process.env.EPISODA_MACHINE_UUID || "";
1344
1427
  targetSchema = {
1345
1428
  moduleUid: z2.string().optional().describe("Module UID to target (overrides server default)")
1346
1429
  };
@@ -1839,42 +1922,37 @@ var init_git_server = __esm({
1839
1922
  };
1840
1923
  }
1841
1924
  );
1842
- main2().catch((error) => {
1843
- console.error("[episoda-git] Fatal error:", error);
1844
- process.exit(1);
1845
- });
1925
+ if (process.env.EPISODA_MCP_NO_AUTOSTART !== "1") {
1926
+ startServer2().catch((error) => {
1927
+ console.error("[episoda-git] Fatal error:", error);
1928
+ process.exit(1);
1929
+ });
1930
+ }
1846
1931
  }
1847
1932
  });
1848
1933
 
1849
1934
  // src/dev-server.ts
1850
1935
  var dev_server_exports = {};
1936
+ __export(dev_server_exports, {
1937
+ startServer: () => startServer3
1938
+ });
1851
1939
  import { McpServer as McpServer3 } from "@modelcontextprotocol/sdk/server/mcp.js";
1852
1940
  import { StdioServerTransport as StdioServerTransport3 } from "@modelcontextprotocol/sdk/server/stdio.js";
1853
1941
  import { z as z3 } from "zod";
1854
- async function apiRequest2(method, path2, body) {
1855
- const url = `${EPISODA_API_URL3}${path2}`;
1856
- const options = {
1942
+ async function apiRequest3(method, path2, body) {
1943
+ return apiRequest(
1944
+ {
1945
+ apiUrl: EPISODA_API_URL3,
1946
+ sessionToken: EPISODA_SESSION_TOKEN3,
1947
+ projectId: EPISODA_PROJECT_ID3,
1948
+ workspaceId: EPISODA_WORKSPACE_ID3,
1949
+ // EP1376: Intentional parity with workflow/git MCP servers.
1950
+ machineUuid: EPISODA_MACHINE_UUID3
1951
+ },
1857
1952
  method,
1858
- headers: {
1859
- "Content-Type": "application/json",
1860
- "Authorization": `Bearer ${EPISODA_SESSION_TOKEN3}`
1861
- }
1862
- };
1863
- if (EPISODA_PROJECT_ID3) {
1864
- options.headers["x-project-id"] = EPISODA_PROJECT_ID3;
1865
- }
1866
- if (EPISODA_WORKSPACE_ID3) {
1867
- options.headers["x-workspace-id"] = EPISODA_WORKSPACE_ID3;
1868
- }
1869
- if (body && method !== "GET") {
1870
- options.body = JSON.stringify(body);
1871
- }
1872
- const response = await fetch(url, options);
1873
- if (!response.ok) {
1874
- const text = await response.text();
1875
- throw new Error(`API error ${response.status}: ${text}`);
1876
- }
1877
- return response.json();
1953
+ path2,
1954
+ body
1955
+ );
1878
1956
  }
1879
1957
  function devPath(endpoint, overrideTarget) {
1880
1958
  const target = overrideTarget || MODULE_UID2 || DEV_ENVIRONMENT_ID2;
@@ -1889,12 +1967,13 @@ function formatSize(bytes) {
1889
1967
  if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
1890
1968
  return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)}GB`;
1891
1969
  }
1892
- async function main3() {
1970
+ async function startServer3() {
1893
1971
  const runtimeConfig = await hydrateRuntimeConfig();
1894
1972
  EPISODA_API_URL3 = runtimeConfig.apiUrl;
1895
1973
  EPISODA_SESSION_TOKEN3 = runtimeConfig.sessionToken;
1896
1974
  EPISODA_PROJECT_ID3 = runtimeConfig.projectId;
1897
1975
  EPISODA_WORKSPACE_ID3 = runtimeConfig.workspaceId;
1976
+ EPISODA_MACHINE_UUID3 = runtimeConfig.machineUuid || "";
1898
1977
  if (!MODULE_UID2 && !DEV_ENVIRONMENT_ID2) {
1899
1978
  console.warn("[episoda-dev] Warning: MODULE_UID/DEV_ENVIRONMENT_ID not set. Provide moduleUid per tool call.");
1900
1979
  }
@@ -1906,17 +1985,19 @@ async function main3() {
1906
1985
  console.error(`[episoda-dev] Dev Target: ${devTarget}`);
1907
1986
  console.error(`[episoda-dev] Token: ${EPISODA_SESSION_TOKEN3 ? "****" : "NOT SET"}`);
1908
1987
  }
1909
- var EPISODA_API_URL3, EPISODA_SESSION_TOKEN3, DEV_ENVIRONMENT_ID2, MODULE_UID2, EPISODA_PROJECT_ID3, EPISODA_WORKSPACE_ID3, targetSchema2, server3;
1988
+ var EPISODA_API_URL3, EPISODA_SESSION_TOKEN3, DEV_ENVIRONMENT_ID2, MODULE_UID2, EPISODA_PROJECT_ID3, EPISODA_WORKSPACE_ID3, EPISODA_MACHINE_UUID3, targetSchema2, server3;
1910
1989
  var init_dev_server = __esm({
1911
1990
  "src/dev-server.ts"() {
1912
1991
  "use strict";
1913
1992
  init_runtime_config();
1993
+ init_request_executors();
1914
1994
  EPISODA_API_URL3 = process.env.EPISODA_API_URL || "https://episoda.dev";
1915
1995
  EPISODA_SESSION_TOKEN3 = process.env.EPISODA_SESSION_TOKEN || "";
1916
1996
  DEV_ENVIRONMENT_ID2 = process.env.DEV_ENVIRONMENT_ID || "";
1917
1997
  MODULE_UID2 = process.env.MODULE_UID || "";
1918
1998
  EPISODA_PROJECT_ID3 = process.env.EPISODA_PROJECT_ID || "";
1919
1999
  EPISODA_WORKSPACE_ID3 = process.env.EPISODA_WORKSPACE_ID || "";
2000
+ EPISODA_MACHINE_UUID3 = process.env.EPISODA_MACHINE_UUID || "";
1920
2001
  targetSchema2 = {
1921
2002
  moduleUid: z3.string().optional().describe("Module UID to target (overrides server default)")
1922
2003
  };
@@ -1957,7 +2038,7 @@ var init_dev_server = __esm({
1957
2038
  },
1958
2039
  async (args) => {
1959
2040
  try {
1960
- const result = await apiRequest2(
2041
+ const result = await apiRequest3(
1961
2042
  "POST",
1962
2043
  devPath("/read-file", args.moduleUid),
1963
2044
  {
@@ -1996,7 +2077,7 @@ var init_dev_server = __esm({
1996
2077
  },
1997
2078
  async (args) => {
1998
2079
  try {
1999
- const result = await apiRequest2(
2080
+ const result = await apiRequest3(
2000
2081
  "POST",
2001
2082
  devPath("/write-file", args.moduleUid),
2002
2083
  {
@@ -2040,7 +2121,7 @@ var init_dev_server = __esm({
2040
2121
  },
2041
2122
  async (args) => {
2042
2123
  try {
2043
- const result = await apiRequest2(
2124
+ const result = await apiRequest3(
2044
2125
  "POST",
2045
2126
  devPath("/edit-file", args.moduleUid),
2046
2127
  {
@@ -2088,7 +2169,7 @@ var init_dev_server = __esm({
2088
2169
  },
2089
2170
  async (args) => {
2090
2171
  try {
2091
- const result = await apiRequest2(
2172
+ const result = await apiRequest3(
2092
2173
  "DELETE",
2093
2174
  `${devPath("/delete-file", args.moduleUid)}?path=${encodeURIComponent(args.path)}&recursive=${args.recursive || false}`
2094
2175
  );
@@ -2122,7 +2203,7 @@ var init_dev_server = __esm({
2122
2203
  },
2123
2204
  async (args) => {
2124
2205
  try {
2125
- const result = await apiRequest2(
2206
+ const result = await apiRequest3(
2126
2207
  "POST",
2127
2208
  devPath("/list-dir", args.moduleUid),
2128
2209
  {
@@ -2169,7 +2250,7 @@ var init_dev_server = __esm({
2169
2250
  },
2170
2251
  async (args) => {
2171
2252
  try {
2172
- const result = await apiRequest2(
2253
+ const result = await apiRequest3(
2173
2254
  "POST",
2174
2255
  devPath("/mkdir", args.moduleUid),
2175
2256
  { path: args.path }
@@ -2204,7 +2285,7 @@ var init_dev_server = __esm({
2204
2285
  },
2205
2286
  async (args) => {
2206
2287
  try {
2207
- const result = await apiRequest2(
2288
+ const result = await apiRequest3(
2208
2289
  "POST",
2209
2290
  devPath("/search-files", args.moduleUid),
2210
2291
  {
@@ -2255,7 +2336,7 @@ ${result.data.files.join("\n")}`
2255
2336
  async (args) => {
2256
2337
  try {
2257
2338
  const flags = args.caseSensitive === false ? "-rni" : "-rn";
2258
- const result = await apiRequest2(
2339
+ const result = await apiRequest3(
2259
2340
  "POST",
2260
2341
  devPath("/grep", args.moduleUid),
2261
2342
  {
@@ -2306,7 +2387,7 @@ ${matches}`
2306
2387
  },
2307
2388
  async (args) => {
2308
2389
  try {
2309
- const result = await apiRequest2(
2390
+ const result = await apiRequest3(
2310
2391
  "POST",
2311
2392
  devPath("/exec", args.moduleUid),
2312
2393
  {
@@ -2370,7 +2451,7 @@ Exit code: ${exitCode}`;
2370
2451
  },
2371
2452
  async (args) => {
2372
2453
  try {
2373
- const result = await apiRequest2(
2454
+ const result = await apiRequest3(
2374
2455
  "POST",
2375
2456
  devPath("/batch", args.moduleUid),
2376
2457
  {
@@ -2407,10 +2488,12 @@ Exit code: ${exitCode}`;
2407
2488
  }
2408
2489
  }
2409
2490
  );
2410
- main3().catch((error) => {
2411
- console.error("[episoda-dev] Fatal error:", error);
2412
- process.exit(1);
2413
- });
2491
+ if (process.env.EPISODA_MCP_NO_AUTOSTART !== "1") {
2492
+ startServer3().catch((error) => {
2493
+ console.error("[episoda-dev] Fatal error:", error);
2494
+ process.exit(1);
2495
+ });
2496
+ }
2414
2497
  }
2415
2498
  });
2416
2499
 
@@ -2419,7 +2502,7 @@ var usage = () => {
2419
2502
  console.error("Usage: @episoda/mcp <workflow|git|dev>");
2420
2503
  console.error("Aliases: episoda-workflow, episoda-git, episoda-dev");
2421
2504
  };
2422
- async function main4() {
2505
+ async function main() {
2423
2506
  const [server4] = process.argv.slice(2);
2424
2507
  if (!server4 || server4 === "-h" || server4 === "--help" || server4 === "help") {
2425
2508
  usage();
@@ -2444,7 +2527,7 @@ async function main4() {
2444
2527
  process.exit(2);
2445
2528
  }
2446
2529
  }
2447
- main4().catch((error) => {
2530
+ main().catch((error) => {
2448
2531
  console.error("[episoda-mcp] Fatal error:", error);
2449
2532
  process.exit(1);
2450
2533
  });