diff --git a/magento/app.json b/magento/app.json index 0588894a..41cd3ff3 100644 --- a/magento/app.json +++ b/magento/app.json @@ -13,12 +13,6 @@ "title": "Base URL", "description": "Magento store base URL, e.g. https://loja.granado.com.br (no trailing slash, no /rest)" }, - "apiToken": { - "type": "string", - "title": "API Token", - "description": "Magento integration access token (sent as Authorization: Bearer)", - "format": "password" - }, "storeCode": { "type": "string", "title": "Store Code", @@ -49,7 +43,7 @@ } } }, - "required": ["baseUrl", "apiToken"] + "required": ["baseUrl"] } }, "description": "MCP for Magento 2 REST APIs — live sales dashboard widgets (orders per hour, sales cards, cancellation rate, top products, status breakdown) plus curated tools for orders, catalog, customers, inventory and CMS.", diff --git a/magento/server/lib/client.ts b/magento/server/lib/client.ts index cbb9014f..7c67f27c 100644 --- a/magento/server/lib/client.ts +++ b/magento/server/lib/client.ts @@ -14,7 +14,7 @@ const INITIAL_RETRY_DELAY_MS = 500; export const DEFAULT_STORE_CODE = "all"; export const DEFAULT_CURRENCY = "BRL"; -type CredentialSource = "state" | "env" | "missing"; +type CredentialSource = "authorization" | "state" | "env" | "missing"; export interface ResolvedCredentials extends MagentoCredentials { /** Where each credential value came from — useful for diagnosing missing config. */ @@ -35,15 +35,26 @@ function pickSource( return { value: "", source: "missing" }; } +export interface MeshRequestContext { + authorization?: string; + state?: Partial; +} + export function resolveCredentials( - state: Partial | undefined, + ctx: MeshRequestContext | undefined, ): ResolvedCredentials { - const safeState = state ?? {}; + const safeState = ctx?.state ?? {}; + const baseUrl = pickSource(safeState.baseUrl, process.env.MAGENTO_BASE_URL); - const apiToken = pickSource( - safeState.apiToken, - process.env.MAGENTO_API_TOKEN, - ); + + const rawAuth = ctx?.authorization; + const tokenFromAuth = rawAuth + ? rawAuth.replace(/^Bearer\s+/i, "") + : undefined; + const apiToken: { value: string; source: CredentialSource } = tokenFromAuth + ? { value: tokenFromAuth, source: "authorization" } + : pickSource(undefined, process.env.MAGENTO_API_TOKEN); + const storeCode = pickSource( safeState.storeCode, process.env.MAGENTO_STORE_CODE, @@ -54,11 +65,10 @@ export function resolveCredentials( ); if (baseUrl.source === "missing") { - const stateKeys = state ? Object.keys(state) : []; + const stateKeys = safeState ? Object.keys(safeState) : []; console.warn( "[Magento] resolveCredentials: baseUrl is missing — neither MESH_REQUEST_CONTEXT.state.baseUrl nor process.env.MAGENTO_BASE_URL is set.", JSON.stringify({ - receivedStateType: state === undefined ? "undefined" : typeof state, receivedStateKeys: stateKeys, envMagentoBaseUrlSet: Boolean(process.env.MAGENTO_BASE_URL), }), @@ -94,7 +104,7 @@ export function assertValidCredentials( } if (!creds.apiToken) { throw new Error( - `Magento apiToken is missing${where} — set MESH_REQUEST_CONTEXT.state.apiToken or the MAGENTO_API_TOKEN env var (integration access token).`, + `Magento apiToken is missing${where} — set the Token field in the MCP connection (Authorization: Bearer) or the MAGENTO_API_TOKEN env var (integration access token).`, ); } } diff --git a/magento/server/tools/custom/cancellation-rate.ts b/magento/server/tools/custom/cancellation-rate.ts index 5d8393c9..e11c6dc5 100644 --- a/magento/server/tools/custom/cancellation-rate.ts +++ b/magento/server/tools/custom/cancellation-rate.ts @@ -42,7 +42,7 @@ export const cancellationRate = (_env: Env) => annotations: { readOnlyHint: true }, execute: async ({ context, runtimeContext }) => { const env = runtimeContext.env as Env; - const creds = resolveCredentials(env.MESH_REQUEST_CONTEXT?.state); + const creds = resolveCredentials(env.MESH_REQUEST_CONTEXT); assertValidCredentials(creds, TOOL_ID); const timezone = creds.timezone ?? DEFAULT_STORE_TIMEZONE; diff --git a/magento/server/tools/custom/orders-sales-card.ts b/magento/server/tools/custom/orders-sales-card.ts index 3137a5eb..dc4fcf95 100644 --- a/magento/server/tools/custom/orders-sales-card.ts +++ b/magento/server/tools/custom/orders-sales-card.ts @@ -55,7 +55,7 @@ export const ordersSalesCard = (_env: Env) => annotations: { readOnlyHint: true }, execute: async ({ context, runtimeContext }) => { const env = runtimeContext.env as Env; - const creds = resolveCredentials(env.MESH_REQUEST_CONTEXT?.state); + const creds = resolveCredentials(env.MESH_REQUEST_CONTEXT); assertValidCredentials(creds, TOOL_ID); const timezone = creds.timezone ?? DEFAULT_STORE_TIMEZONE; diff --git a/magento/server/tools/custom/orders-timeline.ts b/magento/server/tools/custom/orders-timeline.ts index ebae4d8b..2b60a6a6 100644 --- a/magento/server/tools/custom/orders-timeline.ts +++ b/magento/server/tools/custom/orders-timeline.ts @@ -36,7 +36,7 @@ export const ordersTimeline = (_env: Env) => annotations: { readOnlyHint: true }, execute: async ({ runtimeContext }) => { const env = runtimeContext.env as Env; - const creds = resolveCredentials(env.MESH_REQUEST_CONTEXT?.state); + const creds = resolveCredentials(env.MESH_REQUEST_CONTEXT); assertValidCredentials(creds, TOOL_ID); const timezone = creds.timezone ?? DEFAULT_STORE_TIMEZONE; diff --git a/magento/server/tools/custom/status-breakdown.ts b/magento/server/tools/custom/status-breakdown.ts index 066004e9..54e33750 100644 --- a/magento/server/tools/custom/status-breakdown.ts +++ b/magento/server/tools/custom/status-breakdown.ts @@ -48,7 +48,7 @@ export const statusBreakdown = (_env: Env) => annotations: { readOnlyHint: true }, execute: async ({ context, runtimeContext }) => { const env = runtimeContext.env as Env; - const creds = resolveCredentials(env.MESH_REQUEST_CONTEXT?.state); + const creds = resolveCredentials(env.MESH_REQUEST_CONTEXT); assertValidCredentials(creds, TOOL_ID); const timezone = creds.timezone ?? DEFAULT_STORE_TIMEZONE; diff --git a/magento/server/tools/custom/top-products.ts b/magento/server/tools/custom/top-products.ts index ae0dd73d..fe47d634 100644 --- a/magento/server/tools/custom/top-products.ts +++ b/magento/server/tools/custom/top-products.ts @@ -62,7 +62,7 @@ export const topProducts = (_env: Env) => annotations: { readOnlyHint: true }, execute: async ({ context, runtimeContext }) => { const env = runtimeContext.env as Env; - const creds = resolveCredentials(env.MESH_REQUEST_CONTEXT?.state); + const creds = resolveCredentials(env.MESH_REQUEST_CONTEXT); assertValidCredentials(creds, TOOL_ID); const timezone = creds.timezone ?? DEFAULT_STORE_TIMEZONE; diff --git a/magento/server/tools/registry.ts b/magento/server/tools/registry.ts index eafe85e1..9ca3521b 100644 --- a/magento/server/tools/registry.ts +++ b/magento/server/tools/registry.ts @@ -71,7 +71,7 @@ function createSearchTool(config: { annotations: { readOnlyHint: true }, execute: async ({ context, runtimeContext }) => { const env = runtimeContext.env as Env; - const creds = resolveCredentials(env.MESH_REQUEST_CONTEXT?.state); + const creds = resolveCredentials(env.MESH_REQUEST_CONTEXT); assertValidCredentials(creds, config.id); const params = buildSearchCriteriaParams({ @@ -115,7 +115,7 @@ function createGetTool>(config: { annotations: { readOnlyHint: true }, execute: async ({ context, runtimeContext }) => { const env = runtimeContext.env as Env; - const creds = resolveCredentials(env.MESH_REQUEST_CONTEXT?.state); + const creds = resolveCredentials(env.MESH_REQUEST_CONTEXT); assertValidCredentials(creds, config.id); const input = context as z.infer; diff --git a/magento/server/types/env.ts b/magento/server/types/env.ts index 38fdfbd2..85b71a56 100644 --- a/magento/server/types/env.ts +++ b/magento/server/types/env.ts @@ -10,11 +10,6 @@ export const StateSchema = z.object({ .describe( "Magento store base URL, e.g. https://loja.granado.com.br (no trailing slash, no /rest)", ), - apiToken: z - .string() - .describe( - "Magento integration access token, sent as Authorization: Bearer", - ), storeCode: z .string() .optional()