Skip to content

feat(mcp): send orchestration hints via InitializeResult.instructions#27

Open
kLOsk wants to merge 1 commit intomainfrom
feat/mcp-instructions-orchestration
Open

feat(mcp): send orchestration hints via InitializeResult.instructions#27
kLOsk wants to merge 1 commit intomainfrom
feat/mcp-instructions-orchestration

Conversation

@kLOsk
Copy link
Copy Markdown
Owner

@kLOsk kLOsk commented Apr 28, 2026

Summary

The MCP spec defines serverInfo.instructions as a free-text field servers return during the initialize handshake. Clients that honor it (Claude Code, VSCode Copilot Chat, Goose, Cursor v1.6+, per the published support landscape) inject it into the LLM's system prompt automatically — exactly the behavior we built adloop install-rules for, except it works at the protocol layer for every MCP-compliant client without any install step.

This PR replaces AdLoop's existing 3-sentence instructions placeholder with a compact ~500-token orchestration hint covering the absolute must-knows.

Why

Discovered during research for #25's upstream-feedback question. Before this change, every MCP client connecting to AdLoop got a near-useless instructions field:

"AdLoop connects Google Ads and Google Analytics (GA4) data to your codebase. Use the read tools to analyze performance, and the write tools (with safety confirmation) to manage campaigns."

That's a description, not orchestration guidance. Clients honoring the field were dutifully injecting it into the system prompt and getting nothing useful out of it. Meanwhile we'd been building elaborate machinery (workspace rules, install-rules, manual paste flows) to deliver orchestration content out-of-band — when the protocol already had the right slot for it.

Design choice: compact, not full ruleset

I deliberately did NOT pipe the entire 50KB rules file through instructions. The spec describes it as:

"This can be used by clients to improve the LLM's understanding of available tools, resources, etc. It can be thought of like a 'hint' to the model."

"Hint" is the operative word. Sending an entire user manual through every initialize handshake would be technically valid but stylistically wrong, and would tax every session connecting to AdLoop — including read-only sessions that don't even use any write tools.

So _build_orchestration_instructions() returns the safety must-knows only:

  • Two-step write pattern (preview -> confirm_and_apply, never skip)
  • dry_run=true defaults
  • max_daily_budget cap, PAUSED-by-default for new campaigns/RSAs
  • BROAD match requires Smart Bidding (refuse on MANUAL_CPC)
  • Verify final_url before creating ads/sitelinks
  • Don't throw budget at zero-conversion campaigns
  • GDPR consent gap is normal in EU (clicks > sessions ≠ tracking bug)
  • Geo + language targeting mandatory on new campaigns
  • Pointer at where the full ruleset lives

The full ruleset stays canonical in .cursor/rules/adloop.mdc, .claude/rules/adloop.md, and (after adloop install-rules) ~/.claude/CLAUDE.md. The compact hint is the floor; full rules are the ceiling.

A test caps the field at <5KB so we don't accidentally regress to dumping the whole thing.

Implications for install-rules

install-rules is still valuable but its scope narrows in practice:

Feature Where it comes from now
Compact safety must-knows MCP instructions field (this PR)
Full orchestration rules .cursor/rules/, .claude/rules/, or ~/.claude/CLAUDE.md (via install-rules)
Slash commands (adloop-*) install-rules only
Coverage for Cowork (and any client not honoring instructions) install-rules only

This is a healthy split. The protocol-native path covers the most important things automatically; the install path provides the long-tail orchestration patterns and slash commands.

Tests

6 new in tests/test_server.py::TestOrchestrationInstructions:

  • Coverage tests: safety essentials (preview, plan_id, dry_run), pre-write checks (BROAD/Smart Bidding, final_url, conversions), data literacy (GDPR, geo/language).
  • Pointer test: hint references the full ruleset (install-rules, adloop.mdc, or CLAUDE.md).
  • Compactness cap: hard <5KB assertion to guard against regression.
  • Wiring test: confirms FastMCP instance has the instructions string attached and it's non-trivial.

Full suite: 190 passed (184 baseline + 6 new).

Test plan

  • uv run pytest — 190 pass
  • Reviewer: connect AdLoop to Claude Code and check the system prompt for the new hint (debug log or --debug mode shows injected instructions)
  • Reviewer (optional): test against Cowork to confirm whether it honors instructions — if not, that's the right scope for any future Anthropic feedback ("Cowork should honor MCP-spec serverInfo.instructions, the way Claude Code already does")

Out of scope

  • Making the instructions field configurable (e.g. safety.instructions_mode: compact|minimal). Doable as a follow-up if anyone hits the cap; for now compact is small enough that opt-out probably isn't needed.
  • Updating the install-rules CLI to detect that compact instructions are now coming through the handshake and skip its own inline embed. Premature optimisation — the duplication is harmless.

The MCP spec defines `serverInfo.instructions` as a free-text field servers
return during the initialize handshake. Clients that honor it (Claude Code,
VSCode Copilot, Goose, Cursor v1.6+) inject it into the LLM's system
prompt automatically.

Before this change, AdLoop set `instructions` to a 3-sentence placeholder.
This commit replaces it with a compact ~500-token orchestration hint
covering the must-knows that prevent the most expensive mistakes:

- Two-step write pattern (preview -> confirm_and_apply, never skip).
- dry_run=true defaults, require_dry_run config override.
- max_daily_budget cap, PAUSED-by-default for new campaigns/RSAs.
- BROAD match requires Smart Bidding (refuse on MANUAL_CPC).
- Verify final_url before creating ads/sitelinks.
- Don't throw budget at zero-conversion campaigns; fix tracking first.
- GDPR consent gap is normal in EU (clicks > sessions != tracking bug).
- Geo + language targeting mandatory on new campaigns.
- Pointer to the full ruleset (.cursor/rules/adloop.mdc / install-rules).

The full ruleset stays canonical in the existing locations; `instructions`
is intentionally compact because the spec describes it as "a hint to the
model" rather than a place to ship 50KB user manuals. Tests cap the field
at <5KB so we don't accidentally regress.

Net effect: every MCP client that honors `instructions` (most major
implementations, per spec compliance) now gets the safety must-knows
automatically — no install-rules step needed for the bare-minimum
orchestration. install-rules remains valuable for full rules + slash
commands, especially for clients like Cowork that may not yet honor
the field.

6 new tests verify content coverage, compactness cap, and wiring through
to the FastMCP instance. Full suite: 190 passed (184 + 6 new).
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