frontmcp 0.3.0 → 0.4.0
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/LICENSE +201 -0
- package/README.md +448 -0
- package/package.json +4 -4
- package/src/args.d.ts +8 -0
- package/src/args.js +19 -0
- package/src/args.js.map +1 -0
- package/src/cli.d.ts +0 -1
- package/src/cli.js +30 -574
- package/src/cli.js.map +1 -1
- package/src/colors.d.ts +12 -0
- package/src/colors.js +17 -0
- package/src/colors.js.map +1 -0
- package/src/commands/build.d.ts +2 -0
- package/src/commands/build.js +43 -0
- package/src/commands/build.js.map +1 -0
- package/src/commands/create.d.ts +1 -0
- package/src/commands/create.js +204 -0
- package/src/commands/create.js.map +1 -0
- package/src/commands/dev.d.ts +2 -0
- package/src/commands/dev.js +49 -0
- package/src/commands/dev.js.map +1 -0
- package/src/commands/doctor.d.ts +1 -0
- package/src/commands/doctor.js +87 -0
- package/src/commands/doctor.js.map +1 -0
- package/src/commands/inspector.d.ts +1 -0
- package/src/commands/inspector.js +10 -0
- package/src/commands/inspector.js.map +1 -0
- package/src/commands/template.d.ts +13 -0
- package/src/commands/template.js +179 -0
- package/src/commands/template.js.map +1 -0
- package/src/templates/3rd-party-integration/src/http-client.d.ts +20 -0
- package/src/templates/3rd-party-integration/src/http-client.js +101 -0
- package/src/templates/3rd-party-integration/src/http-client.js.map +1 -0
- package/src/templates/3rd-party-integration/src/mcp-http-types.d.ts +199 -0
- package/src/templates/3rd-party-integration/src/mcp-http-types.js +51 -0
- package/src/templates/3rd-party-integration/src/mcp-http-types.js.map +1 -0
- package/src/templates/3rd-party-integration/src/tools/example.list.d.ts +125 -0
- package/src/templates/3rd-party-integration/src/tools/example.list.js +85 -0
- package/src/templates/3rd-party-integration/src/tools/example.list.js.map +1 -0
- package/src/tsconfig.d.ts +34 -0
- package/src/tsconfig.js +103 -0
- package/src/tsconfig.js.map +1 -0
- package/src/utils/fs.d.ts +11 -0
- package/src/utils/fs.js +104 -0
- package/src/utils/fs.js.map +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runInspector(): Promise<void>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runInspector = runInspector;
|
|
4
|
+
const colors_1 = require("../colors");
|
|
5
|
+
const fs_1 = require("../utils/fs");
|
|
6
|
+
async function runInspector() {
|
|
7
|
+
console.log(`${(0, colors_1.c)('cyan', '[inspector]')} launching MCP Inspector...`);
|
|
8
|
+
await (0, fs_1.runCmd)('npx', ['-y', '@modelcontextprotocol/inspector']);
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=inspector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inspector.js","sourceRoot":"","sources":["../../../src/commands/inspector.ts"],"names":[],"mappings":";;AAGA,oCAGC;AAND,sCAA8B;AAC9B,oCAAqC;AAE9B,KAAK,UAAU,YAAY;IAChC,OAAO,CAAC,GAAG,CAAC,GAAG,IAAA,UAAC,EAAC,MAAM,EAAE,aAAa,CAAC,6BAA6B,CAAC,CAAC;IACtE,MAAM,IAAA,WAAM,EAAC,KAAK,EAAE,CAAC,IAAI,EAAE,iCAAiC,CAAC,CAAC,CAAC;AACjE,CAAC","sourcesContent":["import { c } from '../colors';\nimport { runCmd } from '../utils/fs';\n\nexport async function runInspector(): Promise<void> {\n console.log(`${c('cyan', '[inspector]')} launching MCP Inspector...`);\n await runCmd('npx', ['-y', '@modelcontextprotocol/inspector']);\n}\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* frontmcp template <templateName>
|
|
3
|
+
*
|
|
4
|
+
* Example:
|
|
5
|
+
* npx frontmcp template 3rd-party-integration
|
|
6
|
+
*
|
|
7
|
+
* This will look for templates in:
|
|
8
|
+
* src/templates/<templateName> (built to dist/templates/<templateName>)
|
|
9
|
+
* and copy them into:
|
|
10
|
+
* integrations/<owner>/<service>
|
|
11
|
+
* with __OWNER__, __SERVICE__, __AUTH_TYPE__ replaced.
|
|
12
|
+
*/
|
|
13
|
+
export declare function runTemplate(templateName?: string): Promise<void>;
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runTemplate = runTemplate;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const path = tslib_1.__importStar(require("path"));
|
|
6
|
+
const readline = tslib_1.__importStar(require("readline"));
|
|
7
|
+
const colors_1 = require("../colors");
|
|
8
|
+
const fs_1 = require("../utils/fs");
|
|
9
|
+
/**
|
|
10
|
+
* frontmcp template <templateName>
|
|
11
|
+
*
|
|
12
|
+
* Example:
|
|
13
|
+
* npx frontmcp template 3rd-party-integration
|
|
14
|
+
*
|
|
15
|
+
* This will look for templates in:
|
|
16
|
+
* src/templates/<templateName> (built to dist/templates/<templateName>)
|
|
17
|
+
* and copy them into:
|
|
18
|
+
* integrations/<owner>/<service>
|
|
19
|
+
* with __OWNER__, __SERVICE__, __AUTH_TYPE__ replaced.
|
|
20
|
+
*/
|
|
21
|
+
async function runTemplate(templateName) {
|
|
22
|
+
const name = (templateName || '').trim();
|
|
23
|
+
if (!name) {
|
|
24
|
+
console.log((0, colors_1.c)('red', 'Template name is required (e.g. 3rd-party-integration).'));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const rl = readline.createInterface({
|
|
28
|
+
input: process.stdin,
|
|
29
|
+
output: process.stdout,
|
|
30
|
+
});
|
|
31
|
+
const ask = (q) => new Promise((resolve) => rl.question(q, (ans) => resolve(ans.trim())));
|
|
32
|
+
try {
|
|
33
|
+
const templatesRoot = path.resolve(__dirname, '..', 'templates');
|
|
34
|
+
// 1) Collect available templates up-front
|
|
35
|
+
const available = await listAvailableTemplates(templatesRoot);
|
|
36
|
+
if (!available.length) {
|
|
37
|
+
console.log((0, colors_1.c)('red', `No templates found under ${path.relative(process.cwd(), templatesRoot)}.`));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
// 2) Sanitize the name and choose the actual template folder
|
|
41
|
+
const sanitized = sanitizeTemplateName(name);
|
|
42
|
+
let selected = null;
|
|
43
|
+
if (available.includes(name)) {
|
|
44
|
+
selected = name;
|
|
45
|
+
}
|
|
46
|
+
else if (available.includes(sanitized)) {
|
|
47
|
+
selected = sanitized;
|
|
48
|
+
if (sanitized !== name) {
|
|
49
|
+
console.log((0, colors_1.c)('yellow', `[template] Template "${name}" not found, using sanitized "${sanitized}"`));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (!selected) {
|
|
53
|
+
console.log((0, colors_1.c)('red', `Template "${name}" not found under ${path.relative(process.cwd(), templatesRoot)}.`));
|
|
54
|
+
console.log((0, colors_1.c)('gray', '\nAvailable templates:'));
|
|
55
|
+
for (const t of available) {
|
|
56
|
+
console.log(' -', t);
|
|
57
|
+
}
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const templateRoot = path.resolve(templatesRoot, selected);
|
|
61
|
+
console.log((0, colors_1.c)('cyan', '[template]') + ` Using template "${selected}" from src/templates/${selected}`);
|
|
62
|
+
// Ask for owner & service
|
|
63
|
+
let owner = await ask(`Third-party owner (e.g. google, github, slack): `);
|
|
64
|
+
if (!owner) {
|
|
65
|
+
console.log((0, colors_1.c)('red', 'Owner is required.'));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
owner = sanitizeSlug(owner);
|
|
69
|
+
let service = await ask(`Third-party service (e.g. gmail, slack-bot): `);
|
|
70
|
+
if (!service) {
|
|
71
|
+
console.log((0, colors_1.c)('red', 'Service is required.'));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
service = sanitizeSlug(service);
|
|
75
|
+
// Auth type (for placeholders only – you can use or ignore in templates)
|
|
76
|
+
let authRaw = await ask(`Default auth type (oauth2/bearer/apiKey) [oauth2]: `);
|
|
77
|
+
if (!authRaw)
|
|
78
|
+
authRaw = 'oauth2';
|
|
79
|
+
const authType = authRaw;
|
|
80
|
+
if (!['oauth2', 'bearer', 'apiKey'].includes(authType)) {
|
|
81
|
+
console.log((0, colors_1.c)('red', `Invalid auth type: ${authRaw}`));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const ctx = { owner, service, authType };
|
|
85
|
+
const cwd = process.cwd();
|
|
86
|
+
const destRoot = path.resolve(cwd, 'integrations', owner, service);
|
|
87
|
+
await (0, fs_1.ensureDir)(destRoot);
|
|
88
|
+
console.log((0, colors_1.c)('gray', `[template] Scaffolding into ${path.relative(cwd, destRoot)}`));
|
|
89
|
+
await copyTemplateTree(templateRoot, destRoot, ctx);
|
|
90
|
+
console.log('');
|
|
91
|
+
console.log((0, colors_1.c)('green', '✔ Template hydrated into: ') +
|
|
92
|
+
path.relative(cwd, destRoot));
|
|
93
|
+
console.log((0, colors_1.c)('gray', 'You can now edit or delete the generated example files and create your own tools.'));
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
rl.close();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/* -------------------------------------------------------------------------- */
|
|
100
|
+
/* Helpers */
|
|
101
|
+
/* -------------------------------------------------------------------------- */
|
|
102
|
+
function sanitizeSlug(val) {
|
|
103
|
+
return (val
|
|
104
|
+
.trim()
|
|
105
|
+
.toLowerCase()
|
|
106
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
107
|
+
.replace(/-+/g, '-')
|
|
108
|
+
.replace(/^-|-$/g, '') || 'service');
|
|
109
|
+
}
|
|
110
|
+
// More permissive for template folder names (allow . _ -)
|
|
111
|
+
function sanitizeTemplateName(val) {
|
|
112
|
+
return (val
|
|
113
|
+
.trim()
|
|
114
|
+
.replace(/[^a-zA-Z0-9._-]/g, '-') // replace weird chars
|
|
115
|
+
.replace(/-+/g, '-') // collapse multiple dashes
|
|
116
|
+
.replace(/^-|-$/g, '') || 'template');
|
|
117
|
+
}
|
|
118
|
+
async function dirExists(p) {
|
|
119
|
+
try {
|
|
120
|
+
const st = await fs_1.fsp.stat(p);
|
|
121
|
+
return st.isDirectory();
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
async function listAvailableTemplates(templatesRoot) {
|
|
128
|
+
try {
|
|
129
|
+
const entries = await fs_1.fsp.readdir(templatesRoot, { withFileTypes: true });
|
|
130
|
+
return entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return [];
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Replace placeholders in both filenames and file contents.
|
|
138
|
+
*
|
|
139
|
+
* Supported placeholders:
|
|
140
|
+
* __OWNER__
|
|
141
|
+
* __SERVICE__
|
|
142
|
+
* __AUTH_TYPE__
|
|
143
|
+
*/
|
|
144
|
+
function applyPlaceholders(input, ctx) {
|
|
145
|
+
return input
|
|
146
|
+
.replaceAll('__OWNER__', ctx.owner)
|
|
147
|
+
.replaceAll('__SERVICE__', ctx.service)
|
|
148
|
+
.replaceAll('__AUTH_TYPE__', ctx.authType);
|
|
149
|
+
}
|
|
150
|
+
async function copyTemplateTree(templateRoot, destRoot, ctx) {
|
|
151
|
+
const walk = async (srcDir) => {
|
|
152
|
+
const entries = await fs_1.fsp.readdir(srcDir, { withFileTypes: true });
|
|
153
|
+
for (const entry of entries) {
|
|
154
|
+
const srcPath = path.join(srcDir, entry.name);
|
|
155
|
+
const relFromRoot = path.relative(templateRoot, srcPath);
|
|
156
|
+
// Apply placeholders in path segments too
|
|
157
|
+
const relWithPlaceholders = applyPlaceholders(relFromRoot, ctx);
|
|
158
|
+
const destPath = path.join(destRoot, relWithPlaceholders);
|
|
159
|
+
if (entry.isDirectory()) {
|
|
160
|
+
await (0, fs_1.ensureDir)(destPath);
|
|
161
|
+
await walk(srcPath);
|
|
162
|
+
}
|
|
163
|
+
else if (entry.isFile()) {
|
|
164
|
+
// Read template file, apply placeholders, write if not existing
|
|
165
|
+
const content = await fs_1.fsp.readFile(srcPath, 'utf8');
|
|
166
|
+
const processed = applyPlaceholders(content, ctx);
|
|
167
|
+
if (await (0, fs_1.fileExists)(destPath)) {
|
|
168
|
+
console.log((0, colors_1.c)('gray', `skip: ${path.relative(process.cwd(), destPath)} already exists`));
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
await (0, fs_1.ensureDir)(path.dirname(destPath));
|
|
172
|
+
await fs_1.fsp.writeFile(destPath, processed, 'utf8');
|
|
173
|
+
console.log((0, colors_1.c)('green', `✓ created ${path.relative(process.cwd(), destPath)}`));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
await walk(templateRoot);
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.js","sourceRoot":"","sources":["../../../src/commands/template.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'path';\nimport * as readline from 'readline';\nimport { c } from '../colors';\nimport { ensureDir, fileExists, fsp } from '../utils/fs';\n\ntype AuthType = 'oauth2' | 'bearer' | 'apiKey';\n\ninterface TemplateContext {\n owner: string;\n service: string;\n authType: AuthType;\n}\n\n/**\n * frontmcp template <templateName>\n *\n * Example:\n * npx frontmcp template 3rd-party-integration\n *\n * This will look for templates in:\n * src/templates/<templateName> (built to dist/templates/<templateName>)\n * and copy them into:\n * integrations/<owner>/<service>\n * with __OWNER__, __SERVICE__, __AUTH_TYPE__ replaced.\n */\nexport async function runTemplate(templateName?: string): Promise<void> {\n const name = (templateName || '').trim();\n if (!name) {\n console.log(c('red', 'Template name is required (e.g. 3rd-party-integration).'));\n return;\n }\n\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n const ask = (q: string): Promise<string> =>\n new Promise((resolve) => rl.question(q, (ans) => resolve(ans.trim())));\n\n try {\n const templatesRoot = path.resolve(__dirname, '..', 'templates');\n\n // 1) Collect available templates up-front\n const available = await listAvailableTemplates(templatesRoot);\n if (!available.length) {\n console.log(\n c('red', `No templates found under ${path.relative(process.cwd(), templatesRoot)}.`),\n );\n return;\n }\n\n // 2) Sanitize the name and choose the actual template folder\n const sanitized = sanitizeTemplateName(name);\n let selected: string | null = null;\n\n if (available.includes(name)) {\n selected = name;\n } else if (available.includes(sanitized)) {\n selected = sanitized;\n if (sanitized !== name) {\n console.log(\n c('yellow', `[template] Template \"${name}\" not found, using sanitized \"${sanitized}\"`),\n );\n }\n }\n\n if (!selected) {\n console.log(\n c('red', `Template \"${name}\" not found under ${path.relative(process.cwd(), templatesRoot)}.`),\n );\n console.log(c('gray', '\\nAvailable templates:'));\n for (const t of available) {\n console.log(' -', t);\n }\n return;\n }\n\n const templateRoot = path.resolve(templatesRoot, selected);\n\n console.log(\n c('cyan', '[template]') + ` Using template \"${selected}\" from src/templates/${selected}`,\n );\n\n // Ask for owner & service\n let owner = await ask(`Third-party owner (e.g. google, github, slack): `);\n if (!owner) {\n console.log(c('red', 'Owner is required.'));\n return;\n }\n owner = sanitizeSlug(owner);\n\n let service = await ask(\n `Third-party service (e.g. gmail, slack-bot): `,\n );\n if (!service) {\n console.log(c('red', 'Service is required.'));\n return;\n }\n service = sanitizeSlug(service);\n\n // Auth type (for placeholders only – you can use or ignore in templates)\n let authRaw = await ask(\n `Default auth type (oauth2/bearer/apiKey) [oauth2]: `,\n );\n if (!authRaw) authRaw = 'oauth2';\n const authType = authRaw as AuthType;\n if (!['oauth2', 'bearer', 'apiKey'].includes(authType)) {\n console.log(c('red', `Invalid auth type: ${authRaw}`));\n return;\n }\n\n const ctx: TemplateContext = { owner, service, authType };\n\n const cwd = process.cwd();\n const destRoot = path.resolve(cwd, 'integrations', owner, service);\n await ensureDir(destRoot);\n\n console.log(\n c('gray', `[template] Scaffolding into ${path.relative(cwd, destRoot)}`),\n );\n\n await copyTemplateTree(templateRoot, destRoot, ctx);\n\n console.log('');\n console.log(\n c('green', '✔ Template hydrated into: ') +\n path.relative(cwd, destRoot),\n );\n console.log(\n c(\n 'gray',\n 'You can now edit or delete the generated example files and create your own tools.',\n ),\n );\n } finally {\n rl.close();\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* Helpers */\n/* -------------------------------------------------------------------------- */\n\nfunction sanitizeSlug(val: string): string {\n return (\n val\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9-]/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '') || 'service'\n );\n}\n\n// More permissive for template folder names (allow . _ -)\nfunction sanitizeTemplateName(val: string): string {\n return (\n val\n .trim()\n .replace(/[^a-zA-Z0-9._-]/g, '-') // replace weird chars\n .replace(/-+/g, '-') // collapse multiple dashes\n .replace(/^-|-$/g, '') || 'template'\n );\n}\n\nasync function dirExists(p: string): Promise<boolean> {\n try {\n const st = await fsp.stat(p);\n return st.isDirectory();\n } catch {\n return false;\n }\n}\n\nasync function listAvailableTemplates(templatesRoot: string): Promise<string[]> {\n try {\n const entries = await fsp.readdir(templatesRoot, { withFileTypes: true });\n return entries.filter((e) => e.isDirectory()).map((e) => e.name);\n } catch {\n return [];\n }\n}\n\n/**\n * Replace placeholders in both filenames and file contents.\n *\n * Supported placeholders:\n * __OWNER__\n * __SERVICE__\n * __AUTH_TYPE__\n */\nfunction applyPlaceholders(input: string, ctx: TemplateContext): string {\n return input\n .replaceAll('__OWNER__', ctx.owner)\n .replaceAll('__SERVICE__', ctx.service)\n .replaceAll('__AUTH_TYPE__', ctx.authType);\n}\n\nasync function copyTemplateTree(\n templateRoot: string,\n destRoot: string,\n ctx: TemplateContext,\n): Promise<void> {\n const walk = async (srcDir: string) => {\n const entries = await fsp.readdir(srcDir, { withFileTypes: true });\n\n for (const entry of entries) {\n const srcPath = path.join(srcDir, entry.name);\n const relFromRoot = path.relative(templateRoot, srcPath);\n\n // Apply placeholders in path segments too\n const relWithPlaceholders = applyPlaceholders(relFromRoot, ctx);\n const destPath = path.join(destRoot, relWithPlaceholders);\n\n if (entry.isDirectory()) {\n await ensureDir(destPath);\n await walk(srcPath);\n } else if (entry.isFile()) {\n // Read template file, apply placeholders, write if not existing\n const content = await fsp.readFile(srcPath, 'utf8');\n const processed = applyPlaceholders(content, ctx);\n\n if (await fileExists(destPath)) {\n console.log(\n c('gray', `skip: ${path.relative(process.cwd(), destPath)} already exists`),\n );\n continue;\n }\n\n await ensureDir(path.dirname(destPath));\n await fsp.writeFile(destPath, processed, 'utf8');\n console.log(\n c('green', `✓ created ${path.relative(process.cwd(), destPath)}`),\n );\n }\n }\n };\n\n await walk(templateRoot);\n}\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ExecuteContext } from "./mcp-http-types";
|
|
2
|
+
export interface HttpRequestOptions {
|
|
3
|
+
method: string;
|
|
4
|
+
path: string;
|
|
5
|
+
query?: Record<string, string | number | boolean | undefined>;
|
|
6
|
+
headers?: Record<string, string>;
|
|
7
|
+
bodyJson?: unknown;
|
|
8
|
+
}
|
|
9
|
+
export interface HttpResponseRaw {
|
|
10
|
+
status: number;
|
|
11
|
+
headers: Record<string, string>;
|
|
12
|
+
json?: any;
|
|
13
|
+
text?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare class HttpClient {
|
|
16
|
+
private ctx;
|
|
17
|
+
constructor(ctx: ExecuteContext);
|
|
18
|
+
private resolveAuthHeader;
|
|
19
|
+
request(options: HttpRequestOptions): Promise<HttpResponseRaw>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HttpClient = void 0;
|
|
4
|
+
class HttpClient {
|
|
5
|
+
constructor(ctx) {
|
|
6
|
+
this.ctx = ctx;
|
|
7
|
+
}
|
|
8
|
+
resolveAuthHeader() {
|
|
9
|
+
const { auth } = this.ctx;
|
|
10
|
+
if (auth.oauth2?.accessToken) {
|
|
11
|
+
return { Authorization: `Bearer ${auth.oauth2.accessToken}` };
|
|
12
|
+
}
|
|
13
|
+
if (auth.bearer?.token) {
|
|
14
|
+
return { Authorization: `Bearer ${auth.bearer.token}` };
|
|
15
|
+
}
|
|
16
|
+
if (auth.apiKey?.value) {
|
|
17
|
+
const headerName = auth.apiKey.name ?? "X-API-Key";
|
|
18
|
+
return { [headerName]: auth.apiKey.value };
|
|
19
|
+
}
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
async request(options) {
|
|
23
|
+
const fetchImpl = this.ctx.fetch ?? fetch;
|
|
24
|
+
const url = new URL(options.path, this.ctx.baseUrl);
|
|
25
|
+
if (options.query) {
|
|
26
|
+
for (const [k, v] of Object.entries(options.query)) {
|
|
27
|
+
if (v === undefined)
|
|
28
|
+
continue;
|
|
29
|
+
url.searchParams.set(k, String(v));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const baseHeaders = {
|
|
33
|
+
Accept: "application/json",
|
|
34
|
+
...(this.ctx.requestId ? { "X-Request-Id": this.ctx.requestId } : {}),
|
|
35
|
+
...this.resolveAuthHeader(),
|
|
36
|
+
...(options.headers ?? {})
|
|
37
|
+
};
|
|
38
|
+
const controller = new AbortController();
|
|
39
|
+
const timeoutMs = this.ctx.timeoutMs ?? 15000;
|
|
40
|
+
const t = setTimeout(() => controller.abort(), timeoutMs);
|
|
41
|
+
const init = {
|
|
42
|
+
method: options.method,
|
|
43
|
+
headers: baseHeaders,
|
|
44
|
+
signal: controller.signal
|
|
45
|
+
};
|
|
46
|
+
if (options.bodyJson !== undefined) {
|
|
47
|
+
init.body = JSON.stringify(options.bodyJson);
|
|
48
|
+
baseHeaders["Content-Type"] = baseHeaders["Content-Type"] ?? "application/json";
|
|
49
|
+
}
|
|
50
|
+
if (this.ctx.logDebug) {
|
|
51
|
+
console.debug("[HttpClient] Request", {
|
|
52
|
+
url: url.toString(),
|
|
53
|
+
method: options.method,
|
|
54
|
+
headers: baseHeaders,
|
|
55
|
+
body: options.bodyJson
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
const res = await fetchImpl(url.toString(), init);
|
|
60
|
+
const headersObj = Object.fromEntries(res.headers.entries());
|
|
61
|
+
let json;
|
|
62
|
+
let text;
|
|
63
|
+
const contentType = res.headers.get("content-type") || "";
|
|
64
|
+
if (contentType.includes("application/json")) {
|
|
65
|
+
try {
|
|
66
|
+
json = await res.json();
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
json = undefined;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
try {
|
|
74
|
+
text = await res.text();
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
text = undefined;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (this.ctx.logDebug) {
|
|
81
|
+
console.debug("[HttpClient] Response", {
|
|
82
|
+
status: res.status,
|
|
83
|
+
headers: headersObj,
|
|
84
|
+
json,
|
|
85
|
+
text
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
status: res.status,
|
|
90
|
+
headers: headersObj,
|
|
91
|
+
json,
|
|
92
|
+
text
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
clearTimeout(t);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
exports.HttpClient = HttpClient;
|
|
101
|
+
//# sourceMappingURL=http-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-client.js","sourceRoot":"","sources":["../../../../../src/templates/3rd-party-integration/src/http-client.ts"],"names":[],"mappings":"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","sourcesContent":["import { ExecuteContext } from \"./mcp-http-types\";\n\nexport interface HttpRequestOptions {\n method: string;\n path: string; // already resolved (including path params)\n query?: Record<string, string | number | boolean | undefined>;\n headers?: Record<string, string>;\n bodyJson?: unknown;\n}\n\nexport interface HttpResponseRaw {\n status: number;\n headers: Record<string, string>;\n json?: any;\n text?: string;\n}\n\nexport class HttpClient {\n constructor(private ctx: ExecuteContext) {}\n\n private resolveAuthHeader(): Record<string, string> {\n const { auth } = this.ctx;\n if (auth.oauth2?.accessToken) {\n return { Authorization: `Bearer ${auth.oauth2.accessToken}` };\n }\n if (auth.bearer?.token) {\n return { Authorization: `Bearer ${auth.bearer.token}` };\n }\n if (auth.apiKey?.value) {\n const headerName = auth.apiKey.name ?? \"X-API-Key\";\n return { [headerName]: auth.apiKey.value };\n }\n return {};\n }\n\n async request(options: HttpRequestOptions): Promise<HttpResponseRaw> {\n const fetchImpl = this.ctx.fetch ?? fetch;\n const url = new URL(options.path, this.ctx.baseUrl);\n\n if (options.query) {\n for (const [k, v] of Object.entries(options.query)) {\n if (v === undefined) continue;\n url.searchParams.set(k, String(v));\n }\n }\n\n const baseHeaders: Record<string, string> = {\n Accept: \"application/json\",\n ...(this.ctx.requestId ? { \"X-Request-Id\": this.ctx.requestId } : {}),\n ...this.resolveAuthHeader(),\n ...(options.headers ?? {})\n };\n\n const controller = new AbortController();\n const timeoutMs = this.ctx.timeoutMs ?? 15000;\n const t = setTimeout(() => controller.abort(), timeoutMs);\n\n const init: RequestInit = {\n method: options.method,\n headers: baseHeaders,\n signal: controller.signal\n };\n\n if (options.bodyJson !== undefined) {\n init.body = JSON.stringify(options.bodyJson);\n baseHeaders[\"Content-Type\"] = baseHeaders[\"Content-Type\"] ?? \"application/json\";\n }\n\n if (this.ctx.logDebug) {\n console.debug(\"[HttpClient] Request\", {\n url: url.toString(),\n method: options.method,\n headers: baseHeaders,\n body: options.bodyJson\n });\n }\n\n try {\n const res = await fetchImpl(url.toString(), init);\n const headersObj = Object.fromEntries(res.headers.entries());\n\n let json: any | undefined;\n let text: string | undefined;\n const contentType = res.headers.get(\"content-type\") || \"\";\n\n if (contentType.includes(\"application/json\")) {\n try {\n json = await res.json();\n } catch {\n json = undefined;\n }\n } else {\n try {\n text = await res.text();\n } catch {\n text = undefined;\n }\n }\n\n if (this.ctx.logDebug) {\n console.debug(\"[HttpClient] Response\", {\n status: res.status,\n headers: headersObj,\n json,\n text\n });\n }\n\n return {\n status: res.status,\n headers: headersObj,\n json,\n text\n };\n } finally {\n clearTimeout(t);\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Shared output wrapper: all HTTP tools must return { status, headers, body }.
|
|
4
|
+
*/
|
|
5
|
+
export declare const HttpStatusSchema: z.ZodNumber;
|
|
6
|
+
export declare const HttpHeadersSchema: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
7
|
+
export declare const makeHttpOutputSchema: <TBody extends z.ZodTypeAny>(bodySchema: TBody) => z.ZodObject<{
|
|
8
|
+
status: z.ZodNumber;
|
|
9
|
+
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
10
|
+
body: TBody;
|
|
11
|
+
}, "strip", z.ZodTypeAny, z.objectUtil.addQuestionMarks<z.baseObjectOutputType<{
|
|
12
|
+
status: z.ZodNumber;
|
|
13
|
+
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
14
|
+
body: TBody;
|
|
15
|
+
}>, any> extends infer T ? { [k in keyof T]: T[k]; } : never, z.baseObjectInputType<{
|
|
16
|
+
status: z.ZodNumber;
|
|
17
|
+
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
18
|
+
body: TBody;
|
|
19
|
+
}> extends infer T_1 ? { [k_1 in keyof T_1]: T_1[k_1]; } : never>;
|
|
20
|
+
export type HttpOutput<TBody> = {
|
|
21
|
+
status: number;
|
|
22
|
+
headers?: Record<string, string>;
|
|
23
|
+
body: TBody;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Shared auth/context types for TS tools.
|
|
27
|
+
*/
|
|
28
|
+
export declare const AuthContextSchema: z.ZodObject<{
|
|
29
|
+
oauth2: z.ZodOptional<z.ZodObject<{
|
|
30
|
+
accessToken: z.ZodString;
|
|
31
|
+
idToken: z.ZodOptional<z.ZodString>;
|
|
32
|
+
claims: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
33
|
+
}, "strip", z.ZodTypeAny, {
|
|
34
|
+
accessToken: string;
|
|
35
|
+
idToken?: string | undefined;
|
|
36
|
+
claims?: Record<string, any> | undefined;
|
|
37
|
+
}, {
|
|
38
|
+
accessToken: string;
|
|
39
|
+
idToken?: string | undefined;
|
|
40
|
+
claims?: Record<string, any> | undefined;
|
|
41
|
+
}>>;
|
|
42
|
+
bearer: z.ZodOptional<z.ZodObject<{
|
|
43
|
+
token: z.ZodString;
|
|
44
|
+
}, "strip", z.ZodTypeAny, {
|
|
45
|
+
token: string;
|
|
46
|
+
}, {
|
|
47
|
+
token: string;
|
|
48
|
+
}>>;
|
|
49
|
+
apiKey: z.ZodOptional<z.ZodObject<{
|
|
50
|
+
name: z.ZodOptional<z.ZodString>;
|
|
51
|
+
value: z.ZodString;
|
|
52
|
+
}, "strip", z.ZodTypeAny, {
|
|
53
|
+
value: string;
|
|
54
|
+
name?: string | undefined;
|
|
55
|
+
}, {
|
|
56
|
+
value: string;
|
|
57
|
+
name?: string | undefined;
|
|
58
|
+
}>>;
|
|
59
|
+
}, "strip", z.ZodTypeAny, {
|
|
60
|
+
oauth2?: {
|
|
61
|
+
accessToken: string;
|
|
62
|
+
idToken?: string | undefined;
|
|
63
|
+
claims?: Record<string, any> | undefined;
|
|
64
|
+
} | undefined;
|
|
65
|
+
bearer?: {
|
|
66
|
+
token: string;
|
|
67
|
+
} | undefined;
|
|
68
|
+
apiKey?: {
|
|
69
|
+
value: string;
|
|
70
|
+
name?: string | undefined;
|
|
71
|
+
} | undefined;
|
|
72
|
+
}, {
|
|
73
|
+
oauth2?: {
|
|
74
|
+
accessToken: string;
|
|
75
|
+
idToken?: string | undefined;
|
|
76
|
+
claims?: Record<string, any> | undefined;
|
|
77
|
+
} | undefined;
|
|
78
|
+
bearer?: {
|
|
79
|
+
token: string;
|
|
80
|
+
} | undefined;
|
|
81
|
+
apiKey?: {
|
|
82
|
+
value: string;
|
|
83
|
+
name?: string | undefined;
|
|
84
|
+
} | undefined;
|
|
85
|
+
}>;
|
|
86
|
+
export type AuthContext = z.infer<typeof AuthContextSchema>;
|
|
87
|
+
export declare const ExecuteContextSchema: z.ZodObject<{
|
|
88
|
+
baseUrl: z.ZodString;
|
|
89
|
+
auth: z.ZodObject<{
|
|
90
|
+
oauth2: z.ZodOptional<z.ZodObject<{
|
|
91
|
+
accessToken: z.ZodString;
|
|
92
|
+
idToken: z.ZodOptional<z.ZodString>;
|
|
93
|
+
claims: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
94
|
+
}, "strip", z.ZodTypeAny, {
|
|
95
|
+
accessToken: string;
|
|
96
|
+
idToken?: string | undefined;
|
|
97
|
+
claims?: Record<string, any> | undefined;
|
|
98
|
+
}, {
|
|
99
|
+
accessToken: string;
|
|
100
|
+
idToken?: string | undefined;
|
|
101
|
+
claims?: Record<string, any> | undefined;
|
|
102
|
+
}>>;
|
|
103
|
+
bearer: z.ZodOptional<z.ZodObject<{
|
|
104
|
+
token: z.ZodString;
|
|
105
|
+
}, "strip", z.ZodTypeAny, {
|
|
106
|
+
token: string;
|
|
107
|
+
}, {
|
|
108
|
+
token: string;
|
|
109
|
+
}>>;
|
|
110
|
+
apiKey: z.ZodOptional<z.ZodObject<{
|
|
111
|
+
name: z.ZodOptional<z.ZodString>;
|
|
112
|
+
value: z.ZodString;
|
|
113
|
+
}, "strip", z.ZodTypeAny, {
|
|
114
|
+
value: string;
|
|
115
|
+
name?: string | undefined;
|
|
116
|
+
}, {
|
|
117
|
+
value: string;
|
|
118
|
+
name?: string | undefined;
|
|
119
|
+
}>>;
|
|
120
|
+
}, "strip", z.ZodTypeAny, {
|
|
121
|
+
oauth2?: {
|
|
122
|
+
accessToken: string;
|
|
123
|
+
idToken?: string | undefined;
|
|
124
|
+
claims?: Record<string, any> | undefined;
|
|
125
|
+
} | undefined;
|
|
126
|
+
bearer?: {
|
|
127
|
+
token: string;
|
|
128
|
+
} | undefined;
|
|
129
|
+
apiKey?: {
|
|
130
|
+
value: string;
|
|
131
|
+
name?: string | undefined;
|
|
132
|
+
} | undefined;
|
|
133
|
+
}, {
|
|
134
|
+
oauth2?: {
|
|
135
|
+
accessToken: string;
|
|
136
|
+
idToken?: string | undefined;
|
|
137
|
+
claims?: Record<string, any> | undefined;
|
|
138
|
+
} | undefined;
|
|
139
|
+
bearer?: {
|
|
140
|
+
token: string;
|
|
141
|
+
} | undefined;
|
|
142
|
+
apiKey?: {
|
|
143
|
+
value: string;
|
|
144
|
+
name?: string | undefined;
|
|
145
|
+
} | undefined;
|
|
146
|
+
}>;
|
|
147
|
+
fetch: z.ZodOptional<z.ZodFunction<z.ZodTuple<[z.ZodString, z.ZodAny], z.ZodUnknown>, z.ZodPromise<z.ZodAny>>>;
|
|
148
|
+
requestId: z.ZodOptional<z.ZodString>;
|
|
149
|
+
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
150
|
+
logDebug: z.ZodOptional<z.ZodBoolean>;
|
|
151
|
+
}, "strip", z.ZodTypeAny, {
|
|
152
|
+
baseUrl: string;
|
|
153
|
+
auth: {
|
|
154
|
+
oauth2?: {
|
|
155
|
+
accessToken: string;
|
|
156
|
+
idToken?: string | undefined;
|
|
157
|
+
claims?: Record<string, any> | undefined;
|
|
158
|
+
} | undefined;
|
|
159
|
+
bearer?: {
|
|
160
|
+
token: string;
|
|
161
|
+
} | undefined;
|
|
162
|
+
apiKey?: {
|
|
163
|
+
value: string;
|
|
164
|
+
name?: string | undefined;
|
|
165
|
+
} | undefined;
|
|
166
|
+
};
|
|
167
|
+
fetch?: ((args_0: string, args_1: any, ...args: unknown[]) => Promise<any>) | undefined;
|
|
168
|
+
requestId?: string | undefined;
|
|
169
|
+
timeoutMs?: number | undefined;
|
|
170
|
+
logDebug?: boolean | undefined;
|
|
171
|
+
}, {
|
|
172
|
+
baseUrl: string;
|
|
173
|
+
auth: {
|
|
174
|
+
oauth2?: {
|
|
175
|
+
accessToken: string;
|
|
176
|
+
idToken?: string | undefined;
|
|
177
|
+
claims?: Record<string, any> | undefined;
|
|
178
|
+
} | undefined;
|
|
179
|
+
bearer?: {
|
|
180
|
+
token: string;
|
|
181
|
+
} | undefined;
|
|
182
|
+
apiKey?: {
|
|
183
|
+
value: string;
|
|
184
|
+
name?: string | undefined;
|
|
185
|
+
} | undefined;
|
|
186
|
+
};
|
|
187
|
+
fetch?: ((args_0: string, args_1: any, ...args: unknown[]) => Promise<any>) | undefined;
|
|
188
|
+
requestId?: string | undefined;
|
|
189
|
+
timeoutMs?: number | undefined;
|
|
190
|
+
logDebug?: boolean | undefined;
|
|
191
|
+
}>;
|
|
192
|
+
export type ExecuteContext = z.infer<typeof ExecuteContextSchema>;
|
|
193
|
+
/**
|
|
194
|
+
* Generic execute signature for TS tools.
|
|
195
|
+
*/
|
|
196
|
+
export type ToolExecute<I, B> = (args: {
|
|
197
|
+
input: I;
|
|
198
|
+
ctx: ExecuteContext;
|
|
199
|
+
}) => Promise<HttpOutput<B>>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ExecuteContextSchema = exports.AuthContextSchema = exports.makeHttpOutputSchema = exports.HttpHeadersSchema = exports.HttpStatusSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
/**
|
|
6
|
+
* Shared output wrapper: all HTTP tools must return { status, headers, body }.
|
|
7
|
+
*/
|
|
8
|
+
exports.HttpStatusSchema = zod_1.z.number().int().min(100).max(599);
|
|
9
|
+
exports.HttpHeadersSchema = zod_1.z.record(zod_1.z.string());
|
|
10
|
+
const makeHttpOutputSchema = (bodySchema) => zod_1.z.object({
|
|
11
|
+
status: exports.HttpStatusSchema,
|
|
12
|
+
headers: exports.HttpHeadersSchema.optional(),
|
|
13
|
+
body: bodySchema
|
|
14
|
+
});
|
|
15
|
+
exports.makeHttpOutputSchema = makeHttpOutputSchema;
|
|
16
|
+
/**
|
|
17
|
+
* Shared auth/context types for TS tools.
|
|
18
|
+
*/
|
|
19
|
+
exports.AuthContextSchema = zod_1.z.object({
|
|
20
|
+
oauth2: zod_1.z
|
|
21
|
+
.object({
|
|
22
|
+
accessToken: zod_1.z.string(),
|
|
23
|
+
idToken: zod_1.z.string().optional(),
|
|
24
|
+
claims: zod_1.z.record(zod_1.z.any()).optional()
|
|
25
|
+
})
|
|
26
|
+
.optional(),
|
|
27
|
+
bearer: zod_1.z
|
|
28
|
+
.object({
|
|
29
|
+
token: zod_1.z.string()
|
|
30
|
+
})
|
|
31
|
+
.optional(),
|
|
32
|
+
apiKey: zod_1.z
|
|
33
|
+
.object({
|
|
34
|
+
name: zod_1.z.string().optional(),
|
|
35
|
+
value: zod_1.z.string()
|
|
36
|
+
})
|
|
37
|
+
.optional()
|
|
38
|
+
});
|
|
39
|
+
exports.ExecuteContextSchema = zod_1.z.object({
|
|
40
|
+
baseUrl: zod_1.z.string().url(),
|
|
41
|
+
auth: exports.AuthContextSchema,
|
|
42
|
+
fetch: zod_1.z
|
|
43
|
+
.function()
|
|
44
|
+
.args(zod_1.z.string(), zod_1.z.any())
|
|
45
|
+
.returns(zod_1.z.promise(zod_1.z.any()))
|
|
46
|
+
.optional(),
|
|
47
|
+
requestId: zod_1.z.string().optional(),
|
|
48
|
+
timeoutMs: zod_1.z.number().int().positive().optional(),
|
|
49
|
+
logDebug: zod_1.z.boolean().optional()
|
|
50
|
+
});
|
|
51
|
+
//# sourceMappingURL=mcp-http-types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-http-types.js","sourceRoot":"","sources":["../../../../../src/templates/3rd-party-integration/src/mcp-http-types.ts"],"names":[],"mappings":";;;AAAA,6BAAwB;AAExB;;GAEG;AAEU,QAAA,gBAAgB,GAAG,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AACtD,QAAA,iBAAiB,GAAG,OAAC,CAAC,MAAM,CAAC,OAAC,CAAC,MAAM,EAAE,CAAC,CAAC;AAE/C,MAAM,oBAAoB,GAAG,CAA6B,UAAiB,EAAE,EAAE,CACpF,OAAC,CAAC,MAAM,CAAC;IACP,MAAM,EAAE,wBAAgB;IACxB,OAAO,EAAE,yBAAiB,CAAC,QAAQ,EAAE;IACrC,IAAI,EAAE,UAAU;CACjB,CAAC,CAAC;AALQ,QAAA,oBAAoB,wBAK5B;AAQL;;GAEG;AAEU,QAAA,iBAAiB,GAAG,OAAC,CAAC,MAAM,CAAC;IACxC,MAAM,EAAE,OAAC;SACN,MAAM,CAAC;QACN,WAAW,EAAE,OAAC,CAAC,MAAM,EAAE;QACvB,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC9B,MAAM,EAAE,OAAC,CAAC,MAAM,CAAC,OAAC,CAAC,GAAG,EAAE,CAAC,CAAC,QAAQ,EAAE;KACrC,CAAC;SACD,QAAQ,EAAE;IACb,MAAM,EAAE,OAAC;SACN,MAAM,CAAC;QACN,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE;KAClB,CAAC;SACD,QAAQ,EAAE;IACb,MAAM,EAAE,OAAC;SACN,MAAM,CAAC;QACN,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC3B,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE;KAClB,CAAC;SACD,QAAQ,EAAE;CACd,CAAC,CAAC;AAIU,QAAA,oBAAoB,GAAG,OAAC,CAAC,MAAM,CAAC;IAC3C,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;IACzB,IAAI,EAAE,yBAAiB;IACvB,KAAK,EAAE,OAAC;SACL,QAAQ,EAAE;SACV,IAAI,CAAC,OAAC,CAAC,MAAM,EAAE,EAAE,OAAC,CAAC,GAAG,EAAE,CAAC;SACzB,OAAO,CAAC,OAAC,CAAC,OAAO,CAAC,OAAC,CAAC,GAAG,EAAE,CAAC,CAAC;SAC3B,QAAQ,EAAE;IACb,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACjD,QAAQ,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CACjC,CAAC,CAAC","sourcesContent":["import { z } from \"zod\";\n\n/**\n * Shared output wrapper: all HTTP tools must return { status, headers, body }.\n */\n\nexport const HttpStatusSchema = z.number().int().min(100).max(599);\nexport const HttpHeadersSchema = z.record(z.string());\n\nexport const makeHttpOutputSchema = <TBody extends z.ZodTypeAny>(bodySchema: TBody) =>\n z.object({\n status: HttpStatusSchema,\n headers: HttpHeadersSchema.optional(),\n body: bodySchema\n });\n\nexport type HttpOutput<TBody> = {\n status: number;\n headers?: Record<string, string>;\n body: TBody;\n};\n\n/**\n * Shared auth/context types for TS tools.\n */\n\nexport const AuthContextSchema = z.object({\n oauth2: z\n .object({\n accessToken: z.string(),\n idToken: z.string().optional(),\n claims: z.record(z.any()).optional()\n })\n .optional(),\n bearer: z\n .object({\n token: z.string()\n })\n .optional(),\n apiKey: z\n .object({\n name: z.string().optional(),\n value: z.string()\n })\n .optional()\n});\n\nexport type AuthContext = z.infer<typeof AuthContextSchema>;\n\nexport const ExecuteContextSchema = z.object({\n baseUrl: z.string().url(),\n auth: AuthContextSchema,\n fetch: z\n .function()\n .args(z.string(), z.any())\n .returns(z.promise(z.any()))\n .optional(),\n requestId: z.string().optional(),\n timeoutMs: z.number().int().positive().optional(),\n logDebug: z.boolean().optional()\n});\n\nexport type ExecuteContext = z.infer<typeof ExecuteContextSchema>;\n\n/**\n * Generic execute signature for TS tools.\n */\n\nexport type ToolExecute<I, B> = (args: {\n input: I;\n ctx: ExecuteContext;\n}) => Promise<HttpOutput<B>>;\n"]}
|