Skip to main content
Glama

Analytics Legends — SAP Analytics Intelligence

Server Details

AI agent for SAP analytics: firms, day rates, contract radar, news, concepts, studies

If you are the author of this connector, you can claim ownership with GitHub, an HTTP challenge, or a DNS record. Claimed connector authors can inspect health checks, view analytics, and manage their listing.
Status
Healthy
Last Tested
Transport
Streamable HTTP
URL
Repository
analyticslegends/analytics-legends-mcp
GitHub Stars
0
Server Listing
ai.analyticslegends/sap-analytics

Available Tools

20 tools
count_firms_byCount the firm directory by country, kind, module or SAP signalA
Read-onlyIdempotent
Inspect

Answer a COUNTING question about the published firm directory in one call: how many organisations per country, per kind, per declared SAP module, or per SAP signal band — with the same country/kind/module/query filters search_firms takes, so you can count a slice as easily as the whole. Use this instead of paging search_firms and tallying rows: the directory holds thousands of organisations, and reading them all to produce a table of counts costs hundreds of calls and megabytes of rows for numbers Postgres computes in one scan. Every bucket is a value the directory actually stores; value: null is a real bucket meaning the field is unknown for those rows, and it is served rather than hidden — a country table that silently drops the rows with no country adds up to less than the population and says nothing about it.

ParametersJSON Schema
NameRequiredDescriptionDefault
byYesWhich facet to count on. Required — there is no default worth guessing.
kindNoRestrict to one organisation kind before counting, same vocabulary as search_firms. Combining it with `by:"kind"` is legal and returns that single bucket.
queryNoFree-text filter, case-insensitive. EVERY word must appear in the record (substring per word, any order), so a natural-language phrase narrows the answer instead of having to match verbatim.
moduleNoRestrict to organisations with a DECLARED link to one SAP module code before counting, same vocabulary as search_firms' `module`. Combining it with `by:"module"` is legal and returns that single bucket (plus the null bucket).
countryNoISO-3166-1 alpha-2 country code, e.g. DE, FR, CH.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes
toolYes
_metaNo
_attributionYes
result_countYes

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already provide readOnlyHint, idempotentHint, and destructiveHint, lowering the burden. The description adds valuable behavior beyond those hints: null buckets are real and served rather than hidden, counts reflect the full population, and filters match search_firms exactly. This prevents an agent from misinterpreting missing facets.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, each earning its place: purpose, usage rationale, and a crucial data-completeness behavior. It is front-loaded with the core action and keeps the details relevant rather than padding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only counting tool with a rich input schema and an output schema present, the description covers the essential context: what it counts, how it relates to search_firms, when to prefer it, and how null buckets behave. Nothing critical is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema already documents each parameter well. The description still adds useful relational context: the filters are the same ones search_firms accepts, and counting a filtered slice is as easy as counting the whole. This helps an agent reuse knowledge of a sibling tool.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Answer a COUNTING question about the published firm directory'. It names the four facets (country, kind, module, SAP signal band) and explicitly contrasts with search_firms, so an agent can tell this tool from its sibling without opening schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives explicit when-to-use guidance: use this instead of paging through search_firms and tallying rows. It even quantifies why, citing hundreds of calls and megabytes of rows, which makes the trade-off concrete and actionable.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

find_academy_modulesSearch the Academy training catalogueA
Read-onlyIdempotent
Inspect

Search the Analytics Legends Academy — the written training modules on SAP Datasphere, Business Data Cloud, SAP Analytics Cloud, BW/4HANA and Databricks — by track, level and free text. _meta.tranche_total_row_count carries the live catalogue size on every call; it is the only count to quote. Returns the catalogue entry: id, slug, EN/FR title, track, level, duration in minutes, tags and the editor's summary. DO NOT CONFUSE IT WITH list_sap_modules, which serves a different population under the same word: that one is the 40-row PRODUCT taxonomy (codes such as SAC, DATASPHERE) used to normalise product wording. This one is the course catalogue. Without query, rows come back in the catalogue's own CURRICULUM order — the order a reader is meant to take them in — track by track. This catalogue is written training, NOT SAP certification tracks: this server publishes no certification data at any tier, so a certification question has no answer here rather than a partial one. CATALOGUE ONLY — the module BODY is subscriber content, served by get_academy_module on this same endpoint with a subscriber key (Consultant tier or above), which is the same door the €29.90 Consultant Pass opens on the site. On THIS endpoint the machine-access subscription is the MCP Pass (€39.90/month, analyticslegends.ai/pricing/), which opens the ENTIRE paid tranche from one key; the €29.90 Consultant Pass is its web-subscriber equivalent and opens the same tier floor here. status and is_preview are SERVED, never filtered on: they are the two flags the platform marks free access with, they do not coincide (measured 2026-08-16: 38 rows status='available', 56 rows is_preview), and you decide which one your answer needs. PAGINATED: pass _meta.next_cursor back as cursor with the same filters until it is null. Read _meta.available_tracks and _meta.available_levels — both counted on the served population at call time — before assuming a facet value exists.

ParametersJSON Schema
NameRequiredDescriptionDefault
levelNoRestrict to one level, matched case-insensitively: Beginner · Intermediate · Advanced · Expert. Counts in `_meta.available_levels`.
limitNoMax rows (hard cap 50).
queryNoFree-text filter, case-insensitive. EVERY word must appear in the record (substring per word, any order), so a natural-language phrase narrows the answer instead of having to match verbatim.
trackNoRestrict to one track, by SLUG (`databricks-data-eng`) or by English name (`Databricks & Data Eng.`), matched case-insensitively. The live vocabulary with per-track counts is `_meta.available_tracks` on every response. The numeric track_id is deliberately NOT accepted — it is an internal counter, and passing `6` would look like naming a subject.
cursorNoOpaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments; `null` means the last page. Changing a filter refuses the cursor.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes
toolYes
_metaNo
_attributionYes
result_countYes

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the readOnly/idempotent annotations, the description discloses default ordering, pagination behavior via _meta.next_cursor, the live count semantics of tranche_total_row_count, that status and is_preview are served but not filterable, and the measured discrepancy between them. This is substantial behavioral context that the annotations alone do not provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is dense and front-loaded with the core search purpose, and the critical disambiguation from list_sap_modules appears early. Some pricing/subscription detail is longer than strictly necessary for invoking this endpoint, but it supports correct routing to get_academy_module.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers filtering, pagination, ordering, live facet counts, the distinction from sibling tools, access limitations, and count semantics. With a rich output schema present, no essential calling context is missing for an agent to use this tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Although schema coverage is 100%, the description adds crucial parameter semantics: query words must all appear as substrings, track accepts slug or English name but deliberately not numeric track_id, and cursor must be passed back with identical filters or it is refused. These details are not inferable from the schema alone.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific verb ('Search'), a resource (the Academy training catalogue), and the available facets (track, level, free text). It explicitly distinguishes it from list_sap_modules, so an agent can select between the two without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit guidance on when to use this tool versus list_sap_modules and get_academy_module, noting the product taxonomy and the subscriber-only module body respectively. It also states clearly that certification questions have no answer here, preventing misuse.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

find_opportunitiesSearch the public SAP analytics contract radarA
Read-onlyIdempotent
Inspect

Search every SAP contract and permanent-role posting Analytics Legends publishes to an ANONYMOUS visitor — the same population a human browses on /opportunities/, where each posting has its own prerendered page. It merges the platform's TWO public legs, which are near-disjoint (measured 2026-07-30: 1 row in common): (a) the PROMOTED feed (public.public_opportunities) — general SAP work (FI/CO, SD, EWM, MDG, BTP, ABAP), all German cities, dated (posted_at is populated on EVERY active row of that leg — an invariant held since 2026-07-31, not a snapshot). 🔴 THIS LEG CHANGED SHAPE ON 2026-08-28: until then it was fed by three keyless APIs and carried no contract_type, country_code, expires_at or rate at all; it was then loaded from the site radar and now declares contract_type and country_code on most of its rows, an expiry on most, and an advertised rate on a small minority. Do NOT assume a field is null on this leg — read the _meta counters on YOUR OWN response, which are computed at query time; (b) the SITE RADAR (/api/contracts-lean.json) — these carry country, category, seniority, posted_at, employment_type and, on most of them, expires_at; they are the analytics-specific ones (SAC Planning, Datasphere Technical Lead, Business Data Cloud). READ employment_type BEFORE CALLING THIS A CONTRACT MARKET: the radar is mostly PERMANENT roles, so an unfiltered page answers a freelance question with salaried jobs unless you filter. The argument of the same name does the filtering, and _meta.tranche_total_row_count on your own response is the live population — read the split from a filtered call, never from a figure quoted in this text. TWO DIFFERENT RATE FIELDS, AND THEY MEAN DIFFERENT THINGS. currency / daily_rate_min / daily_rate_max are the posting's OWN advertised rate and are almost always null — most listings publish no rate at all. rate_band is the platform's editorial benchmark for that posting's (seniority × product × region) cell, present on most rows, and it is what the posting's public page leads with. It is rate_basis: "panel_inferred" — Eursap n=312 plus the Analytics Legends operator panel, permanent rows restated as a TJM equivalent at ~220 billable days a year — NOT a rate this employer offered. Quote it as a band with its basis, kind and source, never as the posting's rate, and never average bands across postings: many rows share one cell. WHAT IS GATED IS A FIELD, NOT A ROW: on most radar rows source_url is null and application_link reads "members_only" — the verified link to the original listing is the paid Consultant-tier deliverable. Everything else about the posting is public, and citation_url is that posting's own page on analyticslegends.ai. Quote it. Report _meta.tranche_row_count as the published public population, never as the size of the market.

ParametersJSON Schema
NameRequiredDescriptionDefault
langNoReading language for the TITLE — 'EN' (default), 'FR' or 'DE'. This is a RENDERING choice, never a filter: it changes which string `title` carries, never which rows come back. Read `title_lang` on every row for the language actually served: it differs from what you asked for exactly when that translation does not exist (FR covers 1,492 of 1,588 site-radar rows, DE 1,373 — measured 2026-09-04), and the verbatim is served instead, labelled with the language the harvest chain measured. `source_lang` always carries the language the ADVERTISER wrote in, translated or not. The promoted leg has no translated columns at all: its rows ignore this argument and say so with `title_lang: null` — see `_meta.untranslated_leg`.
limitNoMax rows (hard cap 50).
queryNoFree-text filter, case-insensitive. EVERY word must appear in the record (substring per word, any order), so a natural-language phrase narrows the answer instead of having to match verbatim.
cursorNoOpaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments; `null` means the last page. Changing a filter refuses the cursor.
countryNoISO-3166-1 alpha-2 code, applied to both legs as a predicate on the row's own country_code. It NO LONGER selects the site-radar leg alone: the promoted feed carried country_code on almost none of its rows until 2026-08-28 and now carries it on most, so a country filter now returns both legs. A row still without one is dropped because it does not match, not because its leg was excluded by assumption. `_meta.match_count_by_leg` shows what each leg contributed on YOUR call — read the split there, never from a figure quoted in this text.
locationNoCity or place, matched case-insensitively as a substring of the posting's location. The promoted leg is all-German (Hamburg, Frankfurt am Main, Bremen, Munich, Cologne, Dortmund, Hanover, Landshut, Mannheim, Stuttgart); the site-radar leg is worldwide.
remote_modeNoRestrict to one work-location policy: `remote`, `hybrid` or `onsite`. READ THIS BEFORE ANSWERING A REMOTE QUESTION: a large share of the radar declares no policy at all (`_meta.remote_mode_undeclared` carries the live count — roughly half the radar when last measured, and a frozen pair written here drifted ~30% in two days), and an undeclared row is NOT an on-site row — it is a posting that does not say. Any value here therefore sets those rows aside rather than classifying them, exactly as the site's own filter does, and `_meta.remote_mode_undeclared` reports how many were set aside. The promoted leg carries its own `remote_mode` column and is filtered by the same predicate. Read `_meta.available_remote_modes` for the live spread before assuming a value exists.
employment_typeNoRestrict to one engagement type. THE RADAR IS MOSTLY PERMANENT, so a freelance or contract question answered off an unfiltered page is answered with salaried jobs. For the actual split, make the filtered call and read `_meta.tranche_total_row_count` — it is counted at query time. The promoted leg declared NO contract_type until 2026-08-28 and now declares one on most of its rows, so a value here no longer drops that leg wholesale — only the rows still silent. THOSE ROWS ARE NOT A FOURTH TYPE AND NOT PERMANENT ONES: `_meta.available_employment_types` counts only what declares, and `_meta.employment_type_undeclared` carries the rest, so the two together are the population and either alone is not. Read both before quoting a mix.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes
toolYes
_metaNo
_attributionYes
result_countYes

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, openWorldHint, and idempotentHint, so the bar is lower — yet the description adds substantial behavioral context: the two near-disjoint legs and the 2026-08-28 shape change, the semantic distinction between advertised rate vs panel-inferred rate_band, the gating of source_url/application_link, and the instruction to read `_meta` counters computed at query time. No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The core purpose is front-loaded, followed by a logical organization (legs, rates, gating, parameter warnings). It is long, but nearly every sentence carries critical operational detail. Some redundancy detracts — 'read the split from a filtered call, never from a figure quoted in this text' recurs, and employment_type/remote_mode warnings are duplicated in the schema param descriptions. Dense and justified, but not maximally tight.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with 8 parameters, two data legs, a temporal schema change, two distinct rate fields, and gated fields, the description is remarkably complete. It covers leg semantics, rate-field meaning, gating, employment-type distribution, remote-mode undeclared handling, and meta-counter guidance. With an output schema present, return-value explanation is unnecessary; nothing an agent needs to call this correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% with already-detailed parameter descriptions, so baseline is 3. The description adds genuine value beyond the schema: the employment_type warning ('radar is mostly PERMANENT'), the remote_mode undeclared-rows-not-onsite caveat, the lang rendering-vs-filter distinction, and the country filter's leg behavior. This is meaningful enrichment over the schema, not mere restatement.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource+scope: 'Search every SAP contract and permanent-role posting Analytics Legends publishes to an ANONYMOUS visitor', clarifying the exact population searched and the /opportunities/ browsing equivalent. Clearly distinguishes itself from the sibling search tools (search_firms, search_concepts, search_news, find_sap_clients), which target different resources entirely.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives strong, explicit usage context: 'READ employment_type BEFORE CALLING THIS A CONTRACT MARKET', instructs to read `_meta.tranche_total_row_count` at query time rather than trusting quoted figures, and tells the agent to make a filtered call before answering freelance/remote questions. Lacks explicit when-not-to-use or named alternative-tool routing, but provides clear operational guidance for correct invocation.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

find_sap_clientsSearch the SAP end-customer corpus (Legend tier)A
Read-onlyIdempotent
Inspect

Search the SAP END-CUSTOMER corpus — the companies that RUN SAP, not the firms that sell services (those are search_firms). This is the paid Legend+ dataset locked away from the public surface on 2026-07-08; it requires a subscriber API key, Legend tier or above. Verification status is SERVED, never silently filtered: sap_client_verification_status and status are columns on every row ('verified' on ~550 of ~21k rows), and you decide what standard of proof your answer needs. product filters on the detected-adoption flags every profile already carries (the uses_* columns get_sap_client_profile serves): it keeps only rows where that product was DETECTED. A row it drops is 'not detected by our detection pass', never 'does not use it' — detection is a positive signal with no negative counterpart.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax rows (hard cap 50).
queryNoFree-text filter, case-insensitive. EVERY word must appear in the record (substring per word, any order), so a natural-language phrase narrows the answer instead of having to match verbatim.
cursorNoOpaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments; `null` means the last page. Changing a filter refuses the cursor.
countryNoISO-3166-1 alpha-2 country code, e.g. DE, FR, CH.
productNoKeep only end-customers where this SAP product was DETECTED in use. Absence from the result means undetected, not unused.
industryNoIndustry or sector filter, matched case-insensitively.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes
toolYes
_metaNo
_attributionYes
result_countYes

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Although annotations already mark readOnlyHint, idempotentHint, and destructiveHint, the description adds key behavior beyond those flags: the dataset is gated behind a subscriber key/Legend tier; verification status is NEVER silently filtered and appears as columns; product filter drops rows meaning 'not detected' rather than 'does not use'; cursor must be passed with the SAME filter arguments and changing filters refuses the cursor. This gives an agent safety-relevant and semantic behavior that annotations alone wouldn't convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is information-dense but every clause earns its place: the corpus distinction, access tier, verification semantics, product detection semantics, and cursor behavior are all core to correct invocation. There is no fluff or repetition of schema content; it is front-loaded with the end-customer vs firm distinction, which is the most important routing signal.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool has an output schema, six optional params, and a sibling set that includes get_sap_client_profile and search_firms, this description fully disambiguates where and how to use it. It covers access requirements, result semantics, filtering semantics, and pagination constraints. Nothing critical an agent needs to know before calling is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents every parameter. The description adds value beyond the schema by explaining the detection semantics of the product enum, the 'never silently filtered' verification columns, and the cursor's same-filter requirement. It does not repeat schema details, so a 4 is appropriate; a 5 would require even more domain-specific parameter nuance beyond what the schema gives.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description explicitly identifies the corpus (SAP end-customers), contrasts it with the sibling tool search_firms (services firms), and highlights the Legend tier requirement. It also says exactly what 'product' filtering means in terms of detection flags, so an agent can distinguish this tool from get_sap_client_profile and search_firms.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The first sentence explicitly contrasts with search_firms: end-customers vs firms that sell services. It also explains the paid subscriber key / Legend tier context, tells the agent that verification status is always served and not silently filtered, and describes detection semantics so the agent can decide when to use this versus more detailed profile tools. This is strong when/when-not guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_academy_moduleRead an Academy module (Consultant tier)A
Read-onlyIdempotent
Inspect

Read one Academy training module in full — body, learning objectives and summary, EN and FR — the written course corpus the €29.90 Consultant Pass sells. On THIS endpoint the machine-access subscription is the MCP Pass (€39.90/month, analyticslegends.ai/pricing/), which opens the ENTIRE paid tranche from one key; the €29.90 Consultant Pass is its web-subscriber equivalent and opens the same tier floor here. Requires a subscriber API key (Authorization: Bearer alk_…), Consultant tier or above; without one this tool refuses and find_academy_modules keeps serving the catalogue. Takes the module id (M001) or its slug (datasphere-foundations), both matched case-insensitively — find_academy_modules returns both on every row, and query_knowledge_graph returns the same ids as module:M001 node ids, so a graph walk now ENDS somewhere. Unlike get_study, the whole module is served in one call: the longest body measured is 17 865 characters, two orders of magnitude under the response ceiling, so sectioning it would cost the caller context without protecting anything.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesModule id (`M001`) or slug (`datasphere-foundations`), verbatim from find_academy_modules.rows[].id / .slug.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes
toolYes
_metaNo
_attributionYes
result_countYes

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already mark it readOnly, idempotent, and non-destructive. The description adds real behavioral context: API-key authentication, refusal without the key, one-call response behavior, and payload size justification. No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core action and every major sentence adds real value. However, it is a long paragraph, and some pricing and response-ceiling detail could be trimmed without losing essential guidance.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter read tool with annotations, an output schema, and a rich description, the definition covers identifier handling, auth requirements, refusal behavior, alternative tools, and response shape. Nothing needed for correct invocation is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema covers the parameter fully, but the description enriches it further by explaining that id accepts both 'M001' and slug values, matching is case-insensitive, and values should come verbatim from find_academy_modules rows. It even ties the id format to query_knowledge_graph node ids.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: 'Read one Academy training module in full', with the body, objectives, languages, and paid-tier context. It clearly distinguishes this from catalogue/search behaviors and from the get_study resource.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says when this tool is usable (subscriber API key, Consultant tier) and what happens otherwise: it refuses and find_academy_modules keeps serving the catalogue. It also contrasts with get_study and ties the id/slug source to find_academy_modules and query_knowledge_graph, giving clear routing guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_conceptConcept metadata and editor's summary (public)A
Read-onlyIdempotent
Inspect

Fetch one concept entry by slug: title, category, level, tags and the editor's summary. Written by a named human editor, not generated. The card body, why-it-matters, key points and pro tip are subscriber content and are NOT returned — follow citation_url for those.

ParametersJSON Schema
NameRequiredDescriptionDefault
slugYesConcept slug from search_concepts.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes
toolYes
_metaNo
_attributionYes
result_countYes

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The annotations already define read-only and idempotent behavior; the description goes further by revealing human-editor provenance ('not generated'), clearly listing the pro tip and business-specific fields that are excluded, and providing a follow-up (citation_url). This shifts the behavioral boundary beyond what annotations encode.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences: purpose and fields, editorial human origin, and a clear exclusion list with a redirect. No fluff; every sentence adds actionable information and the most important scoping info is forced to the front.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with one parameter and an output schema, the description covers what is returned and what isn't, plus the source of slug and an alternative for missing content. The only gap is not referring by name to the obvious sibling 'get_concept_card', which would fully seve the disambiguation for an agent, but the citation_url guidance partially compensates.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema has 100% description coverage for the single 'slug' parameter, stating it comes from search_concepts. The description's mention of 'by slug' is redundant with the schema. It does not add additional format, example examples, or edge-case info, so it's at the expected baseline for full coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb 'fetch', the resource ('one concept entry by slug'), and the expected output fields (title, category, tags, editor's summary). The explicit list of what is NOT returned distinguishes it from the sibling get_concept_card and makes the tool's scope unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explains the intended use case (fetch public metadata and editor summary) and warns against expecting subscriber-only content, directing the caller to citation_url for those. However, it does not name the sibling tool get_concept_card or explicitly state conditions for choosing one over the other, leaving partential ambiguity.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_concept_cardFull concept card (Consultant tier)A
Read-onlyIdempotent
Inspect

The FULL encyclopaedia card for one concept — body, why-it-matters, key points, cheat sheet, glossary, pro tip, and the four analysis tables (decision table, peer comparison, named pitfalls, performance facts), EN and FR — the corpus the €29.90 Consultant Pass sells. On THIS endpoint the machine-access subscription is the MCP Pass (€39.90/month, analyticslegends.ai/pricing/), which opens the ENTIRE paid tranche from one key; the €29.90 Consultant Pass is its web-subscriber equivalent and opens the same tier floor here. Requires a subscriber API key (Authorization: Bearer alk_…), Consultant tier or above; without one this tool refuses and get_concept keeps serving the public metadata. Find slugs with search_concepts.

ParametersJSON Schema
NameRequiredDescriptionDefault
slugYesConcept slug, verbatim from search_concepts.rows[].slug.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes
toolYes
_metaNo
_attributionYes
result_countYes

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover read-only, idempotent, non-destructive behavior; the description adds the key behavioral detail that the tool refuses requests without a paid-tier API key. It also explains the pass/tier model and the fallback behavior of get_concept, which is useful context beyond the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The first sentence is clear and front-loaded, but the description then spends extra sentences on MCP Pass versus Consultant Pass pricing and web-subscriber equivalences. The authentication requirement is necessary, but the commercial pass comparison is over-verbose for an agent-facing tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With one required parameter, full schema coverage, an output schema, and annotations, the description covers all essential agent needs: what it returns, how to authorize, when it refuses, and where to discover slugs. The pricing/pass details are not needed for correctness but introduce minor ambiguity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the only parameter, slug, is fully documented as a verbatim value from search_concepts.rows[].slug. The description repeats the pointer to search_concepts but adds no new parameter-level semantic beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states this tool returns the full encyclopaedia card for one concept, enumerating the card's content blocks and the EN/FR variants, and distinguishes it from get_concept, which serves public metadata only. The scope is unmistakable and clearly differentiated from sibling tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly says to use this tool with a subscriber API key at Consultant tier or above, and that without such a key the tool refuses while get_concept remains available. It also routes the agent to search_concepts for finding valid slugs, giving exact when-to-use and alternative-tool guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_day_rate_benchmarkPublic SAP analytics day-rate aggregateA
Read-onlyIdempotent
Inspect

The PUBLIC day-rate aggregate for SAP analytics freelance work: min/max daily rate by country, specialisation and seniority, each row carrying its own currency, source, source date and confidence. This is the free aggregate published at analyticslegends.ai/api/market-rates.json, and it is SMALL — a few dozen rows at most, every one of them a secondary source (a published market study or a job-board scan), and sample_size is null on most of them. NO COUNT IS WRITTEN HERE ON PURPOSE: _meta.tranche_total_row_count and the rows themselves are the live measure. A frozen pair stood here until 2026-08-27 — '11 rows on 2026-08-09 … sample_size null on 8 of them' — and the second half was WRONG (7 of 11) while the first was still right, which is the whole argument against writing either. NOTHING IS HELD BACK BEHIND IT: there is no paid counterpart to this aggregate. The community-contribution path exists (public.rate_contributions) but publishes nothing yet — v_community_rate_aggregates and v_rate_index are still empty, because a contributed rate only surfaces once a cell holds enough submissions to be reported without identifying anyone. So whatever percentile a source row happens to carry is served here, free, to everyone. The GB row carries a median, p10 and p90, and its own note says its min/max ARE the 25th and 75th percentiles. What is missing from this answer is missing from THIS aggregate; it is not a paid tier. THIS IS NOT THE ONLY RATE THE PLATFORM PUBLISHES, AND ON THE QUESTIONS THIS MARKET ASKS MOST IT IS THE THINNER ONE. find_opportunities returns a rate_band on most live radar postings — a panel-inferred P25–P75 band per (seniority × product × region) cell, Eursap n=312 plus the Analytics Legends operator panel, and it is what each posting's public page leads with. It prices exactly the cells this small aggregate cannot — Senior Datasphere DACH, Senior BDC DACH — where specialisation:"bdc" here returns nothing. When this tool comes back empty for a country × product, say the AGGREGATE holds no row and go read the radar band — do not report that the platform cannot price it. The two are different instruments: this one is a published market study, that one is an editorial benchmark attached to a live posting. Read _meta.available_countries / available_specialisations / available_seniorities — they are computed from the aggregate on every call — before concluding that a rate is unpublished, and quote each row with its own currency, its confidence and its source date.

ParametersJSON Schema
NameRequiredDescriptionDefault
countryNoISO-3166-1 alpha-2 country code, e.g. DE, FR, CH.
seniorityNoe.g. senior, principal.
specialisationNoOne of the codes the aggregate actually holds — analytics_all, bw, bw4hana, bw4hana_sac, sac, datasphere (2026-08-09; the live list comes back as `_meta.available_specialisations` on every call). They are NOT evenly spread across countries: DE holds analytics_all only, at three seniorities, and datasphere exists for FR alone, as a median with confidence 'low'. There is no bdc row and no joule row in THIS aggregate — but the platform does price BDC: `find_opportunities` carries a panel-inferred band on the live postings, €1,000–€1,400/day P25–P75 for Senior BDC on 52 German postings (2026-08-10). A code this aggregate does not hold returns 0 rows and the available codes; it never widens to a neighbouring band, and 0 here does not mean the platform is silent.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes
toolYes
_metaNo
_attributionYes
result_countYes

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description goes far beyond the annotations (readOnlyHint, openWorldHint, idempotentHint, destructiveHint=false). It discloses the deliberate absence of a written count and explains why with a concrete historical counter-example, states that sample_size is null on most rows, explains the GB row's special percentile semantics (min/max are the 25th/75th percentiles), discloses there is no paid tier behind the tool, and spells out the open-world empty-result behavior: unknown codes return 0 rows plus available codes and never widen to a neighbouring band. This richly contextualizes the openWorldHint.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness2/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is very long and repetitive, with multiple ALL-CAPS admonitions restating the same points ('it is not a paid tier', '0 here does not mean the platform is silent'). The frozen-pair historical anecdote about the wrong row count is verbose for the point it makes, and some detail (e.g., 'Eursap n=312 plus the Analytics Legends operator panel') is tangential to invoking the tool. It is front-loaded with the core purpose, but every sentence does not earn its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity — open-world behavior, optional parameters, subtle empty-result semantics, and a close sibling that answers the same market questions — the description covers purpose, scoping, alternatives, result-interpretation rules, and meta-field guidance. Output schema exists, so return-value structure need not be restated. Nothing an agent needs to call this correctly or interpret its results is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, and the description adds genuine value on top: it explains that specialisation codes are not evenly spread across countries (DE holds analytics_all only, datasphere exists for FR alone as a low-confidence median), gives concrete negative examples (no bdc/joule rows), and defines empty-result semantics for unsupported codes. The main description and parameter descriptions are somewhat redundant with each other, which keeps this from a 5.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence names a specific resource (public day-rate aggregate for SAP analytics freelance work) with explicit scope: min/max daily rate by country, specialisation and seniority, each row carrying currency, source, source date and confidence. It directly differentiates itself from find_opportunities, calling itself 'the thinner one' on the questions the market asks most, so an agent can pick the right instrument without opening schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit routing guidance is provided: when the aggregate returns empty for a country × product, the agent must say the aggregate holds no row and 'go read the radar band' via find_opportunities, and must not report that the platform cannot price it. It also instructs reading `_meta.available_countries` / `available_specialisations` / `available_seniorities` before concluding a rate is unpublished. This is model-level when-to-use vs. when-not-to-use guidance with a named alternative.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_firmGet one firm's published profileA
Read-onlyIdempotent
Inspect

Fetch one organisation from the published directory by its database slug (rows[].slug from search_firms, verbatim). Returns the same public fields plus partnerships_declared, the count of partnerships this directory records for the firm — 0 on ~97 % of rows (re-measured 2026-08-14 on the published tranche: 96,8 %), meaning none declared here, never that the firm has no partners. Does not return the paid firm-intelligence profile, contacts, or any person.

ParametersJSON Schema
NameRequiredDescriptionDefault
slugYesThe DATABASE slug, taken verbatim from search_firms.rows[].slug. It is not always the web slug in citation_url: a minority of published rows carry a numeric firm id instead (365 of them on 2026-08-09 — the population is read at query time and returned as `_meta.tranche_total_row_count`, never written down here). get_firm{slug:"00393"} is GULP, whose page is /companies/gulp/. Deriving a slug from the citation URL fails on those rows, and deriving it from the NAME is not safe either; carry rows[].slug across instead. A slug this tool refuses is not proof the firm is absent from the market or even from the database — the published tranche is an editorial subset, and a row the editor has not published is refused here exactly as a wrong slug is.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes
toolYes
_metaNo
_attributionYes
result_countYes

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the annotations (readOnly, idempotent, destructiveHint false), the description adds critical behavioral context: it explains that `partnerships_declared` being 0 means none declared, not that the firm has no partners, and provides measured frequency data. It also notes that a refused slug does not mean the firm is absent, because the published tranche is an editorial subset. This is valuable nuance not captured in annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is efficiently structured: an action sentence, a return-semantics sentence, and an exclusions sentence. It is slightly dense due to the parenthetical measurement detail ('re-measured 2026-08-14... 96,8 %'), but every clause contributes to understanding. It is front-loaded and appropriately sized for a tool with this nuance.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the presence of an output schema and annotations, the description is complete. It explains the key return field's semantics and the potential misinterpretation, while the schema covers slug handling in depth. No important behavioral or usage aspects are left undocumented for the tool's complexity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With schema description coverage at 100%, the baseline is 3. The tool description merely recaps the slug source ('rows[].slug from search_firms') already fully detailed in the schema, adding no new parameter semantics. The schema itself contains extensive guidance on numeric slugs and refusal behavior, but that belongs to the schema, not the tool description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Fetch one organisation from the published directory') and the specific resource ('published profile'), using the verb 'Fetch' with a precise scope. It distinguishes from siblings by noting it returns public fields and does not return the paid firm-intelligence profile or contacts, making its purpose unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies when to use the tool (when a single firm's published profile is needed) and tells the user to source the slug from search_firms, noting that the slug is taken verbatim. It also states exclusions (no paid profile, contacts, or persons), which hints at alternatives, but it does not explicitly name tools like get_firm_intel or search_firms for those needs.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_firm_intelFirm intelligence profile (Legend tier)A
Read-onlyIdempotent
Inspect

The paid intelligence profile of a services firm — SAP practice size and partner level, delivery flags per product, typical day rate and seniority, notable clients, analytics practice summary, and a LinkedIn company URL (present on ~39% of the corpus — glassdoor_rating, glassdoor_reviews_count and linkedin_followers are null on the entire corpus as of 2026-08-10, absence here is a data gap, not a signal). READ THE SPARSITY BEFORE QUOTING A ROW: on the 9,103 profiles measured 2026-08-23, typical_day_rate_eur is null on 78.0% and sap_partner_level on 85.7% — the two headline fields are the exception, not the rule, and a null means 'not researched', never 'no partner level'. Requires a subscriber API key, Legend tier or above. Person-shaped fields (contacts, founders, leadership, recruiters, postal addresses) are NEVER served by this endpoint at any tier — they remain behind the platform's signed-URL path. Search by name; the public directory (search_firms) is a different, wider population.

ParametersJSON Schema
NameRequiredDescriptionDefault
modeNoKeep only firms whose profile declares this engagement mode (mode_freelance / mode_permanent / mode_subcontract). Same reading as `delivers`: declared-only.
nameNoFirm name, matched case-insensitively. Omit to browse the corpus by data completeness.
limitNoMax rows (hard cap 10 — these rows are wide).
cursorNoOpaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments; `null` means the last page. Changing a filter refuses the cursor.
countryNoISO-3166-1 alpha-2 country code, e.g. DE, FR, CH.
deliversNoKeep only firms whose profile DECLARES delivery of this product (the `delivers_*` flags every row already carries). An undeclared flag drops the row: absence from the result means the profile does not declare it, not that the firm cannot deliver it.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes
toolYes
_metaNo
_attributionYes
result_countYes

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Even though annotations already mark the tool readOnly, idempotent, and non-destructive, the description adds crucial behavioral context: null means 'not researched' for key fields, specific sparsity percentages with measurement dates, LinkedIn fields being null corpus-wide, and person-shaped data never being sent. This goes well beyond what annotations provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long but every sentence adds operational value: auth requirements, sparsity caveats, data absence caveats, endpoint boundary, and sibling-tool disambiguation. There is no filler or repetition of the schema, and the most decision-critical warning about sparse fields is front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a paid read-only look-up with six fully documented parameters, an output schema, and helpful annotations, the description covers all remaining contextual gaps: auth level, null interpretation, population scope, search behavior, and what the endpoint will not return.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and each parameter already has detailed semantics including types, defaults, enums, constraints, and cursor semantics. The description reinforces the correct interpretation of missing values, but does not need to add per-parameter detail; baseline 3 is appropriate because the schema carries the burden.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states what the tool returns: a paid firm intelligence profile with explicit data content beyond SAP practice size, partner level, delivery flags, day rate, clients, and LinkedIn URL. It also differentiates itself from search_firms by noting the public directory covers a different, wider population, so the agent can distinguish it from siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives explicit when-to-use guidance with 'search by name', licensing prerequisites ('requires a subscriber API key, Legend tier or above'), an exclusion ('person-shaped fields are NEVER served by this endpoint at any tier'), and a routing cue toward the alternative ('the public directory search_firms is a different, wider population').

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_sap_client_profileOne SAP end-customer profile (Legend tier)A
Read-onlyIdempotent
Inspect

The full profile of one SAP end-customer — SAP footprint (products in use, modules known), analytics solutions, identity and evidence fields. Requires a subscriber API key, Legend tier or above. id comes verbatim from find_sap_clients.rows[].id.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesProfile id, verbatim from find_sap_clients.rows[].id.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes
toolYes
_metaNo
_attributionYes
result_countYes

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly, idempotent, and non-destructive, so the description adds value by disclosing the API key/tier requirement and the source of the id. It does not contradict annotations and provides meaningful operational context beyond the schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two compact sentences. The first states the purpose and content; the second covers auth and id provenance. No filler or repetition of annotations.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple single-parameter retrieval tool with an output schema present, the description covers purpose, content, auth, and id provenance. It is sufficiently complete to guide correct invocation without needing to explain return values.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema already covers the only parameter with 100% coverage. The description adds extra semantic value by explicitly stating that `id` comes verbatim from find_sap_clients.rows[].id, reinforcing correctness of parameter use beyond the schema's brief label.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description specifies a singular, concrete resource: the full profile of one SAP end-customer with listed content domains (SAP footprint, analytics, identity, evidence). It clearly distinguishes from the sibling find_sap_clients by referencing a specific id from that list.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

States the prerequisite (subscriber API key, Legend tier) and tells the agent that the id should come from find_sap_clients.rows[].id, implying the natural workflow: first list, then get detail. It does not explicitly mention when not to use alternatives, but the referential guidance provides sufficient usage direction.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_studyRead a study (Consultant tier)A
Read-onlyIdempotent
Inspect

Read one Analytics Legends study BODY — the paid text behind list_studies' metadata. Requires a subscriber API key, Consultant tier or above. Bodies run to 38k words and exceed the 256 KiB response ceiling, so this tool serves STRUCTURE first: called without section it returns the section list and the introduction; pass section (a heading from that list, matched case-insensitively) to read one section. Find slugs and languages with list_studies.

ParametersJSON Schema
NameRequiredDescriptionDefault
langNoISO-639-1 language of the edition, e.g. en or fr. Defaults to en.en
slugYesStudy slug, verbatim from list_studies.rows[].slug.
sectionNoA section heading from a previous call's `sections` list. Omit to get the list.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes
toolYes
_metaNo
_attributionYes
result_countYes

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the annotations (readOnly, idempotent, non-destructive), the description discloses significant behavioral traits: a subscriber API key and Consultant tier requirement, the 256 KiB response ceiling, the structure-first behavior, case-insensitive section matching, and inclusion of the introduction. This is rich context useful for an agent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Every sentence earns its place: purpose, auth constraint, size limitation, usage mechanism, and pointer to list_studies. The description is front-loaded with the core action and then methodically explains the chunked reading approach without redundancy. Length is justified by the tool's complexity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (auth tier, size limit, section-based reading) and the presence of an output schema, the description fully covers how to invoke it correctly: what to pass, what to expect, and where to get inputs. No critical usage aspect is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3. The description adds value by noting that `section` matching is case-insensitive and that omitting `section` returns not just the section list but also the introduction. It also clarifies that `slug` is verbatim from list_studies, reinforcing schema hints.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb+resource: 'Read one Analytics Legends study BODY — the paid text behind list_studies' metadata.' This clearly distinguishes it from sibling list_studies and other get_* tools by focusing on the full text body, not metadata or other entity types.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly tells when to use this tool vs list_studies: 'Find slugs and languages with list_studies.' It also provides a full usage pattern: call without `section` for the section list and introduction, then pass a section heading for one section. No exclusions are needed because the alternative is named.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_firm_kindsList firm kinds with live countsA
Read-onlyIdempotent
Inspect

Breakdown of the published firm directory by organisation kind, with a live row count per kind. Use this before search_firms to know what the population actually is instead of guessing.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes
toolYes
_metaNo
_attributionYes
result_countYes

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, and destructiveHint=false, covering the safety profile. The description adds useful behavioral detail: the counts are 'live', indicating freshness and that the data reflects the current state, which is not conveyed by annotations or schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences, front-loaded with the core function and then a practical usage hint. Every word earns its place, with no redundancy or filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the simple parameterless signature, rich annotations, and presence of an output schema, the description provides sufficient context for an agent to understand the tool's role and when to invoke it. It adds domain context (published directory, live counts) and usage guidance, making it complete for this tool's complexity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There are no parameters, so the description carries no parameter burden. The baseline for zero parameters is 4, and though the description doesn't add parameter-specific meaning, it explains the output grouping by organization kind, which helps set expectations for the result.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool provides a breakdown of the published firm directory by organization kind, with live counts. It distinguishes itself from sibling tools like search_firms by focusing on population-level aggregation rather than individual firm lookup, and explicitly frames it as a precursor to searching.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly says to use this tool before search_firms, providing clear context and a naming the alternative. It lacks explicit 'when not to use' exclusions, but the guidance to use it as a preliminary step is direct and helpful.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_freelance_platformsList the CV/profile platforms a consultant can sign up onA
Read-onlyIdempotent
Inspect

The subset of the published directory where a consultant can CREATE A PROFILE — freelance marketplaces, job boards with candidate profiles, talent platforms and expert networks — each with its signup URL, an editorial confidence grade and the date it was assessed. This answers the entering-contractor's first practical question ('where do I register?') in one call. Everything here is also in search_firms — this tool adds the platform fields and the filter, never a wider population. signup_url is the platform's own page: it was verified on assessed_at, and a platform absent here is not proven to refuse signups — it is unassessed or unpublished.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax rows (hard cap 50).
cursorNoOpaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments; `null` means the last page. Changing a filter refuses the cursor.
countryNoISO-3166-1 alpha-2 country code, e.g. DE, FR, CH.
platform_typeNoRestrict to one platform type, lowercase snake_case. The live vocabulary with counts is `_meta.available_platform_types` on every response — a well-formed unknown value returns no rows, it never widens the result.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes
toolYes
_metaNo
_attributionYes
result_countYes

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare read-only, non-destructive, and idempotent behavior, so the description does not need to re-cover those. It does add meaningful semantics: signup_url is verified on assessed_at, and a platform absent here is not proof of refusal—it is merely unassessed or unpublished. These boundary details would be hard to infer from the schema alone.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The main purpose is front-loaded, and every sentence carries signal: what the list contains, which question it answers, how it relates to search_firms, and how URL verification/absence should be interpreted. There is no filler or redundancy with the title.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description is complete for this read-only list tool: it clarifies scope, a reliable sibling relationship, verification meaning of the returned URL, and absence semantics. With four optional parameters all documented in the schema and read-only annotations present, nothing critical is missing for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, and every parameter already has a focused description including cursor rules, country format, and platform_type behavior. The tool description adds little parameter-level meaning beyond clarifying that the tool applies a platform-type filter, so the appropriate baseline is 3.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific verb (list) and resource (freelance platforms where a consultant can create a profile) and gives concrete examples of the included platform kinds. It also clearly distinguishes itself from search_firms by stating this is a narrower subset that adds platform fields, so an agent can tell it apart from sibling tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly positions this tool as the one answering 'where do I register?' and names search_firms as the broader alternative, clarifying that this tool never returns a wider population. That gives an agent a direct routing decision: use this when platform signup fields are the goal, and search_firms for the broader directory.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_sap_modulesSAP analytics module taxonomyA
Read-onlyIdempotent
Inspect

The canonical SAP module/product taxonomy Analytics Legends classifies against (codes and EN/FR labels by category). Use it to normalise a user's loose product wording — 'SAC', 'Analytics Cloud', 'Datasphere' — onto the codes the other tools filter on.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax rows (hard cap 50).
queryNoFree-text filter, case-insensitive. EVERY word must appear in the record (substring per word, any order), so a natural-language phrase narrows the answer instead of having to match verbatim.
cursorNoOpaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments; `null` means the last page. Changing a filter refuses the cursor.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes
toolYes
_metaNo
_attributionYes
result_countYes

TDQS

A4.1/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false, so the safety profile is covered. The description adds useful context about the taxonomy being canonical and containing codes with EN/FR labels by category, but it does not disclose pagination or filtering behavior beyond what the schema already provides.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences with no wasted words: the first defines what the tool is and what it classifies, the second gives concrete usage and examples. The most decision-relevant information is front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With rich annotations, a fully described input schema, and an output schema present, the description only needs to convey the tool's selection rationale and canonical role. It does so clearly, including concrete examples of loose wording, making it complete for an agent to invoke correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents limit, query, and cursor in detail. The description adds high-level context about mapping product wording to codes but does not add parameter-level semantics beyond the input schema. Baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('normalise') and resource ('SAP module/product taxonomy') and clearly explains the tool's role: mapping loose user wording onto canonical codes. It distinguishes itself from the sibling tools by positioning it as the taxonomy underlying the codes the other tools filter on.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly says to use it when normalising loose product wording like 'SAC' or 'Analytics Cloud', which gives clear context for selection. It does not explicitly name an alternative tool or state when not to use it, but the intended use case is specific enough for an agent to route correctly.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_studiesList the deep-research studies (metadata only)A
Read-onlyIdempotent
Inspect

List the Analytics Legends deep-research studies with their edition, as-of date, audience, word count and canonical URL. METADATA ONLY: study bodies are a paid Consultant-tier deliverable, served by get_study on this same endpoint with a subscriber key. Use this to tell a reader that a study exists and where to read it.

ParametersJSON Schema
NameRequiredDescriptionDefault
langNoLanguage code, EN or FR.
limitNoMax rows (hard cap 50).
queryNoFree-text filter, case-insensitive. EVERY word must appear in the record (substring per word, any order), so a natural-language phrase narrows the answer instead of having to match verbatim.
cursorNoOpaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments; `null` means the last page. Changing a filter refuses the cursor.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes
toolYes
_metaNo
_attributionYes
result_countYes

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false. The description adds genuinely useful behavioral context beyond these: it explicitly discloses that study bodies are not returned, that they require a subscriber key, and that this endpoint serves only metadata. This is meaningful extra transparency about access control and content scope.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences with no filler. It front-loads the core action and return scope, then immediately states the metadata-only constraint and the relevant alternative. Every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool has a comprehensive input schema, a full output schema, and annotations covering read-only, idempotent, and non-destructive behavior. The description completes the picture by explaining the paid-tier restriction, the relationship to get_study, and the intended use case. Nothing needed for correct invocation is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the input schema fully documents all four parameters (lang, limit, query, cursor) including constraints, defaults, and pagination behavior. The description adds no parameter-level semantics beyond what the schema already provides, which matches the baseline score.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('List'), a precise resource ('Analytics Legends deep-research studies'), and enumerates the returned fields (edition, as-of date, audience, word count, canonical URL). It explicitly distinguishes itself from get_study by calling out 'METADATA ONLY', so an agent can easily tell this tool apart from its sibling.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit usage context: use this to tell a reader a study exists and where to read it, while full bodies are served by get_study with a subscriber key. It clearly routes the agent to the correct sibling tool and states the boundary between metadata and paid deliverables.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

query_knowledge_graphTraverse the learning knowledge graphA
Read-onlyIdempotent
Inspect

The RELATIONS between the platform's teaching objects — which Academy module teaches which concept, which study covers which module, what a concept relates to. THIS IS THE ONLY TOOL ON THIS SERVER THAT SERVES EDGES; the others serve rows. Ask it what connects to what, not what exists. SCOPE, AND IT IS NARROWER THAN 'the knowledge graph': it carries four node types — concept, module, study, vendor — and every edge whose BOTH endpoints are one of them. The whole graph holds eleven node types; the seven it does not carry are each either served by their own tool or named as not served at all, and _meta.excluded_node_types says which per type (consultant data is served at NO tier), so a missing type is a documented boundary and never a silent gap. Call it with node_id (e.g. module:M178, concept:C001, study:ai-impact-2026-EN) to walk one node's neighbourhood; with node_type and/or query to find a node id first. edge_type and direction narrow a walk. Read _meta.available_edge_types — computed from the served projection on every call — before assuming an edge type exists.

ParametersJSON Schema
NameRequiredDescriptionDefault
langNoISO-639-1 language for the LABELS — en, fr or de. Defaults to en. The fallback is declared and never silent: the language asked for, then English, then French, and `_meta.label_language_coverage` says on how many nodes each language is actually filled. Concept and module titles carry French; German arrives as the corpus is translated, and an untranslated node falls back rather than being hidden.
limitNoMax rows (hard cap 50).
queryNoCase-insensitive substring of a node's label. Applies to the NODE listing, not to a walk. Matched against the label SERVED, so it follows `lang`.
cursorNoOpaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments; `null` means the last page. Changing a filter refuses the cursor.
node_idNoFully-qualified node id, `<type>:<id>` — `module:M178`, `concept:C001`, `study:ai-impact-2026-EN`, `vendor:alteryx`. With it, rows are that node's EDGES (one row per neighbour). Without it, rows are NODES.
directionNoWhich side of the edge `node_id` must sit on. Default `both`. Ignored without `node_id`, and the response says so rather than pretending it applied.
edge_typeNoRestrict a walk to one relation. The served projection carries FIVE — teaches · taught_by · covers · related · mentions — and this list is a HINT, not the authority: read `_meta.available_edge_types`, computed on every call. A four-name list stood here while the projection served five, so `mentions` was reachable and undocumented.
node_typeNoRestrict to one carried node type: concept · module · study · vendor. Read `_meta.available_node_types`.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes
toolYes
_metaNo
_attributionYes
result_countYes

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already provide readOnly=true, idempotent=true, destructive=false, and openWorld=false, and the description adds substantial behavior beyond them: the closed-world boundary is explicit ('a missing type is a documented boundary and never a silent gap'), the edge_type list is disclosed as 'a HINT, not the authority' with a historical failure incident (mentions reachable but undocumented), lang fallback is declared non-silent with coverage metadata, and changing a filter 'refuses the cursor'. No contradiction with the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Long but dense — every sentence earns its place (purpose, differentiation, scope boundary, call modes, meta-guidance) and the key differentiators are front-loaded. Minor deductions: the 'HINT, not the authority' point and the four node types are repeated in parameter schema descriptions, and the ALL-CAPS emphasis is heavy-handed.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with 8 parameters, three call modes, pagination, language fallback, and a documented scope boundary, nothing needed to invoke it correctly is missing: boundaries, modes, meta-keys (_meta.available_edge_types, _meta.excluded_node_types, _meta.label_language_coverage, _meta.next_cursor), and fallback behavior are all disclosed. An output schema exists, so return-value structure need not be in the description.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and the parameter descriptions are themselves rich, so the baseline is 3. The tool description adds genuine cross-parameter meaning: the walk-vs-find mode distinction (node_id present => edges, absent => nodes), the sequencing guidance to 'find a node id first' with node_type/query, and the rule that edge_type/direction merely narrow a walk. That pushes it above baseline, though part of the mode distinction is also stated in the node_id schema description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a precise verb+resource — query/traverse the SERVED EDGES of the knowledge graph — and immediately differentiates it from every sibling: 'THIS IS THE ONLY TOOL ON THIS SERVER THAT SERVES EDGES; the others serve rows. Ask it what connects to what, not what exists.' It enumerates the four carried node types and gives concrete relation examples, so an agent cannot confuse it with get_concept, get_study, or the search tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit when-to-use guidance is present and contrasts directly with alternatives: use it for relations, not existence ('the others serve rows'), and the seven excluded node types are 'each either served by their own tool or named as not served at all'. It also separates the two call modes — walk with node_id vs. find with node_type/query — and names the hard boundary case (consultant data is served at NO tier).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_conceptsSearch the SAP analytics concept encyclopaediaA
Read-onlyIdempotent
Inspect

Search the SAP analytics concept encyclopaedia — the vocabulary of the stack, written for practitioners. Returns slug, title, category, level, tags and the editor's summary. level is SPARSE — null on 108 of the 330 active rows, measured 2026-08-27 — and a null there means 'not graded', never 'Beginner'. These are the same fields get_concept returns for ONE slug. The card BODY (why-it-matters, key points, cheat sheet, the four analysis tables) is Consultant-tier: call get_concept_card.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax rows (hard cap 50).
queryNoFree-text filter, case-insensitive. EVERY word must appear in the record (substring per word, any order), so a natural-language phrase narrows the answer instead of having to match verbatim.
cursorNoOpaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments; `null` means the last page. Changing a filter refuses the cursor.
categoryNoConcept category, matched case-insensitively as an exact value OR a prefix — so category:"datasphere" reaches 'Datasphere Core'. The values are long human labels, not codes. DO NOT GUESS THEM FROM THIS TEXT: the live vocabulary with a row count per label comes back as `_meta.available_categories` on EVERY call, including a call that matched nothing. A list written here would say 14 labels with 2026-07-30 counts; the corpus holds 15 today, and five of those counts have moved. Read the envelope, not the prose.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes
toolYes
_metaNo
_attributionYes
result_countYes

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, and destructiveHint, and the description is consistent with a safe read operation. It adds meaningful behavioral context beyond annotations: the level field is sparse, null means 'not graded' rather than 'Beginner', category counts can drift, and the live envelope should be trusted over prose.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is longer than minimal, but each sentence earns its place by adding operational context. It is front-loaded with purpose and return fields, then moves through caveats and sibling routing. The category paragraph is dense and somewhat convoluted, but the structure is logical and not wasteful.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only search tool with a rich schema and output schema, the description covers all important context: return fields, null semantics, pagination envelope behavior, category vocabulary drift, and when to use sibling tools. Nothing critical is missing for an agent to invoke it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3. The description adds value beyond the schema by explaining level null semantics, emphasizing that category labels are long human labels and not to be guessed, and pointing to the always-returned _meta.available_categories. Cursor and query semantics are already well documented in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource — search the SAP analytics concept encyclopaedia — and enumerates the exact returned fields: slug, title, category, level, tags, and the editor's summary. It also explicitly distinguishes itself from get_concept and get_concept_card, making its purpose unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives clear routing guidance: get_concept returns the same fields for one slug, while get_concept_card should be used for the body content. It also explains practical search semantics and warns against guessing category labels, telling the agent to rely on the live _meta.available_categories envelope instead.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_firmsSearch the SAP analytics firm directoryA
Read-onlyIdempotent
Inspect

Search the published Analytics Legends directory of SAP analytics service providers — placement agencies, Big-4 and ESN practices, SAP vendors, platforms and community groups — by country, kind, declared SAP module and free text. Returns name, HQ country/city, website, careers URL and a one-line editorial claim. SAP END-CUSTOMER companies are NOT in this directory: they are a separate paid dataset, excluded here by the is_client FLAG — not by the client_enterprise kind code. The two are different columns, and where a row's flag and its kind label disagree in the SSOT it is the flag that decides what this tool serves, so read the flag's meaning into the answer and not the label's. PAGINATED: the whole matched set is reachable — pass _meta.next_cursor back as cursor with the same filters until it is null. When query is set, rows are ordered by how well the NAME matches it (exact, then prefix, then substring), and rows matching only the description come last; without query the order is the directory's own quality ranking.

ParametersJSON Schema
NameRequiredDescriptionDefault
kindNoRestrict to one organisation kind. This list is the vocabulary the corpus holds today, not a frontier — call list_firm_kinds for the live one. A malformed code is refused; a well-formed code the corpus does not hold returns no rows. Neither case is silently ignored, and neither widens the result.
limitNoMax rows (hard cap 50).
queryNoFree-text filter, case-insensitive. EVERY word must appear in the record (substring per word, any order), so a natural-language phrase narrows the answer instead of having to match verbatim.
cursorNoOpaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments; `null` means the last page. Changing a filter refuses the cursor.
moduleNoRestrict to organisations with a DECLARED link to one SAP module/product code (UPPERCASE snake_case, e.g. DATASPHERE, BDC, SAC, BW4HANA, S4HANA, JOULE — case-insensitive on input). The declared links are structured data, far more selective than free text: `count_firms_by {by:"module"}` gives the live vocabulary with counts. A minority of the directory declares any module at all, so this filter finds the DECLARED specialists — absence from the result means no declared link, never that the firm does not work on the module.
countryNoISO-3166-1 alpha-2 country code, e.g. DE, FR, CH.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes
toolYes
_metaNo
_attributionYes
result_countYes

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, and non-destructive behavior, and the description still adds substantial operational context: the flag-vs-kind precedence rule when the SSOT disagrees, pagination via _meta.next_cursor with refusal when filters change, query-relative ordering (exact, prefix, substring, then description-only matches), and the module-filter interpretation that absence means 'no declared link', never 'does not work on the module'. No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long but every sentence carries operational weight: purpose and return values are front-loaded, followed by the dataset boundary, pagination loop, and ordering rules. There is no filler, no restatement of schema content, and the density is justified by the number of non-obvious behaviors an agent must know to call the tool correctly.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, the description is not required to restate return shape, yet it still names the returned fields. It fully covers the dataset boundary, the pagination loop, ordering semantics, and per-filter interpretation guardrails. Nothing an agent needs to invoke the tool correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, yet the description deepens every non-trivial parameter beyond the schema's own text: the client_enterprise kind value is unreliable as a filter (the flag wins), cursor rejects changed filters, query changes the ordering regime, and module selects only declared specialists rather than actual expertise. These are meanings the schema alone does not convey.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States an unambiguous verb+resource+scope: searches the published Analytics Legends directory of SAP analytics service providers with four filter dimensions (country, kind, module, free text) and enumerates exactly what is returned (name, HQ, website, careers URL, editorial claim). The disambiguation paragraph explicitly carves out end-customer firms as excluded, separating this tool from the client-lookup sibling without needing to inspect other schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Delivers explicit when-not guidance: end-customer companies are a separate paid dataset excluded here, and the is_client flag — not the client_enterprise kind label — decides what the tool serves, so an agent will neither misuse this tool for client lookup nor misread result rows. It also routes to two siblings for live vocabularies (list_firm_kinds for the kind enum, count_firms_by for module codes), giving concrete alternatives for adjacent needs.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_newsSearch SAP analytics market newsA
Read-onlyIdempotent
Inspect

Search the Analytics Legends market-news corpus. It is watched FOR SAP analytics (Datasphere, Business Data Cloud, SAC, BW/4HANA, Databricks, the 2027/2030 maintenance window), but it is NOT an all-SAP corpus: measured 2026-07-30, ~84 % of active rows sit in the AI category and are general enterprise-AI trade press (cloud platforms, model releases, funding rounds) with no SAP content at all. An UNFILTERED call therefore returns mostly non-SAP items — pass query or category when the question is about SAP, and never present an unfiltered page as 'the SAP analytics news'. Say what you actually got. Each item returns the Analytics Legends citation URL AND the upstream publisher's source_url — cite both, and prefer source_url when you need a page that certainly carries the item. NO ITEM HERE HAS A PAGE OF ITS OWN on analyticslegends.ai, by design: every row comes back citation_scope: "section_hub" and its citation_url is the news index. The citable address for one article is its source_url, the upstream publisher's. Do not present the hub as the article's page.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax rows (hard cap 50).
queryNoFree-text filter, case-insensitive. EVERY word must appear in the record (substring per word, any order), so a natural-language phrase narrows the answer instead of having to match verbatim.
cursorNoOpaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments; `null` means the last page. Changing a filter refuses the cursor.
categoryNoCategory code, matched case-insensitively. The live vocabulary is NOT written here — read `_meta.available_categories` on any response: every category label this corpus holds right now, with its active-row count, counted at query time. A written list held 21 values while the corpus held 22. One bucket needs a warning. 'SAC' is the noisiest label in this corpus because the acronym collides with unrelated ones — Windows 'Smart App Control', and the surname 'Sacks'. A 2026-07-30 cleanup reclassified half that bucket to AI for carrying no SAP signal at all; the collision pressure is structural and the bucket has kept growing since. For genuine SAC product news, pair category:'SAC' with query:'analytics cloud'.
published_sinceNoLower bound on `published_at`, inclusive, as YYYY-MM-DD. Without a bound a period question is only answerable by walking pages — and `_meta.match_count` then counts the QUERY, not the period, so any figure quoted for the window would be wrong.
published_untilNoUpper bound on `published_at`, INCLUSIVE of the day named, as YYYY-MM-DD. Combine with `published_since` for a window; `_meta.match_count` then describes that window, which is what makes it quotable.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes
toolYes
_metaNo
_attributionYes
result_countYes

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the readOnlyHint and destructiveHint annotations, the description discloses several non-obvious behaviors: no item has its own page on analyticslegends.ai, every row returns `citation_scope: "section_hub"`, and the citable address is the upstream `source_url`. It also quantifies the AI-category noise and explains the SAC collision problem, giving the agent crucial context annotations cannot express.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long but densely packed with non-obvious, decision-relevant facts. It is front-loaded with the core scope warning, then progresses to filtering guidance, citation semantics, and page-ownership behavior. Every sentence adds value; there is no filler or repetition of the title.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the output schema exists and the parameter schemas are rich, the description covers all critical contextual gaps: corpus composition, filtering requirements, citation behavior, how to cite properly, and the period/count caveats in the schema. An agent has everything it needs to invoke this tool correctly and interpret results safely.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description adds incremental cross-parameter guidance by telling the agent to supply `query` or `category` for SAP-related questions and warning that an unfiltered call returns mostly non-SAP items. Individual parameter formats and edge cases are already documented well in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence names a specific verb and resource: 'Search the Analytics Legends market-news corpus.' The description immediately clarifies the corpus's scope—it is watched for SAP analytics but is not an all-SAP corpus—so an agent can distinguish it from generic or concept-oriented searches without inspecting sibling tool schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit when-to-filter guidance: pass `query` or `category` when the question is about SAP, and never present an unfiltered page as 'the SAP analytics news.' It also warns about the AI-category noise. It does not name a specific alternative tool, but no sibling is a news search, so the absence is not a major gap.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

  1. 1 tool update
    • Changedfind_opportunities3 fields changed
      • addedInput schema / properties / lang
        Added value: +{
        +  "description": "Reading language for the TITLE — 'EN' (default), 'FR' or 'DE'. This is a RENDERING choice, never a filter: it changes which string `title` carries, never which rows come back. Read `title_lang` on every row for the language actually served: it differs from what you asked for exactly when that translation does not exist (FR covers 1,492 of 1,588 site-radar rows, DE 1,373 — measured 2026-09-04), and the verbatim is served instead, labelled with the language the harvest chain measured. `source_lang` always carries the language the ADVERTISER wrote in, translated or not. The promoted leg has no translated columns at all: its rows ignore this argument and say so with `title_lang: null` — see `_meta.untranslated_leg`.",
        +  "pattern": "^[A-Za-z]{2}$",
        +  "type": "string"
        +}
      • addedOutput schema / properties / rows / items / properties / source_lang
        Added value: +{
        +  "description": "ISO-639-1 language the ADVERTISER wrote the title in, measured by the harvest chain and never changed by `lang` — 'en' on 1,566 rows, 'de' on 337 (2026-08-23). Null when the chain could not decide, and on the promoted leg. Compare it with `title_lang` to know whether the title you are quoting is the advertiser's own words or this platform's rendering of them: a harvested posting is a third party's text, and saying which of the two you are citing is the only honest way to quote it.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • changedOutput schema / properties / rows / items / properties / title_lang / description
        Previous value: -"ISO-639-1 language the TITLE is written in, as measured by the harvest chain — 'en' on 1,566 rows, 'de' on 337 (2026-08-23). Null when the chain could not decide. A harvested title is a third party's own words and is never translated, so this label is the only honest way to tell a reader the posting you are quoting is not in their language. Absent from the served payload until 2026-08-23: the projector's keep-list dropped it."New value: +"ISO-639-1 language the SERVED `title` is written in. READ IT ON EVERY ROW: it is what you asked for in `lang` when that translation exists, and the advertiser's own language when it does not (FR covers 1,492 of 1,588 site-radar rows, DE 1,373 — measured 2026-09-04). Null when the harvest chain could not decide, and on every promoted-leg row, which carries no translated column at all. ⚠️ THIS FIELD CHANGED REFERENT ON 2026-09-04: until then it named the language the ADVERTISER wrote in — that fact now lives in `source_lang`. The two coincided while no translation was served and diverge from the first one that is."
  2. 1 tool update
    • Changedquery_knowledge_graph4 fields changed
      • addedInput schema / properties / lang
        Added value: +{
        +  "description": "ISO-639-1 language for the LABELS — en, fr or de. Defaults to en. The fallback is declared and never silent: the language asked for, then English, then French, and `_meta.label_language_coverage` says on how many nodes each language is actually filled. Concept and module titles carry French; German arrives as the corpus is translated, and an untranslated node falls back rather than being hidden.",
        +  "pattern": "^[A-Za-z]{2}$",
        +  "type": "string"
        +}
      • changedInput schema / properties / query / description
        Previous value: -"Case-insensitive substring of a node's label. Applies to the NODE listing, not to a walk."New value: +"Case-insensitive substring of a node's label. Applies to the NODE listing, not to a walk. Matched against the label SERVED, so it follows `lang`."
      • addedOutput schema / properties / rows / items / properties / label / description
        Added value: +"In the language asked for when the node carries it, else the declared fallback — see `label_lang`."
      • addedOutput schema / properties / rows / items / properties / label_lang
        Added value: +{
        +  "description": "The language the served `label` is actually written in. It differs from the requested `lang` exactly when that translation does not exist.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
  3. 1 tool update
    • Changedfind_opportunities1 field changed
      • changedOutput schema / properties / rows / items / properties / rate_band / description
        Previous value: -"Editorial benchmark for this posting's (seniority × product × region) cell — NOT a rate the employer offered. `basis` and `kind` are inside the object on purpose, so no extraction can lift the numbers away from what they mean."New value: +"Editorial benchmark for this posting's (seniority × product × region) cell — NOT a rate the employer offered. `basis` and `kind` are inside the object on purpose, so no extraction can lift the numbers away from what they mean. 2026-08-29: this band is a SUBSCRIBER surface and is no longer carried by the public radar file this lane reads — expect it to be null here. The posting's OWN published rate, when it has one, stays in currency / daily_rate_min / daily_rate_max."
  4. 1 tool update
    • Changedfind_opportunities2 fields changed
      • changedInput schema / properties / country / description
        Previous value: -"ISO-3166-1 alpha-2 code, applied to both legs as a predicate on the row's own country_code. It effectively selects the SITE-RADAR leg: the promoted feed leaves country_code NULL on all but a handful of its active rows, so a country filter drops the rest of that leg because they do not match, not because the leg was excluded by assumption. `_meta.match_count_by_leg` shows what each leg contributed on YOUR call — read the split there, never from a figure quoted in this text."New value: +"ISO-3166-1 alpha-2 code, applied to both legs as a predicate on the row's own country_code. It NO LONGER selects the site-radar leg alone: the promoted feed carried country_code on almost none of its rows until 2026-08-28 and now carries it on most, so a country filter now returns both legs. A row still without one is dropped because it does not match, not because its leg was excluded by assumption. `_meta.match_count_by_leg` shows what each leg contributed on YOUR call — read the split there, never from a figure quoted in this text."
      • changedInput schema / properties / employment_type / description
        Previous value: -"Restrict to one engagement type. THE RADAR IS MOSTLY PERMANENT, so a freelance or contract question answered off an unfiltered page is answered with salaried jobs. For the actual split, make the filtered call and read `_meta.tranche_total_row_count` — it is counted at query time. The promoted leg stores contract_type NULL on every one of its active rows, so any value here drops that leg by predicate — `_meta.note` says so. THOSE ROWS ARE NOT A FOURTH TYPE AND NOT PERMANENT ONES: `_meta.available_employment_types` counts only what declares, and `_meta.employment_type_undeclared` carries the rest, so the two together are the population and either alone is not. Read both before quoting a mix."New value: +"Restrict to one engagement type. THE RADAR IS MOSTLY PERMANENT, so a freelance or contract question answered off an unfiltered page is answered with salaried jobs. For the actual split, make the filtered call and read `_meta.tranche_total_row_count` — it is counted at query time. The promoted leg declared NO contract_type until 2026-08-28 and now declares one on most of its rows, so a value here no longer drops that leg wholesale — only the rows still silent. THOSE ROWS ARE NOT A FOURTH TYPE AND NOT PERMANENT ONES: `_meta.available_employment_types` counts only what declares, and `_meta.employment_type_undeclared` carries the rest, so the two together are the population and either alone is not. Read both before quoting a mix."
  5. 3 tool updates
    • Changedfind_academy_modules1 field changed
      • changedOutput schema / properties / rows / items / properties / status / description
        Previous value: -"Editorial access marker: 'available' or 'legend-pass'. Served, not filtered on."New value: +"Editorial access marker: 'available' or 'legend-pass'. Served, not filtered on. READ 'legend-pass' AS 'sold at the CONSULTANT tier': it is a LEGACY label from when the paid tier was called Legend, it sits on the majority of rows, and the tier that actually opens the body here is Consultant (get_academy_module). The site stopped rendering this field for exactly that reason on 2026-07-26; this endpoint still serves the raw value, so the LABEL is the stale half and the tier floor is the true one."
    • Changedfind_opportunities1 field changed
      • changedInput schema / properties / employment_type / description
        Previous value: -"Restrict to one engagement type. THE RADAR IS MOSTLY PERMANENT, so a freelance or contract question answered off an unfiltered page is answered with salaried jobs. For the actual split, make the filtered call and read `_meta.tranche_total_row_count` — it is counted at query time. The promoted leg stores contract_type NULL on every one of its active rows, so any value here drops that leg by predicate — `_meta.note` says so."New value: +"Restrict to one engagement type. THE RADAR IS MOSTLY PERMANENT, so a freelance or contract question answered off an unfiltered page is answered with salaried jobs. For the actual split, make the filtered call and read `_meta.tranche_total_row_count` — it is counted at query time. The promoted leg stores contract_type NULL on every one of its active rows, so any value here drops that leg by predicate — `_meta.note` says so. THOSE ROWS ARE NOT A FOURTH TYPE AND NOT PERMANENT ONES: `_meta.available_employment_types` counts only what declares, and `_meta.employment_type_undeclared` carries the rest, so the two together are the population and either alone is not. Read both before quoting a mix."
    • Changedquery_knowledge_graph1 field changed
      • changedInput schema / properties / edge_type / description
        Previous value: -"Restrict a walk to one relation (teaches · taught_by · covers · related). Read `_meta.available_edge_types` — it is computed, never written here."New value: +"Restrict a walk to one relation. The served projection carries FIVE — teaches · taught_by · covers · related · mentions — and this list is a HINT, not the authority: read `_meta.available_edge_types`, computed on every call. A four-name list stood here while the projection served five, so `mentions` was reachable and undocumented."
  6. 9 tool updates
    • Changedcount_firms_by1 field changed
      • changedInput schema / properties / query / description
        Previous value: -"Free-text filter, matched case-insensitively."New value: +"Free-text filter, case-insensitive. EVERY word must appear in the record (substring per word, any order), so a natural-language phrase narrows the answer instead of having to match verbatim."
    • Changedfind_academy_modules1 field changed
      • changedInput schema / properties / query / description
        Previous value: -"Free-text filter, matched case-insensitively."New value: +"Free-text filter, case-insensitive. EVERY word must appear in the record (substring per word, any order), so a natural-language phrase narrows the answer instead of having to match verbatim."
    • Changedfind_opportunities1 field changed
      • changedInput schema / properties / query / description
        Previous value: -"Free-text filter, matched case-insensitively."New value: +"Free-text filter, case-insensitive. EVERY word must appear in the record (substring per word, any order), so a natural-language phrase narrows the answer instead of having to match verbatim."
    • Changedfind_sap_clients1 field changed
      • changedInput schema / properties / query / description
        Previous value: -"Free-text filter, matched case-insensitively."New value: +"Free-text filter, case-insensitive. EVERY word must appear in the record (substring per word, any order), so a natural-language phrase narrows the answer instead of having to match verbatim."
    • Changedlist_sap_modules1 field changed
      • changedInput schema / properties / query / description
        Previous value: -"Free-text filter, matched case-insensitively."New value: +"Free-text filter, case-insensitive. EVERY word must appear in the record (substring per word, any order), so a natural-language phrase narrows the answer instead of having to match verbatim."
    • Changedlist_studies1 field changed
      • changedInput schema / properties / query / description
        Previous value: -"Free-text filter, matched case-insensitively."New value: +"Free-text filter, case-insensitive. EVERY word must appear in the record (substring per word, any order), so a natural-language phrase narrows the answer instead of having to match verbatim."
    • Changedsearch_concepts1 field changed
      • changedInput schema / properties / query / description
        Previous value: -"Free-text filter, matched case-insensitively."New value: +"Free-text filter, case-insensitive. EVERY word must appear in the record (substring per word, any order), so a natural-language phrase narrows the answer instead of having to match verbatim."
    • Changedsearch_firms1 field changed
      • changedInput schema / properties / query / description
        Previous value: -"Free-text filter, matched case-insensitively."New value: +"Free-text filter, case-insensitive. EVERY word must appear in the record (substring per word, any order), so a natural-language phrase narrows the answer instead of having to match verbatim."
    • Changedsearch_news1 field changed
      • changedInput schema / properties / query / description
        Previous value: -"Free-text filter, matched case-insensitively."New value: +"Free-text filter, case-insensitive. EVERY word must appear in the record (substring per word, any order), so a natural-language phrase narrows the answer instead of having to match verbatim."
  7. 1 tool update
    • Changedfind_opportunities1 field changed
      • addedOutput schema / properties / rows / items / properties / title_lang
        Added value: +{
        +  "description": "ISO-639-1 language the TITLE is written in, as measured by the harvest chain — 'en' on 1,566 rows, 'de' on 337 (2026-08-23). Null when the chain could not decide. A harvested title is a third party's own words and is never translated, so this label is the only honest way to tell a reader the posting you are quoting is not in their language. Absent from the served payload until 2026-08-23: the projector's keep-list dropped it.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
  8. 11 tool updates
    • Changedfind_academy_modules1 field changed
      • changedInput schema / properties / cursor / description
        Previous value: -"Opaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments to read the next page; a null `next_cursor` means you have reached the end. It is bound to those filters and refused if they change — a cursor names a POSITION in one ordering, and applying it to another query would start the page in the wrong place."New value: +"Opaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments; `null` means the last page. Changing a filter refuses the cursor."
    • Changedfind_opportunities1 field changed
      • changedInput schema / properties / cursor / description
        Previous value: -"Opaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments to read the next page; a null `next_cursor` means you have reached the end. It is bound to those filters and refused if they change — a cursor names a POSITION in one ordering, and applying it to another query would start the page in the wrong place."New value: +"Opaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments; `null` means the last page. Changing a filter refuses the cursor."
    • Changedfind_sap_clients1 field changed
      • changedInput schema / properties / cursor / description
        Previous value: -"Opaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments to read the next page; a null `next_cursor` means you have reached the end. It is bound to those filters and refused if they change — a cursor names a POSITION in one ordering, and applying it to another query would start the page in the wrong place."New value: +"Opaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments; `null` means the last page. Changing a filter refuses the cursor."
    • Changedget_firm_intel1 field changed
      • changedInput schema / properties / cursor / description
        Previous value: -"Opaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments to read the next page; a null `next_cursor` means you have reached the end. It is bound to those filters and refused if they change — a cursor names a POSITION in one ordering, and applying it to another query would start the page in the wrong place."New value: +"Opaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments; `null` means the last page. Changing a filter refuses the cursor."
    • Changedlist_freelance_platforms1 field changed
      • changedInput schema / properties / cursor / description
        Previous value: -"Opaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments to read the next page; a null `next_cursor` means you have reached the end. It is bound to those filters and refused if they change — a cursor names a POSITION in one ordering, and applying it to another query would start the page in the wrong place."New value: +"Opaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments; `null` means the last page. Changing a filter refuses the cursor."
    • Changedlist_sap_modules1 field changed
      • changedInput schema / properties / cursor / description
        Previous value: -"Opaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments to read the next page; a null `next_cursor` means you have reached the end. It is bound to those filters and refused if they change — a cursor names a POSITION in one ordering, and applying it to another query would start the page in the wrong place."New value: +"Opaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments; `null` means the last page. Changing a filter refuses the cursor."
    • Changedlist_studies1 field changed
      • changedInput schema / properties / cursor / description
        Previous value: -"Opaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments to read the next page; a null `next_cursor` means you have reached the end. It is bound to those filters and refused if they change — a cursor names a POSITION in one ordering, and applying it to another query would start the page in the wrong place."New value: +"Opaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments; `null` means the last page. Changing a filter refuses the cursor."
    • Changedquery_knowledge_graph1 field changed
      • changedInput schema / properties / cursor / description
        Previous value: -"Opaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments to read the next page; a null `next_cursor` means you have reached the end. It is bound to those filters and refused if they change — a cursor names a POSITION in one ordering, and applying it to another query would start the page in the wrong place."New value: +"Opaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments; `null` means the last page. Changing a filter refuses the cursor."
    • Changedsearch_concepts1 field changed
      • changedInput schema / properties / cursor / description
        Previous value: -"Opaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments to read the next page; a null `next_cursor` means you have reached the end. It is bound to those filters and refused if they change — a cursor names a POSITION in one ordering, and applying it to another query would start the page in the wrong place."New value: +"Opaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments; `null` means the last page. Changing a filter refuses the cursor."
    • Changedsearch_firms1 field changed
      • changedInput schema / properties / cursor / description
        Previous value: -"Opaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments to read the next page; a null `next_cursor` means you have reached the end. It is bound to those filters and refused if they change — a cursor names a POSITION in one ordering, and applying it to another query would start the page in the wrong place."New value: +"Opaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments; `null` means the last page. Changing a filter refuses the cursor."
    • Changedsearch_news2 fields changed
      • changedInput schema / properties / category / description
        Previous value: -"Category code, matched case-insensitively. The live vocabulary is NOT written here — read `_meta.available_categories` on any response: every category label this corpus holds right now, with its active-row count, counted at query time. A written list held 21 values while the corpus held 22. One bucket needs a warning. 'SAC' is the noisiest label in this corpus because the acronym collides with unrelated ones — Windows 'Smart App Control', and the surname 'Sacks'. A 2026-07-30 cleanup reclassified half that bucket to AI for carrying no SAP signal at all (Robinhood, Stripe, Google Pay); the collision pressure is structural and the bucket has kept growing since. For genuine SAC product news, pair category:'SAC' with query:'analytics cloud'."New value: +"Category code, matched case-insensitively. The live vocabulary is NOT written here — read `_meta.available_categories` on any response: every category label this corpus holds right now, with its active-row count, counted at query time. A written list held 21 values while the corpus held 22. One bucket needs a warning. 'SAC' is the noisiest label in this corpus because the acronym collides with unrelated ones — Windows 'Smart App Control', and the surname 'Sacks'. A 2026-07-30 cleanup reclassified half that bucket to AI for carrying no SAP signal at all; the collision pressure is structural and the bucket has kept growing since. For genuine SAC product news, pair category:'SAC' with query:'analytics cloud'."
      • changedInput schema / properties / cursor / description
        Previous value: -"Opaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments to read the next page; a null `next_cursor` means you have reached the end. It is bound to those filters and refused if they change — a cursor names a POSITION in one ordering, and applying it to another query would start the page in the wrong place."New value: +"Opaque token from a previous response's `_meta.next_cursor`. Pass it back with the SAME filter arguments; `null` means the last page. Changing a filter refuses the cursor."
  9. 3 tool updates
    • Changedfind_academy_modules6 fields changed
      • addedOutput schema / properties / rows / items / properties / summary_fr
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / rows / items / properties / tags
        Added value: +{
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": [
        +    "array",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / rows / items / properties / title_fr
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / rows / items / properties / track_name_en
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / rows / items / properties / track_name_fr
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / rows / items / properties / updated_at
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
    • Changedsearch_firms8 fields changed
      • addedOutput schema / properties / rows / items / properties / claim
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / rows / items / properties / founded_year
        Added value: +{
        +  "type": [
        +    "integer",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / rows / items / properties / hq_city
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / rows / items / properties / jobs_url
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / rows / items / properties / last_verified_at
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / rows / items / properties / region
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / rows / items / properties / sap_signal_band
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / rows / items / properties / size_bracket
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
    • Changedsearch_news2 fields changed
      • addedInput schema / properties / published_since
        Added value: +{
        +  "description": "Lower bound on `published_at`, inclusive, as YYYY-MM-DD. Without a bound a period question is only answerable by walking pages — and `_meta.match_count` then counts the QUERY, not the period, so any figure quoted for the window would be wrong.",
        +  "type": "string"
        +}
      • addedInput schema / properties / published_until
        Added value: +{
        +  "description": "Upper bound on `published_at`, INCLUSIVE of the day named, as YYYY-MM-DD. Combine with `published_since` for a window; `_meta.match_count` then describes that window, which is what makes it quotable.",
        +  "type": "string"
        +}
  10. 1 tool update
    • Changedcount_firms_by1 field changed
      • changedInput schema / properties / by / enum
        Previous value: -[
        -  "country",
        -  "kind",
        -  "sap_signal_band"
        -]New value: +[
        +  "country",
        +  "kind",
        +  "module",
        +  "sap_signal_band"
        +]
  11. 10 tool updates
    • Changedcount_firms_by1 field changed
      • addedInput schema / properties / module
        Added value: +{
        +  "description": "Restrict to organisations with a DECLARED link to one SAP module code before counting, same vocabulary as search_firms' `module`. Combining it with `by:\"module\"` is legal and returns that single bucket (plus the null bucket).",
        +  "type": "string"
        +}
    • Addedfind_academy_modules
    • Changedfind_opportunities16 fields changed
      • changedInput schema / properties / country / description
        Previous value: -"ISO-3166-1 alpha-2 code, applied to both legs as a predicate on the row's own country_code. It effectively selects the SITE-RADAR leg: the promoted feed stores country_code on 2 of its 109 active rows (measured 2026-08-10), so a country filter drops the rest of that leg because they do not match, not because the leg was excluded by assumption. `_meta.match_count_by_leg` shows what each leg contributed."New value: +"ISO-3166-1 alpha-2 code, applied to both legs as a predicate on the row's own country_code. It effectively selects the SITE-RADAR leg: the promoted feed leaves country_code NULL on all but a handful of its active rows, so a country filter drops the rest of that leg because they do not match, not because the leg was excluded by assumption. `_meta.match_count_by_leg` shows what each leg contributed on YOUR call — read the split there, never from a figure quoted in this text."
      • changedInput schema / properties / remote_mode / description
        Previous value: -"Restrict to one work-location policy: `remote`, `hybrid` or `onsite`. READ THIS BEFORE ANSWERING A REMOTE QUESTION: the radar declares no policy at all on 1,409 of its 2,661 active rows (measured 2026-08-14), and an undeclared row is NOT an on-site row — it is a posting that does not say. Any value here therefore sets those rows aside rather than classifying them, exactly as the site's own filter does, and `_meta.remote_mode_undeclared` reports how many were set aside. The promoted leg carries its own `remote_mode` column and is filtered by the same predicate. Read `_meta.available_remote_modes` for the live spread before assuming a value exists."New value: +"Restrict to one work-location policy: `remote`, `hybrid` or `onsite`. READ THIS BEFORE ANSWERING A REMOTE QUESTION: a large share of the radar declares no policy at all (`_meta.remote_mode_undeclared` carries the live count — roughly half the radar when last measured, and a frozen pair written here drifted ~30% in two days), and an undeclared row is NOT an on-site row — it is a posting that does not say. Any value here therefore sets those rows aside rather than classifying them, exactly as the site's own filter does, and `_meta.remote_mode_undeclared` reports how many were set aside. The promoted leg carries its own `remote_mode` column and is filtered by the same predicate. Read `_meta.available_remote_modes` for the live spread before assuming a value exists."
      • addedOutput schema / properties / rows / items / properties / application_link
        Added value: +{
        +  "description": "`public` (source_url is served) or `members_only` (the link to the original listing is the paid Consultant-tier deliverable; the row and its facts stay public).",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / rows / items / properties / category
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / rows / items / properties / currency
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / rows / items / properties / daily_rate_max
        Added value: +{
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / rows / items / properties / daily_rate_min
        Added value: +{
        +  "description": "Only when the source declares a DAILY rate period — never converted from other periods.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / rows / items / properties / description
        Added value: +{
        +  "description": "Posting text, HTML stripped, clamped. Null on the radar leg.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / rows / items / properties / duration_months
        Added value: +{
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / rows / items / properties / id
        Added value: +{
        +  "description": "The posting's identity — stable across calls, key for dedup.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / rows / items / properties / leg
        Added value: +{
        +  "description": "`promoted` or `site_radar` — which of the two merged public legs served this row.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / rows / items / properties / rate_period
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / rows / items / properties / salary_max
        Added value: +{
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / rows / items / properties / salary_min
        Added value: +{
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / rows / items / properties / skills
        Added value: +{
        +  "description": "Skills the posting names. Null when it names none.",
        +  "type": [
        +    "array",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / rows / items / properties / source
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
    • Changedfind_sap_clients1 field changed
      • addedInput schema / properties / product
        Added value: +{
        +  "description": "Keep only end-customers where this SAP product was DETECTED in use. Absence from the result means undetected, not unused.",
        +  "enum": [
        +    "datasphere",
        +    "bdc",
        +    "sac",
        +    "bw",
        +    "bw4hana",
        +    "s4hana",
        +    "ecc",
        +    "hana",
        +    "joule",
        +    "businessobjects",
        +    "bpc",
        +    "successfactors",
        +    "ariba",
        +    "concur"
        +  ],
        +  "type": "string"
        +}
    • Addedget_academy_module
    • Changedget_firm_intel2 fields changed
      • addedInput schema / properties / delivers
        Added value: +{
        +  "description": "Keep only firms whose profile DECLARES delivery of this product (the `delivers_*` flags every row already carries). An undeclared flag drops the row: absence from the result means the profile does not declare it, not that the firm cannot deliver it.",
        +  "enum": [
        +    "datasphere",
        +    "bdc",
        +    "sac",
        +    "bw",
        +    "bw4hana",
        +    "s4hana",
        +    "ecc",
        +    "hana",
        +    "joule",
        +    "businessobjects",
        +    "bpc",
        +    "successfactors",
        +    "ariba",
        +    "concur"
        +  ],
        +  "type": "string"
        +}
      • addedInput schema / properties / mode
        Added value: +{
        +  "description": "Keep only firms whose profile declares this engagement mode (mode_freelance / mode_permanent / mode_subcontract). Same reading as `delivers`: declared-only.",
        +  "enum": [
        +    "freelance",
        +    "permanent",
        +    "subcontract"
        +  ],
        +  "type": "string"
        +}
    • Changedget_study1 field changed
      • changedInput schema / properties / section / maxLength
        Previous value: -300New value: +200
    • Addedlist_freelance_platforms
    • Changedsearch_firms1 field changed
      • addedInput schema / properties / module
        Added value: +{
        +  "description": "Restrict to organisations with a DECLARED link to one SAP module/product code (UPPERCASE snake_case, e.g. DATASPHERE, BDC, SAC, BW4HANA, S4HANA, JOULE — case-insensitive on input). The declared links are structured data, far more selective than free text: `count_firms_by {by:\"module\"}` gives the live vocabulary with counts. A minority of the directory declares any module at all, so this filter finds the DECLARED specialists — absence from the result means no declared link, never that the firm does not work on the module.",
        +  "type": "string"
        +}
    • Changedsearch_news1 field changed
      • changedInput schema / properties / category / description
        Previous value: -"Category code, matched case-insensitively. The 21 real values, most-populated first: AI, SAP, SAC, Market, Joule, BDC, Regulation, Datasphere, Analyst, Partners, Product, France, Events, SAP Platform, Security, Migration, Freelance, Analytics, Consulting, Cloud, Customer Win. Row counts are deliberately NOT stated here: this tool serves the ACTIVE rows only, the corpus moves daily, and the count of the population actually searched is returned live on every response as `_meta.tranche_row_count`. One bucket needs a warning. 'SAC' is the noisiest label in this corpus because the acronym collides with unrelated ones — Windows 'Smart App Control', and the surname 'Sacks'. Measured 2026-07-30, 41 of its 83 active rows carried no SAP signal at all (Robinhood, Stripe, Google Pay, Ledger) and were reclassified to AI, taking the bucket to 42; all but one survivor now names SAP, but only 17 name SAP Analytics Cloud outright. For genuine SAC product news, pair category:'SAC' with query:'analytics cloud'."New value: +"Category code, matched case-insensitively. The live vocabulary is NOT written here — read `_meta.available_categories` on any response: every category label this corpus holds right now, with its active-row count, counted at query time. A written list held 21 values while the corpus held 22. One bucket needs a warning. 'SAC' is the noisiest label in this corpus because the acronym collides with unrelated ones — Windows 'Smart App Control', and the surname 'Sacks'. A 2026-07-30 cleanup reclassified half that bucket to AI for carrying no SAP signal at all (Robinhood, Stripe, Google Pay); the collision pressure is structural and the bucket has kept growing since. For genuine SAC product news, pair category:'SAC' with query:'analytics cloud'."
  12. 1 tool update
    • Changedfind_opportunities1 field changed
      • changedInput schema / properties / employment_type / description
        Previous value: -"Restrict to one engagement type. THE RADAR IS MOSTLY PERMANENT (2,172 permanent, 176 freelance, 33 contract of 2,381 active rows, measured 2026-08-10), so a freelance or contract question answered off an unfiltered page is answered with salaried jobs. The promoted leg stores contract_type NULL on all 109 of its active rows, so any value here drops that leg by predicate — `_meta.note` says so."New value: +"Restrict to one engagement type. THE RADAR IS MOSTLY PERMANENT, so a freelance or contract question answered off an unfiltered page is answered with salaried jobs. For the actual split, make the filtered call and read `_meta.tranche_total_row_count` — it is counted at query time. The promoted leg stores contract_type NULL on every one of its active rows, so any value here drops that leg by predicate — `_meta.note` says so."
  13. 1 tool update
    • Addedquery_knowledge_graph
  14. 1 tool update
    • Changedfind_opportunities5 fields changed
      • addedInput schema / properties / remote_mode
        Added value: +{
        +  "description": "Restrict to one work-location policy: `remote`, `hybrid` or `onsite`. READ THIS BEFORE ANSWERING A REMOTE QUESTION: the radar declares no policy at all on 1,409 of its 2,661 active rows (measured 2026-08-14), and an undeclared row is NOT an on-site row — it is a posting that does not say. Any value here therefore sets those rows aside rather than classifying them, exactly as the site's own filter does, and `_meta.remote_mode_undeclared` reports how many were set aside. The promoted leg carries its own `remote_mode` column and is filtered by the same predicate. Read `_meta.available_remote_modes` for the live spread before assuming a value exists.",
        +  "type": "string"
        +}
      • addedOutput schema / properties / rows / items / properties / country_code
        Added value: +{
        +  "description": "ISO-3166-1 alpha-2 of the posting's own country. Null on almost all of the promoted leg.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / rows / items / properties / posted_at
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / rows / items / properties / remote_mode
        Added value: +{
        +  "description": "The posting's declared work-location policy (`remote` / `hybrid` / `onsite`). NULL means the posting does not say — never read it as on-site.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / rows / items / properties / seniority
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}

Frequently Asked Questions

Discussions

No comments yet. Be the first to start the discussion!

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    B
    maintenance
    AI access to SAP ECC and on-premise S/4HANA: natural-language table queries, ABAP source reads, where-used lookups, syntax checks, dumps and background jobs — served by an in-system ABAP add-on behind a single ICF node, with no BTP, ADT or RISE dependency. Every call runs under the calling user's own SAP authorizations and is audit-logged inside the customer's system; the connector is MIT-licensed
    2
    8
    3
    MIT
  • F
    license
    Not graded
    quality
    B
    maintenance
    Enables SAP Value Advisors to instantly generate prioritized recommendations for client pain points by leveraging historical cases, Next-Gen Ariba features, and VLM KPIs, producing structured analysis cards with effort, timeline, and benefits.
    -
  • A
    license
    A
    quality
    B
    maintenance
    Enables AI agents to find, score, and monitor government contract opportunities across UK, EU, and US with AI-powered relevance scoring.
    2
    169
    MIT
Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

TDQS

A4.5/5.0
Disambiguation4/5

Each tool targets a distinct resource or action (firms, clients, modules, concepts, studies, opportunities, rates, news, knowledge graph). Some pairs like find_academy_modules vs list_sap_modules and find_sap_clients vs search_firms could be confused, but the descriptions explicitly disambiguate them.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern in lowercase snake_case: find_, get_, list_, search_, count_, query_. Verbs are consistently used for their roles (find/search for querying, get for single items, list for enumerations), with no mixed casing or style.

Tool Count4/5

20 tools is on the higher end, but the server covers a broad domain with multiple distinct datasets (directory, clients, academy, concepts, studies, opportunities, rates, news, graph). Each tool earns its place, though the count is slightly above the ideal 3-15 range.

Completeness5/5

The domain is a read-only intelligence platform, and it provides search/list and get operations for every major entity: firms, clients, modules, concepts, studies, and opportunities. The knowledge graph adds relational querying, and rates/news are covered. No essential lifecycle operations are missing for the stated purpose.