meguro-mcp 0.2.0 → 0.2.1
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/CHANGELOG.md +8 -0
- package/README.md +3 -3
- package/package.json +1 -1
- package/src/server.mjs +1 -1
- package/src/tools.mjs +47 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `meguro-mcp` are recorded here. Versions follow Semantic Versioning.
|
|
4
4
|
|
|
5
|
+
## 0.2.1 — 2026-08-01
|
|
6
|
+
|
|
7
|
+
- Preserves the API's approved wrong/revoked workspace-key code and actionable Connection settings
|
|
8
|
+
guidance through MCP tool errors while keeping malformed authentication responses fail-closed.
|
|
9
|
+
- Keeps authentication failures secret-free, single-attempt, and explicitly classified as HTTP 401
|
|
10
|
+
or 403 without changing unrelated tool-error projections.
|
|
11
|
+
- Pins Console and customer scenario fixtures to the exact `meguro-mcp@0.2.1` artifact.
|
|
12
|
+
|
|
5
13
|
## 0.2.0 — 2026-08-01
|
|
6
14
|
|
|
7
15
|
- Publishes the frozen 43-tool control-plane registry already present in the repository, without
|
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ Dependency-free, no build step: the server is plain Node ≥ 20.
|
|
|
14
14
|
Customer MCP clients run the exact public version directly from npm:
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
npx -y meguro-mcp@0.2.
|
|
17
|
+
npx -y meguro-mcp@0.2.1
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
Pin the version in client configuration. A pinned quickstart stays reproducible and never changes its
|
|
@@ -161,7 +161,7 @@ claude mcp add meguro \
|
|
|
161
161
|
-e MEGURO_API_BASE_URL=https://api-dev.meguro.io \
|
|
162
162
|
-e MEGURO_API_TOKEN=meg_sk_... \
|
|
163
163
|
-e MEGURO_DASHBOARD_URL=https://... \
|
|
164
|
-
-- npx -y meguro-mcp@0.2.
|
|
164
|
+
-- npx -y meguro-mcp@0.2.1
|
|
165
165
|
```
|
|
166
166
|
|
|
167
167
|
## Register — Cursor (`.cursor/mcp.json`)
|
|
@@ -171,7 +171,7 @@ claude mcp add meguro \
|
|
|
171
171
|
"mcpServers": {
|
|
172
172
|
"meguro": {
|
|
173
173
|
"command": "npx",
|
|
174
|
-
"args": ["-y", "meguro-mcp@0.2.
|
|
174
|
+
"args": ["-y", "meguro-mcp@0.2.1"],
|
|
175
175
|
"env": {
|
|
176
176
|
"MEGURO_API_BASE_URL": "https://api-dev.meguro.io",
|
|
177
177
|
"MEGURO_API_TOKEN": "meg_sk_...",
|
package/package.json
CHANGED
package/src/server.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import { createInterface } from 'node:readline';
|
|
|
7
7
|
import { documentationResource, documentationResources } from './docs.mjs';
|
|
8
8
|
import { createTools, redactSecrets } from './tools.mjs';
|
|
9
9
|
|
|
10
|
-
const SERVER_INFO = { name: 'meguro', version: '0.2.
|
|
10
|
+
const SERVER_INFO = { name: 'meguro', version: '0.2.1' };
|
|
11
11
|
const PROTOCOL_VERSION = '2025-03-26';
|
|
12
12
|
|
|
13
13
|
const tools = createTools({
|
package/src/tools.mjs
CHANGED
|
@@ -11,6 +11,10 @@ const SECRET_HEADER = /\b(?:authorization|cookie|set-cookie|x-shopify-access-tok
|
|
|
11
11
|
const SECRET_KEY = /(?:authorization|cookie|password|secret|token|api[_-]?key|signedsnapshot)/iu;
|
|
12
12
|
const PRIVATE_RESPONSE_KEY = /^(?:raw|rawBody|rawBodyPreview|responsePayload|requestHeaders|responseHeaders|headers|cookies)$/iu;
|
|
13
13
|
const RESERVED_WORLD_IDS = new Set(['api', 'api-dev', 'www', 'dev', 'hooks', 'control']);
|
|
14
|
+
const AUTH_TEACHING = Object.freeze({
|
|
15
|
+
'workspace-api-key-invalid': 'This workspace API key is invalid or revoked. Create or replace it in Connection settings.',
|
|
16
|
+
'workspace-api-key-revoked': 'This workspace API key is revoked. Create or replace it in Connection settings.',
|
|
17
|
+
});
|
|
14
18
|
const FLEET_TOOL_NAMES = new Set([
|
|
15
19
|
'stores_list', 'store_create', 'store_delete', 'store_passport',
|
|
16
20
|
'workspaces_list', 'workspace_create', 'workspace_archive', 'workspace_unarchive',
|
|
@@ -93,6 +97,33 @@ function secretSafe(value, depth = 0) {
|
|
|
93
97
|
return value;
|
|
94
98
|
}
|
|
95
99
|
|
|
100
|
+
function authenticationErrorResult(status, value) {
|
|
101
|
+
if (status !== 401 && status !== 403) return null;
|
|
102
|
+
const body = value && typeof value === 'object' ? value : {};
|
|
103
|
+
const candidates = [body.practiceRunError, body.error, body];
|
|
104
|
+
const source = candidates.find((candidate) => candidate && typeof candidate === 'object'
|
|
105
|
+
&& typeof candidate.code === 'string' && typeof candidate.message === 'string');
|
|
106
|
+
const routeCode = source?.code
|
|
107
|
+
?? (typeof body.code === 'string' ? body.code : undefined)
|
|
108
|
+
?? (typeof body.error === 'string' && body.error !== 'unauthorized' ? body.error : undefined);
|
|
109
|
+
if (routeCode && !Object.hasOwn(AUTH_TEACHING, routeCode)) return null;
|
|
110
|
+
const expectedMessage = source ? AUTH_TEACHING[source.code] : undefined;
|
|
111
|
+
const error = expectedMessage && source.message === expectedMessage
|
|
112
|
+
? { code: source.code, message: expectedMessage }
|
|
113
|
+
: { message: 'Meguro practice request failed.' };
|
|
114
|
+
return {
|
|
115
|
+
content: [{ type: 'text', text: JSON.stringify({ httpStatus: status, error }, null, 2) }],
|
|
116
|
+
isError: true,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
class ToolResultError extends Error {
|
|
121
|
+
constructor(result) {
|
|
122
|
+
super('Meguro request failed.');
|
|
123
|
+
this.result = result;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
96
127
|
// A store-deletion preview intentionally returns one narrow capability for the caller to
|
|
97
128
|
// present back on the second step. Keep every other response field behind the normal secret
|
|
98
129
|
// scrubber and opt this single, format-checked value back in explicitly.
|
|
@@ -122,6 +153,8 @@ function catalogSnapshotProjection(value) {
|
|
|
122
153
|
}
|
|
123
154
|
|
|
124
155
|
function practiceErrorResult(status, value, retryAfterSeconds) {
|
|
156
|
+
const authenticationError = authenticationErrorResult(status, value);
|
|
157
|
+
if (authenticationError) return authenticationError;
|
|
125
158
|
const body = value && typeof value === 'object' ? value : {};
|
|
126
159
|
const stable = { httpStatus: status };
|
|
127
160
|
if (body.practiceRunError && typeof body.practiceRunError === 'object') {
|
|
@@ -664,6 +697,8 @@ export function createTools(config) {
|
|
|
664
697
|
let json;
|
|
665
698
|
try { json = JSON.parse(text); } catch { json = { raw: text }; }
|
|
666
699
|
if (!response.ok) {
|
|
700
|
+
const authenticationError = authenticationErrorResult(response.status, json);
|
|
701
|
+
if (authenticationError) throw new ToolResultError(authenticationError);
|
|
667
702
|
const message = json.errors?.[0]?.message ?? json.error ?? text.slice(0, 300);
|
|
668
703
|
throw new Error(`${response.status} ${path}: ${message}`);
|
|
669
704
|
}
|
|
@@ -786,7 +821,10 @@ export function createTools(config) {
|
|
|
786
821
|
|
|
787
822
|
async function fleetRequest(toolName, method, path, body, options = {}) {
|
|
788
823
|
const response = await apiRaw(method, path, body, options);
|
|
789
|
-
if (!response.ok)
|
|
824
|
+
if (!response.ok) {
|
|
825
|
+
const authenticationError = authenticationErrorResult(response.status, response.json);
|
|
826
|
+
return { error: authenticationError ?? await fleetErrorResult(toolName, path, response.status, response.json, response.requestId) };
|
|
827
|
+
}
|
|
790
828
|
return { value: response.json };
|
|
791
829
|
}
|
|
792
830
|
|
|
@@ -883,7 +921,8 @@ export function createTools(config) {
|
|
|
883
921
|
const response = await practiceApi(method, path, body);
|
|
884
922
|
return response.ok
|
|
885
923
|
? { value: response.json ?? {} }
|
|
886
|
-
: { error:
|
|
924
|
+
: { error: authenticationErrorResult(response.status, response.json)
|
|
925
|
+
?? await examErrorResult(toolName, path, response, attemptId) };
|
|
887
926
|
}
|
|
888
927
|
|
|
889
928
|
async function examReceiptArtifact(examId, kind, expectedDigest) {
|
|
@@ -894,6 +933,8 @@ export function createTools(config) {
|
|
|
894
933
|
const text = await response.text();
|
|
895
934
|
let json;
|
|
896
935
|
try { json = text ? JSON.parse(text) : null; } catch { json = { examError: { message: 'Meguro returned a non-JSON receipt error.' } }; }
|
|
936
|
+
const authenticationError = authenticationErrorResult(response.status, json);
|
|
937
|
+
if (authenticationError) return { error: authenticationError };
|
|
897
938
|
const error = await examErrorResult('exam_report', path, {
|
|
898
939
|
status: response.status,
|
|
899
940
|
json,
|
|
@@ -1999,12 +2040,14 @@ export function createTools(config) {
|
|
|
1999
2040
|
// MEG-34: always ask the server to mint a durable Console reference for a successful probe.
|
|
2000
2041
|
createConsoleReference: true,
|
|
2001
2042
|
};
|
|
2002
|
-
const { ok, json } = await apiRaw('POST', `/practice/${encodeURIComponent(worldId)}/probe/admin`, body);
|
|
2043
|
+
const { ok, status, json } = await apiRaw('POST', `/practice/${encodeURIComponent(worldId)}/probe/admin`, body);
|
|
2003
2044
|
if (ok) {
|
|
2004
2045
|
// Replace the internal reference id with a ready-to-open Console URL (opaque ref only).
|
|
2005
2046
|
const { consoleRef, consoleRefExpiresAt, ...envelope } = json;
|
|
2006
2047
|
return textResult({ ...envelope, ...consoleUrlFields(consoleRef, consoleRefExpiresAt) });
|
|
2007
2048
|
}
|
|
2049
|
+
const authenticationError = authenticationErrorResult(status, json);
|
|
2050
|
+
if (authenticationError) return authenticationError;
|
|
2008
2051
|
// Preserve the structured meguro.admin-probe.v1 error body (code + message) with isError: true,
|
|
2009
2052
|
// instead of collapsing it to an unstructured thrown message.
|
|
2010
2053
|
return { content: [{ type: 'text', text: JSON.stringify(secretSafe(json), null, 2) }], isError: true };
|
|
@@ -2023,6 +2066,7 @@ export function createTools(config) {
|
|
|
2023
2066
|
return errorResult(`Unknown tool: ${name}`);
|
|
2024
2067
|
}
|
|
2025
2068
|
} catch (error) {
|
|
2069
|
+
if (error instanceof ToolResultError) return error.result;
|
|
2026
2070
|
if (FLEET_TOOL_NAMES.has(name)) {
|
|
2027
2071
|
return fleetErrorResult(name, `mcp:${name}`, 0, {
|
|
2028
2072
|
errors: [{
|