@caelo-cms/mcp-server 0.1.0 → 0.1.1

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.
@@ -0,0 +1,31 @@
1
+ /**
2
+ * HTTP bridge — translates a `caelo_chat` MCP call into a single POST
3
+ * against the admin install's `/api/mcp/chat` endpoint. The endpoint
4
+ * dispatches `mcp.send_chat` (system-only op) which resolves the
5
+ * bearer to a Caelo actor and drives `runChatTurn`.
6
+ *
7
+ * 30-second client-side timeout — chat-runner turns longer than that
8
+ * are likely stuck (the runner has its own provider-call timeouts).
9
+ * MCP clients see a clean error instead of a hung connection.
10
+ */
11
+ export interface SendChatOpts {
12
+ readonly adminUrl: string;
13
+ readonly token: string;
14
+ readonly message: string;
15
+ readonly chatSessionId?: string;
16
+ readonly pageId?: string;
17
+ }
18
+ export interface SendChatResult {
19
+ readonly chatSessionId: string;
20
+ readonly requestId: string;
21
+ readonly assistant: string;
22
+ readonly toolCalls: ReadonlyArray<{
23
+ name: string;
24
+ summary: string;
25
+ succeeded: boolean;
26
+ }>;
27
+ readonly pendingProposals: number;
28
+ readonly costMicrocents: number;
29
+ }
30
+ export declare function sendChat(opts: SendChatOpts): Promise<SendChatResult>;
31
+ //# sourceMappingURL=chat-bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-bridge.d.ts","sourceRoot":"","sources":["../src/chat-bridge.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AAEH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACzF,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAID,wBAAsB,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC,CAgC1E"}
@@ -0,0 +1,46 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+ const TIMEOUT_MS = 30_000;
3
+ export async function sendChat(opts) {
4
+ const controller = new AbortController();
5
+ const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
6
+ try {
7
+ const url = `${opts.adminUrl.replace(/\/+$/, "")}/api/mcp/chat`;
8
+ const res = await fetch(url, {
9
+ method: "POST",
10
+ headers: {
11
+ "content-type": "application/json",
12
+ "x-caelo-mcp-token": opts.token,
13
+ },
14
+ body: JSON.stringify({
15
+ message: opts.message,
16
+ chatSessionId: opts.chatSessionId,
17
+ pageId: opts.pageId,
18
+ }),
19
+ signal: controller.signal,
20
+ });
21
+ if (!res.ok) {
22
+ const body = await safeText(res);
23
+ throw new Error(`HTTP ${res.status}: ${body || res.statusText}`);
24
+ }
25
+ const json = (await res.json());
26
+ return json;
27
+ }
28
+ catch (e) {
29
+ if (e instanceof Error && e.name === "AbortError") {
30
+ throw new Error(`timeout after ${TIMEOUT_MS / 1000}s`);
31
+ }
32
+ throw e;
33
+ }
34
+ finally {
35
+ clearTimeout(timer);
36
+ }
37
+ }
38
+ async function safeText(res) {
39
+ try {
40
+ return (await res.text()).slice(0, 500);
41
+ }
42
+ catch {
43
+ return "";
44
+ }
45
+ }
46
+ //# sourceMappingURL=chat-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-bridge.js","sourceRoot":"","sources":["../src/chat-bridge.ts"],"names":[],"mappings":"AAAA,mCAAmC;AA8BnC,MAAM,UAAU,GAAG,MAAM,CAAC;AAE1B,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAkB;IAC/C,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,UAAU,CAAC,CAAC;IAC/D,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,eAAe,CAAC;QAChE,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAC3B,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,mBAAmB,EAAE,IAAI,CAAC,KAAK;aAChC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,aAAa,EAAE,IAAI,CAAC,aAAa;gBACjC,MAAM,EAAE,IAAI,CAAC,MAAM;aACpB,CAAC;YACF,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC;YACjC,MAAM,IAAI,KAAK,CAAC,QAAQ,GAAG,CAAC,MAAM,KAAK,IAAI,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;QACnE,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAmB,CAAC;QAClD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,YAAY,KAAK,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;YAClD,MAAM,IAAI,KAAK,CAAC,iBAAiB,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC;QACzD,CAAC;QACD,MAAM,CAAC,CAAC;IACV,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,QAAQ,CAAC,GAAa;IACnC,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bun
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env bun
2
2
  // SPDX-License-Identifier: MPL-2.0
3
-
4
3
  /**
5
4
  * @caelo-cms/mcp-server — entrypoint.
6
5
  *
@@ -15,19 +14,16 @@
15
14
  * database directly — it just translates between MCP semantics and
16
15
  * the admin's HTTP surface.
17
16
  */
18
-
19
17
  import { startMcpServer } from "./server.js";
20
-
21
18
  const adminUrl = process.env["CAELO_ADMIN_URL"];
22
19
  const token = process.env["CAELO_MCP_TOKEN"];
23
-
24
20
  if (!adminUrl) {
25
- console.error("CAELO_ADMIN_URL not set — point me at your admin install (e.g. https://admin.example.com)");
26
- process.exit(2);
21
+ console.error("CAELO_ADMIN_URL not set — point me at your admin install (e.g. https://admin.example.com)");
22
+ process.exit(2);
27
23
  }
28
24
  if (!token) {
29
- console.error("CAELO_MCP_TOKEN not set — mint one at /security/mcp on your admin install");
30
- process.exit(2);
25
+ console.error("CAELO_MCP_TOKEN not set — mint one at /security/mcp on your admin install");
26
+ process.exit(2);
31
27
  }
32
-
33
28
  await startMcpServer({ adminUrl, token });
29
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,mCAAmC;AAEnC;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;AAChD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;AAE7C,IAAI,CAAC,QAAQ,EAAE,CAAC;IACd,OAAO,CAAC,KAAK,CAAC,2FAA2F,CAAC,CAAC;IAC3G,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AACD,IAAI,CAAC,KAAK,EAAE,CAAC;IACX,OAAO,CAAC,KAAK,CAAC,2EAA2E,CAAC,CAAC;IAC3F,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,cAAc,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC"}
@@ -0,0 +1,6 @@
1
+ export interface StartOpts {
2
+ readonly adminUrl: string;
3
+ readonly token: string;
4
+ }
5
+ export declare function startMcpServer(opts: StartOpts): Promise<void>;
6
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAUD,wBAAsB,cAAc,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAoGnE"}
package/dist/server.js ADDED
@@ -0,0 +1,112 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+ /**
3
+ * MCP server construction. Registers exactly one tool — `caelo_chat` —
4
+ * and binds it to a stdio transport. The single-tool design is
5
+ * deliberate: the remote agent talks to Caelo's chat-runner the same
6
+ * way a human in the browser does. Browse / publish / propose actions
7
+ * happen through the chat ("which pages exist?" → agent calls
8
+ * pages.list internally → text response). Same auth surface, same
9
+ * actor scope, same audit trail.
10
+ */
11
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
12
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
13
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
14
+ import { z } from "zod";
15
+ import { sendChat } from "./chat-bridge.js";
16
+ const caeloChatInputSchema = z
17
+ .object({
18
+ message: z.string().min(1).max(50_000),
19
+ chatSessionId: z.string().uuid().optional(),
20
+ pageId: z.string().uuid().optional(),
21
+ })
22
+ .strict();
23
+ export async function startMcpServer(opts) {
24
+ const server = new Server({
25
+ name: "caelo-mcp-server",
26
+ version: "0.1.0",
27
+ }, {
28
+ capabilities: { tools: {} },
29
+ });
30
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
31
+ tools: [
32
+ {
33
+ name: "caelo_chat",
34
+ description: "Talk to your Caelo CMS install's AI agent. Same agent that powers the live-edit chat overlay — it can read pages, propose edits, queue Owner-approval proposals, summarise plugin data. Continues an existing chat session (when chatSessionId is supplied) or starts a fresh one. Returns the agent's reply text plus structured tool-call summaries.",
35
+ inputSchema: {
36
+ type: "object",
37
+ required: ["message"],
38
+ properties: {
39
+ message: {
40
+ type: "string",
41
+ description: "What you want to say to the Caelo agent.",
42
+ },
43
+ chatSessionId: {
44
+ type: "string",
45
+ description: "Optional. Chat session UUID to continue. Omit to start a fresh page-unbound chat.",
46
+ },
47
+ pageId: {
48
+ type: "string",
49
+ description: "Optional. Bind a NEW chat to one page so the agent's page-context block is populated. Same shape as /edit?page=<id>.",
50
+ },
51
+ },
52
+ },
53
+ },
54
+ ],
55
+ }));
56
+ server.setRequestHandler(CallToolRequestSchema, async (req) => {
57
+ if (req.params.name !== "caelo_chat") {
58
+ return {
59
+ isError: true,
60
+ content: [
61
+ { type: "text", text: `unknown tool: ${req.params.name}` },
62
+ ],
63
+ };
64
+ }
65
+ const parsed = caeloChatInputSchema.safeParse(req.params.arguments ?? {});
66
+ if (!parsed.success) {
67
+ return {
68
+ isError: true,
69
+ content: [
70
+ { type: "text", text: `invalid arguments: ${parsed.error.message}` },
71
+ ],
72
+ };
73
+ }
74
+ try {
75
+ const result = await sendChat({
76
+ adminUrl: opts.adminUrl,
77
+ token: opts.token,
78
+ message: parsed.data.message,
79
+ ...(parsed.data.chatSessionId ? { chatSessionId: parsed.data.chatSessionId } : {}),
80
+ ...(parsed.data.pageId ? { pageId: parsed.data.pageId } : {}),
81
+ });
82
+ // Two content blocks: human-readable assistant text + a JSON
83
+ // structured block the calling agent can parse for the
84
+ // requestId, tool calls, cost, pending proposals.
85
+ return {
86
+ content: [
87
+ { type: "text", text: result.assistant },
88
+ {
89
+ type: "text",
90
+ text: JSON.stringify({
91
+ chatSessionId: result.chatSessionId,
92
+ requestId: result.requestId,
93
+ toolCalls: result.toolCalls,
94
+ pendingProposals: result.pendingProposals,
95
+ costMicrocents: result.costMicrocents,
96
+ }, null, 2),
97
+ },
98
+ ],
99
+ };
100
+ }
101
+ catch (e) {
102
+ const msg = e instanceof Error ? e.message : String(e);
103
+ return {
104
+ isError: true,
105
+ content: [{ type: "text", text: `caelo_chat failed: ${msg}` }],
106
+ };
107
+ }
108
+ });
109
+ const transport = new StdioServerTransport();
110
+ await server.connect(transport);
111
+ }
112
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,23 +1,37 @@
1
1
  {
2
2
  "name": "@caelo-cms/mcp-server",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "private": false,
5
5
  "license": "MPL-2.0",
6
6
  "description": "MCP server exposing the Caelo CMS chat-runner as a `caelo_chat` tool. Lets Claude Code (or any MCP-aware client) drive a Caelo install from outside the browser.",
7
7
  "type": "module",
8
- "main": "./src/index.ts",
9
- "types": "./src/index.ts",
8
+ "main": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
10
  "bin": {
11
- "caelo-mcp-server": "./src/index.ts"
11
+ "caelo-mcp-server": "./dist/index.js"
12
12
  },
13
13
  "exports": {
14
- ".": "./src/index.ts"
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.js",
17
+ "default": "./dist/index.js"
18
+ }
15
19
  },
20
+ "files": [
21
+ "dist/",
22
+ "README.md",
23
+ "package.json"
24
+ ],
16
25
  "scripts": {
17
- "typecheck": "tsc -b"
26
+ "typecheck": "tsc -b",
27
+ "build": "tsc -b --force",
28
+ "prepublishOnly": "tsc -b --force"
18
29
  },
19
30
  "dependencies": {
20
31
  "@modelcontextprotocol/sdk": "1.29.0",
21
32
  "zod": "4.3.6"
33
+ },
34
+ "publishConfig": {
35
+ "access": "public"
22
36
  }
23
37
  }
@@ -1,73 +0,0 @@
1
- // SPDX-License-Identifier: MPL-2.0
2
-
3
- /**
4
- * HTTP bridge — translates a `caelo_chat` MCP call into a single POST
5
- * against the admin install's `/api/mcp/chat` endpoint. The endpoint
6
- * dispatches `mcp.send_chat` (system-only op) which resolves the
7
- * bearer to a Caelo actor and drives `runChatTurn`.
8
- *
9
- * 30-second client-side timeout — chat-runner turns longer than that
10
- * are likely stuck (the runner has its own provider-call timeouts).
11
- * MCP clients see a clean error instead of a hung connection.
12
- */
13
-
14
- export interface SendChatOpts {
15
- readonly adminUrl: string;
16
- readonly token: string;
17
- readonly message: string;
18
- readonly chatSessionId?: string;
19
- readonly pageId?: string;
20
- }
21
-
22
- export interface SendChatResult {
23
- readonly chatSessionId: string;
24
- readonly requestId: string;
25
- readonly assistant: string;
26
- readonly toolCalls: ReadonlyArray<{ name: string; summary: string; succeeded: boolean }>;
27
- readonly pendingProposals: number;
28
- readonly costMicrocents: number;
29
- }
30
-
31
- const TIMEOUT_MS = 30_000;
32
-
33
- export async function sendChat(opts: SendChatOpts): Promise<SendChatResult> {
34
- const controller = new AbortController();
35
- const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
36
- try {
37
- const url = `${opts.adminUrl.replace(/\/+$/, "")}/api/mcp/chat`;
38
- const res = await fetch(url, {
39
- method: "POST",
40
- headers: {
41
- "content-type": "application/json",
42
- "x-caelo-mcp-token": opts.token,
43
- },
44
- body: JSON.stringify({
45
- message: opts.message,
46
- chatSessionId: opts.chatSessionId,
47
- pageId: opts.pageId,
48
- }),
49
- signal: controller.signal,
50
- });
51
- if (!res.ok) {
52
- const body = await safeText(res);
53
- throw new Error(`HTTP ${res.status}: ${body || res.statusText}`);
54
- }
55
- const json = (await res.json()) as SendChatResult;
56
- return json;
57
- } catch (e) {
58
- if (e instanceof Error && e.name === "AbortError") {
59
- throw new Error(`timeout after ${TIMEOUT_MS / 1000}s`);
60
- }
61
- throw e;
62
- } finally {
63
- clearTimeout(timer);
64
- }
65
- }
66
-
67
- async function safeText(res: Response): Promise<string> {
68
- try {
69
- return (await res.text()).slice(0, 500);
70
- } catch {
71
- return "";
72
- }
73
- }
package/src/server.ts DELETED
@@ -1,135 +0,0 @@
1
- // SPDX-License-Identifier: MPL-2.0
2
-
3
- /**
4
- * MCP server construction. Registers exactly one tool — `caelo_chat` —
5
- * and binds it to a stdio transport. The single-tool design is
6
- * deliberate: the remote agent talks to Caelo's chat-runner the same
7
- * way a human in the browser does. Browse / publish / propose actions
8
- * happen through the chat ("which pages exist?" → agent calls
9
- * pages.list internally → text response). Same auth surface, same
10
- * actor scope, same audit trail.
11
- */
12
-
13
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
14
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
15
- import {
16
- CallToolRequestSchema,
17
- ListToolsRequestSchema,
18
- } from "@modelcontextprotocol/sdk/types.js";
19
- import { z } from "zod";
20
- import { sendChat } from "./chat-bridge.js";
21
-
22
- export interface StartOpts {
23
- readonly adminUrl: string;
24
- readonly token: string;
25
- }
26
-
27
- const caeloChatInputSchema = z
28
- .object({
29
- message: z.string().min(1).max(50_000),
30
- chatSessionId: z.string().uuid().optional(),
31
- pageId: z.string().uuid().optional(),
32
- })
33
- .strict();
34
-
35
- export async function startMcpServer(opts: StartOpts): Promise<void> {
36
- const server = new Server(
37
- {
38
- name: "caelo-mcp-server",
39
- version: "0.1.0",
40
- },
41
- {
42
- capabilities: { tools: {} },
43
- },
44
- );
45
-
46
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
47
- tools: [
48
- {
49
- name: "caelo_chat",
50
- description:
51
- "Talk to your Caelo CMS install's AI agent. Same agent that powers the live-edit chat overlay — it can read pages, propose edits, queue Owner-approval proposals, summarise plugin data. Continues an existing chat session (when chatSessionId is supplied) or starts a fresh one. Returns the agent's reply text plus structured tool-call summaries.",
52
- inputSchema: {
53
- type: "object",
54
- required: ["message"],
55
- properties: {
56
- message: {
57
- type: "string",
58
- description: "What you want to say to the Caelo agent.",
59
- },
60
- chatSessionId: {
61
- type: "string",
62
- description:
63
- "Optional. Chat session UUID to continue. Omit to start a fresh page-unbound chat.",
64
- },
65
- pageId: {
66
- type: "string",
67
- description:
68
- "Optional. Bind a NEW chat to one page so the agent's page-context block is populated. Same shape as /edit?page=<id>.",
69
- },
70
- },
71
- },
72
- },
73
- ],
74
- }));
75
-
76
- server.setRequestHandler(CallToolRequestSchema, async (req) => {
77
- if (req.params.name !== "caelo_chat") {
78
- return {
79
- isError: true,
80
- content: [
81
- { type: "text", text: `unknown tool: ${req.params.name}` },
82
- ],
83
- };
84
- }
85
- const parsed = caeloChatInputSchema.safeParse(req.params.arguments ?? {});
86
- if (!parsed.success) {
87
- return {
88
- isError: true,
89
- content: [
90
- { type: "text", text: `invalid arguments: ${parsed.error.message}` },
91
- ],
92
- };
93
- }
94
- try {
95
- const result = await sendChat({
96
- adminUrl: opts.adminUrl,
97
- token: opts.token,
98
- message: parsed.data.message,
99
- ...(parsed.data.chatSessionId ? { chatSessionId: parsed.data.chatSessionId } : {}),
100
- ...(parsed.data.pageId ? { pageId: parsed.data.pageId } : {}),
101
- });
102
- // Two content blocks: human-readable assistant text + a JSON
103
- // structured block the calling agent can parse for the
104
- // requestId, tool calls, cost, pending proposals.
105
- return {
106
- content: [
107
- { type: "text", text: result.assistant },
108
- {
109
- type: "text",
110
- text: JSON.stringify(
111
- {
112
- chatSessionId: result.chatSessionId,
113
- requestId: result.requestId,
114
- toolCalls: result.toolCalls,
115
- pendingProposals: result.pendingProposals,
116
- costMicrocents: result.costMicrocents,
117
- },
118
- null,
119
- 2,
120
- ),
121
- },
122
- ],
123
- };
124
- } catch (e) {
125
- const msg = e instanceof Error ? e.message : String(e);
126
- return {
127
- isError: true,
128
- content: [{ type: "text", text: `caelo_chat failed: ${msg}` }],
129
- };
130
- }
131
- });
132
-
133
- const transport = new StdioServerTransport();
134
- await server.connect(transport);
135
- }
package/tsconfig.json DELETED
@@ -1,11 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "composite": true,
5
- "rootDir": "./src",
6
- "outDir": "./dist",
7
- "noEmit": false
8
- },
9
- "include": ["src/**/*"],
10
- "exclude": ["**/*.test.ts", "**/dist", "**/node_modules"]
11
- }