Best for
- Use when writing or reviewing an MCP server or its tools - picking a transport, designing tool schemas and results, handling errors, adding OAuth, cutting token bloat, or migrating SDK versions.
tenequm/skills/skills/mcp-best-practices/SKILL.md
Build, harden, and debug production MCP servers with the TypeScript SDK. Use when writing or reviewing an MCP server or its tools - picking a transport, designing tool schemas and results, handling errors, adding OAuth, cutting token bloat, or migrating SDK versions. Also covers MCP Apps, extensions, and the Registry. Assumes a working server already exists rather than scaffolding one from scratch.
Decision brief
Decision reference for building production MCP servers with the TypeScript SDK. Not a tutorial - assumes you already have a working server and need to make it correct, fast, and secure.
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Declared | Source record | Install path and trigger |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/tenequm/skills --skill "skills/mcp-best-practices"Inspect the Agent Skill "mcp-best-practices" from https://github.com/tenequm/skills/blob/9b9fb5a29c103ed207dc255d753939e4e2ed29f5/skills/mcp-best-practices/SKILL.md at commit 9b9fb5a29c103ed207dc255d753939e4e2ed29f5. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
Per-request server+transport creation is the canonical pattern. Maintainer @ihrpr confirms: "each transport should have an instance of MCPServer" (343). Sharing instances leaks cross-client data (GHSA-345p-7cg4-v4c7).
Set instructions in the server constructor - a system-level hint to the LLM about how to use your server:
v1 imports (legacy line, still widely deployed):
The most decision-relevant fact after the 2026-07-28 release: upgrading to SDK v2.0.0 does not move you to the new spec. A hand-constructed Client/Server/McpServer keeps speaking the 2025-era protocol it was written for.
Review the “Transport Decision” section in the pinned source before continuing.
Permission review
No configured static risk pattern was detected
This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.
Evidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 95/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 35 | Source | Repository attention, not individual Skill quality |
| Compatibility | 1 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Decision reference for building production MCP servers with the TypeScript SDK. Not a tutorial - assumes you already have a working server and need to make it correct, fast, and secure.
| Component | Current | Notes |
|---|---|---|
| Spec (released) | 2026-07-28 (specification) | Stateless/sessionless overhaul - see "Spec 2026-07-28" below and references/spec-2026-07-28.md |
| Spec (still deployed) | 2025-11-25 | What most shipped clients and servers actually speak today; the v2 SDK's default |
| TS SDK (current) | v2.0.0 (2026-07-27), nine packages in lockstep: /server, /client, /core, /hono, /express, /node, /fastify, /codemod, /server-legacy | Speaks 2025-era by default; 2026-07-28 is opt-in |
| TS SDK (legacy) | v1.30.0 (@modelcontextprotocol/sdk) | Bug + security fixes for >=6 months after v2 GA; source on the v1.x branch |
| JSON Schema | 2020-12 default (2019-09 / draft-07 accepted since v2.0.0) | - |
| Transport | Streamable HTTP (remote), stdio (local) | SSE + WebSocket removed in v2 |
| Extensions | MCP Apps (Stable, SEP-1865), Auth Extensions (official), Tasks (ext-tasks) | Domain-specific WGs |
| Registry | Preview with v0.1 API freeze since 2025-10-24 (registry) | GA pending |
v2 imports (current):
import { McpServer } from "@modelcontextprotocol/server";
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/server";
import { ProtocolError, ProtocolErrorCode } from "@modelcontextprotocol/core";
v1 imports (legacy line, still widely deployed):
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
The most decision-relevant fact after the 2026-07-28 release: upgrading to SDK v2.0.0 does not move you to the new spec. A hand-constructed Client/Server/McpServer keeps speaking the 2025-era protocol it was written for.
Every revision from 2024-10-07 through 2025-11-25 opens with initialize and shares one wire behavior - the SDK calls that family legacy. 2026-07-28 starts the modern era: no initialize, a server/discover advertisement instead, a _meta envelope on every request. Selection is explicit:
versionNegotiation.mode | Behavior |
|---|---|
absent / 'legacy' | The 2025 initialize handshake, byte for byte. No probe. This is the default. |
'auto' | Probe with server/discover; fall back to initialize against a 2025-only server |
{ pin: '2026-07-28' } | That revision or nothing - a pin never falls back |
Build new servers on the 2025-era wire unless you control both ends. The stateless design guidance throughout this skill is what makes the eventual era switch cheap.
Tooling: SDK docs (v2); MCP Inspector, which connects as legacy by default (see "Testing Against Each Era" in references/spec-2026-07-28.md); the conformance suite; and the mcp-server-dev plugin for scaffolding.
| Scenario | Transport | Key Config |
|---|---|---|
| Remote, stateless (K8s, CF Workers) | WebStandardStreamableHTTPServerTransport | sessionIdGenerator: undefined, enableJsonResponse: true |
| Remote, stateful (long tasks, SSE) | WebStandardStreamableHTTPServerTransport | sessionIdGenerator: () => randomUUID() |
| Local CLI / Claude Desktop | StdioServerTransport | Default |
| Legacy SSE clients | SSE removed in v2 - migrate to Streamable HTTP | - |
Per-request server+transport creation is the canonical pattern. Maintainer @ihrpr confirms: "each transport should have an instance of MCPServer" (#343). Sharing instances leaks cross-client data (GHSA-345p-7cg4-v4c7).
app.post("/mcp", async (c) => {
const server = new McpServer({ name: "my-server", version: "1.0.0" });
// Register tools, resources, prompts...
registerTools(server);
const transport = new WebStandardStreamableHTTPServerTransport({
sessionIdGenerator: undefined, // stateless - no session tracking
enableJsonResponse: true, // JSON responses, no SSE streaming
});
// All tools/resources must be registered before connect() (#893)
try {
await server.connect(transport);
return transport.handleRequest(c.req.raw);
} finally {
await transport.close();
await server.close();
}
});
The McpServer must be per-request, but its constant inputs must not be. Hoist to module level: Zod schemas, annotation objects ({ readOnlyHint: true, ... }), tool description strings, payment configs, upstream API clients.
If you only route POST (the common stateless layout), answer GET /mcp with an explicit 405 Method Not Allowed - the spec requires it when no SSE stream is offered, and the official TS client reads 405 as the benign no-stream signal, while an empty 200 sends it into a reconnect storm.
For transports, sessions, HTTP/2 gotchas, and K8s deployment: see
references/transport-patterns.md
The transport is web-standard, so Hono and the Workers runtime need no adapter; v2 also ships @modelcontextprotocol/hono (createMcpHonoApp()) and @modelcontextprotocol/express (wrapping NodeStreamableHTTPServerTransport for IncomingMessage/ServerResponse). On Cloudflare Workers call preloadSchemas() at module scope - v2's workerd build does it automatically. Examples: references/transport-patterns.md.
v1 (legacy line) - server.tool(name, description, zodShape, annotations, handler). Positional overloads are ambiguous; same fields as v2 below minus outputSchema. Removed entirely in v2.
v2 (current) - registerTool() with config object:
server.registerTool("search_docs", {
title: "Document Search",
description: "Search documents by keyword or phrase",
inputSchema: z.object({
query: z.string().describe("Search query"),
max_results: z.number().optional().describe("Max results (default 20)"),
}),
outputSchema: z.object({
results: z.array(z.object({ id: z.string(), text: z.string() })),
has_more: z.boolean(),
}),
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
}, async ({ query, max_results }) => {
const result = await fetchDocs(query, max_results);
return {
// Both channels carry IDENTICAL bytes. Divergent payloads = the text block
// silently vanishes on Claude Code/Codex/Copilot. See "Tool Result Delivery" below.
structuredContent: result,
content: [{ type: "text", text: JSON.stringify(result) }],
};
});
Spec 2025-11-25 (SHOULD, not MUST): 1-128 chars, case-sensitive, A-Za-z0-9_-. only. DO: search_docs, get_user_profile, admin.tools.list. DON'T: search (generic names collide across servers), Search Docs (spaces disallowed). Service-prefix (github_*, jira_*) when multiple servers are active - LLMs confuse generic names.
.describe() on every field - this is what LLMs use for argument generation. Three constructs break silently (z.union(), raw JSON Schema, z.transform()), as does client-side AJV strict validation - see "Known SDK Bugs" below.
Pagination is the primitive most servers hit first: a tools/list or resources/list with 50+ entries should paginate. The protocol cursor is opaque - never parse or synthesize it; loop until nextCursor is absent. It is distinct from in-tool offset/limit args.
Zod-to-JSON-Schema conversion rules, outputSchema/structuredContent patterns, non-text content types, the other tool-definition fields (
icons,listChanged,execution.taskSupport), and the remaining primitives (prompts, resources, resource templates, completions, cancellation): seereferences/tool-schema-guide.md
All are optional hints (untrusted from untrusted servers per spec):
| Annotation | Default | Meaning |
|---|---|---|
readOnlyHint | false | Tool doesn't modify its environment |
destructiveHint | true | May perform destructive updates (only when readOnly=false) |
idempotentHint | false | Repeated calls with same args have no additional effect |
openWorldHint | true | Interacts with external entities (APIs, web) |
Set them accurately - clients use them for consent prompts and auto-approval decisions.
The "Lethal Trifecta": private-data access + exposure to untrusted content + external communication in one agent creates data-theft conditions (demonstrated with a malicious calendar event, an MCP calendar server, and a code-execution tool). Design tool sets so no single agent holds all three.
With no protocol-level session on 2026-07-28, cross-call state uses server-minted handles passed as ordinary tool arguments: a creation tool returns { basket_id: "bsk_a1b2c3" }, later tools take basket_id as an argument, and the model carries it forward. A handle is a name, not a capability - validate the caller against it on every call, keep it opaque with real entropy, and state its retention policy in the creation tool's description. Expired or unknown handles return a tool execution error so the model can recover by creating new state. Full rules: references/spec-2026-07-28.md.
content vs structuredContentThe footgun: when a tool returns BOTH a text content block and structuredContent, several major clients (Claude Code, Codex CLI, VS Code Copilot, Goose) silently drop the text block and forward only structuredContent to the model. If the two payloads differ, the human-readable one vanishes. This is client behavior the spec does not constrain - not an SDK transform. Don't return both channels expecting both to reach the model.
Measured with claude -p --output-format=stream-json, reading the exact tool_result the model received:
| Tool returns | What the model receives |
|---|---|
One text block, no structuredContent | text verbatim |
content: [] + structuredContent | JSON.stringify(structuredContent) as a string in the content slot - works |
text block + structuredContent | text block silently dropped; structuredContent wins |
text + structuredContent + outputSchema | same - outputSchema makes zero difference |
two text blocks, no structuredContent | both preserved verbatim |
structuredContent is not a separate typed channel to the model on Claude Code - it is stringified into the standard tool_result content slot, so it costs the same tokens as the equivalent JSON-as-text. It does not buy cheaper or out-of-band structured data.
Intentional, per Anthropic maintainer (anthropics/claude-code#9962): structuredContent support landed in Claude Code v2.0.21 and "we made structuredContent the default when both formats are present... optimizing for agent performance." Reproduced across unrelated servers (Laravel, Roblox Studio, YouTube) - host-side precedence, not a server bug.
There is no precedence rule - the spec never says which field a client should prefer when both are present (Discussion #1563), and that gap is the documented root cause of client divergence. The only relevant normative line is a backwards-compat SHOULD: "a tool that returns structured content SHOULD also return the serialized JSON in a TextContent block." The official TypeScript SDK passes both fields through verbatim; any stringify-into-content you observe is the host harness, not the SDK.
| Client | When both content + structuredContent present |
|---|---|
| Claude Code CLI, OpenAI Codex CLI, VS Code Copilot, Goose | shadow - only structuredContent reaches the model (text dropped) |
| Cursor, Claude.ai web, ChatGPT MCP connector | prefer content / surface both to the model |
| Google ADK (framework) | forwards both by default; content-only is opt-in |
(Non-Claude-Code rows come from issue trackers and maintainer statements, not the stream-json harness - treat exact delivery as client-version-dependent.)
content and structuredContent (e.g. a rendered ASCII table as text + different JSON as structured). On shadowing clients the text silently disappears and only the JSON reaches the model.structuredContent, mirror the same bytes into a text block: content: [{ type: "text", text: JSON.stringify(payload) }]. This is the spec's backwards-compat SHOULD. Shadowing clients use the structured copy; others fall back to the identical text - either way the model gets the data. Mirroring does not double tokens on shadowing clients (they drop the text).structuredContent - or expose a format: "table" | "json" arg (table -> text-only; json -> JSON mirrored into both channels). Both are empirically valid on Claude Code and keep one channel per call.outputSchema gates client-side validation only; it does not make the text block survive on shadowing clients.content blocks are not text-only - image, audio, resource_link, and embedded resource blocks all exist, with annotations (audience, priority, lastModified); for those and the image preview + URL pattern see references/tool-schema-guide.md.
Two distinct mechanisms with different LLM visibility:
| Type | LLM Sees It? | Use For |
|---|---|---|
Tool error (isError: true in CallToolResult) | Yes - enables self-correction | Input validation, API failures, business logic errors |
| Protocol error (JSON-RPC error response) | Maybe - clients MAY expose | Unknown tool, malformed request, server crash |
Per SEP-1303 (merged into spec 2025-11-25): input validation errors MUST be tool execution errors, not protocol errors. The LLM needs to see "date must be in the future" to self-correct.
// DO: Tool execution error - LLM can self-correct
return {
isError: true,
content: [{ type: "text", text: "Date must be in the future. Current date: 2026-03-25" }],
};
// DON'T: Protocol error for validation - LLM can't see this
throw new McpError(ErrorCode.InvalidParams, "Invalid date");
Known SDK behavior: converting an McpError thrown from a tool handler into a CallToolResult drops the error.data field, so structured data embedded there may never reach the client. The x402/MPP ecosystem standardized on isError: true results with structuredContent for this reason.
For full error taxonomy, code examples, payment error patterns, and why
-32042is not available as a "Payment Required" code: seereferences/error-handling.md
Set instructions in the server constructor - a system-level hint to the LLM about how to use your server:
const server = new McpServer({
name: "docs-api",
version: "1.0.0",
instructions: "Knowledge base API. Use search_docs for full-text search, get_doc for retrieval by ID. All tools are read-only.",
});
Ship guides and structured data as resources under a docs:// URI scheme (server.resource(...)) - see "Other Server Primitives" in references/tool-schema-guide.md.
Tool definitions consume context window before any conversation starts. GitHub MCP: 20,444 tokens for 80 tools (SEP-1576).
Strategies:
track_order(email) not get_user + list_orders + get_status).outputSchema + structuredContent - typed output for programmatic/PTC clients. Caveat: on shadowing clients structuredContent is stringified into the model's context at the same token cost as text - not a free out-of-band channel (see "Tool Result Delivery").?tools=search,fetch query param). Pair with listChanged if the set changes mid-session.search_tools meta-tool and programmatic tool calling, where structuredContent is consumed outside the model context (client best practices). Curated, well-described tools make these flows work.Clients silently truncate large tool results. Budget for the strictest client you target:
| Client | Default cap | Configurable |
|---|---|---|
| Claude Code | 25,000 tokens (warning at 10k) | MAX_MCP_OUTPUT_TOKENS env; per-tool _meta["anthropic/maxResultSizeChars"] up to 500,000 chars |
| OpenAI Codex CLI | 10,000 bytes on byte-policy models (includes the JSON envelope) | tool_output_token_limit config |
| Gemini CLI | 40,000 chars (head 20% / tail 80% trim; full output saved to a file) | settings; 0 or negative disables |
Enforce your own cap server-side - see "Result-Size Budgets and Truncation" in references/tool-schema-guide.md. Two rules worth stating here: never truncate isError results (payment/auth challenges must survive intact), and treat client budgets as per-connection properties - accept them as URL query params (?max_chars=, alongside ?tools=) rather than growing every tool schema with override args.
For tools with no inputs, use an explicit empty schema - not undefined or omission:
inputSchema: { type: "object" as const, additionalProperties: false }
| Attack | Example | Mitigation |
|---|---|---|
| Tool poisoning | Hidden instructions in descriptions (WhatsApp MCP, Apr 2025) | Review tool descriptions; clients should display them |
| Supply chain | Malicious npm packages (Smithery breach, Oct 2025) | Pin versions, audit dependencies |
| Stdio config injection | User-controlled input reaches StdioServerParameters unsanitized (OX Security, 2026-04-15) | Sanitize stdio config in client code; prefer first-party servers. Treated as "by design" - not patched in the SDK |
| Cross-server shadowing | Malicious server overrides legitimate tool names | Service-prefix tool names; validate tool sources |
| Token theft | Over-privileged PATs with broad scopes | Minimal scopes; OAuth 2.1 Resource Indicators (RFC 8707) |
| Token passthrough | Server accepts/forwards tokens not issued for it | Validate audience claim; never transit client tokens to upstream APIs |
| Confused deputy | Proxy server consent cookies exploited via DCR | Per-client consent before forwarding to third-party auth |
| Session hijacking | Stolen/guessed session IDs for impersonation | Cryptographically random IDs, bind to user identity, never use for auth |
| Cross-client response leak | Shared McpServer/transport reused across clients (CVE-2026-25536, affects v1.10.0-1.25.3) | Require SDK >= v1.26.0; per-request server+transport |
| UriTemplate ReDoS | Malicious URI patterns (CVE-2026-0621) | Upgrade to v1.25.2+ / v2.0.0-alpha.1+ |
Generic hygiene still applies: validate inputs at tool boundaries, enforce per-user access control, rate limit, never interpolate tool input into shell commands, block private IPs on outbound fetches, bind local servers to 127.0.0.1.
Origin header - but only reject when it is present and invalid: "If the Origin header is present and invalid, servers MUST respond" with 403. Shipping clients exist that send no Origin at all; a blanket 403-on-missing locks them out.MCP-Protocol-Version leniently. On 2025-era wires it is required after initialization (spec 2025-06-18+); on 2026-07-28 there is no initialization and the version rides _meta. Accept a range of declared versions rather than enforcing one - clients advertising 2024-11-05 are still in the wild.MCP normatively requires OAuth 2.1 (draft-ietf-oauth-v2-1-13), not 2.0 - PKCE mandatory, implicit flow removed. Servers are Resource Servers; clients MUST send Resource Indicators (RFC 8707) binding tokens to your server.
S256, short-lived tokens, minimal scopes (elevate via WWW-Authenticate challenges).iss interop footgun: advertising authorization_response_iss_parameter_supported: true makes strict clients MUST-validate a callback iss that some of them drop. Advertise the flag as false while still sending iss - see references/security-auth.md.For full security attack/mitigation patterns and auth implementation details: see
references/security-auth.md
Must-know as of [email protected] / [email protected]:
z.union()/z.discriminatedUnion() silently produce empty schemas on every released v1, v1.30.0 included (#1643, backport still open) - use flat z.object() + z.enum().connect() - later registration throws; open on both main and v1.x (#893).structuredContent extras - .parse() upstream data first, or .passthrough() for intentional extras.Full table (statuses, transport-closure stack overflow, HTTP/2, raw JSON Schema,
z.transform(), ReDoS): seereferences/sdk-bugs.md
For comprehensive migration guide with all breaking changes and before/after code: see
references/v2-migration.md
Key breaking changes:
@modelcontextprotocol/sdk -> @modelcontextprotocol/server + /client + /coreMcpError -> ProtocolError (from @modelcontextprotocol/core)extra parameter -> structured ctx with ctx.mcpReqserver.tool() -> registerTool() (config object, not positional args)@modelcontextprotocol/hono and @modelcontextprotocol/express middleware packagesv1.x gets 6 more months of support after v2 stable ships. No rush, but write new code with v2 patterns in mind.
Published 2026-07-28 (release announcement, changelog) - now the latest revision. Remember it is opt-in on the SDK (see "The Two Eras"): 2025-11-25 remains what most deployed software speaks.
Four shifts that change a decision you make today:
initialize handshake and Mcp-Session-Id are gone (SEP-2575, SEP-2567); every request carries its protocol version, client identity, and capabilities in _meta, and cross-call state uses handles (see "Stateful Tools"). Do not build new servers on session affinity.server/discover is a server MUST - it advertises versions/capabilities/identity; clients MAY skip it and handle UnsupportedProtocolVersionError inline.-32768..-32000 - -32020..-32099 is reserved for the spec and -32000..-32019 is legacy that new implementations SHOULD NOT use at all (PR #2907).The content vs structuredContent dual-delivery footgun is unchanged - no precedence rule landed, so the guidance above still holds.
Everything else - MRTR,
subscriptions/listen,_metaidentity keys,requestState,Mcp-Method/Mcp-Name, cacheable results, per-request log level, auth changes, the removals (SSE resumability,ping,execution.taskSupport), era testing, working groups: seereferences/spec-2026-07-28.md
Optional, strictly additive capabilities named {vendor-prefix}/{extension-name} (official: io.modelcontextprotocol/*; third-party: reversed domain). Negotiated in initialize capabilities on 2025-era wires; on 2026-07-28 clients advertise support per request in _meta["io.modelcontextprotocol/clientCapabilities"]. Official ones: MCP Apps (/ui, interactive HTML UIs, Stable, widely supported), OAuth Client Credentials (Draft), Enterprise-Managed Authorization (Stable 2026-06-18) - client matrix.
Server capabilities beyond tools, all 2025-era APIs (the SDK default):
| Capability | Purpose | v2 API |
|---|---|---|
| Elicitation | Request structured user input mid-tool | ctx.mcpReq.elicitInput() |
| Sampling | Request LLM completion from client | ctx.mcpReq.requestSampling() |
| Tasks | Long-running ops with lifecycle management | Official extension (SEP-2663) |
| Progress | Incremental progress on requests | ctx.mcpReq.sendProgress() |
On 2026-07-28 servers cannot send requests to clients at all: elicitation and sampling go through MRTR (return an InputRequiredResult, read inputResponses on the retry). Tasks moved out of core into the polled io.modelcontextprotocol/tasks extension (ext-tasks).
For MCP Apps architecture, ext-apps SDK, and build patterns: see
references/mcp-apps.mdFor the extensions system, auth extensions, elicitation/sampling/tasks detail, and the MCP Registry: seereferences/extensions-registry.md
Frequently asked questions
Decision reference for building production MCP servers with the TypeScript SDK. Not a tutorial - assumes you already have a working server and need to make it correct, fast, and secure.
The source record exposes this install command: npx skills add https://github.com/tenequm/skills --skill "skills/mcp-best-practices". Inspect the command and pinned source before running it.
The pinned source record declares support for: claude code.
Alternatives
vasilyu1983/AI-Agents-public
Guides iOS testing with XCTest, XCUITest, Swift Testing, simctl, and xcresult. Use when choosing destinations, controlling flakes, or parsing test artifacts for native apps.
upex-galaxy/agentic-qa-boilerplate
Orchestrates in-sprint manual QA per ticket across Stages 1 (Planning), 2 (Execution) and 3 (Reporting). Use for user-story testing, bug retesting, and batch-sprint QA loops. Creates the PBI folder, drives session-start, runs the triage + veto + risk-score decision tree on bugs, produces the ATP + ATR + TC artifacts in the TMS, executes smoke and trifuerza (UI/API/DB) exploration, and files the final QA comment + bug reports. Triggers on: test this ticket, QA this user story, retest this bug, ve
davepoon/buildwithclaude
Activate when the user wants to build a Claude plugin, create a Claude skill, make a Claude agent, structure a Claude Code plugin, says "build a plugin", "create a skill", "new claude skill", "new agent", "help me make a plugin", "plugin builder", "claude plugin helper", "how do I build a Claude skill", "I want to create a Claude plugin", "plugin building", or asks how to structure a Claude Code plugin or publish to the Claude marketplace. Works on both claude.ai (generates files as code blocks)
vasilyu1983/AI-Agents-public
Designs Android testing with Espresso, UI Automator, and Compose. Use when planning device matrices, screenshot tests, CI flows, or flake-control workflows.