@hasna/configs 0.2.31 → 0.2.33
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 +2 -1
- package/README.md +9 -0
- package/dashboard/README.md +73 -0
- package/dist/mcp/http.d.ts +28 -0
- package/dist/mcp/http.d.ts.map +1 -0
- package/dist/mcp/http.test.d.ts +2 -0
- package/dist/mcp/http.test.d.ts.map +1 -0
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +321 -231
- package/dist/mcp/server.d.ts +4 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +15661 -1590
- package/package.json +1 -1
package/dist/mcp/index.js
CHANGED
|
@@ -11250,7 +11250,7 @@ var init_sync_dir = __esm(() => {
|
|
|
11250
11250
|
var require_package = __commonJS((exports, module) => {
|
|
11251
11251
|
module.exports = {
|
|
11252
11252
|
name: "@hasna/configs",
|
|
11253
|
-
version: "0.2.
|
|
11253
|
+
version: "0.2.33",
|
|
11254
11254
|
description: "AI coding agent configuration manager \u2014 store, version, apply, and share all your AI coding configs. CLI + MCP + REST API + Dashboard.",
|
|
11255
11255
|
type: "module",
|
|
11256
11256
|
main: "dist/index.js",
|
|
@@ -11333,12 +11333,14 @@ var require_package = __commonJS((exports, module) => {
|
|
|
11333
11333
|
});
|
|
11334
11334
|
|
|
11335
11335
|
// src/mcp/index.ts
|
|
11336
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
11337
|
+
|
|
11338
|
+
// src/mcp/server.ts
|
|
11336
11339
|
init_dist();
|
|
11337
11340
|
init_configs();
|
|
11338
11341
|
init_apply();
|
|
11339
11342
|
init_sync_dir();
|
|
11340
11343
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
11341
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
11342
11344
|
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
11343
11345
|
|
|
11344
11346
|
// src/db/profiles.ts
|
|
@@ -11397,7 +11399,7 @@ function resolveProfileForMachine(machine = detectMachineContext(), db) {
|
|
|
11397
11399
|
return matches[0]?.profile ?? null;
|
|
11398
11400
|
}
|
|
11399
11401
|
|
|
11400
|
-
// src/mcp/
|
|
11402
|
+
// src/mcp/server.ts
|
|
11401
11403
|
init_apply();
|
|
11402
11404
|
init_snapshots();
|
|
11403
11405
|
init_machine();
|
|
@@ -11457,240 +11459,328 @@ function err(msg) {
|
|
|
11457
11459
|
return { content: [{ type: "text", text: JSON.stringify({ error: msg }) }], isError: true };
|
|
11458
11460
|
}
|
|
11459
11461
|
var _cfgAgents = new Map;
|
|
11460
|
-
|
|
11461
|
-
|
|
11462
|
-
|
|
11463
|
-
server.setRequestHandler(
|
|
11464
|
-
|
|
11465
|
-
|
|
11466
|
-
|
|
11467
|
-
|
|
11468
|
-
|
|
11469
|
-
|
|
11470
|
-
|
|
11471
|
-
|
|
11472
|
-
|
|
11473
|
-
|
|
11474
|
-
|
|
11475
|
-
|
|
11476
|
-
|
|
11477
|
-
|
|
11478
|
-
|
|
11479
|
-
|
|
11480
|
-
|
|
11481
|
-
|
|
11482
|
-
|
|
11483
|
-
|
|
11484
|
-
|
|
11485
|
-
|
|
11486
|
-
|
|
11487
|
-
|
|
11488
|
-
|
|
11489
|
-
|
|
11490
|
-
|
|
11491
|
-
|
|
11492
|
-
|
|
11493
|
-
|
|
11494
|
-
|
|
11495
|
-
|
|
11496
|
-
|
|
11497
|
-
|
|
11498
|
-
|
|
11499
|
-
|
|
11500
|
-
|
|
11501
|
-
|
|
11502
|
-
|
|
11503
|
-
|
|
11504
|
-
|
|
11505
|
-
|
|
11506
|
-
|
|
11507
|
-
|
|
11508
|
-
|
|
11509
|
-
|
|
11510
|
-
|
|
11511
|
-
|
|
11512
|
-
|
|
11513
|
-
|
|
11514
|
-
|
|
11515
|
-
|
|
11516
|
-
|
|
11517
|
-
|
|
11518
|
-
|
|
11519
|
-
|
|
11520
|
-
|
|
11521
|
-
|
|
11522
|
-
|
|
11523
|
-
|
|
11524
|
-
|
|
11525
|
-
|
|
11526
|
-
|
|
11527
|
-
|
|
11528
|
-
|
|
11529
|
-
|
|
11530
|
-
|
|
11531
|
-
|
|
11532
|
-
|
|
11533
|
-
|
|
11534
|
-
|
|
11535
|
-
|
|
11536
|
-
|
|
11537
|
-
|
|
11538
|
-
|
|
11539
|
-
|
|
11540
|
-
|
|
11541
|
-
|
|
11542
|
-
|
|
11543
|
-
|
|
11544
|
-
|
|
11545
|
-
|
|
11546
|
-
|
|
11547
|
-
|
|
11548
|
-
|
|
11549
|
-
|
|
11550
|
-
|
|
11551
|
-
|
|
11552
|
-
|
|
11553
|
-
|
|
11554
|
-
|
|
11555
|
-
|
|
11556
|
-
|
|
11557
|
-
|
|
11558
|
-
|
|
11559
|
-
|
|
11560
|
-
|
|
11561
|
-
|
|
11562
|
-
|
|
11563
|
-
|
|
11564
|
-
|
|
11565
|
-
|
|
11566
|
-
|
|
11567
|
-
|
|
11568
|
-
|
|
11569
|
-
|
|
11570
|
-
|
|
11571
|
-
|
|
11572
|
-
|
|
11573
|
-
|
|
11574
|
-
|
|
11575
|
-
}
|
|
11576
|
-
return ok({
|
|
11577
|
-
total: stats["total"] || 0,
|
|
11578
|
-
by_category: Object.fromEntries(Object.entries(stats).filter(([k]) => k !== "total")),
|
|
11579
|
-
templates,
|
|
11580
|
-
drifted,
|
|
11581
|
-
drifted_configs: driftedSlugs.slice(0, 5),
|
|
11582
|
-
missing,
|
|
11583
|
-
db_path: process.env["CONFIGS_DB_PATH"] || "~/.hasna/configs/configs.db"
|
|
11584
|
-
});
|
|
11585
|
-
}
|
|
11586
|
-
case "sync_known": {
|
|
11587
|
-
const { syncKnown: syncKnown2 } = await Promise.resolve().then(() => (init_sync(), exports_sync));
|
|
11588
|
-
const result = await syncKnown2({
|
|
11589
|
-
agent: args["agent"] || undefined,
|
|
11590
|
-
category: args["category"] || undefined
|
|
11591
|
-
});
|
|
11592
|
-
return ok(result);
|
|
11593
|
-
}
|
|
11594
|
-
case "sync_project": {
|
|
11595
|
-
const { syncProject: syncProject2 } = await Promise.resolve().then(() => (init_sync(), exports_sync));
|
|
11596
|
-
const dir = args["project_dir"] || process.cwd();
|
|
11597
|
-
const result = await syncProject2({ projectDir: dir });
|
|
11598
|
-
return ok(result);
|
|
11599
|
-
}
|
|
11600
|
-
case "render_template": {
|
|
11601
|
-
const { renderTemplate: renderTemplate2 } = await Promise.resolve().then(() => (init_template(), exports_template));
|
|
11602
|
-
const config = getConfig(args["id_or_slug"]);
|
|
11603
|
-
const vars = args["vars"] || {};
|
|
11604
|
-
if (args["use_env"]) {
|
|
11605
|
-
const { extractTemplateVars: extractTemplateVars2 } = await Promise.resolve().then(() => (init_template(), exports_template));
|
|
11606
|
-
for (const v of extractTemplateVars2(config.content)) {
|
|
11607
|
-
if (!(v.name in vars) && process.env[v.name]) {
|
|
11608
|
-
vars[v.name] = process.env[v.name];
|
|
11462
|
+
function buildServer() {
|
|
11463
|
+
const server = new Server({ name: "configs", version: require_package().version }, { capabilities: { tools: {} } });
|
|
11464
|
+
const LEAN_TOOLS = profileFilter && profileFilter.length > 0 ? ALL_LEAN_TOOLS.filter((t) => profileFilter.includes(t.name)) : ALL_LEAN_TOOLS;
|
|
11465
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: LEAN_TOOLS }));
|
|
11466
|
+
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
11467
|
+
const { name, arguments: args = {} } = req.params;
|
|
11468
|
+
try {
|
|
11469
|
+
switch (name) {
|
|
11470
|
+
case "list_configs": {
|
|
11471
|
+
const configs = listConfigs({
|
|
11472
|
+
category: args["category"] || undefined,
|
|
11473
|
+
agent: args["agent"] || undefined,
|
|
11474
|
+
kind: args["kind"] || undefined,
|
|
11475
|
+
search: args["search"] || undefined
|
|
11476
|
+
});
|
|
11477
|
+
return ok(configs.map((c) => ({ id: c.id, slug: c.slug, name: c.name, category: c.category, agent: c.agent, kind: c.kind, target_path: c.target_path, version: c.version })));
|
|
11478
|
+
}
|
|
11479
|
+
case "get_config": {
|
|
11480
|
+
const c = getConfig(args["id_or_slug"]);
|
|
11481
|
+
return ok(c);
|
|
11482
|
+
}
|
|
11483
|
+
case "create_config": {
|
|
11484
|
+
const c = createConfig({
|
|
11485
|
+
name: args["name"],
|
|
11486
|
+
content: args["content"],
|
|
11487
|
+
category: args["category"],
|
|
11488
|
+
agent: args["agent"] || undefined,
|
|
11489
|
+
target_path: args["target_path"] || undefined,
|
|
11490
|
+
kind: args["kind"] || undefined,
|
|
11491
|
+
format: args["format"] || undefined,
|
|
11492
|
+
tags: args["tags"] || undefined,
|
|
11493
|
+
description: args["description"] || undefined,
|
|
11494
|
+
is_template: args["is_template"] || undefined
|
|
11495
|
+
});
|
|
11496
|
+
return ok({ id: c.id, slug: c.slug, name: c.name });
|
|
11497
|
+
}
|
|
11498
|
+
case "update_config": {
|
|
11499
|
+
const c = updateConfig(args["id_or_slug"], {
|
|
11500
|
+
content: args["content"],
|
|
11501
|
+
name: args["name"],
|
|
11502
|
+
tags: args["tags"],
|
|
11503
|
+
description: args["description"],
|
|
11504
|
+
category: args["category"],
|
|
11505
|
+
agent: args["agent"],
|
|
11506
|
+
target_path: args["target_path"]
|
|
11507
|
+
});
|
|
11508
|
+
return ok({ id: c.id, slug: c.slug, version: c.version });
|
|
11509
|
+
}
|
|
11510
|
+
case "delete_config": {
|
|
11511
|
+
const { deleteConfig: deleteConfig2 } = await Promise.resolve().then(() => (init_configs(), exports_configs));
|
|
11512
|
+
deleteConfig2(args["id_or_slug"]);
|
|
11513
|
+
return ok({ deleted: true });
|
|
11514
|
+
}
|
|
11515
|
+
case "apply_config": {
|
|
11516
|
+
const config = getConfig(args["id_or_slug"]);
|
|
11517
|
+
const result = await applyConfig(config, { dryRun: args["dry_run"] });
|
|
11518
|
+
return ok(result);
|
|
11519
|
+
}
|
|
11520
|
+
case "sync_directory": {
|
|
11521
|
+
const dir = args["dir"];
|
|
11522
|
+
const direction = args["direction"] || "from_disk";
|
|
11523
|
+
const result = direction === "to_disk" ? await syncToDir(dir) : await syncFromDir(dir);
|
|
11524
|
+
return ok(result);
|
|
11525
|
+
}
|
|
11526
|
+
case "list_profiles": {
|
|
11527
|
+
return ok(listProfiles());
|
|
11528
|
+
}
|
|
11529
|
+
case "apply_profile": {
|
|
11530
|
+
const machine = detectMachineContext({
|
|
11531
|
+
hostname: args["hostname"],
|
|
11532
|
+
os: args["os"],
|
|
11533
|
+
arch: args["arch"]
|
|
11534
|
+
});
|
|
11535
|
+
if (!args["auto"] && !args["id_or_slug"])
|
|
11536
|
+
return err("id_or_slug is required unless auto=true");
|
|
11537
|
+
const profile = args["auto"] ? resolveProfileForMachine(machine) : getProfile(args["id_or_slug"]);
|
|
11538
|
+
if (!profile)
|
|
11539
|
+
return err("No matching machine-aware profile found");
|
|
11540
|
+
const configs = getProfileConfigs(profile.id);
|
|
11541
|
+
const vars = resolveProfileVariables(profile, machine);
|
|
11542
|
+
const results = await applyConfigs(configs, { dryRun: args["dry_run"], vars });
|
|
11543
|
+
return ok({ profile, machine, results });
|
|
11544
|
+
}
|
|
11545
|
+
case "get_snapshot": {
|
|
11546
|
+
const config = getConfig(args["config_id_or_slug"]);
|
|
11547
|
+
if (args["version"]) {
|
|
11548
|
+
const snap = getSnapshotByVersion(config.id, args["version"]);
|
|
11549
|
+
return snap ? ok(snap) : err("Snapshot not found");
|
|
11550
|
+
}
|
|
11551
|
+
const snaps = listSnapshots(config.id);
|
|
11552
|
+
return ok(snaps[0] ?? null);
|
|
11553
|
+
}
|
|
11554
|
+
case "get_status": {
|
|
11555
|
+
const stats = getConfigStats();
|
|
11556
|
+
const allConfigs = listConfigs({ kind: "file" });
|
|
11557
|
+
const { existsSync: ex, readFileSync: rf } = await import("fs");
|
|
11558
|
+
const { expandPath: expandPath2 } = await Promise.resolve().then(() => (init_apply(), exports_apply));
|
|
11559
|
+
const { redactContent: redactContent2 } = await Promise.resolve().then(() => (init_redact(), exports_redact));
|
|
11560
|
+
let drifted = 0, missing = 0, templates = 0;
|
|
11561
|
+
const driftedSlugs = [];
|
|
11562
|
+
for (const c of allConfigs) {
|
|
11563
|
+
if (c.is_template)
|
|
11564
|
+
templates++;
|
|
11565
|
+
if (!c.target_path)
|
|
11566
|
+
continue;
|
|
11567
|
+
const abs = expandPath2(c.target_path);
|
|
11568
|
+
if (!ex(abs)) {
|
|
11569
|
+
missing++;
|
|
11570
|
+
continue;
|
|
11571
|
+
}
|
|
11572
|
+
const disk = rf(abs, "utf-8");
|
|
11573
|
+
const { content: redactedDisk } = redactContent2(disk, c.format);
|
|
11574
|
+
if (redactedDisk !== c.content) {
|
|
11575
|
+
drifted++;
|
|
11576
|
+
driftedSlugs.push(c.slug);
|
|
11609
11577
|
}
|
|
11610
11578
|
}
|
|
11579
|
+
return ok({
|
|
11580
|
+
total: stats["total"] || 0,
|
|
11581
|
+
by_category: Object.fromEntries(Object.entries(stats).filter(([k]) => k !== "total")),
|
|
11582
|
+
templates,
|
|
11583
|
+
drifted,
|
|
11584
|
+
drifted_configs: driftedSlugs.slice(0, 5),
|
|
11585
|
+
missing,
|
|
11586
|
+
db_path: process.env["CONFIGS_DB_PATH"] || "~/.hasna/configs/configs.db"
|
|
11587
|
+
});
|
|
11611
11588
|
}
|
|
11612
|
-
|
|
11613
|
-
|
|
11614
|
-
|
|
11615
|
-
|
|
11616
|
-
|
|
11617
|
-
|
|
11618
|
-
|
|
11619
|
-
|
|
11620
|
-
|
|
11621
|
-
const
|
|
11622
|
-
|
|
11623
|
-
|
|
11624
|
-
|
|
11625
|
-
|
|
11626
|
-
|
|
11589
|
+
case "sync_known": {
|
|
11590
|
+
const { syncKnown: syncKnown2 } = await Promise.resolve().then(() => (init_sync(), exports_sync));
|
|
11591
|
+
const result = await syncKnown2({
|
|
11592
|
+
agent: args["agent"] || undefined,
|
|
11593
|
+
category: args["category"] || undefined
|
|
11594
|
+
});
|
|
11595
|
+
return ok(result);
|
|
11596
|
+
}
|
|
11597
|
+
case "sync_project": {
|
|
11598
|
+
const { syncProject: syncProject2 } = await Promise.resolve().then(() => (init_sync(), exports_sync));
|
|
11599
|
+
const dir = args["project_dir"] || process.cwd();
|
|
11600
|
+
const result = await syncProject2({ projectDir: dir });
|
|
11601
|
+
return ok(result);
|
|
11602
|
+
}
|
|
11603
|
+
case "render_template": {
|
|
11604
|
+
const { renderTemplate: renderTemplate2 } = await Promise.resolve().then(() => (init_template(), exports_template));
|
|
11605
|
+
const config = getConfig(args["id_or_slug"]);
|
|
11606
|
+
const vars = args["vars"] || {};
|
|
11607
|
+
if (args["use_env"]) {
|
|
11608
|
+
const { extractTemplateVars: extractTemplateVars2 } = await Promise.resolve().then(() => (init_template(), exports_template));
|
|
11609
|
+
for (const v of extractTemplateVars2(config.content)) {
|
|
11610
|
+
if (!(v.name in vars) && process.env[v.name]) {
|
|
11611
|
+
vars[v.name] = process.env[v.name];
|
|
11612
|
+
}
|
|
11627
11613
|
}
|
|
11628
11614
|
}
|
|
11615
|
+
const rendered = renderTemplate2(config.content, vars);
|
|
11616
|
+
return ok({ rendered, config_id: config.id, slug: config.slug });
|
|
11617
|
+
}
|
|
11618
|
+
case "scan_secrets": {
|
|
11619
|
+
const { scanSecrets: scanSecrets2, redactContent: redactContent2 } = await Promise.resolve().then(() => (init_redact(), exports_redact));
|
|
11620
|
+
const configs = args["id_or_slug"] ? [getConfig(args["id_or_slug"])] : listConfigs({ kind: "file" });
|
|
11621
|
+
const findings = [];
|
|
11622
|
+
for (const c of configs) {
|
|
11623
|
+
const fmt = c.format;
|
|
11624
|
+
const secrets = scanSecrets2(c.content, fmt);
|
|
11625
|
+
if (secrets.length > 0) {
|
|
11626
|
+
findings.push({ slug: c.slug, secrets: secrets.length, vars: secrets.map((s) => s.varName) });
|
|
11627
|
+
if (args["fix"]) {
|
|
11628
|
+
const { content, isTemplate: isTemplate2 } = redactContent2(c.content, fmt);
|
|
11629
|
+
updateConfig(c.id, { content, is_template: isTemplate2 });
|
|
11630
|
+
}
|
|
11631
|
+
}
|
|
11632
|
+
}
|
|
11633
|
+
return ok({ clean: findings.length === 0, findings, fixed: !!args["fix"] });
|
|
11634
|
+
}
|
|
11635
|
+
case "search_tools": {
|
|
11636
|
+
const query = (args["query"] || "").toLowerCase();
|
|
11637
|
+
const matches = Object.entries(TOOL_DOCS).filter(([k, v]) => k.includes(query) || v.toLowerCase().includes(query)).map(([name2, description]) => ({ name: name2, description }));
|
|
11638
|
+
return ok(matches);
|
|
11639
|
+
}
|
|
11640
|
+
case "describe_tools": {
|
|
11641
|
+
const names = args["names"];
|
|
11642
|
+
if (names) {
|
|
11643
|
+
return ok(Object.fromEntries(names.map((n) => [n, TOOL_DOCS[n] ?? "Unknown tool"])));
|
|
11644
|
+
}
|
|
11645
|
+
return ok(TOOL_DOCS);
|
|
11646
|
+
}
|
|
11647
|
+
case "register_agent": {
|
|
11648
|
+
const n = String(args["name"] ?? "");
|
|
11649
|
+
const existing = [..._cfgAgents.values()].find((x) => x.name === n);
|
|
11650
|
+
if (existing) {
|
|
11651
|
+
existing.last_seen_at = new Date().toISOString();
|
|
11652
|
+
return ok(existing);
|
|
11653
|
+
}
|
|
11654
|
+
const id = Math.random().toString(36).slice(2, 10);
|
|
11655
|
+
const ag = { id, name: n, last_seen_at: new Date().toISOString() };
|
|
11656
|
+
_cfgAgents.set(id, ag);
|
|
11657
|
+
return ok(ag);
|
|
11658
|
+
}
|
|
11659
|
+
case "heartbeat": {
|
|
11660
|
+
const ag = _cfgAgents.get(String(args["agent_id"] ?? ""));
|
|
11661
|
+
if (!ag)
|
|
11662
|
+
return err(`Agent not found: ${args["agent_id"]}`);
|
|
11663
|
+
ag.last_seen_at = new Date().toISOString();
|
|
11664
|
+
return ok({ agent_id: ag.id, name: ag.name, last_seen_at: ag.last_seen_at });
|
|
11665
|
+
}
|
|
11666
|
+
case "set_focus": {
|
|
11667
|
+
const ag = _cfgAgents.get(String(args["agent_id"] ?? ""));
|
|
11668
|
+
if (!ag)
|
|
11669
|
+
return err(`Agent not found: ${args["agent_id"]}`);
|
|
11670
|
+
ag["project_id"] = args["project_id"];
|
|
11671
|
+
return ok({ agent_id: ag.id, project_id: args["project_id"] ?? null });
|
|
11672
|
+
}
|
|
11673
|
+
case "list_agents": {
|
|
11674
|
+
return ok([..._cfgAgents.values()]);
|
|
11675
|
+
}
|
|
11676
|
+
case "send_feedback": {
|
|
11677
|
+
const { getDatabase: getDatabase2 } = await Promise.resolve().then(() => (init_database(), exports_database));
|
|
11678
|
+
const db = getDatabase2();
|
|
11679
|
+
const pkg = require_package();
|
|
11680
|
+
db.run("INSERT INTO feedback (message, email, category, version) VALUES (?, ?, ?, ?)", [args["message"], args["email"] || null, args["category"] || "general", pkg.version]);
|
|
11681
|
+
return ok({ message: "Feedback saved. Thank you!" });
|
|
11629
11682
|
}
|
|
11630
|
-
|
|
11631
|
-
|
|
11632
|
-
case "search_tools": {
|
|
11633
|
-
const query = (args["query"] || "").toLowerCase();
|
|
11634
|
-
const matches = Object.entries(TOOL_DOCS).filter(([k, v]) => k.includes(query) || v.toLowerCase().includes(query)).map(([name2, description]) => ({ name: name2, description }));
|
|
11635
|
-
return ok(matches);
|
|
11636
|
-
}
|
|
11637
|
-
case "describe_tools": {
|
|
11638
|
-
const names = args["names"];
|
|
11639
|
-
if (names) {
|
|
11640
|
-
return ok(Object.fromEntries(names.map((n) => [n, TOOL_DOCS[n] ?? "Unknown tool"])));
|
|
11641
|
-
}
|
|
11642
|
-
return ok(TOOL_DOCS);
|
|
11643
|
-
}
|
|
11644
|
-
case "register_agent": {
|
|
11645
|
-
const n = String(args["name"] ?? "");
|
|
11646
|
-
const existing = [..._cfgAgents.values()].find((x) => x.name === n);
|
|
11647
|
-
if (existing) {
|
|
11648
|
-
existing.last_seen_at = new Date().toISOString();
|
|
11649
|
-
return ok(existing);
|
|
11650
|
-
}
|
|
11651
|
-
const id = Math.random().toString(36).slice(2, 10);
|
|
11652
|
-
const ag = { id, name: n, last_seen_at: new Date().toISOString() };
|
|
11653
|
-
_cfgAgents.set(id, ag);
|
|
11654
|
-
return ok(ag);
|
|
11655
|
-
}
|
|
11656
|
-
case "heartbeat": {
|
|
11657
|
-
const ag = _cfgAgents.get(String(args["agent_id"] ?? ""));
|
|
11658
|
-
if (!ag)
|
|
11659
|
-
return err(`Agent not found: ${args["agent_id"]}`);
|
|
11660
|
-
ag.last_seen_at = new Date().toISOString();
|
|
11661
|
-
return ok({ agent_id: ag.id, name: ag.name, last_seen_at: ag.last_seen_at });
|
|
11662
|
-
}
|
|
11663
|
-
case "set_focus": {
|
|
11664
|
-
const ag = _cfgAgents.get(String(args["agent_id"] ?? ""));
|
|
11665
|
-
if (!ag)
|
|
11666
|
-
return err(`Agent not found: ${args["agent_id"]}`);
|
|
11667
|
-
ag["project_id"] = args["project_id"];
|
|
11668
|
-
return ok({ agent_id: ag.id, project_id: args["project_id"] ?? null });
|
|
11669
|
-
}
|
|
11670
|
-
case "list_agents": {
|
|
11671
|
-
return ok([..._cfgAgents.values()]);
|
|
11672
|
-
}
|
|
11673
|
-
case "send_feedback": {
|
|
11674
|
-
const { getDatabase: getDatabase2 } = await Promise.resolve().then(() => (init_database(), exports_database));
|
|
11675
|
-
const db = getDatabase2();
|
|
11676
|
-
const pkg = require_package();
|
|
11677
|
-
db.run("INSERT INTO feedback (message, email, category, version) VALUES (?, ?, ?, ?)", [args["message"], args["email"] || null, args["category"] || "general", pkg.version]);
|
|
11678
|
-
return ok({ message: "Feedback saved. Thank you!" });
|
|
11683
|
+
default:
|
|
11684
|
+
return err(`Unknown tool: ${name}`);
|
|
11679
11685
|
}
|
|
11680
|
-
|
|
11681
|
-
|
|
11686
|
+
} catch (e) {
|
|
11687
|
+
return err(e instanceof Error ? e.message : String(e));
|
|
11682
11688
|
}
|
|
11683
|
-
}
|
|
11684
|
-
|
|
11689
|
+
});
|
|
11690
|
+
try {
|
|
11691
|
+
registerCloudTools(server, "configs");
|
|
11692
|
+
} catch {}
|
|
11693
|
+
return server;
|
|
11694
|
+
}
|
|
11695
|
+
|
|
11696
|
+
// src/mcp/http.ts
|
|
11697
|
+
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
11698
|
+
var MCP_HTTP_PORT = 8853;
|
|
11699
|
+
var MCP_NAME = "configs";
|
|
11700
|
+
function isStdioMode(argv) {
|
|
11701
|
+
return argv.includes("--stdio") || process.env.MCP_STDIO === "1";
|
|
11702
|
+
}
|
|
11703
|
+
function resolveHttpPort(argv) {
|
|
11704
|
+
const eqArg = argv.find((a) => a.startsWith("--port="));
|
|
11705
|
+
if (eqArg) {
|
|
11706
|
+
const parsed = Number.parseInt(eqArg.slice("--port=".length), 10);
|
|
11707
|
+
if (!Number.isNaN(parsed))
|
|
11708
|
+
return parsed;
|
|
11709
|
+
}
|
|
11710
|
+
const idx = argv.indexOf("--port");
|
|
11711
|
+
if (idx >= 0) {
|
|
11712
|
+
const parsed = Number.parseInt(argv[idx + 1] ?? "", 10);
|
|
11713
|
+
if (!Number.isNaN(parsed))
|
|
11714
|
+
return parsed;
|
|
11715
|
+
}
|
|
11716
|
+
const envPort = process.env.MCP_HTTP_PORT;
|
|
11717
|
+
if (envPort) {
|
|
11718
|
+
const parsed = Number.parseInt(envPort, 10);
|
|
11719
|
+
if (!Number.isNaN(parsed))
|
|
11720
|
+
return parsed;
|
|
11721
|
+
}
|
|
11722
|
+
return MCP_HTTP_PORT;
|
|
11723
|
+
}
|
|
11724
|
+
function healthPayload() {
|
|
11725
|
+
return { status: "ok", name: MCP_NAME };
|
|
11726
|
+
}
|
|
11727
|
+
async function handleMcpRequest(req) {
|
|
11728
|
+
const server = buildServer();
|
|
11729
|
+
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
11730
|
+
sessionIdGenerator: undefined,
|
|
11731
|
+
enableJsonResponse: true
|
|
11732
|
+
});
|
|
11733
|
+
await server.connect(transport);
|
|
11734
|
+
return transport.handleRequest(req);
|
|
11735
|
+
}
|
|
11736
|
+
async function handleMcpHttpRequest(req) {
|
|
11737
|
+
const url = new URL(req.url);
|
|
11738
|
+
if (url.pathname === "/health" && req.method === "GET") {
|
|
11739
|
+
return Response.json(healthPayload());
|
|
11685
11740
|
}
|
|
11686
|
-
|
|
11687
|
-
|
|
11688
|
-
|
|
11689
|
-
|
|
11690
|
-
|
|
11691
|
-
|
|
11692
|
-
|
|
11693
|
-
|
|
11694
|
-
|
|
11695
|
-
|
|
11696
|
-
await
|
|
11741
|
+
if (url.pathname === "/mcp") {
|
|
11742
|
+
return handleMcpRequest(req);
|
|
11743
|
+
}
|
|
11744
|
+
return null;
|
|
11745
|
+
}
|
|
11746
|
+
async function startMcpHttpServer(port) {
|
|
11747
|
+
const httpServer = Bun.serve({
|
|
11748
|
+
hostname: "127.0.0.1",
|
|
11749
|
+
port,
|
|
11750
|
+
async fetch(req) {
|
|
11751
|
+
const handled = await handleMcpHttpRequest(req);
|
|
11752
|
+
if (handled)
|
|
11753
|
+
return handled;
|
|
11754
|
+
return Response.json({ error: "Not found" }, { status: 404 });
|
|
11755
|
+
}
|
|
11756
|
+
});
|
|
11757
|
+
return { port: httpServer.port, stop: () => httpServer.stop() };
|
|
11758
|
+
}
|
|
11759
|
+
|
|
11760
|
+
// src/mcp/index.ts
|
|
11761
|
+
async function main() {
|
|
11762
|
+
const argv = process.argv.slice(2);
|
|
11763
|
+
if (argv.includes("--claude")) {
|
|
11764
|
+
const proc = Bun.spawn(["claude", "mcp", "add", "--transport", "stdio", "--scope", "user", "configs", "--", "configs-mcp"], { stdout: "inherit", stderr: "inherit" });
|
|
11765
|
+
await proc.exited;
|
|
11766
|
+
process.exit(0);
|
|
11767
|
+
}
|
|
11768
|
+
if (isStdioMode(argv)) {
|
|
11769
|
+
const server = buildServer();
|
|
11770
|
+
const transport = new StdioServerTransport;
|
|
11771
|
+
await server.connect(transport);
|
|
11772
|
+
return;
|
|
11773
|
+
}
|
|
11774
|
+
const port = resolveHttpPort(argv);
|
|
11775
|
+
const { port: boundPort } = await startMcpHttpServer(port);
|
|
11776
|
+
console.error(`configs-mcp HTTP listening on http://127.0.0.1:${boundPort}/mcp`);
|
|
11777
|
+
}
|
|
11778
|
+
if (import.meta.main) {
|
|
11779
|
+
main().catch((err2) => {
|
|
11780
|
+
console.error("MCP server error:", err2);
|
|
11781
|
+
process.exit(1);
|
|
11782
|
+
});
|
|
11783
|
+
}
|
|
11784
|
+
export {
|
|
11785
|
+
buildServer
|
|
11786
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AA8EnE,wBAAgB,WAAW,IAAI,MAAM,CAkPpC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":";;;;;;AA6XA,wBAAgE"}
|