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.
- package/dist/index.js +18 -108
- 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:
|
|
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
|
|
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
|
|
133
|
-
const raw =
|
|
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 `${
|
|
122
|
+
return `${resolveAppBaseUrl()}${pathname}`;
|
|
144
123
|
}
|
|
145
124
|
function buildHeadedUrl(pathname) {
|
|
146
|
-
return `${
|
|
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
|
|
189
|
-
const response = await fetch(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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': {
|