Mcp tool intent metadata injection
Skill kjuhwa/skills-hub/skills/mcp-integration/mcp-tool-intent-metadata-injection
Inject a hidden _intent / _displayName field into every MCP tool schema via a fetch interceptor so large-response summarization and UI rendering have context that the LLM never sees.From its SKILL.md
npx -y skills add kjuhwa/skills-hub --skill mcp-tool-intent-metadata-injectionAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
3.2 KB, 606 tokens by cl100k_base, as published. Nobody here has run it
MCP tool intent-metadata injection via fetch interceptor
When to use
- Large MCP tool responses (60KB+) need automatic summarization, but summarization needs to know why the user called the tool.
- You have an LLM SDK that owns the fetch loop and you can't modify how tools are defined.
- Different UI surfaces want different display names for the same underlying tool.
How it works
- Build a shared fetch interceptor as a standalone CJS bundle (e.g. via esbuild) -
interceptor.cjs. - Inject it into every Node-based SDK subprocess with
--require=/path/to/interceptor.cjs(or--preloadunder Bun). This patchesglobalThis.fetchBEFORE any SDK captures it. - On outgoing LLM requests, walk the
tools[]array in the JSON body. For every tool, add two hidden schema fields:_intent: one-liner on why the assistant is calling it._displayName: override for UI rendering.
- The LLM fills them in as normal schema fields. Your host code reads them off the
tool_useevent. - On SSE response streaming, strip these fields back out before handing to the SDK - the SDK will reject unknown fields on Anthropic. For OpenAI, pass them through and let a downstream hook strip later (the two providers differ on strictness).
- Store per-call metadata in a
toolMetadataStorekeyed by tool-use ID; re-inject on the next request so the model still sees the history it wrote.
Example
// interceptor.cjs (preloaded into every SDK subprocess)
const origFetch = globalThis.fetch;
globalThis.fetch = async (input, init) => {
if (init?.body && isClaudeMessagesRequest(input)) {
const body = JSON.parse(init.body);
for (const t of body.tools ?? []) {
t.input_schema.properties._intent = { type: 'string', description: '1-line why' };
t.input_schema.properties._displayName = { type: 'string' };
}
init = { ...init, body: JSON.stringify(body) };
}
const res = await origFetch(input, init);
return interceptSse(res, /* strip metadata for Anthropic */);
};
Gotchas
- Bundle as CJS with bundled deps - you're
--require-ing into arbitrary Node processes that don't have yournode_modules. - Anthropic's SSE validation is strict; you MUST strip metadata from streamed deltas before the SDK parses them.
- Re-injection on follow-up turns is easy to forget. Store metadata by the tool-use-id and walk the
messages[].contentarray to rewrite. - The LLM will "waste" tokens writing
_intentvalues - cap the description with a firm instruction like "one short sentence, <120 chars".
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.