diff --git a/.well-known/mcp.json b/.well-known/mcp.json
new file mode 100644
index 0000000..44a4d5b
--- /dev/null
+++ b/.well-known/mcp.json
@@ -0,0 +1,149 @@
+{
+ "schema_version": "1.0",
+ "name": "Elysia Documentation",
+ "description": "Development documentation",
+ "version": "0.0.0",
+ "endpoints": {
+ "openapi": "http://localhost:3000/docs/json",
+ "mcp": "http://localhost:3000/.well-known/mcp.json"
+ },
+ "capabilities": {
+ "apikey": {
+ "postApiApikeyCreate": {
+ "method": "POST",
+ "path": "/api/apikey/create",
+ "summary": "create api key",
+ "parameters": {
+ "name": "string",
+ "description": "string",
+ "expiredAt": "string"
+ },
+ "required": [
+ "name",
+ "description"
+ ],
+ "command": "curl -X POST http://localhost:3000/api/apikey/create \\\n -H 'Content-Type: application/json' -d '{\"name\":\"name\",\"description\":\"description\",\"expiredAt\":\"expiredAt\"}'"
+ },
+ "getApiApikeyList": {
+ "method": "GET",
+ "path": "/api/apikey/list",
+ "summary": "get api key list",
+ "command": "curl -X GET http://localhost:3000/api/apikey/list"
+ },
+ "deleteApiApikeyDelete": {
+ "method": "DELETE",
+ "path": "/api/apikey/delete",
+ "summary": "delete api key",
+ "parameters": {
+ "id": "string"
+ },
+ "required": [
+ "id"
+ ],
+ "command": "curl -X DELETE http://localhost:3000/api/apikey/delete \\\n -H 'Content-Type: application/json' -d '{\"id\":\"id\"}'"
+ }
+ },
+ "darmasaba": {
+ "getApiDarmasabaRepos": {
+ "method": "GET",
+ "path": "/api/darmasaba/repos",
+ "summary": "/repos",
+ "command": "curl -X GET http://localhost:3000/api/darmasaba/repos"
+ },
+ "getApiDarmasabaLs": {
+ "method": "GET",
+ "path": "/api/darmasaba/ls",
+ "summary": "/ls",
+ "command": "curl -X GET http://localhost:3000/api/darmasaba/ls"
+ },
+ "getApiDarmasabaLsByDir": {
+ "method": "GET",
+ "path": "/api/darmasaba/ls/{dir}",
+ "summary": "/ls/:dir",
+ "parameters": {
+ "dir": "string"
+ },
+ "required": [
+ "dir"
+ ],
+ "command": "curl -X GET http://localhost:3000/api/darmasaba/ls/{dir} \\\n -H 'Content-Type: application/json' -d '{\"dir\":\"dir\"}'"
+ },
+ "getApiDarmasabaFileByDirByFile_name": {
+ "method": "GET",
+ "path": "/api/darmasaba/file/{dir}/{file_name}",
+ "summary": "/file/:dir/:file_name",
+ "parameters": {
+ "dir": "string",
+ "file_name": "string"
+ },
+ "required": [
+ "dir",
+ "file_name"
+ ],
+ "command": "curl -X GET http://localhost:3000/api/darmasaba/file/{dir}/{file_name} \\\n -H 'Content-Type: application/json' -d '{\"dir\":\"dir\",\"file_name\":\"file_name\"}'"
+ }
+ },
+ "default": {
+ "getApiUserFind": {
+ "method": "GET",
+ "path": "/api/user/find",
+ "summary": "",
+ "command": "curl -X GET http://localhost:3000/api/user/find"
+ },
+ "postApiCredentialCreate": {
+ "method": "POST",
+ "path": "/api/credential/create",
+ "summary": "",
+ "parameters": {
+ "name": "string",
+ "value": "string"
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "command": "curl -X POST http://localhost:3000/api/credential/create \\\n -H 'Content-Type: application/json' -d '{\"name\":\"name\",\"value\":\"value\"}'"
+ },
+ "getApiCredentialList": {
+ "method": "GET",
+ "path": "/api/credential/list",
+ "summary": "",
+ "command": "curl -X GET http://localhost:3000/api/credential/list"
+ },
+ "deleteApiCredentialRm": {
+ "method": "DELETE",
+ "path": "/api/credential/rm",
+ "summary": "",
+ "parameters": {
+ "id": "string"
+ },
+ "required": [
+ "id"
+ ],
+ "command": "curl -X DELETE http://localhost:3000/api/credential/rm \\\n -H 'Content-Type: application/json' -d '{\"id\":\"id\"}'"
+ }
+ },
+ "auth": {
+ "postAuthLogin": {
+ "method": "POST",
+ "path": "/auth/login",
+ "summary": "login",
+ "parameters": {
+ "email": "string",
+ "password": "string"
+ },
+ "required": [
+ "email",
+ "password"
+ ],
+ "command": "curl -X POST http://localhost:3000/auth/login \\\n -H 'Content-Type: application/json' -d '{\"email\":\"email\",\"password\":\"password\"}'"
+ },
+ "deleteAuthLogout": {
+ "method": "DELETE",
+ "path": "/auth/logout",
+ "summary": "logout",
+ "command": "curl -X DELETE http://localhost:3000/auth/logout"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/bun.lock b/bun.lock
index 271afc5..8b87bab 100644
--- a/bun.lock
+++ b/bun.lock
@@ -21,6 +21,7 @@
"react-dom": "^19",
"react-router-dom": "^7.9.3",
"swr": "^2.3.6",
+ "valtio": "^2.1.8",
},
"devDependencies": {
"@types/bun": "latest",
@@ -212,6 +213,8 @@
"prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
+ "proxy-compare": ["proxy-compare@3.0.1", "", {}, "sha512-V9plBAt3qjMlS1+nC8771KNf6oJ12gExvaxnNzN/9yVRLdTv/lc+oJlnSzrdYDAvBfTStPCoiaCOTmTs0adv7Q=="],
+
"pure-rand": ["pure-rand@6.1.0", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="],
"rc9": ["rc9@2.1.2", "", { "dependencies": { "defu": "^6.1.4", "destr": "^2.0.3" } }, "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg=="],
@@ -276,6 +279,8 @@
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
+ "valtio": ["valtio@2.1.8", "", { "dependencies": { "proxy-compare": "^3.0.1" }, "peerDependencies": { "@types/react": ">=18.0.0", "react": ">=18.0.0" }, "optionalPeers": ["@types/react", "react"] }, "sha512-fjTPbJyKEmfVBZUOh3V0OtMHoFUGr4+4XpejjxhNJE/IS2l8rDbyJuzi3w/fZWBDyk7BJOpG+lmvTK5iiVhXuQ=="],
+
"zhead": ["zhead@2.2.4", "", {}, "sha512-8F0OI5dpWIA5IGG5NHUg9staDwz/ZPxZtvGVf01j7vHqSyZ0raHY+78atOVxRqb73AotX22uV1pXt3gYSstGag=="],
"zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
diff --git a/mcp.json b/mcp.json
new file mode 100644
index 0000000..d6ff80b
--- /dev/null
+++ b/mcp.json
@@ -0,0 +1,72 @@
+{
+ "name": "Elysia Documentation",
+ "description": "Development documentation",
+ "version": "0.0.0",
+ "capabilities": {
+ "postapiapikeycreate": {
+ "enabled": true,
+ "command": "curl -X POST http://localhost:3000/api/apikey/create",
+ "description": "create api key"
+ },
+ "getapiapikeylist": {
+ "enabled": true,
+ "command": "curl -X GET http://localhost:3000/api/apikey/list",
+ "description": "get api key list"
+ },
+ "deleteapiapikeydelete": {
+ "enabled": true,
+ "command": "curl -X DELETE http://localhost:3000/api/apikey/delete",
+ "description": "delete api key"
+ },
+ "getapidarmasabarepos": {
+ "enabled": true,
+ "command": "curl -X GET http://localhost:3000/api/darmasaba/repos",
+ "description": "/repos"
+ },
+ "getapidarmasabals": {
+ "enabled": true,
+ "command": "curl -X GET http://localhost:3000/api/darmasaba/ls",
+ "description": "/ls"
+ },
+ "getapidarmasabalsbydir": {
+ "enabled": true,
+ "command": "curl -X GET http://localhost:3000/api/darmasaba/ls/{dir}",
+ "description": "/ls/:dir"
+ },
+ "getapidarmasabafilebydirbyfile_name": {
+ "enabled": true,
+ "command": "curl -X GET http://localhost:3000/api/darmasaba/file/{dir}/{file_name}",
+ "description": "/file/:dir/:file_name"
+ },
+ "getapiuserfind": {
+ "enabled": true,
+ "command": "curl -X GET http://localhost:3000/api/user/find",
+ "description": "GET /api/user/find"
+ },
+ "postapicredentialcreate": {
+ "enabled": true,
+ "command": "curl -X POST http://localhost:3000/api/credential/create",
+ "description": "POST /api/credential/create"
+ },
+ "getapicredentiallist": {
+ "enabled": true,
+ "command": "curl -X GET http://localhost:3000/api/credential/list",
+ "description": "GET /api/credential/list"
+ },
+ "deleteapicredentialrm": {
+ "enabled": true,
+ "command": "curl -X DELETE http://localhost:3000/api/credential/rm",
+ "description": "DELETE /api/credential/rm"
+ },
+ "postauthlogin": {
+ "enabled": true,
+ "command": "curl -X POST http://localhost:3000/auth/login",
+ "description": "login"
+ },
+ "deleteauthlogout": {
+ "enabled": true,
+ "command": "curl -X DELETE http://localhost:3000/auth/logout",
+ "description": "logout"
+ }
+ }
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index 8c24816..f32686a 100644
--- a/package.json
+++ b/package.json
@@ -26,7 +26,8 @@
"react": "^19",
"react-dom": "^19",
"react-router-dom": "^7.9.3",
- "swr": "^2.3.6"
+ "swr": "^2.3.6",
+ "valtio": "^2.1.8"
},
"devDependencies": {
"@types/bun": "latest",
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 8540516..53bfd2b 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -29,3 +29,11 @@ model ApiKey {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
+
+model Credential {
+ id String @id @default(cuid())
+ name String?
+ value String?
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+}
diff --git a/src/AppRoutes.tsx b/src/AppRoutes.tsx
index 45bc1f4..6f5bf04 100644
--- a/src/AppRoutes.tsx
+++ b/src/AppRoutes.tsx
@@ -7,6 +7,7 @@ import ProtectedRoute from "./components/ProtectedRoute";
import Dashboard from "./pages/dashboard/dashboard_page";
import DashboardLayout from "./pages/dashboard/dashboard_layout";
import ApiKeyPage from "./pages/dashboard/apikey/apikey_page";
+import CredentialPage from "./pages/dashboard/credential/credential_page";
export default function AppRoutes() {
return (
@@ -19,6 +20,7 @@ export default function AppRoutes() {
} />
} />
} />
+ } />
diff --git a/src/clientRoutes.ts b/src/clientRoutes.ts
index cf19abe..147a844 100644
--- a/src/clientRoutes.ts
+++ b/src/clientRoutes.ts
@@ -5,6 +5,7 @@ const clientRoutes = {
"/dashboard": "/dashboard",
"/dashboard/landing": "/dashboard/landing",
"/dashboard/apikey": "/dashboard/apikey",
+ "/dashboard/credential": "/dashboard/credential",
"/*": "/*"
} as const;
diff --git a/src/index.tsx b/src/index.tsx
index 2909cec..d22d428 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -1,12 +1,14 @@
-import Elysia, { t } from "elysia";
import Swagger from "@elysiajs/swagger";
-import html from "./index.html"
-import Dashboard from "./server/routes/darmasaba";
-import apiAuth from "./server/middlewares/apiAuth";
-import Auth from "./server/routes/auth_route";
-import ApiKeyRoute from "./server/routes/apikey_route";
+import Elysia from "elysia";
import type { User } from "generated/prisma";
+import html from "./index.html";
+import apiAuth from "./server/middlewares/apiAuth";
+import ApiKeyRoute from "./server/routes/apikey_route";
+import Auth from "./server/routes/auth_route";
+import CredentialRoute from "./server/routes/credential_route";
+import DarmasabaRoute from "./server/routes/darmasaba_route";
+import { convertOpenApiToMcp } from "./server/lib/mcp-converter";
const Docs = new Elysia()
.use(Swagger({
@@ -28,13 +30,23 @@ const Api = new Elysia({
})
.use(apiAuth)
.use(ApiKeyRoute)
- .use(Dashboard)
+ .use(DarmasabaRoute)
.use(ApiUser)
+ .use(CredentialRoute)
const app = new Elysia()
.use(Api)
.use(Docs)
.use(Auth)
+ .get("/.well-known/mcp.json", async () => {
+ const baseUrl = process.env.BUN_PUBLIC_BASE_URL!
+ return await convertOpenApiToMcp(baseUrl)
+ }, {
+ detail: {
+ description: "MCP manifest",
+ tags: ["MCP"],
+ }
+ })
.get("*", html)
.listen(3000, () => {
console.log("Server running at http://localhost:3000");
diff --git a/src/pages/dashboard/credential/credential_page.tsx b/src/pages/dashboard/credential/credential_page.tsx
new file mode 100644
index 0000000..a3670cb
--- /dev/null
+++ b/src/pages/dashboard/credential/credential_page.tsx
@@ -0,0 +1,89 @@
+import apiFetch from "@/lib/apiFetch";
+import { Button, Card, Container, Flex, Group, Paper, Stack, Text, TextInput, Title } from "@mantine/core";
+import { useShallowEffect } from "@mantine/hooks";
+import { showNotification } from "@mantine/notifications";
+import { useState } from "react";
+import useSwr from 'swr'
+import { proxy, subscribe, useSnapshot } from 'valtio'
+
+const state = proxy({
+ reload: ""
+})
+
+function reloadState() {
+ state.reload = Math.random().toString()
+}
+
+export default function CredentialPage() {
+ return
+
+
+
+
+
+}
+
+function CredentialCreate() {
+ const [name, setName] = useState("")
+ const [apikey, setApikey] = useState("")
+
+ async function handleSubmit() {
+ const { data } = await apiFetch.api.credential.create.post({
+ name: name,
+ value: apikey
+ })
+
+ setName("")
+ setApikey("")
+
+ showNotification({
+ message: data?.message
+ })
+
+ reloadState()
+ }
+ return
+
+ Credential Create
+ setName(e.target.value)} />
+ setApikey(e.target.value)} />
+
+
+
+
+
+}
+
+function CredentialList() {
+ const { data, mutate } = useSwr("/", () => apiFetch.api.credential.list.get())
+
+ useShallowEffect(() => {
+ const unsubscribe = subscribe(state, async () => {
+ console.log('state has changed to', state)
+ mutate()
+ })
+
+ return () => unsubscribe()
+ }, [])
+
+ async function handleRm(id: string) {
+ await apiFetch.api.credential.rm.delete({
+ id: id
+ })
+
+ reloadState()
+
+ }
+ return
+
+ {data?.data?.list.map((v, k) =>
+
+ {v.name}
+
+
+
+
+ )}
+
+
+}
\ No newline at end of file
diff --git a/src/pages/dashboard/dashboard_layout.tsx b/src/pages/dashboard/dashboard_layout.tsx
index fa6b626..5f7a1db 100644
--- a/src/pages/dashboard/dashboard_layout.tsx
+++ b/src/pages/dashboard/dashboard_layout.tsx
@@ -21,7 +21,9 @@ import { useLocalStorage } from '@mantine/hooks'
import {
IconChevronLeft,
IconChevronRight,
- IconDashboard
+ IconDashboard,
+ IconKey,
+ IconLock
} from '@tabler/icons-react'
import type { User } from 'generated/prisma'
import { Outlet, useLocation, useNavigate } from 'react-router-dom'
@@ -170,11 +172,18 @@ function NavigationDashboard() {
/>
}
+ leftSection={}
label="Dashboard Overview"
description="Quick summary and activity highlights"
onClick={() => navigate(clientRoutes['/dashboard/apikey'])}
/>
+ }
+ label="Dashboard Overview"
+ description="Quick summary and activity highlights"
+ onClick={() => navigate(clientRoutes['/dashboard/credential'])}
+ />
)
}
diff --git a/src/server/lib/mcp-converter.ts b/src/server/lib/mcp-converter.ts
new file mode 100644
index 0000000..ebeff1d
--- /dev/null
+++ b/src/server/lib/mcp-converter.ts
@@ -0,0 +1,133 @@
+/**
+ * src/utils/swagger-to-mcp.ts
+ *
+ * Auto-converter: Swagger (OpenAPI) → MCP manifest (real-time)
+ *
+ * - Fetch swagger JSON dynamically from process.env.BUN_PUBLIC_BASE_URL + "/docs/json"
+ * - Generate MCP manifest for AI discovery (/.well-known/mcp.json)
+ * - Can be used as Bun CLI or integrated in Elysia route
+ */
+
+import { writeFileSync } from "fs"
+
+interface OpenAPI {
+ info: { title?: string; description?: string; version?: string }
+ paths: Record
+}
+
+interface McpManifest {
+ schema_version: string
+ name: string
+ description: string
+ version?: string
+ endpoints: Record
+ capabilities: Record
+ contact?: { email?: string }
+}
+
+/**
+ * Convert OpenAPI JSON to MCP manifest format
+ */
+export async function convertOpenApiToMcp(baseUrl: string): Promise {
+ const res = await fetch(`${baseUrl}/docs/json`)
+ if (!res.ok) throw new Error(`Failed to fetch Swagger JSON from ${baseUrl}/docs/json`)
+
+ const openapi: OpenAPI = await res.json()
+
+ const manifest: McpManifest = {
+ schema_version: "1.0",
+ name: openapi.info?.title ?? "MCP Server",
+ description: openapi.info?.description ?? "Auto-generated MCP manifest from Swagger",
+ version: openapi.info?.version ?? "0.0.0",
+ endpoints: {
+ openapi: `${baseUrl}/docs/json`,
+ mcp: `${baseUrl}/.well-known/mcp.json`
+ },
+ capabilities: {}
+ }
+
+ for (const [path, methods] of Object.entries(openapi.paths || {})) {
+ for (const [method, def] of Object.entries(methods)) {
+ const tags = def.tags || ["default"]
+ const tag = tags[0]
+ const operationId = def.operationId || `${method}_${path.replace(/[\/{}]/g, "_")}`
+
+ manifest.capabilities[tag] ??= {}
+
+ // Extract parameters and body schema
+ const params: Record = {}
+ const required: string[] = []
+
+ if (Array.isArray(def.parameters)) {
+ for (const p of def.parameters) {
+ const type = p.schema?.type || "string"
+ params[p.name] = type
+ if (p.required) required.push(p.name)
+ }
+ }
+
+ const bodySchema = def.requestBody?.content?.["application/json"]?.schema
+ if (bodySchema?.properties) {
+ for (const [key, prop] of Object.entries(bodySchema.properties)) {
+ params[key] = prop.type || "string"
+ }
+ if (Array.isArray(bodySchema.required))
+ required.push(...bodySchema.required)
+ }
+
+ // Generate example cURL
+ const sampleCurl = [
+ `curl -X ${method.toUpperCase()} ${baseUrl}${path}`,
+ Object.keys(params).length > 0
+ ? ` -H 'Content-Type: application/json' -d '${JSON.stringify(
+ Object.fromEntries(Object.keys(params).map(k => [k, params[k] === "string" ? k : "value"]))
+ )}'`
+ : ""
+ ]
+ .filter(Boolean)
+ .join(" \\\n")
+
+ manifest.capabilities[tag][operationId] = {
+ method: method.toUpperCase(),
+ path,
+ summary: def.summary || def.description || "",
+ parameters: Object.keys(params).length > 0 ? params : undefined,
+ required: required.length > 0 ? required : undefined,
+ command: sampleCurl
+ }
+ }
+ }
+
+ return manifest
+}
+
+/**
+ * CLI entry
+ * bun run src/utils/swagger-to-mcp.ts
+ */
+if (import.meta.main) {
+ const baseUrl = process.env.BUN_PUBLIC_BASE_URL
+ if (!baseUrl) {
+ console.error("❌ Missing BUN_PUBLIC_BASE_URL environment variable.")
+ process.exit(1)
+ }
+
+ convertOpenApiToMcp(baseUrl)
+ .then(manifest => {
+ writeFileSync(".well-known/mcp.json", JSON.stringify(manifest, null, 2))
+ console.log("✅ Generated .well-known/mcp.json")
+ })
+ .catch(err => console.error("❌ Failed to convert Swagger → MCP:", err))
+}
+
+/**
+ * Optional: Elysia integration
+ * Automatically serve /.well-known/mcp.json
+ */
+// import Elysia from "elysia"
+// new Elysia()
+// .get("/.well-known/mcp.json", async () => {
+// const baseUrl = process.env.BUN_PUBLIC_BASE_URL!
+// return await convertOpenApiToMcp(baseUrl)
+// })
+// .listen(3000)
diff --git a/src/server/routes/credential_route.ts b/src/server/routes/credential_route.ts
new file mode 100644
index 0000000..9b9a5e6
--- /dev/null
+++ b/src/server/routes/credential_route.ts
@@ -0,0 +1,46 @@
+import Elysia, { t } from "elysia";
+import { prisma } from "../lib/prisma";
+
+const CredentialRoute = new Elysia({
+ prefix: "/credential"
+})
+ .post("/create", async (ctx) => {
+ const { name, value } = ctx.body
+ const create = await prisma.credential.create({
+ data: {
+ name,
+ value
+ }
+ })
+ return {
+ message: "success",
+ create
+ }
+ }, {
+ body: t.Object({
+ name: t.String(),
+ value: t.String(),
+ })
+ })
+ .get("/list", async (ctx) => {
+ const list = await prisma.credential.findMany()
+ return {
+ message: "success",
+ list
+ }
+ })
+ .delete("/rm", async (ctx) => {
+ const { id } = ctx.body
+ const rm = await prisma.credential.delete({
+ where: {
+ id: id
+ }
+ })
+
+ }, {
+ body: t.Object({
+ id: t.String()
+ })
+ })
+
+export default CredentialRoute
\ No newline at end of file
diff --git a/src/server/routes/darmasaba.ts b/src/server/routes/darmasaba.ts
deleted file mode 100644
index 3d483eb..0000000
--- a/src/server/routes/darmasaba.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import Elysia from "elysia";
-
-const Dashboard = new Elysia({
- prefix: "/dashboard"
-})
- .get("/apa", () => "Hello World")
-
-export default Dashboard
diff --git a/src/server/routes/darmasaba_route.ts b/src/server/routes/darmasaba_route.ts
new file mode 100644
index 0000000..6b2ec78
--- /dev/null
+++ b/src/server/routes/darmasaba_route.ts
@@ -0,0 +1,131 @@
+import Elysia, { t } from "elysia";
+
+const url = "https://cld-dkr-makuro-seafile.wibudev.com/api2"
+const TOKEN = "fa49bf1774cad2ec89d2882ae2c6ac1f5d7df445"
+
+const DarmasabaRoute = new Elysia({
+ prefix: "/darmasaba",
+ tags: ["darmasaba"]
+})
+ .get("/repos", async () => {
+ const res = await fetch(url + "/repos", {
+ headers: {
+ Authorization: "Bearer " + TOKEN
+ }
+ })
+
+ if (!res.ok) {
+ console.log(res)
+ return {
+ message: "Failed to fetch directory"
+ }
+ }
+ const data = await res.json() as { name: string, id: string, type: string }[]
+ return data.map((v) => {
+ return {
+ name: v.name,
+ id: v.id,
+ type: v.type
+ }
+ })
+ }, {
+ detail: {
+ summary: "/repos",
+ description: "get list of repositories"
+ }
+ })
+ .get("/ls", async () => {
+ const res = await fetch(url + `/repos/de64ff3c-0081-45f3-a5a6-6c799a098649/dir/?p=${encodeURIComponent('darmasaba')}`, {
+ headers: {
+ Authorization: "Bearer " + TOKEN
+ }
+ })
+
+ if (!res.ok) {
+ console.log(res)
+ return {
+ message: "Failed to fetch directory"
+ }
+ }
+ const data = await res.json() as { name: string, id: string, type: string }[]
+ return data.map((v) => {
+ return {
+ name: v.name,
+ id: v.id,
+ type: v.type
+ }
+ })
+ }, {
+ detail: {
+ summary: "/ls",
+ description: "get list of dir in darmasaba"
+ }
+ })
+ .get("/ls/:dir", async ({ params }) => {
+ const { dir } = params
+ const res = await fetch(url + `/repos/de64ff3c-0081-45f3-a5a6-6c799a098649/dir/?p=${encodeURIComponent('darmasaba/' + dir)}`, {
+ headers: {
+ Authorization: "Bearer " + TOKEN
+ }
+ })
+
+ if (!res.ok) {
+ console.log(res)
+ return {
+ message: "Failed to fetch directory"
+ }
+ }
+ const data = await res.json() as { name: string, id: string, type: string }[]
+ return data.map((v) => {
+ return {
+ name: v.name,
+ id: v.id,
+ type: v.type
+ }
+ })
+ }, {
+ params: t.Object({
+ dir: t.String()
+ }),
+ detail: {
+ summary: "/ls/:dir",
+ description: "get list of files in darmasaba/"
+ }
+ })
+ .get("/file/:dir/:file_name", async ({ params }) => {
+ const { dir, file_name } = params
+ const res = await fetch(url + `/repos/de64ff3c-0081-45f3-a5a6-6c799a098649/file/?p=${encodeURIComponent('darmasaba/' + dir + '/' + file_name)}`, {
+ headers: {
+ Authorization: "Bearer " + TOKEN
+ }
+ })
+
+ if (!res.ok) {
+ console.log(res)
+ return {
+ message: "Failed to fetch directory"
+ }
+ }
+
+ const downloadUrl = (await res.text()).replace(/"/g, '');
+
+ const resText = await fetch(downloadUrl, {
+ headers: {
+ Authorization: "Bearer " + TOKEN
+ }
+ })
+
+ return resText.text()
+ }, {
+ params: t.Object({
+ dir: t.String(),
+ file_name: t.String()
+ }),
+ detail: {
+ summary: "/file/:dir/:file_name",
+ description: "get content of file in darmasaba//"
+ }
+ })
+
+
+export default DarmasabaRoute
diff --git a/x.ts b/x.ts
new file mode 100644
index 0000000..787484d
--- /dev/null
+++ b/x.ts
@@ -0,0 +1,133 @@
+/**
+ * src/utils/swagger-to-mcp.ts
+ *
+ * Auto-converter: Swagger (OpenAPI) → MCP manifest (real-time)
+ *
+ * - Fetch swagger JSON dynamically from process.env.BUN_PUBLIC_BASE_URL + "/docs/json"
+ * - Generate MCP manifest for AI discovery (/.well-known/mcp.json)
+ * - Can be used as Bun CLI or integrated in Elysia route
+ */
+
+import { writeFileSync } from "fs"
+
+interface OpenAPI {
+ info: { title?: string; description?: string; version?: string }
+ paths: Record
+}
+
+interface McpManifest {
+ schema_version: string
+ name: string
+ description: string
+ version?: string
+ endpoints: Record
+ capabilities: Record
+ contact?: { email?: string }
+}
+
+/**
+ * Convert OpenAPI JSON to MCP manifest format
+ */
+async function convertOpenApiToMcp(baseUrl: string): Promise {
+ const res = await fetch(`${baseUrl}/docs/json`)
+ if (!res.ok) throw new Error(`Failed to fetch Swagger JSON from ${baseUrl}/docs/json`)
+
+ const openapi: OpenAPI = await res.json()
+
+ const manifest: McpManifest = {
+ schema_version: "1.0",
+ name: openapi.info?.title ?? "MCP Server",
+ description: openapi.info?.description ?? "Auto-generated MCP manifest from Swagger",
+ version: openapi.info?.version ?? "0.0.0",
+ endpoints: {
+ openapi: `${baseUrl}/docs/json`,
+ mcp: `${baseUrl}/.well-known/mcp.json`
+ },
+ capabilities: {}
+ }
+
+ for (const [path, methods] of Object.entries(openapi.paths || {})) {
+ for (const [method, def] of Object.entries(methods)) {
+ const tags = def.tags || ["default"]
+ const tag = tags[0]
+ const operationId = def.operationId || `${method}_${path.replace(/[\/{}]/g, "_")}`
+
+ manifest.capabilities[tag] ??= {}
+
+ // Extract parameters and body schema
+ const params: Record = {}
+ const required: string[] = []
+
+ if (Array.isArray(def.parameters)) {
+ for (const p of def.parameters) {
+ const type = p.schema?.type || "string"
+ params[p.name] = type
+ if (p.required) required.push(p.name)
+ }
+ }
+
+ const bodySchema = def.requestBody?.content?.["application/json"]?.schema
+ if (bodySchema?.properties) {
+ for (const [key, prop] of Object.entries(bodySchema.properties)) {
+ params[key] = prop.type || "string"
+ }
+ if (Array.isArray(bodySchema.required))
+ required.push(...bodySchema.required)
+ }
+
+ // Generate example cURL
+ const sampleCurl = [
+ `curl -X ${method.toUpperCase()} ${baseUrl}${path}`,
+ Object.keys(params).length > 0
+ ? ` -H 'Content-Type: application/json' -d '${JSON.stringify(
+ Object.fromEntries(Object.keys(params).map(k => [k, params[k] === "string" ? k : "value"]))
+ )}'`
+ : ""
+ ]
+ .filter(Boolean)
+ .join(" \\\n")
+
+ manifest.capabilities[tag][operationId] = {
+ method: method.toUpperCase(),
+ path,
+ summary: def.summary || def.description || "",
+ parameters: Object.keys(params).length > 0 ? params : undefined,
+ required: required.length > 0 ? required : undefined,
+ command: sampleCurl
+ }
+ }
+ }
+
+ return manifest
+}
+
+/**
+ * CLI entry
+ * bun run src/utils/swagger-to-mcp.ts
+ */
+if (import.meta.main) {
+ const baseUrl = process.env.BUN_PUBLIC_BASE_URL
+ if (!baseUrl) {
+ console.error("❌ Missing BUN_PUBLIC_BASE_URL environment variable.")
+ process.exit(1)
+ }
+
+ convertOpenApiToMcp(baseUrl)
+ .then(manifest => {
+ writeFileSync(".well-known/mcp.json", JSON.stringify(manifest, null, 2))
+ console.log("✅ Generated .well-known/mcp.json")
+ })
+ .catch(err => console.error("❌ Failed to convert Swagger → MCP:", err))
+}
+
+/**
+ * Optional: Elysia integration
+ * Automatically serve /.well-known/mcp.json
+ */
+// import Elysia from "elysia"
+// new Elysia()
+// .get("/.well-known/mcp.json", async () => {
+// const baseUrl = process.env.BUN_PUBLIC_BASE_URL!
+// return await convertOpenApiToMcp(baseUrl)
+// })
+// .listen(3000)