feat(mcp): send orchestration hints via InitializeResult.instructions#27
Open
feat(mcp): send orchestration hints via InitializeResult.instructions#27
Conversation
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).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The MCP spec defines
serverInfo.instructionsas 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 builtadloop install-rulesfor, except it works at the protocol layer for every MCP-compliant client without any install step.This PR replaces AdLoop's existing 3-sentence
instructionsplaceholder 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:
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:"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:dry_run=truedefaultsmax_daily_budgetcap, PAUSED-by-default for new campaigns/RSAsfinal_urlbefore creating ads/sitelinksThe full ruleset stays canonical in
.cursor/rules/adloop.mdc,.claude/rules/adloop.md, and (afteradloop 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-rulesinstall-rulesis still valuable but its scope narrows in practice:instructionsfield (this PR).cursor/rules/,.claude/rules/, or~/.claude/CLAUDE.md(via install-rules)adloop-*)instructions)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:install-rules,adloop.mdc, orCLAUDE.md).Full suite: 190 passed (184 baseline + 6 new).
Test plan
uv run pytest— 190 pass--debugmode shows injected instructions)instructions— if not, that's the right scope for any future Anthropic feedback ("Cowork should honor MCP-specserverInfo.instructions, the way Claude Code already does")Out of scope
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.