@nooticr/mcp 1.26.32 → 1.26.34
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 +25 -3
- package/dist/shared/amazon.js +480 -0
- package/dist/shared/evidence.js +4 -4
- package/dist/shared/jobs.js +1 -1
- package/dist/shared/loading-plans.js +54 -2
- package/dist/shared/output-schemas.js +86 -2
- package/dist/shared/tools-def.js +26 -2
- package/dist/shared/tools.js +34 -17
- package/dist/shared/ui-template.js +471 -1
- package/package.json +1 -1
|
@@ -225,6 +225,65 @@ const analyzed = { ...singlePost, analysis, analyzed: scalar() };
|
|
|
225
225
|
* One entry per tool. A tool missing from here declares nothing, which is the
|
|
226
226
|
* honest option when the shape has not been checked against a real response.
|
|
227
227
|
*/
|
|
228
|
+
/**
|
|
229
|
+
* One Amazon listing, as the scan hands it over.
|
|
230
|
+
*
|
|
231
|
+
* `aspects` and `reviewSummary` are Amazon's own, not ours, and they are the
|
|
232
|
+
* half of a listing that survives the sign-in gate which withholds review
|
|
233
|
+
* bodies — so they are declared beside `reviews` rather than folded into it.
|
|
234
|
+
*/
|
|
235
|
+
const amazonProduct = open({
|
|
236
|
+
asin: scalar(),
|
|
237
|
+
url: scalar(),
|
|
238
|
+
title: scalar(),
|
|
239
|
+
brand: scalar(),
|
|
240
|
+
price: scalar(),
|
|
241
|
+
priceValue: scalar(),
|
|
242
|
+
listPrice: scalar(),
|
|
243
|
+
availability: scalar(),
|
|
244
|
+
rating: scalar(),
|
|
245
|
+
ratingCount: scalar(),
|
|
246
|
+
image: scalar(),
|
|
247
|
+
images: listOf(z.string()),
|
|
248
|
+
features: listOf(z.string()),
|
|
249
|
+
categories: listOf(z.string()),
|
|
250
|
+
specs: open({}).nullish(),
|
|
251
|
+
ratingHistogram: open({}).nullish().describe("Star bucket to share, e.g. {\"5\": \"73%\"}."),
|
|
252
|
+
reviewSummary: scalar().describe("Amazon's own digest of the reviews."),
|
|
253
|
+
aspects: listOf(open({ name: scalar(), mentions: scalar(), sentiment: scalar() })),
|
|
254
|
+
reviewsGated: scalar().describe("True when Amazon withheld the review bodies behind sign-in."),
|
|
255
|
+
reviews: anyList(),
|
|
256
|
+
});
|
|
257
|
+
/** One thing you concluded, with what it rests on. */
|
|
258
|
+
const amazonFinding = open({
|
|
259
|
+
label: scalar(),
|
|
260
|
+
detail: scalar(),
|
|
261
|
+
evidence: listOf(z.string()).describe("Review ids or short quotes."),
|
|
262
|
+
strength: scalar().describe("strong / moderate / thin — how well the collected text supports it."),
|
|
263
|
+
});
|
|
264
|
+
const amazonScanShape = {
|
|
265
|
+
guidance: scalar(),
|
|
266
|
+
mode: scalar(),
|
|
267
|
+
marketplace: scalar(),
|
|
268
|
+
scanId: scalar().describe("Pass to amazon_scan_status to pick a running collection back up."),
|
|
269
|
+
status: scalar(),
|
|
270
|
+
complete: scalar().describe("False means more listings are still coming."),
|
|
271
|
+
query: scalar(),
|
|
272
|
+
domain: scalar(),
|
|
273
|
+
progress: open({
|
|
274
|
+
done: scalar(),
|
|
275
|
+
total: scalar(),
|
|
276
|
+
message: scalar(),
|
|
277
|
+
elapsedMs: scalar(),
|
|
278
|
+
fromCache: scalar(),
|
|
279
|
+
}).nullish(),
|
|
280
|
+
rollup: open({}).nullish().describe("The arithmetic over the set: price and rating spread, aspects by brand, 1-2 star share."),
|
|
281
|
+
products: listOf(amazonProduct),
|
|
282
|
+
reviews: anyList().describe("Every review across the scan, worst-rated first — the barriers are the half a summary drops."),
|
|
283
|
+
errors: anyList(),
|
|
284
|
+
mcpCredits,
|
|
285
|
+
};
|
|
286
|
+
const amazonScan = open(amazonScanShape);
|
|
228
287
|
export const OUTPUT_SCHEMAS = {
|
|
229
288
|
analyze_post: open({ ...analyzed, ...evidence }),
|
|
230
289
|
analyze_post_fast: open({ ...evidence, ...analyzed }),
|
|
@@ -418,7 +477,7 @@ export const OUTPUT_SCHEMAS = {
|
|
|
418
477
|
// thinking" (README) — but until these existed, the thinking had nowhere
|
|
419
478
|
// to land except chat text. Free, and make no requests, same as
|
|
420
479
|
// show_comment_review: they only draw what they're handed.
|
|
421
|
-
|
|
480
|
+
show_compared_posts: open({
|
|
422
481
|
posts: listOf(post).describe("The 2-5 posts being compared, same shape as compare_posts returned."),
|
|
423
482
|
comparison: open({
|
|
424
483
|
winner: scalar().describe("1-indexed position of the post that won, matching the posts array."),
|
|
@@ -429,7 +488,7 @@ export const OUTPUT_SCHEMAS = {
|
|
|
429
488
|
}).nullish(),
|
|
430
489
|
mcpCredits,
|
|
431
490
|
}),
|
|
432
|
-
|
|
491
|
+
show_post_analysis: open({
|
|
433
492
|
url: scalar(),
|
|
434
493
|
post: post.nullish().describe("The post analyze_post/analyze_post_fast/understand_social_post handed back."),
|
|
435
494
|
analysis: open({}).passthrough().nullish().describe("Your own analysis, any of the fields analyze_post's guidance asked for — summary, " +
|
|
@@ -1288,4 +1347,29 @@ export const OUTPUT_SCHEMAS = {
|
|
|
1288
1347
|
connectUrl: scalar().describe("Open this in a browser to approve the connection."),
|
|
1289
1348
|
message: scalar(),
|
|
1290
1349
|
}),
|
|
1350
|
+
// Marketplace. One shape for all three collection tools: a status poll
|
|
1351
|
+
// returns exactly what the scan that started it returns, and a single
|
|
1352
|
+
// product is a scan of one — describing them differently would be three
|
|
1353
|
+
// names for one payload and three places for the view to drift.
|
|
1354
|
+
scan_amazon_category: amazonScan,
|
|
1355
|
+
amazon_scan_status: amazonScan,
|
|
1356
|
+
get_amazon_product: open({
|
|
1357
|
+
...amazonScanShape,
|
|
1358
|
+
product: amazonProduct.nullish().describe("The single listing, also present as products[0]."),
|
|
1359
|
+
}),
|
|
1360
|
+
show_amazon_category_insights: open({
|
|
1361
|
+
marketplace: scalar(),
|
|
1362
|
+
view: scalar(),
|
|
1363
|
+
category: scalar(),
|
|
1364
|
+
summary: scalar(),
|
|
1365
|
+
drivers: listOf(amazonFinding).describe("What makes someone buy, as you read it."),
|
|
1366
|
+
barriers: listOf(amazonFinding).describe("What stops them, or makes them return it."),
|
|
1367
|
+
strengths: listOf(open({ brand: scalar(), detail: scalar(), asin: scalar() })),
|
|
1368
|
+
gaps: listOf(amazonFinding),
|
|
1369
|
+
positioning: listOf(open({ angle: scalar(), who: scalar(), why: scalar(), risk: scalar() })),
|
|
1370
|
+
products: listOf(amazonProduct).describe("The listings, passed through so the view can draw them."),
|
|
1371
|
+
rollup: open({}).nullish(),
|
|
1372
|
+
scanId: scalar(),
|
|
1373
|
+
mcpCredits,
|
|
1374
|
+
}),
|
|
1291
1375
|
};
|
package/dist/shared/tools-def.js
CHANGED
|
@@ -157,13 +157,13 @@ export const TOOL_DEFINITIONS = [
|
|
|
157
157
|
inputSchema: z.object({ url: z.string().describe("The post the comments came from."), summary: z.string().optional(), title: z.string().optional(), comments: z.array(z.object({ id: z.string(), text: z.string(), author: z.string().optional(), likes: z.number().optional(), sentiment: z.string().optional(), category: z.string().optional(), note: z.string().optional() })), themes: z.array(z.string()).optional(), nextSteps: z.array(z.string()).optional() }).strict(),
|
|
158
158
|
},
|
|
159
159
|
{
|
|
160
|
-
name: "
|
|
160
|
+
name: "show_compared_posts",
|
|
161
161
|
title: "Show Comparison",
|
|
162
162
|
description: "Display a comparison you wrote after compare_posts fetched the first post and you fetched the rest yourself. Free, and makes no requests — it only draws what you pass it: each post with a BEST badge on the winner, what differed, shared strengths and the next experiment worth running. Call this after you have done the comparing, not instead of it.",
|
|
163
163
|
inputSchema: z.object({ posts: z.array(z.object({}).passthrough()).min(2).max(5), winner: z.number().int(), winnerReason: z.string().optional(), differences: z.array(z.object({ factor: z.string(), detail: z.string() })).optional(), lessons: z.array(z.string()).optional(), nextTest: z.string().optional() }).strict(),
|
|
164
164
|
},
|
|
165
165
|
{
|
|
166
|
-
name: "
|
|
166
|
+
name: "show_post_analysis",
|
|
167
167
|
title: "Show Analysis",
|
|
168
168
|
description: "Display an analysis you wrote after analyze_post, analyze_post_fast or understand_social_post handed you the material. Free, and makes no requests — it only draws what you pass it: hook strength, script structure, quotable lines, hashtags, target audience, viral triggers and more, whichever of these you actually produced. Call this after you have done the analysing, not instead of it.",
|
|
169
169
|
inputSchema: z.object({ url: z.string(), post: z.object({}).passthrough().optional(), analysis: z.object({}).passthrough() }).strict(),
|
|
@@ -414,4 +414,28 @@ export const TOOL_DEFINITIONS = [
|
|
|
414
414
|
description: "Patch your own product's fields \u2014 omitted arguments leave their column unchanged, so this cannot blank a field by not mentioning it. Takes appId, optional when your workspace has exactly one product; every other field is snake_case, the same names create_product takes and read the same way. The result lists which fields were actually written, so a name you spelled wrong shows up as a field that did not change rather than as a silent no-op. Free \u2014 no AI call, just a row.",
|
|
415
415
|
inputSchema: z.object({ appId: z.number().int().optional().describe("Your product's id. Omit only with a single-product workspace."), name: z.string().optional(), slug: z.string().optional(), description: z.string().optional(), website_url: z.string().optional(), niche: z.string().optional(), product_type: z.string().optional(), icon_url: z.string().optional(), primary_cta_label: z.string().optional(), primary_cta_url: z.string().optional(), external_listing_id: z.string().optional(), ios_bundle_id: z.string().optional(), android_package: z.string().optional() }).strict(),
|
|
416
416
|
},
|
|
417
|
+
{
|
|
418
|
+
name: "scan_amazon_category",
|
|
419
|
+
title: "Scan Amazon Category",
|
|
420
|
+
description: "Collect an Amazon category and read it: listings with prices, star histograms, Amazon's own review-aspect counts and the review text itself, plus the arithmetic over them (price spread, rating spread, which aspects recur across how many brands, and the share of each listing's ratings sitting at 1-2 stars). Pass query to search the category, asins to pin named competitors, or both \u2014 named competitors keep their place at the front. You write the read: purchase drivers, barriers, what the incumbents do well, the gaps, and how a new entrant could position; show_amazon_category_insights draws it for free afterwards. Collection runs in the background, so this returns everything ready within waitSeconds plus a scanId to continue with amazon_scan_status. Consumes 3 nooticr credits per listing collected \u2014 a listing with reviews is a real browser render behind Amazon's bot defences.",
|
|
421
|
+
inputSchema: z.object({ query: z.string().optional().describe("Category or keyword to search on Amazon, e.g. 'ashwagandha gummies'."), asins: z.array(z.string()).optional().describe("Named competitors: ASINs or any Amazon product URL. Kept at the front of the set."), limit: z.number().int().optional().describe("Listings to collect (default 10, max 50)."), reviews: z.boolean().optional().describe("Collect review text too (default true)."), domain: z.string().optional().describe("Marketplace host, e.g. www.amazon.co.uk (default www.amazon.com)."), waitSeconds: z.number().int().optional().describe("How long to wait before returning partial results (default 90, max 240)."), focus: z.string().optional().describe("An extra question to answer from the reviews.") }).strict(),
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
name: "amazon_scan_status",
|
|
425
|
+
title: "Amazon Scan Status",
|
|
426
|
+
description: "Pick up a scan_amazon_category collection that was still running, by scanId \u2014 the listings and reviews collected since, with the same rollup and the same instructions for reading them. Free: it is the poll scan_amazon_category asked for, and charging for the second half of one answer would bill a wait this server chose. Call it when a scan came back incomplete, then write your read from the full set.",
|
|
427
|
+
inputSchema: z.object({ scanId: z.string().describe("The scanId scan_amazon_category returned."), waitSeconds: z.number().int().optional().describe("How long to wait for more listings before answering (default 30, max 240)."), focus: z.string().optional().describe("Same optional steer as scan_amazon_category.") }).strict(),
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
name: "get_amazon_product",
|
|
431
|
+
title: "Get Amazon Product",
|
|
432
|
+
description: "Fetch one Amazon listing by ASIN or URL: price, rating, the full star histogram, features, specs, Amazon's review digest and aspect breakdown, and the review text. Use for a single product; for a category or a competitive set, scan_amazon_category collects many in one job and rolls them up. Consumes 3 nooticr credits.",
|
|
433
|
+
inputSchema: z.object({ asin: z.string().optional().describe("ASIN, or a full Amazon product URL."), url: z.string().optional().describe("Full Amazon product URL, if you have that instead."), reviews: z.boolean().optional().describe("Collect review text too (default true)."), domain: z.string().optional().describe("Marketplace host (default www.amazon.com).") }).strict(),
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
name: "show_amazon_category_insights",
|
|
437
|
+
title: "Show Amazon Category Insights",
|
|
438
|
+
description: "Display the category read you produced from scan_amazon_category: purchase drivers, barriers, what each brand does well, the gaps, and the positioning angles \u2014 drawn beside the listings and their reviews, so a person can click a product and check any claim against the text it came from. Free, and makes no requests: it only draws what you pass it, attributed to you rather than presented as a nooticr rating of anyone's product. Call this after you have read the reviews, not instead of reading them.",
|
|
439
|
+
inputSchema: z.object({ category: z.string().describe("The category this is a read of."), summary: z.string().optional(), drivers: z.array(z.any()).optional().describe("What makes someone buy, each with the review evidence behind it."), barriers: z.array(z.any()).optional().describe("What stops them buying, or makes them return it."), strengths: z.array(z.any()).optional().describe("What each incumbent does well."), gaps: z.array(z.any()).optional().describe("What buyers keep asking for that nobody serves."), positioning: z.array(z.any()).optional().describe("Angles a new entrant could take."), products: z.array(z.any()).optional().describe("The listings from the scan, passed straight through."), rollup: z.record(z.unknown()).optional().describe("The rollup from the scan, passed straight through."), scanId: z.string().optional() }).passthrough(),
|
|
440
|
+
},
|
|
417
441
|
];
|
package/dist/shared/tools.js
CHANGED
|
@@ -25,8 +25,9 @@ import { registerConnectionTools } from "./connections.js";
|
|
|
25
25
|
import { loadingPlansJson } from "./loading-plans.js";
|
|
26
26
|
import { registerHandoff } from "./handoff.js";
|
|
27
27
|
import { registerCollabTools } from "./collab.js";
|
|
28
|
+
import { registerAmazonTools } from "./amazon.js";
|
|
28
29
|
/** Current MCP server version — bumped on every deploy for traceability. */
|
|
29
|
-
export const MCP_SERVER_VERSION = "1.26.
|
|
30
|
+
export const MCP_SERVER_VERSION = "1.26.34";
|
|
30
31
|
/** MCP Apps extension identifier */
|
|
31
32
|
const UI_EXTENSION = "io.modelcontextprotocol/ui";
|
|
32
33
|
/** MIME type for MCP Apps HTML resources */
|
|
@@ -407,9 +408,9 @@ async function runEvidence(tool, args, client) {
|
|
|
407
408
|
//
|
|
408
409
|
// Measured before changing it, over 59 real runs of the quest suite: 0
|
|
409
410
|
// guidance phrases across 175 tool results. `analyze_post` handed back frames,
|
|
410
|
-
// a transcript and 2,000 characters ending in "call
|
|
411
|
+
// a transcript and 2,000 characters ending in "call show_post_analysis when you are
|
|
411
412
|
// done"; the model wrote the analysis and delivered it in chat, and
|
|
412
|
-
//
|
|
413
|
+
// show_post_analysis was called in 1 run of 12.
|
|
413
414
|
//
|
|
414
415
|
// The text block stays for hosts that honour it (and for the images, which
|
|
415
416
|
// must sit in `content`). `guidance` on the payload is what reaches the rest.
|
|
@@ -561,8 +562,8 @@ export function createMcpServer(rawMakeClient, opts) {
|
|
|
561
562
|
// Close the loop the evidence-only tools open: your own analysis/hooks/
|
|
562
563
|
// variants/repurposing/comparison, drawn — same shape as
|
|
563
564
|
// show_comment_review, free and no requests.
|
|
564
|
-
"
|
|
565
|
-
"
|
|
565
|
+
"show_compared_posts",
|
|
566
|
+
"show_post_analysis",
|
|
566
567
|
"show_hooks",
|
|
567
568
|
"show_variants",
|
|
568
569
|
"show_repurposed_post",
|
|
@@ -627,6 +628,12 @@ export function createMcpServer(rawMakeClient, opts) {
|
|
|
627
628
|
// grant each connection carries is a table, which is what a view is for.
|
|
628
629
|
"list_social_connections",
|
|
629
630
|
"connect_social_account",
|
|
631
|
+
// Marketplace. All four draw: the three collection tools render the
|
|
632
|
+
// listings and their reviews, and the view draws the read on top of them.
|
|
633
|
+
"scan_amazon_category",
|
|
634
|
+
"amazon_scan_status",
|
|
635
|
+
"get_amazon_product",
|
|
636
|
+
"show_amazon_category_insights",
|
|
630
637
|
];
|
|
631
638
|
// Human-readable resource name per tool (used in resources/list + tools/list).
|
|
632
639
|
function resourceName(tool) {
|
|
@@ -706,6 +713,12 @@ export function createMcpServer(rawMakeClient, opts) {
|
|
|
706
713
|
// LinkedIn
|
|
707
714
|
"https://*.licdn.com",
|
|
708
715
|
"https://*.linkedin.com",
|
|
716
|
+
// Amazon product images. Listings serve from m.media-amazon.com and, on
|
|
717
|
+
// older listings, images-na.ssl-images-amazon.com — a listing whose picture
|
|
718
|
+
// is blocked draws as a brand initial, which is a worse comparison screen
|
|
719
|
+
// than the one the scan paid for.
|
|
720
|
+
"https://*.media-amazon.com",
|
|
721
|
+
"https://*.ssl-images-amazon.com",
|
|
709
722
|
];
|
|
710
723
|
const apiUrl = process.env.NOOTICR_API_URL || process.env.NOOTICR_BASE_URL;
|
|
711
724
|
if (apiUrl && apiUrl.trim()) {
|
|
@@ -1426,8 +1439,8 @@ export function createMcpServer(rawMakeClient, opts) {
|
|
|
1426
1439
|
// the plain post card it started on. Same shape as show_comment_review in
|
|
1427
1440
|
// every way that matters: free, no requests, draws only what it is
|
|
1428
1441
|
// handed. Each one's structuredContent is built to match an existing view
|
|
1429
|
-
// ui-template.ts already renders (
|
|
1430
|
-
// scoreboard,
|
|
1442
|
+
// ui-template.ts already renders (show_compared_posts → the comparison
|
|
1443
|
+
// scoreboard, show_post_analysis → analysisCard) or a new one added alongside
|
|
1431
1444
|
// it (show_hooks, show_variants, show_repurposed_post).
|
|
1432
1445
|
server.registerTool("show_trend", {
|
|
1433
1446
|
title: "Show Trend",
|
|
@@ -1547,7 +1560,7 @@ export function createMcpServer(rawMakeClient, opts) {
|
|
|
1547
1560
|
},
|
|
1548
1561
|
};
|
|
1549
1562
|
});
|
|
1550
|
-
server.registerTool("
|
|
1563
|
+
server.registerTool("show_compared_posts", {
|
|
1551
1564
|
title: "Show Comparison",
|
|
1552
1565
|
description: "Display a comparison you wrote after compare_posts fetched the first post and you fetched " +
|
|
1553
1566
|
"the rest yourself (get_social_media, 1 credit each). Free, and makes no requests — it only " +
|
|
@@ -1555,12 +1568,12 @@ export function createMcpServer(rawMakeClient, opts) {
|
|
|
1555
1568
|
"strengths and the next experiment worth running. Call this after you have done the " +
|
|
1556
1569
|
"comparing, not instead of it.",
|
|
1557
1570
|
_meta: {
|
|
1558
|
-
ui: { resourceUri: uiResource("
|
|
1559
|
-
"ui/resourceUri": uiResource("
|
|
1560
|
-
"openai/outputTemplate": appsSdkResource("
|
|
1571
|
+
ui: { resourceUri: uiResource("show_compared_posts") },
|
|
1572
|
+
"ui/resourceUri": uiResource("show_compared_posts"),
|
|
1573
|
+
"openai/outputTemplate": appsSdkResource("show_compared_posts"),
|
|
1561
1574
|
},
|
|
1562
1575
|
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
1563
|
-
outputSchema: OUTPUT_SCHEMAS.
|
|
1576
|
+
outputSchema: OUTPUT_SCHEMAS.show_compared_posts,
|
|
1564
1577
|
inputSchema: z
|
|
1565
1578
|
.object({
|
|
1566
1579
|
posts: z
|
|
@@ -1600,7 +1613,7 @@ export function createMcpServer(rawMakeClient, opts) {
|
|
|
1600
1613
|
},
|
|
1601
1614
|
};
|
|
1602
1615
|
});
|
|
1603
|
-
server.registerTool("
|
|
1616
|
+
server.registerTool("show_post_analysis", {
|
|
1604
1617
|
title: "Show Analysis",
|
|
1605
1618
|
description: "Display an analysis you wrote after analyze_post, analyze_post_fast or understand_social_post " +
|
|
1606
1619
|
"handed you the material. Free, and makes no requests — it only draws what you pass it: hook " +
|
|
@@ -1608,12 +1621,12 @@ export function createMcpServer(rawMakeClient, opts) {
|
|
|
1608
1621
|
"more, whichever of these you actually produced. Call this after you have done the analysing, " +
|
|
1609
1622
|
"not instead of it.",
|
|
1610
1623
|
_meta: {
|
|
1611
|
-
ui: { resourceUri: uiResource("
|
|
1612
|
-
"ui/resourceUri": uiResource("
|
|
1613
|
-
"openai/outputTemplate": appsSdkResource("
|
|
1624
|
+
ui: { resourceUri: uiResource("show_post_analysis") },
|
|
1625
|
+
"ui/resourceUri": uiResource("show_post_analysis"),
|
|
1626
|
+
"openai/outputTemplate": appsSdkResource("show_post_analysis"),
|
|
1614
1627
|
},
|
|
1615
1628
|
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
1616
|
-
outputSchema: OUTPUT_SCHEMAS.
|
|
1629
|
+
outputSchema: OUTPUT_SCHEMAS.show_post_analysis,
|
|
1617
1630
|
inputSchema: z
|
|
1618
1631
|
.object({
|
|
1619
1632
|
url: z.string().describe("The post you analyzed."),
|
|
@@ -2351,5 +2364,9 @@ export function createMcpServer(rawMakeClient, opts) {
|
|
|
2351
2364
|
// classified for a tracker on another server, the other draws what it scored.
|
|
2352
2365
|
registerHandoff(server);
|
|
2353
2366
|
registerCollabTools(server);
|
|
2367
|
+
// Marketplace intelligence (amazon.ts): the category collection, the poll
|
|
2368
|
+
// that picks it back up, a single listing, and the free view for the read
|
|
2369
|
+
// the model writes out of them.
|
|
2370
|
+
registerAmazonTools(server, makeClient);
|
|
2354
2371
|
return server;
|
|
2355
2372
|
}
|