godmode 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -0
- package/dist/{add-ZRNIX6DM.js → add-AJERMPEA.js} +11 -9
- package/dist/chunk-2J2VDCQ4.js +503 -0
- package/dist/{chunk-EHS56XXY.js → chunk-EXWYJU2I.js} +1 -1
- package/dist/{chunk-I4WS4MMI.js → chunk-GJCJO7YT.js} +3 -3
- package/dist/index.js +1424 -275
- package/dist/{prompt-DCFFOQ7K.js → prompt-SWOWWAOH.js} +1 -1
- package/dist/{mcp-server.js → server-4YVOAJJG.js} +3 -3
- package/package.json +27 -9
- package/dist/chunk-FBMQ3AC4.js +0 -339
- package/dist/mcp-NZIEMQOM.js +0 -58
|
@@ -89,7 +89,7 @@ async function configWizard() {
|
|
|
89
89
|
writeFileSync(filepath, yaml);
|
|
90
90
|
console.log(`
|
|
91
91
|
Saved ${filepath}`);
|
|
92
|
-
console.log(`Run \x1B[1mgodmode add ${response.name.toLowerCase()}\x1B[0m to register it.`);
|
|
92
|
+
console.log(`Run \x1B[1mgodmode extension add ${response.name.toLowerCase()}\x1B[0m to register it.`);
|
|
93
93
|
}
|
|
94
94
|
export {
|
|
95
95
|
configWizard
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
executeToString
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-GJCJO7YT.js";
|
|
5
5
|
import {
|
|
6
6
|
executeMcpTool
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-EXWYJU2I.js";
|
|
8
8
|
|
|
9
|
-
// src/
|
|
9
|
+
// ../../interfaces/mcp/src/server.ts
|
|
10
10
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
11
11
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
12
12
|
import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
package/package.json
CHANGED
|
@@ -1,15 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "godmode",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "any API as CLI. any API as MCP. zero code generation.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Tomas Sivicki",
|
|
8
|
+
"homepage": "https://godmode.so",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/tomsiwik/godmode",
|
|
12
|
+
"directory": "packages/cli"
|
|
13
|
+
},
|
|
14
|
+
"bugs": "https://github.com/tomsiwik/godmode/issues",
|
|
15
|
+
"keywords": [
|
|
16
|
+
"cli",
|
|
17
|
+
"api",
|
|
18
|
+
"mcp",
|
|
19
|
+
"openapi",
|
|
20
|
+
"graphql",
|
|
21
|
+
"claude-code"
|
|
22
|
+
],
|
|
8
23
|
"bin": {
|
|
9
24
|
"godmode": "./dist/index.js"
|
|
10
25
|
},
|
|
11
26
|
"exports": {
|
|
12
|
-
"./
|
|
27
|
+
"./spec": "./src/spec.ts",
|
|
28
|
+
"./config": "./src/config.ts",
|
|
29
|
+
"./help": "./src/help.ts"
|
|
13
30
|
},
|
|
14
31
|
"files": [
|
|
15
32
|
"dist",
|
|
@@ -21,10 +38,14 @@
|
|
|
21
38
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
22
39
|
"fuzzysort": "^3.1.0",
|
|
23
40
|
"prompts": "^2.4.2",
|
|
24
|
-
"yaml": "^2.8.0"
|
|
41
|
+
"yaml": "^2.8.0",
|
|
42
|
+
"@godmode-cli/cli": "0.0.1",
|
|
43
|
+
"@godmode-cli/command-agent": "0.0.1",
|
|
44
|
+
"@godmode-cli/interface-graphql": "0.0.1",
|
|
45
|
+
"@godmode-cli/interface-api": "0.0.1",
|
|
46
|
+
"@godmode-cli/interface-mcp": "0.0.1"
|
|
25
47
|
},
|
|
26
48
|
"devDependencies": {
|
|
27
|
-
"@changesets/cli": "^2.30.0",
|
|
28
49
|
"@types/node": "^22.0.0",
|
|
29
50
|
"@types/prompts": "^2.4.9",
|
|
30
51
|
"tsup": "^8.0.0",
|
|
@@ -38,9 +59,6 @@
|
|
|
38
59
|
"build": "tsup",
|
|
39
60
|
"dev": "tsup --watch",
|
|
40
61
|
"start": "node dist/index.js",
|
|
41
|
-
"test": "vitest run"
|
|
42
|
-
"changeset": "changeset",
|
|
43
|
-
"version": "changeset version",
|
|
44
|
-
"release": "pnpm build && changeset publish"
|
|
62
|
+
"test": "vitest run"
|
|
45
63
|
}
|
|
46
64
|
}
|
package/dist/chunk-FBMQ3AC4.js
DELETED
|
@@ -1,339 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
parseMcp
|
|
4
|
-
} from "./chunk-EHS56XXY.js";
|
|
5
|
-
|
|
6
|
-
// src/protocols/graphql.ts
|
|
7
|
-
var INTROSPECTION_QUERY = `{
|
|
8
|
-
__schema {
|
|
9
|
-
queryType { name }
|
|
10
|
-
mutationType { name }
|
|
11
|
-
subscriptionType { name }
|
|
12
|
-
types {
|
|
13
|
-
name kind description
|
|
14
|
-
fields { name description args { name type { name kind ofType { name kind } } } }
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}`;
|
|
18
|
-
async function introspect(url, headers) {
|
|
19
|
-
const res = await fetch(url, {
|
|
20
|
-
method: "POST",
|
|
21
|
-
headers: { "Content-Type": "application/json", ...headers },
|
|
22
|
-
body: JSON.stringify({ query: INTROSPECTION_QUERY })
|
|
23
|
-
});
|
|
24
|
-
if (!res.ok) throw new Error(`Introspection failed: ${res.status} ${res.statusText}`);
|
|
25
|
-
const json = await res.json();
|
|
26
|
-
return json.data?.__schema?.types?.filter((t) => !t.name.startsWith("__")) || [];
|
|
27
|
-
}
|
|
28
|
-
function parseSdl(text) {
|
|
29
|
-
const types = [];
|
|
30
|
-
const typeRegex = /type\s+(\w+)\s*\{([^}]*)}/g;
|
|
31
|
-
let match;
|
|
32
|
-
while ((match = typeRegex.exec(text)) !== null) {
|
|
33
|
-
const [, typeName, body] = match;
|
|
34
|
-
const fields = [];
|
|
35
|
-
const fieldRegex = /(\w+)(?:\(([^)]*)\))?\s*:\s*\S+/g;
|
|
36
|
-
let fm;
|
|
37
|
-
while ((fm = fieldRegex.exec(body)) !== null) {
|
|
38
|
-
const args = [];
|
|
39
|
-
if (fm[2]) {
|
|
40
|
-
const argRegex = /(\w+)\s*:\s*(\w+)/g;
|
|
41
|
-
let am;
|
|
42
|
-
while ((am = argRegex.exec(fm[2])) !== null) {
|
|
43
|
-
args.push({ name: am[1], type: { name: am[2], kind: "SCALAR" } });
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
fields.push({ name: fm[1], args: args.length ? args : void 0 });
|
|
47
|
-
}
|
|
48
|
-
types.push({ name: typeName, kind: "OBJECT", fields });
|
|
49
|
-
}
|
|
50
|
-
return types;
|
|
51
|
-
}
|
|
52
|
-
async function parseGraphQL(name, config) {
|
|
53
|
-
let types;
|
|
54
|
-
if (config.spec) {
|
|
55
|
-
process.stderr.write(`Fetching ${config.spec}...
|
|
56
|
-
`);
|
|
57
|
-
const res = await fetch(config.spec);
|
|
58
|
-
if (!res.ok) throw new Error(`Failed to fetch spec: ${res.status}`);
|
|
59
|
-
types = parseSdl(await res.text());
|
|
60
|
-
} else {
|
|
61
|
-
if (!config.url) throw new Error('GraphQL config needs either "spec" or "url"');
|
|
62
|
-
process.stderr.write(`Introspecting ${config.url}...
|
|
63
|
-
`);
|
|
64
|
-
const headers = { ...config.headers };
|
|
65
|
-
const token = config.auth?.env ? process.env[config.auth.env] : void 0;
|
|
66
|
-
if (token) {
|
|
67
|
-
const authType = config.auth?.type || "bearer";
|
|
68
|
-
if (authType === "bearer") headers["Authorization"] = `Bearer ${token}`;
|
|
69
|
-
else if (authType === "api-key") headers[config.auth?.header || "X-API-Key"] = token;
|
|
70
|
-
}
|
|
71
|
-
types = await introspect(config.url, headers);
|
|
72
|
-
}
|
|
73
|
-
const routes = [];
|
|
74
|
-
for (const type of types) {
|
|
75
|
-
if (!type.fields) continue;
|
|
76
|
-
const isQuery = type.name === "Query" || type.name === "RootQuery";
|
|
77
|
-
const isMutation = type.name === "Mutation" || type.name === "RootMutation";
|
|
78
|
-
if (!isQuery && !isMutation) continue;
|
|
79
|
-
const method = isMutation ? "post" : "get";
|
|
80
|
-
for (const field of type.fields) {
|
|
81
|
-
routes.push({
|
|
82
|
-
path: field.name,
|
|
83
|
-
method,
|
|
84
|
-
summary: field.description || "",
|
|
85
|
-
version: "",
|
|
86
|
-
segments: [{ value: field.name, isParam: false }]
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
routes.sort((a, b) => a.path.localeCompare(b.path));
|
|
91
|
-
process.stderr.write(`Parsed ${routes.length} fields (${types.length} types)
|
|
92
|
-
`);
|
|
93
|
-
return { name, description: config.description || "", specVersion: "", config, versions: [], resourceDescriptions: {}, routes };
|
|
94
|
-
}
|
|
95
|
-
function validateGraphQLFlags(method, query, body, apiName) {
|
|
96
|
-
if (["put", "patch", "delete", "head"].includes(method)) {
|
|
97
|
-
return `GraphQL only supports POST. ${method.toUpperCase()} is not valid.`;
|
|
98
|
-
}
|
|
99
|
-
if (method === "post") {
|
|
100
|
-
process.stderr.write(`Note: POST is implicit for GraphQL, flag not needed.
|
|
101
|
-
`);
|
|
102
|
-
}
|
|
103
|
-
if (Object.keys(query).length || Object.keys(body).length) {
|
|
104
|
-
return `GraphQL uses document syntax, not key=value params.
|
|
105
|
-
godmode ${apiName} '{ field { subfield } }'`;
|
|
106
|
-
}
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// src/config.ts
|
|
111
|
-
import { resolve, basename } from "path";
|
|
112
|
-
import { homedir } from "os";
|
|
113
|
-
import { mkdir, readFile, writeFile, readdir, unlink, access } from "fs/promises";
|
|
114
|
-
import { execSync } from "child_process";
|
|
115
|
-
import { parse as parseYaml2 } from "yaml";
|
|
116
|
-
|
|
117
|
-
// src/protocols/api.ts
|
|
118
|
-
import { parse as parseYaml } from "yaml";
|
|
119
|
-
async function parseOpenApi(name, config) {
|
|
120
|
-
process.stderr.write(`Fetching ${config.spec}...
|
|
121
|
-
`);
|
|
122
|
-
const res = await fetch(config.spec);
|
|
123
|
-
if (!res.ok) throw new Error(`Failed to fetch spec: ${res.status} ${res.statusText}`);
|
|
124
|
-
const text = await res.text();
|
|
125
|
-
const isJson = text.trimStart().startsWith("{");
|
|
126
|
-
const spec = isJson ? JSON.parse(text) : parseYaml(text);
|
|
127
|
-
if (!spec.paths) throw new Error("No paths found - is this a valid OpenAPI document?");
|
|
128
|
-
if (!config.url) {
|
|
129
|
-
if (spec.servers?.[0]?.url) {
|
|
130
|
-
config.url = spec.servers[0].url;
|
|
131
|
-
} else if (spec.host) {
|
|
132
|
-
const scheme = spec.schemes?.[0] || "https";
|
|
133
|
-
config.url = `${scheme}://${spec.host}${spec.basePath || ""}`;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
const versions = resolveVersions(config, Object.keys(spec.paths));
|
|
137
|
-
const httpMethods = ["get", "post", "put", "patch", "delete", "head"];
|
|
138
|
-
const routes = [];
|
|
139
|
-
for (const [path, pathItem] of Object.entries(spec.paths)) {
|
|
140
|
-
for (const method of httpMethods) {
|
|
141
|
-
const op = pathItem[method];
|
|
142
|
-
if (!op) continue;
|
|
143
|
-
const ver = versions.find((v) => path.startsWith(v.prefix));
|
|
144
|
-
const version = ver?.name || "";
|
|
145
|
-
const stripped = ver ? path.slice(ver.prefix.length).replace(/^\//, "") : path.slice(1);
|
|
146
|
-
const rawSegments = stripped.split("/").filter(Boolean);
|
|
147
|
-
const segments = rawSegments.map((s) => {
|
|
148
|
-
const isParam = s.startsWith("{") && s.endsWith("}");
|
|
149
|
-
return { value: isParam ? s.slice(1, -1) : s, isParam };
|
|
150
|
-
});
|
|
151
|
-
const tag = op.tags?.[0] || "";
|
|
152
|
-
routes.push({ path, method, summary: op.summary || "", version, tag, segments });
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
routes.sort((a, b) => a.path.localeCompare(b.path) || a.method.localeCompare(b.method));
|
|
156
|
-
const resourceDescriptions = {};
|
|
157
|
-
if (spec.tags) {
|
|
158
|
-
const stripHtml = (s) => s.replace(/<[^>]+>/g, "").trim();
|
|
159
|
-
for (const t of spec.tags) {
|
|
160
|
-
if (t.name && t.description) {
|
|
161
|
-
const first = stripHtml(t.description).split(/\.\s/)[0];
|
|
162
|
-
resourceDescriptions[t.name] = first.endsWith(".") ? first : first + ".";
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
const description = config.description || spec.info?.description || "";
|
|
167
|
-
const specVersion = spec.info?.version || "";
|
|
168
|
-
const versionNames = versions.map((v) => v.name).join(", ");
|
|
169
|
-
process.stderr.write(`Parsed ${routes.length} routes (versions: ${versionNames || "none"})
|
|
170
|
-
`);
|
|
171
|
-
return { name, description, specVersion, config, versions, resourceDescriptions, routes };
|
|
172
|
-
}
|
|
173
|
-
function resolveVersions(config, paths) {
|
|
174
|
-
if (config.versions?.length) {
|
|
175
|
-
return config.versions.map((v) => ({
|
|
176
|
-
prefix: v.prefix.endsWith("/") ? v.prefix.slice(0, -1) : v.prefix,
|
|
177
|
-
name: v.name || v.prefix.replace(/^\//, "")
|
|
178
|
-
}));
|
|
179
|
-
}
|
|
180
|
-
if (config.prefix) {
|
|
181
|
-
const prefix = config.prefix.endsWith("/") ? config.prefix.slice(0, -1) : config.prefix;
|
|
182
|
-
return [{ prefix, name: prefix.replace(/^\//, "") }];
|
|
183
|
-
}
|
|
184
|
-
const versionPattern = /^(\/(?:api\/)?v\d+)/;
|
|
185
|
-
const detected = /* @__PURE__ */ new Map();
|
|
186
|
-
for (const p of paths) {
|
|
187
|
-
const match = p.match(versionPattern);
|
|
188
|
-
if (match) detected.set(match[1], (detected.get(match[1]) || 0) + 1);
|
|
189
|
-
}
|
|
190
|
-
if (detected.size) {
|
|
191
|
-
return [...detected.entries()].sort((a, b) => b[1] - a[1]).map(([prefix]) => ({ prefix, name: prefix.replace(/^\//, "") }));
|
|
192
|
-
}
|
|
193
|
-
return [{ prefix: "", name: "" }];
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// src/spec.ts
|
|
197
|
-
var parsers = {
|
|
198
|
-
api: parseOpenApi,
|
|
199
|
-
graphql: parseGraphQL,
|
|
200
|
-
mcp: parseMcp
|
|
201
|
-
};
|
|
202
|
-
async function parseSpec(name, config) {
|
|
203
|
-
const parser = parsers[config.type];
|
|
204
|
-
if (!parser) throw new Error(`Unknown type "${config.type}" - supported: ${Object.keys(parsers).join(", ")}`);
|
|
205
|
-
return parser(name, config);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// src/config.ts
|
|
209
|
-
var GODMODE_HOME = process.platform === "linux" && process.env.XDG_CONFIG_HOME ? resolve(process.env.XDG_CONFIG_HOME, "godmode") : resolve(homedir(), ".godmode");
|
|
210
|
-
var APIS_DIR = resolve(GODMODE_HOME, "apis");
|
|
211
|
-
async function ensureDirs() {
|
|
212
|
-
await mkdir(APIS_DIR, { recursive: true });
|
|
213
|
-
}
|
|
214
|
-
async function exists(path) {
|
|
215
|
-
try {
|
|
216
|
-
await access(path);
|
|
217
|
-
return true;
|
|
218
|
-
} catch {
|
|
219
|
-
return false;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
async function loadManifestFromDir(dir) {
|
|
223
|
-
for (const ext of [".yaml", ".yml", ".json"]) {
|
|
224
|
-
const filePath = resolve(dir, `manifest${ext}`);
|
|
225
|
-
if (await exists(filePath)) {
|
|
226
|
-
const text = await readFile(filePath, "utf-8");
|
|
227
|
-
const config = ext === ".json" ? JSON.parse(text) : parseYaml2(text);
|
|
228
|
-
return { config, ext };
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
return null;
|
|
232
|
-
}
|
|
233
|
-
async function resolveConfig(input) {
|
|
234
|
-
const asPath = resolve(process.cwd(), input);
|
|
235
|
-
const fromPath = await loadManifestFromDir(asPath);
|
|
236
|
-
if (fromPath) return { name: fromPath.config.slug || basename(asPath), config: fromPath.config, dir: asPath };
|
|
237
|
-
try {
|
|
238
|
-
const pkgDir = resolve(import.meta.dirname, "..", "..", "..", "adapters", input);
|
|
239
|
-
const fromPkg = await loadManifestFromDir(pkgDir);
|
|
240
|
-
if (fromPkg) return { name: fromPkg.config.slug || input, config: fromPkg.config, dir: pkgDir };
|
|
241
|
-
} catch {
|
|
242
|
-
}
|
|
243
|
-
throw new Error(`No config found: ${input}/manifest.yaml or @godmode-cli/${input}`);
|
|
244
|
-
}
|
|
245
|
-
async function addApi(input) {
|
|
246
|
-
await ensureDirs();
|
|
247
|
-
const resolved = await resolveConfig(input).catch(() => null);
|
|
248
|
-
if (resolved) {
|
|
249
|
-
const { name: name2, config } = resolved;
|
|
250
|
-
const type = config.type;
|
|
251
|
-
if (type && ["api", "graphql", "mcp"].includes(type)) {
|
|
252
|
-
if (type === "api" && !config.spec) throw new Error('Config missing "spec" (OpenAPI spec URL or path)');
|
|
253
|
-
if (type === "graphql" && !config.spec && !config.url) throw new Error('GraphQL config needs "spec" (SDL) or "url" (for introspection)');
|
|
254
|
-
if (type === "mcp" && !config.url) throw new Error('MCP config needs "url" (MCP endpoint)');
|
|
255
|
-
if (!config.url && type === "api") process.stderr.write('Warning: no "url" in config - will try to detect from spec\n');
|
|
256
|
-
const manifest = await parseSpec(name2, config);
|
|
257
|
-
await writeFile(resolve(APIS_DIR, `${name2}.json`), JSON.stringify(manifest, null, 2));
|
|
258
|
-
const urlNote = manifest.config.url ? ` at ${manifest.config.url}` : "";
|
|
259
|
-
process.stderr.write(`Registered "${name2}" - ${manifest.routes.length} routes${urlNote}
|
|
260
|
-
`);
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
const name = resolved?.name || input;
|
|
265
|
-
const installTarget = resolved?.dir && await exists(resolve(resolved.dir, "package.json")) ? resolved.dir : input.startsWith("@") ? input : `@godmode-cli/${input}`;
|
|
266
|
-
process.stderr.write(`Installing ${name}...
|
|
267
|
-
`);
|
|
268
|
-
try {
|
|
269
|
-
execSync(`npm install ${installTarget} --prefix ${GODMODE_HOME}`, { stdio: "pipe" });
|
|
270
|
-
} catch (e) {
|
|
271
|
-
throw new Error(`Failed to install ${name}: ${e.stderr?.toString().trim() || e.message}`);
|
|
272
|
-
}
|
|
273
|
-
const pkgName = `@godmode-cli/${name}`;
|
|
274
|
-
const mcpConfigPath = resolve(GODMODE_HOME, "node_modules", pkgName, ".mcp.json");
|
|
275
|
-
if (await exists(mcpConfigPath)) {
|
|
276
|
-
process.stderr.write(`Installed "${name}" (MCP server adapter)
|
|
277
|
-
`);
|
|
278
|
-
} else {
|
|
279
|
-
process.stderr.write(`Installed "${name}"
|
|
280
|
-
`);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
async function updateApi(name) {
|
|
284
|
-
await ensureDirs();
|
|
285
|
-
const manifest = await loadManifestFromDir(name);
|
|
286
|
-
process.stderr.write(`Updating "${name}"...
|
|
287
|
-
`);
|
|
288
|
-
const updated = await parseSpec(name, manifest.config);
|
|
289
|
-
await writeFile(resolve(APIS_DIR, `${name}.json`), JSON.stringify(updated, null, 2));
|
|
290
|
-
process.stderr.write(`Updated "${name}" - ${updated.routes.length} routes
|
|
291
|
-
`);
|
|
292
|
-
}
|
|
293
|
-
async function removeApi(name) {
|
|
294
|
-
try {
|
|
295
|
-
await unlink(resolve(APIS_DIR, `${name}.json`));
|
|
296
|
-
process.stderr.write(`Removed "${name}"
|
|
297
|
-
`);
|
|
298
|
-
} catch {
|
|
299
|
-
process.stderr.write(`API "${name}" not found
|
|
300
|
-
`);
|
|
301
|
-
process.exit(1);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
async function listApis() {
|
|
305
|
-
await ensureDirs();
|
|
306
|
-
const files = await readdir(APIS_DIR);
|
|
307
|
-
const apis = files.filter((f) => f.endsWith(".json"));
|
|
308
|
-
if (!apis.length) {
|
|
309
|
-
console.log("No APIs registered. Create a <name>.yaml config and run: godmode add <name>");
|
|
310
|
-
return;
|
|
311
|
-
}
|
|
312
|
-
for (const file of apis) {
|
|
313
|
-
const m = JSON.parse(await readFile(resolve(APIS_DIR, file), "utf-8"));
|
|
314
|
-
const ver = m.specVersion ? ` v${m.specVersion}` : "";
|
|
315
|
-
const desc = m.description ? ` ${m.description}` : "";
|
|
316
|
-
const url = m.config.url || "(local)";
|
|
317
|
-
console.log(` ${m.name}${ver} ${url} ${m.routes.length} routes${desc}`);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
async function loadManifest(name) {
|
|
321
|
-
await ensureDirs();
|
|
322
|
-
try {
|
|
323
|
-
return JSON.parse(await readFile(resolve(APIS_DIR, `${name}.json`), "utf-8"));
|
|
324
|
-
} catch {
|
|
325
|
-
process.stderr.write(`API "${name}" not found. Create ${name}.yaml and run: godmode add ${name}
|
|
326
|
-
`);
|
|
327
|
-
process.exit(1);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
export {
|
|
332
|
-
validateGraphQLFlags,
|
|
333
|
-
GODMODE_HOME,
|
|
334
|
-
addApi,
|
|
335
|
-
updateApi,
|
|
336
|
-
removeApi,
|
|
337
|
-
listApis,
|
|
338
|
-
loadManifest
|
|
339
|
-
};
|
package/dist/mcp-NZIEMQOM.js
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
GODMODE_HOME,
|
|
4
|
-
loadManifest
|
|
5
|
-
} from "./chunk-FBMQ3AC4.js";
|
|
6
|
-
import "./chunk-EHS56XXY.js";
|
|
7
|
-
|
|
8
|
-
// src/commands/mcp.ts
|
|
9
|
-
import { resolve } from "path";
|
|
10
|
-
import { readFile } from "fs/promises";
|
|
11
|
-
import { spawn } from "child_process";
|
|
12
|
-
async function runMcp(args) {
|
|
13
|
-
const name = args[0];
|
|
14
|
-
if (!name || name === "--help" || name === "-h") {
|
|
15
|
-
console.log(`Serve a registered API as an MCP server over stdio.
|
|
16
|
-
|
|
17
|
-
Usage:
|
|
18
|
-
godmode mcp <name> [options]
|
|
19
|
-
|
|
20
|
-
Options:
|
|
21
|
-
--filter <text> Fuzzy-filter routes by resource name
|
|
22
|
-
--method <method> Filter by HTTP method (get, post, etc.)
|
|
23
|
-
|
|
24
|
-
For package-based adapters, spawns the adapter's MCP server.
|
|
25
|
-
For spec-based adapters, serves routes as MCP tools.`);
|
|
26
|
-
process.exit(name ? 0 : 1);
|
|
27
|
-
}
|
|
28
|
-
let filter;
|
|
29
|
-
let method;
|
|
30
|
-
for (let i = 1; i < args.length; i++) {
|
|
31
|
-
if (args[i] === "--filter" && args[i + 1]) filter = args[++i];
|
|
32
|
-
else if (args[i] === "--method" && args[i + 1]) method = args[++i];
|
|
33
|
-
}
|
|
34
|
-
const pkgName = name.startsWith("@") ? name : `@godmode-cli/${name}`;
|
|
35
|
-
const pkgDir = resolve(GODMODE_HOME, "node_modules", pkgName);
|
|
36
|
-
const mcpConfigPath = resolve(pkgDir, ".mcp.json");
|
|
37
|
-
try {
|
|
38
|
-
const mcpConfig = JSON.parse(await readFile(mcpConfigPath, "utf-8"));
|
|
39
|
-
const serverKey = Object.keys(mcpConfig.mcpServers)[0];
|
|
40
|
-
const server = mcpConfig.mcpServers[serverKey];
|
|
41
|
-
if (server.type === "stdio" && server.command) {
|
|
42
|
-
const child = spawn(server.command, server.args || [], {
|
|
43
|
-
stdio: "inherit",
|
|
44
|
-
cwd: pkgDir,
|
|
45
|
-
env: { ...process.env, ...server.env }
|
|
46
|
-
});
|
|
47
|
-
child.on("exit", (code) => process.exit(code ?? 0));
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
} catch {
|
|
51
|
-
}
|
|
52
|
-
const manifest = await loadManifest(name);
|
|
53
|
-
const { serveMcp } = await import("./mcp-server.js");
|
|
54
|
-
await serveMcp(manifest, { filter, method });
|
|
55
|
-
}
|
|
56
|
-
export {
|
|
57
|
-
runMcp
|
|
58
|
-
};
|