Skip to content

fix(mcp): decode and show each tool's title in tools-list - #20

Merged
nictuku merged 1 commit into
mainfrom
nictuku/marseille-v4
Aug 3, 2026
Merged

nictuku merged 1 commit into
mainfrom
nictuku/marseille-v4

Conversation

@nictuku

@nictuku nictuku commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

The bug

ToolDefinition modeled only the legacy annotations.title, not the top-level title that MCP spec 2025-06-18 defines and that every Pipeboard server populates. So the field was dropped at decode time and tools-list reported 0 of 63 Google Ads tools titled against a server returning 63 of 63.

Because --json and the on-disk tools cache both re-marshal this struct, the field disappeared from those too.

Why it's not cosmetic

A title on every tool is a blocking Claude Connectors Directory submission requirement — one untitled tool auto-rejects the whole server. docs/CLAUDE_CONNECTORS_SUBMISSION_TITLE_REQUIREMENT.md and §11 of the mcp-server-requirements skill both point at pipeboard mcp tools-list as the live compliance check. Following that advice reported the entire fleet as non-compliant when it was fine — a false negative on the one check that gates a submission.

Found while verifying pipeboard.co#2326 in production.

The fix

  • Add Title to ToolDefinition; DisplayTitle() prefers it and falls back to annotations.title.
  • Surface it in all three output modes: a title column in the compact list, a Title: line under --verbose, the raw field under --json.
  • An untitled tool renders (no title!) rather than blank — on a Pipeboard server that's a real defect and should be visible.

Before / after

# before
[read]  execute_google_ads_gaql_query   Executes an arbitrary Google Ads Query Language (GAQL) query...

# after
[read]  execute_google_ads_gaql_query   Execute Google Ads GAQL Query   Executes an arbitrary Google Ads Query Lang...

Verification

make test / make lint / make build all pass, plus the locally built binary against live production:

Server --json result
meta-ads-mcp 122 tools, 122 titled
tiktok-ads-mcp 79 tools, 79 titled
google-ads-mcp 63 tools, 63 titled

Spot-checks: update_ad_url_tags → "Update Ad URL Tags", get_adsets → "Get Ad Sets", get_tiktok_adgroups → "Get TikTok Ad Groups", create_google_ads_pmax_campaign → "Create Google Ads PMax Campaign". --verbose confirmed showing the Title: line.

New tests cover the top-level round-trip (including re-marshal, which is what --json and the cache depend on), the DisplayTitle() precedence chain, and the missing-title placeholder.

ToolDefinition had no top-level Title field, so every tool decoded as untitled. tools/list showed 0/63 titled against a google-ads-mcp that returns 63/63 -- and because --json and the on-disk tools cache both re-marshal this struct, the field vanished from those too.

That is worse than cosmetic: a title is a BLOCKING Claude Connectors Directory submission requirement, and the pipeboard.co docs point at this command as the live compliance check. Following that advice reported the whole fleet as non-compliant when it was fine.

MCP puts the display name in two places: the top-level title (spec 2025-06-18, what Pipeboard servers populate) and the legacy annotations.title. Only the latter was modeled. Add the top-level field, prefer it via DisplayTitle() with the legacy one as fallback, and surface it in all three output modes -- a title column in the compact list, a Title: line under --verbose, and the raw field under --json. An untitled tool renders "(no title!)" rather than blank, since on a Pipeboard server that is a real defect worth seeing.
@nictuku
nictuku merged commit 02ac550 into main Aug 3, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant