惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

罗磊的独立博客
Apple Machine Learning Research
Apple Machine Learning Research
The Cloudflare Blog
WordPress大学
WordPress大学
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 叶小钗
博客园 - 聂微东
阮一峰的网络日志
阮一峰的网络日志
腾讯CDC
博客园 - 三生石上(FineUI控件)
V
V2EX
有赞技术团队
有赞技术团队
V
Visual Studio Blog
小众软件
小众软件
Jina AI
Jina AI
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - Franky
量子位
T
Tailwind CSS Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
P
Palo Alto Networks Blog
Cisco Talos Blog
Cisco Talos Blog
I
Intezer
Project Zero
Project Zero
A
Arctic Wolf
P
Privacy International News Feed
V
Vulnerabilities – Threatpost
L
Lohrmann on Cybersecurity
S
Securelist
C
Cybersecurity and Infrastructure Security Agency CISA
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
Tor Project blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
S
Security @ Cisco Blogs
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Google DeepMind News
Google DeepMind News
N
News and Events Feed by Topic
TaoSecurity Blog
TaoSecurity Blog
L
LINUX DO - 热门话题
G
GRAHAM CLULEY
Help Net Security
Help Net Security
N
News | PayPal Newsroom
W
WeLiveSecurity
G
Google Developers Blog
Microsoft Security Blog
Microsoft Security Blog
Engineering at Meta
Engineering at Meta
MongoDB | Blog
MongoDB | Blog
C
Check Point Blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Why we kept named MCP tools despite a 96% token saving
Bryan Clark · 2026-06-24 · via DEV Community

The boat-agent stack here runs on a prime directive: if there's something usable out there, improve it; build our own only as a last resort. So when we needed a SignalK MCP server, the honest first move wasn't to write one — it was to evaluate the one that already exists.

VesselSense/signalk-mcp-server (TypeScript, MIT) is good work. It exposes SignalK to an agent through a single execute_code tool: the model writes JavaScript, the server runs it in a sandboxed V8 isolate (isolated-vm), and only the result comes back. Its README claims a 90–96% token reduction versus traditional named MCP tools — 2,000 tokens down to 120 for a vessel-state query, 13,000 down to 300 for a multi-call workflow. Those numbers are plausible, and they line up with the broader industry result that code execution beats tool-calling on token efficiency for complex multi-step work.

We read it, ran the numbers against our own agent, and kept our discrete-named-tool signalk-mcp anyway — then harvested three of VesselSense's ideas into our roadmap. This post is that evaluation: the two philosophies, why the obvious-sounding win doesn't bind for a voice-first agent, and a decision framework you can reuse before you adopt-or-build your own MCP server.

This is a design-reasoning post, not a debugging saga, but it maps to the same arc: a question, the dead-end that looks like an obvious yes, and the call that actually held.

The question

Two SignalK MCP servers, two genuinely different designs:

VesselSense/signalk-mcp-server      sailingnaturali/signalk-mcp
─────────────────────────────       ───────────────────────────
one tool: execute_code              discrete named tools:
  → agent writes JavaScript           read_sensor(path)
  → runs in a V8 isolate              battery_state(bank)
  → queries SignalK, returns          depth_state()
    only the result                   get_route()
                                      get_local_time()
TypeScript / Node + isolated-vm       list_paths(prefix)
claims 90–96% fewer tokens            get_active_alarms()
                                    Python, end-to-end

The adopt-vs-keep question: does the token-efficiency win bind for our agent? If it does, adopting beats maintaining a second server. If it doesn't, the directive doesn't compel adoption — it compels building the right thing for the target.

The target matters more than anything else here. Our design anchor is a voice-first agent on a small local model — Hermes 3 8B driving a text-to-speech front end on a boat. Not a frontier model in a chat window. That single fact decides the whole evaluation.

Two valid philosophies, different targets

execute_code is clever, and the token math is real. When an agent needs to fetch every AIS target, filter to the close ones, sort by CPA, and format a summary, the named-tool pattern pays the full input+output token cost of every intermediate call — the model emits a structured call, the whole result flows back into context, repeat. Code execution collapses that into one script and one aggregated result. On a frontier model doing complex, multi-step marine queries, the 90–96% claim is believable.

But the saving is paid for in one currency: the agent must reliably write correct code. That's a cheap price for a frontier model and an expensive one for an 8B. The capability gap here is not subtle. From the field:

Small models like llama3.2:3b and llama3.1:8b support tool calling specs but fail inconsistently in practice, especially on sequential or multi-entity commands… Tool calling is the biggest capability gap between local and cloud models — the plumbing exists but model reliability doesn't yet.

If a small model is shaky at emitting a structured tool call, asking it to emit correct JavaScript is strictly harder. execute_code doesn't reduce the model's burden for our agent — it raises it. The token budget was never our binding constraint; reliability is.

So the comparison isn't "which design is better." It's:

  • Frontier model, complex queries, token budget is the constraintexecute_code wins. Adopt VesselSense.
  • Small local model, voice front end, reliability is the constraint → discrete named tools win. A named tool with one argument — battery_state("house") — is the most robust thing you can hand an 8B. It cannot get the JavaScript wrong because there is no JavaScript.

Both are correct. They're tuned for different agents.

Why the token win doesn't bind #1: the speech contract

Here's the part the token comparison silently drops. Our tools don't return raw SignalK — every value carries a TTS-safe display string the agent can speak verbatim. SignalK stores everything in SI units and terse codes; a TTS engine mispronounces all of it. Our response contract makes the spoken form a first-class field:

{
  "path": "environment.wind.speedApparent",
  "value": 8.5,
  "display": "16.5 knots",
  "unit": "knots",
  "timestamp": "2026-05-18T00:00:00Z"
}

{
  "bank": "house",
  "soc_fraction": 0.68,
  "voltage": 12.84,
  "current": -8.2,
  "display": "68 percent, 12.8 volts, 8.2 amps discharging",
  "timestamp": "2026-05-14T18:00:00Z"
}

The rules behind that display: spelled-out units ("knots", never "kn"), spelled-out compass points ("North-East", never "NE"), cardinal-name lat/lon, no °T suffix a TTS engine reads as letters, no ISO timestamp narrated digit by digit. Position is the instructive case — the raw {latitude, longitude} dict stays in value for programmatic use, but the agent speaks display, never the raw pair.

execute_code returns whatever the agent's script returns — raw SignalK. That pushes all of this formatting onto the agent, which is exactly the layer that fails on a small model. We've written before about why formatting belongs in the tool layer, not the prompt: a prompt rule is advisory and leaks; the tool response is deterministic. execute_code is the maximal version of pushing formatting onto the model — it doesn't just leak the contract, it has no place to put one. For a voice-first agent that's disqualifying, and no token saving buys it back.

Why the token win doesn't bind #2: 404-as-null and the circuit breaker

The second structural reason is error handling. A SignalK 404 is not an error in our client — it means the vessel doesn't publish that path (no such sensor, or a guessed path). The client returns a clean null instead of raising:

# read_sensor on a path the vessel doesn't publish
{ "path": "navigation.headingTrue", "value": None, "display": None,
  "unit": None, "timestamp": None }   # not an exception

This is deliberate, and it's a small-model lesson. Agent runtimes commonly run a per-tool circuit breaker — ours (Hermes) trips after 3 consecutive same-tool failures. A small model fanning out across guessed paths on an unfamiliar vessel — headingTrue, headingMagnetic, courseOverGroundTrue — on a boat with no compass would generate a burst of 404s, each counted as a tool failure. That trips the breaker and blocks the valid reads queued behind it. Returning a clean null keeps a missing path a successful call, so the breaker never trips on absence.

Under execute_code, a missing path is whatever the SignalK client throws inside the isolate, and the agent has to catch and interpret it in code it wrote — on a model that's already at the edge of writing correct code. The named-tool design makes "this sensor doesn't exist" a normal, non-fatal result by construction. That's a reusable lesson for any MCP author building for small models: decide what your tool does on absence, and make absence a success, not a fault.

The coverage audit: receipts, not vibes

A "prefer adopting" directive still demands a coverage audit — does the existing tool actually do the job, or does it leave the work to the agent? We diffed feature by feature.

Active alarms. VesselSense's getActiveAlarms() returns alarms with their state and leaves filtering and sorting to client-side code in the isolate — its own example filters with a.state === "alarm" || a.state === "emergency" in agent-written JS. Normal-state notifications stay in the result; there's no severity ordering. Ours does that work in the tool, so the agent never writes a filter:

# signalk-mcp: get_active_alarms does the filtering + ordering in-tool
_ALARM_SEVERITY = {"emergency": 0, "alarm": 1, "warn": 2, "alert": 3}
_INACTIVE_STATES = {"normal", "nominal"}

# normal/nominal dropped; rows sorted worst-severity-first
rows.sort(key=lambda r: _ALARM_SEVERITY.get(r["state"], 99))

It also strips the notifications. prefix off each path so the result feeds straight into our downstream alarm-explanation tooling. Worst-first, normal filtered out, paths cleaned — no JavaScript required.

Missing tools. VesselSense's getVesselState dumps the SignalK tree and lets the agent dig. We have no equivalent dump tool — instead we ship purpose-built ones the dump would otherwise require the agent to assemble: battery_state, depth_state (under-keel clearance first, so the agent answers "how close are we to running aground?" without draft math), get_route, get_local_time (GPS-aware timezone). Each returns the speakable, contract-compliant answer directly.

The pattern across the audit: VesselSense pushes the last mile of work — filter, sort, format, interpret — into agent-written code, which is exactly the work a small model is worst at. That's not a flaw in VesselSense; it's the correct division of labor for a frontier model. It's the wrong division for ours.

The maturity audit (still matters under "prefer adopting")

Adopting code means inheriting its maintenance. The public signals on the upstream repo, at evaluation time:

last push      2025-11-26   (6+ months dormant)
stars          8
MCP SDK        @modelcontextprotocol/sdk pinned ^0.5.0  (two majors behind)
license        MIT in package.json; no LICENSE file in the repo tree
runtime        Node + native isolated-vm  (our stack is Python end to end)

None of these is damning on its own. Together they say: adopting means taking on a dormant codebase, in a second language runtime, with a native sandbox dependency (isolated-vm), pinned to an MCP SDK two majors back — to gain a token efficiency our agent doesn't spend. The maintenance cost is real and the benefit doesn't land. That's the directive not compelling adoption, on the merits.

The decision framework (reuse this)

Strip out the marine specifics and this is a general adopt-vs-build checklist for an MCP server:

  1. Name the target agent first. Frontier model or small/local? Chat or voice/TTS? The target decides which currency you're optimizing — tokens or reliability. Most disagreements about MCP design are actually disagreements about the target.
  2. Identify the binding constraint. Is your token budget the wall, or is model reliability the wall? execute_code trades reliability for tokens. Only adopt it if tokens are the wall.
  3. Check who does the last mile. Does the existing tool filter/sort/format/interpret, or hand that to agent-written code? For a small model, every line of last-mile code you push to the agent is a failure mode.
  4. Check the output contract. If output is consumed by something with formatting needs (TTS, a strict downstream parser), a raw-data tool externalizes that contract onto the model. Named tools can bake it in.
  5. Decide what absence means. Make "the thing isn't there" a successful result, not an exception — especially behind a circuit breaker, especially for a model that guesses paths.
  6. Run the maturity audit anyway. Dormancy, pinned-back SDKs, a second runtime, native deps. Adopting is inheriting.
  7. Steelman, then harvest. If you build your own, still mine the alternative for ideas. A "no" on the architecture isn't a "no" on every idea in it.

The honest steelman, and what we harvested

We didn't dismiss VesselSense — we mined it. Three ideas went straight onto our roadmap from reading their server:

  • get_active_alarms — shipped (v0.5.0). Active notifications, worst-severity-first, normal filtered out, paths prefix-stripped for downstream tooling.
  • list_paths — shipped (v0.3.0). Path discovery so the agent can explore an unfamiliar SignalK tree without guessing — and without tripping the 404 circuit breaker.
  • AIS targets — open on the roadmap, with the same speech contract ("cargo vessel, 1.2 nautical miles, bearing North-East").

And execute_code stays on the watch-list. The day we add a cloud-reasoning layer — a frontier model doing complex multi-step marine analysis where the token budget genuinely is the wall — code execution is the right tool and we'll reach for it. The evaluation isn't "named tools are better." It's "named tools are better for this agent, and here's exactly when that flips."

If you're driving SignalK with a frontier model and want maximum query flexibility, VesselSense is likely the better choice — it's well-built and worth your time. If you want simple, reliable, speakable tools for a local or voice-first agent, that's the niche our server fills.

Close

This came out of building an AI ops layer for an all-electric charter catamaran, where the agent runs on a small local model behind a Home Assistant voice front end, and "what's our battery?" has to come back as something a human can hear — reliably, every time, on an 8B. The server is open source, and so is the comparison baked into its README: github.com/sailingnaturali/signalk-mcp. Go read VesselSense too — different target, good work.