@episoda/mcp 0.1.11 → 0.1.12

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,7 +725,7 @@ ${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}`);
728
+ const result = await apiRequest2("GET", `/api/modules/${args.module_uid}`);
636
729
  if (!result.success || !result.module) {
637
730
  return { content: [{ type: "text", text: `Error: ${result.error || "Module not found"}` }], isError: true };
638
731
  }
@@ -659,7 +752,7 @@ ${mod.description_md || "_No description_"}`
659
752
  }
660
753
  },
661
754
  async (args) => {
662
- const result = await apiRequest("DELETE", `/api/modules/${args.module_uid}`);
755
+ const result = await apiRequest2("DELETE", `/api/modules/${args.module_uid}`);
663
756
  if (!result.success) {
664
757
  return { content: [{ type: "text", text: `Error: ${result.error}` }], isError: true };
665
758
  }
@@ -692,7 +785,7 @@ USE THIS WHEN:
692
785
  if (args.limit) params.set("limit", String(args.limit));
693
786
  if (args.include_task_counts) params.set("includeTaskCounts", "true");
694
787
  const query = params.toString();
695
- const result = await apiRequest("GET", `/api/modules${query ? `?${query}` : ""}`);
788
+ const result = await apiRequest2("GET", `/api/modules${query ? `?${query}` : ""}`);
696
789
  if (!result.success || !result.modules) {
697
790
  return { content: [{ type: "text", text: `Error: ${result.error || "Failed to fetch modules"}` }], isError: true };
698
791
  }
@@ -725,22 +818,7 @@ Use this instead of request_review or mark_done for full control.`,
725
818
  target_state: z.enum(["ready", "doing", "review", "done"]).describe("Target state for the module")
726
819
  }
727
820
  },
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
- }
821
+ handleTransitionModule
744
822
  );
745
823
  server.registerTool(
746
824
  "archive_module",
@@ -751,7 +829,7 @@ Use this instead of request_review or mark_done for full control.`,
751
829
  }
752
830
  },
753
831
  async (args) => {
754
- const result = await apiRequest(
832
+ const result = await apiRequest2(
755
833
  "POST",
756
834
  `/api/modules/${args.module_uid}/archive`,
757
835
  {}
@@ -776,7 +854,7 @@ Use this instead of request_review or mark_done for full control.`,
776
854
  }
777
855
  },
778
856
  async (args) => {
779
- const result = await apiRequest("GET", `/api/knowledge/${args.knowledge_id}`);
857
+ const result = await apiRequest2("GET", `/api/knowledge/${args.knowledge_id}`);
780
858
  if (!result.success || !result.knowledge) {
781
859
  return { content: [{ type: "text", text: `Error: ${result.error || "Knowledge not found"}` }], isError: true };
782
860
  }
@@ -819,7 +897,7 @@ DO NOT USE WHEN:
819
897
  }
820
898
  },
821
899
  async (args) => {
822
- const result = await apiRequest(
900
+ const result = await apiRequest2(
823
901
  "POST",
824
902
  "/api/search/semantic",
825
903
  {
@@ -880,7 +958,7 @@ DO NOT USE WHEN:
880
958
  }
881
959
  },
882
960
  async (args) => {
883
- const result = await apiRequest(
961
+ const result = await apiRequest2(
884
962
  "POST",
885
963
  "/api/search/text",
886
964
  {
@@ -940,13 +1018,13 @@ This is the recommended default search for most queries.`,
940
1018
  },
941
1019
  async (args) => {
942
1020
  const [semanticResult, textResult] = await Promise.all([
943
- apiRequest("POST", "/api/search/semantic", {
1021
+ apiRequest2("POST", "/api/search/semantic", {
944
1022
  query: args.query,
945
1023
  types: ["knowledge"],
946
1024
  limit: 50,
947
1025
  threshold: 0.4
948
1026
  }),
949
- apiRequest("POST", "/api/search/text", {
1027
+ apiRequest2("POST", "/api/search/text", {
950
1028
  query: args.query,
951
1029
  types: ["knowledge"],
952
1030
  limit: 50
@@ -1019,7 +1097,7 @@ USE THIS WHEN:
1019
1097
  }
1020
1098
  },
1021
1099
  async (args) => {
1022
- const result = await apiRequest(
1100
+ const result = await apiRequest2(
1023
1101
  "POST",
1024
1102
  "/api/search/conversations",
1025
1103
  {
@@ -1064,7 +1142,7 @@ ${list}`
1064
1142
  }
1065
1143
  },
1066
1144
  async (args) => {
1067
- const result = await apiRequest(
1145
+ const result = await apiRequest2(
1068
1146
  "GET",
1069
1147
  `/api/modules/${args.module_uid}/knowledge`
1070
1148
  );
@@ -1095,7 +1173,7 @@ ${list}`
1095
1173
  }
1096
1174
  },
1097
1175
  async (args) => {
1098
- const result = await apiRequest(
1176
+ const result = await apiRequest2(
1099
1177
  "POST",
1100
1178
  `/api/modules/${args.module_uid}/knowledge/link`,
1101
1179
  { knowledge_id: args.knowledge_id }
@@ -1131,7 +1209,7 @@ All documentation MUST be created in the knowledge base, not as markdown files.`
1131
1209
  }
1132
1210
  },
1133
1211
  async (args) => {
1134
- const result = await apiRequest("POST", "/api/knowledge", {
1212
+ const result = await apiRequest2("POST", "/api/knowledge", {
1135
1213
  title: args.title,
1136
1214
  domain: args.domain,
1137
1215
  doc_type: args.doc_type,
@@ -1170,7 +1248,7 @@ All documentation MUST be created in the knowledge base, not as markdown files.`
1170
1248
  if (args.domain) updates.domain = args.domain;
1171
1249
  if (args.doc_type) updates.doc_type = args.doc_type;
1172
1250
  if (args.significance !== void 0) updates.significance = args.significance;
1173
- const result = await apiRequest("PATCH", `/api/knowledge/${args.knowledge_id}`, updates);
1251
+ const result = await apiRequest2("PATCH", `/api/knowledge/${args.knowledge_id}`, updates);
1174
1252
  if (!result.success) {
1175
1253
  return { content: [{ type: "text", text: `Error: ${result.error}` }], isError: true };
1176
1254
  }
@@ -1191,7 +1269,7 @@ All documentation MUST be created in the knowledge base, not as markdown files.`
1191
1269
  }
1192
1270
  },
1193
1271
  async (args) => {
1194
- const result = await apiRequest("DELETE", `/api/knowledge/${args.knowledge_id}`);
1272
+ const result = await apiRequest2("DELETE", `/api/knowledge/${args.knowledge_id}`);
1195
1273
  if (!result.success) {
1196
1274
  return { content: [{ type: "text", text: `Error: ${result.error}` }], isError: true };
1197
1275
  }
@@ -1212,7 +1290,7 @@ All documentation MUST be created in the knowledge base, not as markdown files.`
1212
1290
  }
1213
1291
  },
1214
1292
  async (args) => {
1215
- const result = await apiRequest(
1293
+ const result = await apiRequest2(
1216
1294
  "GET",
1217
1295
  `/api/agent/context/module/${args.module_uid}`
1218
1296
  );
@@ -1234,7 +1312,7 @@ All documentation MUST be created in the knowledge base, not as markdown files.`
1234
1312
  inputSchema: {}
1235
1313
  },
1236
1314
  async () => {
1237
- const result = await apiRequest(
1315
+ const result = await apiRequest2(
1238
1316
  "GET",
1239
1317
  "/api/agent/context/system"
1240
1318
  );
@@ -1249,15 +1327,20 @@ All documentation MUST be created in the knowledge base, not as markdown files.`
1249
1327
  };
1250
1328
  }
1251
1329
  );
1252
- main().catch((error) => {
1253
- console.error("[episoda-workflow] Fatal error:", error);
1254
- process.exit(1);
1255
- });
1330
+ if (process.env.EPISODA_MCP_NO_AUTOSTART !== "1") {
1331
+ startServer().catch((error) => {
1332
+ console.error("[episoda-workflow] Fatal error:", error);
1333
+ process.exit(1);
1334
+ });
1335
+ }
1256
1336
  }
1257
1337
  });
1258
1338
 
1259
1339
  // src/git-server.ts
1260
1340
  var git_server_exports = {};
1341
+ __export(git_server_exports, {
1342
+ startServer: () => startServer2
1343
+ });
1261
1344
  import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
1262
1345
  import { StdioServerTransport as StdioServerTransport2 } from "@modelcontextprotocol/sdk/server/stdio.js";
1263
1346
  import { z as z2 } from "zod";
@@ -1270,28 +1353,17 @@ async function execCommand(command, options = {}) {
1270
1353
  error: "Module target missing. Provide moduleUid in the tool call or set MODULE_UID/DEV_ENVIRONMENT_ID."
1271
1354
  };
1272
1355
  }
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;
1356
+ return executeGitCommandRequest(
1357
+ {
1358
+ apiUrl: EPISODA_API_URL2,
1359
+ sessionToken: EPISODA_SESSION_TOKEN2,
1360
+ projectId: EPISODA_PROJECT_ID2,
1361
+ workspaceId: EPISODA_WORKSPACE_ID2,
1362
+ // EP1376: Intentional parity with workflow/dev MCP servers.
1363
+ machineUuid: EPISODA_MACHINE_UUID2
1364
+ },
1365
+ { target, command, cwd, timeout }
1366
+ );
1295
1367
  }
1296
1368
  function formatGitOutput(result, errorPrefix = "Git error") {
1297
1369
  if (!result.success || !result.data) {
@@ -1313,12 +1385,13 @@ ${errorOutput}` }],
1313
1385
  content: [{ type: "text", text: stdout || "(no output)" }]
1314
1386
  };
1315
1387
  }
1316
- async function main2() {
1388
+ async function startServer2() {
1317
1389
  const runtimeConfig = await hydrateRuntimeConfig();
1318
1390
  EPISODA_API_URL2 = runtimeConfig.apiUrl;
1319
1391
  EPISODA_SESSION_TOKEN2 = runtimeConfig.sessionToken;
1320
1392
  EPISODA_PROJECT_ID2 = runtimeConfig.projectId;
1321
1393
  EPISODA_WORKSPACE_ID2 = runtimeConfig.workspaceId;
1394
+ EPISODA_MACHINE_UUID2 = runtimeConfig.machineUuid || "";
1322
1395
  if (!MODULE_UID && !DEV_ENVIRONMENT_ID) {
1323
1396
  console.warn("[episoda-git] Warning: MODULE_UID/DEV_ENVIRONMENT_ID not set. Provide moduleUid per tool call.");
1324
1397
  }
@@ -1330,17 +1403,19 @@ async function main2() {
1330
1403
  console.error(`[episoda-git] Dev Target: ${devTarget}`);
1331
1404
  console.error(`[episoda-git] Token: ${EPISODA_SESSION_TOKEN2 ? "****" : "NOT SET"}`);
1332
1405
  }
1333
- var EPISODA_API_URL2, EPISODA_SESSION_TOKEN2, DEV_ENVIRONMENT_ID, MODULE_UID, EPISODA_PROJECT_ID2, EPISODA_WORKSPACE_ID2, targetSchema, server2;
1406
+ var EPISODA_API_URL2, EPISODA_SESSION_TOKEN2, DEV_ENVIRONMENT_ID, MODULE_UID, EPISODA_PROJECT_ID2, EPISODA_WORKSPACE_ID2, EPISODA_MACHINE_UUID2, targetSchema, server2;
1334
1407
  var init_git_server = __esm({
1335
1408
  "src/git-server.ts"() {
1336
1409
  "use strict";
1337
1410
  init_runtime_config();
1411
+ init_request_executors();
1338
1412
  EPISODA_API_URL2 = process.env.EPISODA_API_URL || "https://episoda.dev";
1339
1413
  EPISODA_SESSION_TOKEN2 = process.env.EPISODA_SESSION_TOKEN || "";
1340
1414
  DEV_ENVIRONMENT_ID = process.env.DEV_ENVIRONMENT_ID || "";
1341
1415
  MODULE_UID = process.env.MODULE_UID || "";
1342
1416
  EPISODA_PROJECT_ID2 = process.env.EPISODA_PROJECT_ID || "";
1343
1417
  EPISODA_WORKSPACE_ID2 = process.env.EPISODA_WORKSPACE_ID || "";
1418
+ EPISODA_MACHINE_UUID2 = process.env.EPISODA_MACHINE_UUID || "";
1344
1419
  targetSchema = {
1345
1420
  moduleUid: z2.string().optional().describe("Module UID to target (overrides server default)")
1346
1421
  };
@@ -1839,42 +1914,37 @@ var init_git_server = __esm({
1839
1914
  };
1840
1915
  }
1841
1916
  );
1842
- main2().catch((error) => {
1843
- console.error("[episoda-git] Fatal error:", error);
1844
- process.exit(1);
1845
- });
1917
+ if (process.env.EPISODA_MCP_NO_AUTOSTART !== "1") {
1918
+ startServer2().catch((error) => {
1919
+ console.error("[episoda-git] Fatal error:", error);
1920
+ process.exit(1);
1921
+ });
1922
+ }
1846
1923
  }
1847
1924
  });
1848
1925
 
1849
1926
  // src/dev-server.ts
1850
1927
  var dev_server_exports = {};
1928
+ __export(dev_server_exports, {
1929
+ startServer: () => startServer3
1930
+ });
1851
1931
  import { McpServer as McpServer3 } from "@modelcontextprotocol/sdk/server/mcp.js";
1852
1932
  import { StdioServerTransport as StdioServerTransport3 } from "@modelcontextprotocol/sdk/server/stdio.js";
1853
1933
  import { z as z3 } from "zod";
1854
- async function apiRequest2(method, path2, body) {
1855
- const url = `${EPISODA_API_URL3}${path2}`;
1856
- const options = {
1934
+ async function apiRequest3(method, path2, body) {
1935
+ return apiRequest(
1936
+ {
1937
+ apiUrl: EPISODA_API_URL3,
1938
+ sessionToken: EPISODA_SESSION_TOKEN3,
1939
+ projectId: EPISODA_PROJECT_ID3,
1940
+ workspaceId: EPISODA_WORKSPACE_ID3,
1941
+ // EP1376: Intentional parity with workflow/git MCP servers.
1942
+ machineUuid: EPISODA_MACHINE_UUID3
1943
+ },
1857
1944
  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();
1945
+ path2,
1946
+ body
1947
+ );
1878
1948
  }
1879
1949
  function devPath(endpoint, overrideTarget) {
1880
1950
  const target = overrideTarget || MODULE_UID2 || DEV_ENVIRONMENT_ID2;
@@ -1889,12 +1959,13 @@ function formatSize(bytes) {
1889
1959
  if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
1890
1960
  return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)}GB`;
1891
1961
  }
1892
- async function main3() {
1962
+ async function startServer3() {
1893
1963
  const runtimeConfig = await hydrateRuntimeConfig();
1894
1964
  EPISODA_API_URL3 = runtimeConfig.apiUrl;
1895
1965
  EPISODA_SESSION_TOKEN3 = runtimeConfig.sessionToken;
1896
1966
  EPISODA_PROJECT_ID3 = runtimeConfig.projectId;
1897
1967
  EPISODA_WORKSPACE_ID3 = runtimeConfig.workspaceId;
1968
+ EPISODA_MACHINE_UUID3 = runtimeConfig.machineUuid || "";
1898
1969
  if (!MODULE_UID2 && !DEV_ENVIRONMENT_ID2) {
1899
1970
  console.warn("[episoda-dev] Warning: MODULE_UID/DEV_ENVIRONMENT_ID not set. Provide moduleUid per tool call.");
1900
1971
  }
@@ -1906,17 +1977,19 @@ async function main3() {
1906
1977
  console.error(`[episoda-dev] Dev Target: ${devTarget}`);
1907
1978
  console.error(`[episoda-dev] Token: ${EPISODA_SESSION_TOKEN3 ? "****" : "NOT SET"}`);
1908
1979
  }
1909
- var EPISODA_API_URL3, EPISODA_SESSION_TOKEN3, DEV_ENVIRONMENT_ID2, MODULE_UID2, EPISODA_PROJECT_ID3, EPISODA_WORKSPACE_ID3, targetSchema2, server3;
1980
+ var EPISODA_API_URL3, EPISODA_SESSION_TOKEN3, DEV_ENVIRONMENT_ID2, MODULE_UID2, EPISODA_PROJECT_ID3, EPISODA_WORKSPACE_ID3, EPISODA_MACHINE_UUID3, targetSchema2, server3;
1910
1981
  var init_dev_server = __esm({
1911
1982
  "src/dev-server.ts"() {
1912
1983
  "use strict";
1913
1984
  init_runtime_config();
1985
+ init_request_executors();
1914
1986
  EPISODA_API_URL3 = process.env.EPISODA_API_URL || "https://episoda.dev";
1915
1987
  EPISODA_SESSION_TOKEN3 = process.env.EPISODA_SESSION_TOKEN || "";
1916
1988
  DEV_ENVIRONMENT_ID2 = process.env.DEV_ENVIRONMENT_ID || "";
1917
1989
  MODULE_UID2 = process.env.MODULE_UID || "";
1918
1990
  EPISODA_PROJECT_ID3 = process.env.EPISODA_PROJECT_ID || "";
1919
1991
  EPISODA_WORKSPACE_ID3 = process.env.EPISODA_WORKSPACE_ID || "";
1992
+ EPISODA_MACHINE_UUID3 = process.env.EPISODA_MACHINE_UUID || "";
1920
1993
  targetSchema2 = {
1921
1994
  moduleUid: z3.string().optional().describe("Module UID to target (overrides server default)")
1922
1995
  };
@@ -1957,7 +2030,7 @@ var init_dev_server = __esm({
1957
2030
  },
1958
2031
  async (args) => {
1959
2032
  try {
1960
- const result = await apiRequest2(
2033
+ const result = await apiRequest3(
1961
2034
  "POST",
1962
2035
  devPath("/read-file", args.moduleUid),
1963
2036
  {
@@ -1996,7 +2069,7 @@ var init_dev_server = __esm({
1996
2069
  },
1997
2070
  async (args) => {
1998
2071
  try {
1999
- const result = await apiRequest2(
2072
+ const result = await apiRequest3(
2000
2073
  "POST",
2001
2074
  devPath("/write-file", args.moduleUid),
2002
2075
  {
@@ -2040,7 +2113,7 @@ var init_dev_server = __esm({
2040
2113
  },
2041
2114
  async (args) => {
2042
2115
  try {
2043
- const result = await apiRequest2(
2116
+ const result = await apiRequest3(
2044
2117
  "POST",
2045
2118
  devPath("/edit-file", args.moduleUid),
2046
2119
  {
@@ -2088,7 +2161,7 @@ var init_dev_server = __esm({
2088
2161
  },
2089
2162
  async (args) => {
2090
2163
  try {
2091
- const result = await apiRequest2(
2164
+ const result = await apiRequest3(
2092
2165
  "DELETE",
2093
2166
  `${devPath("/delete-file", args.moduleUid)}?path=${encodeURIComponent(args.path)}&recursive=${args.recursive || false}`
2094
2167
  );
@@ -2122,7 +2195,7 @@ var init_dev_server = __esm({
2122
2195
  },
2123
2196
  async (args) => {
2124
2197
  try {
2125
- const result = await apiRequest2(
2198
+ const result = await apiRequest3(
2126
2199
  "POST",
2127
2200
  devPath("/list-dir", args.moduleUid),
2128
2201
  {
@@ -2169,7 +2242,7 @@ var init_dev_server = __esm({
2169
2242
  },
2170
2243
  async (args) => {
2171
2244
  try {
2172
- const result = await apiRequest2(
2245
+ const result = await apiRequest3(
2173
2246
  "POST",
2174
2247
  devPath("/mkdir", args.moduleUid),
2175
2248
  { path: args.path }
@@ -2204,7 +2277,7 @@ var init_dev_server = __esm({
2204
2277
  },
2205
2278
  async (args) => {
2206
2279
  try {
2207
- const result = await apiRequest2(
2280
+ const result = await apiRequest3(
2208
2281
  "POST",
2209
2282
  devPath("/search-files", args.moduleUid),
2210
2283
  {
@@ -2255,7 +2328,7 @@ ${result.data.files.join("\n")}`
2255
2328
  async (args) => {
2256
2329
  try {
2257
2330
  const flags = args.caseSensitive === false ? "-rni" : "-rn";
2258
- const result = await apiRequest2(
2331
+ const result = await apiRequest3(
2259
2332
  "POST",
2260
2333
  devPath("/grep", args.moduleUid),
2261
2334
  {
@@ -2306,7 +2379,7 @@ ${matches}`
2306
2379
  },
2307
2380
  async (args) => {
2308
2381
  try {
2309
- const result = await apiRequest2(
2382
+ const result = await apiRequest3(
2310
2383
  "POST",
2311
2384
  devPath("/exec", args.moduleUid),
2312
2385
  {
@@ -2370,7 +2443,7 @@ Exit code: ${exitCode}`;
2370
2443
  },
2371
2444
  async (args) => {
2372
2445
  try {
2373
- const result = await apiRequest2(
2446
+ const result = await apiRequest3(
2374
2447
  "POST",
2375
2448
  devPath("/batch", args.moduleUid),
2376
2449
  {
@@ -2407,10 +2480,12 @@ Exit code: ${exitCode}`;
2407
2480
  }
2408
2481
  }
2409
2482
  );
2410
- main3().catch((error) => {
2411
- console.error("[episoda-dev] Fatal error:", error);
2412
- process.exit(1);
2413
- });
2483
+ if (process.env.EPISODA_MCP_NO_AUTOSTART !== "1") {
2484
+ startServer3().catch((error) => {
2485
+ console.error("[episoda-dev] Fatal error:", error);
2486
+ process.exit(1);
2487
+ });
2488
+ }
2414
2489
  }
2415
2490
  });
2416
2491
 
@@ -2419,7 +2494,7 @@ var usage = () => {
2419
2494
  console.error("Usage: @episoda/mcp <workflow|git|dev>");
2420
2495
  console.error("Aliases: episoda-workflow, episoda-git, episoda-dev");
2421
2496
  };
2422
- async function main4() {
2497
+ async function main() {
2423
2498
  const [server4] = process.argv.slice(2);
2424
2499
  if (!server4 || server4 === "-h" || server4 === "--help" || server4 === "help") {
2425
2500
  usage();
@@ -2444,7 +2519,7 @@ async function main4() {
2444
2519
  process.exit(2);
2445
2520
  }
2446
2521
  }
2447
- main4().catch((error) => {
2522
+ main().catch((error) => {
2448
2523
  console.error("[episoda-mcp] Fatal error:", error);
2449
2524
  process.exit(1);
2450
2525
  });