|
| 1 | +You’re right to push on this. **Typing `browser-echo://logs` shouldn’t be required** for beginners. Two things are true at once: |
| 2 | + |
| 3 | +1. Most MCP hosts *can* call your resource automatically from plain English (they read your resource & tool **names/descriptions** and decide what to invoke). |
| 4 | +2. In practice, **discoverability improves a lot** if you give the model a small, NL‑friendly *tool alias* and a couple of “quick actions” (prompts). |
| 5 | + |
| 6 | +Below is a practical plan so “check frontend logs” *just works* in Cursor and Claude—**no URIs required**—while still keeping your “fewer tools” philosophy. |
| 7 | + |
| 8 | +--- |
| 9 | + |
| 10 | +## The fix: a *hybrid* interface (Resource + one NL tool + optional prompts) |
| 11 | + |
| 12 | +### 1) Keep the resource |
| 13 | + |
| 14 | +* It’s perfect for streaming/reading the whole buffer and for structured returns. |
| 15 | +* Keep `browser-echo://logs` and (optionally) `browser-echo://logs/{session}`. |
| 16 | + |
| 17 | +### 2) Add **one** tool alias: `get_logs` |
| 18 | + |
| 19 | +* Purpose: make intent resolution trivial. The LLM sees a tool literally named “get logs” with a description like “Fetch recent **frontend browser console logs** (errors/warnings/info) from the running dev app.” |
| 20 | +* Inputs (all optional): |
| 21 | + |
| 22 | + ```ts |
| 23 | + { |
| 24 | + level?: ('error'|'warn'|'info'|'log'|'debug')[]; // default: all |
| 25 | + session?: string; // 8-char id prefix |
| 26 | + includeStack?: boolean; // default: true |
| 27 | + limit?: number; // e.g., 200 |
| 28 | + contains?: string; // substring filter |
| 29 | + sinceMs?: number; // e.g., Date.now()-300000 |
| 30 | + } |
| 31 | + ``` |
| 32 | +* Output: **both** a text summary *and* a JSON list of entries (models love JSON for follow-up reasoning). |
| 33 | + |
| 34 | +> Why one tool? It acts as the **NLP entry point**. The planner can easily decide: “User said ‘check error logs’ → call `get_logs` with `level:['error','warn']`.” You still have just **two tools total** (`get_logs`, `clear_logs`). |
| 35 | +
|
| 36 | +### 3) (Optional) Register **prompt templates** (“Quick actions”) |
| 37 | + |
| 38 | +* “Check frontend logs (last 2 minutes)” |
| 39 | +* “Check only errors & warnings” |
| 40 | +* “Clear and capture new logs for 30s” |
| 41 | +* Many hosts surface these as clickable actions or as suggestions when users type. |
| 42 | + |
| 43 | +*(If your SDK version exposes a `registerPrompt` API, use it. If not, you still get most of the win from the `get_logs` tool naming/description.)* |
| 44 | + |
| 45 | +### 4) Tune **names & descriptions** (this matters a lot) |
| 46 | + |
| 47 | +* Server name: **“Browser Echo (Frontend Logs)”** |
| 48 | +* Resource title: **“Frontend Browser Console Logs”** |
| 49 | +* Resource description: include keywords beginners use: *frontend logs, console errors, warnings, React/Next hydration, network failures*. |
| 50 | +* Tool description: repeat those keywords and examples (e.g., “show hydration errors, network failures, React warnings”). |
| 51 | + |
| 52 | +### 5) Return **JSON alongside text** |
| 53 | + |
| 54 | +* When resolving the resource and the tool, include **two contents**: a human‑readable plain text block (like your terminal format) **and** a JSON array of entries. |
| 55 | +* This lets the model filter/sort programmatically without fighting text parsing. |
| 56 | + |
| 57 | +--- |
| 58 | + |
| 59 | +## What this changes in the user experience |
| 60 | + |
| 61 | +* In **Cursor** or **Claude Code**, users can simply write: |
| 62 | + |
| 63 | + * “**Check frontend logs**” |
| 64 | + * “**Show only errors from the last 2 minutes**” |
| 65 | + * “**Find hydration mismatch warnings**” |
| 66 | +* The IDE’s planner will typically call **`get_logs`** with sensible args. If it doesn’t, the descriptive names make it very likely on the second try. |
| 67 | +* Power users can still say “fetch the logs resource” or click a quick action. |
| 68 | + |
| 69 | +--- |
| 70 | + |
| 71 | +## Minimal code sketch (drop‑in to your server snippet) |
| 72 | + |
| 73 | +Add the `get_logs` tool *next to* your existing `clear_logs` registration: |
| 74 | + |
| 75 | +```ts |
| 76 | +server.registerTool( |
| 77 | + 'get_logs', |
| 78 | + { |
| 79 | + title: 'Get Frontend Browser Logs', |
| 80 | + description: |
| 81 | + 'Fetch recent frontend browser console logs (errors/warnings/info). ' + |
| 82 | + 'Use this when the user asks to check frontend logs, errors, hydration issues, or network failures.', |
| 83 | + inputSchema: { |
| 84 | + type: 'object', |
| 85 | + properties: { |
| 86 | + level: { |
| 87 | + type: 'array', |
| 88 | + items: { enum: ['log','info','warn','error','debug'] }, |
| 89 | + description: 'Filter by levels' |
| 90 | + }, |
| 91 | + session: { type: 'string', description: '8-char session id prefix' }, |
| 92 | + includeStack: { type: 'boolean', default: true }, |
| 93 | + limit: { type: 'number', minimum: 1, maximum: 5000 }, |
| 94 | + contains: { type: 'string', description: 'Substring filter' }, |
| 95 | + sinceMs: { type: 'number', description: 'Only entries with time >= sinceMs' } |
| 96 | + }, |
| 97 | + additionalProperties: false |
| 98 | + } as any |
| 99 | + }, |
| 100 | + async ({ level, session, includeStack = true, limit = 1000, contains, sinceMs }) => { |
| 101 | + let items = STORE.snapshot(); |
| 102 | + if (sinceMs) items = items.filter(e => !e.time || e.time >= sinceMs); |
| 103 | + if (session) items = items.filter(e => (e.sessionId||'').slice(0,8) === session.slice(0,8)); |
| 104 | + if (level?.length) items = items.filter(e => level.includes(e.level)); |
| 105 | + if (contains) items = items.filter(e => (e.text||'').includes(contains)); |
| 106 | + if (!includeStack) items = items.map(e => ({ ...e, stack: '' })); |
| 107 | + if (limit && items.length > limit) items = items.slice(-limit); |
| 108 | + |
| 109 | + // Text view (like terminal but no ANSI) |
| 110 | + const text = items.map(e => { |
| 111 | + const sid = (e.sessionId||'anon').slice(0,8); |
| 112 | + const lvl = (e.level||'log').toUpperCase(); |
| 113 | + const tag = e.tag || '[browser]'; |
| 114 | + let line = `${tag} [${sid}] ${lvl}: ${e.text}`; |
| 115 | + if (e.source) line += ` (${e.source})`; |
| 116 | + if (includeStack && e.stack?.trim()) { |
| 117 | + const indented = e.stack.split(/\r?\n/g).map(l => l ? ` ${l}` : l).join('\n'); |
| 118 | + return `${line}\n${indented}`; |
| 119 | + } |
| 120 | + return line; |
| 121 | + }).join('\n'); |
| 122 | + |
| 123 | + return { |
| 124 | + content: [ |
| 125 | + { type: 'text', text }, |
| 126 | + { type: 'json', json: { entries: items } } |
| 127 | + ] |
| 128 | + }; |
| 129 | + } |
| 130 | +); |
| 131 | +``` |
| 132 | + |
| 133 | +> That’s it: one tool. Keep your existing resource; now beginners never need to type its URI. |
| 134 | +
|
| 135 | +*(If you want “quick actions”, add prompt registrations that internally call `get_logs` with common defaults. If your SDK version doesn’t expose prompts, you can simulate by adding small, well‑named tools like `get_error_logs` that just call `get_logs` internally with `level:['error','warn']`. Still a tiny tool count.)* |
| 136 | + |
| 137 | +--- |
| 138 | + |
| 139 | +## Real‑world flows that become “NLP‑easy” |
| 140 | + |
| 141 | +* “**Check frontend logs**” → calls `get_logs()` with defaults → shows text + JSON, model summarizes. |
| 142 | +* “**Only errors since I refreshed**” → `get_logs({ level:['error','warn'], sinceMs: <timestamp of refresh> })` |
| 143 | +* “**Hydration issues**” → `get_logs({ contains:'Hydration', level:['error','warn'] })` |
| 144 | +* “**Focus my current tab**” → model lists sessions then calls `get_logs({ session:'a1b2c3d4' })` |
| 145 | +* “**Start a clean capture, then I’ll repro**” → `clear_logs()` → wait → `get_logs({ level:['error','warn'] })` |
| 146 | + |
| 147 | +--- |
| 148 | + |
| 149 | +## Do you *have* to keep the resource? |
| 150 | + |
| 151 | +No, but I recommend it: |
| 152 | + |
| 153 | +* Tools improve **intent mapping**. |
| 154 | +* Resources are great for **streaming and bulk reads** and are a natural fit for “log feed”. |
| 155 | +* Together, they maximize compatibility and robustness across hosts. |
| 156 | + |
| 157 | +--- |
| 158 | + |
| 159 | +### Bottom line |
| 160 | + |
| 161 | +You didn’t misunderstand—**URIs are optional** in day‑to‑day use. Make it NLP‑easy by adding **one** `get_logs` tool (plus good naming/descriptions, optional quick prompts). Keep the resource as the high‑throughput backbone. That strikes the balance between **beginner‑friendly UX** and **minimal surface area**, while staying aligned with “fewer tools” best practices. |
| 162 | + |
| 163 | + |
| 164 | +what about clear_logs. should we keep this or not? |
| 165 | + |
| 166 | +**Keep `clear_logs`.** |
| 167 | +It’s one of the few side‑effect tools that’s *worth having* even under a “fewer tools” philosophy. It makes “fresh capture → reproduce → analyze” dead‑simple for the model and for beginners—no parameters, no URIs—just *“clear the logs, I’ll repro, then check errors.”* |
| 168 | + |
| 169 | +Below is the why, when, and how—plus a safe refinement you can add (optional) without increasing tool count. |
| 170 | + |
| 171 | +--- |
| 172 | + |
| 173 | +## Why keep it? |
| 174 | + |
| 175 | +* **Deterministic captures:** Guarantees the next fetch contains *only* the logs from your repro, avoiding stale noise. This is hard to get right if you rely solely on “since time” filters—the planner may forget to pass them. |
| 176 | +* **Shorter, cleaner context:** Fewer irrelevant lines → cheaper + more accurate reasoning/summarization. |
| 177 | +* **Beginner‑friendly NLP:** Users can say *“start a clean capture”* and the assistant calls a single, obvious tool with no arguments. |
| 178 | +* **Side‑effect separation:** Getting data (resource / `get_logs`) vs. performing an action (`clear_logs`) are cleanly distinguished for the agent. |
| 179 | + |
| 180 | +You’ll still have just **two** tools total: |
| 181 | + |
| 182 | +1. `get_logs` (NLP entry point for fetching) |
| 183 | +2. `clear_logs` (fresh capture) |
| 184 | + |
| 185 | +That’s minimal, and it maps perfectly to how people actually debug. |
| 186 | + |
| 187 | +--- |
| 188 | + |
| 189 | +## When to use it (practical patterns) |
| 190 | + |
| 191 | +* **Repro loop:** |
| 192 | + *“Run `clear_logs`. I’ll trigger the bug. In 30s, fetch logs and summarize only errors/warnings with the top stack + source.”* |
| 193 | +* **Fix verification:** |
| 194 | + *“Clear logs, reload, then confirm the previous hydration error is gone; if anything new appears, list it.”* |
| 195 | +* **Race conditions / intermittent failures:** |
| 196 | + *“Clear logs, I’ll click around for 60s. Then group errors by stack top and propose a guard.”* |
| 197 | +* **Per‑session focus:** |
| 198 | + *“Clear logs for my session `ab12cd34`, I’ll repro on that tab, then fetch only that session’s logs.”* (see refinement below) |
| 199 | + |
| 200 | +--- |
| 201 | + |
| 202 | +## What if we removed it? |
| 203 | + |
| 204 | +You *can* approximate with `get_logs({ sinceMs })`, but: |
| 205 | + |
| 206 | +* The assistant might omit or mis‑set `sinceMs`. |
| 207 | +* Other background logs (extensions, other tabs) can still creep in. |
| 208 | +* New users don’t know to ask for a time‑window; “clear and check” is intuitive. |
| 209 | + |
| 210 | +In practice, **`clear_logs` saves mistakes** and reduces friction. |
| 211 | + |
| 212 | +--- |
| 213 | + |
| 214 | +## Recommended refinement (optional, still one tool) |
| 215 | + |
| 216 | +Keep the same tool name, but give it **gentle scoping** so it’s safer and more flexible **without** adding tools: |
| 217 | + |
| 218 | +**Inputs (optional):** |
| 219 | + |
| 220 | +* `session?: string` — clear only one 8‑char session (good for multi‑tab/dev setups) |
| 221 | +* `scope?: 'soft' | 'hard'` — default `'soft'` |
| 222 | + |
| 223 | +**Semantics:** |
| 224 | + |
| 225 | +* **soft** (default): Don’t destroy history; just set a **baseline marker** so subsequent reads only return entries *after* the clear. |
| 226 | + |
| 227 | + * Pros: O(1), reversible for auditing (file logging remains intact), less risky if multiple clients are connected. |
| 228 | +* **hard**: Drop in‑memory entries (today’s behavior). |
| 229 | + |
| 230 | + * Pros: truly empties the ring buffer; |
| 231 | + * Cons: destructive for everyone until new logs arrive. |
| 232 | + |
| 233 | +> You can implement *soft clear* by storing a per‑server (or per‑session) `baselineTimestamp`/generation and filtering on read; file logging stays untouched either way. |
| 234 | +
|
| 235 | +**Example prompt that takes advantage of it:** |
| 236 | + |
| 237 | +> “Clear logs (soft) only for session `ab12cd34`. I’ll repro now. In 20 seconds, fetch logs for that session and summarize errors with source and top stack frame.” |
| 238 | +
|
| 239 | +--- |
| 240 | + |
| 241 | +## Defaults & UX guidance |
| 242 | + |
| 243 | +* **Keep `clear_logs` in v1** exactly as you have it (global hard clear). |
| 244 | + Add the optional `session` + `scope` later; it’s backwards‑compatible. |
| 245 | +* Encourage assistants to use: |
| 246 | + |
| 247 | + 1. `clear_logs` → |
| 248 | + 2. user reproduces → |
| 249 | + 3. `get_logs({ level:['error','warn'] })` or plain “check frontend logs”. |
| 250 | +* Make sure your **resource/tool descriptions** contain NL keywords (“frontend logs”, “console errors”, “hydration”, “network failures”), so planners map user intent correctly. |
| 251 | +* **Don’t** let `clear_logs` touch file logs—keep it in‑memory only. The file log (if enabled) is your ground truth trail. |
| 252 | + |
| 253 | +--- |
| 254 | + |
| 255 | +### TL;DR |
| 256 | + |
| 257 | +Yes—**keep `clear_logs`**. It’s the single, beginner‑friendly action that makes fresh, noise‑free captures trivial and reliable. Pair it with `get_logs` as the NLP fetch tool and you’ve got a minimal, powerful, and natural workflow. If you want extra polish, add optional `{ session, scope: 'soft' | 'hard' }`—still one tool, but safer and even more useful. |
0 commit comments