diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index c6576c4..ad16d86 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "postman", - "version": "1.0.0", + "version": "1.1.0", "description": "Full API lifecycle management for Claude Code. Sync collections, generate client code, discover APIs, run tests, create mocks, publish docs, and audit security. Powered by the Postman MCP Server.", "author": { "name": "Postman", diff --git a/.github/.markdownlint.json b/.github/.markdownlint.json new file mode 100644 index 0000000..bc0a51f --- /dev/null +++ b/.github/.markdownlint.json @@ -0,0 +1,14 @@ +{ + "MD009": false, + "MD013": false, + "MD022": false, + "MD026": false, + "MD029": false, + "MD031": false, + "MD032": false, + "MD033": false, + "MD034": false, + "MD040": false, + "MD041": false, + "MD060": false +} diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 0000000..82f6693 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,29 @@ +commands: + - changed-files: + - any-glob-to-any-file: "commands/**" + +skills: + - changed-files: + - any-glob-to-any-file: "skills/**" + +agents: + - changed-files: + - any-glob-to-any-file: "agents/**" + +docs: + - changed-files: + - any-glob-to-any-file: + - "README.md" + - "CLAUDE.md" + - "examples/**" + +config: + - changed-files: + - any-glob-to-any-file: + - ".claude-plugin/**" + - ".mcp.json" + - ".github/**" + +examples: + - changed-files: + - any-glob-to-any-file: "examples/**" diff --git a/.github/scripts/check-internal-links.py b/.github/scripts/check-internal-links.py new file mode 100755 index 0000000..102526e --- /dev/null +++ b/.github/scripts/check-internal-links.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +"""Check that internal markdown links resolve to existing files.""" + +import re +import sys +from pathlib import Path + +# Matches [text](path) and ![alt](path) — excludes URLs +LINK_RE = re.compile(r"!?\[([^\]]*)\]\(([^)]+)\)") + + +def check_file_links(file_path: Path, root: Path) -> list[str]: + errors = [] + text = file_path.read_text() + file_dir = file_path.parent + + for match in LINK_RE.finditer(text): + target = match.group(2) + + # Skip external URLs + if target.startswith(("http://", "https://", "mailto:")): + continue + + # Skip anchor-only links + if target.startswith("#"): + continue + + # Strip anchor fragments from file paths + target_path = target.split("#")[0] + if not target_path: + continue + + # Resolve relative to the file's directory + resolved = (file_dir / target_path).resolve() + if not resolved.exists(): + rel = file_path.relative_to(root) + errors.append(f"{rel}: Broken link '{target}' — file not found") + + return errors + + +def main(): + root = Path(__file__).resolve().parent.parent.parent + errors = [] + + for md_file in sorted(root.rglob("*.md")): + # Skip .git and node_modules + parts = md_file.relative_to(root).parts + if any(p.startswith(".git") or p == "node_modules" for p in parts): + continue + errors.extend(check_file_links(md_file, root)) + + if errors: + print("Link check failed:") + for e in errors: + print(f" ✗ {e}") + sys.exit(1) + else: + print("✓ Link check passed") + + +if __name__ == "__main__": + main() diff --git a/.github/scripts/validate-frontmatter.py b/.github/scripts/validate-frontmatter.py new file mode 100755 index 0000000..9c7c87f --- /dev/null +++ b/.github/scripts/validate-frontmatter.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +"""Validate YAML frontmatter in commands, skills, and agents.""" + +import re +import sys +from pathlib import Path +from typing import Optional + +# PyYAML is not guaranteed on all runners, so parse simple YAML manually +FRONTMATTER_RE = re.compile(r"^---\s*\n(.*?)\n---", re.DOTALL) + +KNOWN_TOOLS = {"Bash", "Read", "Write", "Glob", "Grep", "mcp__postman__*"} + + +def parse_frontmatter(text: str) -> Optional[dict]: + match = FRONTMATTER_RE.match(text) + if not match: + return None + result = {} + for line in match.group(1).splitlines(): + line = line.strip() + if not line or line.startswith("#"): + continue + if ":" in line: + key, _, value = line.partition(":") + key = key.strip() + value = value.strip().strip('"').strip("'") + result[key] = value + return result + + +def validate_commands(root: Path) -> list[str]: + errors = [] + commands_dir = root / "commands" + if not commands_dir.is_dir(): + return [f"{commands_dir}: Directory not found"] + + for f in sorted(commands_dir.glob("*.md")): + text = f.read_text() + fm = parse_frontmatter(text) + if fm is None: + errors.append(f"{f.name}: Missing YAML frontmatter") + continue + if "description" not in fm or not fm["description"]: + errors.append(f"{f.name}: Missing required field 'description'") + if "allowed-tools" in fm and fm["allowed-tools"]: + tools = [t.strip() for t in fm["allowed-tools"].split(",")] + for tool in tools: + if tool not in KNOWN_TOOLS: + errors.append(f"{f.name}: Unknown tool '{tool}' in allowed-tools") + + return errors + + +def validate_skills(root: Path) -> list[str]: + errors = [] + skills_dir = root / "skills" + if not skills_dir.is_dir(): + return [f"{skills_dir}: Directory not found"] + + for skill_dir in sorted(skills_dir.iterdir()): + if not skill_dir.is_dir(): + continue + skill_file = skill_dir / "SKILL.md" + if not skill_file.exists(): + errors.append(f"skills/{skill_dir.name}/: Missing SKILL.md") + continue + text = skill_file.read_text() + fm = parse_frontmatter(text) + if fm is None: + errors.append(f"skills/{skill_dir.name}/SKILL.md: Missing YAML frontmatter") + continue + if "name" not in fm or not fm["name"]: + errors.append(f"skills/{skill_dir.name}/SKILL.md: Missing required field 'name'") + if "description" not in fm or not fm["description"]: + errors.append(f"skills/{skill_dir.name}/SKILL.md: Missing required field 'description'") + + return errors + + +def validate_agents(root: Path) -> list[str]: + errors = [] + agents_dir = root / "agents" + if not agents_dir.is_dir(): + return [f"{agents_dir}: Directory not found"] + + required_fields = ["name", "description", "model", "allowed-tools"] + + for f in sorted(agents_dir.glob("*.md")): + text = f.read_text() + fm = parse_frontmatter(text) + if fm is None: + errors.append(f"agents/{f.name}: Missing YAML frontmatter") + continue + for field in required_fields: + if field not in fm or not fm[field]: + errors.append(f"agents/{f.name}: Missing required field '{field}'") + if "allowed-tools" in fm and fm["allowed-tools"]: + tools = [t.strip() for t in fm["allowed-tools"].split(",")] + for tool in tools: + if tool not in KNOWN_TOOLS: + errors.append(f"agents/{f.name}: Unknown tool '{tool}' in allowed-tools") + + return errors + + +def main(): + root = Path(__file__).resolve().parent.parent.parent + errors = [] + + errors.extend(validate_commands(root)) + errors.extend(validate_skills(root)) + errors.extend(validate_agents(root)) + + if errors: + print("Frontmatter validation failed:") + for e in errors: + print(f" ✗ {e}") + sys.exit(1) + else: + print("✓ Frontmatter validation passed") + + +if __name__ == "__main__": + main() diff --git a/.github/scripts/validate-json.py b/.github/scripts/validate-json.py new file mode 100755 index 0000000..19870a5 --- /dev/null +++ b/.github/scripts/validate-json.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +"""Validate JSON config files for the Claude Code plugin.""" + +import json +import sys +from pathlib import Path + + +def validate_plugin_json(path: Path) -> list[str]: + errors = [] + try: + with open(path) as f: + data = json.load(f) + except json.JSONDecodeError as e: + return [f"{path}: Invalid JSON — {e}"] + + required_fields = ["name", "version", "description"] + for field in required_fields: + if field not in data: + errors.append(f"{path}: Missing required field '{field}'") + elif not isinstance(data[field], str) or not data[field].strip(): + errors.append(f"{path}: Field '{field}' must be a non-empty string") + + if "version" in data and isinstance(data["version"], str): + parts = data["version"].split(".") + if len(parts) != 3 or not all(p.isdigit() for p in parts): + errors.append(f"{path}: Field 'version' must be semver (e.g. 1.0.0)") + + return errors + + +def validate_mcp_json(path: Path) -> list[str]: + errors = [] + try: + with open(path) as f: + data = json.load(f) + except json.JSONDecodeError as e: + return [f"{path}: Invalid JSON — {e}"] + + if "mcpServers" not in data: + errors.append(f"{path}: Missing required key 'mcpServers'") + elif not isinstance(data["mcpServers"], dict): + errors.append(f"{path}: 'mcpServers' must be an object") + else: + for name, config in data["mcpServers"].items(): + if "type" not in config: + errors.append(f"{path}: Server '{name}' missing 'type'") + if "url" not in config: + errors.append(f"{path}: Server '{name}' missing 'url'") + + return errors + + +def main(): + root = Path(__file__).resolve().parent.parent.parent + errors = [] + + plugin_json = root / ".claude-plugin" / "plugin.json" + if plugin_json.exists(): + errors.extend(validate_plugin_json(plugin_json)) + else: + errors.append(f"{plugin_json}: File not found") + + mcp_json = root / ".mcp.json" + if mcp_json.exists(): + errors.extend(validate_mcp_json(mcp_json)) + else: + errors.append(f"{mcp_json}: File not found") + + if errors: + print("JSON validation failed:") + for e in errors: + print(f" ✗ {e}") + sys.exit(1) + else: + print("✓ JSON validation passed") + + +if __name__ == "__main__": + main() diff --git a/.github/scripts/validate-structure.py b/.github/scripts/validate-structure.py new file mode 100755 index 0000000..f9e9e96 --- /dev/null +++ b/.github/scripts/validate-structure.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +"""Validate plugin structure integrity — cross-reference components.""" + +import json +import re +import sys +from pathlib import Path + +FRONTMATTER_RE = re.compile(r"^---\s*\n(.*?)\n---", re.DOTALL) + + +def has_frontmatter(path: Path) -> bool: + text = path.read_text() + return FRONTMATTER_RE.match(text) is not None + + +def main(): + root = Path(__file__).resolve().parent.parent.parent + errors = [] + + # 1. plugin.json must exist and be valid + plugin_json = root / ".claude-plugin" / "plugin.json" + if not plugin_json.exists(): + errors.append(".claude-plugin/plugin.json: File not found") + else: + try: + json.loads(plugin_json.read_text()) + except json.JSONDecodeError: + errors.append(".claude-plugin/plugin.json: Invalid JSON") + + # 2. .mcp.json must exist + mcp_json = root / ".mcp.json" + if not mcp_json.exists(): + errors.append(".mcp.json: File not found") + + # 3. Every commands/*.md must have frontmatter + commands_dir = root / "commands" + if commands_dir.is_dir(): + command_files = sorted(commands_dir.glob("*.md")) + if not command_files: + errors.append("commands/: No command files found") + for f in command_files: + if not has_frontmatter(f): + errors.append(f"commands/{f.name}: Missing YAML frontmatter") + else: + errors.append("commands/: Directory not found") + + # 4. Every skills/*/ directory must have a SKILL.md + skills_dir = root / "skills" + if skills_dir.is_dir(): + skill_dirs = sorted([d for d in skills_dir.iterdir() if d.is_dir()]) + if not skill_dirs: + errors.append("skills/: No skill directories found") + for d in skill_dirs: + skill_file = d / "SKILL.md" + if not skill_file.exists(): + errors.append(f"skills/{d.name}/: Missing SKILL.md") + elif not has_frontmatter(skill_file): + errors.append(f"skills/{d.name}/SKILL.md: Missing YAML frontmatter") + else: + errors.append("skills/: Directory not found") + + # 5. Every agents/*.md must have frontmatter + agents_dir = root / "agents" + if agents_dir.is_dir(): + agent_files = sorted(agents_dir.glob("*.md")) + if not agent_files: + errors.append("agents/: No agent files found") + for f in agent_files: + if not has_frontmatter(f): + errors.append(f"agents/{f.name}: Missing YAML frontmatter") + else: + errors.append("agents/: Directory not found") + + # 6. Check for stray markdown files in root (not README, CLAUDE, LICENSE, or examples) + expected_root_md = {"README.md", "CLAUDE.md", "LICENSE"} + for f in sorted(root.glob("*.md")): + if f.name not in expected_root_md: + errors.append(f"{f.name}: Unexpected markdown file in repo root") + + if errors: + print("Structure validation failed:") + for e in errors: + print(f" ✗ {e}") + sys.exit(1) + else: + print("✓ Structure validation passed") + + +if __name__ == "__main__": + main() diff --git a/.github/workflows/plugin-checks.yml b/.github/workflows/plugin-checks.yml new file mode 100644 index 0000000..bd76f16 --- /dev/null +++ b/.github/workflows/plugin-checks.yml @@ -0,0 +1,59 @@ +name: Plugin Checks + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + json-validation: + name: JSON Validation + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - run: python .github/scripts/validate-json.py + + frontmatter-validation: + name: Frontmatter Validation + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - run: python .github/scripts/validate-frontmatter.py + + markdown-lint: + name: Markdown Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: "20" + - run: npm install -g markdownlint-cli + - run: markdownlint "**/*.md" --config .github/.markdownlint.json + + link-check: + name: Link Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - run: python .github/scripts/check-internal-links.py + + structure-integrity: + name: Structure Integrity + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - run: python .github/scripts/validate-structure.py diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml new file mode 100644 index 0000000..05e7e9c --- /dev/null +++ b/.github/workflows/pr-labeler.yml @@ -0,0 +1,18 @@ +name: PR Labeler + +on: + pull_request_target: + types: [opened, synchronize, reopened] + +permissions: + contents: read + pull-requests: write + +jobs: + label: + name: Label PR + runs-on: ubuntu-latest + steps: + - uses: actions/labeler@v5 + with: + configuration-path: .github/labeler.yml diff --git a/.mcp.json b/.mcp.json index 388b07f..7a4d6da 100644 --- a/.mcp.json +++ b/.mcp.json @@ -4,8 +4,9 @@ "type": "http", "url": "https://mcp.postman.com/mcp", "headers": { - "Authorization": "Bearer ${POSTMAN_API_KEY}", - "X-Source": "claude-code-plugin" + "X-Source": "claude-code-plugin", + "X-Plugin-Version": "1.1.0", + "User-Agent": "postman-claude-code-plugin/1.1.0" } } } diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..fa8db36 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,67 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What This Is + +The Postman Plugin for Claude Code — a pure-markdown, configuration-driven plugin that provides full API lifecycle management via the Postman MCP Server. No compiled code, no runtime dependencies, no build step. + +## Repository Structure + +``` +.claude-plugin/plugin.json # Plugin manifest (name, version, metadata) +.mcp.json # MCP server auto-config (Postman MCP at mcp.postman.com) +commands/*.md # 11 slash commands (/postman:) +skills/*/SKILL.md # 8 skills (routing, knowledge, agent-ready APIs, CLI, send-request, generate-spec, run-collection, context) +agents/readiness-analyzer.md # Sub-agent for API readiness analysis +examples/ # Sample output (readiness report) +assets/ # GIFs for README +``` + +## How the Plugin Works + +- Claude Code discovers components via `.claude-plugin/plugin.json` manifest +- `.mcp.json` auto-configures the Postman MCP server, providing `mcp__postman__*` tools (111 tools) +- MCP commands use the cloud Postman MCP Server — requires `POSTMAN_API_KEY` environment variable +- CLI commands use the locally installed Postman CLI (`npm install -g postman-cli`) — requires `postman login` +- Plugin is loaded with `claude --plugin-dir /path/to/postman-claude-code-plugin` + +## Component Conventions + +**Commands** (`commands/*.md`): YAML front matter with `description` and `allowed-tools`. Each defines a structured workflow invoked as `/postman:`. +- MCP commands: setup, sync, search, test, mock, docs, security +- CLI commands: request, generate-spec, run-collection + +**Skills** (`skills/*/SKILL.md`): YAML front matter with `name`, `description`, `user-invocable`. Auto-injected context, not directly invoked. `postman-routing` routes requests to commands; `postman-knowledge` provides MCP tool guidance; `agent-ready-apis` provides readiness criteria; `postman-cli` provides CLI and git sync file structure knowledge; `postman-context` provides API discovery, exploration, and code generation from real API definitions via `postman context` CLI commands. + +**Agent** (`agents/readiness-analyzer.md`): YAML front matter with `name`, `description`, `model`, `allowed-tools`. Runs as a sub-agent (sonnet model) for deep API readiness analysis (8 pillars, 48 checks). + +## Key MCP Limitations + +These are documented in `skills/postman-knowledge/mcp-limitations.md` and must be respected in all commands: + +- `searchPostmanElements` searches PUBLIC network only — use `getWorkspaces` + `getCollections` for private content +- `generateCollection` and `syncCollectionWithSpec` return HTTP 202 — must poll for completion +- `syncCollectionWithSpec` supports OpenAPI 3.0 only — use `updateSpecFile` + `generateCollection` for Swagger 2.0 or OpenAPI 3.1 +- `createCollection` creates flat collections — nest via `createCollectionFolder` + `createCollectionRequest` +- `createSpec` struggles with specs >50KB — decompose into collection items instead + +## Postman CLI Commands + +Three commands use the Postman CLI instead of MCP. They require `postman-cli` installed locally (`npm install -g postman-cli`) and authenticated (`postman login`). If CLI is not found, show install instructions and stop. + +- `/postman:request` — Send HTTP requests via `postman request ` +- `/postman:generate-spec` — Scan code for API routes, generate OpenAPI 3.0 YAML, validate with `postman spec lint` +- `/postman:run-collection` — Run collection tests via `postman collection run ` using cloud IDs from `.postman/resources.yaml` +- `/postman:context` — Discover, explore, and install APIs via `postman context`. Searches Postman's API network, fetches real API definitions, and generates client code from them. + +CLI commands work with Postman's git sync structure: `postman/collections/` (v3 folder format), `postman/environments/`, `postman/specs/`, and `.postman/resources.yaml` for cloud ID mapping. + +## Development Notes + +- There is no build, lint, or test suite — all "code" is instructional markdown +- Changes are purely editing markdown files with YAML front matter +- When adding a new command, follow the existing front matter pattern in `commands/` +- When adding a new skill, create `skills//SKILL.md` with proper front matter +- The `allowed-tools` field in front matter controls what tools a command/agent can use +- CLI commands need `Bash` in `allowed-tools`; MCP commands need `mcp__postman__*` diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2e4a4c9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,200 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please also get in touch with + Anthropic to be added as a Known User if you wish. + + Copyright 2026 Postman, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 6d4782d..91ee4f5 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,17 @@ +

+ Postman +

+ # Postman Plugin for Claude Code The Postman Plugin provides a single, simple install for Claude Code. It provides full API lifecycle management, and best practices when working with Postman APIs. -What's included: +https://github.com/user-attachments/assets/4fdaa183-e8cd-4658-b5f4-f9d3684fbf46 + +## What's included: - Commands for setting up the Postman MCP Server (no more manual installs!), working with Collections, Tests, Mock Servers, and generating code and documentation from Collections - Skills for Postman Routing, API best practices, and API OWASP security reviews -- Agent for reviewing API production readiness and providing recommendations based on the Postman API Readiness Guide." +- Agent for reviewing API production readiness and providing recommendations based on the Postman API Readiness Guide. ## Installation @@ -42,16 +48,18 @@ claude --plugin-dir /path/to/postman-claude-code-plugin That's it. The plugin auto-configures the Postman MCP Server, verifies your connection, and lists your workspaces. You're ready to go. -Get your API key at [postman.postman.co/settings/me/api-keys](https://postman.postman.co/settings/me/api-keys). +Get your API key at [go.postman.co/settings/me/api-keys](https://go.postman.co/settings/me/api-keys). ## Commands +https://github.com/user-attachments/assets/a6d19b9b-aa1d-4815-8379-8b76beeba2df + | Command | What It Does | |---------|-------------| | `/postman:setup` | Configure API key, verify connection, select workspace | | `/postman:sync` | Create or update Postman collections from OpenAPI specs | -| `/postman:codegen` | Generate typed client code from any Postman collection | -| `/postman:search` | Find APIs across your private workspaces | +| `/postman:search` | Find APIs across your org's private network, your workspaces and the public Postman network | +| `/postman:context` | Fetch real API definitions, generate and maintain typed client code | | `/postman:test` | Run collection tests, diagnose failures, suggest fixes | | `/postman:mock` | Create mock servers for frontend development | | `/postman:docs` | Generate, improve, and publish API documentation | @@ -59,6 +67,8 @@ Get your API key at [postman.postman.co/settings/me/api-keys](https://postman.po ## What You Can Do +https://github.com/user-attachments/assets/8fa52dc1-6e0b-4fb5-8005-e2d614a1d50e + ### Sync your API to Postman ``` "Sync my OpenAPI spec with Postman" @@ -106,12 +116,13 @@ Get your API key at [postman.postman.co/settings/me/api-keys](https://postman.po You don't need to remember command names. The plugin's routing skill detects your intent and runs the right command: - "Sync my collection" routes to `/postman:sync` -- "Generate a client" routes to `/postman:codegen` - "Check for vulnerabilities" routes to `/postman:security` - "Is my API agent-ready?" triggers the readiness analyzer ## API Readiness Analyzer +https://github.com/user-attachments/assets/20e4edfb-d7c6-493d-82ff-79dd0403cd83 + The built-in readiness analyzer evaluates APIs for AI agent compatibility across 8 pillars: | Pillar | What It Checks | @@ -141,8 +152,14 @@ The plugin bundles a `.mcp.json` file that auto-configures the [Postman MCP Serv Apache-2.0 +## See Also + +- [Postman Plugin for Cursor](https://github.com/Postman-Devrel/cursor-postman-plugin) - Same capabilities, adapted for Cursor IDE +- [Postman Agent Skills](https://github.com/Postman-Devrel/agent-skills) - Portable skills for any skills.sh-compatible agent +- [Postman Cursor Rules](https://github.com/Postman-Devrel/postman-cursor-rules) - Lightweight MCP config + rules for Cursor + ## Links - [Postman MCP Server Docs](https://learning.postman.com/docs/developer/postman-mcp-server/) -- [Get a Postman API Key](https://postman.postman.co/settings/me/api-keys) +- [Get a Postman API Key](https://go.postman.co/settings/me/api-keys) - [Postman Status](https://status.postman.com) diff --git a/commands/codegen.md b/commands/codegen.md deleted file mode 100644 index b884d71..0000000 --- a/commands/codegen.md +++ /dev/null @@ -1,93 +0,0 @@ ---- -description: Generate typed client code from Postman collections. Reads your private APIs and writes production-ready code. -allowed-tools: Bash, Read, Write, Glob, Grep, mcp__postman__* ---- - -# Generate Client Code - -Generate typed client code from Postman collections. Reads your private API definitions and writes production-ready code that matches your project's conventions. - -## Prerequisites - -The Postman MCP Server must be connected. If MCP tools aren't available, tell the user: "Run `/postman:setup` to configure the Postman MCP Server." - -## Workflow - -### Step 1: Find the API - -1. Call `getWorkspaces` to get the user's workspace ID. If multiple workspaces exist, ask which to use. -2. Call `getCollections` with the `workspace` parameter. Use the `name` filter if the user specified an API name. -3. If no results in private workspace, fall back to `searchPostmanElements` to search the public Postman network. -4. If multiple matches, list them and ask which one. -5. Call `getCollection` to get the full collection. -6. Call `getSpecDefinition` if a linked spec exists (richer type info). - -### Step 2: Understand the API Shape - -For the target collection: -1. Call `getCollectionFolder` for each folder to understand resource grouping -2. Call `getCollectionRequest` for each relevant endpoint to get: - - HTTP method and URL - - Request headers and auth - - Request body schema - - Path and query parameters -3. Call `getCollectionResponse` for each request to get: - - Response status codes - - Response body shapes (for typing) - - Error response formats -4. Call `getEnvironment` to understand base URLs and variables -5. Call `getCodeGenerationInstructions` for any Postman-specific codegen guidance - -### Step 3: Detect Project Language - -If the user doesn't specify a language, detect from the project: -- `package.json` or `tsconfig.json` → TypeScript/JavaScript -- `requirements.txt` or `pyproject.toml` → Python -- `go.mod` → Go -- `Cargo.toml` → Rust -- `pom.xml` or `build.gradle` → Java -- `Gemfile` → Ruby - -### Step 4: Generate Code - -Generate a typed client class or module with: -- Method per endpoint with proper parameters -- Request/response types from collection schemas -- Authentication handling (from collection auth config) -- Error handling based on documented error responses -- Environment-based configuration (base URL from env vars) -- Pagination support if the API uses it - -**Code conventions:** -- Match the project's existing style (imports, formatting, naming) -- Include JSDoc/docstrings from collection descriptions -- Use the project's HTTP library if one exists (axios, fetch, requests, etc.) -- Write to a sensible file path (e.g., `src/clients/.ts`) - -### Step 5: Present - -``` -Generated: src/clients/users-api.ts - - Endpoints covered: - GET /users → getUsers(filters) - GET /users/{id} → getUser(id) - POST /users → createUser(data) - PUT /users/{id} → updateUser(id, data) - DELETE /users/{id} → deleteUser(id) - - Types generated: - User, CreateUserRequest, UpdateUserRequest, - UserListResponse, ApiError - - Auth: Bearer token (from USERS_API_TOKEN env var) - Base URL: from USERS_API_BASE_URL env var -``` - -## Error Handling - -- **MCP not configured:** "Run `/postman:setup` to configure the Postman MCP Server." -- **Collection not found:** "No collection matching that name. Run `/postman:search` to find available APIs." -- **401 Unauthorized:** "Your Postman API key was rejected. Generate a new one at https://postman.postman.co/settings/me/api-keys and run `/postman:setup`." -- **Empty collection:** "This collection has no requests. Add endpoints in Postman or use `/postman:sync` to push a spec." -- **Language not detected:** Ask the user what language to generate. diff --git a/commands/docs.md b/commands/docs.md index 0882538..c856c30 100644 --- a/commands/docs.md +++ b/commands/docs.md @@ -82,7 +82,7 @@ If both a spec and collection exist, keep them in sync: ## Error Handling - **MCP not configured:** Local markdown docs can be generated without MCP. For Postman publishing: "Run `/postman:setup` to configure the Postman MCP Server." -- **401 Unauthorized:** "Your Postman API key was rejected. Generate a new one at https://postman.postman.co/settings/me/api-keys and run `/postman:setup`." +- **401 Unauthorized:** "Your Postman API key was rejected. Generate a new one at https://go.postman.co/settings/me/api-keys and run `/postman:setup`." - **Invalid spec:** Report parse errors and offer to fix common YAML/JSON syntax issues. - **Plan limitations:** "Publishing documentation may require a paid Postman plan. Check https://www.postman.com/pricing/" - **Too many results:** Ask the user to specify a collection by name. diff --git a/commands/generate-spec.md b/commands/generate-spec.md new file mode 100644 index 0000000..032456b --- /dev/null +++ b/commands/generate-spec.md @@ -0,0 +1,55 @@ +--- +description: Generate or update an OpenAPI spec from your codebase +--- + +Generate or update an OpenAPI 3.0 specification by analyzing API routes in your codebase. + +## Step 1: Check for Existing Spec + +```bash +ls postman/specs/**/*.yaml postman/specs/**/*.yml postman/specs/**/*.json 2>/dev/null +ls openapi.yaml openapi.yml swagger.yaml swagger.yml 2>/dev/null +``` + +If a spec exists, read it to understand current state. You'll update it rather than replace it. + +## Step 2: Discover API Endpoints + +Scan the project for route definitions based on the framework: + +- **Express/Node**: `app.get()`, `router.post()`, `@Get()` (NestJS) +- **Python**: `@app.route()`, `@router.get()` (FastAPI), `path()` (Django) +- **Go**: `http.HandleFunc()`, `r.GET()` (Gin/Echo) +- **Java**: `@GetMapping`, `@PostMapping`, `@RequestMapping` +- **Ruby**: `get`, `post`, `resources` in routes.rb + +Read the route files and extract: methods, paths, parameters, request bodies, response schemas, auth requirements. + +## Step 3: Generate or Update the Spec + +Write a valid OpenAPI 3.0.3 YAML spec including: +- `info` with title, version, description +- `servers` with local dev URL +- `paths` with all discovered endpoints +- `components/schemas` with models derived from code (types, models, structs) +- `components/securitySchemes` if auth is used + +**When updating**: Add new endpoints, update changed ones, remove endpoints no longer in code. Preserve existing descriptions and examples. + +**When creating**: Write to `postman/specs/openapi.yaml`. + +## Step 4: Validate + +```bash +postman spec lint ./postman/specs/openapi.yaml +``` + +Fix any validation errors and re-run until clean. + +## Step 5: Report + +Show what was created or changed: +- Endpoints documented (count and list) +- Schemas defined +- Changes from previous spec (if updating) +- Validation result diff --git a/commands/mock.md b/commands/mock.md index 1020dd7..7b86456 100644 --- a/commands/mock.md +++ b/commands/mock.md @@ -96,6 +96,6 @@ If the user wants the mock publicly accessible: - **MCP not configured:** "Run `/postman:setup` to configure the Postman MCP Server." - **No examples in collection:** Auto-generate from schemas (Step 2). If no schemas either, ask the user to provide sample responses. -- **401 Unauthorized:** "Your Postman API key was rejected. Generate a new one at https://postman.postman.co/settings/me/api-keys and run `/postman:setup`." +- **401 Unauthorized:** "Your Postman API key was rejected. Generate a new one at https://go.postman.co/settings/me/api-keys and run `/postman:setup`." - **MCP timeout:** Retry once. If it still fails, check https://status.postman.com for outages. - **Plan limitations:** "Mock server creation may require a Postman Basic plan or higher for increased usage limits." diff --git a/commands/run-collection.md b/commands/run-collection.md new file mode 100644 index 0000000..b344a1f --- /dev/null +++ b/commands/run-collection.md @@ -0,0 +1,55 @@ +--- +description: Run Postman collection tests using the CLI +--- + +Run Postman collection tests to verify your API endpoints. + +## Step 1: Find Collections and IDs + +List collection folders and look up their cloud IDs: + +```bash +ls postman/collections/ +cat .postman/resources.yaml +``` + +The `cloudResources.collections` section maps local collection paths to cloud IDs. + +If no collections found, tell the user and stop. +If one collection, use it directly. +If multiple, list them and ask which to run. + +## Step 2: Run the Collection + +Run by **collection ID** (from `.postman/resources.yaml`): + +```bash +postman collection run +``` + +Common options: +```bash +# Stop on first failure +postman collection run --bail + +# With request timeout +postman collection run --timeout-request 10000 + +# With environment +postman collection run -e ./postman/environments/.json + +# Override environment variable +postman collection run --env-var "base_url=http://localhost:3000" +``` + +## Step 3: Parse and Report Results + +Parse the CLI output for pass/fail counts, failed test names, error messages, and status codes. + +## Step 4: Handle Failures + +If tests fail: +1. Analyze error messages +2. Read relevant source code +3. Suggest fixes +4. After fixes, re-run to verify diff --git a/commands/search.md b/commands/search.md index 2367578..f27f872 100644 --- a/commands/search.md +++ b/commands/search.md @@ -15,14 +15,13 @@ The Postman MCP Server must be connected. If MCP tools aren't available, tell th ### Step 1: Search -1. Call `getWorkspaces` to get the user's workspace ID. If multiple workspaces exist, ask which to use. -2. Call `getCollections` with the `workspace` parameter. Use the `name` filter to narrow results. -3. If results are sparse, broaden the search: - - Call `getTaggedEntities` to find collections by tag - - Call `getWorkspaces` to search across all workspaces - - As a fallback for public APIs, call `searchPostmanElements` with the user's query +1. Call `searchPostmanElementsInPrivateNetwork` with the user's query. This searches the organization's private API network and is the **primary** search path. +2. If private network results are sparse or private network search is not available, broaden the search: + - Call `getWorkspaces` to get the user's workspace ID. If multiple workspaces exist, ask which to use. Then use `getCollections` with the `workspace` parameter. Use the `name` filter to narrow results. + - Call `getTaggedEntities` to find collections by tag. +3. If the user is looking for a public/third-party API (e.g., Stripe, GitHub, Twilio), call `searchPostmanElementsInPublicNetwork` with the user's query. -**Important:** `searchPostmanElements` only searches the PUBLIC Postman network, not private workspaces. Always search private workspace first using `getCollections`. +**Important:** Default to `searchPostmanElementsInPrivateNetwork` for trusted APIs in user's organisation. Only use `searchPostmanElementsInPublicNetwork` when the user explicitly wants public/third-party APIs or private search returns no results. ### Step 2: Drill Into Results @@ -52,8 +51,6 @@ Yes, you can get a user's email via the API. "name": "Jane Smith", "role": "admin" } - - Want me to generate a client for this API? → /postman:codegen ``` **When not found:** @@ -74,6 +71,6 @@ List relevant collections with endpoint counts, then ask which to explore furthe ## Error Handling - **MCP not configured:** "Run `/postman:setup` to configure the Postman MCP Server." -- **No results:** "Nothing matched in your workspace. Try different keywords, or search the public Postman network with broader terms." -- **401 Unauthorized:** "Your Postman API key was rejected. Generate a new one at https://postman.postman.co/settings/me/api-keys and run `/postman:setup`." +- **No results:** "Nothing matched in your private API network. Try different keywords, browse in user's workspaces, or search the public Postman network." +- **401 Unauthorized:** "Your Postman API key was rejected. Generate a new one at https://go.postman.co/settings/me/api-keys and run `/postman:setup`." - **Too many results:** Ask the user to be more specific. Suggest filtering by workspace or using tags. diff --git a/commands/security.md b/commands/security.md index 0bb8273..273c9e3 100644 --- a/commands/security.md +++ b/commands/security.md @@ -123,7 +123,7 @@ After fixes, re-run the audit to show improvement. ## Error Handling - **MCP not configured:** Local spec auditing works without MCP. For Postman-specific checks: "Run `/postman:setup` to configure the Postman MCP Server." -- **401 Unauthorized:** "Your Postman API key was rejected. Generate a new one at https://postman.postman.co/settings/me/api-keys and run `/postman:setup`." +- **401 Unauthorized:** "Your Postman API key was rejected. Generate a new one at https://go.postman.co/settings/me/api-keys and run `/postman:setup`." - **No spec found:** Ask the user for the path. Offer to audit a Postman collection directly via MCP. - **Spec too large:** For large specs (100+ endpoints), audit in batches by tag or path prefix. - **Plan limitations:** "Some audit features may require a paid Postman plan. Check https://www.postman.com/pricing/" diff --git a/commands/send-request.md b/commands/send-request.md new file mode 100644 index 0000000..06e8c48 --- /dev/null +++ b/commands/send-request.md @@ -0,0 +1,33 @@ +--- +description: Send HTTP requests using Postman CLI +--- + +Send an HTTP request using the Postman CLI. Ask the user what URL and method they want, or detect it from context. + +## Step 1: Determine Request Details + +Ask the user for: +- URL to send the request to +- HTTP method (default: GET) +- Any headers, body, or auth needed + +If the user wants to send a request from a collection, find collection folders in `postman/collections/` and read the `*.request.yaml` files to extract method and URL. Collections use the v3 folder format. + +## Step 2: Build and Execute + +```bash +postman request "" +``` + +**With headers:** add `-H "Header: value"` +**With body:** add `-d '{"key": "value"}'` +**With bearer auth:** add `--auth-bearer-token ""` +**With API key:** add `--auth-apikey-key "" --auth-apikey-value ""` +**With basic auth:** add `--auth-basic-username "" --auth-basic-password ""` +**With environment:** add `-e ./postman/environments/.json` + +Always show the exact command before running it. + +## Step 3: Report Results + +Parse the response and report status code, response time, and body. Suggest fixes for errors (auth issues, connection problems, invalid URLs). diff --git a/commands/setup.md b/commands/setup.md index 3a7426e..f628f79 100644 --- a/commands/setup.md +++ b/commands/setup.md @@ -1,5 +1,5 @@ --- -description: Set up Postman MCP Server. Configure API key, verify connection, select workspace. +description: Set up Postman MCP Server. Authenticate via OAuth or API key, verify connection, select workspace. allowed-tools: mcp__postman__* --- @@ -13,16 +13,55 @@ Walk the user through Postman setup for Claude Code. Validate everything works b Verify the Postman MCP Server is available by calling `getAuthenticatedUser`. -**If it works:** Skip to Step 3 (workspace verification). +**If it works:** Skip to Step 4 (workspace verification). -**If it fails or tools aren't available:** Proceed to Step 2. +**If it fails:** Check whether `mcp__postman__authenticate` is available. +- Available → offer the user a choice: **OAuth (recommended)** or **API key**. Default to OAuth unless they ask for API key. +- Not available → the MCP server isn't loaded. Show the "MCP tools not available" error below and stop. -### Step 2: Configure API Key +### Step 2: OAuth Authentication (Recommended) +Present: +``` +Let's connect your Postman account via OAuth — no key copying required. + +I'll generate an authorization URL. Open it in your browser, sign in, and paste the callback URL back here. +``` + +1. Call `mcp__postman__authenticate` — it returns an authorization URL. +2. Show the URL: + ``` + Open this URL in your browser: + + + After you authorize, your browser will redirect to a localhost URL. + The page may not load — that's expected. Copy the full URL from the address bar and paste it here. + ``` +3. Wait for the user to paste the callback URL. +4. Call `mcp__postman__complete_authentication` with the pasted URL as `callback_url`. +5. The MCP server may restart after saving the OAuth token, temporarily dropping the connection. This is expected. + - Wait a few seconds, then retry `getAuthenticatedUser` up to 3 times with short pauses between attempts. + - If tools become unavailable (server disconnected), tell the user: + ``` + The MCP server is restarting after saving your credentials — this is normal. + Give it a moment and I'll retry the connection... + ``` + - If tools are still unavailable after retries: + ``` + The server hasn't reconnected yet. Restart Claude Code and run /postman:setup again. + Your OAuth token is already saved — you won't need to re-authorize. + ``` +6. Once `getAuthenticatedUser` succeeds, proceed to Step 4. + +**If OAuth fails:** "OAuth didn't complete. You can try again or use an API key instead — just say 'use API key'." → offer Step 3. + +### Step 3: API Key Authentication (Alternative) + +Present: ``` -Let's set up Postman for Claude Code. +Let's set up Postman using an API key. -1. Go to: https://postman.postman.co/settings/me/api-keys +1. Go to: https://go.postman.co/settings/me/api-keys 2. Click "Generate API Key" 3. Name it "Claude Code" 4. Copy the key (starts with PMAK-) @@ -32,21 +71,22 @@ Then set it as an environment variable: export POSTMAN_API_KEY=PMAK-your-key-here Add it to your shell profile (~/.zshrc or ~/.bashrc) to persist across sessions. +When done, let me know and I'll verify the connection. ``` Wait for the user to confirm they've set the key. Then verify with `getAuthenticatedUser`. -**If 401:** "API key was rejected. Check for extra spaces or generate a new one at https://postman.postman.co/settings/me/api-keys" +**If 401:** "API key was rejected. Check for extra spaces or generate a new one at https://go.postman.co/settings/me/api-keys" **If timeout:** "Can't reach the Postman MCP Server. Check your network and https://status.postman.com" -### Step 3: Workspace Verification +### Step 4: Workspace Verification -After successful connection: +After successful connection (either auth method): -1. Call `getWorkspaces` to list workspaces -2. Call `getCollections` with the first workspace ID to count collections -3. Call `getAllSpecs` with the workspace ID to count specs +1. Call `getWorkspaces` to list workspaces. +2. Call `getCollections` with the first workspace ID to count collections. +3. Call `getAllSpecs` with the workspace ID to count specs. Present: ``` @@ -63,10 +103,10 @@ If workspace is empty: ``` Your workspace is empty. You can: /postman:sync — Push a local OpenAPI spec to Postman - /postman:search — Search the public Postman API Network + /postman:search — Search for APIs across your org's private network or the public Postman network ``` -### Step 4: Suggest First Command +### Step 5: Suggest First Command Based on what the user has: @@ -75,7 +115,6 @@ Based on what the user has: Try one of these: /postman:search — Find APIs across your workspace /postman:test — Run collection tests - /postman:codegen — Generate client code from a collection ``` **Has specs but no collections:** @@ -93,7 +132,10 @@ Try this: ## Error Handling - **MCP tools not available:** "The Postman MCP Server isn't loaded. Make sure the plugin is installed and restart Claude Code." -- **API key not set:** Walk through Step 2 above. -- **401 Unauthorized:** "Your API key was rejected. Generate a new one at https://postman.postman.co/settings/me/api-keys" +- **OAuth callback invalid:** "That URL doesn't look right — make sure you copied the full address bar URL including `?code=` and `&state=`." +- **OAuth flow expired:** "The authorization URL has expired. Run `/postman:setup` again to get a fresh one." +- **MCP server disconnected after OAuth:** The server restarts after saving credentials. Retry `getAuthenticatedUser` up to 3 times. If still unavailable, tell the user to restart Claude Code — the token is saved, no re-auth needed. +- **API key not set:** Walk through Step 3 above. +- **401 Unauthorized:** "Authentication failed. Try `/postman:setup` to re-authenticate via OAuth, or generate a new API key at https://go.postman.co/settings/me/api-keys" - **Network timeout:** "Can't reach the Postman MCP Server. Check your network and https://status.postman.com for outages." - **Plan limitations:** "Some features (team workspaces, monitors) require a paid Postman plan. Core commands work on all plans." diff --git a/commands/sync.md b/commands/sync.md index 3b55ea8..c3585f7 100644 --- a/commands/sync.md +++ b/commands/sync.md @@ -79,7 +79,7 @@ Collection synced: "Pet Store API" (15 requests) - **MCP not configured:** "Run `/postman:setup` to configure the Postman MCP Server." - **MCP timeout:** Retry once. If `generateCollection` or `syncCollectionWithSpec` times out, the spec may be too large. Suggest breaking it into smaller specs by domain. -- **401 Unauthorized:** "Your Postman API key was rejected. Generate a new one at https://postman.postman.co/settings/me/api-keys and run `/postman:setup`." +- **401 Unauthorized:** "Your Postman API key was rejected. Generate a new one at https://go.postman.co/settings/me/api-keys and run `/postman:setup`." - **Invalid spec:** Report specific parse errors with line numbers. Offer to fix common YAML/JSON syntax issues. - **Async operation stuck:** If polling shows no progress after 30 seconds, inform the user and suggest checking the Postman app directly. - **Plan limitations:** "Workspace creation may be limited on free plans. Using your default workspace instead." diff --git a/commands/test.md b/commands/test.md index 27ad1f3..19c40ee 100644 --- a/commands/test.md +++ b/commands/test.md @@ -79,6 +79,6 @@ If the tests themselves need updating (not the API): - **MCP not configured:** "Run `/postman:setup` to configure the Postman MCP Server." - **Collection not found:** "No collection matching that name. Run `/postman:search` to find available collections, or `/postman:sync` to create one." -- **401 Unauthorized:** "Your Postman API key was rejected. Generate a new one at https://postman.postman.co/settings/me/api-keys and run `/postman:setup`." +- **401 Unauthorized:** "Your Postman API key was rejected. Generate a new one at https://go.postman.co/settings/me/api-keys and run `/postman:setup`." - **MCP timeout:** Retry once. For large collections, suggest running a single folder to narrow the test run. - **Plan limitations:** "Collection runs may require a Postman Basic plan or higher for increased limits." diff --git a/skills/generate-spec/SKILL.md b/skills/generate-spec/SKILL.md new file mode 100644 index 0000000..74cb4c5 --- /dev/null +++ b/skills/generate-spec/SKILL.md @@ -0,0 +1,340 @@ +--- +name: generate-spec +description: Generate or update an OpenAPI specification from code - use when user says "generate spec", "create spec", "create openapi spec", "update spec", "generate API documentation", "create API definition", "write openapi", "document my API", "create swagger", or wants to create/update an API specification from their codebase +--- + +You are an API specification assistant that generates and updates OpenAPI 3.0 specifications by analyzing the user's codebase. + +## When to Use This Skill + +Trigger this skill when: +- User asks to "generate a spec" or "create an OpenAPI spec" +- User wants to "document my API" or "create API documentation" +- User says "update the spec" or "sync spec with code" +- User asks to "create a swagger file" or "write an API definition" +- User wants to generate an API spec from their existing routes/endpoints + +--- + +## Step 1: Discover API Endpoints in the Codebase + +Scan the project for API route definitions. Check common patterns by framework: + +**Express.js / Node.js:** +```bash +# Find route files +find . -type f \( -name "*.js" -o -name "*.ts" \) -not -path "*/node_modules/*" | head -30 +``` +Look for: `app.get()`, `app.post()`, `router.get()`, `router.post()`, `@Get()`, `@Post()` (NestJS) + +**Python (Flask/Django/FastAPI):** +```bash +find . -type f -name "*.py" -not -path "*/.venv/*" -not -path "*/venv/*" | head -30 +``` +Look for: `@app.route()`, `@router.get()`, `path()`, `url()`, `@app.get()` (FastAPI) + +**Go:** +```bash +find . -type f -name "*.go" -not -path "*/vendor/*" | head -30 +``` +Look for: `http.HandleFunc()`, `r.GET()`, `e.GET()` (Echo), `router.Handle()` + +**Java (Spring):** +```bash +find . -type f -name "*.java" | head -30 +``` +Look for: `@GetMapping`, `@PostMapping`, `@RequestMapping`, `@RestController` + +**Ruby (Rails):** +```bash +find . -type f -name "routes.rb" -o -name "*controller*.rb" | head -20 +``` +Look for: `get`, `post`, `resources`, `namespace` + +Read the relevant source files to extract: +- HTTP methods (GET, POST, PUT, PATCH, DELETE) +- URL paths and path parameters +- Query parameters +- Request body schemas (from validation, types, or models) +- Response schemas (from return types, serializers, or examples) +- Authentication requirements +- Status codes + +--- + +## Step 2: Check for Existing Spec + +Look for an existing OpenAPI spec to update: + +```bash +# Check Postman specs directory +ls postman/specs/**/*.yaml postman/specs/**/*.yml postman/specs/**/*.json 2>/dev/null + +# Check common root locations +ls openapi.yaml openapi.yml openapi.json swagger.yaml swagger.yml swagger.json api-spec.yaml 2>/dev/null +``` + +**If an existing spec is found:** +- Read it to understand current state +- Identify what's changed (new endpoints, modified schemas, removed routes) +- Update it preserving existing descriptions, examples, and custom fields +- Tell user what was added/changed/removed + +**If no spec exists:** +- Create a new one at `postman/specs/openapi.yaml` (Postman's standard location) +- Create the `postman/specs/` directory if needed + +--- + +## Step 3: Generate the OpenAPI 3.0 Spec + +Build a valid OpenAPI 3.0 specification in YAML format. Follow this structure: + +```yaml +openapi: 3.0.3 +info: + title: + version: + description: +servers: + - url: http://localhost: + description: Local development server +paths: + /endpoint: + get: + summary: + description: + operationId: + tags: + - + parameters: + - name: id + in: path + required: true + schema: + type: string + description: + responses: + "200": + description: Successful response + content: + application/json: + schema: + $ref: "#/components/schemas/ModelName" + "400": + description: Bad request + "401": + description: Unauthorized + "404": + description: Not found + "500": + description: Internal server error + post: + summary: + operationId: + tags: + - + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/CreateModel" + responses: + "201": + description: Created successfully + content: + application/json: + schema: + $ref: "#/components/schemas/ModelName" +components: + schemas: + ModelName: + type: object + required: + - id + - name + properties: + id: + type: string + description: Unique identifier + name: + type: string + description: Display name + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + apiKey: + type: apiKey + in: header + name: X-API-Key +``` + +### Key rules for generating the spec: + +1. **Derive from code, don't guess** — Only include endpoints that actually exist in the codebase +2. **Extract real schemas** — Use model definitions, TypeScript types, Pydantic models, or struct definitions to build component schemas +3. **Include all status codes** — Add response codes the endpoint actually returns (from error handling in code) +4. **Use $ref for shared schemas** — Define models once in `components/schemas` and reference them +5. **Group with tags** — Use tags based on route file grouping or resource names +6. **operationId** — Generate unique camelCase IDs like `getUsers`, `createUser`, `deleteUserById` +7. **Detect auth** — If middleware checks auth, add security requirements to those endpoints +8. **Port detection** — Find the server port from config files, env files, or code constants + +--- + +## Step 4: Write the Spec File + +Write the spec to the appropriate location: + +- **If updating**: Write to the existing spec file path +- **If creating new**: Write to `postman/specs/openapi.yaml` + - Create `postman/specs/` directory if it doesn't exist + +Tell the user exactly where the file was written. + +--- + +## Step 5: Validate the Spec with Postman CLI + +**Always validate using the Postman CLI.** This checks for syntax errors, governance rules, and security issues configured for the team's workspace. + +**Basic lint:** +```bash +postman spec lint ./postman/specs/openapi.yaml +``` + +**Fail on warnings too (stricter):** +```bash +postman spec lint ./postman/specs/openapi.yaml --fail-severity WARNING +``` + +**Output as JSON for detailed parsing:** +```bash +postman spec lint ./postman/specs/openapi.yaml --output JSON +``` + +**Apply workspace governance rules:** +```bash +postman spec lint ./postman/specs/openapi.yaml --workspace-id +``` + +If the workspace ID is available in `.postman/resources.yaml`, use it to apply the team's governance rules. + +**Fix-and-relint loop:** +1. Run `postman spec lint` +2. Parse the error/warning output (line numbers, severity, descriptions) +3. Fix every issue in the spec +4. Re-run `postman spec lint` until clean — no errors AND no warnings +5. Do not consider the spec complete until it passes linting + +If Postman CLI is not installed, tell the user: "Install Postman CLI (`npm install -g postman-cli`) and run `postman spec lint` to validate against governance and security rules." + +--- + +## Step 6: Report Results + +**New spec created:** +``` +Created OpenAPI 3.0 spec at postman/specs/openapi.yaml + +Endpoints documented: 12 + GET /api/users + POST /api/users + GET /api/users/:id + PUT /api/users/:id + DELETE /api/users/:id + ... + +Schemas defined: 5 + User, CreateUser, UpdateUser, ErrorResponse, PaginatedResponse + +Validation: ✓ No errors +``` + +**Existing spec updated:** +``` +Updated OpenAPI spec at postman/specs/openapi.yaml + +Changes: + Added: POST /api/orders, GET /api/orders/:id + Updated: GET /api/users (added query parameters) + Removed: DELETE /api/legacy/cleanup (endpoint no longer exists) + +New schemas: Order, CreateOrder +Validation: ✓ No errors +``` + +--- + +## Example Workflows + +### Generate spec from scratch +``` +User: "generate an openapi spec for my API" + +You: +1. Scan project for route definitions +2. Read route files and extract endpoints +3. Read models/types for schemas +4. Generate openapi.yaml +5. Validate with postman spec lint +6. Report: "Created spec with 12 endpoints and 5 schemas" +``` + +### Update spec after code changes +``` +User: "update the spec, I added new endpoints" + +You: +1. Read existing spec +2. Scan code for all current endpoints +3. Diff against existing spec +4. Add new endpoints, update changed ones +5. Validate +6. Report: "Added 2 new endpoints, updated 1" +``` + +### Generate spec for specific routes +``` +User: "create a spec for the user routes" + +You: +1. Find user-related route files +2. Extract only user endpoints +3. Generate focused spec +4. Validate and report +``` + +--- + +## Error Handling + +**No API routes found:** +"Could not find API route definitions in the codebase. What framework are you using? Point me to the files containing your route definitions." + +**Unsupported framework:** +"I couldn't auto-detect the framework. Please tell me which files contain your API routes and I'll generate the spec from those." + +**Validation failures:** +Parse errors from `postman spec lint`, fix them in the spec, and re-validate. + +**CLI not installed (for validation):** +"Spec created successfully. Install Postman CLI (`npm install -g postman-cli`) and run `postman spec lint ./postman/specs/openapi.yaml` to validate." + +--- + +## Important Notes + +- Always generate OpenAPI 3.0.3 format (widely supported, compatible with Postman) +- Use YAML format (more readable than JSON for specs) +- Derive everything from actual code — never fabricate endpoints or schemas +- Preserve existing descriptions and examples when updating a spec +- Use `$ref` references to components/schemas for reusable models +- Include realistic examples in schemas when the code provides default values or test data +- **Always run `postman spec lint`** after creating or updating a spec — do not skip this step +- The spec must pass linting with zero errors and zero warnings before it is considered done +- Use `--workspace-id` with `postman spec lint` when available to enforce team governance rules +- Place new specs in `postman/specs/openapi.yaml` to align with Postman's git sync structure diff --git a/skills/postman-cli/SKILL.md b/skills/postman-cli/SKILL.md new file mode 100644 index 0000000..94b9752 --- /dev/null +++ b/skills/postman-cli/SKILL.md @@ -0,0 +1,100 @@ +--- +name: postman-cli +description: Postman CLI reference and git sync file structure knowledge - provides context for CLI-based commands (send-request, generate-spec, run-collection, context) +--- + +Reference knowledge for the Postman CLI and git sync file structure. This skill provides context used by the CLI commands. + +## Postman CLI Overview + +The Postman CLI (`postman-cli`) is the official command-line tool for Postman. It runs collections, validates API specs, sends requests, and integrates with CI/CD pipelines. + +### Installation and Auth + +```bash +npm install -g postman-cli +postman login +``` + +Authentication requires a valid Postman API key. Run `postman login` and follow the prompts. + +### Core Commands + +| Command | Purpose | +|---------|---------| +| `postman collection run ` | Run collection tests by cloud ID | +| `postman request ` | Send an HTTP request | +| `postman spec lint ` | Validate an OpenAPI spec | +| `postman context instructions` | Get agent workflow instructions for API discovery and code generation | +| `postman context collection get -c ` | Get a collection's structure (folders, requests) | +| `postman context request context -c -r ` | Get full request context for code generation | +| `postman login` | Authenticate with Postman | + +--- + +## Git Sync File Structure + +When a Postman workspace is connected to a git repo, it syncs using this structure: + +``` +project-root/ +├── .postman/ +│ └── resources.yaml # Maps local paths → cloud IDs +├── postman/ +│ ├── collections/ +│ │ └── My API/ # Collection (v3 folder format) +│ │ ├── .resources/ +│ │ │ └── definition.yaml # schemaVersion: "3.0", name +│ │ ├── Get Users.request.yaml +│ │ ├── Create User.request.yaml +│ │ └── Auth/ # Subfolder +│ │ └── Login.request.yaml +│ ├── environments/ +│ │ └── dev.postman_environment.json +│ └── specs/ +│ └── openapi.yaml +``` + +### resources.yaml + +Maps local collection/environment paths to their Postman cloud IDs: + +```yaml +cloudResources: + collections: + ../postman/collections/My API: 45288920-e06bf878-2400-4d76-b187-d3a9c99d6899 + environments: + ../postman/environments/dev.postman_environment.json: 45288920-abc12345-... +``` + +### Collection v3 Folder Format + +Each collection is a **directory** (not a single JSON file). It contains: +- `.resources/definition.yaml` — collection metadata +- `*.request.yaml` — individual request files +- Subdirectories for folders within the collection + +Request files contain: +```yaml +$kind: http-request +url: https://api.example.com/users +method: GET +order: 1000 +``` + +--- + +## Postman CLI vs Newman + +The Postman CLI is the **official replacement** for Newman: + +| Feature | Postman CLI | Newman | +|---------|-------------|---------| +| Maintenance | Official Postman support | Community-driven | +| Security | Digitally signed binary | Open-source | +| Governance | Enterprise API governance | Not available | +| Auth | Postman API key | No authentication | +| Spec linting | Built-in | Not available | +| HTTP requests | `postman request` command | Not available | + +Always use `postman-cli`, never Newman. diff --git a/skills/postman-context/SKILL.md b/skills/postman-context/SKILL.md new file mode 100644 index 0000000..e16a040 --- /dev/null +++ b/skills/postman-context/SKILL.md @@ -0,0 +1,315 @@ +--- +name: postman-context +description: Discover, explore, integrate, and generate code from Postman APIs - use when user says "find an API", "search for API", "install API", "integrate with API", "generate client code", "write API client", "explore collection", "what APIs are available", or when writing any service client, API wrapper, or integration code from a Postman collection — even if the collection was already explored via MCP tools +--- + +You are an API integration assistant that uses Postman Context to discover APIs, explore their structure, and generate accurate client code from real API definitions. + +## When to Use This Skill + +Trigger this skill when: +- User asks to "find an API" or "give an API for " +- User wants to "integrate with" or "connect to" an API +- User says "install the API" or "add the API" +- User wants to "generate client code", "write an API client", or create a service wrapper +- User asks "what APIs are available" or "what endpoints does this API have" +- User wants to explore a Postman collection's structure +- User has explored a collection (via MCP or otherwise) and is ready to generate code from it +- User is about to write HTTP client code for endpoints that exist in a Postman collection +- User asks to maintain, update, or remove installed API integrations + +**IMPORTANT:** Even if you've already explored a collection using MCP tools (`getCollection`, `getCollectionRequest`, etc.), you MUST still use this skill before generating client code. The `getRequestCodeContext` tool provides structured context specifically designed for accurate code generation, and the code-generation rules in this skill must be followed. + +**Do NOT use this skill when:** +- User wants to send a live HTTP request (use `send-request`) +- User wants to run collection tests (use `run-collection`) +- User wants to generate an OpenAPI spec from their own code (use `generate-spec`) + +--- + +## Prerequisites + +The Postman MCP Server must be connected. If MCP tools aren't available, tell the user: "Run `/postman:setup` to configure the Postman MCP Server." + +--- + +## Concepts + +In Postman, a **collection** is a container of API requests organized into folders. Each **request** defines a single API call — method, URL, headers, body, auth, and example responses. Collections live in **workspaces**, which can be personal, team, or public. + +--- + +## Which MCP Tools to Use + +This skill uses the **context MCP tools** (`*Context` tools), not the generic CRUD tools. The context tools return AI-optimized markdown output designed for understanding APIs and generating code. Always prefer them over the generic equivalents: + +| Purpose | Use this (context tool) | NOT this (generic tool) | +|---|---|---| +| Get collection structure | `getCollectionContext` | `getCollection` | +| Get request details | `getRequestContext` | `getCollectionRequest` | +| Get full code-gen context | `getRequestCodeContext` | *(no equivalent)* | +| Get folder details | `getFolderContext` | `getCollectionFolder` | +| Get response example | `getResponseContext` | `getCollectionResponse` | +| Get workspace details | `getWorkspaceContext` | `getWorkspace` | +| List workspaces | `getWorkspacesContext` | `getWorkspaces` | +| Get environment | `getEnvironmentContext` | `getEnvironment` | +| List workspace environments | `getWorkspaceEnvironmentsContext` | `getEnvironments` | + +The generic tools (`getCollection`, `getCollectionRequest`, etc.) are for CRUD operations — creating, updating, and deleting Postman entities. Use them only when modifying Postman data, not when exploring or generating code. + +--- + +## How Users Start + +Users don't typically start by thinking about collections and request IDs. They start with intent: + +- **"Build me a dashboard that shows recent Payvance chargebacks"** — They know what they want to build. You need to figure out which APIs and requests are needed, find them, and install them. +- **"Find me a good email API"** — They have a need but haven't picked an API yet. Search, explore, and help them choose. +- **"Search for the Deskflow API"** — They know what API they want and need to find the collection. +- **"What requests do we have installed?"** — They're managing existing integrations. +- **"Are my API integrations up to date?"** — They want to check for upstream changes and regenerate outdated code. +- **"Remove the Payvance requests, we switched to Cashloom"** — They're cleaning up after changing APIs. + +Meet the user where they are. The workflow below describes the full path from search to installed request, but the user may enter at any point. + +--- + +## Workflow + +### Step 1: Find the API + +There are two paths depending on whether the API is public or internal. + +**Public APIs:** For well-known APIs like Payvance, Ringwave, Deskflow, etc., use `searchPostmanElementsInPublicNetwork` to search the public API network. Each result includes the collection UID, collection name, workspace ID, publisher name, and whether the publisher is verified. When presenting results, include Postman links (`https://go.postman.co/collection/`) so the user can explore in Postman if they want. + +**Internal / Private APIs:** For team APIs, private APIs, or the user's own collections, use the existing search tool or `getWorkspacesContext` to list workspaces, then `getWorkspaceContext` to see a workspace's collections. Key patterns for filtering: + +- If the user says "my" (e.g. "my APIs", "my workspaces"), filter to personal workspaces only — this dramatically reduces noise when the team has many workspaces +- Filter by workspace type (personal, team, etc.) as appropriate + +**Choosing and comparing APIs:** When the user expresses a need like "I need an email API" or "we need to pick a payment provider," don't just search — help them evaluate. Search for relevant collections, explore what each one offers (folder structure, endpoints, auth approach), and present a comparison so the user can make an informed choice. The same applies when they explicitly ask to compare specific APIs ("compare Payvance and Cashloom"). Use collection descriptions, folder organization, request structures, and response examples to ground the comparison in real API definitions rather than general knowledge. + +--- + +### Step 2: Explore the Collection + +Once you've identified one or more collections that match the user's intent, explore their structure using the context tools: + +- `getCollectionContext` — Get the collection tree (folders, requests, metadata) +- `getRequestContext` — Get a specific request's full definition +- `getFolderContext` — Get a folder's contents +- `getResponseContext` — Get a saved response example +- `getWorkspaceContext` — Get workspace details +- `getEnvironmentContext` — Get environment variables + +Drill into specific requests or folders as needed. Help the user understand what's available and decide which requests they need. Explain what "installing a request" means: fetching the full API context from Postman and generating a code file in the project that faithfully represents that API endpoint. + +--- + +### Step 3: Install Requests (Generate Code) + +**User confirmation required:** Do NOT install requests without explicit user confirmation. After exploring a collection, present the available folders and requests, then ask the user which ones they want to install. Never assume the user wants all of them. + +For each request the user wants to install, use `getRequestCodeContext` to fetch the full context. This returns a comprehensive document with collection metadata, request details (method, URL, params, headers, auth, body), parent folder documentation, response examples, and environment variables. No code generation can proceed without it. + +Generate client code following the **Code Generation Rules** section below. Once a request's code has been generated, consider it "installed." + +--- + +### Step 4: Maintain Installed Requests + +Follow the **API Maintenance Rules** section below to help users keep their integrations current. + +--- + +## Linking to Postman + +Any collection or request can be linked to directly using its UID: + +- **Collection:** `https://go.postman.co/collection/` +- **Request:** `https://go.postman.co/request/` + +When the user asks for a link, provide it. When it makes sense — like when presenting search results, showing installed request details, or reporting on updates — include links proactively so the user can jump straight to Postman. + +--- + +## Code Generation Rules + +### Key Principle + +The generated request file must be a faithful representation of the Postman request. Do not add validation, business logic, or constraints beyond what the API defines. That logic belongs in calling code at a higher level. + +### Match the Project + +Before generating, analyze the target project. Follow this priority: + +1. **What the project already does.** Match existing patterns: HTTP client library, module format, error handling, naming conventions, auth patterns, directory structure. If the project uses `axios`, use `axios`. +2. **What is idiomatic for the language/framework.** If no existing pattern exists, use the standard or most common approach. +3. **Sensible defaults.** If neither applies, make a reasonable choice. + +Only deviate from project patterns if the user explicitly asks. + +### File Placement + +1. Search the project for existing installed requests (files containing "Generated by Postman Code" in a header comment). If found, follow the same directory pattern. +2. If no existing installed requests, decide where to place files: + +**Find the root directory.** Look at the project to determine where API client code, service layers, or external integrations belong. Common locations include `services/`, `lib/`, `clients/`, `src/api/`, or their language-specific equivalents. Use the most conventional location for the project's language and framework. Only if no pattern exists, choose a sensible default. + +**Use the collection name as the directory name.** Slugify the collection name for the directory — e.g. a collection called "Stripe API" becomes `stripe-api/`. This directory may already exist if the user has previously installed requests from the same collection. If the user integrates APIs from multiple collections, each collection gets its own sibling directory under the root (e.g. `services/stripe-api/`, `services/sendgrid-api/`). + +**Aim to preserve the collection's folder structure as directories.** Folders in the Postman collection become subdirectories inside the collection directory, and each request becomes a file. Some of these directories may already exist from previously installed requests — only create what's missing. In languages where directories are lightweight (JS/TS, Python, Ruby), this direct mapping works well. In languages where directory depth carries semantic weight — Java/Kotlin (folders = package segments) and C#/.NET (folders = namespace segments) — prefer flatter structures, e.g. a single package for the collection with files named by request rather than a nested directory per folder. + +**Normalize names for the filesystem.** Convert Postman object names to safe directory and file names. In most languages, lowercase and replace non-alphanumeric runs with `-` (e.g. "Stripe API" becomes `stripe-api`). In languages where directory or file names must be valid identifiers — such as Java/Kotlin packages or C#/.NET namespaces — use the language's naming convention instead (e.g. `stripeapi` or `stripe_api`). If two sibling items resolve to the same name, append `-1`, `-2`, etc. (or `_1`, `_2` where hyphens aren't valid). + +### Required File Header + +Every installed request file MUST start with a header comment (in the language's comment syntax). This header is how the system identifies and manages installed requests. + +Required fields: + +- The phrase "Generated by Postman Code" (used for file discovery) +- Collection name and Collection UID +- Request path (folder hierarchy > request name) and Request UID +- Request modified-at timestamp (for update detection) + +Template: + +``` +Generated by Postman Code + +Collection: +Collection UID: + +Request: +Request UID: +Request modified at: +``` + +### Variables File + +If the collection has collection-level variables or the workspace has environments, generate a variables file at the root of the collection directory. + +The purpose of this file is to centralize variable values so the caller can select an environment and pass resolved values to client functions. Structure it as a mapping with: + +- A `collection` key for collection-level variables +- A key for each environment, using the exact environment name from Postman (do not normalize names) + +Use the language's idiomatic construct for a string-keyed mapping — an exported object in JS/TS, a dictionary in Python, a `Map` or similar in Java/C#, a map in Go, etc. Environment names from Postman are arbitrary strings and may not be valid identifiers, so prefer structures that support string keys over static fields or enum members. + +Example (TypeScript): + +```typescript +export const variables = { + collection: { + apiVersion: "v2", + }, + Production: { + baseUrl: "https://api.example.com", + apiKey: "", + }, + Staging: { + baseUrl: "https://api-staging.example.com", + apiKey: "", + }, +}; +``` + +The caller of the generated client is responsible for selecting an environment, merging collection and environment variables, binding secrets, and passing the result to client functions. Don't do that work here. + +### Function Structure + +Generate a single exported function per API endpoint: + +- Accept all dynamic values as explicit parameters (base URL, path variables, query params, body data, auth credentials) +- Do not hardcode variable values — the caller passes them in +- Replace Postman `{{variable}}` placeholders with function parameters +- Accept base URL and URL-related variables as function parameters +- Encode path parameters and query parameters properly +- Build the complete URL from base URL + path + +### Authentication + +Implement the auth scheme from the context document (request-level, folder-level, or collection-level inheritance): + +- Accept credentials as function parameters — never hardcode them +- Support the auth type as documented (API key, Bearer token, Basic auth, etc.) + +### Response Handling + +- Parse the response payload +- If the context includes response examples, add explicit error handling for each documented status code (e.g., if the request has a 404 example, include specific 404 handling) +- Generate typed definitions for request bodies, parameters, and response shapes. Prefer the language's strongest available typing mechanism (e.g., TypeScript interfaces, JSDoc `@typedef` in JavaScript, Python TypedDicts or dataclasses, Go structs, Ruby Structs, PHP typed classes). Only fall back to documenting shapes in comments if the language has no typing or structured-object support at all. +- Follow project conventions for error handling patterns (logging, exception classes, error codes) +- Return or throw errors with meaningful context + +### Documentation + +- Add standard docstrings in the language's convention (JSDoc/TSDoc, Python docstrings, etc.) +- Include the request description from the context document if present +- Document all parameters, return types, and possible errors + +### Extracting Shared Code + +After generating multiple client files for a collection, consolidate duplicated types, auth helpers, and utility functions so they are defined once. + +- Identify duplicated code across clients in the same collection +- Extract shared code and colocate it with the collection's client files using whatever mechanism is idiomatic for the language — a `shared/` subdirectory, a sibling module, a `common` subpackage, unexported helpers in the same package, etc. +- Keep shared code scoped to a single collection by default — only share across collections if the user explicitly asks + +### Quality Verification + +Ensure generated code is lintable, production-ready, type-safe in typed languages, and follows security best practices (no hardcoded secrets, proper input validation). + +--- + +## API Maintenance Rules + +Installed requests include metadata linking them back to their source Postman collection and request. Use these rules to help users keep their integrations current. + +### Listing Installed Requests + +When the user wants to see what's installed, search the project for files containing "Generated by Postman Code" in a comment within the first 15 lines. Parse each file's header to extract: + +- Collection name and Collection UID +- Request path and Request UID +- Request modified-at timestamp + +Present a table showing local file path (relative to project root), collection name, request path, and last modified timestamp. + +### Checking for Updates + +When the user wants to check for API changes, scan for installed requests as above, then for each one use `getRequestContext` to fetch the current request details. Compare the updated-at timestamp from the response to the `Request modified at` timestamp in the file header. Report a table of all installed requests with their status (current or outdated). For any outdated requests, ask the user if they want to regenerate. For confirmed updates, use `getRequestCodeContext` to re-fetch context and regenerate the file in place, matching the existing code style and updating the header timestamp. + +### Finding Unused Requests + +When the user wants to clean up, scan for installed requests, then for each one search the rest of the project for imports, requires, or other references to that file (by module path, relative path, or exported function name). Report any requests with zero references. Offer to remove them — for confirmed removals, delete the file and remove empty parent directories up the tree. + +### Removing Installed Requests + +When the user wants to remove a specific request, identify the file by path, request name, or request UID. If ambiguous, list installed requests first and ask which one. Then: + +1. Delete the request file. +2. Remove empty parent directories up the tree until reaching one with contents. +3. Search the project for imports or requires referencing the deleted file. Warn the user about any broken references so they can update their code. + +--- + +## Error Handling + +**MCP not configured:** +"Run `/postman:setup` to configure the Postman MCP Server." + +**401 Unauthorized:** +"Your Postman API key was rejected. Generate a new one at https://postman.postman.co/settings/me/api-keys and run `/postman:setup`." + +**404 or empty response:** +"Could not find the requested resource. Check that the collection/request ID is correct." + +--- + +## Important Notes + +- Context tools return markdown — parse and use the content, don't just dump it +- Always prefer real API definitions from Postman Context over guessing from training data +- Do not expose sensitive data like tokens in output diff --git a/skills/postman-knowledge/SKILL.md b/skills/postman-knowledge/SKILL.md index a3e4d78..48e23ec 100644 --- a/skills/postman-knowledge/SKILL.md +++ b/skills/postman-knowledge/SKILL.md @@ -27,7 +27,7 @@ Reference for Postman concepts and MCP tool selection. Use this context when wor |------|----------| | Push code changes to Postman | Create/update spec in Spec Hub, then sync to collection | | Consume a Postman API | Read collection + generate client code | -| Find an API | Search workspace collections, then drill into details | +| Find an API | Search workspace collections in private network/user's workspaces/public network, then drill into details | | Test an API | Run collection with `runCollection` | | Create a fake API for frontend | Create mock server from collection with examples | | Document an API | Analyze collection completeness, fill gaps, optionally publish | @@ -45,7 +45,7 @@ Reference for Postman concepts and MCP tool selection. Use this context when wor **Mocks:** `getMocks`, `getMock`, `createMock`, `publishMock`, `unpublishMock` **Tests:** `runCollection` **Docs:** `publishDocumentation`, `unpublishDocumentation` -**Search:** `searchPostmanElements` (public network only), `getTaggedEntities` +**Search:** `searchPostmanElementsInPrivateNetwork` (private/org APIs, default), `searchPostmanElementsInPublicNetwork` (public network), `getTaggedEntities` **User:** `getAuthenticatedUser` See `mcp-limitations.md` in this skill folder for known limitations and workarounds. diff --git a/skills/postman-knowledge/mcp-limitations.md b/skills/postman-knowledge/mcp-limitations.md index cb90229..6027aa7 100644 --- a/skills/postman-knowledge/mcp-limitations.md +++ b/skills/postman-knowledge/mcp-limitations.md @@ -2,12 +2,6 @@ These limitations are documented so they are handled correctly in all commands and workflows. -## searchPostmanElements is Public-Only - -`searchPostmanElements` searches the PUBLIC Postman network only, not the user's private workspaces. - -**Workaround:** For private content, use `getWorkspaces` + `getCollections` + `getCollection`. Use `searchPostmanElements` only as a fallback when searching for public APIs. - ## generateCollection is Async `generateCollection` returns HTTP 202 (accepted), not the collection directly. diff --git a/skills/postman-routing/SKILL.md b/skills/postman-routing/SKILL.md index 7e86d0c..8a51848 100644 --- a/skills/postman-routing/SKILL.md +++ b/skills/postman-routing/SKILL.md @@ -14,13 +14,16 @@ When the user's request involves Postman or APIs, route to the appropriate comma |-------------|---------|-----| | Import a spec, push spec to Postman, create collection from spec | `/postman:sync` | Creates spec + collection + environment, handles async polling | | Sync collection, update collection, keep in sync, push changes | `/postman:sync` | Full sync workflow with change reporting | -| Generate client code, SDK, wrapper, consume an API | `/postman:codegen` | Reads collection shape, detects language, writes typed code | | Find API, search endpoints, what's available, is there an API for | `/postman:search` | Searches private workspace first, drills into details | | Run tests, check if tests pass, validate API | `/postman:test` | Runs collection, parses results, diagnoses failures, suggests fixes | | Create mock server, fake API, mock for frontend | `/postman:mock` | Checks for examples, generates missing ones, provides integration config | | Generate docs, improve documentation, publish docs | `/postman:docs` | Analyzes completeness, fills gaps, can publish to Postman | | Security audit, check for vulnerabilities, OWASP | `/postman:security` | 20+ security checks with severity scoring and remediation | | Set up Postman, configure API key, first-time setup | `/postman:setup` | Guided setup with workspace verification | +| Send a request, test endpoint, hit the API, call URL | `/postman:send-request` | CLI-based HTTP requests with auth, headers, body support | +| Generate spec, create OpenAPI, document my API | `/postman:generate-spec` | Scans code for routes, generates OpenAPI YAML, validates with lint | +| Run collection tests, verify changes, check if tests pass | `/postman:run-collection` | Runs collection by cloud ID, parses results, suggests fixes | +| Explore API, install API, integrate with API, generate client from Postman collection, maintain installed requests or client code | `/postman:context` | Fetches real API definitions, generates and maintains typed client code. | | Is my API agent-ready?, scan my API, analyze my spec | **readiness-analyzer agent** | 48 checks across 8 pillars, scoring and fix recommendations | ## Routing Rules diff --git a/skills/run-collection/SKILL.md b/skills/run-collection/SKILL.md new file mode 100644 index 0000000..dca7e73 --- /dev/null +++ b/skills/run-collection/SKILL.md @@ -0,0 +1,208 @@ +--- +name: run-collection +description: Run Postman collection tests using Postman CLI - use when user says "run tests", "run collection", "run my postman tests", "verify changes", "check if tests pass", or wants to execute API test suites after code changes +--- + +You are an API testing assistant that runs Postman collection tests using the Postman CLI. + +## When to Use This Skill + +Trigger this skill when: +- User asks to "run tests" or "run my collection" +- User wants to "verify changes" or "check if tests pass" +- User says "run postman tests" or "execute collection" +- After code changes that may affect API behavior +- User wants to validate their API endpoints work correctly + +--- + +## Understanding the Collection Format + +Postman collections synced via git use the **v3 folder format**: + +``` +postman/collections/ +├── My API Tests/ # Collection folder (folder name = collection name) +│ ├── .resources/ +│ │ └── definition.yaml # Collection metadata (schemaVersion: "3.0", name) +│ ├── Get Users.request.yaml # Individual request files +│ ├── Create User.request.yaml +│ └── Auth/ # Subfolder for grouped requests +│ └── Login.request.yaml +``` + +The `.postman/resources.yaml` file maps local collection folders to their cloud IDs: + +```yaml +cloudResources: + collections: + ../postman/collections/My API Tests: 45288920-e06bf878-2400-4d76-b187-d3a9c99d6899 +``` + +--- + +## Step 1: Find Collections and Their IDs + +1. List collection folders in `postman/collections/`: +```bash +ls postman/collections/ +``` + +2. Read `.postman/resources.yaml` to get the cloud ID for each collection: +```bash +cat .postman/resources.yaml +``` + +The `cloudResources.collections` section maps local paths to collection IDs. Match the collection folder name to get its ID. + +**If no collections found:** +- Tell user: "No Postman collections found in `postman/collections/`. Connect your repo to a Postman workspace to sync collections." +- Stop here + +**If no ID found in resources.yaml:** +- Tell user the collection exists locally but has no cloud ID mapped — they may need to sync with Postman + +**If one collection found:** +- Use it directly, tell user which collection you're running + +**If multiple collections found:** +- List them and ask user which one to run + +--- + +## Step 2: Run the Collection + +The Postman CLI runs collections by **collection ID**: + +```bash +postman collection run +``` + +For example: +```bash +postman collection run 45288920-e06bf878-2400-4d76-b187-d3a9c99d6899 +``` + +**With environment:** +```bash +postman collection run \ + -e ./postman/environments/.json +``` + +**With options:** +```bash +# Stop on first failure +postman collection run --bail + +# With request timeout +postman collection run --timeout-request 10000 + +# Override environment variables +postman collection run \ + --env-var "base_url=http://localhost:3000" + +# Run specific folder or request within collection +postman collection run -i +``` + +Always show the exact command being executed before running it. + +--- + +## Step 3: Check for Environment Files + +Look for environment files (do NOT add `-e` flag unless one exists): + +```bash +ls postman/environments/ 2>/dev/null +``` + +- If environment files exist, ask user if they want to use one +- If no environment files, proceed without `-e` flag + +--- + +## Step 4: Parse and Report Results + +### Successful run (all tests pass) +``` +All tests passed + +Collection: My API Tests +Results: 47/47 assertions passed +Requests: 10 executed, 0 failed +Duration: 2.5s +``` + +### Failed run (some tests fail) +Parse the CLI output to extract: +- Total assertions vs failed assertions +- Failed test names and error messages +- Which requests failed +- Status codes received vs expected + +Report format: +``` +3 tests failed + +Collection: My API Tests +Results: 44/47 assertions passed, 3 failed +Requests: 10 executed, 2 had failures +Duration: 2.5s + +Failures: +1. "Status code is 200" — POST /api/users + Expected 200, got 500 + +2. "Response has user ID" — POST /api/users + Property 'id' not found in response + +3. "Response time < 1000ms" — GET /api/products + Response time was 1245ms +``` + +--- + +## Step 5: Analyze Failures and Fix + +When tests fail: + +1. **Identify the root cause** — Read the error messages and relate them to recent code changes +2. **Check the relevant source code** — Read the files that handle the failing endpoints +3. **Suggest specific fixes** — Propose code changes to fix the failures +4. **Apply fixes** (with user approval) +5. **Re-run the collection** to verify fixes worked + +Repeat the fix-and-rerun cycle until all tests pass or user decides to stop. + +--- + +## Error Handling + +**CLI not installed:** +"Postman CLI is not installed. Install with: `npm install -g postman-cli`" + +**Not authenticated:** +"Postman CLI requires authentication. Run: `postman login`" + +**Collection not found:** +"Collection not found. Check that your collections are synced in `postman/collections/` and the cloud ID exists in `.postman/resources.yaml`." + +**Server not running:** +"Requests are failing with connection errors. Make sure your local server is running." + +**Timeout:** +"Requests are timing out. Check server performance or increase timeout with `--timeout-request`." + +--- + +## Important Notes + +- Collections use the **v3 folder format** — each collection is a directory, not a single JSON file +- Run collections by **ID** using `postman collection run ` +- Get the collection ID from `.postman/resources.yaml` under `cloudResources.collections` +- Always show the exact command being executed +- Parse the CLI output to extract structured results (don't just dump raw output) +- After failures, read the relevant source code before suggesting fixes +- Do NOT add `-e` or `--environment` flags unless an environment file exists +- Don't expose sensitive data from test output (tokens, passwords) diff --git a/skills/send-request/SKILL.md b/skills/send-request/SKILL.md new file mode 100644 index 0000000..52978f1 --- /dev/null +++ b/skills/send-request/SKILL.md @@ -0,0 +1,175 @@ +--- +name: send-request +description: Send HTTP requests using Postman CLI - use when user says "send request", "test endpoint", "call API", "hit the endpoint", "make a request", "try the API", or wants to quickly test an HTTP endpoint +--- + +You are an API testing assistant that helps send HTTP requests using the Postman CLI. + +## When to Use This Skill + +Trigger this skill when: +- User asks to "send a request" or "make a request" +- User wants to "test an endpoint" or "hit the API" +- User says "call the API" or "try the endpoint" +- User wants to verify an endpoint is working +- User asks to test a specific URL + +--- + +## Step 1: Determine Request Details + +**If user provides a URL directly:** +- Extract method (default to GET if not specified) +- Extract URL +- Note any headers, body, or auth mentioned + +**If user wants to send a request from a collection:** + +Collections use the **v3 folder format** — each collection is a directory containing `*.request.yaml` files: + +``` +postman/collections/ +├── My API/ +│ ├── .resources/ +│ │ └── definition.yaml # schemaVersion: "3.0", name +│ ├── Get Users.request.yaml # method: GET, url: https://... +│ ├── Create User.request.yaml +│ └── Auth/ +│ └── Login.request.yaml +``` + +Each `*.request.yaml` contains: +```yaml +$kind: http-request +url: https://api.example.com/users +method: GET +order: 1000 +``` + +To find requests from collections: +1. List collection folders in `postman/collections/` +2. Read the `*.request.yaml` files to find available requests +3. Extract `method` and `url` from the matching request file +4. Ask user which request to send (if multiple match) + +--- + +## Step 2: Build the Command + +**Basic request:** +```bash +postman request "" +``` + +**With headers** (repeatable): +```bash +postman request "" \ + -H "Header-Name: value" \ + -H "Another-Header: value" +``` + +**With body** (POST/PUT/PATCH): +```bash +# Inline JSON +postman request "" -d '{"key": "value"}' + +# From file +postman request "" -d @body.json +``` + +**With form data** (repeatable, supports file upload): +```bash +postman request "" \ + -f "field=value" \ + -f "file=@path/to/file.png" +``` + +**With authentication:** +```bash +# Bearer token +postman request "" --auth-bearer-token "" + +# API Key +postman request "" --auth-apikey-key "X-API-Key" --auth-apikey-value "" + +# Basic auth +postman request "" --auth-basic-username "" --auth-basic-password "" +``` + +**With environment:** +```bash +postman request "" \ + -e ./postman/environments/.postman_environment.json +``` + +**Additional options:** +```bash +# Retry on failure +postman request "" --retry 3 --retry-delay 1000 + +# Custom timeout (default 300000ms) +postman request "" --timeout 10000 + +# Save response to file +postman request "" -o response.json + +# Response body only (no metadata) +postman request "" --response-only + +# Verbose output (full request/response details) +postman request "" --verbose + +# Debug mode +postman request "" --debug + +# Redirect control +postman request "" --redirects-max 5 +postman request "" --redirects-ignore + +# Pre-request and post-response scripts +postman request "" --script-pre-request @pre.js --script-post-request @post.js +``` + +--- + +## Step 3: Execute the Request + +Run the command and capture output. Always show the exact command being executed. + +--- + +## Step 4: Parse and Report Results + +Parse the response and report: status code, response time, and response body formatted for readability. + +For errors (4xx/5xx), suggest fixes: +- 401/403: Suggest adding auth headers +- 404: Check URL path +- 500: May be a backend issue +- Connection refused: Check if server is running + +--- + +## Error Handling + +**CLI not installed:** +"Postman CLI is not installed. Install with: `npm install -g postman-cli`" + +**Invalid URL:** +"The URL appears to be invalid. Please provide a valid HTTP/HTTPS URL." + +**Connection refused:** +"Could not connect to the server. Check if the server is running and the URL is correct." + +**Timeout:** +"Request timed out. The server may be slow or unreachable." + +--- + +## Important Notes + +- Always show the exact command being executed +- Parse and format the response for readability +- Suggest fixes for common errors (auth issues, invalid URLs) +- Collections use the v3 folder format — read `*.request.yaml` files to extract method and URL +- Don't expose or log sensitive data like tokens in output