marble-headed-mcp 0.1.41 → 0.1.43

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 (2) hide show
  1. package/dist/index.js +18 -108
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -6,12 +6,7 @@ import { promisify } from 'util';
6
6
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
7
7
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
8
8
  import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
9
- const DEFAULT_BASE_URL = 'http://localhost:3000';
10
- const ENVIRONMENT_BASE_URLS = {
11
- localhost: 'http://localhost:3000',
12
- dev: 'https://dev.withmarble.ai',
13
- production: 'https://withmarble.ai',
14
- };
9
+ const DEFAULT_BASE_URL = 'http://localhost:4000';
15
10
  const WEBAPP_LOG_PATH = '/tmp/webapp';
16
11
  const DEFAULT_DEV_CONTAINER_PATH = '/Users/akilanbabu/code/marble-container';
17
12
  const VSCODE_REPO_PATH = '/Users/akilanbabu/code/vscode-source-marble/code-server-7/lib/vscode';
@@ -20,7 +15,6 @@ const DEFAULT_CODEX_LOGGER_PATH = path.join(process.env.HOME || '/Users/akilanba
20
15
  const CODEX_LOGGER_PATH_ENV = 'CODEX_LOGGER_PATH';
21
16
  const CODEX_LOG_PREFIX = 'codex-run-';
22
17
  const execFileAsync = promisify(execFile);
23
- let selectedEnvironment = null;
24
18
  function parseCodexJsonOutput(output) {
25
19
  const result = {};
26
20
  if (!output)
@@ -56,15 +50,6 @@ function parseCodexJsonOutput(output) {
56
50
  function normalizeBaseUrl(input) {
57
51
  return input.replace(/\/+$/, '');
58
52
  }
59
- function parseEnvironment(input) {
60
- if (typeof input !== 'string')
61
- return null;
62
- const normalized = input.trim().toLowerCase();
63
- if (normalized === 'localhost' || normalized === 'dev' || normalized === 'production') {
64
- return normalized;
65
- }
66
- return null;
67
- }
68
53
  function shellQuote(value) {
69
54
  return `'${value.replace(/'/g, `'\"'\"'`)}'`;
70
55
  }
@@ -125,28 +110,19 @@ async function readCodexThreadIdFromLog(filePath) {
125
110
  return null;
126
111
  }
127
112
  }
128
- function resolveHeadedServerRawBaseUrl() {
113
+ function resolveRawBaseUrl() {
129
114
  const base = process.env.HEADED_SERVER_BASE_URL || DEFAULT_BASE_URL;
130
115
  return normalizeBaseUrl(base);
131
116
  }
132
- function resolveHeadedServerAppBaseUrl() {
133
- const raw = resolveHeadedServerRawBaseUrl();
117
+ function resolveAppBaseUrl() {
118
+ const raw = resolveRawBaseUrl();
134
119
  return raw.replace(/\/api\/headed\/?$/, '');
135
120
  }
136
- function resolveBrowserAppBaseUrl() {
137
- if (selectedEnvironment) {
138
- return normalizeBaseUrl(ENVIRONMENT_BASE_URLS[selectedEnvironment]);
139
- }
140
- return resolveHeadedServerAppBaseUrl();
141
- }
142
121
  function buildUrl(pathname) {
143
- return `${resolveHeadedServerAppBaseUrl()}${pathname}`;
122
+ return `${resolveAppBaseUrl()}${pathname}`;
144
123
  }
145
124
  function buildHeadedUrl(pathname) {
146
- return `${resolveHeadedServerAppBaseUrl()}/api/headed${pathname}`;
147
- }
148
- function buildBrowserHeadedUrl(pathname) {
149
- return `${resolveBrowserAppBaseUrl()}/api/headed${pathname}`;
125
+ return `${resolveAppBaseUrl()}/api/headed${pathname}`;
150
126
  }
151
127
  async function postJson(pathname, payload) {
152
128
  const response = await fetch(buildUrl(pathname), {
@@ -185,24 +161,8 @@ async function postHeadedJson(pathname, payload) {
185
161
  }
186
162
  return { status: response.status, ok: response.ok, text, json };
187
163
  }
188
- async function postBrowserHeadedJson(pathname, payload) {
189
- const response = await fetch(buildBrowserHeadedUrl(pathname), {
190
- method: 'POST',
191
- headers: { 'Content-Type': 'application/json' },
192
- body: JSON.stringify(payload ?? {}),
193
- });
194
- const text = await response.text();
195
- let json = null;
196
- try {
197
- json = JSON.parse(text);
198
- }
199
- catch (error) {
200
- json = null;
201
- }
202
- return { status: response.status, ok: response.ok, text, json };
203
- }
204
- async function getBrowserHeadedText(pathname) {
205
- const response = await fetch(buildBrowserHeadedUrl(pathname));
164
+ async function getHeadedText(pathname) {
165
+ const response = await fetch(buildHeadedUrl(pathname));
206
166
  const text = await response.text();
207
167
  return { status: response.status, ok: response.ok, text };
208
168
  }
@@ -438,22 +398,6 @@ async function saveImageFromUrl({ url, filename }) {
438
398
  return { ok: true, path: targetPath, bytes: buffer.length };
439
399
  }
440
400
  const TOOLS = [
441
- {
442
- name: 'set_environment',
443
- description: 'Set the base URL for browser-session actions. localhost -> http://localhost:3000, dev -> https://dev.withmarble.ai, production -> https://withmarble.ai. Non-browser server/workflow actions continue using HEADED_SERVER_BASE_URL (default localhost:3000).',
444
- inputSchema: {
445
- type: 'object',
446
- properties: {
447
- environment: {
448
- type: 'string',
449
- enum: ['localhost', 'dev', 'production'],
450
- description: 'Environment to target.',
451
- },
452
- },
453
- required: ['environment'],
454
- additionalProperties: false,
455
- },
456
- },
457
401
  {
458
402
  name: 'headed_start_session',
459
403
  description: 'Start a headed browser session (if email or password are not provided, they default to akilan@withmarble.ai and marbledebug123, respectively). Remember to call `headed_end_session` when finished to avoid leaving sessions open.',
@@ -772,50 +716,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
772
716
  const { name, arguments: args } = request.params;
773
717
  try {
774
718
  switch (name) {
775
- case 'set_environment': {
776
- const environment = parseEnvironment(args?.environment);
777
- if (!environment) {
778
- return {
779
- content: [
780
- {
781
- type: 'text',
782
- text: JSON.stringify({
783
- ok: false,
784
- error: 'environment must be one of: localhost, dev, production.',
785
- }, null, 2),
786
- },
787
- ],
788
- };
789
- }
790
- selectedEnvironment = environment;
791
- const browserAppBaseUrl = resolveBrowserAppBaseUrl();
792
- const headedServerAppBaseUrl = resolveHeadedServerAppBaseUrl();
793
- return {
794
- content: [
795
- {
796
- type: 'text',
797
- text: JSON.stringify({
798
- ok: true,
799
- environment,
800
- browserAppBaseUrl,
801
- browserHeadedApiBaseUrl: `${browserAppBaseUrl}/api/headed`,
802
- headedServerAppBaseUrl,
803
- headedServerHeadedApiBaseUrl: `${headedServerAppBaseUrl}/api/headed`,
804
- }, null, 2),
805
- },
806
- ],
807
- };
808
- }
809
719
  case 'headed_start_session': {
810
- const result = await postBrowserHeadedJson('/start_session', args);
720
+ const result = await postHeadedJson('/start_session', args);
811
721
  return { content: [{ type: 'text', text: JSON.stringify(result.json || { status: result.status, body: result.text }, null, 2) }] };
812
722
  }
813
723
  case 'headed_end_session': {
814
- const result = await postBrowserHeadedJson('/end_session', args);
724
+ const result = await postHeadedJson('/end_session', args);
815
725
  return { content: [{ type: 'text', text: JSON.stringify(result.json || { status: result.status, body: result.text }, null, 2) }] };
816
726
  }
817
727
  case 'headed_navigate_to_project': {
818
- const result = await postBrowserHeadedJson('/navigate_to_project', args);
728
+ const result = await postHeadedJson('/navigate_to_project', args);
819
729
  return { content: [{ type: 'text', text: JSON.stringify(result.json || { status: result.status, body: result.text }, null, 2) }] };
820
730
  }
821
731
  case 'navigate_to_url': {
@@ -828,15 +738,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
828
738
  if (!browserSessionId || Number.isNaN(browserSessionId)) {
829
739
  return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: 'browserSession is required.' }, null, 2) }] };
830
740
  }
831
- const result = await postBrowserHeadedJson('/navigate_to_url', { browserSessionId, url });
741
+ const result = await postHeadedJson('/navigate_to_url', { browserSessionId, url });
832
742
  return { content: [{ type: 'text', text: JSON.stringify(result.json || { status: result.status, body: result.text }, null, 2) }] };
833
743
  }
834
744
  case 'start_plan_project': {
835
- const result = await postBrowserHeadedJson('/start_plan_project', args);
745
+ const result = await postHeadedJson('/start_plan_project', args);
836
746
  return { content: [{ type: 'text', text: JSON.stringify(result.json || { status: result.status, body: result.text }, null, 2) }] };
837
747
  }
838
748
  case 'headed_send_msg': {
839
- const result = await postBrowserHeadedJson('/send_msg_headed', args);
749
+ const result = await postHeadedJson('/send_msg_headed', args);
840
750
  const payload = (result.json && typeof result.json === 'object') ? result.json : null;
841
751
  if (payload?.workflowRunId) {
842
752
  const summaryResult = await fetchWorkflowEntries(String(payload.workflowRunId));
@@ -851,7 +761,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
851
761
  return { content: [{ type: 'text', text: JSON.stringify(payload || { status: result.status, body: result.text }, null, 2) }] };
852
762
  }
853
763
  case 'take_screenshot': {
854
- const result = await postBrowserHeadedJson('/take_screenshot', args);
764
+ const result = await postHeadedJson('/take_screenshot', args);
855
765
  const payload = (result.json && typeof result.json === 'object') ? result.json : null;
856
766
  if (payload?.workflowRunId) {
857
767
  const summaryResult = await fetchWorkflowEntries(String(payload.workflowRunId));
@@ -866,19 +776,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
866
776
  return { content: [{ type: 'text', text: JSON.stringify(payload || { status: result.status, body: result.text }, null, 2) }] };
867
777
  }
868
778
  case 'take_screenshot': {
869
- const result = await postBrowserHeadedJson('/take_screenshot', args);
779
+ const result = await postHeadedJson('/take_screenshot', args);
870
780
  return { content: [{ type: 'text', text: JSON.stringify(result.json || { status: result.status, body: result.text }, null, 2) }] };
871
781
  }
872
782
  case 'screenshots_preview': {
873
783
  const payload = args?.screenshotUrls
874
784
  ? { screenshotUrls: args.screenshotUrls }
875
785
  : args?.response || args;
876
- const result = await postBrowserHeadedJson('/preview_screenshots', payload);
786
+ const result = await postHeadedJson('/preview_screenshots', payload);
877
787
  return { content: [{ type: 'text', text: JSON.stringify({ status: result.status, ok: result.ok, html: result.text }, null, 2) }] };
878
788
  }
879
789
  case 'screenshots_get_preview': {
880
790
  const previewId = args?.previewId;
881
- const result = await getBrowserHeadedText(`/preview_screenshots/${encodeURIComponent(previewId)}`);
791
+ const result = await getHeadedText(`/preview_screenshots/${encodeURIComponent(previewId)}`);
882
792
  return { content: [{ type: 'text', text: JSON.stringify({ status: result.status, ok: result.ok, html: result.text }, null, 2) }] };
883
793
  }
884
794
  case 'workflow_end_to_end_project_generation': {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "marble-headed-mcp",
3
- "version": "0.1.41",
3
+ "version": "0.1.43",
4
4
  "description": "MCP server for Marble headed automation endpoints",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",