openapi-explorer-mcp 0.0.1

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/dist/http.js ADDED
@@ -0,0 +1,52 @@
1
+ const RATE_LIMIT_HEADERS = ['retry-after', 'x-ratelimit-limit', 'x-ratelimit-remaining', 'x-ratelimit-reset'];
2
+ /**
3
+ * Fills path parameters into a path template and appends the query string.
4
+ */
5
+ export function buildUrl(baseUrl, pathTemplate, pathParams, query) {
6
+ let filled = pathTemplate;
7
+ for (const [key, value] of Object.entries(pathParams)) {
8
+ filled = filled.split(`{${key}}`).join(encodeURIComponent(String(value)));
9
+ }
10
+ const missing = filled.match(/\{[^}]+\}/g);
11
+ if (missing)
12
+ throw new Error(`missing path parameters: ${missing.join(', ')}`);
13
+ const url = new URL(`${baseUrl}${filled}`);
14
+ for (const [key, value] of Object.entries(query))
15
+ url.searchParams.set(key, String(value));
16
+ return url;
17
+ }
18
+ /**
19
+ * Sends a request and returns a structured result; authorization and retries are the caller's job.
20
+ */
21
+ export async function send(method, url, headers, body, timeoutMs) {
22
+ const startedAt = Date.now();
23
+ const response = await fetch(url, {
24
+ method,
25
+ headers: { Accept: 'application/json', ...(body !== undefined ? { 'Content-Type': 'application/json' } : {}), ...headers },
26
+ body: body !== undefined ? JSON.stringify(body) : undefined,
27
+ signal: AbortSignal.timeout(timeoutMs),
28
+ });
29
+ const text = await response.text();
30
+ let parsed = null;
31
+ if (text) {
32
+ try {
33
+ parsed = JSON.parse(text);
34
+ }
35
+ catch {
36
+ parsed = text;
37
+ }
38
+ }
39
+ const rateLimit = {};
40
+ for (const header of RATE_LIMIT_HEADERS) {
41
+ const value = response.headers.get(header);
42
+ if (value !== null)
43
+ rateLimit[header] = value;
44
+ }
45
+ return {
46
+ status: response.status,
47
+ ok: response.ok,
48
+ durationMs: Date.now() - startedAt,
49
+ ...(Object.keys(rateLimit).length ? { rateLimit } : {}),
50
+ body: parsed,
51
+ };
52
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ import { OpenApiExplorerServer } from './server.js';
3
+ // Tool handlers catch their own errors; anything reaching here is a bug worth a visible exit.
4
+ process.on('unhandledRejection', (reason) => {
5
+ process.stderr.write(`openapi-explorer-mcp: unhandled rejection: ${reason instanceof Error ? (reason.stack ?? reason.message) : String(reason)}\n`);
6
+ process.exit(1);
7
+ });
8
+ const server = await OpenApiExplorerServer.fromEnvironment();
9
+ await server.start();
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Appends one JSON line, creating the directory when needed; a failed write never fails the call.
3
+ */
4
+ export declare function appendJsonl(file: string, entry: unknown): void;
5
+ /**
6
+ * Returns the last entries of a JSONL journal.
7
+ */
8
+ export declare function tailJsonl(file: string, limit: number): unknown[];
@@ -0,0 +1,26 @@
1
+ import { appendFileSync, existsSync, mkdirSync, readFileSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ /**
4
+ * Appends one JSON line, creating the directory when needed; a failed write never fails the call.
5
+ */
6
+ export function appendJsonl(file, entry) {
7
+ try {
8
+ mkdirSync(path.dirname(file), { recursive: true });
9
+ appendFileSync(file, `${JSON.stringify(entry)}\n`);
10
+ }
11
+ catch (error) {
12
+ process.stderr.write(`openapi-explorer-mcp: could not write the journal ${file} — ${error.message}\n`);
13
+ }
14
+ }
15
+ /**
16
+ * Returns the last entries of a JSONL journal.
17
+ */
18
+ export function tailJsonl(file, limit) {
19
+ if (!existsSync(file))
20
+ return [];
21
+ return readFileSync(file, 'utf8')
22
+ .split('\n')
23
+ .filter(Boolean)
24
+ .slice(-limit)
25
+ .map((line) => JSON.parse(line));
26
+ }
@@ -0,0 +1,24 @@
1
+ import type { Operation, SchemaNode, SpecIndex } from './spec-index.js';
2
+ /**
3
+ * Finds an operation by "METHOD /path", a path with a single operation, or a unique operationId.
4
+ */
5
+ export declare function resolveEndpoint(index: SpecIndex, endpoint: string): Operation;
6
+ /**
7
+ * Parameter names by location, with ? marking optional ones.
8
+ */
9
+ export declare function paramSummary(params: Operation['params']): Record<string, string[]>;
10
+ /**
11
+ * Component schema name referenced by a node, directly or as array items.
12
+ */
13
+ export declare function componentRef(node: SchemaNode | null | undefined): {
14
+ name: string;
15
+ array: boolean;
16
+ } | null;
17
+ /**
18
+ * Type name base for an operation: its operationId, or the method and path when there is none.
19
+ */
20
+ export declare function operationTypeName(prefix: string, op: Operation): string;
21
+ /**
22
+ * Renders `export type <Name>` from the path and query parameters of an operation.
23
+ */
24
+ export declare function renderParams(op: Operation, name: string): string | null;
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Finds an operation by "METHOD /path", a path with a single operation, or a unique operationId.
3
+ */
4
+ export function resolveEndpoint(index, endpoint) {
5
+ const raw = endpoint.trim();
6
+ const exact = index.byKey.get(raw) ?? index.byKey.get(raw.replace(/^(\w+)/, (m) => m.toUpperCase()));
7
+ if (exact)
8
+ return exact;
9
+ const asPath = raw.startsWith('/') ? raw : `/${raw}`;
10
+ const byPath = index.operations.filter((o) => o.path === asPath);
11
+ if (byPath.length === 1)
12
+ return byPath[0];
13
+ if (byPath.length > 1)
14
+ throw new Error(`the path ${asPath} has several methods: ${byPath.map((o) => o.key).join(', ')}. Pass METHOD /path.`);
15
+ const byOperationId = index.byOperationId.get(raw) ?? [];
16
+ if (byOperationId.length === 1) {
17
+ const op = index.byKey.get(byOperationId[0]);
18
+ if (op)
19
+ return op;
20
+ }
21
+ if (byOperationId.length > 1)
22
+ throw new Error(`operationId "${raw}" is ambiguous: ${byOperationId.join(', ')}. Pass METHOD /path.`);
23
+ throw new Error(`endpoint not found: "${endpoint}". Search with api_search.`);
24
+ }
25
+ /**
26
+ * Parameter names by location, with ? marking optional ones.
27
+ */
28
+ export function paramSummary(params) {
29
+ return Object.fromEntries(Object.entries(params)
30
+ .filter(([, list]) => list.length > 0)
31
+ .map(([where, list]) => [where, list.map((p) => `${p.name}${p.required ? '' : '?'}`)]));
32
+ }
33
+ /**
34
+ * Component schema name referenced by a node, directly or as array items.
35
+ */
36
+ export function componentRef(node) {
37
+ if (typeof node?.$ref === 'string')
38
+ return { name: node.$ref.split('/').pop() ?? '', array: false };
39
+ if (node?.type === 'array' && typeof node.items?.$ref === 'string')
40
+ return { name: node.items.$ref.split('/').pop() ?? '', array: true };
41
+ return null;
42
+ }
43
+ /**
44
+ * PascalCase from snake_case, kebab-case or camelCase.
45
+ */
46
+ function pascal(value) {
47
+ return value.replace(/(^\w|[_\-\s]+\w)/g, (m) => m.replace(/[_\-\s]+/, '').toUpperCase());
48
+ }
49
+ /**
50
+ * Type name base for an operation: its operationId, or the method and path when there is none.
51
+ */
52
+ export function operationTypeName(prefix, op) {
53
+ const base = op.operationId ?? `${op.method.toLowerCase()}_${op.path.replace(/[{}]/g, '').split('/').filter(Boolean).join('_')}`;
54
+ return `${prefix}${pascal(base.replace(/[^A-Za-z0-9_\-\s]/g, '_'))}`;
55
+ }
56
+ /**
57
+ * TypeScript type of a parameter schema: primitives, enums and arrays.
58
+ */
59
+ function paramType(schema = {}) {
60
+ if (Array.isArray(schema.enum))
61
+ return schema.enum.map((v) => (typeof v === 'string' ? `'${v}'` : String(v))).join(' | ');
62
+ if (schema.type === 'integer' || schema.type === 'number')
63
+ return 'number';
64
+ if (schema.type === 'boolean')
65
+ return 'boolean';
66
+ if (schema.type === 'array')
67
+ return `${paramType(schema.items)}[]`;
68
+ return 'string';
69
+ }
70
+ /**
71
+ * Renders `export type <Name>` from the path and query parameters of an operation.
72
+ */
73
+ export function renderParams(op, name) {
74
+ const fields = [...op.params.path, ...op.params.query];
75
+ if (fields.length === 0)
76
+ return null;
77
+ const lines = fields.map((p) => {
78
+ const doc = p.description ? ` /** ${String(p.description).slice(0, 80)} */\n` : '';
79
+ return `${doc} ${p.name}${p.required ? '' : '?'}: ${paramType(p.schema)};`;
80
+ });
81
+ return `export type ${name} = {\n${lines.join('\n')}\n};`;
82
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * A markdown recipe.
3
+ */
4
+ export interface Recipe {
5
+ /** File name without .md. */
6
+ name: string;
7
+ /** `description:` from the front matter. */
8
+ description: string;
9
+ /** Full text. */
10
+ text: string;
11
+ }
12
+ /**
13
+ * Lists the markdown recipes of a directory.
14
+ */
15
+ export declare function listRecipes(dir: string): Recipe[];
@@ -0,0 +1,16 @@
1
+ import { existsSync, readFileSync, readdirSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ /**
4
+ * Lists the markdown recipes of a directory.
5
+ */
6
+ export function listRecipes(dir) {
7
+ if (!existsSync(dir))
8
+ return [];
9
+ return readdirSync(dir)
10
+ .filter((file) => file.endsWith('.md'))
11
+ .sort()
12
+ .map((file) => {
13
+ const text = readFileSync(path.join(dir, file), 'utf8');
14
+ return { name: path.basename(file, '.md'), description: /^description:\s*(.+)$/m.exec(text)?.[1] ?? '', text };
15
+ });
16
+ }
package/dist/risk.d.ts ADDED
@@ -0,0 +1,28 @@
1
+ /** How risky calling an operation is. */
2
+ export type Danger = 'safe' | 'write' | 'destructive';
3
+ /**
4
+ * Danger level of an operation with the reason for destructive ones.
5
+ */
6
+ export interface DangerVerdict {
7
+ /** Danger level. */
8
+ danger: Danger;
9
+ /** Why the operation is destructive. */
10
+ reason?: string;
11
+ }
12
+ /**
13
+ * Rules that classify operations.
14
+ */
15
+ export interface DangerRules {
16
+ /** Exact "METHOD /path" keys marked destructive, with the reason. */
17
+ operations: Record<string, string>;
18
+ /** Path words that mark a non-GET operation destructive. */
19
+ pathPattern: RegExp;
20
+ }
21
+ /**
22
+ * Loads danger rules: built-in path words plus an optional JSON file with exact operations and extra words.
23
+ */
24
+ export declare function loadDangerRules(file?: string): DangerRules;
25
+ /**
26
+ * Classifies an operation: exact rules first, then GET is safe, DELETE and path words are destructive.
27
+ */
28
+ export declare function classifyDanger(rules: DangerRules, method: string, path: string): DangerVerdict;
package/dist/risk.js ADDED
@@ -0,0 +1,57 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { ConfigError } from './config.js';
3
+ const DEFAULT_PATH_WORDS = ['drop', 'purge', 'reset', 'destroy', 'bulk', 'broadcast'];
4
+ /**
5
+ * Escapes a word for use inside a regular expression.
6
+ */
7
+ function escapeRegExp(word) {
8
+ return word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
9
+ }
10
+ /**
11
+ * Loads danger rules: built-in path words plus an optional JSON file with exact operations and extra words.
12
+ */
13
+ export function loadDangerRules(file) {
14
+ let operations = {};
15
+ const words = [...DEFAULT_PATH_WORDS];
16
+ if (file) {
17
+ let parsed;
18
+ try {
19
+ parsed = JSON.parse(readFileSync(file, 'utf8'));
20
+ }
21
+ catch (error) {
22
+ throw new ConfigError(`OPENAPI_DANGER_FILE is not valid JSON: ${error.message}`);
23
+ }
24
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
25
+ throw new ConfigError('OPENAPI_DANGER_FILE must hold an object with "operations" and/or "pathPatterns"');
26
+ }
27
+ const { operations: ops, pathPatterns } = parsed;
28
+ if (ops !== undefined) {
29
+ if (!ops || typeof ops !== 'object' || Array.isArray(ops) || !Object.values(ops).every((v) => typeof v === 'string')) {
30
+ throw new ConfigError('OPENAPI_DANGER_FILE: "operations" must map "METHOD /path" to a reason string');
31
+ }
32
+ operations = ops;
33
+ }
34
+ if (pathPatterns !== undefined) {
35
+ if (!Array.isArray(pathPatterns) || !pathPatterns.every((w) => typeof w === 'string' && w.length > 0)) {
36
+ throw new ConfigError('OPENAPI_DANGER_FILE: "pathPatterns" must be a list of non-empty strings');
37
+ }
38
+ words.push(...pathPatterns);
39
+ }
40
+ }
41
+ return { operations, pathPattern: new RegExp(`(${words.map(escapeRegExp).join('|')})`, 'i') };
42
+ }
43
+ /**
44
+ * Classifies an operation: exact rules first, then GET is safe, DELETE and path words are destructive.
45
+ */
46
+ export function classifyDanger(rules, method, path) {
47
+ const exact = rules.operations[`${method} ${path}`];
48
+ if (exact)
49
+ return { danger: 'destructive', reason: exact };
50
+ if (method === 'GET')
51
+ return { danger: 'safe' };
52
+ if (method === 'DELETE')
53
+ return { danger: 'destructive', reason: 'DELETE removes data' };
54
+ if (rules.pathPattern.test(path))
55
+ return { danger: 'destructive', reason: 'the path contains a sign of an irreversible operation' };
56
+ return { danger: 'write' };
57
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Compact rendering of OpenAPI schemas. A single schema can be hundreds of kilobytes, so the outline folds it into
3
+ * a short pseudo-type with a depth limit and a field budget, and says how to dig deeper where it cuts.
4
+ */
5
+ import type { SchemaNode } from './spec-index.js';
6
+ /** The part of a spec schema rendering needs. */
7
+ export type SchemaSpec = {
8
+ components: {
9
+ schemas: Record<string, SchemaNode>;
10
+ };
11
+ };
12
+ /**
13
+ * Result of rendering an outline.
14
+ */
15
+ export interface Outline {
16
+ /** Rendered pseudo-type. */
17
+ text: string;
18
+ /** Whether depth or budget cut something. */
19
+ truncated: boolean;
20
+ /** Number of rendered fields. */
21
+ fields: number;
22
+ }
23
+ /**
24
+ * Renders a compact pseudo-type of a schema with a depth limit and a field budget.
25
+ */
26
+ export declare function renderOutline(spec: SchemaSpec, schema: SchemaNode, { depth, budget }?: {
27
+ depth?: number;
28
+ budget?: number;
29
+ }): Outline;
30
+ /**
31
+ * Returns the schema as JSON with $refs substituted, cut at a depth.
32
+ */
33
+ export declare function resolveJson(spec: SchemaSpec, node: SchemaNode | undefined, depth?: number, seen?: Set<string>): unknown;
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Follows a JSON pointer like #/components/schemas/X.
3
+ */
4
+ function pointerWalk(spec, ref) {
5
+ const parts = ref
6
+ .replace(/^#\//, '')
7
+ .split('/')
8
+ .map((p) => p.replace(/~1/g, '/').replace(/~0/g, '~'));
9
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
+ let node = spec;
11
+ for (const part of parts) {
12
+ node = node?.[part];
13
+ if (node === undefined)
14
+ return undefined;
15
+ }
16
+ return node;
17
+ }
18
+ /**
19
+ * Resolves a chain of $refs, returning the schema and the name of the last reference.
20
+ */
21
+ function resolveRef(spec, node, seen) {
22
+ let name = null;
23
+ let current = node;
24
+ let guard = 0;
25
+ while (current && typeof current.$ref === 'string' && guard++ < 20) {
26
+ const ref = current.$ref;
27
+ name = ref.split('/').pop() ?? null;
28
+ if (seen.has(ref))
29
+ return { schema: { __cycle: name }, name };
30
+ current = pointerWalk(spec, ref) ?? { __unresolved: ref };
31
+ }
32
+ return { schema: current, name };
33
+ }
34
+ /**
35
+ * Whether a schema allows null.
36
+ */
37
+ function isNullable(schema) {
38
+ return schema.nullable === true || (Array.isArray(schema.type) && schema.type.includes('null'));
39
+ }
40
+ /**
41
+ * The primary type of a schema, ignoring null in type arrays.
42
+ */
43
+ function primaryType(schema) {
44
+ return Array.isArray(schema.type) ? schema.type.find((t) => t !== 'null') : schema.type;
45
+ }
46
+ /**
47
+ * A one-line label for scalar, enum and plain types; null when the type needs a block.
48
+ */
49
+ function scalarLabel(schema) {
50
+ const type = primaryType(schema);
51
+ if (schema.enum) {
52
+ const values = schema.enum.filter((v) => v !== null).map((v) => (typeof v === 'string' ? `'${v}'` : String(v)));
53
+ return values.length <= 8 ? values.join(' | ') : `${values.slice(0, 5).join(' | ')} | … (${values.length})`;
54
+ }
55
+ // A record comes as an object without properties; its value type is expanded as a block rather than lost.
56
+ const record = schema.additionalProperties && typeof schema.additionalProperties === 'object';
57
+ if (type === 'object' && (schema.properties || record))
58
+ return null;
59
+ const extra = [schema.format, schema.pattern && `pattern ${schema.pattern}`].filter(Boolean).join(', ');
60
+ return `${type ?? 'unknown'}${extra ? ` (${extra})` : ''}`;
61
+ }
62
+ /**
63
+ * Renders a schema node recursively, counting fields and marking cuts.
64
+ */
65
+ function walk(spec, rawNode, ctx) {
66
+ const { schema, name } = resolveRef(spec, rawNode, ctx.seen);
67
+ if (!schema || typeof schema !== 'object')
68
+ return String(schema);
69
+ if (schema.__cycle)
70
+ return `${schema.__cycle} (cycle)`;
71
+ if (schema.__unresolved)
72
+ return `${schema.__unresolved} (not found)`;
73
+ const suffix = isNullable(schema) ? ' | null' : '';
74
+ const branches = schema.oneOf ?? schema.anyOf;
75
+ if (branches) {
76
+ const rendered = branches.map((b) => walk(spec, b, { ...ctx, depth: ctx.depth - 1 }));
77
+ return [...new Set(rendered)].join(' | ') + suffix;
78
+ }
79
+ if (schema.allOf) {
80
+ const merged = schema.allOf
81
+ .map((b) => resolveRef(spec, b, ctx.seen).schema ?? {})
82
+ .reduce((acc, s) => ({ ...acc, ...s, properties: { ...acc.properties, ...s.properties }, required: [...(acc.required ?? []), ...(s.required ?? [])] }), {});
83
+ return walk(spec, merged, ctx);
84
+ }
85
+ if (primaryType(schema) === 'array') {
86
+ const inner = walk(spec, schema.items ?? {}, ctx);
87
+ return (inner.includes('\n') ? `Array<${inner}>` : `${inner}[]`) + suffix;
88
+ }
89
+ const label = scalarLabel(schema);
90
+ if (label !== null)
91
+ return label + suffix;
92
+ if (ctx.depth <= 0) {
93
+ const fields = Object.keys(schema.properties ?? {}).length;
94
+ ctx.state.truncated = true;
95
+ const hint = name ? ` → api_schema('${name}')` : ' → increase depth';
96
+ return `{ … ${fields ? `${fields} fields` : '[key: string]'}${hint} }${suffix}`;
97
+ }
98
+ const required = new Set(schema.required ?? []);
99
+ const lines = [];
100
+ const pad = ' '.repeat(ctx.indent + 1);
101
+ for (const [key, propRaw] of Object.entries(schema.properties ?? {})) {
102
+ if (ctx.state.count >= ctx.state.budget) {
103
+ lines.push(`${pad}… cut (budget of ${ctx.state.budget} fields) — increase depth or narrow the path`);
104
+ ctx.state.truncated = true;
105
+ break;
106
+ }
107
+ ctx.state.count += 1;
108
+ const rendered = walk(spec, propRaw, { ...ctx, depth: ctx.depth - 1, indent: ctx.indent + 1 });
109
+ const prop = resolveRef(spec, propRaw, ctx.seen).schema;
110
+ const note = prop?.description ? ` // ${String(prop.description).slice(0, 80)}` : '';
111
+ lines.push(`${pad}${key}${required.has(key) ? '' : '?'}: ${rendered}${note}`);
112
+ }
113
+ if (schema.additionalProperties && typeof schema.additionalProperties === 'object') {
114
+ lines.push(`${pad}[key: string]: ${walk(spec, schema.additionalProperties, { ...ctx, depth: ctx.depth - 1, indent: ctx.indent + 1 })}`);
115
+ }
116
+ return `{\n${lines.join('\n')}\n${' '.repeat(ctx.indent)}}${suffix}`;
117
+ }
118
+ /**
119
+ * Renders a compact pseudo-type of a schema with a depth limit and a field budget.
120
+ */
121
+ export function renderOutline(spec, schema, { depth = 3, budget = 400 } = {}) {
122
+ const state = { count: 0, budget, truncated: false };
123
+ const text = walk(spec, schema, { depth, indent: 0, seen: new Set(), state });
124
+ return { text, truncated: state.truncated, fields: state.count };
125
+ }
126
+ /**
127
+ * Returns the schema as JSON with $refs substituted, cut at a depth.
128
+ */
129
+ export function resolveJson(spec, node, depth = 6, seen = new Set()) {
130
+ const { schema } = resolveRef(spec, node, seen);
131
+ if (!schema || typeof schema !== 'object')
132
+ return schema;
133
+ if (schema.__cycle || schema.__unresolved) {
134
+ return { $ref: schema.__cycle ?? schema.__unresolved, note: schema.__cycle ? 'cycle' : 'not found' };
135
+ }
136
+ if (depth <= 0)
137
+ return schema.type ? { type: schema.type } : { note: '… deeper levels cut' };
138
+ const out = {};
139
+ for (const [key, value] of Object.entries(schema)) {
140
+ if (key === 'properties' && value && typeof value === 'object') {
141
+ out.properties = {};
142
+ for (const [prop, sub] of Object.entries(value))
143
+ out.properties[prop] = resolveJson(spec, sub, depth - 1, new Set(seen));
144
+ }
145
+ else if (key === 'items') {
146
+ out.items = resolveJson(spec, value, depth - 1, new Set(seen));
147
+ }
148
+ else {
149
+ out[key] = value;
150
+ }
151
+ }
152
+ return out;
153
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Input schemas of the tools. The server infers handler argument types from them.
3
+ */
4
+ import { z } from 'zod';
5
+ export declare const specInfoInput: {
6
+ refresh: z.ZodDefault<z.ZodBoolean>;
7
+ };
8
+ export declare const searchInput: {
9
+ query: z.ZodOptional<z.ZodString>;
10
+ method: z.ZodOptional<z.ZodEnum<{
11
+ GET: "GET";
12
+ DELETE: "DELETE";
13
+ POST: "POST";
14
+ PUT: "PUT";
15
+ PATCH: "PATCH";
16
+ }>>;
17
+ group: z.ZodOptional<z.ZodString>;
18
+ include_admin: z.ZodDefault<z.ZodBoolean>;
19
+ has_body: z.ZodOptional<z.ZodBoolean>;
20
+ limit: z.ZodDefault<z.ZodNumber>;
21
+ };
22
+ export declare const endpointInput: {
23
+ endpoint: z.ZodString;
24
+ depth: z.ZodDefault<z.ZodNumber>;
25
+ mode: z.ZodDefault<z.ZodEnum<{
26
+ outline: "outline";
27
+ json: "json";
28
+ }>>;
29
+ };
30
+ export declare const schemaInput: {
31
+ name: z.ZodString;
32
+ path: z.ZodOptional<z.ZodString>;
33
+ depth: z.ZodDefault<z.ZodNumber>;
34
+ mode: z.ZodDefault<z.ZodEnum<{
35
+ outline: "outline";
36
+ json: "json";
37
+ }>>;
38
+ };
39
+ export declare const typesInput: {
40
+ endpoint: z.ZodString;
41
+ include: z.ZodDefault<z.ZodArray<z.ZodEnum<{
42
+ params: "params";
43
+ request: "request";
44
+ response: "response";
45
+ }>>>;
46
+ name_prefix: z.ZodDefault<z.ZodString>;
47
+ };
48
+ export declare const getInput: {
49
+ endpoint: z.ZodString;
50
+ path_params: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>>;
51
+ query: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
52
+ as: z.ZodDefault<z.ZodString>;
53
+ identity: z.ZodOptional<z.ZodString>;
54
+ };
55
+ export declare const requestInput: {
56
+ method: z.ZodEnum<{
57
+ GET: "GET";
58
+ DELETE: "DELETE";
59
+ POST: "POST";
60
+ PUT: "PUT";
61
+ PATCH: "PATCH";
62
+ }>;
63
+ endpoint: z.ZodString;
64
+ path_params: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>>;
65
+ query: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
66
+ body: z.ZodOptional<z.ZodUnknown>;
67
+ as: z.ZodDefault<z.ZodString>;
68
+ identity: z.ZodOptional<z.ZodString>;
69
+ reason: z.ZodOptional<z.ZodString>;
70
+ confirm_danger: z.ZodDefault<z.ZodBoolean>;
71
+ };
72
+ export declare const authInput: {
73
+ identity: z.ZodOptional<z.ZodString>;
74
+ refresh: z.ZodDefault<z.ZodBoolean>;
75
+ show_token: z.ZodDefault<z.ZodBoolean>;
76
+ };
77
+ export declare const callLogInput: {
78
+ limit: z.ZodDefault<z.ZodNumber>;
79
+ };
80
+ export declare const recipeInput: {
81
+ name: z.ZodOptional<z.ZodString>;
82
+ };
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Input schemas of the tools. The server infers handler argument types from them.
3
+ */
4
+ import { z } from 'zod';
5
+ const method = z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']);
6
+ const endpoint = z.string().min(1).describe('"METHOD /path", a path with a single operation, or a unique operationId');
7
+ const depth = z.number().int().min(1).max(6).default(3).describe('How deep nested schemas are expanded');
8
+ const renderMode = z.enum(['outline', 'json']).default('outline').describe('outline: a compact pseudo-type; json: the schema with $refs resolved');
9
+ const as = z
10
+ .string()
11
+ .default('auto')
12
+ .describe("'auto' uses the first security alternative with configured credentials; 'anonymous' sends none; or a security scheme name from the spec");
13
+ const identity = z.string().optional().describe('Identity passed to the auth module, e.g. a user id');
14
+ const pathParams = z.record(z.string(), z.union([z.string(), z.number()])).default({}).describe('Path parameters');
15
+ const query = z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).default({}).describe('Query-string parameters');
16
+ export const specInfoInput = {
17
+ refresh: z.boolean().default(false).describe('Revalidate the spec now'),
18
+ };
19
+ export const searchInput = {
20
+ query: z.string().optional().describe('Words separated by spaces; all of them must match'),
21
+ method: method.optional(),
22
+ group: z.string().optional().describe('Group or path prefix, e.g. users or admin/accounts'),
23
+ include_admin: z.boolean().default(true).describe('Include /admin endpoints'),
24
+ has_body: z.boolean().optional().describe('Only operations with (true) or without (false) a request body'),
25
+ limit: z.number().int().min(1).max(100).default(30),
26
+ };
27
+ export const endpointInput = { endpoint, depth, mode: renderMode };
28
+ export const schemaInput = {
29
+ name: z.string().min(1).describe('Schema name in components.schemas'),
30
+ path: z.string().optional().describe('Dotted path inside the schema, e.g. data.meta'),
31
+ depth,
32
+ mode: renderMode,
33
+ };
34
+ export const typesInput = {
35
+ endpoint,
36
+ include: z.array(z.enum(['request', 'response', 'params'])).default(['request', 'response', 'params']),
37
+ name_prefix: z.string().default('').describe('Prefix for generated type names'),
38
+ };
39
+ export const getInput = { endpoint, path_params: pathParams, query, as, identity };
40
+ export const requestInput = {
41
+ method,
42
+ endpoint,
43
+ path_params: pathParams,
44
+ query,
45
+ body: z.unknown().optional().describe('JSON body'),
46
+ as,
47
+ identity,
48
+ reason: z.string().optional().describe('Note for the call journal: why the call was made'),
49
+ confirm_danger: z.boolean().default(false).describe('Required for operations classified as destructive'),
50
+ };
51
+ export const authInput = {
52
+ identity,
53
+ refresh: z.boolean().default(false).describe('Mint new tokens even if cached ones are still valid'),
54
+ show_token: z.boolean().default(false).describe('Return full tokens instead of previews'),
55
+ };
56
+ export const callLogInput = {
57
+ limit: z.number().int().min(1).max(500).default(50),
58
+ };
59
+ export const recipeInput = {
60
+ name: z.string().optional().describe('Recipe name; omit to list recipes'),
61
+ };