@roxyon/mcp 0.2.0 → 0.3.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.
|
@@ -35,6 +35,36 @@ Steps:
|
|
|
35
35
|
]
|
|
36
36
|
})
|
|
37
37
|
);
|
|
38
|
+
server.registerPrompt(
|
|
39
|
+
"build-and-ship-site",
|
|
40
|
+
{
|
|
41
|
+
title: "Build a site and host it on Roxyon",
|
|
42
|
+
description: "Generate a static site (or no-build LumenJS SPA) in the conversation and publish it to a Roxyon host \u2014 for the hosted connector, no local files.",
|
|
43
|
+
argsSchema: {
|
|
44
|
+
brief: z.string().describe("What the site should be."),
|
|
45
|
+
host: z.string().describe("Where to host it, e.g. promo.mycompany.com")
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
({ brief, host }) => ({
|
|
49
|
+
messages: [
|
|
50
|
+
{
|
|
51
|
+
role: "user",
|
|
52
|
+
content: {
|
|
53
|
+
type: "text",
|
|
54
|
+
text: `Build and publish a website to ${host}.
|
|
55
|
+
|
|
56
|
+
Brief: ${brief}
|
|
57
|
+
|
|
58
|
+
1. Read "roxyon://docs/recipe" and follow it. (Read "roxyon://docs/lumenjs" too only if the site needs reactivity.)
|
|
59
|
+
2. If ${host} is not already on the account, roxyon_add_domain { host: "${host}", confirm: true } (add spa:true only for a real single-page app).
|
|
60
|
+
3. Generate the site \u2014 self-contained static files, relative paths, real .html per page or <page>/index.html, \u226460 files / 2 MB.
|
|
61
|
+
4. roxyon_deploy_content { host: "${host}", files: [...], clean: true, confirm: true } (dry-run first).
|
|
62
|
+
5. roxyon_list_files { host: "${host}" } to verify, then give me the URL. Note that HTTPS may take a couple of minutes on a brand-new host.`
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
})
|
|
67
|
+
);
|
|
38
68
|
server.registerPrompt(
|
|
39
69
|
"deploy-to-roxyon",
|
|
40
70
|
{
|
|
@@ -87,6 +117,13 @@ var DOCS = [
|
|
|
87
117
|
file: "deploy.md",
|
|
88
118
|
title: "Deploying to Roxyon",
|
|
89
119
|
description: "How roxyon_init / roxyon_deploy work, the roxyon.json schema, static vs app deploys, env vars, and git push-to-deploy."
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
uri: "roxyon://docs/recipe",
|
|
123
|
+
name: "web-deploy-recipe",
|
|
124
|
+
file: "recipe-web-deploy.md",
|
|
125
|
+
title: "Recipe \u2014 build a site and host it",
|
|
126
|
+
description: 'The end-to-end flow for "build a landing page and put it on sub.mydomain.com" over the hosted connector: roxyon_add_domain \u2192 roxyon_deploy_content \u2192 roxyon_list_files.'
|
|
90
127
|
}
|
|
91
128
|
];
|
|
92
129
|
function registerResources(server) {
|
|
@@ -112,6 +149,7 @@ import {
|
|
|
112
149
|
detectRuntime,
|
|
113
150
|
listFiles,
|
|
114
151
|
loadProjectConfig,
|
|
152
|
+
packFiles,
|
|
115
153
|
saveProjectConfig
|
|
116
154
|
} from "@roxyon/deploy-core";
|
|
117
155
|
import { z as z2 } from "zod";
|
|
@@ -193,6 +231,9 @@ async function guard(fn) {
|
|
|
193
231
|
}
|
|
194
232
|
|
|
195
233
|
// src/tools.ts
|
|
234
|
+
var MAX_FILES = 60;
|
|
235
|
+
var MAX_FILE_BYTES = 1024 * 1024;
|
|
236
|
+
var MAX_TOTAL_BYTES = 2 * 1024 * 1024;
|
|
196
237
|
function remoteFilesystemBlock(tool) {
|
|
197
238
|
if (!currentContext().remote) return null;
|
|
198
239
|
return errorResult(
|
|
@@ -235,9 +276,7 @@ function registerTools(server) {
|
|
|
235
276
|
`Auth: ${session.source}${ctx.scopes.length ? ` (scopes: ${ctx.scopes.join(", ")})` : ""}`,
|
|
236
277
|
"",
|
|
237
278
|
"Subscriptions:",
|
|
238
|
-
...ctx.subscriptions.map(
|
|
239
|
-
(s) => ` ${s.name || s.id} \u2014 ${s.status || "?"}${s.datacenter ? ` \xB7 ${s.datacenter}` : ""}`
|
|
240
|
-
),
|
|
279
|
+
...ctx.subscriptions.map((s) => ` ${s.name || s.id} \u2014 ${s.status || "?"}`),
|
|
241
280
|
"",
|
|
242
281
|
`Hosts: ${ctx.domains.map((d) => d.name).join(", ") || "(none)"}`
|
|
243
282
|
];
|
|
@@ -260,8 +299,10 @@ function registerTools(server) {
|
|
|
260
299
|
const session = await getSession();
|
|
261
300
|
const { domains } = await accountContext(session);
|
|
262
301
|
return text(
|
|
263
|
-
domains.length ? domains.map(
|
|
264
|
-
|
|
302
|
+
domains.length ? domains.map(
|
|
303
|
+
(d) => `- ${d.name}${d.status && d.status !== "active" ? ` (${d.status})` : ""}`
|
|
304
|
+
).join("\n") : "(no hosts on this account)",
|
|
305
|
+
{ hosts: domains }
|
|
265
306
|
);
|
|
266
307
|
})
|
|
267
308
|
);
|
|
@@ -592,6 +633,164 @@ ${res.hint}` : ""
|
|
|
592
633
|
});
|
|
593
634
|
})
|
|
594
635
|
);
|
|
636
|
+
server.registerTool(
|
|
637
|
+
"roxyon_add_domain",
|
|
638
|
+
{
|
|
639
|
+
title: "Roxyon: add a subdomain / host",
|
|
640
|
+
description: "Provision a host \u2014 DNS + web server + automatic HTTPS. The host must be a subdomain of a domain the account already hosts (or a *.roxyon.com subdomain). Returns while it is still coming up; TLS follows a minute or two later. Needs confirm:true.",
|
|
641
|
+
inputSchema: {
|
|
642
|
+
host: z2.string().describe("The full hostname, e.g. promo.mycompany.com"),
|
|
643
|
+
subscription: z2.string().optional().describe("Which subscription to attach it to (omit if the account has one)."),
|
|
644
|
+
spa: z2.boolean().optional().describe("Single-page app: unmatched paths serve /index.html."),
|
|
645
|
+
confirm: z2.boolean().optional()
|
|
646
|
+
}
|
|
647
|
+
},
|
|
648
|
+
(args) => guard(async () => {
|
|
649
|
+
const session = await getSession();
|
|
650
|
+
if (!args.confirm) {
|
|
651
|
+
return text(
|
|
652
|
+
`Would provision ${args.host}${args.spa ? " (SPA routing)" : ""}. Pass confirm:true.`,
|
|
653
|
+
{ dryRun: true }
|
|
654
|
+
);
|
|
655
|
+
}
|
|
656
|
+
const res = await session.roxyon.domains.create({
|
|
657
|
+
host: args.host,
|
|
658
|
+
subscription: args.subscription ?? session.preferredSubscription,
|
|
659
|
+
siteType: args.spa ? "spa" : void 0
|
|
660
|
+
});
|
|
661
|
+
const live = res.status === "active";
|
|
662
|
+
return text(
|
|
663
|
+
[
|
|
664
|
+
`${res.host} \u2014 ${res.type}, ${res.status}.`,
|
|
665
|
+
live ? "The host is up. HTTPS may take another minute to issue." : "DNS + web server are being set up (~1\u20132 min), then HTTPS. Check roxyon_list_domains, or just deploy now \u2014 files are kept and served once it is live."
|
|
666
|
+
].join("\n"),
|
|
667
|
+
{ ok: true, host: res.host, type: res.type, status: res.status }
|
|
668
|
+
);
|
|
669
|
+
})
|
|
670
|
+
);
|
|
671
|
+
server.registerTool(
|
|
672
|
+
"roxyon_deploy_content",
|
|
673
|
+
{
|
|
674
|
+
title: "Roxyon: deploy generated files",
|
|
675
|
+
description: [
|
|
676
|
+
"Publish a set of files you generated (HTML/CSS/JS/assets) to a host on the account.",
|
|
677
|
+
"The host must already exist (roxyon_add_domain, or an existing site). Overlay by",
|
|
678
|
+
"default \u2014 pass clean:true to replace the whole document root. Needs confirm:true.",
|
|
679
|
+
`Limits: ${MAX_FILES} files, ${MAX_FILE_BYTES / 1024 | 0} KB per file,`,
|
|
680
|
+
`${(MAX_TOTAL_BYTES / 1024 / 1024).toFixed(1)} MB total (base64-encode binaries).`
|
|
681
|
+
].join(" "),
|
|
682
|
+
inputSchema: {
|
|
683
|
+
host: z2.string().describe("Target host, e.g. promo.mycompany.com"),
|
|
684
|
+
folder: z2.string().optional().describe('Sub-path under the host; "" = site root.'),
|
|
685
|
+
files: z2.array(
|
|
686
|
+
z2.object({
|
|
687
|
+
path: z2.string().describe("Relative path, e.g. index.html, about/index.html"),
|
|
688
|
+
content: z2.string(),
|
|
689
|
+
encoding: z2.enum(["utf8", "base64"]).optional()
|
|
690
|
+
})
|
|
691
|
+
).min(1),
|
|
692
|
+
clean: z2.boolean().optional().describe("Replace the whole docroot (keeps .well-known)."),
|
|
693
|
+
spa: z2.boolean().optional().describe("Also flip the host to SPA routing."),
|
|
694
|
+
confirm: z2.boolean().optional()
|
|
695
|
+
}
|
|
696
|
+
},
|
|
697
|
+
(args) => guard(async () => {
|
|
698
|
+
const session = await getSession();
|
|
699
|
+
if (args.files.length > MAX_FILES) {
|
|
700
|
+
return errorResult(`Too many files (${args.files.length}); the limit is ${MAX_FILES}.`);
|
|
701
|
+
}
|
|
702
|
+
let total = 0;
|
|
703
|
+
for (const f of args.files) {
|
|
704
|
+
const n = Buffer.byteLength(f.content, f.encoding === "base64" ? "base64" : "utf8");
|
|
705
|
+
if (n > MAX_FILE_BYTES) {
|
|
706
|
+
return errorResult(
|
|
707
|
+
`"${f.path}" is ${n / 1024 | 0} KB; the per-file limit is ${MAX_FILE_BYTES / 1024 | 0} KB.`
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
total += n;
|
|
711
|
+
}
|
|
712
|
+
if (total > MAX_TOTAL_BYTES) {
|
|
713
|
+
return errorResult(
|
|
714
|
+
`Total content is ${(total / 1024 / 1024).toFixed(1)} MB; the limit is ${(MAX_TOTAL_BYTES / 1024 / 1024).toFixed(1)} MB. Deploy in parts or use the CLI.`
|
|
715
|
+
);
|
|
716
|
+
}
|
|
717
|
+
const folder = args.folder ?? "";
|
|
718
|
+
if (!args.confirm) {
|
|
719
|
+
return text(
|
|
720
|
+
[
|
|
721
|
+
"DRY RUN \u2014 pass confirm:true to deploy.",
|
|
722
|
+
"",
|
|
723
|
+
`Host: ${args.host}${folder ? `/${folder}` : ""}`,
|
|
724
|
+
`Files: ${args.files.length} (${total / 1024 | 0} KB)`,
|
|
725
|
+
`Mode: ${args.clean ? "clean (replaces the document root)" : "overlay"}${args.spa ? " + SPA routing" : ""}`,
|
|
726
|
+
...args.files.slice(0, 20).map((f) => ` ${f.path}`),
|
|
727
|
+
args.files.length > 20 ? ` \u2026 ${args.files.length - 20} more` : ""
|
|
728
|
+
].filter(Boolean).join("\n"),
|
|
729
|
+
{ dryRun: true }
|
|
730
|
+
);
|
|
731
|
+
}
|
|
732
|
+
const { buffer, files } = await packFiles(args.files, { maxBytes: MAX_TOTAL_BYTES });
|
|
733
|
+
const r = await session.roxyon.sites.deploy(args.host, folder, buffer, {
|
|
734
|
+
clean: args.clean,
|
|
735
|
+
spa: args.spa
|
|
736
|
+
});
|
|
737
|
+
const size = r.bytes ? ` (${r.bytes / 1024 | 0} KB)` : "";
|
|
738
|
+
const spaNote = args.spa ? "\nSPA routing enabled \u2014 deep links resolve to index.html." : "";
|
|
739
|
+
return text(
|
|
740
|
+
`\u2713 Deployed ${files.length} file(s) to https://${args.host}${folder ? `/${folder}` : ""}${size}.${spaNote}`,
|
|
741
|
+
{ ok: true, host: args.host, files: files.length }
|
|
742
|
+
);
|
|
743
|
+
})
|
|
744
|
+
);
|
|
745
|
+
server.registerTool(
|
|
746
|
+
"roxyon_list_files",
|
|
747
|
+
{
|
|
748
|
+
title: "Roxyon: list a site\u2019s files",
|
|
749
|
+
description: "The files currently deployed to a host's document root.",
|
|
750
|
+
inputSchema: {
|
|
751
|
+
host: z2.string(),
|
|
752
|
+
folder: z2.string().optional(),
|
|
753
|
+
path: z2.string().optional().describe("List under this sub-path only.")
|
|
754
|
+
}
|
|
755
|
+
},
|
|
756
|
+
(args) => guard(async () => {
|
|
757
|
+
const session = await getSession();
|
|
758
|
+
const files = await session.roxyon.sites.listFiles(
|
|
759
|
+
args.host,
|
|
760
|
+
args.folder ?? "",
|
|
761
|
+
args.path ?? ""
|
|
762
|
+
);
|
|
763
|
+
const body = files.length ? files.map(
|
|
764
|
+
(f) => `${f.type === "dir" ? "d" : "-"} ${String(f.size).padStart(8)} ${f.path}`
|
|
765
|
+
).join("\n") : "(empty)";
|
|
766
|
+
return text(body, { files });
|
|
767
|
+
})
|
|
768
|
+
);
|
|
769
|
+
server.registerTool(
|
|
770
|
+
"roxyon_read_file",
|
|
771
|
+
{
|
|
772
|
+
title: "Roxyon: read a site file",
|
|
773
|
+
description: "Read one file from a host's document root (to review or edit it).",
|
|
774
|
+
inputSchema: {
|
|
775
|
+
host: z2.string(),
|
|
776
|
+
folder: z2.string().optional(),
|
|
777
|
+
path: z2.string().describe("Relative path, e.g. index.html")
|
|
778
|
+
}
|
|
779
|
+
},
|
|
780
|
+
(args) => guard(async () => {
|
|
781
|
+
const session = await getSession();
|
|
782
|
+
const f = await session.roxyon.sites.readFile(args.host, args.folder ?? "", args.path);
|
|
783
|
+
if (f.encoding === "base64") {
|
|
784
|
+
return text(
|
|
785
|
+
`${f.path} \u2014 ${f.size} bytes, binary (base64):
|
|
786
|
+
|
|
787
|
+
${f.content.slice(0, 4096)}`,
|
|
788
|
+
{ path: f.path, size: f.size, encoding: "base64" }
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
return text(f.content, { path: f.path, size: f.size, encoding: "utf8" });
|
|
792
|
+
})
|
|
793
|
+
);
|
|
595
794
|
}
|
|
596
795
|
|
|
597
796
|
// src/server.ts
|
|
@@ -616,4 +815,4 @@ export {
|
|
|
616
815
|
SERVER_VERSION,
|
|
617
816
|
createServer
|
|
618
817
|
};
|
|
619
|
-
//# sourceMappingURL=chunk-
|
|
818
|
+
//# sourceMappingURL=chunk-U6ILIHUR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server.ts","../src/prompts.ts","../src/resources.ts","../src/tools.ts","../src/context.ts","../src/session.ts","../src/result.ts"],"sourcesContent":["import { createRequire } from 'node:module';\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { registerPrompts } from './prompts.js';\nimport { registerResources } from './resources.js';\nimport { registerTools } from './tools.js';\n\nexport const SERVER_NAME = 'roxyon';\nexport const SERVER_VERSION: string = (\n createRequire(import.meta.url)('../package.json') as { version: string }\n).version;\n\n/** Build the Roxyon MCP server with every tool, resource and prompt registered. */\nexport function createServer(): McpServer {\n const server = new McpServer(\n { name: SERVER_NAME, version: SERVER_VERSION },\n {\n instructions:\n 'Roxyon lets you build LumenJS SPAs, use the Roxyon BaaS, and deploy apps and web ' +\n 'projects to Roxyon infrastructure. Read the roxyon://docs/* resources before writing ' +\n 'LumenJS or calling the BaaS. To deploy: roxyon_init once per project, then roxyon_deploy ' +\n '(dry run, then confirm:true). Side-effecting tools require confirm:true.',\n },\n );\n\n registerResources(server);\n registerPrompts(server);\n registerTools(server);\n return server;\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\n\nexport function registerPrompts(server: McpServer): void {\n server.registerPrompt(\n 'scaffold-lumen-app',\n {\n title: 'Scaffold a LumenJS + Roxyon app',\n description:\n 'Guidance for creating a new LumenJS SPA wired to the Roxyon BaaS and ready to deploy.',\n argsSchema: {\n idea: z.string().describe('What the app should do.'),\n dir: z.string().optional().describe('Where to create it.'),\n },\n },\n ({ idea, dir }) => ({\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: `Build a LumenJS single-page app: ${idea}\\n\\nSteps:\\n1. Read the \"roxyon://docs/lumenjs\" resource — follow V1 rules exactly (one bind per region, no nested bind, build option lists in JS not :for, check every BaaS write for an error field).\\n2. Read \"roxyon://docs/baas\" for the data/auth API.\\n3. Scaffold with the LumenJS CLI: \\`lm create <name>\\`${dir ? ` in ${dir}` : ''}. Put views under src/views/, wire the BaaS in src/index.js via config.json.\\n4. When it runs locally (\\`lm serve\\`), call roxyon_init then roxyon_deploy (dry run first, then confirm:true).`,\n },\n },\n ],\n }),\n );\n\n server.registerPrompt(\n 'build-and-ship-site',\n {\n title: 'Build a site and host it on Roxyon',\n description:\n 'Generate a static site (or no-build LumenJS SPA) in the conversation and publish it to ' +\n 'a Roxyon host — for the hosted connector, no local files.',\n argsSchema: {\n brief: z.string().describe('What the site should be.'),\n host: z.string().describe('Where to host it, e.g. promo.mycompany.com'),\n },\n },\n ({ brief, host }) => ({\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: `Build and publish a website to ${host}.\\n\\nBrief: ${brief}\\n\\n1. Read \"roxyon://docs/recipe\" and follow it. (Read \"roxyon://docs/lumenjs\" too only if the site needs reactivity.)\\n2. If ${host} is not already on the account, roxyon_add_domain { host: \"${host}\", confirm: true } (add spa:true only for a real single-page app).\\n3. Generate the site — self-contained static files, relative paths, real .html per page or <page>/index.html, ≤60 files / 2 MB.\\n4. roxyon_deploy_content { host: \"${host}\", files: [...], clean: true, confirm: true } (dry-run first).\\n5. roxyon_list_files { host: \"${host}\" } to verify, then give me the URL. Note that HTTPS may take a couple of minutes on a brand-new host.`,\n },\n },\n ],\n }),\n );\n\n server.registerPrompt(\n 'deploy-to-roxyon',\n {\n title: 'Deploy this project to Roxyon',\n description: 'Walk through deploying the current project to Roxyon infrastructure.',\n argsSchema: { dir: z.string().describe('Absolute path to the project.') },\n },\n ({ dir }) => ({\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: `Deploy the project at ${dir} to Roxyon.\\n\\n1. If there is no roxyon.json, call roxyon_init (it detects the runtime; ask me which host if there are several).\\n2. Call roxyon_deploy with just the dir first to see the plan.\\n3. If the plan looks right, call roxyon_deploy again with confirm:true.\\n4. For an app runtime, report the final status and, if it failed, the last error from roxyon_app_status.`,\n },\n },\n ],\n }),\n );\n}\n","import { readFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\n\nconst RESOURCE_DIR = join(dirname(fileURLToPath(import.meta.url)), '..', 'resources');\n\ninterface Doc {\n uri: string;\n name: string;\n file: string;\n title: string;\n description: string;\n}\n\nconst DOCS: Doc[] = [\n {\n uri: 'roxyon://docs/lumenjs',\n name: 'lumenjs-reference',\n file: 'lumenjs.md',\n title: 'LumenJS — complete reference',\n description:\n 'Full LumenJS V1 reference for building SPAs with vanilla JS/HTML/CSS (no build step), ' +\n 'contrasted with Vue/React. Read before writing .view files.',\n },\n {\n uri: 'roxyon://docs/baas',\n name: 'baas-reference',\n file: 'baas.md',\n title: 'Roxyon BaaS reference',\n description:\n 'The Roxyon Backend-as-a-Service REST API: endpoints, auth flow, query rules, and the ' +\n 'resolve-on-failure write behaviour.',\n },\n {\n uri: 'roxyon://docs/deploy',\n name: 'deploy-reference',\n file: 'deploy.md',\n title: 'Deploying to Roxyon',\n description:\n 'How roxyon_init / roxyon_deploy work, the roxyon.json schema, static vs app deploys, env ' +\n 'vars, and git push-to-deploy.',\n },\n {\n uri: 'roxyon://docs/recipe',\n name: 'web-deploy-recipe',\n file: 'recipe-web-deploy.md',\n title: 'Recipe — build a site and host it',\n description:\n 'The end-to-end flow for \"build a landing page and put it on sub.mydomain.com\" over the ' +\n 'hosted connector: roxyon_add_domain → roxyon_deploy_content → roxyon_list_files.',\n },\n];\n\nexport function registerResources(server: McpServer): void {\n for (const doc of DOCS) {\n server.registerResource(\n doc.name,\n doc.uri,\n { title: doc.title, description: doc.description, mimeType: 'text/markdown' },\n async () => {\n const body = await readFile(join(RESOURCE_DIR, doc.file), 'utf8');\n return { contents: [{ uri: doc.uri, mimeType: 'text/markdown', text: body }] };\n },\n );\n }\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { type Roxyon, envFromStored, formatEnv } from '@roxyon/api-client';\nimport {\n buildIgnore,\n buildProjectConfig,\n deployProject,\n detectRuntime,\n listFiles,\n loadProjectConfig,\n packFiles,\n saveProjectConfig,\n} from '@roxyon/deploy-core';\nimport { z } from 'zod';\nimport { currentContext } from './context.js';\nimport { type ToolResult, errorResult, guard, text } from './result.js';\nimport { type RoxyonSession, getSession } from './session.js';\n\n/** `roxyon_deploy_content` budget — keeps the whole JSON-RPC body under the 4 MB cap. */\nconst MAX_FILES = 60;\nconst MAX_FILE_BYTES = 1024 * 1024;\nconst MAX_TOTAL_BYTES = 2 * 1024 * 1024;\n\n/**\n * `roxyon_init` / `roxyon_deploy` read and write the caller's local project\n * directory — impossible over the remote HTTP server. Returns an explanatory\n * result when running in that mode, `null` otherwise (proceed).\n */\nfunction remoteFilesystemBlock(tool: 'roxyon_init' | 'roxyon_deploy'): ToolResult | null {\n if (!currentContext().remote) return null;\n return errorResult(\n [\n `${tool} needs access to your local project files, which the hosted Roxyon MCP`,\n 'server does not have. Either:',\n ' • run the `roxyon` CLI locally (`npm i -g @roxyon/cli`, then `roxyon deploy`), or',\n ' • use `roxyon_link_github` to connect a git repo for push-to-deploy, then',\n ' `roxyon_app_status` / `roxyon_logs` to watch it here.',\n ].join('\\n'),\n );\n}\n\n/** Resolve an application id from an explicit id or a project directory. */\nasync function resolveApp(args: { application?: string; dir?: string }): Promise<{\n applicationId: string;\n host?: string;\n}> {\n if (args.application) return { applicationId: args.application };\n if (args.dir) {\n const cfg = await loadProjectConfig(args.dir);\n if (cfg?.application) return { applicationId: cfg.application, host: cfg.host };\n throw new Error(\n `No application linked in ${args.dir}/roxyon.json. Deploy it once (roxyon_deploy) or pass \"application\".`,\n );\n }\n throw new Error('Pass either \"application\" (an id) or \"dir\" (a project directory).');\n}\n\n/** The account context (user + subscriptions + domains) — one call, PAT-safe. */\nfunction accountContext(session: RoxyonSession) {\n return session.roxyon.account.context();\n}\n\nexport function registerTools(server: McpServer): void {\n // -----------------------------------------------------------------------\n server.registerTool(\n 'roxyon_whoami',\n {\n title: 'Roxyon: who am I',\n description: 'Show the signed-in Roxyon user and their subscriptions (which one is active).',\n inputSchema: {},\n },\n () =>\n guard(async () => {\n const session = await getSession();\n const ctx = await accountContext(session);\n const lines = [\n `User: ${ctx.user.email || ctx.user.id} (${ctx.user.id})`,\n `Auth: ${session.source}${ctx.scopes.length ? ` (scopes: ${ctx.scopes.join(', ')})` : ''}`,\n '',\n 'Subscriptions:',\n ...ctx.subscriptions.map((s) => ` ${s.name || s.id} — ${s.status || '?'}`),\n '',\n `Hosts: ${ctx.domains.map((d) => d.name).join(', ') || '(none)'}`,\n ];\n return text(lines.join('\\n'), {\n user: ctx.user,\n scopes: ctx.scopes,\n subscriptions: ctx.subscriptions,\n hosts: ctx.domains.map((d) => d.name),\n });\n }),\n );\n\n // -----------------------------------------------------------------------\n server.registerTool(\n 'roxyon_list_domains',\n {\n title: 'Roxyon: list hosts',\n description:\n 'List the domains (hosts) on the active subscription — the possible deploy targets.',\n inputSchema: {},\n },\n () =>\n guard(async () => {\n const session = await getSession();\n const { domains } = await accountContext(session);\n return text(\n domains.length\n ? domains\n .map(\n (d) => `- ${d.name}${d.status && d.status !== 'active' ? ` (${d.status})` : ''}`,\n )\n .join('\\n')\n : '(no hosts on this account)',\n { hosts: domains },\n );\n }),\n );\n\n // -----------------------------------------------------------------------\n server.registerTool(\n 'roxyon_list_apps',\n {\n title: 'Roxyon: list applications',\n description:\n 'List the applications on the active subscription with their status and revision.',\n inputSchema: {},\n },\n () =>\n guard(async () => {\n const session = await getSession();\n const apps = await session.roxyon.account.apps();\n const body = apps.length\n ? apps\n .map(\n (a) =>\n `- ${a.name} [${a.id}] — ${a.status || '?'} ` +\n `(rev ${a.appliedRevision}/${a.configRevision})` +\n `${a.repo ? ` · git:${a.repo.branch}` : ''}`,\n )\n .join('\\n')\n : '(no applications)';\n return text(body, { applications: apps });\n }),\n );\n\n // -----------------------------------------------------------------------\n server.registerTool(\n 'roxyon_init',\n {\n title: 'Roxyon: initialise a project',\n description:\n \"Detect a project's type and write roxyon.json. Run this once per project before roxyon_deploy. \" +\n 'Does not create anything on the platform.',\n inputSchema: {\n dir: z.string().describe('Absolute path to the project directory.'),\n runtime: z.enum(['lumen', 'node', 'python', 'php']).optional(),\n host: z.string().optional().describe('Deploy host (a domain on the subscription).'),\n folder: z.string().optional().describe('Sub-path under the host; \"\" = site root.'),\n overwrite: z.boolean().optional().describe('Replace an existing roxyon.json.'),\n },\n },\n (args) =>\n guard(async () => {\n const blocked = remoteFilesystemBlock('roxyon_init');\n if (blocked) return blocked;\n const session = await getSession();\n const existing = await loadProjectConfig(args.dir);\n if (existing && !args.overwrite) {\n return errorResult(\n `roxyon.json already exists in ${args.dir}. Pass overwrite:true to replace it.`,\n { config: existing },\n );\n }\n const { domains } = await accountContext(session);\n if (domains.length === 0) {\n return errorResult(\n 'This account has no hosts. Add a domain in the Roxyon console first.',\n );\n }\n const detected = await detectRuntime(args.dir);\n const runtime = args.runtime ?? detected.runtime;\n const host = args.host ?? domains[0]!.name;\n if (!domains.some((d) => d.name === host)) {\n return errorResult(\n `Host \"${host}\" is not on this account (have: ${domains.map((d) => d.name).join(', ')}).`,\n );\n }\n const config = buildProjectConfig({\n cwd: args.dir,\n runtime,\n host,\n folder: args.folder,\n start: detected.start,\n });\n const path = await saveProjectConfig(config, args.dir);\n return text(\n `Wrote ${path}\\nDetected: ${detected.runtime} (${detected.reason})\\n\\n${JSON.stringify(config, null, 2)}`,\n { path, detected: detected.runtime, config },\n );\n }),\n );\n\n // -----------------------------------------------------------------------\n server.registerTool(\n 'roxyon_deploy',\n {\n title: 'Roxyon: deploy a project',\n description:\n 'Build (if configured) and deploy the project in \"dir\" to Roxyon. Without confirm:true this ' +\n 'returns a dry-run plan (what would build, where it would go, how many files). With confirm:true ' +\n 'it runs the build, uploads, and — for app runtimes — waits for it to go live. Requires roxyon.json ' +\n '(run roxyon_init first).',\n inputSchema: {\n dir: z\n .string()\n .describe('Absolute path to the project directory (must contain roxyon.json).'),\n confirm: z.boolean().optional().describe('Actually deploy. Omit for a dry-run plan.'),\n build: z.boolean().optional().describe('Run the configured build command (default true).'),\n follow: z\n .boolean()\n .optional()\n .describe('For app runtimes, wait until running/failed (default true).'),\n },\n },\n (args) =>\n guard(async () => {\n const blocked = remoteFilesystemBlock('roxyon_deploy');\n if (blocked) return blocked;\n const session = await getSession();\n const config = await loadProjectConfig(args.dir);\n if (!config) {\n return errorResult(`No roxyon.json in ${args.dir}. Run roxyon_init first.`);\n }\n\n if (!args.confirm) {\n const ig = await buildIgnore(args.dir);\n const packRoot =\n config.kind === 'static' && config.outDir ? `${args.dir}/${config.outDir}` : args.dir;\n let files: string[] = [];\n try {\n files = await listFiles(packRoot, ig);\n } catch {\n /* outDir may not exist until the build runs */\n }\n const plan = [\n 'DRY RUN — pass confirm:true to deploy.',\n '',\n `Project: ${config.name} (${config.runtime}, ${config.kind})`,\n `Target: ${config.host}${config.folder ? `/${config.folder}` : ''}`,\n config.build ? `Build: ${config.build}` : 'Build: (none)',\n config.kind === 'app'\n ? `App: ${config.application ?? '(created on first deploy)'}`\n : 'Serve: static files from the host document root',\n files.length\n ? `Would pack ${files.length} file(s) from ${config.kind === 'static' && config.outDir ? config.outDir : '.'}`\n : `Would pack the build output (${config.outDir ?? 'dist'}) after building`,\n ].join('\\n');\n return text(plan, { dryRun: true, config });\n }\n\n const logLines: string[] = [];\n const outcome = await deployProject({\n cwd: args.dir,\n roxyon: session.roxyon,\n preferredSubscription: session.preferredSubscription,\n build: args.build,\n follow: args.follow ?? true,\n timeoutMs: 5 * 60 * 1000,\n reporter: {\n step: (m) => logLines.push(`» ${m}`),\n log: (l) => logLines.push(` ${l}`),\n },\n });\n\n const tail = logLines.slice(-60).join('\\n');\n if (outcome.kind === 'static' && outcome.ok) {\n return text(`${tail}\\n\\n✓ Live: ${outcome.url} (${outcome.files} files)`, {\n ok: true,\n kind: 'static',\n url: outcome.url,\n });\n }\n if (outcome.kind === 'app' && outcome.ok) {\n return text(\n `${tail}\\n\\n✓ ${outcome.status === 'running' ? `Live: ${outcome.url ?? outcome.application}` : `Build queued (revision ${outcome.revision})`}`,\n {\n ok: true,\n kind: 'app',\n application: outcome.application,\n status: outcome.status,\n url: outcome.url,\n },\n );\n }\n return errorResult(\n `${tail}\\n\\n✗ Deploy failed: ${'error' in outcome ? outcome.error : 'unknown'}`,\n { ok: false, application: 'application' in outcome ? outcome.application : undefined },\n );\n }),\n );\n\n // -----------------------------------------------------------------------\n server.registerTool(\n 'roxyon_app_status',\n {\n title: 'Roxyon: application status',\n description:\n 'Current status, revisions and last error for an application (by id or project dir).',\n inputSchema: {\n application: z.string().optional(),\n dir: z\n .string()\n .optional()\n .describe('A project directory whose roxyon.json links an application.'),\n },\n },\n (args) =>\n guard(async () => {\n const session = await getSession();\n const { applicationId } = await resolveApp(args);\n const app = await session.roxyon.account.getApp(applicationId);\n if (!app) return errorResult(`Application ${applicationId} not found.`);\n const settled = app.configRevision <= app.appliedRevision;\n const body = [\n `${app.name} [${app.id}]`,\n `Status: ${app.status || '?'}${settled ? '' : ' (applying)'}`,\n `Revision: ${app.appliedRevision} / ${app.configRevision}`,\n `Runtime: ${app.runtime}`.trim(),\n app.lastError ? `\\nLast error:\\n${app.lastError}` : '',\n ]\n .filter(Boolean)\n .join('\\n');\n return text(body, {\n id: app.id,\n status: app.status,\n settled,\n configRevision: app.configRevision,\n appliedRevision: app.appliedRevision,\n lastError: app.lastError || undefined,\n });\n }),\n );\n\n // -----------------------------------------------------------------------\n server.registerTool(\n 'roxyon_logs',\n {\n title: 'Roxyon: application logs',\n description: 'Recent journal lines for an application (by id or project dir).',\n inputSchema: {\n application: z.string().optional(),\n dir: z.string().optional(),\n lines: z\n .number()\n .int()\n .min(1)\n .max(1000)\n .optional()\n .describe('How many lines (default 100).'),\n },\n },\n (args) =>\n guard(async () => {\n const session = await getSession();\n const { applicationId } = await resolveApp(args);\n const lines = await session.roxyon.applications.logs(applicationId, args.lines ?? 100);\n return text(lines.join('\\n') || '(no log lines)', { lines });\n }),\n );\n\n // -----------------------------------------------------------------------\n server.registerTool(\n 'roxyon_restart',\n {\n title: 'Roxyon: restart an application',\n description: 'Bounce the application process (no rebuild). Needs confirm:true.',\n inputSchema: {\n application: z.string().optional(),\n dir: z.string().optional(),\n confirm: z.boolean().optional(),\n },\n },\n (args) =>\n guard(async () => {\n const session = await getSession();\n const { applicationId } = await resolveApp(args);\n if (!args.confirm)\n return text(`Would restart ${applicationId}. Pass confirm:true.`, { dryRun: true });\n await session.roxyon.applications.restart(applicationId);\n return text(`Restart queued for ${applicationId}.`, { ok: true });\n }),\n );\n\n // -----------------------------------------------------------------------\n server.registerTool(\n 'roxyon_env_get',\n {\n title: 'Roxyon: read environment variables',\n description: \"An application's environment variables as KEY=value lines.\",\n inputSchema: { application: z.string().optional(), dir: z.string().optional() },\n },\n (args) =>\n guard(async () => {\n const session = await getSession();\n const { applicationId } = await resolveApp(args);\n const env = await session.roxyon.applications.getEnv(applicationId);\n return text(formatEnv(env) || '(none set)', { env });\n }),\n );\n\n // -----------------------------------------------------------------------\n server.registerTool(\n 'roxyon_env_set',\n {\n title: 'Roxyon: set environment variables',\n description:\n \"Merge variables into an application's environment and bump its revision. Run roxyon_deploy \" +\n 'afterwards to apply. Needs confirm:true. PORT and HOST are platform-managed and ignored.',\n inputSchema: {\n application: z.string().optional(),\n dir: z.string().optional(),\n vars: z.record(z.string()).describe('{ KEY: \"value\", ... } to set.'),\n remove: z.array(z.string()).optional().describe('Keys to delete.'),\n confirm: z.boolean().optional(),\n },\n },\n (args) =>\n guard(async () => {\n const session = await getSession();\n const { applicationId } = await resolveApp(args);\n const set: Record<string, string> = {};\n for (const [k, v] of Object.entries(args.vars)) {\n if (k === 'PORT' || k === 'HOST') continue;\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(k)) return errorResult(`Invalid env key \"${k}\".`);\n set[k] = v;\n }\n const planned = [...Object.keys(set), ...(args.remove ?? []).map((k) => `-${k}`)];\n if (!args.confirm) {\n return text(`Would update: ${planned.join(', ') || '(nothing)'}. Pass confirm:true.`, {\n dryRun: true,\n changed: planned,\n });\n }\n const r = await session.roxyon.applications.setEnv(applicationId, {\n set,\n remove: args.remove ?? [],\n });\n return text(`Updated ${r.changed.join(', ') || '(nothing)'}. Run roxyon_deploy to apply.`, {\n ok: true,\n changed: r.changed,\n });\n }),\n );\n\n // -----------------------------------------------------------------------\n server.registerTool(\n 'roxyon_link_github',\n {\n title: 'Roxyon: connect a git remote',\n description:\n 'Connect a git repository to an application for push-to-deploy. Returns the deploy key and ' +\n 'webhook URL to add to the repo. Needs confirm:true.',\n inputSchema: {\n application: z.string().optional(),\n dir: z.string().optional(),\n repoUrl: z.string().describe('SSH git URL, e.g. git@github.com:you/app.git'),\n branch: z.string().optional().describe('Branch to deploy (default main).'),\n confirm: z.boolean().optional(),\n },\n },\n (args) =>\n guard(async () => {\n const session = await getSession();\n const { applicationId } = await resolveApp(args);\n if (!args.confirm) {\n return text(\n `Would connect ${args.repoUrl} (${args.branch ?? 'main'}) to ${applicationId}. Pass confirm:true.`,\n { dryRun: true },\n );\n }\n const res = await session.roxyon.applications.repoConnect(\n applicationId,\n args.repoUrl,\n args.branch ?? 'main',\n );\n const body = [\n `Connected ${args.repoUrl} (${args.branch ?? 'main'}).`,\n res.deployKey ? `\\nDeploy key (add to the repo, read-only):\\n${res.deployKey}` : '',\n res.webhookUrl ? `\\nWebhook URL: ${res.webhookUrl}` : '',\n res.secret ? `Webhook secret: ${res.secret}` : '',\n res.hint ? `\\n${res.hint}` : '',\n ]\n .filter(Boolean)\n .join('\\n');\n return text(body, {\n ok: true,\n deployKey: res.deployKey,\n webhookUrl: res.webhookUrl,\n secret: res.secret,\n });\n }),\n );\n\n // -----------------------------------------------------------------------\n server.registerTool(\n 'roxyon_add_domain',\n {\n title: 'Roxyon: add a subdomain / host',\n description:\n 'Provision a host — DNS + web server + automatic HTTPS. The host must be a subdomain ' +\n 'of a domain the account already hosts (or a *.roxyon.com subdomain). Returns while it ' +\n 'is still coming up; TLS follows a minute or two later. Needs confirm:true.',\n inputSchema: {\n host: z.string().describe('The full hostname, e.g. promo.mycompany.com'),\n subscription: z\n .string()\n .optional()\n .describe('Which subscription to attach it to (omit if the account has one).'),\n spa: z.boolean().optional().describe('Single-page app: unmatched paths serve /index.html.'),\n confirm: z.boolean().optional(),\n },\n },\n (args) =>\n guard(async () => {\n const session = await getSession();\n if (!args.confirm) {\n return text(\n `Would provision ${args.host}${args.spa ? ' (SPA routing)' : ''}. Pass confirm:true.`,\n { dryRun: true },\n );\n }\n const res = await session.roxyon.domains.create({\n host: args.host,\n subscription: args.subscription ?? session.preferredSubscription,\n siteType: args.spa ? 'spa' : undefined,\n });\n const live = res.status === 'active';\n return text(\n [\n `${res.host} — ${res.type}, ${res.status}.`,\n live\n ? 'The host is up. HTTPS may take another minute to issue.'\n : 'DNS + web server are being set up (~1–2 min), then HTTPS. ' +\n 'Check roxyon_list_domains, or just deploy now — files are kept and served once it is live.',\n ].join('\\n'),\n { ok: true, host: res.host, type: res.type, status: res.status },\n );\n }),\n );\n\n // -----------------------------------------------------------------------\n server.registerTool(\n 'roxyon_deploy_content',\n {\n title: 'Roxyon: deploy generated files',\n description: [\n 'Publish a set of files you generated (HTML/CSS/JS/assets) to a host on the account.',\n 'The host must already exist (roxyon_add_domain, or an existing site). Overlay by',\n 'default — pass clean:true to replace the whole document root. Needs confirm:true.',\n `Limits: ${MAX_FILES} files, ${(MAX_FILE_BYTES / 1024) | 0} KB per file,`,\n `${(MAX_TOTAL_BYTES / 1024 / 1024).toFixed(1)} MB total (base64-encode binaries).`,\n ].join(' '),\n inputSchema: {\n host: z.string().describe('Target host, e.g. promo.mycompany.com'),\n folder: z.string().optional().describe('Sub-path under the host; \"\" = site root.'),\n files: z\n .array(\n z.object({\n path: z.string().describe('Relative path, e.g. index.html, about/index.html'),\n content: z.string(),\n encoding: z.enum(['utf8', 'base64']).optional(),\n }),\n )\n .min(1),\n clean: z.boolean().optional().describe('Replace the whole docroot (keeps .well-known).'),\n spa: z.boolean().optional().describe('Also flip the host to SPA routing.'),\n confirm: z.boolean().optional(),\n },\n },\n (args) =>\n guard(async () => {\n const session = await getSession();\n if (args.files.length > MAX_FILES) {\n return errorResult(`Too many files (${args.files.length}); the limit is ${MAX_FILES}.`);\n }\n let total = 0;\n for (const f of args.files) {\n const n = Buffer.byteLength(f.content, f.encoding === 'base64' ? 'base64' : 'utf8');\n if (n > MAX_FILE_BYTES) {\n return errorResult(\n `\"${f.path}\" is ${(n / 1024) | 0} KB; the per-file limit is ${(MAX_FILE_BYTES / 1024) | 0} KB.`,\n );\n }\n total += n;\n }\n if (total > MAX_TOTAL_BYTES) {\n return errorResult(\n `Total content is ${(total / 1024 / 1024).toFixed(1)} MB; the limit is ` +\n `${(MAX_TOTAL_BYTES / 1024 / 1024).toFixed(1)} MB. Deploy in parts or use the CLI.`,\n );\n }\n\n const folder = args.folder ?? '';\n if (!args.confirm) {\n return text(\n [\n 'DRY RUN — pass confirm:true to deploy.',\n '',\n `Host: ${args.host}${folder ? `/${folder}` : ''}`,\n `Files: ${args.files.length} (${(total / 1024) | 0} KB)`,\n `Mode: ${args.clean ? 'clean (replaces the document root)' : 'overlay'}${args.spa ? ' + SPA routing' : ''}`,\n ...args.files.slice(0, 20).map((f) => ` ${f.path}`),\n args.files.length > 20 ? ` … ${args.files.length - 20} more` : '',\n ]\n .filter(Boolean)\n .join('\\n'),\n { dryRun: true },\n );\n }\n\n const { buffer, files } = await packFiles(args.files, { maxBytes: MAX_TOTAL_BYTES });\n const r = await session.roxyon.sites.deploy(args.host, folder, buffer, {\n clean: args.clean,\n spa: args.spa,\n });\n const size = r.bytes ? ` (${(r.bytes / 1024) | 0} KB)` : '';\n const spaNote = args.spa ? '\\nSPA routing enabled — deep links resolve to index.html.' : '';\n return text(\n `✓ Deployed ${files.length} file(s) to https://${args.host}${folder ? `/${folder}` : ''}${size}.${spaNote}`,\n { ok: true, host: args.host, files: files.length },\n );\n }),\n );\n\n // -----------------------------------------------------------------------\n server.registerTool(\n 'roxyon_list_files',\n {\n title: 'Roxyon: list a site’s files',\n description: \"The files currently deployed to a host's document root.\",\n inputSchema: {\n host: z.string(),\n folder: z.string().optional(),\n path: z.string().optional().describe('List under this sub-path only.'),\n },\n },\n (args) =>\n guard(async () => {\n const session = await getSession();\n const files = await session.roxyon.sites.listFiles(\n args.host,\n args.folder ?? '',\n args.path ?? '',\n );\n const body = files.length\n ? files\n .map(\n (f) => `${f.type === 'dir' ? 'd' : '-'} ${String(f.size).padStart(8)} ${f.path}`,\n )\n .join('\\n')\n : '(empty)';\n return text(body, { files });\n }),\n );\n\n // -----------------------------------------------------------------------\n server.registerTool(\n 'roxyon_read_file',\n {\n title: 'Roxyon: read a site file',\n description: \"Read one file from a host's document root (to review or edit it).\",\n inputSchema: {\n host: z.string(),\n folder: z.string().optional(),\n path: z.string().describe('Relative path, e.g. index.html'),\n },\n },\n (args) =>\n guard(async () => {\n const session = await getSession();\n const f = await session.roxyon.sites.readFile(args.host, args.folder ?? '', args.path);\n if (f.encoding === 'base64') {\n return text(\n `${f.path} — ${f.size} bytes, binary (base64):\\n\\n${f.content.slice(0, 4096)}`,\n { path: f.path, size: f.size, encoding: 'base64' },\n );\n }\n return text(f.content, { path: f.path, size: f.size, encoding: 'utf8' });\n }),\n );\n}\n\n// Re-exported for tests.\nexport { resolveApp };\nexport type { Roxyon, ToolResult };\n","import { AsyncLocalStorage } from 'node:async_hooks';\n\n/**\n * Per-request state for the HTTP transport. The stdio entry never opens a\n * context, so {@link currentContext} returns `{}` there and everything falls\n * back to the env / stored-login behaviour.\n */\nexport interface RequestContext {\n /** Bearer token presented on this HTTP request (from the OAuth layer). */\n token?: string;\n /**\n * True when serving the remote HTTP server. Tools that need the caller's\n * local filesystem (`roxyon_init`, `roxyon_deploy`) refuse in this mode.\n */\n remote?: boolean;\n}\n\nconst storage = new AsyncLocalStorage<RequestContext>();\n\n/** Run `fn` with `ctx` visible to {@link currentContext} for its whole async tree. */\nexport function runWithContext<T>(ctx: RequestContext, fn: () => T): T {\n return storage.run(ctx, fn);\n}\n\n/** The context for the in-flight request, or `{}` on the stdio transport. */\nexport function currentContext(): RequestContext {\n return storage.getStore() ?? {};\n}\n","import { Roxyon, isAuthError } from '@roxyon/api-client';\nimport { endpointsFromEnv, loadCredentials, tokenFromEnv } from '@roxyon/deploy-core';\nimport { currentContext } from './context.js';\n\nexport interface RoxyonSession {\n roxyon: Roxyon;\n preferredSubscription?: string;\n /** how the session was authenticated, for diagnostics */\n source: 'env-token' | 'stored-login' | 'oauth';\n}\n\nexport class NotAuthenticatedError extends Error {\n constructor() {\n super(\n 'Not signed in to Roxyon. Run `roxyon login` in a terminal, or set ROXYON_TOKEN ' +\n \"in this MCP server's environment.\",\n );\n this.name = 'NotAuthenticatedError';\n }\n}\n\n/**\n * Resolve a Roxyon client for a tool call. Order: `ROXYON_TOKEN` env var, then\n * the login stored by the CLI at `~/.roxyon/config.json`.\n */\nexport async function getSession(): Promise<RoxyonSession> {\n const { apiUrl, consoleUrl } = endpointsFromEnv();\n\n // HTTP transport: the bearer token from this request wins over any ambient\n // env/stored credentials, and each request gets its own client.\n const requestToken = currentContext().token;\n if (requestToken) {\n return {\n roxyon: new Roxyon({ sessionToken: requestToken, baseUrl: apiUrl, consoleUrl }),\n source: 'oauth',\n };\n }\n\n const envToken = tokenFromEnv();\n if (envToken) {\n return {\n roxyon: new Roxyon({ sessionToken: envToken, baseUrl: apiUrl, consoleUrl }),\n source: 'env-token',\n };\n }\n const creds = await loadCredentials();\n if (!creds?.sessionToken) throw new NotAuthenticatedError();\n return {\n roxyon: new Roxyon({ sessionToken: creds.sessionToken, baseUrl: apiUrl, consoleUrl }),\n preferredSubscription: creds.subscription,\n source: 'stored-login',\n };\n}\n\nexport function isSessionGone(err: unknown): boolean {\n return err instanceof NotAuthenticatedError || isAuthError(err);\n}\n","import { isSessionGone } from './session.js';\n\nexport interface ToolResult {\n content: Array<{ type: 'text'; text: string }>;\n structuredContent?: Record<string, unknown>;\n isError?: boolean;\n /** SDK's CallToolResult carries an open index signature. */\n [k: string]: unknown;\n}\n\nexport function text(body: string, structured?: Record<string, unknown>): ToolResult {\n return {\n content: [{ type: 'text', text: body }],\n ...(structured ? { structuredContent: structured } : {}),\n };\n}\n\nexport function errorResult(message: string, structured?: Record<string, unknown>): ToolResult {\n return {\n content: [{ type: 'text', text: message }],\n ...(structured ? { structuredContent: structured } : {}),\n isError: true,\n };\n}\n\n/** Wrap a tool body so thrown errors become clean `isError` results. */\nexport async function guard(fn: () => Promise<ToolResult>): Promise<ToolResult> {\n try {\n return await fn();\n } catch (err) {\n if (isSessionGone(err)) {\n return errorResult(\n (err as Error).message ||\n 'Roxyon session expired — run `roxyon login` again, or refresh ROXYON_TOKEN.',\n );\n }\n return errorResult(err instanceof Error ? err.message : String(err));\n }\n}\n"],"mappings":"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","names":["z","z"]}
|
package/dist/http-entry.js
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@roxyon/mcp",
|
|
3
3
|
"mcpName": "io.github.bea-technology/roxyon",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"description": "Model Context Protocol server for Roxyon — lets any MCP-capable AI assistant build with LumenJS, use the Roxyon BaaS, and deploy to Roxyon infrastructure.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"type": "module",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
36
36
|
"express": "^5.2.1",
|
|
37
37
|
"zod": "^3.25.0",
|
|
38
|
-
"@roxyon/api-client": "0.
|
|
39
|
-
"@roxyon/deploy-core": "0.
|
|
38
|
+
"@roxyon/api-client": "0.2.0",
|
|
39
|
+
"@roxyon/deploy-core": "0.2.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@types/express": "^5.0.0",
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Recipe — build a site and host it on Roxyon
|
|
2
|
+
|
|
3
|
+
The end-to-end flow for *"build me a landing page and put it on
|
|
4
|
+
`promo.mycompany.com`"* over the hosted connector. No local files, no CLI.
|
|
5
|
+
|
|
6
|
+
## The tools
|
|
7
|
+
|
|
8
|
+
| Tool | When |
|
|
9
|
+
|---|---|
|
|
10
|
+
| `roxyon_list_domains` | see the account's hosts + which are still `provisioning` |
|
|
11
|
+
| `roxyon_add_domain` | create a **subdomain** of a domain the account already hosts (or `*.roxyon.com`) — DNS + web server + automatic HTTPS |
|
|
12
|
+
| `roxyon_deploy_content` | publish the files you generated (HTML/CSS/JS/assets as `{path, content}` entries) |
|
|
13
|
+
| `roxyon_list_files` / `roxyon_read_file` | inspect what's live so you can iterate |
|
|
14
|
+
|
|
15
|
+
## Flow
|
|
16
|
+
|
|
17
|
+
1. **Pick / create the host.**
|
|
18
|
+
- Deploying to a domain already in the account's Sites → skip to step 2.
|
|
19
|
+
- New subdomain → `roxyon_add_domain { host: "promo.mycompany.com", confirm: true }`
|
|
20
|
+
(add `spa: true` if the site uses client-side routing). It returns fast, while
|
|
21
|
+
DNS/vhost/TLS are still coming up — that's fine, keep going.
|
|
22
|
+
2. **Generate the site.** Plain, self-contained static output:
|
|
23
|
+
- `index.html` at the root. One real `.html` per page, **or** `about/index.html`,
|
|
24
|
+
`pricing/index.html` — the default routing serves `/about/` from
|
|
25
|
+
`about/index.html`.
|
|
26
|
+
- **Relative** asset paths (`./styles.css`, `assets/logo.png`) — never absolute.
|
|
27
|
+
- Inline small CSS/JS or ship a couple of files; keep it under **60 files /
|
|
28
|
+
2 MB total / 1 MB per file**. Base64-encode images (`encoding: "base64"`).
|
|
29
|
+
- No build step. If you want reactivity, LumenJS `.view` files work with **no
|
|
30
|
+
build** — load `https://cdn.roxyon.com/libs/rxjs/1.0.0/rx.js` in `index.html`
|
|
31
|
+
and follow `roxyon://docs/lumenjs`. Only a genuine single-page app that owns
|
|
32
|
+
every route needs `spa: true` (deep links → `index.html`).
|
|
33
|
+
3. **Deploy.**
|
|
34
|
+
`roxyon_deploy_content { host, files: [{path, content}, …], clean: true, confirm: true }`
|
|
35
|
+
- `clean: true` for a fresh site — it replaces the document root (and removes
|
|
36
|
+
the placeholder "Coming Soon" page). For an edit to an existing site, run
|
|
37
|
+
`roxyon_list_files` first, then deploy **without** `clean` and include only
|
|
38
|
+
the changed files (overlay).
|
|
39
|
+
4. **Verify.** `roxyon_list_files { host }` to confirm the files landed. Then tell
|
|
40
|
+
the user the URL.
|
|
41
|
+
- **HTTPS lag:** a brand-new host serves over http within a minute; the TLS
|
|
42
|
+
certificate follows 1–3 minutes later. If `https://` fails at first, that's
|
|
43
|
+
expected — it resolves itself.
|
|
44
|
+
|
|
45
|
+
## Notes
|
|
46
|
+
|
|
47
|
+
- The connector cannot build a project from a local folder or a git repo — that's
|
|
48
|
+
the `roxyon` CLI (`npm i -g @roxyon/cli`) or `roxyon_link_github`. It *can* take
|
|
49
|
+
files you generate here.
|
|
50
|
+
- `roxyon_add_domain` only makes **subdomains** of domains the account already
|
|
51
|
+
hosts (or `roxyon.com`). Adding a brand-new top-level domain is a console task.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server.ts","../src/prompts.ts","../src/resources.ts","../src/tools.ts","../src/context.ts","../src/session.ts","../src/result.ts"],"sourcesContent":["import { createRequire } from 'node:module';\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { registerPrompts } from './prompts.js';\nimport { registerResources } from './resources.js';\nimport { registerTools } from './tools.js';\n\nexport const SERVER_NAME = 'roxyon';\nexport const SERVER_VERSION: string = (\n createRequire(import.meta.url)('../package.json') as { version: string }\n).version;\n\n/** Build the Roxyon MCP server with every tool, resource and prompt registered. */\nexport function createServer(): McpServer {\n const server = new McpServer(\n { name: SERVER_NAME, version: SERVER_VERSION },\n {\n instructions:\n 'Roxyon lets you build LumenJS SPAs, use the Roxyon BaaS, and deploy apps and web ' +\n 'projects to Roxyon infrastructure. Read the roxyon://docs/* resources before writing ' +\n 'LumenJS or calling the BaaS. To deploy: roxyon_init once per project, then roxyon_deploy ' +\n '(dry run, then confirm:true). Side-effecting tools require confirm:true.',\n },\n );\n\n registerResources(server);\n registerPrompts(server);\n registerTools(server);\n return server;\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\n\nexport function registerPrompts(server: McpServer): void {\n server.registerPrompt(\n 'scaffold-lumen-app',\n {\n title: 'Scaffold a LumenJS + Roxyon app',\n description:\n 'Guidance for creating a new LumenJS SPA wired to the Roxyon BaaS and ready to deploy.',\n argsSchema: {\n idea: z.string().describe('What the app should do.'),\n dir: z.string().optional().describe('Where to create it.'),\n },\n },\n ({ idea, dir }) => ({\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: `Build a LumenJS single-page app: ${idea}\\n\\nSteps:\\n1. Read the \"roxyon://docs/lumenjs\" resource — follow V1 rules exactly (one bind per region, no nested bind, build option lists in JS not :for, check every BaaS write for an error field).\\n2. Read \"roxyon://docs/baas\" for the data/auth API.\\n3. Scaffold with the LumenJS CLI: \\`lm create <name>\\`${dir ? ` in ${dir}` : ''}. Put views under src/views/, wire the BaaS in src/index.js via config.json.\\n4. When it runs locally (\\`lm serve\\`), call roxyon_init then roxyon_deploy (dry run first, then confirm:true).`,\n },\n },\n ],\n }),\n );\n\n server.registerPrompt(\n 'deploy-to-roxyon',\n {\n title: 'Deploy this project to Roxyon',\n description: 'Walk through deploying the current project to Roxyon infrastructure.',\n argsSchema: { dir: z.string().describe('Absolute path to the project.') },\n },\n ({ dir }) => ({\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: `Deploy the project at ${dir} to Roxyon.\\n\\n1. If there is no roxyon.json, call roxyon_init (it detects the runtime; ask me which host if there are several).\\n2. Call roxyon_deploy with just the dir first to see the plan.\\n3. If the plan looks right, call roxyon_deploy again with confirm:true.\\n4. For an app runtime, report the final status and, if it failed, the last error from roxyon_app_status.`,\n },\n },\n ],\n }),\n );\n}\n","import { readFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\n\nconst RESOURCE_DIR = join(dirname(fileURLToPath(import.meta.url)), '..', 'resources');\n\ninterface Doc {\n uri: string;\n name: string;\n file: string;\n title: string;\n description: string;\n}\n\nconst DOCS: Doc[] = [\n {\n uri: 'roxyon://docs/lumenjs',\n name: 'lumenjs-reference',\n file: 'lumenjs.md',\n title: 'LumenJS — complete reference',\n description:\n 'Full LumenJS V1 reference for building SPAs with vanilla JS/HTML/CSS (no build step), ' +\n 'contrasted with Vue/React. Read before writing .view files.',\n },\n {\n uri: 'roxyon://docs/baas',\n name: 'baas-reference',\n file: 'baas.md',\n title: 'Roxyon BaaS reference',\n description:\n 'The Roxyon Backend-as-a-Service REST API: endpoints, auth flow, query rules, and the ' +\n 'resolve-on-failure write behaviour.',\n },\n {\n uri: 'roxyon://docs/deploy',\n name: 'deploy-reference',\n file: 'deploy.md',\n title: 'Deploying to Roxyon',\n description:\n 'How roxyon_init / roxyon_deploy work, the roxyon.json schema, static vs app deploys, env ' +\n 'vars, and git push-to-deploy.',\n },\n];\n\nexport function registerResources(server: McpServer): void {\n for (const doc of DOCS) {\n server.registerResource(\n doc.name,\n doc.uri,\n { title: doc.title, description: doc.description, mimeType: 'text/markdown' },\n async () => {\n const body = await readFile(join(RESOURCE_DIR, doc.file), 'utf8');\n return { contents: [{ uri: doc.uri, mimeType: 'text/markdown', text: body }] };\n },\n );\n }\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { type Roxyon, envFromStored, formatEnv } from '@roxyon/api-client';\nimport {\n buildIgnore,\n buildProjectConfig,\n deployProject,\n detectRuntime,\n listFiles,\n loadProjectConfig,\n saveProjectConfig,\n} from '@roxyon/deploy-core';\nimport { z } from 'zod';\nimport { currentContext } from './context.js';\nimport { type ToolResult, errorResult, guard, text } from './result.js';\nimport { type RoxyonSession, getSession } from './session.js';\n\n/**\n * `roxyon_init` / `roxyon_deploy` read and write the caller's local project\n * directory — impossible over the remote HTTP server. Returns an explanatory\n * result when running in that mode, `null` otherwise (proceed).\n */\nfunction remoteFilesystemBlock(tool: 'roxyon_init' | 'roxyon_deploy'): ToolResult | null {\n if (!currentContext().remote) return null;\n return errorResult(\n [\n `${tool} needs access to your local project files, which the hosted Roxyon MCP`,\n 'server does not have. Either:',\n ' • run the `roxyon` CLI locally (`npm i -g @roxyon/cli`, then `roxyon deploy`), or',\n ' • use `roxyon_link_github` to connect a git repo for push-to-deploy, then',\n ' `roxyon_app_status` / `roxyon_logs` to watch it here.',\n ].join('\\n'),\n );\n}\n\n/** Resolve an application id from an explicit id or a project directory. */\nasync function resolveApp(args: { application?: string; dir?: string }): Promise<{\n applicationId: string;\n host?: string;\n}> {\n if (args.application) return { applicationId: args.application };\n if (args.dir) {\n const cfg = await loadProjectConfig(args.dir);\n if (cfg?.application) return { applicationId: cfg.application, host: cfg.host };\n throw new Error(\n `No application linked in ${args.dir}/roxyon.json. Deploy it once (roxyon_deploy) or pass \"application\".`,\n );\n }\n throw new Error('Pass either \"application\" (an id) or \"dir\" (a project directory).');\n}\n\n/** The account context (user + subscriptions + domains) — one call, PAT-safe. */\nfunction accountContext(session: RoxyonSession) {\n return session.roxyon.account.context();\n}\n\nexport function registerTools(server: McpServer): void {\n // -----------------------------------------------------------------------\n server.registerTool(\n 'roxyon_whoami',\n {\n title: 'Roxyon: who am I',\n description: 'Show the signed-in Roxyon user and their subscriptions (which one is active).',\n inputSchema: {},\n },\n () =>\n guard(async () => {\n const session = await getSession();\n const ctx = await accountContext(session);\n const lines = [\n `User: ${ctx.user.email || ctx.user.id} (${ctx.user.id})`,\n `Auth: ${session.source}${ctx.scopes.length ? ` (scopes: ${ctx.scopes.join(', ')})` : ''}`,\n '',\n 'Subscriptions:',\n ...ctx.subscriptions.map(\n (s) =>\n ` ${s.name || s.id} — ${s.status || '?'}${s.datacenter ? ` · ${s.datacenter}` : ''}`,\n ),\n '',\n `Hosts: ${ctx.domains.map((d) => d.name).join(', ') || '(none)'}`,\n ];\n return text(lines.join('\\n'), {\n user: ctx.user,\n scopes: ctx.scopes,\n subscriptions: ctx.subscriptions,\n hosts: ctx.domains.map((d) => d.name),\n });\n }),\n );\n\n // -----------------------------------------------------------------------\n server.registerTool(\n 'roxyon_list_domains',\n {\n title: 'Roxyon: list hosts',\n description:\n 'List the domains (hosts) on the active subscription — the possible deploy targets.',\n inputSchema: {},\n },\n () =>\n guard(async () => {\n const session = await getSession();\n const { domains } = await accountContext(session);\n return text(\n domains.length\n ? domains.map((d) => `- ${d.name}`).join('\\n')\n : '(no hosts on this account)',\n { hosts: domains.map((d) => d.name) },\n );\n }),\n );\n\n // -----------------------------------------------------------------------\n server.registerTool(\n 'roxyon_list_apps',\n {\n title: 'Roxyon: list applications',\n description:\n 'List the applications on the active subscription with their status and revision.',\n inputSchema: {},\n },\n () =>\n guard(async () => {\n const session = await getSession();\n const apps = await session.roxyon.account.apps();\n const body = apps.length\n ? apps\n .map(\n (a) =>\n `- ${a.name} [${a.id}] — ${a.status || '?'} ` +\n `(rev ${a.appliedRevision}/${a.configRevision})` +\n `${a.repo ? ` · git:${a.repo.branch}` : ''}`,\n )\n .join('\\n')\n : '(no applications)';\n return text(body, { applications: apps });\n }),\n );\n\n // -----------------------------------------------------------------------\n server.registerTool(\n 'roxyon_init',\n {\n title: 'Roxyon: initialise a project',\n description:\n \"Detect a project's type and write roxyon.json. Run this once per project before roxyon_deploy. \" +\n 'Does not create anything on the platform.',\n inputSchema: {\n dir: z.string().describe('Absolute path to the project directory.'),\n runtime: z.enum(['lumen', 'node', 'python', 'php']).optional(),\n host: z.string().optional().describe('Deploy host (a domain on the subscription).'),\n folder: z.string().optional().describe('Sub-path under the host; \"\" = site root.'),\n overwrite: z.boolean().optional().describe('Replace an existing roxyon.json.'),\n },\n },\n (args) =>\n guard(async () => {\n const blocked = remoteFilesystemBlock('roxyon_init');\n if (blocked) return blocked;\n const session = await getSession();\n const existing = await loadProjectConfig(args.dir);\n if (existing && !args.overwrite) {\n return errorResult(\n `roxyon.json already exists in ${args.dir}. Pass overwrite:true to replace it.`,\n { config: existing },\n );\n }\n const { domains } = await accountContext(session);\n if (domains.length === 0) {\n return errorResult(\n 'This account has no hosts. Add a domain in the Roxyon console first.',\n );\n }\n const detected = await detectRuntime(args.dir);\n const runtime = args.runtime ?? detected.runtime;\n const host = args.host ?? domains[0]!.name;\n if (!domains.some((d) => d.name === host)) {\n return errorResult(\n `Host \"${host}\" is not on this account (have: ${domains.map((d) => d.name).join(', ')}).`,\n );\n }\n const config = buildProjectConfig({\n cwd: args.dir,\n runtime,\n host,\n folder: args.folder,\n start: detected.start,\n });\n const path = await saveProjectConfig(config, args.dir);\n return text(\n `Wrote ${path}\\nDetected: ${detected.runtime} (${detected.reason})\\n\\n${JSON.stringify(config, null, 2)}`,\n { path, detected: detected.runtime, config },\n );\n }),\n );\n\n // -----------------------------------------------------------------------\n server.registerTool(\n 'roxyon_deploy',\n {\n title: 'Roxyon: deploy a project',\n description:\n 'Build (if configured) and deploy the project in \"dir\" to Roxyon. Without confirm:true this ' +\n 'returns a dry-run plan (what would build, where it would go, how many files). With confirm:true ' +\n 'it runs the build, uploads, and — for app runtimes — waits for it to go live. Requires roxyon.json ' +\n '(run roxyon_init first).',\n inputSchema: {\n dir: z\n .string()\n .describe('Absolute path to the project directory (must contain roxyon.json).'),\n confirm: z.boolean().optional().describe('Actually deploy. Omit for a dry-run plan.'),\n build: z.boolean().optional().describe('Run the configured build command (default true).'),\n follow: z\n .boolean()\n .optional()\n .describe('For app runtimes, wait until running/failed (default true).'),\n },\n },\n (args) =>\n guard(async () => {\n const blocked = remoteFilesystemBlock('roxyon_deploy');\n if (blocked) return blocked;\n const session = await getSession();\n const config = await loadProjectConfig(args.dir);\n if (!config) {\n return errorResult(`No roxyon.json in ${args.dir}. Run roxyon_init first.`);\n }\n\n if (!args.confirm) {\n const ig = await buildIgnore(args.dir);\n const packRoot =\n config.kind === 'static' && config.outDir ? `${args.dir}/${config.outDir}` : args.dir;\n let files: string[] = [];\n try {\n files = await listFiles(packRoot, ig);\n } catch {\n /* outDir may not exist until the build runs */\n }\n const plan = [\n 'DRY RUN — pass confirm:true to deploy.',\n '',\n `Project: ${config.name} (${config.runtime}, ${config.kind})`,\n `Target: ${config.host}${config.folder ? `/${config.folder}` : ''}`,\n config.build ? `Build: ${config.build}` : 'Build: (none)',\n config.kind === 'app'\n ? `App: ${config.application ?? '(created on first deploy)'}`\n : 'Serve: static files from the host document root',\n files.length\n ? `Would pack ${files.length} file(s) from ${config.kind === 'static' && config.outDir ? config.outDir : '.'}`\n : `Would pack the build output (${config.outDir ?? 'dist'}) after building`,\n ].join('\\n');\n return text(plan, { dryRun: true, config });\n }\n\n const logLines: string[] = [];\n const outcome = await deployProject({\n cwd: args.dir,\n roxyon: session.roxyon,\n preferredSubscription: session.preferredSubscription,\n build: args.build,\n follow: args.follow ?? true,\n timeoutMs: 5 * 60 * 1000,\n reporter: {\n step: (m) => logLines.push(`» ${m}`),\n log: (l) => logLines.push(` ${l}`),\n },\n });\n\n const tail = logLines.slice(-60).join('\\n');\n if (outcome.kind === 'static' && outcome.ok) {\n return text(`${tail}\\n\\n✓ Live: ${outcome.url} (${outcome.files} files)`, {\n ok: true,\n kind: 'static',\n url: outcome.url,\n });\n }\n if (outcome.kind === 'app' && outcome.ok) {\n return text(\n `${tail}\\n\\n✓ ${outcome.status === 'running' ? `Live: ${outcome.url ?? outcome.application}` : `Build queued (revision ${outcome.revision})`}`,\n {\n ok: true,\n kind: 'app',\n application: outcome.application,\n status: outcome.status,\n url: outcome.url,\n },\n );\n }\n return errorResult(\n `${tail}\\n\\n✗ Deploy failed: ${'error' in outcome ? outcome.error : 'unknown'}`,\n { ok: false, application: 'application' in outcome ? outcome.application : undefined },\n );\n }),\n );\n\n // -----------------------------------------------------------------------\n server.registerTool(\n 'roxyon_app_status',\n {\n title: 'Roxyon: application status',\n description:\n 'Current status, revisions and last error for an application (by id or project dir).',\n inputSchema: {\n application: z.string().optional(),\n dir: z\n .string()\n .optional()\n .describe('A project directory whose roxyon.json links an application.'),\n },\n },\n (args) =>\n guard(async () => {\n const session = await getSession();\n const { applicationId } = await resolveApp(args);\n const app = await session.roxyon.account.getApp(applicationId);\n if (!app) return errorResult(`Application ${applicationId} not found.`);\n const settled = app.configRevision <= app.appliedRevision;\n const body = [\n `${app.name} [${app.id}]`,\n `Status: ${app.status || '?'}${settled ? '' : ' (applying)'}`,\n `Revision: ${app.appliedRevision} / ${app.configRevision}`,\n `Runtime: ${app.runtime}`.trim(),\n app.lastError ? `\\nLast error:\\n${app.lastError}` : '',\n ]\n .filter(Boolean)\n .join('\\n');\n return text(body, {\n id: app.id,\n status: app.status,\n settled,\n configRevision: app.configRevision,\n appliedRevision: app.appliedRevision,\n lastError: app.lastError || undefined,\n });\n }),\n );\n\n // -----------------------------------------------------------------------\n server.registerTool(\n 'roxyon_logs',\n {\n title: 'Roxyon: application logs',\n description: 'Recent journal lines for an application (by id or project dir).',\n inputSchema: {\n application: z.string().optional(),\n dir: z.string().optional(),\n lines: z\n .number()\n .int()\n .min(1)\n .max(1000)\n .optional()\n .describe('How many lines (default 100).'),\n },\n },\n (args) =>\n guard(async () => {\n const session = await getSession();\n const { applicationId } = await resolveApp(args);\n const lines = await session.roxyon.applications.logs(applicationId, args.lines ?? 100);\n return text(lines.join('\\n') || '(no log lines)', { lines });\n }),\n );\n\n // -----------------------------------------------------------------------\n server.registerTool(\n 'roxyon_restart',\n {\n title: 'Roxyon: restart an application',\n description: 'Bounce the application process (no rebuild). Needs confirm:true.',\n inputSchema: {\n application: z.string().optional(),\n dir: z.string().optional(),\n confirm: z.boolean().optional(),\n },\n },\n (args) =>\n guard(async () => {\n const session = await getSession();\n const { applicationId } = await resolveApp(args);\n if (!args.confirm)\n return text(`Would restart ${applicationId}. Pass confirm:true.`, { dryRun: true });\n await session.roxyon.applications.restart(applicationId);\n return text(`Restart queued for ${applicationId}.`, { ok: true });\n }),\n );\n\n // -----------------------------------------------------------------------\n server.registerTool(\n 'roxyon_env_get',\n {\n title: 'Roxyon: read environment variables',\n description: \"An application's environment variables as KEY=value lines.\",\n inputSchema: { application: z.string().optional(), dir: z.string().optional() },\n },\n (args) =>\n guard(async () => {\n const session = await getSession();\n const { applicationId } = await resolveApp(args);\n const env = await session.roxyon.applications.getEnv(applicationId);\n return text(formatEnv(env) || '(none set)', { env });\n }),\n );\n\n // -----------------------------------------------------------------------\n server.registerTool(\n 'roxyon_env_set',\n {\n title: 'Roxyon: set environment variables',\n description:\n \"Merge variables into an application's environment and bump its revision. Run roxyon_deploy \" +\n 'afterwards to apply. Needs confirm:true. PORT and HOST are platform-managed and ignored.',\n inputSchema: {\n application: z.string().optional(),\n dir: z.string().optional(),\n vars: z.record(z.string()).describe('{ KEY: \"value\", ... } to set.'),\n remove: z.array(z.string()).optional().describe('Keys to delete.'),\n confirm: z.boolean().optional(),\n },\n },\n (args) =>\n guard(async () => {\n const session = await getSession();\n const { applicationId } = await resolveApp(args);\n const set: Record<string, string> = {};\n for (const [k, v] of Object.entries(args.vars)) {\n if (k === 'PORT' || k === 'HOST') continue;\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(k)) return errorResult(`Invalid env key \"${k}\".`);\n set[k] = v;\n }\n const planned = [...Object.keys(set), ...(args.remove ?? []).map((k) => `-${k}`)];\n if (!args.confirm) {\n return text(`Would update: ${planned.join(', ') || '(nothing)'}. Pass confirm:true.`, {\n dryRun: true,\n changed: planned,\n });\n }\n const r = await session.roxyon.applications.setEnv(applicationId, {\n set,\n remove: args.remove ?? [],\n });\n return text(`Updated ${r.changed.join(', ') || '(nothing)'}. Run roxyon_deploy to apply.`, {\n ok: true,\n changed: r.changed,\n });\n }),\n );\n\n // -----------------------------------------------------------------------\n server.registerTool(\n 'roxyon_link_github',\n {\n title: 'Roxyon: connect a git remote',\n description:\n 'Connect a git repository to an application for push-to-deploy. Returns the deploy key and ' +\n 'webhook URL to add to the repo. Needs confirm:true.',\n inputSchema: {\n application: z.string().optional(),\n dir: z.string().optional(),\n repoUrl: z.string().describe('SSH git URL, e.g. git@github.com:you/app.git'),\n branch: z.string().optional().describe('Branch to deploy (default main).'),\n confirm: z.boolean().optional(),\n },\n },\n (args) =>\n guard(async () => {\n const session = await getSession();\n const { applicationId } = await resolveApp(args);\n if (!args.confirm) {\n return text(\n `Would connect ${args.repoUrl} (${args.branch ?? 'main'}) to ${applicationId}. Pass confirm:true.`,\n { dryRun: true },\n );\n }\n const res = await session.roxyon.applications.repoConnect(\n applicationId,\n args.repoUrl,\n args.branch ?? 'main',\n );\n const body = [\n `Connected ${args.repoUrl} (${args.branch ?? 'main'}).`,\n res.deployKey ? `\\nDeploy key (add to the repo, read-only):\\n${res.deployKey}` : '',\n res.webhookUrl ? `\\nWebhook URL: ${res.webhookUrl}` : '',\n res.secret ? `Webhook secret: ${res.secret}` : '',\n res.hint ? `\\n${res.hint}` : '',\n ]\n .filter(Boolean)\n .join('\\n');\n return text(body, {\n ok: true,\n deployKey: res.deployKey,\n webhookUrl: res.webhookUrl,\n secret: res.secret,\n });\n }),\n );\n}\n\n// Re-exported for tests.\nexport { resolveApp };\nexport type { Roxyon, ToolResult };\n","import { AsyncLocalStorage } from 'node:async_hooks';\n\n/**\n * Per-request state for the HTTP transport. The stdio entry never opens a\n * context, so {@link currentContext} returns `{}` there and everything falls\n * back to the env / stored-login behaviour.\n */\nexport interface RequestContext {\n /** Bearer token presented on this HTTP request (from the OAuth layer). */\n token?: string;\n /**\n * True when serving the remote HTTP server. Tools that need the caller's\n * local filesystem (`roxyon_init`, `roxyon_deploy`) refuse in this mode.\n */\n remote?: boolean;\n}\n\nconst storage = new AsyncLocalStorage<RequestContext>();\n\n/** Run `fn` with `ctx` visible to {@link currentContext} for its whole async tree. */\nexport function runWithContext<T>(ctx: RequestContext, fn: () => T): T {\n return storage.run(ctx, fn);\n}\n\n/** The context for the in-flight request, or `{}` on the stdio transport. */\nexport function currentContext(): RequestContext {\n return storage.getStore() ?? {};\n}\n","import { Roxyon, isAuthError } from '@roxyon/api-client';\nimport { endpointsFromEnv, loadCredentials, tokenFromEnv } from '@roxyon/deploy-core';\nimport { currentContext } from './context.js';\n\nexport interface RoxyonSession {\n roxyon: Roxyon;\n preferredSubscription?: string;\n /** how the session was authenticated, for diagnostics */\n source: 'env-token' | 'stored-login' | 'oauth';\n}\n\nexport class NotAuthenticatedError extends Error {\n constructor() {\n super(\n 'Not signed in to Roxyon. Run `roxyon login` in a terminal, or set ROXYON_TOKEN ' +\n \"in this MCP server's environment.\",\n );\n this.name = 'NotAuthenticatedError';\n }\n}\n\n/**\n * Resolve a Roxyon client for a tool call. Order: `ROXYON_TOKEN` env var, then\n * the login stored by the CLI at `~/.roxyon/config.json`.\n */\nexport async function getSession(): Promise<RoxyonSession> {\n const { apiUrl, consoleUrl } = endpointsFromEnv();\n\n // HTTP transport: the bearer token from this request wins over any ambient\n // env/stored credentials, and each request gets its own client.\n const requestToken = currentContext().token;\n if (requestToken) {\n return {\n roxyon: new Roxyon({ sessionToken: requestToken, baseUrl: apiUrl, consoleUrl }),\n source: 'oauth',\n };\n }\n\n const envToken = tokenFromEnv();\n if (envToken) {\n return {\n roxyon: new Roxyon({ sessionToken: envToken, baseUrl: apiUrl, consoleUrl }),\n source: 'env-token',\n };\n }\n const creds = await loadCredentials();\n if (!creds?.sessionToken) throw new NotAuthenticatedError();\n return {\n roxyon: new Roxyon({ sessionToken: creds.sessionToken, baseUrl: apiUrl, consoleUrl }),\n preferredSubscription: creds.subscription,\n source: 'stored-login',\n };\n}\n\nexport function isSessionGone(err: unknown): boolean {\n return err instanceof NotAuthenticatedError || isAuthError(err);\n}\n","import { isSessionGone } from './session.js';\n\nexport interface ToolResult {\n content: Array<{ type: 'text'; text: string }>;\n structuredContent?: Record<string, unknown>;\n isError?: boolean;\n /** SDK's CallToolResult carries an open index signature. */\n [k: string]: unknown;\n}\n\nexport function text(body: string, structured?: Record<string, unknown>): ToolResult {\n return {\n content: [{ type: 'text', text: body }],\n ...(structured ? { structuredContent: structured } : {}),\n };\n}\n\nexport function errorResult(message: string, structured?: Record<string, unknown>): ToolResult {\n return {\n content: [{ type: 'text', text: message }],\n ...(structured ? { structuredContent: structured } : {}),\n isError: true,\n };\n}\n\n/** Wrap a tool body so thrown errors become clean `isError` results. */\nexport async function guard(fn: () => Promise<ToolResult>): Promise<ToolResult> {\n try {\n return await fn();\n } catch (err) {\n if (isSessionGone(err)) {\n return errorResult(\n (err as Error).message ||\n 'Roxyon session expired — run `roxyon login` again, or refresh ROXYON_TOKEN.',\n );\n }\n return errorResult(err instanceof Error ? err.message : String(err));\n }\n}\n"],"mappings":"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","names":["z","z"]}
|