marble-headed-mcp 0.1.40 → 0.1.42

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 +35 -48
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -125,22 +125,25 @@ async function readCodexThreadIdFromLog(filePath) {
125
125
  return null;
126
126
  }
127
127
  }
128
- function resolveRawBaseUrl() {
129
- if (selectedEnvironment) {
130
- return normalizeBaseUrl(ENVIRONMENT_BASE_URLS[selectedEnvironment]);
131
- }
128
+ function resolveHeadedServerRawBaseUrl() {
132
129
  const base = process.env.HEADED_SERVER_BASE_URL || DEFAULT_BASE_URL;
133
130
  return normalizeBaseUrl(base);
134
131
  }
135
- function resolveAppBaseUrl() {
136
- const raw = resolveRawBaseUrl();
132
+ function resolveHeadedServerAppBaseUrl() {
133
+ const raw = resolveHeadedServerRawBaseUrl();
137
134
  return raw.replace(/\/api\/headed\/?$/, '');
138
135
  }
136
+ function resolveBrowserAppBaseUrl() {
137
+ if (selectedEnvironment) {
138
+ return normalizeBaseUrl(ENVIRONMENT_BASE_URLS[selectedEnvironment]);
139
+ }
140
+ return normalizeBaseUrl(ENVIRONMENT_BASE_URLS.localhost);
141
+ }
139
142
  function buildUrl(pathname) {
140
- return `${resolveAppBaseUrl()}${pathname}`;
143
+ return `${resolveHeadedServerAppBaseUrl()}${pathname}`;
141
144
  }
142
145
  function buildHeadedUrl(pathname) {
143
- return `${resolveAppBaseUrl()}/api/headed${pathname}`;
146
+ return `${resolveHeadedServerAppBaseUrl()}/api/headed${pathname}`;
144
147
  }
145
148
  async function postJson(pathname, payload) {
146
149
  const response = await fetch(buildUrl(pathname), {
@@ -184,6 +187,16 @@ async function getHeadedText(pathname) {
184
187
  const text = await response.text();
185
188
  return { status: response.status, ok: response.ok, text };
186
189
  }
190
+ function withBrowserAppBaseUrl(payload) {
191
+ const appBaseUrl = resolveBrowserAppBaseUrl();
192
+ if (payload && typeof payload === 'object' && !Array.isArray(payload)) {
193
+ return {
194
+ ...payload,
195
+ appBaseUrl,
196
+ };
197
+ }
198
+ return { appBaseUrl };
199
+ }
187
200
  async function getJson(pathname) {
188
201
  const response = await fetch(buildUrl(pathname));
189
202
  const text = await response.text();
@@ -418,7 +431,7 @@ async function saveImageFromUrl({ url, filename }) {
418
431
  const TOOLS = [
419
432
  {
420
433
  name: 'set_environment',
421
- description: 'Set the base URL for HTTP-backed actions. localhost -> http://localhost:3000, dev -> https://dev.withmarble.ai, production -> https://withmarble.ai.',
434
+ description: 'Set the in-browser app environment used by headed session actions. localhost -> http://localhost:3000, dev -> https://dev.withmarble.ai, production -> https://withmarble.ai. API calls still go to HEADED_SERVER_BASE_URL (default localhost:3000).',
422
435
  inputSchema: {
423
436
  type: 'object',
424
437
  properties: {
@@ -469,20 +482,6 @@ const TOOLS = [
469
482
  additionalProperties: false,
470
483
  },
471
484
  },
472
- {
473
- name: 'navigate_to_url',
474
- description: 'Navigate a headed session to a URL. Prefer `headed_navigate_to_project` when possible; use this only for validating code in a separate browser tab (e.g., proxy URL access). If the URL already exists (ignoring query parameters), the existing tab will be focused instead of creating a new one.',
475
- inputSchema: {
476
- type: 'object',
477
- properties: {
478
- browserSession: { type: 'number', description: 'Headed browser session id.' },
479
- browserSessionId: { type: 'number', description: 'Alias for browserSession.' },
480
- url: { type: 'string', description: 'URL to navigate to.' },
481
- },
482
- required: ['url'],
483
- additionalProperties: false,
484
- },
485
- },
486
485
  {
487
486
  name: 'start_plan_project',
488
487
  description: 'Navigate to a project within a plan. If startProject is true (default), clicks BEGIN SIMULATION; if false, only navigates to the plan project without starting the simulation.',
@@ -766,7 +765,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
766
765
  };
767
766
  }
768
767
  selectedEnvironment = environment;
769
- const appBaseUrl = resolveAppBaseUrl();
768
+ const browserAppBaseUrl = resolveBrowserAppBaseUrl();
769
+ const headedServerAppBaseUrl = resolveHeadedServerAppBaseUrl();
770
770
  return {
771
771
  content: [
772
772
  {
@@ -774,45 +774,32 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
774
774
  text: JSON.stringify({
775
775
  ok: true,
776
776
  environment,
777
- appBaseUrl,
778
- apiBaseUrl: `${appBaseUrl}/api`,
779
- headedApiBaseUrl: `${appBaseUrl}/api/headed`,
777
+ browserAppBaseUrl,
778
+ headedServerAppBaseUrl,
779
+ headedServerHeadedApiBaseUrl: `${headedServerAppBaseUrl}/api/headed`,
780
780
  }, null, 2),
781
781
  },
782
782
  ],
783
783
  };
784
784
  }
785
785
  case 'headed_start_session': {
786
- const result = await postHeadedJson('/start_session', args);
786
+ const result = await postHeadedJson('/start_session', withBrowserAppBaseUrl(args));
787
787
  return { content: [{ type: 'text', text: JSON.stringify(result.json || { status: result.status, body: result.text }, null, 2) }] };
788
788
  }
789
789
  case 'headed_end_session': {
790
- const result = await postHeadedJson('/end_session', args);
790
+ const result = await postHeadedJson('/end_session', withBrowserAppBaseUrl(args));
791
791
  return { content: [{ type: 'text', text: JSON.stringify(result.json || { status: result.status, body: result.text }, null, 2) }] };
792
792
  }
793
793
  case 'headed_navigate_to_project': {
794
- const result = await postHeadedJson('/navigate_to_project', args);
795
- return { content: [{ type: 'text', text: JSON.stringify(result.json || { status: result.status, body: result.text }, null, 2) }] };
796
- }
797
- case 'navigate_to_url': {
798
- const url = typeof args?.url === 'string' ? args.url.trim() : '';
799
- const browserSessionRaw = args?.browserSession ?? args?.browserSessionId;
800
- const browserSessionId = typeof browserSessionRaw === 'number' ? browserSessionRaw : Number(browserSessionRaw);
801
- if (!url) {
802
- return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: 'url is required.' }, null, 2) }] };
803
- }
804
- if (!browserSessionId || Number.isNaN(browserSessionId)) {
805
- return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: 'browserSession is required.' }, null, 2) }] };
806
- }
807
- const result = await postHeadedJson('/navigate_to_url', { browserSessionId, url });
794
+ const result = await postHeadedJson('/navigate_to_project', withBrowserAppBaseUrl(args));
808
795
  return { content: [{ type: 'text', text: JSON.stringify(result.json || { status: result.status, body: result.text }, null, 2) }] };
809
796
  }
810
797
  case 'start_plan_project': {
811
- const result = await postHeadedJson('/start_plan_project', args);
798
+ const result = await postHeadedJson('/start_plan_project', withBrowserAppBaseUrl(args));
812
799
  return { content: [{ type: 'text', text: JSON.stringify(result.json || { status: result.status, body: result.text }, null, 2) }] };
813
800
  }
814
801
  case 'headed_send_msg': {
815
- const result = await postHeadedJson('/send_msg_headed', args);
802
+ const result = await postHeadedJson('/send_msg_headed', withBrowserAppBaseUrl(args));
816
803
  const payload = (result.json && typeof result.json === 'object') ? result.json : null;
817
804
  if (payload?.workflowRunId) {
818
805
  const summaryResult = await fetchWorkflowEntries(String(payload.workflowRunId));
@@ -827,7 +814,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
827
814
  return { content: [{ type: 'text', text: JSON.stringify(payload || { status: result.status, body: result.text }, null, 2) }] };
828
815
  }
829
816
  case 'take_screenshot': {
830
- const result = await postHeadedJson('/take_screenshot', args);
817
+ const result = await postHeadedJson('/take_screenshot', withBrowserAppBaseUrl(args));
831
818
  const payload = (result.json && typeof result.json === 'object') ? result.json : null;
832
819
  if (payload?.workflowRunId) {
833
820
  const summaryResult = await fetchWorkflowEntries(String(payload.workflowRunId));
@@ -842,14 +829,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
842
829
  return { content: [{ type: 'text', text: JSON.stringify(payload || { status: result.status, body: result.text }, null, 2) }] };
843
830
  }
844
831
  case 'take_screenshot': {
845
- const result = await postHeadedJson('/take_screenshot', args);
832
+ const result = await postHeadedJson('/take_screenshot', withBrowserAppBaseUrl(args));
846
833
  return { content: [{ type: 'text', text: JSON.stringify(result.json || { status: result.status, body: result.text }, null, 2) }] };
847
834
  }
848
835
  case 'screenshots_preview': {
849
836
  const payload = args?.screenshotUrls
850
837
  ? { screenshotUrls: args.screenshotUrls }
851
838
  : args?.response || args;
852
- const result = await postHeadedJson('/preview_screenshots', payload);
839
+ const result = await postHeadedJson('/preview_screenshots', withBrowserAppBaseUrl(payload));
853
840
  return { content: [{ type: 'text', text: JSON.stringify({ status: result.status, ok: result.ok, html: result.text }, null, 2) }] };
854
841
  }
855
842
  case 'screenshots_get_preview': {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "marble-headed-mcp",
3
- "version": "0.1.40",
3
+ "version": "0.1.42",
4
4
  "description": "MCP server for Marble headed automation endpoints",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",