Better MCP skills
Three MCP skills, one Unsplash prompt, plan mode — here's what held up.
One of the bigger roadblocks for building high-quality MCP servers is a lack of skills that control the chaos. Skills are the guardrails between a REST-wrapper generator and an AI-native MCP server. So I ran three public skills against the same Unsplash prompt — plan mode, one shot, no iteration — and graded what came back. The grades reflect plan quality plus first-pass implementation, not a week of polish.
| Skill | Grade | Dual transport | Auth | Workflows | Response shaping |
|---|---|---|---|---|---|
| MCP Builder | B | Yes (HTTP auth broken in code) | Per-call headers in plan; broken in code | 1:1 REST | None |
| MCP App Builder | C- | STDIO hallucinated | accessKey tool param | Partial | None |
| Workflow Design | A- | Yes | Headers + multi-tenant | Yes | None |
Anthropic’s MCP Builder
Verdict: Solid B. Clean repo structure, extensive error handling, and a plan that nailed
dual-transport with Client-ID and Authorization headers for HTTP and UNSPLASH_ACCESS_KEY for
STDIO. Paired with a medium reasoning model, consider me impressed — with more direction or another
plan iteration this would have been awesome.
Standout: Dependency-injection tool registration and thorough error handling. The agent also randomly added JSON and Markdown response modes; markdown is genuinely more model-friendly.
Dealbreaker: express and axios (sorry, not sorry — graduate to ky or
native fetch), 1:1 mapping of the entire OpenAPI spec including write tools like
unsplash_add_photo_to_collection, and HTTP auth that doesn’t actually work in the generated code.
MCP Use’s MCP App Builder
Verdict: C-. The agent didn’t implement STDIO support properly. The code is well-organized for a single-file dump, but sloppy types and broken STDIO does not instill confidence.
Standout: Native fetch with minimal glue — no axios dependency.
Dealbreaker: STDIO did not work at all. mcp-use doesn’t appear to support it out of the box,
and Sonnet invented a connection path that doesn’t exist:
const isStdio = process.argv.includes("--stdio") || !process.stdin.isTTY;
if (isStdio) { const transport = new StdioServerTransport(); // What sonnet wrote 👇 // const sessionServer = server.getServerForSession("stdio"); // await sessionServer.connect(transport);
// the fix 👇 await server.nativeServer.connect(transport);} else { await server.listen();}Note: maybe there’s a better way with this library. If you know and reading this make you cringe, don’t hate me. I relied entirely on IDE intellisense, viewed zero docs, and did not use a LLM to figure this out.
Also dealbreaking: accessKey as a tool param rather than a header (security risk if keys get
logged), and unknown[] when the API has typed responses 😅
const data = await fetchUnsplash<unknown[]>( "/photos", { page, per_page: perPage, order_by: orderBy }, key,);MCP Workflow Design
I’m not biased here, I genuinely think this is a great skill 😉. Built alongside MCP OpenAPI Typescript Stack to enforce the patterns from Building MCPs is not hard — typed SDK, dual transport without defaulting to a full web framework, tenancy questions upfront, and workflow tools instead of 1:1 REST.
Verdict: A-. Workflows, multi-tenant auth, and repo structure beat the field. The shared response-filtering gap keeps it from a straight A.
Standout: Multi-tenant HTTP auth with Client-ID, Authorization, X-Unsplash-ID, and
X-Unsplash-Secret headers. Workflow tools for managing collections and photos, fanning out with
Promise.allSettled and partial failure handling so one upstream miss doesn’t kill the whole
response.
Dealbreaker: Same as the others — JSON mode dumps the complete API response with no field-level trimming. The plan also skipped asking about tool selection or the intent of the MCP server before generating.
What every skill missed
Three failures showed up everywhere, regardless of grade:
- No response shaping. Every skill returned full JSON payloads —
exif,blur_hash,current_user_collections, the works. Field-level trimming is table stakes; none of the skills enforced it. - No intent clarification. None of the agents asked what the MCP server is actually for before mapping endpoints. That leads to write tools nobody asked for and REST wrappers instead of curated APIs.
- Auth design gaps. MCP Builder planned per-call headers but shipped broken HTTP auth. MCP App Builder put credentials in tool params. Only Workflow Design got auth right end-to-end.
What a good MCP skill should enforce
From this bake-off, a skill worth installing should push the agent to:
- Ask about transport and tenancy before generating code — stdio-only vs multi-tenant HTTP is not a detail you fix later.
- Prefer SDK transport over express-by-default. The TypeScript MCP SDK ships with
WebStandardStreamableHTTPServerTransport; reach for Hono or Elysia only when you actually need middleware. - Composite workflow tools over 1:1 OpenAPI mapping. Agents are great at mirroring REST. Skills should steer toward outcome-oriented tools that combine calls and return summaries.
- Per-request auth via headers or context, never as tool params. Credentials in tool arguments get logged.
- Normalize responses before returning to the model. Trim cruft, add a
summaryfield when it helps, handle partial failures gracefully.
The skills I wrote to address this live in mcp-skills — specifically mcp-workflow-design and mcp-openapi-typescript-stack.
The skill isn’t teaching the agent to wire transport. It’s teaching the agent to ask what you actually want before generating forty REST wrappers.
If you want to see the full approach in the wild, the reference implementation is unsplash-mcp.