Qwen Code E2E Testing
QwenLM/qwen-code
Guides end-to-end testing of the Qwen Code CLI in headless mode with real model calls, MCP test servers and inspection of raw API traffic.
Scaffold a test file for an existing tool, resource, or service.
$ npx skills add cyanheads/pubmed-mcp-server --skill add-test -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install cyanheads/pubmed-mcp-server add-test --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/cyanheads/pubmed-mcp-server.git skills-src && mkdir -p .claude/skills && cp -r skills-src/framework-skills/add-test .claude/skills/add-test && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "add-test" agent skill from https://github.com/cyanheads/pubmed-mcp-server/tree/main/framework-skills/add-test into .claude/skills/add-test/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "add-test", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/cyanheads/pubmed-mcp-server/tree/main/framework-skills/add-testType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add cyanheads/pubmed-mcp-server --skill add-test -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install cyanheads/pubmed-mcp-server add-test --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/cyanheads/pubmed-mcp-server.git skills-src && mkdir -p .agents/skills && cp -r skills-src/framework-skills/add-test .agents/skills/add-test && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "add-test" agent skill from https://github.com/cyanheads/pubmed-mcp-server/tree/main/framework-skills/add-test into .agents/skills/add-test/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "add-test", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add cyanheads/pubmed-mcp-server --skill add-test -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install cyanheads/pubmed-mcp-server add-test --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/cyanheads/pubmed-mcp-server.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/framework-skills/add-test .cursor/skills/add-test && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "add-test" agent skill from https://github.com/cyanheads/pubmed-mcp-server/tree/main/framework-skills/add-test into .cursor/skills/add-test/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "add-test", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/cyanheads/pubmed-mcp-server.git --path framework-skills/add-test--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add cyanheads/pubmed-mcp-server --skill add-test -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install cyanheads/pubmed-mcp-server add-test --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/cyanheads/pubmed-mcp-server.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/framework-skills/add-test .gemini/skills/add-test && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "add-test" agent skill from https://github.com/cyanheads/pubmed-mcp-server/tree/main/framework-skills/add-test into .gemini/skills/add-test/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "add-test", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install cyanheads/pubmed-mcp-server add-testInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add cyanheads/pubmed-mcp-server --skill add-test -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/cyanheads/pubmed-mcp-server.git skills-src && mkdir -p .github/skills && cp -r skills-src/framework-skills/add-test .github/skills/add-test && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "add-test" agent skill from https://github.com/cyanheads/pubmed-mcp-server/tree/main/framework-skills/add-test into .github/skills/add-test/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "add-test", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add cyanheads/pubmed-mcp-server --skill add-test -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install cyanheads/pubmed-mcp-server add-test --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/cyanheads/pubmed-mcp-server.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/framework-skills/add-test .opencode/skills/add-test && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "add-test" agent skill from https://github.com/cyanheads/pubmed-mcp-server/tree/main/framework-skills/add-test into .opencode/skills/add-test/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "add-test", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
add-testScaffold a test file for an existing tool, resource, or service.
Add Test is an agent skill from cyanheads/pubmed-mcp-server. Scaffold a test file for an existing tool, resource, or service. Use when the user asks to add tests, improve coverage, or when a definition exists without a matching test file.
Its SKILL.md is about 4.1k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Testing & QA. It works with Model Context Protocol. The repository describes itself as: Search PubMed/Europe PMC, fetch articles and full text (PMC/EPMC/Unpaywall), citations, MeSH terms via MCP. STDIO or Streamable HTTP. The licence is Apache-2.0.
6 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit 5a417fb. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
bunFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Add Test loads about 4.1k tokens when it runs. Until then it costs about 47 tokens; SKILL.md has 1,083 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from cyanheads/pubmed-mcp-server at commit 5a417fb, republished under its Apache-2.0 licence (© cyanheads). 1,083 words, ~4,051 tokens.
.claude/skills/add-test/SKILL.md (or your agent's skills folder).Tests use Vitest and createMockContext from @cyanheads/mcp-ts-core/testing. If the repo already has tests, match the existing layout. If the repo has no existing tests, create a root tests/ directory that mirrors the src/ structure (e.g. tests/mcp-server/tools/definitions/echo.tool.test.ts for src/mcp-server/tools/definitions/echo.tool.ts).
For the full createMockContext API and testing patterns, read:
framework-skills/api-testing/SKILL.mdctx features it uses*.test.ts files to confirm whether tests are colocated with source or under a root tests/ directorybun run test to verifybun run devcheck to verify lint, types, and MCP definitionsRead the handler and identify:
| Aspect | Test Strategy |
|---|---|
| Happy path | Valid input → expected output. Include at least one. |
| Input variations | Optional fields omitted, defaults applied, boundary values |
| Error paths | Invalid state, missing resources, service failures → correct error thrown |
ctx.state usage | Available on any mock context (tenant 'default' unless { tenantId } says otherwise). It runs the production storage path, so use storage-legal keys (cache/v1/abc, never cache:v1:abc) and assert TTL expiry with fake timers. |
ctx.requestInput / ctx.inputs | Two rounds. First round: assert the handler throws the input-required signal (.rejects.toSatisfy(isInputRequiredSignal)), or catch it and assert on error.result.inputRequests. Second round: seed createMockContext({ inputResponses }) and assert the handler completes. Cover the decline/cancel branch too. A consent gate that redeems a ctx.state record also needs that record written into the second round's context, and a replay case showing the spent record asks again (see api-testing § Mock inputs). |
ctx.clientCapabilities | When the handler asks only if the client declared a capability, seed createMockContext({ clientCapabilities }) with and without it and assert it asks in one case and falls through in the other. Seeding it also filters inputResponses to the declared kinds, as production does. |
ctx.signal | Pass createMockContext({ signal: controller.signal }) and assert a long loop stops early rather than running to completion. |
ctx.fail (typed contract) | Definitions with errors[] need fail attached to the mock ctx — createMockContext({ errors: myTool.errors }) does it for you. Assert on data.reason (stable per-contract entry), not just code. |
format function | Test separately if defined — it's pure, no ctx needed. Verify it renders the IDs and fields the model needs, not just a count or title. For projection-style tools, test non-default field selections. |
| Sparse upstream payloads | For third-party API integrations, build a fixture with omitted fields. Assert normalized output still validates and format() preserves unknown values instead of inventing facts. |
| Form-client payloads | If handler has optional fields: test with empty-string inner values (form clients send "" instead of undefined). Assert handler doesn't break or produce invalid output. |
| Auth scopes | Not tested at handler level (framework enforces) — skip |
/**
* @fileoverview Tests for {{TOOL_NAME}} tool.
* @module tests/tools/{{TOOL_NAME}}.tool.test
*/
import { describe, expect, it } from 'vitest';
import { createMockContext } from '@cyanheads/mcp-ts-core/testing';
import { {{TOOL_EXPORT}} } from '@/mcp-server/tools/definitions/{{tool-name}}.tool.js';
describe('{{TOOL_EXPORT}}', () => {
it('returns expected output for valid input', async () => {
const ctx = createMockContext();
const input = {{TOOL_EXPORT}}.input.parse({
// valid input matching the Zod schema
});
const result = await {{TOOL_EXPORT}}.handler(input, ctx);
expect(result).toMatchObject({
// expected output shape
});
});
it('throws on invalid state', async () => {
const ctx = createMockContext();
const input = {{TOOL_EXPORT}}.input.parse({
// input that triggers an error path
});
await expect({{TOOL_EXPORT}}.handler(input, ctx)).rejects.toThrow();
});
// Only when the tool declares `errors: [...]`. Drop this block otherwise.
it('throws ctx.fail("{{REASON}}") for the declared failure mode', async () => {
const ctx = createMockContext({ errors: {{TOOL_EXPORT}}.errors });
const input = {{TOOL_EXPORT}}.input.parse({
// input that triggers the declared failure mode
});
await expect({{TOOL_EXPORT}}.handler(input, ctx)).rejects.toMatchObject({
data: { reason: '{{REASON}}' },
});
});
it('formats output completely', () => {
const output = { /* mock output matching the output schema */ };
const blocks = {{TOOL_EXPORT}}.format!(output);
expect(blocks.some((block) => block.type === 'text')).toBe(true);
// Assert the rendered text includes the IDs/fields the LLM needs to act on.
});
});/**
* @fileoverview Tests for {{RESOURCE_NAME}} resource.
* @module tests/resources/{{RESOURCE_NAME}}.resource.test
*/
import { describe, expect, it } from 'vitest';
import { createMockContext } from '@cyanheads/mcp-ts-core/testing';
import { {{RESOURCE_EXPORT}} } from '@/mcp-server/resources/definitions/{{resource-name}}.resource.js';
describe('{{RESOURCE_EXPORT}}', () => {
it('returns data for valid params', async () => {
const ctx = createMockContext({ tenantId: 'test-tenant' });
const params = {{RESOURCE_EXPORT}}.params.parse({
// valid params matching the Zod schema
});
const result = await {{RESOURCE_EXPORT}}.handler(params, ctx);
expect(result).toBeDefined();
});
it('throws when resource not found', async () => {
const ctx = createMockContext({ tenantId: 'test-tenant' });
const params = {{RESOURCE_EXPORT}}.params.parse({
// params for a non-existent resource
});
await expect({{RESOURCE_EXPORT}}.handler(params, ctx)).rejects.toThrow();
});
// For resources that declare an `errors: [...]` contract, pass the contract via
// `createMockContext` so the typed `ctx.fail` is wired automatically:
// const ctx = createMockContext({ errors: {{RESOURCE_EXPORT}}.errors });
// const err = await {{RESOURCE_EXPORT}}.handler(params, ctx).catch((e) => e);
// expect(err.code).toBe(JsonRpcErrorCode.NotFound);
// expect(err.data.reason).toBe('no_match');
// Include this block only when the resource definition exports a `list` function.
// Check the source — `list` is optional on resource definitions.
it('lists available resources', async () => {
const listing = await {{RESOURCE_EXPORT}}.list!();
expect(listing.resources).toBeInstanceOf(Array);
expect(listing.resources.length).toBeGreaterThan(0);
for (const r of listing.resources) {
expect(r).toHaveProperty('uri');
expect(r).toHaveProperty('name');
}
});
});/**
* @fileoverview Tests for {{SERVICE_NAME}} service.
* @module tests/services/{{domain}}/{{domain}}-service.test
*/
import { beforeEach, describe, expect, it } from 'vitest';
import { createMockContext } from '@cyanheads/mcp-ts-core/testing';
import { StorageService } from '@cyanheads/mcp-ts-core/storage';
import { get{{ServiceClass}}, init{{ServiceClass}} } from '@/services/{{domain}}/{{domain}}-service.js';
// Derive the minimal mock config from src/config/server-config.ts — read
// the server's Zod schema to see which fields init{{ServiceClass}}() needs.
const mockConfig = { /* fields from server config schema */ } as AppConfig;
describe('{{ServiceClass}}', () => {
beforeEach(async () => {
const mockStorage = await StorageService.create({ type: 'in-memory' });
init{{ServiceClass}}(mockConfig, mockStorage);
});
it('performs the expected operation', async () => {
const ctx = createMockContext({ tenantId: 'test-tenant' });
const service = get{{ServiceClass}}();
const result = await service.doWork('input', ctx);
expect(result).toBeDefined();
});
});If you need to test the accessor's "not initialized" guard, do it in a separate isolated-module test (vi.resetModules() before importing the service module). Don't mix that assertion into a suite that already calls init{{ServiceClass}}() in beforeEach().
A handler that calls ctx.requestInput(...) throws an InputRequiredSignal — in production the handler factory converts it to an input_required result; in a unit test it surfaces as a thrown value. Test both rounds.
import { isInputRequiredSignal } from '@cyanheads/mcp-ts-core';
import { createMockContext } from '@cyanheads/mcp-ts-core/testing';
it('requests the missing input on the first round', async () => {
const ctx = createMockContext();
const input = {{TOOL_EXPORT}}.input.parse({ path: '/tmp/x' });
try {
await {{TOOL_EXPORT}}.handler(input, ctx);
throw new Error('Expected the handler to request input.');
} catch (error) {
if (!isInputRequiredSignal(error)) throw error;
expect(Object.keys(error.result.inputRequests ?? {})).toEqual(['confirm']);
}
});
it('completes once the response is supplied', async () => {
const ctx = createMockContext({
inputResponses: { confirm: { action: 'accept', content: { confirm: true } } },
});
const input = {{TOOL_EXPORT}}.input.parse({ path: '/tmp/x' });
await expect({{TOOL_EXPORT}}.handler(input, ctx)).resolves.toMatchObject({ deleted: '/tmp/x' });
});
it('does not re-ask after a decline', async () => {
const ctx = createMockContext({ inputResponses: { confirm: { action: 'decline' } } });
const input = {{TOOL_EXPORT}}.input.parse({ path: '/tmp/x' });
// Terminal, not another round — re-asking would burn the round budget.
await expect({{TOOL_EXPORT}}.handler(input, ctx)).rejects.toThrow(McpError);
});For a destructive consent gate, the second round completes only when the context holds the single-use record the first round stored, keyed by the requestState it returned — each mock context has its own ctx.state, so write the record into the second context before calling the handler. api-testing § Mock inputs has the full pattern, including the replay case.
Pass an AbortController signal through createMockContext or runToolContract.
Abort after a controlled I/O operation or loop iteration starts, then assert that
the pending call settles and no further requests or iterations run. Use a deferred
fixture or fake timers so the test does not depend on a wall-clock delay; restore
timers and close any fixture resources afterward. A defined result alone does not
prove cancellation stopped the work.
When an abort-aware handler throws after the signal fires, runToolContract returns
isError: true with structuredContent.error.code equal to
JsonRpcErrorCode.RequestCancelled; assert the cancellation message in content[]
as well. Use schema-valid arguments: invalid arguments still return InvalidParams
before the handler runs. A partial success is appropriate only when the tool's
contract explicitly supports it; assert its actual partial-result fields, output
schema, and format() content instead of requiring every handler to return one.
/**
* @fileoverview Tests for {{PROMPT_NAME}} prompt.
* @module tests/prompts/{{PROMPT_NAME}}.prompt.test
*/
import { describe, expect, it } from 'vitest';
import { {{PROMPT_EXPORT}} } from '@/mcp-server/prompts/definitions/{{prompt-name}}.prompt.js';
describe('{{PROMPT_EXPORT}}', () => {
it('generates valid messages for valid args', () => {
const args = {{PROMPT_EXPORT}}.args!.parse({
// valid args matching the Zod schema
});
const messages = {{PROMPT_EXPORT}}.generate(args);
expect(messages).toBeInstanceOf(Array);
expect(messages.length).toBeGreaterThan(0);
for (const msg of messages) {
expect(msg).toHaveProperty('role');
expect(msg).toHaveProperty('content');
}
});
// Include only when the prompt has no required args (args is optional or all fields optional).
it('generates messages with no args', () => {
const messages = {{PROMPT_EXPORT}}.generate({});
expect(messages.length).toBeGreaterThan(0);
});
});For schema-heavy or input-validation-critical handlers, the framework ships fuzz helpers that generate valid + adversarial inputs from your Zod schemas via fast-check and assert handler invariants (no crashes, no prototype pollution, no stack-trace leaks):
import { fuzzTool } from '@cyanheads/mcp-ts-core/testing/fuzz';
it('survives fuzz testing', async () => {
const report = await fuzzTool({{TOOL_EXPORT}}, { numRuns: 100 });
expect(report.crashes).toHaveLength(0);
expect(report.leaks).toHaveLength(0);
expect(report.prototypePollution).toBe(false);
});Available helpers from @cyanheads/mcp-ts-core/testing/fuzz: fuzzTool, fuzzResource, fuzzPrompt, zodToArbitrary (custom property-based tests), adversarialArbitrary and ADVERSARIAL_STRINGS (targeted injection sets). Returns a FuzzReport you can assert against. Options: numRuns, numAdversarial, seed (reproducibility), timeout, ctx (MockContextOptions for stateful handlers).
When scaffolding tests for an existing handler, use the Zod schemas to generate meaningful test cases:
input schema — identify required fields, optional fields with defaults, constrained types (enums, min/max, patterns)output schema — know what shape to assert against.min(), .max(), .length(), test at the boundariesformat() renders uncertainty honestly (Not available, omitted badge, etc.) instead of fabricating values.tests/... or colocated with source)@fileoverview and @module header present.rejects.toThrow())format function tested if definedcreateMockContext options match handler's ctx usage (tenantId, inputResponses, requestState, clientCapabilities, errors, signal)beforeEach if handler depends on a service singletonformat() renders uncertainty honestly instead of inventing valuesgenerate() tested with valid args and (when applicable) no argsbun run test passesbun run devcheck passes© cyanheads, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in framework-skills/add-test of cyanheads/pubmed-mcp-server.
Open the folder on GitHubat commit 5a417fb
Add Test next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Add Test this skillcyanheads/pubmed-mcp-server | 155 | — | ~4.1k | Automated safety check: Pass | Apache-2.0 | |
| Qwen Code E2E TestingQwenLM/qwen-code | 28k | — | ~2.1k | Automated safety check: Pass | Apache-2.0 | |
| Adding LLM MCP ToolsTriliumNext/Trilium | 38k | — | ~2.5k | Automated safety check: Pass | AGPL-3.0 | |
| Chatgpt App Submissionnteract/semiotic | 2.7k | — | ~2.8k | Automated safety check: Pass | Apache-2.0 | |
| Test ScopeGlitterKill/sdl-mcp | 491 | — | ~586 | Automated safety check: Pass | Custom licence | |
| Caliper Harness Smoke Testedonadei/caliper | 207 | — | ~664 | Automated safety check: Pass | MIT |
QwenLM/qwen-code
Guides end-to-end testing of the Qwen Code CLI in headless mode with real model calls, MCP test servers and inspection of raw API traffic.
TriliumNext/Trilium
A skill your agent uses when adding, changing, or reviewing an LLM/MCP tool in Trilium (the defineTools definitions under packages/trilium-core/src/services/llm/tools/ —…
nteract/semiotic
Inspect a ChatGPT Apps MCP server codebase and generate chatgpt-app-submission.json with app info suggestions, tool hint justifications, test cases, and negative test cases, then report review-check…
GlitterKill/sdl-mcp
Determine which test suites are affected by recent code changes and run only the relevant ones.
edonadei/caliper
Runs caliper's smoke evals against the real agent CLIs after a harness or MCP change, with a dry-run plan, failure triage and a report to attach to the PR.
daydreamlive/scope
Test Daydream Scope through its stdio MCP server, including disconnected startup, connecttoscope, direct Python stdio client fallback, and lightweight smoke tests.
cyanheads/pubmed-mcp-server
Scaffold an MCP App tool + UI resource pair. An agent skill from cyanheads/pubmed-mcp-server.
cyanheads/pubmed-mcp-server
Scaffold a new MCP prompt template. An agent skill from cyanheads/pubmed-mcp-server.
cyanheads/pubmed-mcp-server
Scaffold a new MCP resource definition. An agent skill from cyanheads/pubmed-mcp-server.
cyanheads/pubmed-mcp-server
Scaffold a new service integration. An agent skill from cyanheads/pubmed-mcp-server.
cyanheads/pubmed-mcp-server
Authentication, authorization, and multi-tenancy patterns for @cyanheads/mcp-ts-core.
cyanheads/pubmed-mcp-server
Stand up a persistent, self-refreshing local mirror of a bulk upstream dataset with the MirrorService (@cyanheads/mcp-ts-core/mirror).
Works with
Scaffold a test file for an existing tool, resource, or service. Add Test is an agent skill from cyanheads/pubmed-mcp-server. Scaffold a test file for an existing tool, resource, or service.
Add Test fits situations like: the user asks to add tests; improve coverage; A definition exists without a matching test file.
Run `npx skills add cyanheads/pubmed-mcp-server --skill add-test -a claude-code`. Or copy the skill folder (framework-skills/add-test in cyanheads/pubmed-mcp-server) into .claude/skills/add-test in your project. Claude Code loads it when a task matches its description.
Run `npx skills add cyanheads/pubmed-mcp-server --skill add-test -a codex`. Or copy the skill folder (framework-skills/add-test in cyanheads/pubmed-mcp-server) into .agents/skills/add-test in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add cyanheads/pubmed-mcp-server --skill add-test -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/add-test, .gemini/skills/add-test, .github/skills/add-test and .opencode/skills/add-test in your project.
Going by SKILL.md and its folder, Add Test needs the command-line tools its instructions call (bun).
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Add Test is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 4.1k tokens (SKILL.md is roughly 16k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Add Test: Qwen Code E2E Testing (QwenLM/qwen-code, 28k stars), Adding LLM MCP Tools (TriliumNext/Trilium, 38k stars), Chatgpt App Submission (nteract/semiotic, 2.7k stars) and Test Scope (GlitterKill/sdl-mcp, 491 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
cyanheads (a GitHub user) maintains it in cyanheads/pubmed-mcp-server, which has 155 GitHub stars. The repository holds 30 skills in this directory. The repository was last updated on October 4, 2026.
Source: cyanheads/pubmed-mcp-server on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.