migros-mcp 0.3.0 → 0.4.0
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/README.md +35 -19
- package/dist/auth/api.js +18 -1
- package/dist/auth/api.js.map +1 -1
- package/dist/auth/cookies.d.ts +11 -0
- package/dist/auth/cookies.js +4 -0
- package/dist/auth/cookies.js.map +1 -1
- package/dist/auth/index.d.ts +20 -3
- package/dist/auth/index.js +75 -21
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/login.d.ts +20 -2
- package/dist/auth/login.js +48 -5
- package/dist/auth/login.js.map +1 -1
- package/dist/auth/oauth.d.ts +11 -1
- package/dist/auth/oauth.js +41 -13
- package/dist/auth/oauth.js.map +1 -1
- package/dist/auth/rate-limit.d.ts +70 -0
- package/dist/auth/rate-limit.js +117 -0
- package/dist/auth/rate-limit.js.map +1 -0
- package/dist/auth-tools/_shared.d.ts +1 -1
- package/dist/auth-tools/_shared.js +2 -2
- package/dist/auth-tools/_shared.js.map +1 -1
- package/dist/auth-tools/authenticate.d.ts +10 -0
- package/dist/auth-tools/authenticate.js +17 -0
- package/dist/auth-tools/authenticate.js.map +1 -0
- package/dist/auth-tools/cart.d.ts +19 -1
- package/dist/auth-tools/cart.js +38 -4
- package/dist/auth-tools/cart.js.map +1 -1
- package/dist/auth-tools.d.ts +2 -1
- package/dist/auth-tools.js +2 -1
- package/dist/auth-tools.js.map +1 -1
- package/dist/index.d.ts +0 -0
- package/dist/index.js +7 -5
- package/dist/index.js.map +1 -1
- package/dist/migros.d.ts +0 -0
- package/dist/migros.js +0 -0
- package/dist/migros.js.map +0 -0
- package/dist/schemas.d.ts +14 -0
- package/dist/schemas.js +15 -2
- package/dist/schemas.js.map +1 -1
- package/dist/tools.d.ts +0 -0
- package/dist/tools.js +0 -0
- package/dist/tools.js.map +0 -0
- package/dist/types.d.ts +0 -0
- package/dist/types.js +0 -0
- package/dist/types.js.map +0 -0
- package/package.json +3 -1
package/dist/auth/oauth.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import https from "node:https";
|
|
2
2
|
import axios from "axios";
|
|
3
3
|
import { cookieHeader, ingestSetCookie } from "./cookies.js";
|
|
4
|
-
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
const
|
|
4
|
+
import { parseRetryAfter, RateLimitError, setRateLimited, clearRateLimit, } from "./rate-limit.js";
|
|
5
|
+
// User-Agent matters at the Cloudflare edge: a Chrome UA from Node gets
|
|
6
|
+
// blocked on www.migros.ch. This Firefox/Linux UA (borrowed from
|
|
7
|
+
// migros-api-wrapper) passes both www.migros.ch and login.migros.ch.
|
|
8
|
+
const UA = "Mozilla/5.0 (X11; Linux x86_64; rv:144.0) Gecko/20100101 Firefox/144.0";
|
|
9
9
|
const REDIRECT_URI = "https://www.migros.ch/m-login-silent-login-redirect.html";
|
|
10
10
|
let _client = null;
|
|
11
11
|
export function client() {
|
|
@@ -19,12 +19,9 @@ export function client() {
|
|
|
19
19
|
});
|
|
20
20
|
return _client;
|
|
21
21
|
}
|
|
22
|
-
export function uaFor(host) {
|
|
23
|
-
return host === "www.migros.ch" ? UA_WWW : UA_LOGIN;
|
|
24
|
-
}
|
|
25
22
|
export function uniformHeaders(host, session, extra = {}) {
|
|
26
23
|
return {
|
|
27
|
-
"User-Agent":
|
|
24
|
+
"User-Agent": UA,
|
|
28
25
|
"Accept-Language": "en-US,en;q=0.9",
|
|
29
26
|
"Accept-Encoding": "gzip, deflate, br",
|
|
30
27
|
Cookie: cookieHeader(session, host),
|
|
@@ -47,6 +44,10 @@ function decodeJwtExp(jwt) {
|
|
|
47
44
|
* Throws if the SSO cookies are missing/expired (e.g., user logged out of
|
|
48
45
|
* migros.ch in their browser, or session.json is stale by weeks).
|
|
49
46
|
*
|
|
47
|
+
* On a 429 (rate-limit), sets `session.rateLimitedUntil` and throws a
|
|
48
|
+
* {@link RateLimitError} so the caller can surface the cooldown and avoid
|
|
49
|
+
* cascading into a full credentialed login.
|
|
50
|
+
*
|
|
50
51
|
* Caller is responsible for persisting the session afterward.
|
|
51
52
|
*/
|
|
52
53
|
export async function silentOAuth(session) {
|
|
@@ -58,8 +59,14 @@ export async function silentOAuth(session) {
|
|
|
58
59
|
headers: uniformHeaders("www.migros.ch", session, { Accept: "application/json, text/plain, */*" }),
|
|
59
60
|
});
|
|
60
61
|
ingestSetCookie(session, "www.migros.ch", r.headers["set-cookie"]);
|
|
62
|
+
if (r.status === 429) {
|
|
63
|
+
const retryAfter = parseRetryAfter(r.headers["retry-after"]);
|
|
64
|
+
setRateLimited(session, retryAfter, "oauth/authorize -> 429");
|
|
65
|
+
throw new RateLimitError("login.migros.ch rate-limited (429) during silent OAuth authorize step. " +
|
|
66
|
+
"Retry after: " + (retryAfter ?? "unknown") + "s", retryAfter);
|
|
67
|
+
}
|
|
61
68
|
if (r.status !== 200)
|
|
62
|
-
throw new Error(
|
|
69
|
+
throw new Error("oauth/authorize -> " + r.status);
|
|
63
70
|
const data = typeof r.data === "string" ? JSON.parse(r.data) : r.data;
|
|
64
71
|
const ssoUrl = data?.url;
|
|
65
72
|
if (!ssoUrl)
|
|
@@ -70,6 +77,12 @@ export async function silentOAuth(session) {
|
|
|
70
77
|
headers: uniformHeaders("login.migros.ch", session, { Accept: "text/html" }),
|
|
71
78
|
});
|
|
72
79
|
ingestSetCookie(session, "login.migros.ch", r.headers["set-cookie"]);
|
|
80
|
+
if (r.status === 429) {
|
|
81
|
+
const retryAfter = parseRetryAfter(r.headers["retry-after"]);
|
|
82
|
+
setRateLimited(session, retryAfter, "silent SSO -> 429");
|
|
83
|
+
throw new RateLimitError("login.migros.ch rate-limited (429) during silent SSO redirect. " +
|
|
84
|
+
"Retry after: " + (retryAfter ?? "unknown") + "s", retryAfter);
|
|
85
|
+
}
|
|
73
86
|
const location = r.headers.location;
|
|
74
87
|
if (!location) {
|
|
75
88
|
throw new Error("silent SSO did not redirect — login.migros.ch session likely expired");
|
|
@@ -81,15 +94,21 @@ export async function silentOAuth(session) {
|
|
|
81
94
|
throw new Error("silent SSO redirected without code/state");
|
|
82
95
|
// Step 3: exchange the code for a JWT.
|
|
83
96
|
const successUrl = "https://www.migros.ch/authentication/public/v1/api/oauth/login-success" +
|
|
84
|
-
|
|
97
|
+
"?code=" + encodeURIComponent(code) + "&state=" + encodeURIComponent(state) +
|
|
85
98
|
"&redirectUri=" + encodeURIComponent(REDIRECT_URI) +
|
|
86
99
|
"&authorizationNotRequired=true";
|
|
87
100
|
r = await client().get(successUrl, {
|
|
88
101
|
headers: uniformHeaders("www.migros.ch", session, { Accept: "application/json" }),
|
|
89
102
|
});
|
|
90
103
|
ingestSetCookie(session, "www.migros.ch", r.headers["set-cookie"]);
|
|
104
|
+
if (r.status === 429) {
|
|
105
|
+
const retryAfter = parseRetryAfter(r.headers["retry-after"]);
|
|
106
|
+
setRateLimited(session, retryAfter, "oauth/login-success -> 429");
|
|
107
|
+
throw new RateLimitError("login.migros.ch rate-limited (429) during OAuth login-success step. " +
|
|
108
|
+
"Retry after: " + (retryAfter ?? "unknown") + "s", retryAfter);
|
|
109
|
+
}
|
|
91
110
|
if (r.status !== 200)
|
|
92
|
-
throw new Error(
|
|
111
|
+
throw new Error("oauth/login-success -> " + r.status);
|
|
93
112
|
const tokenBody = (typeof r.data === "string" ? JSON.parse(r.data) : r.data);
|
|
94
113
|
let token = (tokenBody.accessToken ?? tokenBody.access_token ?? tokenBody.token ?? null);
|
|
95
114
|
if (!token)
|
|
@@ -100,13 +119,22 @@ export async function silentOAuth(session) {
|
|
|
100
119
|
token = token.replace(/^bearer\s+/i, "");
|
|
101
120
|
session.jwt = token;
|
|
102
121
|
session.jwtExp = decodeJwtExp(token);
|
|
122
|
+
// Successful OAuth clears any stale rate-limit cooldown.
|
|
123
|
+
clearRateLimit(session);
|
|
103
124
|
return token;
|
|
104
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* How much JWT lifetime (in seconds) we keep in reserve before proactively
|
|
128
|
+
* refreshing via silent OAuth. Also used by fail-open paths in getJwt() so a
|
|
129
|
+
* near-expired token is never handed to the API (which would 401 and trigger
|
|
130
|
+
* a rate-limited refresh attempt).
|
|
131
|
+
*/
|
|
132
|
+
export const JWT_BUFFER_SECONDS = 300;
|
|
105
133
|
/**
|
|
106
134
|
* Returns a usable JWT, refreshing via silent OAuth if the cached one is
|
|
107
135
|
* within `bufferSeconds` of expiring (or missing). Mutates `session`.
|
|
108
136
|
*/
|
|
109
|
-
export async function getJwt(session, bufferSeconds =
|
|
137
|
+
export async function getJwt(session, bufferSeconds = JWT_BUFFER_SECONDS) {
|
|
110
138
|
const now = Math.floor(Date.now() / 1000);
|
|
111
139
|
if (session.jwt && session.jwtExp && session.jwtExp - now > bufferSeconds) {
|
|
112
140
|
return session.jwt;
|
package/dist/auth/oauth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../src/auth/oauth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,YAAY,CAAC;AAC/B,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../src/auth/oauth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 429 Too Many Requests handling for login.migros.ch.
|
|
3
|
+
*
|
|
4
|
+
* Cloudflare sits in front of login.migros.ch and throttles repeated login
|
|
5
|
+
* attempts. Without this module every authenticated tool call cascades from
|
|
6
|
+
* silent-OAuth failure straight into a full credentialed login that hammers the
|
|
7
|
+
* same rate-limited host, making the throttle worse and keeping the MCP
|
|
8
|
+
* unusable for the user.
|
|
9
|
+
*
|
|
10
|
+
* All times are in **unix epoch milliseconds** (Date.now() convention) to keep
|
|
11
|
+
* exported constants unambiguous.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Parse a `Retry-After` response header into a **seconds** value.
|
|
15
|
+
*
|
|
16
|
+
* Returns `null` when the header is missing, unparseable, or describes a date
|
|
17
|
+
* in the past (negative delay).
|
|
18
|
+
*/
|
|
19
|
+
export declare function parseRetryAfter(header: string | null | undefined): number | null;
|
|
20
|
+
/**
|
|
21
|
+
* Exponential backoff schedule for when Retry-After is missing.
|
|
22
|
+
*
|
|
23
|
+
* Returns **milliseconds**.
|
|
24
|
+
*/
|
|
25
|
+
export declare function rateLimitBackoff(attempt: number): number;
|
|
26
|
+
/**
|
|
27
|
+
* Thrown by the auth layer when a 429 is detected so upper layers can
|
|
28
|
+
* distinguish "we're being throttled" from "your credentials are wrong".
|
|
29
|
+
*/
|
|
30
|
+
export declare class RateLimitError extends Error {
|
|
31
|
+
/** Parsed Retry-After in **seconds**, or `null` when the header was absent. */
|
|
32
|
+
readonly retryAfterSeconds: number | null;
|
|
33
|
+
constructor(message: string, retryAfterSeconds?: number | null);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Returns `true` when the session carries a rate-limit cooldown that hasn't
|
|
37
|
+
* expired yet.
|
|
38
|
+
*/
|
|
39
|
+
export declare function isRateLimited(session: {
|
|
40
|
+
rateLimitedUntil?: number;
|
|
41
|
+
}): boolean;
|
|
42
|
+
/** Longest cooldown accepted from a Retry-After header (1 hour). */
|
|
43
|
+
export declare const MAX_COOLDOWN_SECONDS = 3600;
|
|
44
|
+
/** After this long without a new 429, the fallback backoff starts again at 30 s. */
|
|
45
|
+
export declare const BACKOFF_RESET_MS = 3600000;
|
|
46
|
+
type CooldownState = {
|
|
47
|
+
rateLimitedUntil?: number;
|
|
48
|
+
rateLimitReason?: string;
|
|
49
|
+
rateLimitAttempts?: number;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Apply a cooldown timestamp to the session in-place (caller persists).
|
|
53
|
+
*
|
|
54
|
+
* Consecutive 429s step the fallback backoff up (30 s, 2 min, 10 min). The
|
|
55
|
+
* count starts again after a successful login (clearRateLimit) or when the
|
|
56
|
+
* last cooldown ended more than BACKOFF_RESET_MS ago.
|
|
57
|
+
*
|
|
58
|
+
* @param retryAfterSeconds Parsed Retry-After seconds (capped at
|
|
59
|
+
* MAX_COOLDOWN_SECONDS), or `null` to use the
|
|
60
|
+
* backoff step for this attempt.
|
|
61
|
+
*/
|
|
62
|
+
export declare function setRateLimited(session: CooldownState, retryAfterSeconds: number | null, reason: string): void;
|
|
63
|
+
/** Remove any rate-limit cooldown from the session. */
|
|
64
|
+
export declare function clearRateLimit(session: CooldownState): void;
|
|
65
|
+
/** Build a human-readable error for `isRateLimited` + expired JWT. */
|
|
66
|
+
export declare function rateLimitBlockedMessage(session: {
|
|
67
|
+
rateLimitedUntil?: number;
|
|
68
|
+
rateLimitReason?: string;
|
|
69
|
+
}): string;
|
|
70
|
+
export {};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 429 Too Many Requests handling for login.migros.ch.
|
|
3
|
+
*
|
|
4
|
+
* Cloudflare sits in front of login.migros.ch and throttles repeated login
|
|
5
|
+
* attempts. Without this module every authenticated tool call cascades from
|
|
6
|
+
* silent-OAuth failure straight into a full credentialed login that hammers the
|
|
7
|
+
* same rate-limited host, making the throttle worse and keeping the MCP
|
|
8
|
+
* unusable for the user.
|
|
9
|
+
*
|
|
10
|
+
* All times are in **unix epoch milliseconds** (Date.now() convention) to keep
|
|
11
|
+
* exported constants unambiguous.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Parse a `Retry-After` response header into a **seconds** value.
|
|
15
|
+
*
|
|
16
|
+
* Returns `null` when the header is missing, unparseable, or describes a date
|
|
17
|
+
* in the past (negative delay).
|
|
18
|
+
*/
|
|
19
|
+
export function parseRetryAfter(header) {
|
|
20
|
+
if (!header)
|
|
21
|
+
return null;
|
|
22
|
+
const v = header.trim();
|
|
23
|
+
// Integer → delay-seconds.
|
|
24
|
+
if (/^\d+$/.test(v)) {
|
|
25
|
+
const s = Number.parseInt(v, 10);
|
|
26
|
+
return Number.isFinite(s) && s >= 0 ? s : null;
|
|
27
|
+
}
|
|
28
|
+
// HTTP-date (RFC 7231) — heuristically detect via the presence of letters
|
|
29
|
+
// or commas; Date.parse is very lenient and would accept "-1" as a valid
|
|
30
|
+
// (negative) timestamp, giving a false positive.
|
|
31
|
+
if (/[a-z,]/i.test(v)) {
|
|
32
|
+
const epoch = Date.parse(v);
|
|
33
|
+
if (!Number.isNaN(epoch)) {
|
|
34
|
+
const sec = Math.ceil((epoch - Date.now()) / 1000);
|
|
35
|
+
// Negative means the date is in the past: clamp to 0 (retry immediately).
|
|
36
|
+
return Math.max(0, sec);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Exponential backoff schedule for when Retry-After is missing.
|
|
43
|
+
*
|
|
44
|
+
* Returns **milliseconds**.
|
|
45
|
+
*/
|
|
46
|
+
export function rateLimitBackoff(attempt) {
|
|
47
|
+
const delays = [30_000, 120_000, 600_000]; // 30 s → 2 min → 10 min
|
|
48
|
+
return delays[Math.min(attempt, delays.length - 1)] ?? 600_000;
|
|
49
|
+
}
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Error class
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
/**
|
|
54
|
+
* Thrown by the auth layer when a 429 is detected so upper layers can
|
|
55
|
+
* distinguish "we're being throttled" from "your credentials are wrong".
|
|
56
|
+
*/
|
|
57
|
+
export class RateLimitError extends Error {
|
|
58
|
+
/** Parsed Retry-After in **seconds**, or `null` when the header was absent. */
|
|
59
|
+
retryAfterSeconds;
|
|
60
|
+
constructor(message, retryAfterSeconds = null) {
|
|
61
|
+
super(message);
|
|
62
|
+
this.name = "RateLimitError";
|
|
63
|
+
this.retryAfterSeconds = retryAfterSeconds;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
// Cooldown state helpers
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
/**
|
|
70
|
+
* Returns `true` when the session carries a rate-limit cooldown that hasn't
|
|
71
|
+
* expired yet.
|
|
72
|
+
*/
|
|
73
|
+
export function isRateLimited(session) {
|
|
74
|
+
return typeof session.rateLimitedUntil === "number" && Date.now() < session.rateLimitedUntil;
|
|
75
|
+
}
|
|
76
|
+
/** Longest cooldown accepted from a Retry-After header (1 hour). */
|
|
77
|
+
export const MAX_COOLDOWN_SECONDS = 3600;
|
|
78
|
+
/** After this long without a new 429, the fallback backoff starts again at 30 s. */
|
|
79
|
+
export const BACKOFF_RESET_MS = 3_600_000;
|
|
80
|
+
/**
|
|
81
|
+
* Apply a cooldown timestamp to the session in-place (caller persists).
|
|
82
|
+
*
|
|
83
|
+
* Consecutive 429s step the fallback backoff up (30 s, 2 min, 10 min). The
|
|
84
|
+
* count starts again after a successful login (clearRateLimit) or when the
|
|
85
|
+
* last cooldown ended more than BACKOFF_RESET_MS ago.
|
|
86
|
+
*
|
|
87
|
+
* @param retryAfterSeconds Parsed Retry-After seconds (capped at
|
|
88
|
+
* MAX_COOLDOWN_SECONDS), or `null` to use the
|
|
89
|
+
* backoff step for this attempt.
|
|
90
|
+
*/
|
|
91
|
+
export function setRateLimited(session, retryAfterSeconds, reason) {
|
|
92
|
+
const recent = typeof session.rateLimitedUntil === "number" &&
|
|
93
|
+
Date.now() - session.rateLimitedUntil < BACKOFF_RESET_MS;
|
|
94
|
+
const attempt = recent ? session.rateLimitAttempts ?? 0 : 0;
|
|
95
|
+
const delay = retryAfterSeconds !== null
|
|
96
|
+
? Math.min(retryAfterSeconds, MAX_COOLDOWN_SECONDS) * 1000
|
|
97
|
+
: rateLimitBackoff(attempt);
|
|
98
|
+
session.rateLimitedUntil = Date.now() + delay;
|
|
99
|
+
session.rateLimitReason = reason;
|
|
100
|
+
session.rateLimitAttempts = attempt + 1;
|
|
101
|
+
}
|
|
102
|
+
/** Remove any rate-limit cooldown from the session. */
|
|
103
|
+
export function clearRateLimit(session) {
|
|
104
|
+
delete session.rateLimitedUntil;
|
|
105
|
+
delete session.rateLimitReason;
|
|
106
|
+
delete session.rateLimitAttempts;
|
|
107
|
+
}
|
|
108
|
+
/** Build a human-readable error for `isRateLimited` + expired JWT. */
|
|
109
|
+
export function rateLimitBlockedMessage(session) {
|
|
110
|
+
const until = session.rateLimitedUntil
|
|
111
|
+
? new Date(session.rateLimitedUntil).toISOString()
|
|
112
|
+
: "unknown";
|
|
113
|
+
return (`login.migros.ch rate-limited until ${until}; ` +
|
|
114
|
+
`reason: ${session.rateLimitReason ?? "unknown"}. ` +
|
|
115
|
+
`Cached JWT is also expired — cannot re-authenticate until the rate limit clears.`);
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=rate-limit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../../src/auth/rate-limit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,MAAiC;IAC/D,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IAExB,2BAA2B;IAC3B,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QACpB,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACjC,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACjD,CAAC;IAED,0EAA0E;IAC1E,yEAAyE;IACzE,iDAAiD;IACjD,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QACtB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC5B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;YACnD,0EAA0E;YAC1E,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC9C,MAAM,MAAM,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,wBAAwB;IACnE,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC;AACjE,CAAC;AAED,8EAA8E;AAC9E,cAAc;AACd,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,OAAO,cAAe,SAAQ,KAAK;IACvC,+EAA+E;IAC/D,iBAAiB,CAAgB;IAEjD,YAAY,OAAe,EAAE,oBAAmC,IAAI;QAClE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;QAC7B,IAAI,CAAC,iBAAiB,GAAG,iBAAiB,CAAC;IAC7C,CAAC;CACF;AAED,8EAA8E;AAC9E,yBAAyB;AACzB,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,OAAsC;IAClE,OAAO,OAAO,OAAO,CAAC,gBAAgB,KAAK,QAAQ,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC;AAC/F,CAAC;AAED,oEAAoE;AACpE,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,CAAC;AAEzC,oFAAoF;AACpF,MAAM,CAAC,MAAM,gBAAgB,GAAG,SAAS,CAAC;AAI1C;;;;;;;;;;GAUG;AACH,MAAM,UAAU,cAAc,CAC5B,OAAsB,EACtB,iBAAgC,EAChC,MAAc;IAEd,MAAM,MAAM,GACV,OAAO,OAAO,CAAC,gBAAgB,KAAK,QAAQ;QAC5C,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;IAC3D,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,iBAAiB,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5D,MAAM,KAAK,GACT,iBAAiB,KAAK,IAAI;QACxB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,GAAG,IAAI;QAC1D,CAAC,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAChC,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;IAC9C,OAAO,CAAC,eAAe,GAAG,MAAM,CAAC;IACjC,OAAO,CAAC,iBAAiB,GAAG,OAAO,GAAG,CAAC,CAAC;AAC1C,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,cAAc,CAAC,OAAsB;IACnD,OAAO,OAAO,CAAC,gBAAgB,CAAC;IAChC,OAAO,OAAO,CAAC,eAAe,CAAC;IAC/B,OAAO,OAAO,CAAC,iBAAiB,CAAC;AACnC,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,uBAAuB,CAAC,OAGvC;IACC,MAAM,KAAK,GAAG,OAAO,CAAC,gBAAgB;QACpC,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,WAAW,EAAE;QAClD,CAAC,CAAC,SAAS,CAAC;IACd,OAAO,CACL,sCAAsC,KAAK,IAAI;QAC/C,WAAW,OAAO,CAAC,eAAe,IAAI,SAAS,IAAI;QACnD,kFAAkF,CACnF,CAAC;AACJ,CAAC"}
|
|
@@ -3,4 +3,4 @@ import type { Credentials } from "../auth/index.js";
|
|
|
3
3
|
* Read credentials from env. Returns undefined if not all vars are set, in
|
|
4
4
|
* which case the auth layer falls back to a cached session if one exists.
|
|
5
5
|
*/
|
|
6
|
-
export declare function credsFromEnv(): Credentials | undefined;
|
|
6
|
+
export declare function credsFromEnv(totpCode?: string): Credentials | undefined;
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* Read credentials from env. Returns undefined if not all vars are set, in
|
|
3
3
|
* which case the auth layer falls back to a cached session if one exists.
|
|
4
4
|
*/
|
|
5
|
-
export function credsFromEnv() {
|
|
5
|
+
export function credsFromEnv(totpCode) {
|
|
6
6
|
const email = process.env.MIGROS_EMAIL;
|
|
7
7
|
const password = process.env.MIGROS_PASSWORD;
|
|
8
8
|
if (!email || !password)
|
|
9
9
|
return undefined;
|
|
10
|
-
return { email, password, totpSecret: process.env.MIGROS_TOTP_SECRET };
|
|
10
|
+
return { email, password, totpSecret: process.env.MIGROS_TOTP_SECRET || undefined, totpCode };
|
|
11
11
|
}
|
|
12
12
|
//# sourceMappingURL=_shared.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_shared.js","sourceRoot":"","sources":["../../src/auth-tools/_shared.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,YAAY;
|
|
1
|
+
{"version":3,"file":"_shared.js","sourceRoot":"","sources":["../../src/auth-tools/_shared.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,QAAiB;IAC5C,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IACvC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IAC7C,IAAI,CAAC,KAAK,IAAI,CAAC,QAAQ;QAAE,OAAO,SAAS,CAAC;IAC1C,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,SAAS,EAAE,QAAQ,EAAE,CAAC;AAChG,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Establish or reuse the authenticated Migros session.
|
|
3
|
+
*
|
|
4
|
+
* The caller supplies a current six-digit TOTP code only when a cached
|
|
5
|
+
* session cannot be reused. The code is held only for this call and is never
|
|
6
|
+
* written to the persisted session.
|
|
7
|
+
*/
|
|
8
|
+
export declare function authenticate(args?: {
|
|
9
|
+
totpCode?: string;
|
|
10
|
+
}): Promise<string>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { getJwt } from "../auth/index.js";
|
|
2
|
+
import { credsFromEnv } from "./_shared.js";
|
|
3
|
+
/**
|
|
4
|
+
* Establish or reuse the authenticated Migros session.
|
|
5
|
+
*
|
|
6
|
+
* The caller supplies a current six-digit TOTP code only when a cached
|
|
7
|
+
* session cannot be reused. The code is held only for this call and is never
|
|
8
|
+
* written to the persisted session.
|
|
9
|
+
*/
|
|
10
|
+
export async function authenticate(args = {}) {
|
|
11
|
+
await getJwt(credsFromEnv(args.totpCode), { tryLoginWithoutCode: true });
|
|
12
|
+
return JSON.stringify({
|
|
13
|
+
authenticated: true,
|
|
14
|
+
message: "Migros authentication is ready. A cached session was reused when possible.",
|
|
15
|
+
}, null, 2);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=authenticate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authenticate.js","sourceRoot":"","sources":["../../src/auth-tools/authenticate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAA8B,EAAE;IACjE,MAAM,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,mBAAmB,EAAE,IAAI,EAAE,CAAC,CAAC;IACzE,OAAO,IAAI,CAAC,SAAS,CACnB;QACE,aAAa,EAAE,IAAI;QACnB,OAAO,EAAE,4EAA4E;KACtF,EACD,IAAI,EACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
interface ShoppingListSummary {
|
|
2
|
+
shoppingListId: number;
|
|
3
|
+
shoppingListName?: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Shape the list overview for the AI: id and name of each list, plus a hint
|
|
7
|
+
* on how to use them. The basket tools fall back to the first list here.
|
|
8
|
+
*/
|
|
9
|
+
export declare function formatShoppingLists(lists: ShoppingListSummary[] | null | undefined): string;
|
|
10
|
+
/**
|
|
11
|
+
* List the user's shopping lists (id and name). Needed when someone keeps
|
|
12
|
+
* several lists: the basket tools otherwise use whichever list Migros returns
|
|
13
|
+
* first, which isn't always the one they mean.
|
|
14
|
+
*/
|
|
15
|
+
export declare function getShoppingLists(): Promise<string>;
|
|
1
16
|
/**
|
|
2
17
|
* Fetch the contents of a shopping list (basket). Returns a flat summary of
|
|
3
18
|
* items + totals. Product names are not returned by this endpoint — call
|
|
@@ -34,4 +49,7 @@ export declare function removeFromBasket(args: {
|
|
|
34
49
|
* real browser (Datatrans 3DS / saved-card prompt), so we hand off rather than
|
|
35
50
|
* try to automate the money step. Includes a basket summary for the LLM.
|
|
36
51
|
*/
|
|
37
|
-
export declare function getCheckoutLink(
|
|
52
|
+
export declare function getCheckoutLink(args?: {
|
|
53
|
+
shoppingListId?: number;
|
|
54
|
+
}): Promise<string>;
|
|
55
|
+
export {};
|
package/dist/auth-tools/cart.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { api } from "../auth/api.js";
|
|
2
2
|
import { credsFromEnv } from "./_shared.js";
|
|
3
|
+
/** All of the user's shopping lists, as Migros returns them. */
|
|
4
|
+
async function fetchShoppingLists() {
|
|
5
|
+
const lists = (await api("GET", "/shopping-list/public/v1/lists/overview", undefined, { creds: credsFromEnv() }));
|
|
6
|
+
return lists ?? [];
|
|
7
|
+
}
|
|
3
8
|
/**
|
|
4
9
|
* Resolve the user's primary shopping list id, calling /lists/overview if not
|
|
5
10
|
* provided. Most users have a single list named "Shopping list".
|
|
@@ -7,11 +12,40 @@ import { credsFromEnv } from "./_shared.js";
|
|
|
7
12
|
async function resolveShoppingListId(provided) {
|
|
8
13
|
if (typeof provided === "number")
|
|
9
14
|
return provided;
|
|
10
|
-
const lists =
|
|
11
|
-
if (!lists
|
|
15
|
+
const lists = await fetchShoppingLists();
|
|
16
|
+
if (!lists.length)
|
|
12
17
|
throw new Error("no shopping lists found for this account");
|
|
13
18
|
return lists[0].shoppingListId;
|
|
14
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Shape the list overview for the AI: id and name of each list, plus a hint
|
|
22
|
+
* on how to use them. The basket tools fall back to the first list here.
|
|
23
|
+
*/
|
|
24
|
+
export function formatShoppingLists(lists) {
|
|
25
|
+
const out = (lists ?? []).map((l) => ({
|
|
26
|
+
shoppingListId: l.shoppingListId,
|
|
27
|
+
name: l.shoppingListName ?? l.name,
|
|
28
|
+
}));
|
|
29
|
+
return JSON.stringify({
|
|
30
|
+
count: out.length,
|
|
31
|
+
lists: out,
|
|
32
|
+
hint: out.length > 1
|
|
33
|
+
? "Several lists. If it isn't clear which one the user means, ask. Then pass its shoppingListId " +
|
|
34
|
+
"to get_basket, add_to_basket, update_basket_quantity, remove_from_basket or get_checkout_link. " +
|
|
35
|
+
"Without it, those tools use the first list here."
|
|
36
|
+
: out.length === 1
|
|
37
|
+
? "One list. The basket tools use it by default."
|
|
38
|
+
: "No shopping lists found for this account.",
|
|
39
|
+
}, null, 2);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* List the user's shopping lists (id and name). Needed when someone keeps
|
|
43
|
+
* several lists: the basket tools otherwise use whichever list Migros returns
|
|
44
|
+
* first, which isn't always the one they mean.
|
|
45
|
+
*/
|
|
46
|
+
export async function getShoppingLists() {
|
|
47
|
+
return formatShoppingLists(await fetchShoppingLists());
|
|
48
|
+
}
|
|
15
49
|
/**
|
|
16
50
|
* Format a basket response into the same flat shape get_basket returns.
|
|
17
51
|
* Used by both reads and writes since the v3/items endpoint returns the
|
|
@@ -93,8 +127,8 @@ export async function removeFromBasket(args) {
|
|
|
93
127
|
* real browser (Datatrans 3DS / saved-card prompt), so we hand off rather than
|
|
94
128
|
* try to automate the money step. Includes a basket summary for the LLM.
|
|
95
129
|
*/
|
|
96
|
-
export async function getCheckoutLink() {
|
|
97
|
-
const shoppingListId = await resolveShoppingListId();
|
|
130
|
+
export async function getCheckoutLink(args = {}) {
|
|
131
|
+
const shoppingListId = await resolveShoppingListId(args.shoppingListId);
|
|
98
132
|
const data = (await api("GET", `/shopping-list/public/v2/list/details?shoppingListId=${shoppingListId}`, undefined, { creds: credsFromEnv() }));
|
|
99
133
|
const items = (data.categories ?? []).flatMap((c) => c.items ?? []);
|
|
100
134
|
if (items.length === 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cart.js","sourceRoot":"","sources":["../../src/auth-tools/cart.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAqC5C;;;GAGG;AACH,KAAK,UAAU,qBAAqB,CAAC,QAAiB;IACpD,IAAI,OAAO,QAAQ,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAClD,MAAM,KAAK,GAAG,CAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"cart.js","sourceRoot":"","sources":["../../src/auth-tools/cart.ts"],"names":[],"mappings":"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"}
|
package/dist/auth-tools.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* keep each file focused and easy to scan.
|
|
5
5
|
*/
|
|
6
6
|
export { getProfile, getAddresses } from "./auth-tools/account.js";
|
|
7
|
-
export {
|
|
7
|
+
export { authenticate } from "./auth-tools/authenticate.js";
|
|
8
|
+
export { getShoppingLists, getBasket, addToBasket, updateBasketQuantity, removeFromBasket, getCheckoutLink, } from "./auth-tools/cart.js";
|
|
8
9
|
export { getOrders, getOrderDetails } from "./auth-tools/orders.js";
|
|
9
10
|
export { getCumulusStatus, getInStoreReceipts, getReceiptDetails, getCumulusCoupons, } from "./auth-tools/cumulus.js";
|
package/dist/auth-tools.js
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* keep each file focused and easy to scan.
|
|
5
5
|
*/
|
|
6
6
|
export { getProfile, getAddresses } from "./auth-tools/account.js";
|
|
7
|
-
export {
|
|
7
|
+
export { authenticate } from "./auth-tools/authenticate.js";
|
|
8
|
+
export { getShoppingLists, getBasket, addToBasket, updateBasketQuantity, removeFromBasket, getCheckoutLink, } from "./auth-tools/cart.js";
|
|
8
9
|
export { getOrders, getOrderDetails } from "./auth-tools/orders.js";
|
|
9
10
|
export { getCumulusStatus, getInStoreReceipts, getReceiptDetails, getCumulusCoupons, } from "./auth-tools/cumulus.js";
|
|
10
11
|
//# sourceMappingURL=auth-tools.js.map
|
package/dist/auth-tools.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-tools.js","sourceRoot":"","sources":["../src/auth-tools.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EACL,SAAS,EACT,WAAW,EACX,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,yBAAyB,CAAC"}
|
|
1
|
+
{"version":3,"file":"auth-tools.js","sourceRoot":"","sources":["../src/auth-tools.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EACL,gBAAgB,EAChB,SAAS,EACT,WAAW,EACX,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,yBAAyB,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
File without changes
|
package/dist/index.js
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { register } from "./register.js";
|
|
5
|
-
import { SearchProductsSchema, GetProductDetailsSchema, GetStockSchema, SearchStoresSchema, GetPromotionsSchema, SearchRecipesSchema, GetRecipeDetailsSchema, GetRecipeProductsSchema, GetBasketSchema, AddToBasketSchema, UpdateBasketQuantitySchema, RemoveFromBasketSchema, GetOrdersSchema, GetOrderDetailsSchema, GetInStoreReceiptsSchema, GetReceiptDetailsSchema, } from "./schemas.js";
|
|
5
|
+
import { SearchProductsSchema, GetProductDetailsSchema, GetStockSchema, SearchStoresSchema, GetPromotionsSchema, SearchRecipesSchema, GetRecipeDetailsSchema, GetRecipeProductsSchema, AuthenticateSchema, GetBasketSchema, GetCheckoutLinkSchema, AddToBasketSchema, UpdateBasketQuantitySchema, RemoveFromBasketSchema, GetOrdersSchema, GetOrderDetailsSchema, GetInStoreReceiptsSchema, GetReceiptDetailsSchema, } from "./schemas.js";
|
|
6
6
|
import { searchProducts, getProductDetails, getStock, getCategories, searchStores, getPromotions, searchRecipes, getRecipeDetails, getRecipeProducts, } from "./tools.js";
|
|
7
|
-
import { getProfile, getBasket, addToBasket, updateBasketQuantity, removeFromBasket, getAddresses, getOrders, getOrderDetails, getCheckoutLink, getCumulusStatus, getInStoreReceipts, getReceiptDetails, getCumulusCoupons, } from "./auth-tools.js";
|
|
7
|
+
import { authenticate, getProfile, getShoppingLists, getBasket, addToBasket, updateBasketQuantity, removeFromBasket, getAddresses, getOrders, getOrderDetails, getCheckoutLink, getCumulusStatus, getInStoreReceipts, getReceiptDetails, getCumulusCoupons, } from "./auth-tools.js";
|
|
8
8
|
// ---------------------------------------------------------------------------
|
|
9
9
|
// Server + tool registrations
|
|
10
10
|
// ---------------------------------------------------------------------------
|
|
11
|
-
const server = new McpServer({ name: "migros-mcp", version: "0.
|
|
11
|
+
const server = new McpServer({ name: "migros-mcp", version: "0.4.0" });
|
|
12
12
|
// --- Anonymous: search & browse ---
|
|
13
13
|
register(server, "search_products", "Search Migros products by name or keyword. Returns product IDs that can be used with get_product_details.", SearchProductsSchema.shape, ({ query, language }) => searchProducts(query, language ?? "de"));
|
|
14
14
|
register(server, "get_product_details", "Get full product details including nutrition facts, ingredients, allergens, price, and ratings. Use search_products first to get product IDs.", GetProductDetailsSchema.shape, ({ productIds }) => getProductDetails(productIds));
|
|
@@ -21,17 +21,19 @@ register(server, "search_recipes", "Search Migusto (Migros' recipe site) by free
|
|
|
21
21
|
register(server, "get_recipe_details", "Fetch a full Migusto recipe by slug: ingredients with quantities, step-by-step instructions, photos, nutrition. Use search_recipes first to find the slug. (Migusto is Migros' recipe site at migusto.migros.ch.)", GetRecipeDetailsSchema.shape, (args) => getRecipeDetails(args));
|
|
22
22
|
register(server, "get_recipe_products", "Fetch the Migros products needed for a Migusto recipe — returns product UIDs you can pass directly to add_to_basket. Bridges recipes to shopping. Use the recipe ID from search_recipes (NOT the slug).", GetRecipeProductsSchema.shape, (args) => getRecipeProducts(args));
|
|
23
23
|
// --- Authenticated: account ---
|
|
24
|
+
register(server, "authenticate", "Log in to Migros with the current six-digit 2FA code from the user's authenticator app (ask the user for it, or read it from a password manager you have access to). Only needed when an authenticated tool reports fresh_code_required, which happens when no TOTP secret is configured. Omit totpCode to reuse a cached session, or if the account has no 2FA. The code is never stored or returned.", AuthenticateSchema.shape, (args) => authenticate(args));
|
|
24
25
|
register(server, "get_profile", "Get the logged-in customer's basic profile (name, email, language, cooperative). Requires authentication.", {}, () => getProfile());
|
|
25
26
|
register(server, "get_addresses", "Get the user's saved delivery and billing addresses. Requires authentication.", {}, () => getAddresses());
|
|
26
27
|
// --- Authenticated: cart ---
|
|
27
|
-
register(server, "
|
|
28
|
+
register(server, "get_shopping_lists", "List the user's Migros shopping lists (id and name). Call this when the user mentions a specific list or has several: pass the chosen shoppingListId to the basket tools. Requires authentication.", {}, () => getShoppingLists());
|
|
29
|
+
register(server, "get_basket", "Fetch the items currently in the user's shopping basket (Migros calls this a shopping list). Returns product IDs and quantities; use get_product_details to enrich. If the user has several lists, use get_shopping_lists and pass shoppingListId. Requires authentication.", GetBasketSchema.shape, (args) => getBasket(args));
|
|
28
30
|
register(server, "add_to_basket", "Add a product to the user's basket (or set its quantity if already present). The underlying API is upsert-to-target, not increment. Returns the updated basket. Requires authentication.", AddToBasketSchema.shape, (args) => addToBasket(args));
|
|
29
31
|
register(server, "update_basket_quantity", "Set an item's exact quantity in the basket. quantity=0 removes the item. Returns the updated basket. Requires authentication.", UpdateBasketQuantitySchema.shape, (args) => updateBasketQuantity(args));
|
|
30
32
|
register(server, "remove_from_basket", "Remove a product from the user's basket. Returns the updated basket. Requires authentication.", RemoveFromBasketSchema.shape, (args) => removeFromBasket(args));
|
|
31
33
|
// --- Authenticated: orders ---
|
|
32
34
|
register(server, "get_orders", "List the user's online orders, paginated, filtered by status. Use to find an order id, then call get_order_details. Requires authentication.", GetOrdersSchema.shape, (args) => getOrders(args));
|
|
33
35
|
register(server, "get_order_details", "Fetch the full details of a single order by id. Requires authentication.", GetOrderDetailsSchema.shape, (args) => getOrderDetails(args));
|
|
34
|
-
register(server, "get_checkout_link", "Return the URL the user should open in their browser to complete checkout (review basket, confirm address/slot, pay). Order placement requires payment confirmation in a real browser, so this tool hands off rather than firing the order automatically. Returns a basket summary alongside the URL. Requires authentication.",
|
|
36
|
+
register(server, "get_checkout_link", "Return the URL the user should open in their browser to complete checkout (review basket, confirm address/slot, pay). Order placement requires payment confirmation in a real browser, so this tool hands off rather than firing the order automatically. Returns a basket summary alongside the URL. Requires authentication.", GetCheckoutLinkSchema.shape, (args) => getCheckoutLink(args));
|
|
35
37
|
// --- Authenticated: Cumulus / in-store ---
|
|
36
38
|
register(server, "get_cumulus_status", "Get the user's Cumulus loyalty card status: points balance, level, cardholder, lifetime stats. Requires authentication.", {}, () => getCumulusStatus());
|
|
37
39
|
register(server, "get_in_store_receipts", "List the user's in-store Migros receipts (Kassenbons) over a date range. Default: last 30 days. Use get_receipt_details with an id for line items. Requires authentication.", GetInStoreReceiptsSchema.shape, (args) => getInStoreReceipts(args));
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/migros.d.ts
CHANGED
|
File without changes
|
package/dist/migros.js
CHANGED
|
File without changes
|
package/dist/migros.js.map
CHANGED
|
File without changes
|