@postman/postman-plugin 0.1.1-rc.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -2
- package/dist/cli.js +7 -1
- package/dist/hosts/index.js +2 -1
- package/dist/hosts/kimi.js +5 -2
- package/dist/hosts/pi.js +84 -0
- package/dist/pi-extension.js +27 -0
- package/dist/run.js +16 -10
- package/dist/source.js +3 -1
- package/hooks/session-start-context.md +11 -0
- package/mcp.pi.json +14 -0
- package/package.json +21 -6
- package/skills/ai-readiness/SKILL.md +50 -0
- package/skills/api-discovery/SKILL.md +135 -0
- package/skills/api-discovery/reference/orbit.md +101 -0
- package/skills/api-documentation/SKILL.md +34 -0
- package/skills/api-documentation/reference/rest-api-best-practices.md +47 -0
- package/skills/api-engineer/SKILL.md +29 -0
- package/skills/api-mocking/SKILL.md +141 -0
- package/skills/api-monitoring/SKILL.md +137 -0
- package/skills/api-testing/SKILL.md +103 -0
- package/skills/bootstrap/SKILL.md +216 -0
- package/skills/bootstrap/reference/cli_installation.md +58 -0
- package/skills/ci-integration/SKILL.md +121 -0
- package/skills/collection-schema-v3/SKILL.md +210 -0
- package/skills/collection-schema-v3/reference/environment.md +63 -0
- package/skills/collection-schema-v3/reference/other_protocols.md +86 -0
- package/skills/datasets/SKILL.md +323 -0
- package/skills/flows/SKILL.md +212 -0
- package/skills/flows/reference/flow_cli_flags.md +111 -0
- package/skills/performance-testing/SKILL.md +71 -0
- package/skills/postman-mcp-server/SKILL.md +71 -0
- package/skills/postman-mcp-server/references/docs.md +88 -0
- package/skills/postman-mcp-server/references/learn.md +73 -0
- package/skills/postman-mcp-server/references/mcp-limitations.md +38 -0
- package/skills/postman-mcp-server/references/mock.md +101 -0
- package/skills/postman-mcp-server/references/search.md +83 -0
- package/skills/postman-mcp-server/references/security.md +129 -0
- package/skills/postman-mcp-server/references/setup.md +141 -0
- package/skills/postman-mcp-server/references/sync.md +85 -0
- package/skills/postman-mcp-server/references/test.md +84 -0
package/README.md
CHANGED
|
@@ -21,10 +21,10 @@ dependencies, ownership, runtime behavior, and the likely impact of a change.
|
|
|
21
21
|
Install Postman in every compatible coding agent detected on your machine:
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
|
-
npx @postman/postman-plugin
|
|
24
|
+
npx @postman/postman-plugin
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
One command configures **Claude Code, Codex, Cursor, Kimi Code and
|
|
27
|
+
One command configures **Claude Code, Codex, Cursor, Kimi Code, OpenCode and Pi**.
|
|
28
28
|
Run it again to update, `status` to see what's installed, and `remove` to
|
|
29
29
|
uninstall; `--agent <id>` limits any of them to one agent.
|
|
30
30
|
|
|
@@ -54,6 +54,18 @@ claude plugin install postman@postman
|
|
|
54
54
|
codex plugin add postman@postman
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
+
### Pi
|
|
58
|
+
|
|
59
|
+
[View Postman in Pi's package gallery](https://pi.dev/packages/@postman/postman-plugin)
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pi install npm:@postman/postman-plugin
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`pi update npm:@postman/postman-plugin` updates it. Sign in to Postman's MCP
|
|
66
|
+
server with `/mcp login postman` inside a Pi session. The shell's `pi mcp login`
|
|
67
|
+
doesn't load extensions, so it reports no server named `postman`.
|
|
68
|
+
|
|
57
69
|
## Highlights
|
|
58
70
|
|
|
59
71
|
### Filesystem-first API development
|
package/dist/cli.js
CHANGED
|
@@ -20,7 +20,13 @@ Options:
|
|
|
20
20
|
-y, --yes Don't ask for confirmation (required when not in a terminal)
|
|
21
21
|
--dry-run Print what would change without changing anything
|
|
22
22
|
-h, --help Show this help
|
|
23
|
-
-v, --version Show the version
|
|
23
|
+
-v, --version Show the version
|
|
24
|
+
|
|
25
|
+
Exit codes:
|
|
26
|
+
0 Done, or nothing needed doing
|
|
27
|
+
1 Something failed or was blocked, you cancelled, or install found no agent
|
|
28
|
+
2 Bad usage, or confirmation needed but no terminal (use --yes)
|
|
29
|
+
3 Done, except a step only you can do (printed as "next:")`;
|
|
24
30
|
function version() {
|
|
25
31
|
const manifest = new URL('../package.json', import.meta.url);
|
|
26
32
|
return JSON.parse(fs.readFileSync(manifest, 'utf8')).version;
|
package/dist/hosts/index.js
CHANGED
|
@@ -3,4 +3,5 @@ import { codex } from './codex.js';
|
|
|
3
3
|
import { cursor } from './cursor.js';
|
|
4
4
|
import { kimi } from './kimi.js';
|
|
5
5
|
import { opencode } from './opencode.js';
|
|
6
|
-
|
|
6
|
+
import { pi } from './pi.js';
|
|
7
|
+
export const HOSTS = [claudeCode, codex, cursor, kimi, opencode, pi];
|
package/dist/hosts/kimi.js
CHANGED
|
@@ -46,10 +46,13 @@ export const kimi = {
|
|
|
46
46
|
if (!(await system.which('npx'))) {
|
|
47
47
|
blocked('npx is not on PATH');
|
|
48
48
|
}
|
|
49
|
-
|
|
49
|
+
// Only picks the verb: an unreadable store must not stop the install that could repair it.
|
|
50
|
+
const installed = await kimi.status(system).then((status) => status.installed, () => null);
|
|
51
|
+
// An explicit --package outranks the npm_config_package an outer `npx -p` exports to us.
|
|
52
|
+
await mustRun(system, 'npx', ['-y', `--package=${PLUGINS_CLI}`, 'plugins', 'add', REPO, '--target', 'kimi', '--yes'], {
|
|
50
53
|
env: { DISABLE_TELEMETRY: '1', DO_NOT_TRACK: '1' }
|
|
51
54
|
});
|
|
52
|
-
return result('done',
|
|
55
|
+
return result('done', `${installed ? 'updated' : 'installed'} ${PLUGIN_ID} in ${kimiHome(system)}`, NEXT);
|
|
53
56
|
});
|
|
54
57
|
},
|
|
55
58
|
async remove(system) {
|
package/dist/hosts/pi.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { PI_SOURCE, REPO, isSameRepo, redact } from '../source.js';
|
|
3
|
+
import { failed, guard, mustRun, parseJson } from './shared.js';
|
|
4
|
+
import { result } from './types.js';
|
|
5
|
+
const NEXT = 'Restart Pi, or run `/reload` in an open session, for the change to take effect.', GIT_PREFIX = /^git:/,
|
|
6
|
+
// Pi's ref is everything after the first `@` in the repo path, slashes included. The path starts
|
|
7
|
+
// after the scp-style `host:` or after the host, so an `@` in URL user-info is never a ref.
|
|
8
|
+
SCP_WITH_REF = /^(git@[^:]+:[^@]*)@/, URL_WITH_REF = /^((?:[a-z][a-z0-9+.-]*:\/\/)?[^/]*\/[^@]*)@/i;
|
|
9
|
+
function agentDir(system) {
|
|
10
|
+
const dir = system.env.PI_CODING_AGENT_DIR;
|
|
11
|
+
if (!dir) {
|
|
12
|
+
return path.join(system.home, '.pi', 'agent');
|
|
13
|
+
}
|
|
14
|
+
return dir === '~' || dir.startsWith('~/') ? path.join(system.home, dir.slice(1)) : dir;
|
|
15
|
+
}
|
|
16
|
+
function withoutRef(source) {
|
|
17
|
+
const url = source.replace(GIT_PREFIX, '');
|
|
18
|
+
return (url.match(SCP_WITH_REF) ?? url.match(URL_WITH_REF))?.[1] ?? url;
|
|
19
|
+
}
|
|
20
|
+
// Pi keys an npm package by its name, so a pinned `npm:@postman/postman-plugin@x` is this
|
|
21
|
+
// package too. A git install of this repo is another package loading the same skills.
|
|
22
|
+
const settingsFile = (system) => path.join(agentDir(system), 'settings.json'), isOurPackage = (source) => source === PI_SOURCE || source.startsWith(`${PI_SOURCE}@`), isRepoClone = (source) => !source.startsWith('npm:') && isSameRepo(withoutRef(source), REPO);
|
|
23
|
+
/** Every package source in Pi's user settings, or `null` when the file can't be parsed. */
|
|
24
|
+
async function packageSources(system) {
|
|
25
|
+
const text = await system.readFile(settingsFile(system));
|
|
26
|
+
if (text === null) {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
// Pi parses the file the same way, so a file that fails here fails in `pi` too.
|
|
30
|
+
const settings = parseJson(text.replace(/^\uFEFF/, ''));
|
|
31
|
+
if (!settings || (settings.packages !== undefined && !Array.isArray(settings.packages))) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
return (settings.packages ?? [])
|
|
35
|
+
.map((entry) => (typeof entry === 'string' ? entry : entry?.source))
|
|
36
|
+
.filter((source) => typeof source === 'string');
|
|
37
|
+
}
|
|
38
|
+
async function readableSources(system) {
|
|
39
|
+
return (await packageSources(system)) ?? failed(`${settingsFile(system)} is not valid JSON; fix it and re-run`);
|
|
40
|
+
}
|
|
41
|
+
export const pi = {
|
|
42
|
+
id: 'pi',
|
|
43
|
+
name: 'Pi',
|
|
44
|
+
route: 'installer/package.json',
|
|
45
|
+
async detect(system) {
|
|
46
|
+
return (await system.which('pi')) !== null;
|
|
47
|
+
},
|
|
48
|
+
async status(system) {
|
|
49
|
+
const sources = await packageSources(system);
|
|
50
|
+
if (sources === null) {
|
|
51
|
+
return { installed: null, detail: `could not read ${settingsFile(system)}`, notes: [] };
|
|
52
|
+
}
|
|
53
|
+
const ours = sources.find(isOurPackage), notes = [
|
|
54
|
+
...(ours && ours !== PI_SOURCE ? [`${ours} is pinned, so an update leaves it at that version`] : []),
|
|
55
|
+
...sources.filter(isRepoClone).map((source) => `${redact(source)} loads the same skills and will be removed`)
|
|
56
|
+
];
|
|
57
|
+
return ours ?
|
|
58
|
+
{ installed: true, detail: `${ours} in ${settingsFile(system)}`, notes } :
|
|
59
|
+
{ installed: false, detail: 'not installed', notes };
|
|
60
|
+
},
|
|
61
|
+
install(system) {
|
|
62
|
+
return guard(async () => {
|
|
63
|
+
const sources = await readableSources(system), installed = sources.some(isOurPackage);
|
|
64
|
+
// Replacement first: if it fails, a git copy of this repo is still a working one.
|
|
65
|
+
await mustRun(system, 'pi', [installed ? 'update' : 'install', PI_SOURCE]);
|
|
66
|
+
for (const source of sources.filter(isRepoClone)) {
|
|
67
|
+
await mustRun(system, 'pi', ['remove', source]);
|
|
68
|
+
}
|
|
69
|
+
return result('done', `${installed ? 'updated' : 'installed'} ${PI_SOURCE} in ${settingsFile(system)}`, NEXT);
|
|
70
|
+
});
|
|
71
|
+
},
|
|
72
|
+
remove(system) {
|
|
73
|
+
return guard(async () => {
|
|
74
|
+
const sources = await readableSources(system), targets = [...(sources.some(isOurPackage) ? [PI_SOURCE] : []), ...sources.filter(isRepoClone)];
|
|
75
|
+
if (!targets.length) {
|
|
76
|
+
return result('skipped', 'not installed');
|
|
77
|
+
}
|
|
78
|
+
for (const source of targets) {
|
|
79
|
+
await mustRun(system, 'pi', ['remove', source]);
|
|
80
|
+
}
|
|
81
|
+
return result('done', `removed ${targets.map(redact).join(', ')}`, NEXT);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
/** In the tarball, where `prepack` staged the repo's shared files beside `dist/`. */
|
|
5
|
+
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'), ENTRY_SKILL = 'api-engineer', SECTION = 'postman';
|
|
6
|
+
/** Pi's skill names are un-namespaced; the shared mandate names them `postman:<skill>` for the other routes. */
|
|
7
|
+
export function toPiSessionContext(source) {
|
|
8
|
+
return source.replace(/`postman:([a-z0-9-]+)`/g, '`$1`');
|
|
9
|
+
}
|
|
10
|
+
/** Reads the mandate and the MCP config from `root`, which is laid out like the repository root. */
|
|
11
|
+
export function postmanExtension(root) {
|
|
12
|
+
return (pi) => {
|
|
13
|
+
const mandate = toPiSessionContext(fs.readFileSync(path.join(root, 'hooks', 'session-start-context.md'), 'utf8')), { mcpServers } = JSON.parse(fs.readFileSync(path.join(root, 'mcp.pi.json'), 'utf8'));
|
|
14
|
+
// A `postman` server in the user's own mcp.json takes precedence over this registration.
|
|
15
|
+
for (const [name, server] of Object.entries(mcpServers)) {
|
|
16
|
+
pi.registerMcpServer(name, server);
|
|
17
|
+
}
|
|
18
|
+
// Pi's stand-in for the SessionStart hook. The mandate routes to a skill, so it goes only
|
|
19
|
+
// where that skill loaded; `pi config` can disable it.
|
|
20
|
+
pi.on('before_agent_start', ({ systemPromptOptions }) => {
|
|
21
|
+
if (systemPromptOptions.skills.some((skill) => skill.name === ENTRY_SKILL)) {
|
|
22
|
+
systemPromptOptions.sections[SECTION] = mandate;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export default postmanExtension(packageRoot);
|
package/dist/run.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
export const EXIT = { ok: 0, failed: 1, usage: 2 };
|
|
2
|
-
|
|
3
|
-
const INCOMPLETE = ['failed', 'blocked', 'manual'];
|
|
1
|
+
export const EXIT = { ok: 0, failed: 1, usage: 2, manual: 3 };
|
|
2
|
+
const FAILED = ['failed', 'blocked'];
|
|
4
3
|
function padEnd(text, width) {
|
|
5
4
|
return text + ' '.repeat(Math.max(1, width - text.length));
|
|
6
5
|
}
|
|
@@ -76,11 +75,15 @@ export async function run(system, hosts, options) {
|
|
|
76
75
|
}
|
|
77
76
|
}
|
|
78
77
|
if (!targets.length) {
|
|
79
|
-
system.log(
|
|
78
|
+
system.log(options.agents.length ?
|
|
79
|
+
'None of the requested agents was found.' :
|
|
80
|
+
`No supported coding agent found. Supported: ${joinNames(requested)}.`);
|
|
80
81
|
reports.forEach((report) => system.log(` ${report.result.message}`));
|
|
81
|
-
|
|
82
|
+
// Nothing to remove is a clean remove; nothing to install into is not a successful install.
|
|
83
|
+
return options.command === 'install' || (options.command === 'remove' && reports.length) ? EXIT.failed : EXIT.ok;
|
|
82
84
|
}
|
|
83
|
-
|
|
85
|
+
// "Found:" over "not installed" read to agents as "not found"; the header says both things.
|
|
86
|
+
system.log(`Found ${targets.length} coding agent${targets.length === 1 ? '' : 's'}. Postman in each:`);
|
|
84
87
|
printStatuses(system, targets, width);
|
|
85
88
|
reports.forEach((report) => system.log(` ${padEnd(report.host.name, width)}${report.result.message}`));
|
|
86
89
|
if (options.command === 'status') {
|
|
@@ -90,12 +93,12 @@ export async function run(system, hosts, options) {
|
|
|
90
93
|
// remove also clears duplicates and half-finished installs that status doesn't count.
|
|
91
94
|
const command = options.command;
|
|
92
95
|
if (!options.yes && !system.dryRun) {
|
|
96
|
+
const verb = command === 'install' ? 'install or update Postman in' : 'remove Postman from', names = joinNames(targets.map((target) => target.host));
|
|
93
97
|
if (!options.isTTY) {
|
|
94
|
-
system.log(
|
|
98
|
+
system.log(`\nNothing changed: there is no terminal to confirm in. To ${verb} ${names}, re-run with --yes (add --agent <id> for only some).`);
|
|
95
99
|
return EXIT.usage;
|
|
96
100
|
}
|
|
97
|
-
|
|
98
|
-
if (!(await options.confirm(`\n${verb} ${joinNames(targets.map((target) => target.host))}? [Y/n] `))) {
|
|
101
|
+
if (!(await options.confirm(`\n${verb[0].toUpperCase()}${verb.slice(1)} ${names}? [Y/n] `))) {
|
|
99
102
|
system.log('Cancelled.');
|
|
100
103
|
return EXIT.failed;
|
|
101
104
|
}
|
|
@@ -110,5 +113,8 @@ export async function run(system, hosts, options) {
|
|
|
110
113
|
for (const { host, result } of reports) {
|
|
111
114
|
system.log(` ${padEnd(host.name, width)}${padEnd(result.outcome, 9)}${result.message.split('\n')[0]}`);
|
|
112
115
|
}
|
|
113
|
-
|
|
116
|
+
if (reports.some(({ result }) => FAILED.includes(result.outcome))) {
|
|
117
|
+
return EXIT.failed;
|
|
118
|
+
}
|
|
119
|
+
return reports.some(({ result }) => result.outcome === 'manual') ? EXIT.manual : EXIT.ok;
|
|
114
120
|
}
|
package/dist/source.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
/** Where every host's copy of the plugin comes from.
|
|
1
|
+
/** Where every host's copy of the plugin comes from. Pi alone installs the npm package instead. */
|
|
2
2
|
export const REPO = 'postmanlabs/postman-plugin';
|
|
3
3
|
export const GIT_URL = `https://github.com/${REPO}.git`;
|
|
4
4
|
/** The branch every clone this installer makes tracks. */
|
|
5
5
|
export const BRANCH = 'main';
|
|
6
|
+
/** Unpinned, so `pi update` moves it with each `latest` release; test/pi-package.test.js checks the name. */
|
|
7
|
+
export const PI_SOURCE = 'npm:@postman/postman-plugin';
|
|
6
8
|
/** Must stay byte-identical to the shim in opencode/README.md; test/routes.test.js enforces it. */
|
|
7
9
|
export const OPENCODE_SHIM = "export { default } from '../postman-plugin/opencode/src/index.ts';\n";
|
|
8
10
|
/** Pinned: this third-party CLI writes Kimi's plugin store for us, and an unpinned npx would run whatever is latest. */
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<EXTREMELY_IMPORTANT>
|
|
2
|
+
You have the Postman plugin.
|
|
3
|
+
|
|
4
|
+
Before responding to any non-trivial API engineering task — designing, implementing, mocking, testing, monitoring, documenting, or deploying an API or Postman Flow — load the `postman:api-engineer` skill and follow it. It is the default entry point and routes to the specific postman skills from there. Pure questions and trivial one-line edits don't need it.
|
|
5
|
+
|
|
6
|
+
When the intent is already specific, you may enter directly into the relevant skill instead: `postman:bootstrap` (link this repo to a Postman workspace), `postman:api-mocking`, `postman:api-testing`, `postman:api-monitoring`, `postman:flows`, `postman:ci-integration`, `postman:api-discovery`, `postman:ai-readiness`, `postman:performance-testing`, `postman:api-documentation`.
|
|
7
|
+
|
|
8
|
+
If you were dispatched as a subagent to execute a specific task, ignore this block — `postman:api-engineer` governs the orchestrating session, and it already shaped your dispatch.
|
|
9
|
+
|
|
10
|
+
User instructions (project instruction files such as AGENTS.md or CLAUDE.md, and direct requests) take precedence over this mandate.
|
|
11
|
+
</EXTREMELY_IMPORTANT>
|
package/mcp.pi.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mcpServers": {
|
|
3
|
+
"postman": {
|
|
4
|
+
"type": "http",
|
|
5
|
+
"url": "https://mcp.postman.com/mcp",
|
|
6
|
+
"description": "Postman workspaces, collections, specs, environments, mocks and monitors",
|
|
7
|
+
"headers": {
|
|
8
|
+
"X-Source": "postman-pi-plugin",
|
|
9
|
+
"X-Plugin-Version": "0.1.2",
|
|
10
|
+
"User-Agent": "postman-pi-plugin/0.1.2"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@postman/postman-plugin",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "Postman's API engineering skills for coding agents: a Pi package, and an npx installer that sets up Claude Code, Codex, Cursor, Kimi Code, OpenCode and Pi.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"author": {
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
},
|
|
18
18
|
"bugs": "https://github.com/postmanlabs/postman-plugin/issues",
|
|
19
19
|
"keywords": [
|
|
20
|
+
"pi-package",
|
|
21
|
+
"pi",
|
|
20
22
|
"postman",
|
|
21
23
|
"claude-code",
|
|
22
24
|
"codex",
|
|
@@ -29,13 +31,26 @@
|
|
|
29
31
|
"postman-plugin": "dist/cli.js"
|
|
30
32
|
},
|
|
31
33
|
"files": [
|
|
32
|
-
"dist/"
|
|
34
|
+
"dist/",
|
|
35
|
+
"skills/",
|
|
36
|
+
"hooks/session-start-context.md",
|
|
37
|
+
"mcp.pi.json"
|
|
33
38
|
],
|
|
39
|
+
"pi": {
|
|
40
|
+
"extensions": [
|
|
41
|
+
"./dist/pi-extension.js"
|
|
42
|
+
],
|
|
43
|
+
"skills": [
|
|
44
|
+
"./skills"
|
|
45
|
+
],
|
|
46
|
+
"image": "https://assets.getpostman.com/common-share/postman-logo-horizontal-320x132.png"
|
|
47
|
+
},
|
|
34
48
|
"scripts": {
|
|
35
49
|
"build": "tsc -p tsconfig.json",
|
|
36
|
-
"prepack": "npm run build && node scripts/pack-
|
|
37
|
-
"postpack": "node scripts/pack-
|
|
38
|
-
"test": "npm run build && node --test test/*.test.js"
|
|
50
|
+
"prepack": "npm run build && node scripts/pack-repo-files.js stage",
|
|
51
|
+
"postpack": "node scripts/pack-repo-files.js clean",
|
|
52
|
+
"test": "npm run build && node --test test/*.test.js",
|
|
53
|
+
"test:pi-harness": "node scripts/pi-harness.js"
|
|
39
54
|
},
|
|
40
55
|
"devDependencies": {
|
|
41
56
|
"@types/node": "24.5.2",
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ai-readiness
|
|
3
|
+
description: Scores a Postman collection or an OpenAPI spec for how well an AI agent can discover, understand, call, and recover from errors with it — missing examples, undocumented errors, and ambiguous parameters all cost points. Use when the user asks "is my API agent-ready," "can AI agents use my API," "how agent-friendly is my API," or wants to scan, score, or improve a collection or spec for AI/agent consumption. Covers `postman collection ai-readiness` and `postman spec ai-readiness`.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# AI Readiness
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
An "agent-ready" API is one that an AI agent can discover, understand, call correctly, and recover from errors without human intervention. Most APIs aren't there yet.
|
|
11
|
+
|
|
12
|
+
Two ways to run this check, same rubric family, different target — pick by what exists:
|
|
13
|
+
|
|
14
|
+
- `collection ai-readiness <collectionId/path>` scores a Postman collection
|
|
15
|
+
— by cloud ID, local file path, or a `postman/collections/<name>`
|
|
16
|
+
local-mode directory.
|
|
17
|
+
- `spec ai-readiness <spec>` scores an OpenAPI specification directly — by
|
|
18
|
+
cloud ID or local file path — with no collection involved at all.
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
## Scoring
|
|
23
|
+
|
|
24
|
+
The command computes and prints the score itself — read the fields it
|
|
25
|
+
gives you, don't recompute them:
|
|
26
|
+
|
|
27
|
+
- **`readiness`** (score 0-100 + bucket) is the headline number. Buckets,
|
|
28
|
+
low to high: **Limited → Fair → Good → Excellent**.
|
|
29
|
+
- **`confidence`** (`high`/`medium`/`low`) says how many signals it
|
|
30
|
+
could actually measure vs. had to mark `unknown` — a data-quality
|
|
31
|
+
caveat, not part of the score.
|
|
32
|
+
|
|
33
|
+
## Interpreting Results
|
|
34
|
+
|
|
35
|
+
Report the bucket, score, and confidence the command actually printed
|
|
36
|
+
— don't infer a percentage band. Doc coverage is a modifier via its
|
|
37
|
+
adjustment, not a separate gate; call it out by name when it's `low`,
|
|
38
|
+
since recommendations flag that first. Also state which verb ran
|
|
39
|
+
(`collection` vs. `spec` `ai-readiness`), which target was scored
|
|
40
|
+
(local path vs. cloud ID), the output mode, and — if `--min-score` was
|
|
41
|
+
set — the resulting exit code, not just "it passed."
|
|
42
|
+
|
|
43
|
+
You can ask user if they would like to set this check with a min score guarantee to run on their CI.
|
|
44
|
+
|
|
45
|
+
## Reference
|
|
46
|
+
|
|
47
|
+
- `collection-schema-v3` skill — what saved examples and descriptions look
|
|
48
|
+
like in the git-synced format this command reads.
|
|
49
|
+
- `ci-integration` skill — where `--min-score` fits as a pipeline gate
|
|
50
|
+
alongside `spec lint`/`collection lint`/`workspace lint`.
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: api-discovery
|
|
3
|
+
description: Discover and use APIs from the web or Postman. Find and integrate public third-party APIs with Orbit, locate Postman entities with search, and use the Context Graph to investigate dependencies, ownership, runtime behavior, and change impact across an API ecosystem.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# API Discovery
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Use this guide for any task that involves discovering an API — whether it
|
|
11
|
+
lives on the public web or inside Postman as a workspace, collection, request,
|
|
12
|
+
spec, mock, document, or flow. You can find entities across every surface: your
|
|
13
|
+
own private work, anything your team or organization shares, and resources
|
|
14
|
+
owned by external organizations. Beyond finding entities, this guide also
|
|
15
|
+
covers understanding how they relate to one another — for example, "which
|
|
16
|
+
services consume this API?"
|
|
17
|
+
|
|
18
|
+
Each discovery option serves a distinct purpose:
|
|
19
|
+
|
|
20
|
+
- **Orbit** → discovers and integrates **public third-party APIs**. No signup
|
|
21
|
+
or API key, and it uses ~27× less context than loading a vendor OpenAPI spec.
|
|
22
|
+
Search returns matching endpoints — including what each one
|
|
23
|
+
*cannot* do — and integrate returns a task brief specific enough to write
|
|
24
|
+
code against. It accepts both keyword and natural-language queries. Reach for
|
|
25
|
+
it instead of writing a third-party integration from memory.
|
|
26
|
+
- **`search`** → **finds any Postman entity**, for tasks like "update the tests
|
|
27
|
+
in my collection and run them" or "where is the documentation for our
|
|
28
|
+
access-control API?"
|
|
29
|
+
- **`context-graph ask`** → answers organization-wide relationship and impact
|
|
30
|
+
questions such as "what depends on billing-api?" or "what could this schema
|
|
31
|
+
change break?"
|
|
32
|
+
|
|
33
|
+
These three draw on different data sources, so a miss in one is not proof of a
|
|
34
|
+
miss in the others. `search` locates a known Postman resource; the Context
|
|
35
|
+
Graph discovers relationships around a known starting point. Use both when a
|
|
36
|
+
task needs the resource itself and its wider impact.
|
|
37
|
+
|
|
38
|
+
## Orbit — Public API Discovery
|
|
39
|
+
|
|
40
|
+
Orbit finds public third-party APIs. It's free, needs no signup or API key, and
|
|
41
|
+
works entirely against publicly available APIs. REST base:
|
|
42
|
+
`https://api.buildwithorbit.ai`. Docs: `https://www.buildwithorbit.ai`.
|
|
43
|
+
|
|
44
|
+
Reach for Orbit whenever a task needs an external capability — weather,
|
|
45
|
+
payments, invoicing, messaging, geocoding, calendar, and so on — even when the
|
|
46
|
+
user already named a provider. Rather than writing integration code from
|
|
47
|
+
memory, let Orbit hand you the details that matter: paths, auth header names,
|
|
48
|
+
required fields, and the rest. It works in two steps, search then integrate,
|
|
49
|
+
and a typical round trip runs ~2,500 tokens and ~15–20s end to end — against
|
|
50
|
+
~69,000 tokens for a full vendor OpenAPI spec.
|
|
51
|
+
|
|
52
|
+
Two REST calls, both `POST`:
|
|
53
|
+
|
|
54
|
+
1. **Search** (`POST /v1/search`) — describe the task, e.g.
|
|
55
|
+
`{ "q": "send email via SMTP" }`. Returns candidate endpoints, each with an
|
|
56
|
+
`id` and `resourceType` (pass both back verbatim) and an `evaluateGuide`
|
|
57
|
+
grading its fit.
|
|
58
|
+
2. **Integrate** (`POST /v1/integrate`) — send the task plus the chosen
|
|
59
|
+
resources (up to 10). Returns a `taskBrief` with `FIT`, `AUTH`, `BASE URL`,
|
|
60
|
+
`STEPS`, and `GOTCHAS` — read the GOTCHAS before writing the client.
|
|
61
|
+
|
|
62
|
+
Full endpoint schemas, request/response shapes, `taskBrief` fields, and error
|
|
63
|
+
handling: [reference/orbit.md](reference/orbit.md).
|
|
64
|
+
|
|
65
|
+
## `search`
|
|
66
|
+
|
|
67
|
+
`postman search <type> <query>` finds any Postman entity, searching across
|
|
68
|
+
`requests`, `collections`, `workspaces`, `flows`, `specs`, `mocks`,
|
|
69
|
+
`environments`, or `documents`. The query can be a keyword or natural language,
|
|
70
|
+
and is optional (omit it to list or filter a type outright). Narrow with
|
|
71
|
+
`--ownership` and `--filter`, and add `-o json` for the enriched payload. An
|
|
72
|
+
empty default-scope result is not proof nothing exists — retry with
|
|
73
|
+
`--ownership all` before reporting that.
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
postman search requests "where do we validate a user's email?"
|
|
77
|
+
postman search collections "payments" --ownership external --filter "visibility=public"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Use `postman search <type> -h` for more details — ownership modes, the
|
|
81
|
+
`--filter` / `--filter-json` syntax, filter fields per type, and the exact
|
|
82
|
+
installed-version flags.
|
|
83
|
+
|
|
84
|
+
## `context-graph`
|
|
85
|
+
|
|
86
|
+
The Context Graph is a private, authenticated map of an API ecosystem. It
|
|
87
|
+
reconciles Postman specifications, collections, monitors, and mocks; GitHub
|
|
88
|
+
repositories, definitions, and call sites; and New Relic deployments, traffic,
|
|
89
|
+
and telemetry. These become typed entities joined by sourced relationships such
|
|
90
|
+
as `calls`, `depends_on`, `owned_by`, and `monitored_by`.
|
|
91
|
+
|
|
92
|
+
Use it before a cross-service or potentially breaking change. Name the endpoint,
|
|
93
|
+
schema, service, database, deployment, or shared module being changed; the graph
|
|
94
|
+
discovers the surrounding scope, including runtime callers and repositories not
|
|
95
|
+
checked out locally:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
postman context-graph ask "What depends on billing-api?" --wait
|
|
99
|
+
postman context-graph ask "What is the likely blast radius of changing this schema?" --wait
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Treat the result as a lead, not proof. For consequential work, verify candidates
|
|
103
|
+
against source, API definitions, deployment configuration, or telemetry and
|
|
104
|
+
cite that evidence. Sources are connected through Postman's Agent Context UI
|
|
105
|
+
and refresh nightly; an unconnected or not-yet-ingested source makes absence
|
|
106
|
+
inconclusive.
|
|
107
|
+
|
|
108
|
+
`--wait` polls the asynchronous API and prints the answer. Without it, `ask`
|
|
109
|
+
returns an ID for `postman context-graph status <askId>`. Use `--json` for the
|
|
110
|
+
structured record; `--timeout`, `--interval`, and `--max-steps` control waiting
|
|
111
|
+
and reasoning.
|
|
112
|
+
|
|
113
|
+
The query runs against the team derived from the API key; there is no workspace
|
|
114
|
+
or team selector. Authentication uses `--api-key`, `POSTMAN_API_KEY`, or the
|
|
115
|
+
current `postman login` session, in that order.
|
|
116
|
+
|
|
117
|
+
## After discovery: reusing what was found
|
|
118
|
+
|
|
119
|
+
`dependency add <type> <nameOrId>` formally adds a collection, environment,
|
|
120
|
+
or mock found in another workspace as a dependency of the current one —
|
|
121
|
+
the step after `search` finds something worth reusing (e.g., feeding
|
|
122
|
+
`application test`'s contract matching), rather than copying it in by hand. It
|
|
123
|
+
takes a Postman entity ID. If the Context Graph identifies a service or API to
|
|
124
|
+
reuse, locate its collection with `search` first, then pass that entity ID to
|
|
125
|
+
`dependency add`.
|
|
126
|
+
|
|
127
|
+
## Reference
|
|
128
|
+
|
|
129
|
+
- [Orbit](reference/orbit.md) — public API discovery: the search/integrate
|
|
130
|
+
REST endpoints, request/response shape, `taskBrief` fields, and error
|
|
131
|
+
handling. (Docs at `https://www.buildwithorbit.ai`, REST at
|
|
132
|
+
`https://api.buildwithorbit.ai`.)
|
|
133
|
+
|
|
134
|
+
For `postman search`, run `postman search <type> -h` — the CLI's own help is
|
|
135
|
+
per-type, complete, and always matches your installed version.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Orbit — public API discovery reference
|
|
2
|
+
|
|
3
|
+
Orbit finds and integrates **public third-party APIs** — weather, payments,
|
|
4
|
+
invoicing, messaging, geocoding, calendar, and the like. It is free, needs no
|
|
5
|
+
signup and no API key, and works entirely against publicly available APIs.
|
|
6
|
+
|
|
7
|
+
- REST base: `https://api.buildwithorbit.ai`
|
|
8
|
+
- Docs: `https://www.buildwithorbit.ai`
|
|
9
|
+
|
|
10
|
+
## Implementation
|
|
11
|
+
|
|
12
|
+
Two REST calls, both `POST`, both read-only (safe to retry). No auth header —
|
|
13
|
+
send `Content-Type: application/json` and a JSON body. Run search first,
|
|
14
|
+
surface candidates, then integrate the chosen ids.
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Step 1 — search
|
|
18
|
+
curl -sS https://api.buildwithorbit.ai/v1/search \
|
|
19
|
+
-H 'Content-Type: application/json' \
|
|
20
|
+
-d '{ "q": "send email via SMTP", "limit": 10 }'
|
|
21
|
+
|
|
22
|
+
# Step 2 — integrate (ids come from the search response, verbatim)
|
|
23
|
+
curl -sS https://api.buildwithorbit.ai/v1/integrate \
|
|
24
|
+
-H 'Content-Type: application/json' \
|
|
25
|
+
-d '{
|
|
26
|
+
"task": "Send a welcome email when a user signs up",
|
|
27
|
+
"resources": [{ "id": "urn:orbit:endpoint:v1:...", "type": "endpoint" }]
|
|
28
|
+
}'
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Implementation notes:
|
|
32
|
+
|
|
33
|
+
- A typical search+integrate is ~2,500 tokens and ~15–20s end to end, vs
|
|
34
|
+
~69,000 tokens for loading a vendor OpenAPI spec.
|
|
35
|
+
- One integrate call can span up to 10 resources across different providers;
|
|
36
|
+
batch every endpoint the task needs into a single `resources` array rather
|
|
37
|
+
than making one call per endpoint.
|
|
38
|
+
- Auth, request bodies, `Threading`, and `GOTCHAS` come from live public API
|
|
39
|
+
schemas, so take them from the brief rather than from memory.
|
|
40
|
+
|
|
41
|
+
## Step 1 — Search (`POST /v1/search`)
|
|
42
|
+
|
|
43
|
+
Describe the task, not a provider name. Good: `"send an invoice to a customer"`.
|
|
44
|
+
Worse: `"PayPal"`. Provider name is fine to include when it is fixed.
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{ "q": "send email via SMTP" }
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Query params:
|
|
51
|
+
|
|
52
|
+
- `limit` — default 10, max 25.
|
|
53
|
+
- `cursor` — from `meta.nextCursor`; pagination stops at 40 results.
|
|
54
|
+
- `q` — max 512 characters.
|
|
55
|
+
|
|
56
|
+
Returns `data[]`. Each item has:
|
|
57
|
+
|
|
58
|
+
- `id` — opaque URN. Pass it back verbatim; never construct, shorten, or edit
|
|
59
|
+
it.
|
|
60
|
+
- `resourceType` — `endpoint` or `mcp`.
|
|
61
|
+
- `name`, `method`, `url`, `description`.
|
|
62
|
+
- `evaluateGuide` — how well the endpoint fits the task, including what it
|
|
63
|
+
cannot do.
|
|
64
|
+
|
|
65
|
+
Hold onto both `id` and `resourceType` — both are required for integrate. Do not
|
|
66
|
+
read `meta.total` as a match count; it reports the page size.
|
|
67
|
+
|
|
68
|
+
## Step 2 — Integrate (`POST /v1/integrate`)
|
|
69
|
+
|
|
70
|
+
Pass the same task plus every endpoint the job needs (up to 10). Use
|
|
71
|
+
`resourceType` from the search result as the `type` field.
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"task": "Send a welcome email when a user signs up",
|
|
76
|
+
"resources": [{ "id": "urn:orbit:endpoint:v1:...", "type": "endpoint" }]
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Returns a `taskBrief` covering:
|
|
81
|
+
|
|
82
|
+
- `FIT` — Fully or Partially (and names the gap if Partial).
|
|
83
|
+
- `AUTH` — use the exact header name given; it is frequently not
|
|
84
|
+
`Authorization`.
|
|
85
|
+
- `BASE URL`.
|
|
86
|
+
- numbered `STEPS` — method, path, every parameter with an example, expected
|
|
87
|
+
responses, `Threading`.
|
|
88
|
+
- `GOTCHAS` — read these before writing the client.
|
|
89
|
+
|
|
90
|
+
## Error handling
|
|
91
|
+
|
|
92
|
+
Both endpoints are read-only, so retries are safe. Free and unauthenticated is
|
|
93
|
+
not unlimited — back off on `429`.
|
|
94
|
+
|
|
95
|
+
- `400` — invalid input.
|
|
96
|
+
- `404` on integrate — no IDs resolved.
|
|
97
|
+
- `500` — server error.
|
|
98
|
+
|
|
99
|
+
If `FIT` is not Fully, say what's missing before writing code. If the brief
|
|
100
|
+
names a credential the user doesn't have yet, stop and tell them which one to
|
|
101
|
+
get.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: api-documentation
|
|
3
|
+
description: Generate filesystem-first agent friendly api documentation that you can share with your teammates without hassle. Use when the user asks to "publish API docs," "generate documentation for this API," "put this on the API Network," "share a docs link for this collection or spec," or "why do my docs look empty."
|
|
4
|
+
---
|
|
5
|
+
The bootstrap skill is a precursor to this one — it scaffolds the project with the directories documentation is stored in.
|
|
6
|
+
|
|
7
|
+
# API Documentation
|
|
8
|
+
When working on any API task, the first step is to establish and capture the contract.
|
|
9
|
+
API documentation can be done in two predominant ways:
|
|
10
|
+
1. through a Postman collection,
|
|
11
|
+
2. with an OpenAPI spec
|
|
12
|
+
|
|
13
|
+
It is recommended to create both. They serve different, complementary use cases, and it takes only one command to convert from one to another. Start with creating a Postman collection in v3 format, **collection-schema-v3**.
|
|
14
|
+
Postman collections are very human-friendly and offer other capabilities like creating an API mock, monitor, SDK, or spec.
|
|
15
|
+
|
|
16
|
+
Specs are vendor-neutral, stay in your repo, and can be linted against governance rules (if any) set by your organization.
|
|
17
|
+
|
|
18
|
+
## Good practices for API design
|
|
19
|
+
|
|
20
|
+
See [reference/rest-api-best-practices.md](reference/rest-api-best-practices.md)
|
|
21
|
+
for the practices well-documented APIs tend to already follow: resource
|
|
22
|
+
naming, HTTP method/status-code usage, error response shape, versioning,
|
|
23
|
+
pagination, filtering, auth, idempotency, and backward compatibility. A
|
|
24
|
+
spec or collection that already follows these renders documentation with
|
|
25
|
+
nothing left to fix.
|
|
26
|
+
|
|
27
|
+
### Examples
|
|
28
|
+
Examples (in a Postman collection) are an excellent way to capture sample API responses. They are helpful because:
|
|
29
|
+
1. anyone can look at them to see how your API behaves,
|
|
30
|
+
2. they can be used to generate a mock from your collection in a single command.
|
|
31
|
+
|
|
32
|
+
## Workflow
|
|
33
|
+
1. Establish the contract - refer to best practices. Don't just accept the user's ask - fight for the right API design.
|
|
34
|
+
2. Choose the instrument - Postman collection / OpenAPI spec - or both. Recommend using both to the user. Start with the Postman collection.
|