imprint-mcp 0.2.1 → 0.3.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.
Files changed (130) hide show
  1. package/README.md +193 -189
  2. package/examples/discoverandgo/README.md +1 -1
  3. package/examples/echo/README.md +1 -1
  4. package/examples/google-flights/README.md +28 -0
  5. package/examples/google-flights/_shared/batchexecute.ts +63 -0
  6. package/examples/google-flights/_shared/flights_request.ts +95 -0
  7. package/examples/google-flights/_shared/package.json +9 -0
  8. package/examples/google-flights/get_flight_booking_details/index.ts +159 -0
  9. package/examples/google-flights/get_flight_booking_details/package.json +9 -0
  10. package/examples/google-flights/get_flight_booking_details/parser.ts +182 -0
  11. package/examples/google-flights/get_flight_booking_details/playbook.yaml +138 -0
  12. package/examples/google-flights/get_flight_booking_details/request-transform.ts +86 -0
  13. package/examples/google-flights/get_flight_booking_details/workflow.json +98 -0
  14. package/examples/google-flights/get_flight_calendar_prices/index.ts +131 -0
  15. package/examples/google-flights/get_flight_calendar_prices/package.json +9 -0
  16. package/examples/google-flights/get_flight_calendar_prices/parser.ts +86 -0
  17. package/examples/google-flights/get_flight_calendar_prices/playbook.yaml +97 -0
  18. package/examples/google-flights/get_flight_calendar_prices/request-transform.ts +31 -0
  19. package/examples/google-flights/get_flight_calendar_prices/workflow.json +78 -0
  20. package/examples/google-flights/lookup_airport/index.ts +101 -0
  21. package/examples/google-flights/lookup_airport/package.json +9 -0
  22. package/examples/google-flights/lookup_airport/parser.ts +66 -0
  23. package/examples/google-flights/lookup_airport/playbook.yaml +47 -0
  24. package/examples/google-flights/lookup_airport/request-transform.ts +20 -0
  25. package/examples/google-flights/lookup_airport/workflow.json +57 -0
  26. package/examples/google-flights/search_flights/index.ts +219 -0
  27. package/examples/google-flights/search_flights/package.json +9 -0
  28. package/examples/google-flights/search_flights/parser.ts +169 -0
  29. package/examples/google-flights/search_flights/playbook.yaml +184 -0
  30. package/examples/google-flights/search_flights/request-transform.ts +119 -0
  31. package/examples/google-flights/search_flights/workflow.json +143 -0
  32. package/examples/google-hotels/README.md +29 -0
  33. package/examples/google-hotels/_shared/batchexecute.ts +73 -0
  34. package/examples/google-hotels/_shared/freq.ts +158 -0
  35. package/examples/google-hotels/_shared/package.json +9 -0
  36. package/examples/google-hotels/autocomplete_hotel_location/index.ts +80 -0
  37. package/examples/google-hotels/autocomplete_hotel_location/package.json +9 -0
  38. package/examples/google-hotels/autocomplete_hotel_location/parser.ts +71 -0
  39. package/examples/google-hotels/autocomplete_hotel_location/playbook.yaml +36 -0
  40. package/examples/google-hotels/autocomplete_hotel_location/request-transform.ts +37 -0
  41. package/examples/google-hotels/autocomplete_hotel_location/workflow.json +36 -0
  42. package/examples/google-hotels/get_hotel_booking_options/index.ts +143 -0
  43. package/examples/google-hotels/get_hotel_booking_options/package.json +9 -0
  44. package/examples/google-hotels/get_hotel_booking_options/parser.ts +271 -0
  45. package/examples/google-hotels/get_hotel_booking_options/playbook.yaml +154 -0
  46. package/examples/google-hotels/get_hotel_booking_options/request-transform.ts +154 -0
  47. package/examples/google-hotels/get_hotel_booking_options/workflow.json +84 -0
  48. package/examples/google-hotels/get_hotel_reviews/index.ts +81 -0
  49. package/examples/google-hotels/get_hotel_reviews/package.json +9 -0
  50. package/examples/google-hotels/get_hotel_reviews/parser.ts +128 -0
  51. package/examples/google-hotels/get_hotel_reviews/playbook.yaml +64 -0
  52. package/examples/google-hotels/get_hotel_reviews/request-transform.ts +42 -0
  53. package/examples/google-hotels/get_hotel_reviews/workflow.json +37 -0
  54. package/examples/google-hotels/search_hotels/index.ts +207 -0
  55. package/examples/google-hotels/search_hotels/package.json +9 -0
  56. package/examples/google-hotels/search_hotels/parser.ts +260 -0
  57. package/examples/google-hotels/search_hotels/playbook.yaml +87 -0
  58. package/examples/google-hotels/search_hotels/request-transform.ts +197 -0
  59. package/examples/google-hotels/search_hotels/workflow.json +127 -0
  60. package/examples/southwest/README.md +3 -2
  61. package/examples/southwest/search_southwest_flights/index.ts +18 -1
  62. package/examples/southwest/search_southwest_flights/workflow.json +18 -1
  63. package/package.json +3 -2
  64. package/prompts/audit-agent.md +71 -0
  65. package/prompts/build-planning.md +74 -0
  66. package/prompts/compile-agent.md +131 -27
  67. package/prompts/prereq-builder.md +64 -0
  68. package/prompts/prereq-planner.md +34 -0
  69. package/prompts/tool-planning.md +39 -0
  70. package/src/cli.ts +116 -3
  71. package/src/imprint/agent.ts +5 -0
  72. package/src/imprint/audit.ts +996 -0
  73. package/src/imprint/backend-ladder.ts +1214 -184
  74. package/src/imprint/build-plan.ts +1051 -0
  75. package/src/imprint/cdp-browser-fetch.ts +592 -0
  76. package/src/imprint/cdp-jar-cache.ts +320 -0
  77. package/src/imprint/chromium.ts +414 -8
  78. package/src/imprint/claude-cli-compile.ts +125 -25
  79. package/src/imprint/codex-cli-compile.ts +26 -23
  80. package/src/imprint/compile-agent-types.ts +38 -0
  81. package/src/imprint/compile-agent.ts +63 -25
  82. package/src/imprint/compile-tools.ts +1666 -66
  83. package/src/imprint/compile.ts +13 -1
  84. package/src/imprint/concurrency.ts +87 -0
  85. package/src/imprint/cron.ts +4 -0
  86. package/src/imprint/doctor.ts +48 -3
  87. package/src/imprint/freeform-redact.ts +5 -4
  88. package/src/imprint/install.ts +79 -4
  89. package/src/imprint/integrations.ts +3 -3
  90. package/src/imprint/llm.ts +56 -8
  91. package/src/imprint/mcp-compile-server.ts +43 -10
  92. package/src/imprint/mcp-maintenance.ts +18 -102
  93. package/src/imprint/mcp-server.ts +73 -7
  94. package/src/imprint/multi-progress.ts +7 -2
  95. package/src/imprint/param-grounding.ts +367 -0
  96. package/src/imprint/paths.ts +29 -0
  97. package/src/imprint/playbook-runner.ts +101 -40
  98. package/src/imprint/prereq-builder.ts +651 -0
  99. package/src/imprint/probe-backends.ts +6 -3
  100. package/src/imprint/record.ts +10 -1
  101. package/src/imprint/redact.ts +30 -2
  102. package/src/imprint/replay-capture.ts +19 -18
  103. package/src/imprint/runtime.ts +19 -10
  104. package/src/imprint/session-diff.ts +79 -2
  105. package/src/imprint/session-merge.ts +9 -5
  106. package/src/imprint/stealth-chromium.ts +79 -0
  107. package/src/imprint/stealth-fetch.ts +309 -29
  108. package/src/imprint/stealth-token-cache.ts +88 -0
  109. package/src/imprint/teach-plan.ts +251 -0
  110. package/src/imprint/teach-state.ts +10 -0
  111. package/src/imprint/teach.ts +456 -142
  112. package/src/imprint/tool-candidates.ts +72 -14
  113. package/src/imprint/tool-plan.ts +313 -0
  114. package/src/imprint/tracing.ts +135 -6
  115. package/src/imprint/types.ts +61 -3
  116. package/examples/google-flights/search_google_flights/index.ts +0 -101
  117. package/examples/google-flights/search_google_flights/parser.test.ts +0 -140
  118. package/examples/google-flights/search_google_flights/parser.ts +0 -189
  119. package/examples/google-flights/search_google_flights/playbook.yaml +0 -130
  120. package/examples/google-flights/search_google_flights/workflow.json +0 -48
  121. package/examples/google-hotels/search_google_hotels/index.ts +0 -194
  122. package/examples/google-hotels/search_google_hotels/parser.test.ts +0 -168
  123. package/examples/google-hotels/search_google_hotels/parser.ts +0 -330
  124. package/examples/google-hotels/search_google_hotels/playbook.yaml +0 -125
  125. package/examples/google-hotels/search_google_hotels/workflow.json +0 -111
  126. package/examples/namecheap-domains/search_namecheap_domains/index.ts +0 -144
  127. package/examples/namecheap-domains/search_namecheap_domains/parser.ts +0 -380
  128. package/examples/namecheap-domains/search_namecheap_domains/playbook.yaml +0 -50
  129. package/examples/namecheap-domains/search_namecheap_domains/request-transform.ts +0 -136
  130. package/examples/namecheap-domains/search_namecheap_domains/workflow.json +0 -97
@@ -0,0 +1,81 @@
1
+ /**
2
+ * GENERATED by `imprint emit` — DO NOT EDIT BY HAND.
3
+ *
4
+ * Tool: get_hotel_reviews
5
+ * Site: google-hotels
6
+ * Intent: Fetch aggregated reviews and reviewer snippets for a selected Google Hotels hotel.
7
+ *
8
+ * To regenerate: imprint emit ~/.imprint/google-hotels/get_hotel_reviews/workflow.json --force
9
+ */
10
+
11
+ import { fileURLToPath } from 'node:url';
12
+ import { dirname, join } from 'node:path';
13
+ import {
14
+ executeWorkflow,
15
+ type CredentialStore,
16
+ } from 'imprint/runtime';
17
+ import type { ToolResult, Workflow } from 'imprint/types';
18
+
19
+ const WORKFLOW: Workflow = {
20
+ "toolName": "get_hotel_reviews",
21
+ "intent": {
22
+ "description": "Fetch aggregated reviews and reviewer snippets for a selected Google Hotels hotel.",
23
+ "userSaid": "clicked one of the offerings, saw booking options"
24
+ },
25
+ "parameters": [
26
+ {
27
+ "name": "hotel_id",
28
+ "type": "string",
29
+ "description": "Opaque hotel token (e.g. \"ChcI78-luoXdhoaIARoKL20vMDJ2cGdnMRAB\") obtained from the search_hotels tool's hotel_id output.",
30
+ "verified": true,
31
+ "sourcedFrom": {
32
+ "tool": "search_hotels",
33
+ "field": "hotel_id"
34
+ }
35
+ }
36
+ ],
37
+ "requests": [
38
+ {
39
+ "method": "POST",
40
+ "url": "https://www.google.com/_/TravelFrontendUi/data/batchexecute?rpcids=ocp93e&source-path=%2Ftravel%2Fsearch&f.sid=7513562915459271421&bl=boq_travel-frontend-ui_20260527.01_p0&hl=en-US&soc-app=162&soc-platform=1&soc-device=1&_reqid=3252256&rt=c",
41
+ "headers": {
42
+ "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
43
+ "X-Same-Domain": "1",
44
+ "Origin": "https://www.google.com",
45
+ "Referer": "https://www.google.com/travel/search"
46
+ },
47
+ "body": "f.req=%5B%5B%5B%22ocp93e%22%2C%22%5Bnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2C%5C%22${param.hotel_id}%5C%22%2Cnull%2Cnull%2C%5B%5B%5D%5D%5D%22%2Cnull%2C%221%22%5D%5D%5D&",
48
+ "effect": "safe"
49
+ }
50
+ ],
51
+ "site": "google-hotels",
52
+ "parserModule": "./parser.ts",
53
+ "requestTransformModule": "./request-transform.ts",
54
+ "liveVerified": true
55
+ };
56
+
57
+ export interface GetHotelReviewsInput {
58
+ /** Opaque hotel token (e.g. "ChcI78-luoXdhoaIARoKL20vMDJ2cGdnMRAB") obtained from the search_hotels tool's hotel_id output. */
59
+ hotel_id: string;
60
+ }
61
+
62
+ export async function getHotelReviews(
63
+ input: GetHotelReviewsInput,
64
+ opts: { credentials?: CredentialStore; fetchImpl?: typeof fetch; initialState?: Record<string, unknown> } = {},
65
+ ): Promise<ToolResult> {
66
+ const __dirname = dirname(fileURLToPath(import.meta.url));
67
+ const params: Record<string, string | number | boolean> = {
68
+ hotel_id: input.hotel_id,
69
+
70
+ };
71
+ return executeWorkflow({
72
+ workflow: WORKFLOW,
73
+ params,
74
+ credentials: opts.credentials,
75
+ fetchImpl: opts.fetchImpl,
76
+ initialState: opts.initialState,
77
+ workflowPath: join(__dirname, 'workflow.json'),
78
+ });
79
+ }
80
+
81
+ export { WORKFLOW };
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "imprint-tool-google-hotels",
3
+ "private": true,
4
+ "devDependencies": {
5
+ "@types/bun": "latest",
6
+ "@types/node": "latest",
7
+ "bun-types": "latest"
8
+ }
9
+ }
@@ -0,0 +1,128 @@
1
+ import { parseBatchExecute } from '../_shared/batchexecute.ts';
2
+
3
+ // Shape of one review-source "entry" in the ocp93e inner JSON (recording seq 497):
4
+ // entry[0] = provider: [ providerName, null, [iconUrl, w, h], code, count ]
5
+ // entry[1] = review: [ [reviewerName, profileLink, [avatarUrl, w, h]],
6
+ // dateText, [score, outOf], [[ [code, text, ...], ... ]],
7
+ // sourceLink, ... ]
8
+ // The entries list lives at root[0][0] (root = [[[ entry, entry, ... ]]]).
9
+
10
+ interface ParsedReview {
11
+ reviewerName: string | null;
12
+ profileLink: string | null;
13
+ avatarUrl: string | null;
14
+ date: string | null;
15
+ ratingScore: number | null;
16
+ ratingOutOf: number | null;
17
+ texts: string[];
18
+ sourceLink: string | null;
19
+ }
20
+
21
+ interface ParsedEntry {
22
+ provider: {
23
+ name: string | null;
24
+ iconUrl: string | null;
25
+ count: number | null;
26
+ };
27
+ review: ParsedReview;
28
+ }
29
+
30
+ function asArray(v: unknown): any[] {
31
+ return Array.isArray(v) ? (v as any[]) : [];
32
+ }
33
+
34
+ function looksLikeEntry(e: unknown): boolean {
35
+ // entry[0] is the provider array whose first element is the provider name string.
36
+ return (
37
+ Array.isArray(e) &&
38
+ Array.isArray((e as any[])[0]) &&
39
+ typeof (e as any[])[0][0] === 'string'
40
+ );
41
+ }
42
+
43
+ // The entries-list nesting depth is the one value the recording does not fully
44
+ // pin down, so locate the array whose elements have the entry shape rather than
45
+ // hardcoding root[0][0]. Falls back to a recursive search if the expected path
46
+ // does not match.
47
+ function findEntriesList(root: unknown): any[] {
48
+ const direct = asArray(asArray(asArray(root)[0])[0]);
49
+ if (direct.length > 0 && direct.some(looksLikeEntry)) return direct;
50
+
51
+ let best: any[] = [];
52
+ const visit = (node: unknown, depth: number) => {
53
+ if (depth > 8 || !Array.isArray(node)) return;
54
+ const arr = node as any[];
55
+ if (arr.length > 0 && arr.every(looksLikeEntry)) {
56
+ if (arr.length > best.length) best = arr;
57
+ return;
58
+ }
59
+ for (const child of arr) visit(child, depth + 1);
60
+ };
61
+ visit(root, 0);
62
+ return best;
63
+ }
64
+
65
+ function parseEntry(entry: any[]): ParsedEntry {
66
+ const provider = asArray(entry[0]);
67
+ const review = asArray(entry[1]);
68
+ const reviewer = asArray(review[0]);
69
+ const rating = asArray(review[2]);
70
+
71
+ // review[3] = [[ [code, text, null, text, ...], ... ]] -> collect text strings.
72
+ const textGroups = asArray(asArray(review[3])[0]);
73
+ const texts: string[] = [];
74
+ for (const t of textGroups) {
75
+ if (Array.isArray(t) && typeof t[1] === 'string' && t[1].trim() !== '') {
76
+ texts.push(t[1]);
77
+ }
78
+ }
79
+
80
+ return {
81
+ provider: {
82
+ name: typeof provider[0] === 'string' ? provider[0] : null,
83
+ iconUrl: Array.isArray(provider[2]) && typeof provider[2][0] === 'string'
84
+ ? provider[2][0]
85
+ : null,
86
+ count: typeof provider[4] === 'number' ? provider[4] : null,
87
+ },
88
+ review: {
89
+ reviewerName: typeof reviewer[0] === 'string' ? reviewer[0] : null,
90
+ profileLink: typeof reviewer[1] === 'string' ? reviewer[1] : null,
91
+ avatarUrl: Array.isArray(reviewer[2]) && typeof reviewer[2][0] === 'string'
92
+ ? reviewer[2][0]
93
+ : null,
94
+ date: typeof review[1] === 'string' && review[1] !== '' ? review[1] : null,
95
+ ratingScore: typeof rating[0] === 'number' ? rating[0] : null,
96
+ ratingOutOf: typeof rating[1] === 'number' ? rating[1] : null,
97
+ texts,
98
+ sourceLink: typeof review[4] === 'string' && review[4] !== '' ? review[4] : null,
99
+ },
100
+ };
101
+ }
102
+
103
+ export function extract(
104
+ rawResponse: unknown,
105
+ _context?: {
106
+ params: Record<string, string | number | boolean>;
107
+ responses: unknown[];
108
+ },
109
+ ): unknown {
110
+ const raw =
111
+ typeof rawResponse === 'string' ? rawResponse : JSON.stringify(rawResponse);
112
+ const root = parseBatchExecute(raw, 'ocp93e');
113
+ if (root == null) return { reviews: [], count: 0 };
114
+
115
+ const entries = findEntriesList(root);
116
+ const reviews = entries
117
+ .filter(looksLikeEntry)
118
+ .map((e) => parseEntry(e as any[]))
119
+ // Drop content-less placeholder rows (API no-match sentinel).
120
+ .filter(
121
+ (e) =>
122
+ e.provider.name != null ||
123
+ e.review.reviewerName != null ||
124
+ e.review.texts.length > 0,
125
+ );
126
+
127
+ return { reviews, count: reviews.length };
128
+ }
@@ -0,0 +1,64 @@
1
+ toolName: get_hotel_reviews
2
+ summary: Open a hotel's detail panel on Google Hotels and capture its aggregated reviews (the ocp93e batchexecute response).
3
+ parameters:
4
+ - name: location
5
+ type: string
6
+ description: Destination/area to search for hotels, e.g. "Chicago Loop". Used to populate the search box and pick the matching autocomplete suggestion.
7
+ - name: hotel_name
8
+ type: string
9
+ description: Display name of the hotel whose reviews are wanted, e.g. "Hyatt Regency Chicago". Used to click the matching result card.
10
+ steps:
11
+ - action: navigate
12
+ url: https://www.google.com/travel/search
13
+ wait_for: networkidle
14
+ - action: click
15
+ locators:
16
+ - by: aria_label
17
+ value: Search for places, hotels and more
18
+ - by: css
19
+ value: input.II2One.j0Ppje
20
+ wait_for:
21
+ sleep_ms: 300
22
+ - action: type
23
+ locators:
24
+ - by: aria_label
25
+ value: Search for places, hotels and more
26
+ - by: css
27
+ value: input.II2One.j0Ppje
28
+ value: ${location}
29
+ wait_for:
30
+ sleep_ms: 600
31
+ - action: click
32
+ locators:
33
+ - by: text
34
+ value_pattern: ${location}
35
+ - by: css
36
+ value: ul.F3AVKd > li.Q1RWxd span.S5TdWc
37
+ wait_for:
38
+ xhr: rpcids=AtySUc
39
+ - action: click
40
+ locators:
41
+ - by: aria_label
42
+ value_pattern: ${hotel_name}
43
+ - by: text
44
+ value_pattern: ${hotel_name}
45
+ - by: css
46
+ value: a.PVOOXe
47
+ wait_for:
48
+ xhr: rpcids=ocp93e
49
+ result:
50
+ source: xhr
51
+ url_pattern: rpcids=ocp93e
52
+ extract: "[0][0][0][]"
53
+ return_as: reviews
54
+ notes: >-
55
+ The ocp93e response uses Google's anti-XSSI batchexecute envelope: strip the ")]}'" prefix and chunk-length lines,
56
+ JSON.parse the outer array, then JSON.parse the escaped inner string at ["wrb.fr","ocp93e", <innerJsonString>].
57
+ The inner JSON is positional (no object keys); the extract index path "[0][0][0][]" iterates the review-source entries.
58
+ Within each entry: index [0] = provider block [providerName, null, [iconUrl,w,h], type, id]; index [1] = reviewer block
59
+ [[reviewerName, profileUrl, [avatarUrl,w,h]], relativeDate, [ratingNum, ratingMax], [[ [sentiment, reviewText, ...] ]], reviewUrl, ...].
60
+ Reviews load automatically when the hotel detail panel opens (the same click also triggers M0CRd pricing) — opening the
61
+ Reviews tab is not required. Check-in/out dates were set during recording but do not affect the review payload, so they are
62
+ omitted as parameters. f.sid / bl / X-Goog-BatchExecute-Bgr are session-bound and are supplied by the live browser context,
63
+ not by this playbook. If the hotel result is not visible in the initial list, the agent may need to scroll the results column
64
+ before the ${hotel_name} click resolves.
@@ -0,0 +1,42 @@
1
+ // Per-tool request-transform for get_hotel_reviews. Builds the ocp93e inner
2
+ // payload from the hotel_id param and delegates envelope/URL construction to the
3
+ // shared google-hotels batchexecute helpers.
4
+ import { buildFreqBody, buildBatchExecuteUrl } from '../_shared/freq.ts';
5
+
6
+ export function transform(
7
+ method: string,
8
+ url: string,
9
+ responses: Record<string, any>,
10
+ params?: Record<string, any>,
11
+ ): { url: string; body: string } {
12
+ void method;
13
+ void responses;
14
+
15
+ const hotelId = String(params?.hotel_id ?? '');
16
+
17
+ // Recorded inner payload (seq 497): positions 0-7 null, [8] = hotel token,
18
+ // 9 & 10 null, [11] = constant [[]].
19
+ const innerPayload = [
20
+ null,
21
+ null,
22
+ null,
23
+ null,
24
+ null,
25
+ null,
26
+ null,
27
+ null,
28
+ hotelId,
29
+ null,
30
+ null,
31
+ [[]],
32
+ ];
33
+
34
+ const u = new URL(url);
35
+ const fSid = u.searchParams.get('f.sid') ?? '';
36
+ const bl = u.searchParams.get('bl') ?? '';
37
+
38
+ return {
39
+ url: buildBatchExecuteUrl('ocp93e', { f_sid: fSid, bl }),
40
+ body: buildFreqBody('ocp93e', innerPayload, '1'),
41
+ };
42
+ }
@@ -0,0 +1,37 @@
1
+ {
2
+ "toolName": "get_hotel_reviews",
3
+ "intent": {
4
+ "description": "Fetch aggregated reviews and reviewer snippets for a selected Google Hotels hotel.",
5
+ "userSaid": "clicked one of the offerings, saw booking options"
6
+ },
7
+ "site": "google-hotels",
8
+ "parameters": [
9
+ {
10
+ "name": "hotel_id",
11
+ "type": "string",
12
+ "description": "Opaque hotel token (e.g. \"ChcI78-luoXdhoaIARoKL20vMDJ2cGdnMRAB\") obtained from the search_hotels tool's hotel_id output.",
13
+ "verified": true,
14
+ "sourcedFrom": {
15
+ "tool": "search_hotels",
16
+ "field": "hotel_id"
17
+ }
18
+ }
19
+ ],
20
+ "requestTransformModule": "./request-transform.ts",
21
+ "parserModule": "./parser.ts",
22
+ "requests": [
23
+ {
24
+ "method": "POST",
25
+ "url": "https://www.google.com/_/TravelFrontendUi/data/batchexecute?rpcids=ocp93e&source-path=%2Ftravel%2Fsearch&f.sid=7513562915459271421&bl=boq_travel-frontend-ui_20260527.01_p0&hl=en-US&soc-app=162&soc-platform=1&soc-device=1&_reqid=3252256&rt=c",
26
+ "headers": {
27
+ "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
28
+ "X-Same-Domain": "1",
29
+ "Origin": "https://www.google.com",
30
+ "Referer": "https://www.google.com/travel/search"
31
+ },
32
+ "body": "f.req=%5B%5B%5B%22ocp93e%22%2C%22%5Bnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2C%5C%22${param.hotel_id}%5C%22%2Cnull%2Cnull%2C%5B%5B%5D%5D%5D%22%2Cnull%2C%221%22%5D%5D%5D&",
33
+ "effect": "safe"
34
+ }
35
+ ],
36
+ "liveVerified": true
37
+ }
@@ -0,0 +1,207 @@
1
+ /**
2
+ * GENERATED by `imprint emit` — DO NOT EDIT BY HAND.
3
+ *
4
+ * Tool: search_hotels
5
+ * Site: google-hotels
6
+ * Intent: Search Google Hotels for a location and date range with optional filters (price, rating, amenities, star class, brands, sort, property type).
7
+ *
8
+ * To regenerate: imprint emit ~/.imprint/google-hotels/search_hotels/workflow.json --force
9
+ */
10
+
11
+ import { fileURLToPath } from 'node:url';
12
+ import { dirname, join } from 'node:path';
13
+ import {
14
+ executeWorkflow,
15
+ type CredentialStore,
16
+ } from 'imprint/runtime';
17
+ import type { ToolResult, Workflow } from 'imprint/types';
18
+
19
+ const WORKFLOW: Workflow = {
20
+ "toolName": "search_hotels",
21
+ "intent": {
22
+ "description": "Search Google Hotels for a location and date range with optional filters (price, rating, amenities, star class, brands, sort, property type).",
23
+ "userSaid": "searched for hotels at chicago loop from july 3-6; filtered for > 4.0 rating; filtered min and max price; added amenities; added 2/4/5 star; added brand filters; sorted by lowest price, highest rating, most reviewed; changed property type to vacation rentals; changed location to tahoe city and denver downtown"
24
+ },
25
+ "parameters": [
26
+ {
27
+ "name": "location",
28
+ "type": "string",
29
+ "description": "Destination query, e.g. 'chicago loop', 'tahoe city', 'denver downtown'",
30
+ "default": "chicago loop",
31
+ "verified": true
32
+ },
33
+ {
34
+ "name": "check_in_date",
35
+ "type": "string",
36
+ "description": "Check-in date (YYYY-MM-DD)",
37
+ "default": "2026-07-03",
38
+ "verified": false,
39
+ "verifyNote": "annotated"
40
+ },
41
+ {
42
+ "name": "check_out_date",
43
+ "type": "string",
44
+ "description": "Check-out date (YYYY-MM-DD)",
45
+ "default": "2026-07-06",
46
+ "verified": false,
47
+ "verifyNote": "annotated"
48
+ },
49
+ {
50
+ "name": "adults",
51
+ "type": "number",
52
+ "description": "Number of adult travelers",
53
+ "default": 2,
54
+ "verified": false,
55
+ "verifyNote": "annotated"
56
+ },
57
+ {
58
+ "name": "children",
59
+ "type": "number",
60
+ "description": "Number of child travelers",
61
+ "default": 0,
62
+ "verified": false,
63
+ "verifyNote": "annotated"
64
+ },
65
+ {
66
+ "name": "min_rating",
67
+ "type": "number",
68
+ "description": "Minimum guest rating filter, e.g. 4.0 (0 = no filter)",
69
+ "default": 0,
70
+ "verified": false,
71
+ "verifyNote": "annotated"
72
+ },
73
+ {
74
+ "name": "min_price",
75
+ "type": "number",
76
+ "description": "Minimum nightly price filter (0 = no filter)",
77
+ "default": 0,
78
+ "verified": false,
79
+ "verifyNote": "annotated"
80
+ },
81
+ {
82
+ "name": "max_price",
83
+ "type": "number",
84
+ "description": "Maximum nightly price filter, e.g. 338 (0 = no filter)",
85
+ "default": 0,
86
+ "verified": true
87
+ },
88
+ {
89
+ "name": "amenities",
90
+ "type": "string",
91
+ "description": "Comma-separated amenity names or codes (e.g. 'free wi-fi,pool' or '6,9'); empty = no filter",
92
+ "default": "",
93
+ "verified": false,
94
+ "verifyNote": "annotated"
95
+ },
96
+ {
97
+ "name": "hotel_class",
98
+ "type": "string",
99
+ "description": "Comma-separated star classes, e.g. '2,3' or '4,5'; empty = no filter",
100
+ "default": "",
101
+ "verified": true
102
+ },
103
+ {
104
+ "name": "brands",
105
+ "type": "string",
106
+ "description": "Comma-separated brand codes ('parent:child' or numeric); empty = no filter",
107
+ "default": "",
108
+ "verified": false,
109
+ "verifyNote": "annotated"
110
+ },
111
+ {
112
+ "name": "sort_by",
113
+ "type": "string",
114
+ "description": "Sort order: relevance | lowest_price | highest_rating | most_reviewed",
115
+ "default": "relevance",
116
+ "verified": true
117
+ },
118
+ {
119
+ "name": "property_type",
120
+ "type": "string",
121
+ "description": "hotels | vacation_rentals",
122
+ "default": "hotels",
123
+ "verified": true
124
+ }
125
+ ],
126
+ "requests": [
127
+ {
128
+ "method": "POST",
129
+ "url": "https://www.google.com/_/TravelFrontendUi/data/batchexecute?rpcids=AtySUc&source-path=%2Ftravel%2Fsearch&f.sid=7513562915459271421&bl=boq_travel-frontend-ui_20260527.01_p0&hl=en-US&soc-app=162&soc-platform=1&soc-device=1&_reqid=2252256&rt=c",
130
+ "headers": {
131
+ "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
132
+ "X-Same-Domain": "1",
133
+ "x-goog-ext-259736195-jspb": "[\"en-US\",\"US\",\"USD\",2,null,[420],null,null,7,[]]",
134
+ "x-goog-ext-190139975-jspb": "[\"US\",\"ZZ\",\"x6c25Q==\"]",
135
+ "Referer": "https://www.google.com/travel/search"
136
+ },
137
+ "body": "f.req={\"location\":\"${param.location}\",\"check_in_date\":\"${param.check_in_date}\",\"check_out_date\":\"${param.check_out_date}\",\"adults\":\"${param.adults}\",\"children\":\"${param.children}\",\"min_rating\":\"${param.min_rating}\",\"min_price\":\"${param.min_price}\",\"max_price\":\"${param.max_price}\",\"amenities\":\"${param.amenities}\",\"hotel_class\":\"${param.hotel_class}\",\"brands\":\"${param.brands}\",\"sort_by\":\"${param.sort_by}\",\"property_type\":\"${param.property_type}\"}",
138
+ "effect": "safe"
139
+ }
140
+ ],
141
+ "site": "google-hotels",
142
+ "parserModule": "./parser.ts",
143
+ "requestTransformModule": "./request-transform.ts",
144
+ "liveVerified": true
145
+ };
146
+
147
+ export interface SearchHotelsInput {
148
+ /** Destination query, e.g. 'chicago loop', 'tahoe city', 'denver downtown' */
149
+ location?: string;
150
+ /** Check-in date (YYYY-MM-DD) */
151
+ check_in_date?: string;
152
+ /** Check-out date (YYYY-MM-DD) */
153
+ check_out_date?: string;
154
+ /** Number of adult travelers */
155
+ adults?: number;
156
+ /** Number of child travelers */
157
+ children?: number;
158
+ /** Minimum guest rating filter, e.g. 4.0 (0 = no filter) */
159
+ min_rating?: number;
160
+ /** Minimum nightly price filter (0 = no filter) */
161
+ min_price?: number;
162
+ /** Maximum nightly price filter, e.g. 338 (0 = no filter) */
163
+ max_price?: number;
164
+ /** Comma-separated amenity names or codes (e.g. 'free wi-fi,pool' or '6,9'); empty = no filter */
165
+ amenities?: string;
166
+ /** Comma-separated star classes, e.g. '2,3' or '4,5'; empty = no filter */
167
+ hotel_class?: string;
168
+ /** Comma-separated brand codes ('parent:child' or numeric); empty = no filter */
169
+ brands?: string;
170
+ /** Sort order: relevance | lowest_price | highest_rating | most_reviewed */
171
+ sort_by?: string;
172
+ /** hotels | vacation_rentals */
173
+ property_type?: string;
174
+ }
175
+
176
+ export async function searchHotels(
177
+ input: SearchHotelsInput,
178
+ opts: { credentials?: CredentialStore; fetchImpl?: typeof fetch; initialState?: Record<string, unknown> } = {},
179
+ ): Promise<ToolResult> {
180
+ const __dirname = dirname(fileURLToPath(import.meta.url));
181
+ const params: Record<string, string | number | boolean> = {
182
+ location: input.location ?? "chicago loop",
183
+ check_in_date: input.check_in_date ?? "2026-07-03",
184
+ check_out_date: input.check_out_date ?? "2026-07-06",
185
+ adults: input.adults ?? 2,
186
+ children: input.children ?? 0,
187
+ min_rating: input.min_rating ?? 0,
188
+ min_price: input.min_price ?? 0,
189
+ max_price: input.max_price ?? 0,
190
+ amenities: input.amenities ?? "",
191
+ hotel_class: input.hotel_class ?? "",
192
+ brands: input.brands ?? "",
193
+ sort_by: input.sort_by ?? "relevance",
194
+ property_type: input.property_type ?? "hotels",
195
+
196
+ };
197
+ return executeWorkflow({
198
+ workflow: WORKFLOW,
199
+ params,
200
+ credentials: opts.credentials,
201
+ fetchImpl: opts.fetchImpl,
202
+ initialState: opts.initialState,
203
+ workflowPath: join(__dirname, 'workflow.json'),
204
+ });
205
+ }
206
+
207
+ export { WORKFLOW };
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "imprint-tool-google-hotels",
3
+ "private": true,
4
+ "devDependencies": {
5
+ "@types/bun": "latest",
6
+ "@types/node": "latest",
7
+ "bun-types": "latest"
8
+ }
9
+ }