← The evidence ledger
Evidence row 293394
Observed , by probe.mcp/1.
- Kind
- mcp.tools
- About
- endpoint 1580af2a-7b9f-419c-922a-54c33d25c3d2
- Recorded
- Previous row
- None: the first of its kind about this subject
- Body SHA-256
- 40a6b0b5b8c83691284328c87feffd676ee12849f8842867f3b1b3ee331f4e0e
- Leaf
- 0x0533ebd7d1329c5aa846750c2be8f4e2d6fca95e1755174300a3bba550d827b0
What was observed
{
"count": 14,
"tools": [
{
"description": "Start tracking a competitor for an extension.\n\nTakes the store and id of a search_catalog row (as external_id), so a\nlisting you just found can be promoted without looking anything up in\nbetween.\n\nPromoting a listing is what earns it a store fetch: its description,\ninstall count, rating and languages start being recorded, and its\nposition is kept even on days it falls out of the results entirely.\nWhere it ranks has been recorded all along, from the searches this\naccount already runs — so a competitor added today arrives with\nhistory rather than starting from zero.\n\nThe description is not available when this returns. Call\nlist_competitors with the same extension_id until this competitor's\nstatus turns from \"pending\" to \"scraped\".\n\nAt most 5 competitors per store on the Free\nplan and 20 on Premium, because each costs a\nfetch of that store on every refresh. The two stores are counted separately.\n\nNeeds an ExtensionDash account: sign in from your app, e.g. `claude mcp login extensions-dashboard` or `codex mcp login extensions-dashboard`.\n",
"inputSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"properties": {
"extension_id": {
"description": "From list_extensions.",
"type": "integer"
},
"external_id": {
"description": "Chrome's 32-letter id or the AMO slug, as a search_catalog row's id gives it.",
"type": "string"
},
"store": {
"enum": [
"chrome",
"firefox"
],
"type": "string"
}
},
"required": [
"extension_id",
"store",
"external_id"
],
"type": "object"
},
"name": "add_competitor"
},
{
"description": "Start tracking a search term for an extension. The term is added once\nper store the extension is listed in, and a rank fetch is enqueued for\neach store that was not already tracking it.\n\nThe position is not available when this returns. Call list_keywords with\nthe same extension_id until this term's status turns from \"pending\" to\n\"scanned\", or list_scrape_runs to watch the fetch itself — a fetch that\nfails shows up there, rather than as a position that never arrives.\n\nA term is tracked per store language. Tracking \"ad blocker\" in en and\nagain in de gives two independent keywords with their own positions.\nCall list_keywords for the languages an extension already tracks.\n\nAn extension tracks at most 10 keywords on the\nFree plan and 50 on Premium, counted per\nlanguage: \"ad blocker\" in en and de is two. Past that this returns a limit\nerror and remove_keyword has to free a slot first.\n\nNeeds an ExtensionDash account: sign in from your app, e.g. `claude mcp login extensions-dashboard` or `codex mcp login extensions-dashboard`.\n",
"inputSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"properties": {
"extension_id": {
"description": "From list_extensions.",
"type": "integer"
},
"locale": {
"description": "Store language to search in: \"en\", \"de\", \"pt-BR\". Defaults to \"en\". The same term ranks differently in each, so this is a separate keyword, not a translation. Canonicalised before use, so \"DE\" and \"de\" are one keyword and \"pt-br\" comes back as \"pt-BR\".",
"type": "string"
},
"term": {
"description": "The search term, e.g. \"json viewer\". Squished and lowercased before use.",
"type": "string"
}
},
"required": [
"extension_id",
"term"
],
"type": "object"
},
"name": "add_keyword"
},
{
"description": "One extension in full: every store listing's latest numbers plus its\ntracked keywords and their current positions.\n\nEach listing's rank is its position by users within its store, computed\nfrom the current user count; ties share a rank (rank_tied = others at the\nsame count); rank_change is the move since last week's recorded rank.\n\nlocales lists every store language this extension tracks, with a\nkeyword count each, so the keywords can be worked a language at a time.\n\nUse get_metrics_history or get_keyword_ranks for movement over time.\n\nThis is the tracked view, built from what we have recorded. For an\nextension this account does not track, use get_listing instead.\n\nNeeds an ExtensionDash account: sign in from your app, e.g. `claude mcp login extensions-dashboard` or `codex mcp login extensions-dashboard`.\n",
"inputSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"properties": {
"extension_id": {
"description": "From list_extensions.",
"type": "integer"
}
},
"required": [
"extension_id"
],
"type": "object"
},
"name": "get_extension"
},
{
"description": "Daily search position history for an extension's tracked keywords, one\nseries per term per store.\n\nEvery point carries the depth it was scanned to, because a position only\nmeans something against the size of the pool it came from: #1 of 9 and\n#1 of 50 are different results. A null position means we searched that\ndeep and this extension was not in the results.\n\nA term tracked in more than one store language comes back as one series\nper language — the same term is a different SERP in each.\n\nEach series has a source. \"store\" is the store's own search. \"google\" is\nwhere the listing page ranks in Google: organic results only (ads and\nother blocks are not counted), top 50, desktop, in the keyword\nlanguage's country (English: the United States). A null Google position\nmeans Google was searched 50 deep and the listing was not there.\n\nNeeds an ExtensionDash account: sign in from your app, e.g. `claude mcp login extensions-dashboard` or `codex mcp login extensions-dashboard`.\n",
"inputSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"properties": {
"days": {
"description": "How far back to look. Defaults to 30.",
"maximum": 365,
"minimum": 1,
"type": "integer"
},
"extension_id": {
"description": "From list_extensions.",
"type": "integer"
},
"locale": {
"description": "Store language, e.g. \"de\". Omit for every language. Canonicalised before use, so \"DE\" and \"de\" are one keyword and \"pt-br\" comes back as \"pt-BR\".",
"type": "string"
},
"source": {
"description": "Store search or Google. Omit for both.",
"enum": [
"store",
"google"
],
"type": "string"
},
"store": {
"enum": [
"chrome",
"firefox"
],
"type": "string"
},
"term": {
"description": "One term. Omit for all of them.",
"type": "string"
}
},
"required": [
"extension_id"
],
"type": "object"
},
"name": "get_keyword_ranks"
},
{
"description": "The search page for one of an extension's tracked keywords, as this\naccount last recorded it — who was on it, in what order, and which way\neach of them moved over the past week.\n\nRead from recorded history, not from the store: no fetch, no waiting,\nno egress spent. Use this when you want movement, or the same page the\ndashboard is showing.\n\nEvery row carries mine and competitor flags, so \"who is above me and\nwhich of them am I already tracking\" is one call. Positions always\ntravel with depth_scanned, because #1 of 9 and #1 of 50 are different\nresults.\n\nNeeds an ExtensionDash account: sign in from your app, e.g. `claude mcp login extensions-dashboard` or `codex mcp login extensions-dashboard`.\n",
"inputSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"properties": {
"extension_id": {
"description": "From list_extensions.",
"type": "integer"
},
"locale": {
"description": "Store language. Defaults to \"en\".",
"type": "string"
},
"store": {
"enum": [
"chrome",
"firefox"
],
"type": "string"
},
"term": {
"description": "A term this extension tracks. From list_keywords.",
"type": "string"
},
"window": {
"description": "Return only the leaders and our own neighbourhood rather than the whole page. Defaults to false.",
"type": "boolean"
}
},
"required": [
"extension_id",
"store",
"term"
],
"type": "object"
},
"name": "get_keyword_serp"
},
{
"description": "One extension's full listing from the catalog -- any extension, tracked by this account or\nnot: description, users, rating, reviews, version, languages, categories, permissions and\npublisher. Pass the store and id of a search_catalog row as store and external_id.\nrank is the position by users within the store; ties share a rank (rank_tied = others at the same\ncount); recomputed weekly on rank_on; rank_change is the move since the week before, positive = up.\n\nEnglish copy, refreshed weekly from the store; refreshed_at says when. This never fetches,\nnever answers \"pending\" and never starts tracking. users, rating and reviews are null when\nthe store shows no number, which is not zero. For an extension this account tracks,\nget_metrics_history has its numbers over time. Shares search_catalog's\n60 calls a minute per account.\n\nWorks without an account too.\n",
"inputSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"properties": {
"external_id": {
"description": "Chrome's 32-letter id or the AMO slug, as a search_catalog row's id gives it.",
"maxLength": 100,
"type": "string"
},
"store": {
"enum": [
"chrome",
"firefox"
],
"type": "string"
}
},
"required": [
"store",
"external_id"
],
"type": "object"
},
"name": "get_listing"
},
{
"description": "Daily-or-sparser history of an extension's numbers: user count,\nrating and ratings count, each as a map of ISO date to value.\n\"listings\" is this extension's own store listings; \"competitors\" is\nits roster from list_competitors. store and days filter both.\n\nA map holds only dates actually observed; an empty map means no\nmeasurement in range, never zero. If users_rounded_in_range is true\nthe store published rounded counts somewhere in the window, so small\nmovements there may be rounding.\n\nCompetitors are captured every few days rather than daily, and not on\na fixed schedule, so compare a rival against \"listings\" on dates\npresent in both, never by position in the map. Join a competitor\nentry back to list_competitors on (store, external_id); name is a\nlabel, not an identifier.\n\ninclude_competitors adds every roster competitor's history.\ncompetitors narrows that to specific (store, external_id) pairs and\nimplies include_competitors; a pair not on the roster fails the whole\ncall rather than silently dropping it.\n\nNeeds an ExtensionDash account: sign in from your app, e.g. `claude mcp login extensions-dashboard` or `codex mcp login extensions-dashboard`.\n",
"inputSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"properties": {
"competitors": {
"description": "Limit competitor history to these (store, external_id) pairs from list_competitors. Implies include_competitors; an unlisted pair fails the call.",
"items": {
"additionalProperties": false,
"properties": {
"external_id": {
"type": "string"
},
"store": {
"enum": [
"chrome",
"firefox"
],
"type": "string"
}
},
"required": [
"store",
"external_id"
],
"type": "object"
},
"type": "array"
},
"days": {
"description": "How far back to look. Defaults to 30.",
"maximum": 365,
"minimum": 1,
"type": "integer"
},
"extension_id": {
"description": "From list_extensions.",
"type": "integer"
},
"include_competitors": {
"description": "Also return history for this extension's promoted competitors. Defaults to false.",
"type": "boolean"
},
"store": {
"description": "Filters both listings and competitors. Omit for every store.",
"enum": [
"chrome",
"firefox"
],
"type": "string"
}
},
"required": [
"extension_id"
],
"type": "object"
},
"name": "get_metrics_history"
},
{
"description": "Who this extension competes with, measured across every keyword it\ntracks.\n\nTwo sets. \"competitors\" is the roster you promoted with\nadd_competitor: their descriptions, install counts and ratings are\nrecorded, and their positions are kept even on days they fall out of\nthe results. \"candidates\" — returned only with include_observed —\nis everyone else who has appeared in those searches, ranked by how\noften they beat you.\n\nBoth are read from positions this account already recorded, not from\nthe store: no fetch, no waiting, and each row carries the evidence\nbehind it (how many of your terms, average position, first seen). This\nis the tool for deciding who is worth promoting.\n\nNeeds an ExtensionDash account: sign in from your app, e.g. `claude mcp login extensions-dashboard` or `codex mcp login extensions-dashboard`.\n",
"inputSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"properties": {
"extension_id": {
"description": "From list_extensions.",
"type": "integer"
},
"include_observed": {
"description": "Also return unpromoted candidates. Defaults to false.",
"type": "boolean"
}
},
"required": [
"extension_id"
],
"type": "object"
},
"name": "list_competitors"
},
{
"description": "Every extension this token's owner tracks, with each store listing's\nlatest numbers: user count (and whether the store rounded it), the\n7-day change, rating, ratings count and category rank.\n\nStart here when this connection has an account: every tracking tool\ntakes an extension_id from this list.\n\nNeeds an ExtensionDash account: sign in from your app, e.g. `claude mcp login extensions-dashboard` or `codex mcp login extensions-dashboard`.\n",
"inputSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"properties": {},
"type": "object"
},
"name": "list_extensions"
},
{
"description": "Every search term tracked for one extension, one row per term per store.\n\nstatus is \"pending\" until a fetch has landed since the term was tracked,\nand \"scanned\" once one has. A \"scanned\" row with a null position means\nwe searched depth_scanned results and this extension was not among\nthem — that is a real observation, not missing data.\n\nA \"pending\" row can still carry a position. Removing a keyword stops the\ntracking but keeps the history, so a term tracked before and added again\narrives with its previous result already in place; captured_on says how\nold that is. Poll until the status reads \"scanned\" to know the number\nanswers the current tracking.\n\nlocale is the store language the term was searched in. The same term\nranks differently under hl=de than under hl=en, so two rows sharing a\nterm and a store are two different results.\n\nlocales lists every store language this extension tracks, with a\nkeyword count each — start there to work a language at a time. It\nalways reports every language, including when the locale argument\nnarrows the rows to one.\n\nsearch_volume is Google Ads' average monthly searches for the term in\nthe row's store language: for English, in the United States or\nworldwide as the account's profile setting says (United States unless\nchanged), and in one country for the others (Germany for de, Spain for\nes, Brazil for pt-BR, and so on).\nIt is web search demand, not searches inside the store, and is the same\nfor both stores. null means not fetched yet, Google has no data, or the\nterm can't be looked up (symbols such as + or %, over 80 characters or\n10 words, or a store language Google Ads has no country for, such as\nru); search_volume_fetched_at says which.\n\nNeeds an ExtensionDash account: sign in from your app, e.g. `claude mcp login extensions-dashboard` or `codex mcp login extensions-dashboard`.\n",
"inputSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"properties": {
"extension_id": {
"description": "From list_extensions.",
"type": "integer"
},
"locale": {
"description": "Store language, e.g. \"de\". Omit for every language. Canonicalised before use, so \"DE\" and \"de\" are one keyword and \"pt-br\" comes back as \"pt-BR\".",
"type": "string"
}
},
"required": [
"extension_id"
],
"type": "object"
},
"name": "list_keywords"
},
{
"description": "Recent scrape attempts against your own extensions, newest first, with\ntheir outcome and any error. Covers both the listing fetches and the\nrank fetches for the search terms they track.\n\nUse this after add_keyword to watch the rank fetch: the run is targeted\nat the search term, and a failed fetch appears here as a failure rather\nthan as a position that never arrives.\n\nNeeds an ExtensionDash account: sign in from your app, e.g. `claude mcp login extensions-dashboard` or `codex mcp login extensions-dashboard`.\n",
"inputSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"properties": {
"extension_id": {
"description": "Narrow to one extension's listings and tracked keywords.",
"type": "integer"
},
"limit": {
"description": "Defaults to 20.",
"maximum": 100,
"minimum": 1,
"type": "integer"
},
"status": {
"enum": [
"running",
"success",
"failure"
],
"type": "string"
}
},
"type": "object"
},
"name": "list_scrape_runs"
},
{
"description": "Stop tracking a competitor for an extension.\n\nStops the store fetches. The recorded history is kept, so adding the\nsame competitor again later restores its charts rather than starting\nfrom zero — and its positions carry on being observed either way,\nbecause they come from searches this account already runs.\n\nNeeds an ExtensionDash account: sign in from your app, e.g. `claude mcp login extensions-dashboard` or `codex mcp login extensions-dashboard`.\n",
"inputSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"properties": {
"extension_id": {
"description": "From list_extensions.",
"type": "integer"
},
"external_id": {
"description": "From list_competitors.",
"type": "string"
},
"store": {
"enum": [
"chrome",
"firefox"
],
"type": "string"
}
},
"required": [
"extension_id",
"store",
"external_id"
],
"type": "object"
},
"name": "remove_competitor"
},
{
"description": "Stop tracking a search term for an extension, across every store it was\ntracked in. Positions already recorded are kept; only the tracking stops.\n\nOther extensions tracking the same term are unaffected, including ones\nthat track the same store listing: every extension's keywords are its own.\n\nA term is tracked per store language. Tracking \"ad blocker\" in en and\nagain in de gives two independent keywords with their own positions.\nCall list_keywords for the languages an extension already tracks.\n\nNeeds an ExtensionDash account: sign in from your app, e.g. `claude mcp login extensions-dashboard` or `codex mcp login extensions-dashboard`.\n",
"inputSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"properties": {
"extension_id": {
"description": "From list_extensions.",
"type": "integer"
},
"locale": {
"description": "Store language to search in: \"en\", \"de\", \"pt-BR\". Defaults to \"en\". The same term ranks differently in each, so this is a separate keyword, not a translation. Canonicalised before use, so \"DE\" and \"de\" are one keyword and \"pt-br\" comes back as \"pt-BR\".",
"type": "string"
},
"term": {
"description": "As reported by list_keywords.",
"type": "string"
}
},
"required": [
"extension_id",
"term"
],
"type": "object"
},
"name": "remove_keyword"
},
{
"description": "Search one store's catalog of extensions fetched so far, for market and niche research:\nstore \"chrome\" (default) or \"firefox\", one per call -- Chrome users are a rounded weekly\ncount, Firefox's an exact daily average, so the two are never mixed. listed and known in\neach answer show how much of that store the catalog covers.\nrank is the position by users within the store; ties share a rank (rank_tied = others at the same\ncount); recomputed weekly on rank_on; rank_change is the move since the week before, positive = up.\n\nFilter by name words (match \"all\" or \"any\"; case and punctuation are ignored, and an exact\nname comes first unless you sort), by one category of that store (a slug such as\n\"productivity/developer\" or \"privacy-security\"; an unknown one is refused with the store's\nlist) and by inclusive ranges on users, rating, reviews, language count and description\nlength; sort by any of those or by last update. This never fetches the store. users,\nrating and reviews are null when the store shows no number, which is not zero. Each row\nsays whether it is one of yours or a competitor you track; pass its store and id to\nadd_competitor to start tracking it. For one extension's full listing, call get_listing.\n\nAllows 60 calls a minute per account.\n\nWorks without an account too.\n",
"inputSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"properties": {
"category": {
"description": "One category slug of the chosen store, as rows return it.",
"maxLength": 64,
"type": "string"
},
"description_length_max": {
"maximum": 2147483647,
"minimum": 0,
"type": "integer"
},
"description_length_min": {
"maximum": 2147483647,
"minimum": 0,
"type": "integer"
},
"dir": {
"description": "Defaults to desc. Nulls always last.",
"enum": [
"desc",
"asc"
],
"type": "string"
},
"langs_max": {
"maximum": 2147483647,
"minimum": 0,
"type": "integer"
},
"langs_min": {
"maximum": 2147483647,
"minimum": 0,
"type": "integer"
},
"limit": {
"description": "Defaults to 25.",
"maximum": 50,
"minimum": 1,
"type": "integer"
},
"match": {
"description": "Defaults to all.",
"enum": [
"all",
"any"
],
"type": "string"
},
"page": {
"description": "Defaults to 1.",
"maximum": 10000,
"minimum": 1,
"type": "integer"
},
"query": {
"description": "Name words, at most 8 words.",
"maxLength": 100,
"type": "string"
},
"rating_max": {
"maximum": 5,
"minimum": 0,
"type": "number"
},
"rating_min": {
"maximum": 5,
"minimum": 0,
"type": "number"
},
"reviews_max": {
"maximum": 2147483647,
"minimum": 0,
"type": "integer"
},
"reviews_min": {
"maximum": 2147483647,
"minimum": 0,
"type": "integer"
},
"screenshots_max": {
"maximum": 2147483647,
"minimum": 0,
"type": "integer"
},
"screenshots_min": {
"maximum": 2147483647,
"minimum": 0,
"type": "integer"
},
"sort": {
"description": "Defaults to users, with an exact name match first when query is given.",
"enum": [
"users",
"rating",
"reviews",
"langs",
"description_length",
"screenshots",
"updated",
"added"
],
"type": "string"
},
"store": {
"description": "Defaults to chrome.",
"enum": [
"chrome",
"firefox"
],
"type": "string"
},
"users_max": {
"maximum": 2147483647,
"minimum": 0,
"type": "integer"
},
"users_min": {
"maximum": 2147483647,
"minimum": 0,
"type": "integer"
}
},
"type": "object"
},
"name": "search_catalog"
}
]
}What the leaf hashes
{"body":"40a6b0b5b8c83691284328c87feffd676ee12849f8842867f3b1b3ee331f4e0e","id":293394,"kind":"mcp.tools","method":"probe.mcp/1","observed_us":1793049362060396,"prev":null,"subject":"1580af2a-7b9f-419c-922a-54c33d25c3d2","subject_kind":"endpoint"}Proof
Fold these into the leaf in order, hashing each sorted pair with keccak256, to reach the root of (296695 rows).
- 0x0533ef1912a0e7375ae698f2985d50047ce3a38d0c74b14b1545e399e1650bb5
- 0x2d01d5990f5562706776832a3f1accf8aed5a27c19bdf57e410f6d74e2653f98
- 0xdd6a90797486f44bcefac4b610654593b052734b65ac69ed27aa3a2b5c3612a8
- 0x751e078c625d519f7e5c12628b1b9bceef217dc17712b73bc0951d2612a0b416
- 0x055030c204b48c066866de9feb73be8f4ad0725c8d04d236bd280063c260db1c
- 0xd2060386927dc9852d1bc815155ebc525739f42ec822a2cc21a5fef79304d5c4
- 0x0fdb96a5a2cc02fa0148f93ceb65965b4ef6509acdfb2ee432b2d50b57dca8da
- 0x38b66d9aefe3456fcfb32efdbcdbc033be177dc67886ffa59ee947a9c5ba8abd
- 0x38552143162cfe72f31e41342d68bf6d0db53dbe3dcc62b5beafa04325bfdc5b
- 0xa3fd99b4d14aebe36a36910f38fbd23ee9ab7b5d169eb4cf8da16b4084155357
- 0xbc986b35695070cad9d398880439ec872b3a6634ffe7036fde0d0dfde7eabfda
- 0x23033d81d9f4be06225e4276d276e0e8e02abc173d156a1b9a8fd5e4803ad199
- 0x610fdf3e989af0a6b4a400a3a8aab9156d757f8c96efe4e9d0594dfd04de1cc0
- 0xd1d3499072edb10edc68f84bcae2365d2ab534df689250cab18c751b6f13b6ff
- 0x7ddc2fead128357f881b63e6c7c26d7fbbb84fca6c7c8e0bbfe386de86ca8d39
- 0xc6a4d7899a0f442958e26505011190b7cff2483611c1b94eba9043e0ba9ac36e
- 0x8c756542e482157df0c36464d75c937b845e4423dbbcfad08f0cf8d5a16b9a13
- 0x20704208aa6923d6b2450ef74ede87a6e0265d46447926038fe9506e9e154b2d
- 0x9b9ce5b74b9bab5121612ef6b64920a8d453cbf311096a423eef9702747e868a
- Day root
- 0xf43e8dcc3d451a4788b26be904c55e8c4c0fa75c9666796c51db38d37e2c0df8
- Chained
- 0x702d5ac508f08b0d1accf36af25822d8e5660367494edd17a642fb61e158a57a
- Published
- On Arc Testnet, block 69251483: 0x495162f8936e19f0039918feebe6c307f327cd6bf8c37219620b1970041fa9ae