stack/apps/backend/scripts/generate-openapi.ts
Moritz Schneider ab81ad14e1
Implement Model Context Protocol server poc (#552)
<!--

Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md

-->

<!-- ELLIPSIS_HIDDEN -->


----

> [!IMPORTANT]
> Introduces a new Model Context Protocol server with OpenAPI
integration, updates build configurations, and adds necessary files for
initial release.
> 
>   - **New MCP Server**:
> - Implements MCP server in `src/index.ts` using
`@modelcontextprotocol/sdk`.
> - Handles tool requests with `ListToolsRequestSchema` and
`CallToolRequestSchema`.
>     - Supports 40 endpoints, defined in `operationIDs`.
>   - **OpenAPI Integration**:
> - Generates OpenAPI schema in `generate-openapi.ts` and writes to
`mcp-server/openapi`.
> - Converts OpenAPI parameters to JSON schema in
`openapi-to-jsonschema.ts`.
>   - **Configuration and Build**:
> - Adds `package.json` for MCP server with dependencies and scripts.
>     - Configures ESLint in `.eslintrc.cjs`.
> - Updates `Dockerfile` and `turbo.json` to include MCP server in build
process.
>   - **Miscellaneous**:
>     - Adds `.gitignore` for OpenAPI JSON files.
>     - Initial release noted in `CHANGELOG.md`.
> 
> <sup>This description was created by </sup>[<img alt="Ellipsis"
src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=stack-auth%2Fstack-auth&utm_source=github&utm_medium=referral)<sup>
for d0970c4059. It will automatically
update as commits are pushed.</sup>


<!-- ELLIPSIS_HIDDEN -->

---------

Co-authored-by: Konsti Wohlwend <n2d4xc@gmail.com>
2025-03-19 18:49:39 +00:00

52 lines
2.4 KiB
TypeScript

import { parseOpenAPI, parseWebhookOpenAPI } from '@/lib/openapi';
import { isSmartRouteHandler } from '@/route-handlers/smart-route-handler';
import { webhookEvents } from '@stackframe/stack-shared/dist/interface/webhooks';
import { writeFileSyncIfChanged } from '@stackframe/stack-shared/dist/utils/fs';
import { HTTP_METHODS } from '@stackframe/stack-shared/dist/utils/http';
import { typedKeys } from '@stackframe/stack-shared/dist/utils/objects';
import { glob } from 'glob';
import path from 'path';
import yaml from 'yaml';
async function main() {
console.log("Started docs schema generator");
for (const audience of ['client', 'server', 'admin'] as const) {
const filePathPrefix = path.resolve(process.platform === "win32" ? "apps/src/app/api/latest" : "src/app/api/latest");
const importPathPrefix = "@/app/api/latest";
const filePaths = [...await glob(filePathPrefix + "/**/route.{js,jsx,ts,tsx}")];
const openApiSchemaObject = parseOpenAPI({
endpoints: new Map(await Promise.all(filePaths.map(async (filePath) => {
if (!filePath.startsWith(filePathPrefix)) {
throw new Error(`Invalid file path: ${filePath}`);
}
const suffix = filePath.slice(filePathPrefix.length);
const midfix = suffix.slice(0, suffix.lastIndexOf("/route."));
const importPath = `${importPathPrefix}${suffix}`;
const urlPath = midfix.replaceAll("[", "{").replaceAll("]", "}");
const myModule = require(importPath);
const handlersByMethod = new Map(
typedKeys(HTTP_METHODS).map(method => [method, myModule[method]] as const)
.filter(([_, handler]) => isSmartRouteHandler(handler))
);
return [urlPath, handlersByMethod] as const;
}))),
audience,
});
const openAPISchema = yaml.stringify(openApiSchemaObject);
writeFileSyncIfChanged(`../mcp-server/openapi/${audience}.json`, JSON.stringify(openApiSchemaObject, null, 2));
writeFileSyncIfChanged(`../../docs/fern/openapi/${audience}.yaml`, openAPISchema);
const webhookOpenAPISchema = yaml.stringify(parseWebhookOpenAPI({
webhooks: webhookEvents,
}));
writeFileSyncIfChanged(`../../docs/fern/openapi/webhooks.yaml`, webhookOpenAPISchema);
}
console.log("Successfully updated docs schemas");
}
main().catch((...args) => {
console.error(`ERROR! Could not update OpenAPI schema`, ...args);
process.exit(1);
});