eddyter-mcp 0.1.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.
- package/README.md +94 -0
- package/dist/client.js +107 -0
- package/dist/client.js.map +1 -0
- package/dist/config.js +28 -0
- package/dist/config.js.map +1 -0
- package/dist/format.js +110 -0
- package/dist/format.js.map +1 -0
- package/dist/index.js +188 -0
- package/dist/index.js.map +1 -0
- package/dist/snippets.js +168 -0
- package/dist/snippets.js.map +1 -0
- package/package.json +32 -0
package/README.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Eddyter MCP Server
|
|
2
|
+
|
|
3
|
+
A [Model Context Protocol](https://modelcontextprotocol.io) server that lets AI
|
|
4
|
+
tools (Claude Desktop, Cursor, Windsurf, …) inspect and — in later steps —
|
|
5
|
+
configure the [Eddyter](https://www.eddyter.com) editor on an admin's behalf.
|
|
6
|
+
|
|
7
|
+
This is the **admin-side** surface: the account owner connects it to their AI
|
|
8
|
+
tool using a license key. Changes made through it apply to every end-user of the
|
|
9
|
+
editor that uses that key, with no redeploy (config lives server-side on the key).
|
|
10
|
+
|
|
11
|
+
## Tools
|
|
12
|
+
|
|
13
|
+
Reads are always available. The config-changing tool is **opt-in**
|
|
14
|
+
(`EDDYTER_ALLOW_WRITES=true`) so a pasted key can't change the live editor unless
|
|
15
|
+
the admin allows it.
|
|
16
|
+
|
|
17
|
+
| Tool | Mode | What it does |
|
|
18
|
+
| --- | --- | --- |
|
|
19
|
+
| `verify_status` | read | Is the key valid/active? Plan, expiry, subscription, BYOK, warnings. |
|
|
20
|
+
| `get_config` | read | Which editor features are enabled, off-but-allowed, or locked by plan. |
|
|
21
|
+
| `get_usage` | read | Credits remaining, usage this period, days until reset. |
|
|
22
|
+
| `get_integration_snippet` | read | SSR-safe copy-paste setup code per framework. |
|
|
23
|
+
| `update_config` | **write** (opt-in) | Patch the editor's features (e.g. turn AI chat off). Clamped to the plan; live with no redeploy. |
|
|
24
|
+
|
|
25
|
+
Backend: reads ride the public `/api/license/validate`; `get_usage` and
|
|
26
|
+
`update_config` use the X-API-Key endpoints `GET /api/license/usage` and
|
|
27
|
+
`POST /api/license/config`.
|
|
28
|
+
|
|
29
|
+
## Setup
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pnpm install
|
|
33
|
+
pnpm build
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Get a license key from the dashboard: <https://www.eddyter.com/user/license-key>
|
|
37
|
+
(The MCP never creates keys — humans mint them in the portal.)
|
|
38
|
+
|
|
39
|
+
### Connect to Claude Desktop
|
|
40
|
+
|
|
41
|
+
Add to `claude_desktop_config.json` (Settings → Developer → Edit Config):
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"mcpServers": {
|
|
46
|
+
"eddyter": {
|
|
47
|
+
"command": "node",
|
|
48
|
+
"args": ["/absolute/path/to/apps/mcp/dist/index.js"],
|
|
49
|
+
"env": {
|
|
50
|
+
"EDDYTER_LICENSE_KEY": "eddyt_your_key_here"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Connect to Cursor
|
|
58
|
+
|
|
59
|
+
`.cursor/mcp.json` in your project, same `command` / `args` / `env` shape.
|
|
60
|
+
|
|
61
|
+
Then ask the agent: *"Is my Eddyter key valid?"* or *"What editor features do I
|
|
62
|
+
have enabled?"*
|
|
63
|
+
|
|
64
|
+
## Configuration
|
|
65
|
+
|
|
66
|
+
| Env var | Default | Purpose |
|
|
67
|
+
| --- | --- | --- |
|
|
68
|
+
| `EDDYTER_LICENSE_KEY` | — | Key the agent acts on. Optional if passed per tool call. |
|
|
69
|
+
| `EDDYTER_ENV` | `production` | `production` \| `staging` \| `local`. |
|
|
70
|
+
| `EDDYTER_API_BASE_URL` | — | Advanced override of the backend base URL. |
|
|
71
|
+
| `EDDYTER_ALLOW_WRITES` | `false` | Set `true` to expose `update_config` (lets the agent change the live editor). |
|
|
72
|
+
|
|
73
|
+
## Develop & test
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pnpm dev # tsc --watch
|
|
77
|
+
pnpm inspect # open the MCP Inspector against this server
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Quick manual smoke test (no real key needed — exercises the live error path):
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
printf '%s\n' \
|
|
84
|
+
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"t","version":"0"}}}' \
|
|
85
|
+
'{"jsonrpc":"2.0","method":"notifications/initialized"}' \
|
|
86
|
+
'{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
|
|
87
|
+
| node dist/index.js
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Notes
|
|
91
|
+
|
|
92
|
+
- Logs go to **stderr** — stdout is the JSON-RPC channel and must stay clean.
|
|
93
|
+
- Feature labels surfaced by `get_config` live in `src/format.ts`
|
|
94
|
+
(`FEATURE_LABELS`) — tune names/ordering there to match the marketing site.
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin client over the Eddyter backend.
|
|
3
|
+
*
|
|
4
|
+
* Step 1 only needs the public, X-API-Key-authenticated validation endpoint —
|
|
5
|
+
* the same one the editor itself calls on mount. No new backend surface.
|
|
6
|
+
*/
|
|
7
|
+
export class EddyterError extends Error {
|
|
8
|
+
status;
|
|
9
|
+
code;
|
|
10
|
+
constructor(message, status, code) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.status = status;
|
|
13
|
+
this.code = code;
|
|
14
|
+
this.name = "EddyterError";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export class EddyterClient {
|
|
18
|
+
config;
|
|
19
|
+
constructor(config) {
|
|
20
|
+
this.config = config;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Resolve the license key for a tool call: explicit arg wins, otherwise the
|
|
24
|
+
* one configured in the MCP client. Throws a clear, actionable error if neither.
|
|
25
|
+
*/
|
|
26
|
+
resolveLicenseKey(explicit) {
|
|
27
|
+
const key = explicit?.trim() || this.config.defaultLicenseKey;
|
|
28
|
+
if (!key) {
|
|
29
|
+
throw new EddyterError("No Eddyter license key provided. Set EDDYTER_LICENSE_KEY in your MCP client " +
|
|
30
|
+
"config, or pass `licenseKey` to the tool. Create a key at " +
|
|
31
|
+
"https://www.eddyter.com/user/license-key");
|
|
32
|
+
}
|
|
33
|
+
return key;
|
|
34
|
+
}
|
|
35
|
+
/** Authenticated request helper: attaches X-API-Key and unwraps {success,data}. */
|
|
36
|
+
async request(method, path, licenseKey, body) {
|
|
37
|
+
const url = `${this.config.baseUrl}${path}`;
|
|
38
|
+
let res;
|
|
39
|
+
try {
|
|
40
|
+
res = await fetch(url, {
|
|
41
|
+
method,
|
|
42
|
+
headers: {
|
|
43
|
+
"Content-Type": "application/json",
|
|
44
|
+
"X-API-Key": licenseKey,
|
|
45
|
+
},
|
|
46
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
throw new EddyterError(`Could not reach Eddyter at ${url}: ${err.message}`);
|
|
51
|
+
}
|
|
52
|
+
let parsed;
|
|
53
|
+
try {
|
|
54
|
+
parsed = (await res.json());
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// fall through to status-based error
|
|
58
|
+
}
|
|
59
|
+
if (!res.ok || !parsed?.success) {
|
|
60
|
+
throw new EddyterError(parsed?.message || `Request to ${path} failed (HTTP ${res.status})`, res.status, parsed?.code);
|
|
61
|
+
}
|
|
62
|
+
return parsed.data;
|
|
63
|
+
}
|
|
64
|
+
/** Calls POST /api/license/validate with the key in the X-API-Key header. */
|
|
65
|
+
async validate(licenseKey) {
|
|
66
|
+
const url = `${this.config.baseUrl}/api/license/validate`;
|
|
67
|
+
let res;
|
|
68
|
+
try {
|
|
69
|
+
res = await fetch(url, {
|
|
70
|
+
method: "POST",
|
|
71
|
+
headers: {
|
|
72
|
+
"Content-Type": "application/json",
|
|
73
|
+
"X-API-Key": licenseKey,
|
|
74
|
+
},
|
|
75
|
+
body: "{}",
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
throw new EddyterError(`Could not reach Eddyter at ${url}: ${err.message}`);
|
|
80
|
+
}
|
|
81
|
+
let body;
|
|
82
|
+
try {
|
|
83
|
+
body = (await res.json());
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// fall through to status-based error below
|
|
87
|
+
}
|
|
88
|
+
if (!res.ok || !body?.success) {
|
|
89
|
+
throw new EddyterError(body?.message || `License validation failed (HTTP ${res.status})`, res.status, body?.code);
|
|
90
|
+
}
|
|
91
|
+
return body;
|
|
92
|
+
}
|
|
93
|
+
/** GET /api/license/usage — credits, current-period usage, plan summary. */
|
|
94
|
+
async getUsage(licenseKey) {
|
|
95
|
+
return this.request("GET", "/api/license/usage", licenseKey);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* POST /api/license/config — patch this key's editor config (clamped to plan).
|
|
99
|
+
* Returns the new effective config plus the plan ceiling.
|
|
100
|
+
*/
|
|
101
|
+
async updateConfig(licenseKey, partialCustomizations) {
|
|
102
|
+
return this.request("POST", "/api/license/config", licenseKey, {
|
|
103
|
+
customizations: partialCustomizations,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves which Eddyter backend to talk to and the license key the agent
|
|
3
|
+
* acts on behalf of.
|
|
4
|
+
*
|
|
5
|
+
* Base URLs mirror apps/editor/src/config/env.ts so the MCP and the editor
|
|
6
|
+
* always agree on which environment a key belongs to.
|
|
7
|
+
*/
|
|
8
|
+
const ENV_BASE_URLS = {
|
|
9
|
+
production: "https://api.eddyter.com",
|
|
10
|
+
staging: "https://api.cteditor.com",
|
|
11
|
+
local: "http://localhost:3000",
|
|
12
|
+
};
|
|
13
|
+
function parseBool(value) {
|
|
14
|
+
if (!value)
|
|
15
|
+
return false;
|
|
16
|
+
return ["1", "true", "yes", "on"].includes(value.trim().toLowerCase());
|
|
17
|
+
}
|
|
18
|
+
export function loadConfig() {
|
|
19
|
+
const explicitBase = process.env.EDDYTER_API_BASE_URL?.trim();
|
|
20
|
+
const envName = (process.env.EDDYTER_ENV?.trim() || "production").toLowerCase();
|
|
21
|
+
const baseUrl = explicitBase || ENV_BASE_URLS[envName] || ENV_BASE_URLS.production;
|
|
22
|
+
return {
|
|
23
|
+
baseUrl: baseUrl.replace(/\/+$/, ""),
|
|
24
|
+
defaultLicenseKey: process.env.EDDYTER_LICENSE_KEY?.trim() || undefined,
|
|
25
|
+
allowWrites: parseBool(process.env.EDDYTER_ALLOW_WRITES),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,aAAa,GAA2B;IAC5C,UAAU,EAAE,yBAAyB;IACrC,OAAO,EAAE,0BAA0B;IACnC,KAAK,EAAE,uBAAuB;CAC/B,CAAC;AAcF,SAAS,SAAS,CAAC,KAAyB;IAC1C,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;AACzE,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAAE,IAAI,EAAE,CAAC;IAC9D,MAAM,OAAO,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,EAAE,IAAI,YAAY,CAAC,CAAC,WAAW,EAAE,CAAC;IAEhF,MAAM,OAAO,GACX,YAAY,IAAI,aAAa,CAAC,OAAO,CAAC,IAAI,aAAa,CAAC,UAAU,CAAC;IAErE,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;QACpC,iBAAiB,EAAE,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,IAAI,EAAE,IAAI,SAAS;QACvE,WAAW,EAAE,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;KACzD,CAAC;AACJ,CAAC"}
|
package/dist/format.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turns the raw `customizations` / `planCustomizations` payloads into something
|
|
3
|
+
* an AI agent (and the admin reading its reply) can reason about:
|
|
4
|
+
*
|
|
5
|
+
* - enabled: feature is ON for this key
|
|
6
|
+
* - disabled: feature is OFF, but the plan allows turning it on
|
|
7
|
+
* - locked: the plan does not permit this feature (upsell, not a toggle)
|
|
8
|
+
*
|
|
9
|
+
* The classification logic is generic. The ONE thing worth tuning by hand is
|
|
10
|
+
* FEATURE_LABELS below: human-friendly names + which toggles are "primary"
|
|
11
|
+
* (surfaced first) vs. advanced. That's a product/UX call — see note at bottom.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Human-facing labels for the toolbarOptions toggles in EditorConfigTypes.
|
|
15
|
+
* Keys not listed here still get reported, just with a derived label.
|
|
16
|
+
*
|
|
17
|
+
* 👉 TUNE ME: order ~= importance; trim/rename to match how you describe
|
|
18
|
+
* features on the marketing site and dashboard.
|
|
19
|
+
*/
|
|
20
|
+
const FEATURE_LABELS = {
|
|
21
|
+
enableAIChat: "AI Chat",
|
|
22
|
+
enableTextFormatting: "Text formatting (bold / italic / underline)",
|
|
23
|
+
enableTableOptions: "Tables",
|
|
24
|
+
enableImageUpload: "Image upload",
|
|
25
|
+
enableComments: "Comments",
|
|
26
|
+
enableMentions: "@mentions",
|
|
27
|
+
enableCharts: "Charts",
|
|
28
|
+
enableEmbeds: "Embeds (YouTube, Loom, …)",
|
|
29
|
+
enablePdfExport: "PDF export",
|
|
30
|
+
enableDocExport: "DOC export",
|
|
31
|
+
enableSpeechToText: "Speech to text",
|
|
32
|
+
enableHeadings: "Headings",
|
|
33
|
+
enableLists: "Lists",
|
|
34
|
+
enableColors: "Text & background colors",
|
|
35
|
+
enableNotePanels: "Callout / note panels",
|
|
36
|
+
enableTodoList: "To-do lists",
|
|
37
|
+
enableFormatPainter: "Format painter",
|
|
38
|
+
};
|
|
39
|
+
function deriveLabel(key) {
|
|
40
|
+
if (FEATURE_LABELS[key])
|
|
41
|
+
return FEATURE_LABELS[key];
|
|
42
|
+
// enableFooBar -> "Foo bar"
|
|
43
|
+
const stripped = key.replace(/^enable/, "");
|
|
44
|
+
const spaced = stripped.replace(/([A-Z])/g, " $1").trim();
|
|
45
|
+
return spaced.charAt(0).toUpperCase() + spaced.slice(1).toLowerCase();
|
|
46
|
+
}
|
|
47
|
+
function asBoolMap(obj) {
|
|
48
|
+
const out = {};
|
|
49
|
+
if (obj && typeof obj === "object") {
|
|
50
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
51
|
+
if (typeof v === "boolean")
|
|
52
|
+
out[k] = v;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Classify each toolbar feature given the key's current config and the plan ceiling.
|
|
59
|
+
* A feature is `locked` when the plan explicitly disallows it (ceiling === false).
|
|
60
|
+
*/
|
|
61
|
+
export function summarizeFeatures(customizations, planCustomizations) {
|
|
62
|
+
const current = asBoolMap(customizations?.toolbarOptions);
|
|
63
|
+
const ceiling = asBoolMap(planCustomizations?.toolbarOptions);
|
|
64
|
+
const allKeys = new Set([...Object.keys(current), ...Object.keys(ceiling)]);
|
|
65
|
+
const summaries = [];
|
|
66
|
+
for (const key of allKeys) {
|
|
67
|
+
const allowedByPlan = ceiling[key] !== false; // absent or true => allowed
|
|
68
|
+
const onNow = current[key] === true;
|
|
69
|
+
let state;
|
|
70
|
+
if (!allowedByPlan)
|
|
71
|
+
state = "locked";
|
|
72
|
+
else if (onNow)
|
|
73
|
+
state = "enabled";
|
|
74
|
+
else
|
|
75
|
+
state = "disabled";
|
|
76
|
+
summaries.push({ key, label: deriveLabel(key), state });
|
|
77
|
+
}
|
|
78
|
+
// Primary features (those we have explicit labels for) first, in declared order.
|
|
79
|
+
const order = Object.keys(FEATURE_LABELS);
|
|
80
|
+
summaries.sort((a, b) => {
|
|
81
|
+
const ai = order.indexOf(a.key);
|
|
82
|
+
const bi = order.indexOf(b.key);
|
|
83
|
+
if (ai !== -1 && bi !== -1)
|
|
84
|
+
return ai - bi;
|
|
85
|
+
if (ai !== -1)
|
|
86
|
+
return -1;
|
|
87
|
+
if (bi !== -1)
|
|
88
|
+
return 1;
|
|
89
|
+
return a.label.localeCompare(b.label);
|
|
90
|
+
});
|
|
91
|
+
return summaries;
|
|
92
|
+
}
|
|
93
|
+
/** Compact, agent-readable text rendering of the feature summary. */
|
|
94
|
+
export function renderFeatureSummary(summaries) {
|
|
95
|
+
const group = (state) => summaries
|
|
96
|
+
.filter((s) => s.state === state)
|
|
97
|
+
.map((s) => ` • ${s.label}`)
|
|
98
|
+
.join("\n") || " (none)";
|
|
99
|
+
return [
|
|
100
|
+
"Enabled:",
|
|
101
|
+
group("enabled"),
|
|
102
|
+
"",
|
|
103
|
+
"Disabled (plan allows turning on):",
|
|
104
|
+
group("disabled"),
|
|
105
|
+
"",
|
|
106
|
+
"Locked by plan (upgrade to unlock):",
|
|
107
|
+
group("locked"),
|
|
108
|
+
].join("\n");
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Eddyter MCP server (step 1 — read-only).
|
|
4
|
+
*
|
|
5
|
+
* Exposes the admin's editor setup over the Model Context Protocol so AI tools
|
|
6
|
+
* (Claude Desktop, Cursor, …) can inspect a license key's status and feature
|
|
7
|
+
* config. Write tools (update_config) and usage land in later steps once the
|
|
8
|
+
* shared, plan-ceiling-guarded backend service is in place.
|
|
9
|
+
*/
|
|
10
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
11
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
import { loadConfig } from "./config.js";
|
|
14
|
+
import { EddyterClient, EddyterError } from "./client.js";
|
|
15
|
+
import { renderFeatureSummary, summarizeFeatures } from "./format.js";
|
|
16
|
+
import { getIntegrationSnippet, renderSnippet, } from "./snippets.js";
|
|
17
|
+
const config = loadConfig();
|
|
18
|
+
const client = new EddyterClient(config);
|
|
19
|
+
const server = new McpServer({
|
|
20
|
+
name: "eddyter",
|
|
21
|
+
version: "0.1.0",
|
|
22
|
+
});
|
|
23
|
+
const licenseKeyArg = {
|
|
24
|
+
licenseKey: z
|
|
25
|
+
.string()
|
|
26
|
+
.optional()
|
|
27
|
+
.describe("Eddyter license key (eddyt_…). Optional if EDDYTER_LICENSE_KEY is set in the client config."),
|
|
28
|
+
};
|
|
29
|
+
/** Wrap tool handlers so EddyterError becomes a clean, non-fatal tool error. */
|
|
30
|
+
function textResult(text) {
|
|
31
|
+
return { content: [{ type: "text", text }] };
|
|
32
|
+
}
|
|
33
|
+
function errorResult(message) {
|
|
34
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
35
|
+
}
|
|
36
|
+
server.registerTool("verify_status", {
|
|
37
|
+
title: "Verify License Status",
|
|
38
|
+
description: "Check whether an Eddyter license key is valid and active: plan name, paid/free, " +
|
|
39
|
+
"expiry, subscription status, BYOK mode, and any payment warning. Use this first " +
|
|
40
|
+
"to confirm the key works before configuring anything.",
|
|
41
|
+
inputSchema: licenseKeyArg,
|
|
42
|
+
annotations: { title: "Verify License Status", readOnlyHint: true, openWorldHint: true },
|
|
43
|
+
}, async ({ licenseKey }) => {
|
|
44
|
+
try {
|
|
45
|
+
const key = client.resolveLicenseKey(licenseKey);
|
|
46
|
+
const { data } = await client.validate(key);
|
|
47
|
+
if (!data)
|
|
48
|
+
return errorResult("No data returned from Eddyter.");
|
|
49
|
+
const expires = new Date(data.expiresAt);
|
|
50
|
+
const daysLeft = Math.ceil((expires.getTime() - Date.now()) / (1000 * 60 * 60 * 24));
|
|
51
|
+
const lines = [
|
|
52
|
+
`Status: ${data.isValid ? "✅ valid" : "❌ invalid"} (${data.status})`,
|
|
53
|
+
`Plan: ${data.planName}${data.isFreePlan ? " (free)" : ""} — ${data.paidPlan ? "paid" : "unpaid"}`,
|
|
54
|
+
`Subscription: ${data.subscriptionStatus}`,
|
|
55
|
+
`Expires: ${data.expiresAt} (${daysLeft} day(s) left)`,
|
|
56
|
+
`BYOK mode: ${data.isByokPlan ? "on (uses your own AI provider keys)" : "off (managed credits)"}`,
|
|
57
|
+
`Live API: ${data.isLiveApi ? "yes" : "no"} • domain: ${data.apiDomain}`,
|
|
58
|
+
];
|
|
59
|
+
if (data.warning) {
|
|
60
|
+
lines.push(`⚠️ ${data.warning.message} (${data.warning.daysRemaining} day(s))`);
|
|
61
|
+
}
|
|
62
|
+
return textResult(lines.join("\n"));
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
if (err instanceof EddyterError)
|
|
66
|
+
return errorResult(err.message);
|
|
67
|
+
throw err;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
server.registerTool("get_config", {
|
|
71
|
+
title: "Get Editor Config",
|
|
72
|
+
description: "Show the editor feature configuration for a license key: which features are " +
|
|
73
|
+
"enabled, which are off but allowed by the plan, and which are locked behind a " +
|
|
74
|
+
"higher plan. Use before changing config so you know what's available.",
|
|
75
|
+
inputSchema: licenseKeyArg,
|
|
76
|
+
annotations: { title: "Get Editor Config", readOnlyHint: true, openWorldHint: true },
|
|
77
|
+
}, async ({ licenseKey }) => {
|
|
78
|
+
try {
|
|
79
|
+
const key = client.resolveLicenseKey(licenseKey);
|
|
80
|
+
const { data } = await client.validate(key);
|
|
81
|
+
if (!data)
|
|
82
|
+
return errorResult("No data returned from Eddyter.");
|
|
83
|
+
const summaries = summarizeFeatures(data.customizations, data.planCustomizations);
|
|
84
|
+
const header = `Editor config for "${data.planName}" plan key:\n`;
|
|
85
|
+
return textResult(header + "\n" + renderFeatureSummary(summaries));
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
if (err instanceof EddyterError)
|
|
89
|
+
return errorResult(err.message);
|
|
90
|
+
throw err;
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
server.registerTool("get_usage", {
|
|
94
|
+
title: "Get Usage & Credits",
|
|
95
|
+
description: "Show the license key owner's AI usage and credits: plan, credits remaining, " +
|
|
96
|
+
"credits used this period, operation counts, and days until reset. Use to answer " +
|
|
97
|
+
"'am I about to hit my limit?'.",
|
|
98
|
+
inputSchema: licenseKeyArg,
|
|
99
|
+
annotations: { title: "Get Usage & Credits", readOnlyHint: true, openWorldHint: true },
|
|
100
|
+
}, async ({ licenseKey }) => {
|
|
101
|
+
try {
|
|
102
|
+
const key = client.resolveLicenseKey(licenseKey);
|
|
103
|
+
const u = await client.getUsage(key);
|
|
104
|
+
const lines = [
|
|
105
|
+
`Plan: ${u.planName}${u.isFreePlan ? " (free)" : ""}${u.isTrial ? " (trial)" : ""}`,
|
|
106
|
+
`BYOK mode: ${u.isByokPlan ? "on — uses your own provider keys (credits not consumed)" : "off — managed credits"}`,
|
|
107
|
+
`Credits remaining: ${u.credits.remaining} (plan ${u.credits.planTotal} + purchased ${u.credits.purchasedCredits})`,
|
|
108
|
+
`Used this period: ${u.currentPeriod.creditsUsed} credits ` +
|
|
109
|
+
`(${u.currentPeriod.textOperations} text ops, ${u.currentPeriod.imagesGenerated} images)`,
|
|
110
|
+
`Resets in: ${u.daysUntilReset} day(s)${u.resetDate ? ` (${u.resetDate})` : ""}`,
|
|
111
|
+
];
|
|
112
|
+
return textResult(lines.join("\n"));
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
if (err instanceof EddyterError)
|
|
116
|
+
return errorResult(err.message);
|
|
117
|
+
throw err;
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
server.registerTool("get_integration_snippet", {
|
|
121
|
+
title: "Get Integration Snippet",
|
|
122
|
+
description: "Return ready-to-paste, SSR-safe code to add the Eddyter editor to an app. " +
|
|
123
|
+
"Picks the correct client-only boundary per framework (the editor is a DOM-only " +
|
|
124
|
+
"React component). Use when setting Eddyter up in a project.",
|
|
125
|
+
inputSchema: {
|
|
126
|
+
framework: z
|
|
127
|
+
.enum(["react", "nextjs-app", "nextjs-pages", "vite", "remix"])
|
|
128
|
+
.describe("Target framework. Use 'nextjs-app' for App Router, 'nextjs-pages' for Pages Router."),
|
|
129
|
+
},
|
|
130
|
+
// Pure/static — no network, no mutation.
|
|
131
|
+
annotations: { title: "Get Integration Snippet", readOnlyHint: true, openWorldHint: false },
|
|
132
|
+
}, async ({ framework }) => {
|
|
133
|
+
const snippet = getIntegrationSnippet(framework);
|
|
134
|
+
return textResult(renderSnippet(snippet));
|
|
135
|
+
});
|
|
136
|
+
// --- Write tools: only registered when the admin opts in (EDDYTER_ALLOW_WRITES) ---
|
|
137
|
+
if (config.allowWrites) {
|
|
138
|
+
server.registerTool("update_config", {
|
|
139
|
+
title: "Update Editor Config (live)",
|
|
140
|
+
description: "Change the editor's feature configuration for a license key (e.g. turn AI chat " +
|
|
141
|
+
"or tables off/on). Send only the features you want to change — a partial patch. " +
|
|
142
|
+
"Changes are clamped to the plan (cannot enable locked features) and go live for " +
|
|
143
|
+
"all users of the key with no redeploy. Call get_config first to see current state.",
|
|
144
|
+
inputSchema: {
|
|
145
|
+
...licenseKeyArg,
|
|
146
|
+
customizations: z
|
|
147
|
+
.record(z.any())
|
|
148
|
+
.describe("Partial EditorConfigTypes patch. Feature toggles live under `toolbarOptions`, " +
|
|
149
|
+
'e.g. { "toolbarOptions": { "enableAIChat": false, "enableTableOptions": false } }. ' +
|
|
150
|
+
"Only the keys you include change; everything else is preserved."),
|
|
151
|
+
},
|
|
152
|
+
// Mutates LIVE editor behavior for every user of the key — flag it so clients
|
|
153
|
+
// can prompt for confirmation before the agent applies it.
|
|
154
|
+
annotations: {
|
|
155
|
+
title: "Update Editor Config (live)",
|
|
156
|
+
readOnlyHint: false,
|
|
157
|
+
destructiveHint: true,
|
|
158
|
+
idempotentHint: true,
|
|
159
|
+
openWorldHint: true,
|
|
160
|
+
},
|
|
161
|
+
}, async ({ licenseKey, customizations }) => {
|
|
162
|
+
try {
|
|
163
|
+
const key = client.resolveLicenseKey(licenseKey);
|
|
164
|
+
const result = await client.updateConfig(key, customizations);
|
|
165
|
+
const summaries = summarizeFeatures(result.customizations, result.planCustomizations);
|
|
166
|
+
return textResult("✅ Config updated (live now, no redeploy needed).\n\n" +
|
|
167
|
+
renderFeatureSummary(summaries));
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
if (err instanceof EddyterError)
|
|
171
|
+
return errorResult(err.message);
|
|
172
|
+
throw err;
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
async function main() {
|
|
177
|
+
const transport = new StdioServerTransport();
|
|
178
|
+
await server.connect(transport);
|
|
179
|
+
// Logs go to stderr so they never corrupt the stdio JSON-RPC channel.
|
|
180
|
+
console.error(`[eddyter-mcp] ready • backend=${config.baseUrl} • ` +
|
|
181
|
+
`default key=${config.defaultLicenseKey ? "set" : "not set"} • ` +
|
|
182
|
+
`writes=${config.allowWrites ? "ON" : "off (read-only)"}`);
|
|
183
|
+
}
|
|
184
|
+
main().catch((err) => {
|
|
185
|
+
console.error("[eddyter-mcp] fatal:", err);
|
|
186
|
+
process.exit(1);
|
|
187
|
+
});
|
|
188
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACtE,OAAO,EACL,qBAAqB,EACrB,aAAa,GAEd,MAAM,eAAe,CAAC;AAEvB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;AAC5B,MAAM,MAAM,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC;AAEzC,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,MAAM,aAAa,GAAG;IACpB,UAAU,EAAE,CAAC;SACV,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CACP,6FAA6F,CAC9F;CACJ,CAAC;AAEF,gFAAgF;AAChF,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;AACxD,CAAC;AACD,SAAS,WAAW,CAAC,OAAe;IAClC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAChF,CAAC;AAED,MAAM,CAAC,YAAY,CACjB,eAAe,EACf;IACE,KAAK,EAAE,uBAAuB;IAC9B,WAAW,EACT,kFAAkF;QAClF,kFAAkF;QAClF,uDAAuD;IACzD,WAAW,EAAE,aAAa;IAC1B,WAAW,EAAE,EAAE,KAAK,EAAE,uBAAuB,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE;CACzF,EACD,KAAK,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;IACvB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;QACjD,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC5C,IAAI,CAAC,IAAI;YAAE,OAAO,WAAW,CAAC,gCAAgC,CAAC,CAAC;QAEhE,MAAM,OAAO,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CACxB,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CACzD,CAAC;QAEF,MAAM,KAAK,GAAG;YACZ,WAAW,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,KAAK,IAAI,CAAC,MAAM,GAAG;YACpE,SAAS,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE;YAClG,iBAAiB,IAAI,CAAC,kBAAkB,EAAE;YAC1C,YAAY,IAAI,CAAC,SAAS,KAAK,QAAQ,eAAe;YACtD,cAAc,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,qCAAqC,CAAC,CAAC,CAAC,uBAAuB,EAAE;YACjG,aAAa,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,cAAc,IAAI,CAAC,SAAS,EAAE;SACzE,CAAC;QACF,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,KAAK,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO,CAAC,aAAa,UAAU,CAAC,CAAC;QACnF,CAAC;QACD,OAAO,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACtC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,YAAY;YAAE,OAAO,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACjE,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC,CACF,CAAC;AAEF,MAAM,CAAC,YAAY,CACjB,YAAY,EACZ;IACE,KAAK,EAAE,mBAAmB;IAC1B,WAAW,EACT,8EAA8E;QAC9E,gFAAgF;QAChF,uEAAuE;IACzE,WAAW,EAAE,aAAa;IAC1B,WAAW,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE;CACrF,EACD,KAAK,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;IACvB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;QACjD,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC5C,IAAI,CAAC,IAAI;YAAE,OAAO,WAAW,CAAC,gCAAgC,CAAC,CAAC;QAEhE,MAAM,SAAS,GAAG,iBAAiB,CACjC,IAAI,CAAC,cAAc,EACnB,IAAI,CAAC,kBAAkB,CACxB,CAAC;QACF,MAAM,MAAM,GAAG,sBAAsB,IAAI,CAAC,QAAQ,eAAe,CAAC;QAClE,OAAO,UAAU,CAAC,MAAM,GAAG,IAAI,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC,CAAC;IACrE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,YAAY;YAAE,OAAO,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACjE,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC,CACF,CAAC;AAEF,MAAM,CAAC,YAAY,CACjB,WAAW,EACX;IACE,KAAK,EAAE,qBAAqB;IAC5B,WAAW,EACT,8EAA8E;QAC9E,kFAAkF;QAClF,gCAAgC;IAClC,WAAW,EAAE,aAAa;IAC1B,WAAW,EAAE,EAAE,KAAK,EAAE,qBAAqB,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE;CACvF,EACD,KAAK,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;IACvB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;QACjD,MAAM,CAAC,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,KAAK,GAAG;YACZ,SAAS,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE;YACnF,cAAc,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,yDAAyD,CAAC,CAAC,CAAC,uBAAuB,EAAE;YAClH,sBAAsB,CAAC,CAAC,OAAO,CAAC,SAAS,UAAU,CAAC,CAAC,OAAO,CAAC,SAAS,gBAAgB,CAAC,CAAC,OAAO,CAAC,gBAAgB,GAAG;YACnH,qBAAqB,CAAC,CAAC,aAAa,CAAC,WAAW,WAAW;gBACzD,IAAI,CAAC,CAAC,aAAa,CAAC,cAAc,cAAc,CAAC,CAAC,aAAa,CAAC,eAAe,UAAU;YAC3F,cAAc,CAAC,CAAC,cAAc,UAAU,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;SACjF,CAAC;QACF,OAAO,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACtC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,YAAY;YAAE,OAAO,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACjE,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC,CACF,CAAC;AAEF,MAAM,CAAC,YAAY,CACjB,yBAAyB,EACzB;IACE,KAAK,EAAE,yBAAyB;IAChC,WAAW,EACT,4EAA4E;QAC5E,iFAAiF;QACjF,6DAA6D;IAC/D,WAAW,EAAE;QACX,SAAS,EAAE,CAAC;aACT,IAAI,CAAC,CAAC,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;aAC9D,QAAQ,CAAC,qFAAqF,CAAC;KACnG;IACD,yCAAyC;IACzC,WAAW,EAAE,EAAE,KAAK,EAAE,yBAAyB,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE;CAC5F,EACD,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;IACtB,MAAM,OAAO,GAAG,qBAAqB,CAAC,SAAsB,CAAC,CAAC;IAC9D,OAAO,UAAU,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;AAC5C,CAAC,CACF,CAAC;AAEF,qFAAqF;AACrF,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;IACvB,MAAM,CAAC,YAAY,CACjB,eAAe,EACf;QACE,KAAK,EAAE,6BAA6B;QACpC,WAAW,EACT,iFAAiF;YACjF,kFAAkF;YAClF,kFAAkF;YAClF,oFAAoF;QACtF,WAAW,EAAE;YACX,GAAG,aAAa;YAChB,cAAc,EAAE,CAAC;iBACd,MAAM,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;iBACf,QAAQ,CACP,gFAAgF;gBAC9E,qFAAqF;gBACrF,iEAAiE,CACpE;SACJ;QACD,8EAA8E;QAC9E,2DAA2D;QAC3D,WAAW,EAAE;YACX,KAAK,EAAE,6BAA6B;YACpC,YAAY,EAAE,KAAK;YACnB,eAAe,EAAE,IAAI;YACrB,cAAc,EAAE,IAAI;YACpB,aAAa,EAAE,IAAI;SACpB;KACF,EACD,KAAK,EAAE,EAAE,UAAU,EAAE,cAAc,EAAE,EAAE,EAAE;QACvC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;YACjD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,YAAY,CACtC,GAAG,EACH,cAAyC,CAC1C,CAAC;YACF,MAAM,SAAS,GAAG,iBAAiB,CACjC,MAAM,CAAC,cAAc,EACrB,MAAM,CAAC,kBAAkB,CAC1B,CAAC;YACF,OAAO,UAAU,CACf,sDAAsD;gBACpD,oBAAoB,CAAC,SAAS,CAAC,CAClC,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,YAAY;gBAAE,OAAO,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACjE,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,sEAAsE;IACtE,OAAO,CAAC,KAAK,CACX,iCAAiC,MAAM,CAAC,OAAO,KAAK;QAClD,eAAe,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,KAAK;QAChE,UAAU,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,iBAAiB,EAAE,CAC5D,CAAC;AACJ,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,GAAG,CAAC,CAAC;IAC3C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/snippets.js
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical, copy-paste integration snippets for the `eddyter` package.
|
|
3
|
+
*
|
|
4
|
+
* The editor is a CLIENT-ONLY React component (it runs Lexical against the DOM
|
|
5
|
+
* and ships no "use client" / SSR guard itself). So every server-rendered
|
|
6
|
+
* framework here loads it behind an SSR-safe boundary — this is the #1 thing a
|
|
7
|
+
* plain LLM gets wrong, and the reason this tool exists.
|
|
8
|
+
*/
|
|
9
|
+
const INSTALL = "npm install eddyter";
|
|
10
|
+
const baseEditor = (envExpr) => `import {
|
|
11
|
+
ConfigurableEditorWithAuth,
|
|
12
|
+
EditorProvider,
|
|
13
|
+
} from 'eddyter';
|
|
14
|
+
import 'eddyter/style.css';
|
|
15
|
+
|
|
16
|
+
export default function Editor() {
|
|
17
|
+
return (
|
|
18
|
+
<EditorProvider>
|
|
19
|
+
<ConfigurableEditorWithAuth
|
|
20
|
+
apiKey={${envExpr}}
|
|
21
|
+
onChange={(html) => console.log(html)}
|
|
22
|
+
onAuthError={(e) => console.error('Eddyter auth failed:', e)}
|
|
23
|
+
/>
|
|
24
|
+
</EditorProvider>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
`;
|
|
28
|
+
export function getIntegrationSnippet(framework) {
|
|
29
|
+
switch (framework) {
|
|
30
|
+
case "react":
|
|
31
|
+
case "vite": {
|
|
32
|
+
const isVite = framework === "vite";
|
|
33
|
+
const varName = isVite
|
|
34
|
+
? "VITE_EDDYTER_API_KEY"
|
|
35
|
+
: "REACT_APP_EDDYTER_API_KEY";
|
|
36
|
+
const envExpr = isVite
|
|
37
|
+
? "import.meta.env.VITE_EDDYTER_API_KEY"
|
|
38
|
+
: "process.env.REACT_APP_EDDYTER_API_KEY";
|
|
39
|
+
return {
|
|
40
|
+
framework,
|
|
41
|
+
installCommand: INSTALL,
|
|
42
|
+
env: { file: ".env", varName, line: `${varName}=your_key_here` },
|
|
43
|
+
files: [
|
|
44
|
+
{ path: "src/components/Editor.tsx", action: "create", content: baseEditor(envExpr + "!") },
|
|
45
|
+
],
|
|
46
|
+
notes: [
|
|
47
|
+
"Import <Editor /> wherever you want the editor.",
|
|
48
|
+
"No SSR boundary needed — this is a client-rendered app.",
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
case "nextjs-app": {
|
|
53
|
+
const varName = "NEXT_PUBLIC_EDDYTER_API_KEY";
|
|
54
|
+
return {
|
|
55
|
+
framework,
|
|
56
|
+
installCommand: INSTALL,
|
|
57
|
+
env: { file: ".env.local", varName, line: `${varName}=your_key_here` },
|
|
58
|
+
files: [
|
|
59
|
+
{
|
|
60
|
+
path: "components/Editor.tsx",
|
|
61
|
+
action: "create",
|
|
62
|
+
content: `'use client';\n\n` + baseEditor("process.env.NEXT_PUBLIC_EDDYTER_API_KEY!"),
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
path: "components/EditorClient.tsx",
|
|
66
|
+
action: "create",
|
|
67
|
+
content: `'use client';
|
|
68
|
+
|
|
69
|
+
import dynamic from 'next/dynamic';
|
|
70
|
+
|
|
71
|
+
// Load the editor with SSR disabled — it is a client-only (DOM) component.
|
|
72
|
+
const Editor = dynamic(() => import('./Editor'), { ssr: false });
|
|
73
|
+
|
|
74
|
+
export default function EditorClient() {
|
|
75
|
+
return <Editor />;
|
|
76
|
+
}
|
|
77
|
+
`,
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
notes: [
|
|
81
|
+
"Render <EditorClient /> from any App Router page (server or client).",
|
|
82
|
+
"ssr:false is required — the editor touches the DOM and will crash if server-rendered.",
|
|
83
|
+
],
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
case "nextjs-pages": {
|
|
87
|
+
const varName = "NEXT_PUBLIC_EDDYTER_API_KEY";
|
|
88
|
+
return {
|
|
89
|
+
framework,
|
|
90
|
+
installCommand: INSTALL,
|
|
91
|
+
env: { file: ".env.local", varName, line: `${varName}=your_key_here` },
|
|
92
|
+
files: [
|
|
93
|
+
{ path: "components/Editor.tsx", action: "create", content: baseEditor("process.env.NEXT_PUBLIC_EDDYTER_API_KEY!") },
|
|
94
|
+
{
|
|
95
|
+
path: "pages/editor.tsx",
|
|
96
|
+
action: "create",
|
|
97
|
+
content: `import dynamic from 'next/dynamic';
|
|
98
|
+
|
|
99
|
+
// Disable SSR — the editor is a client-only (DOM) component.
|
|
100
|
+
const Editor = dynamic(() => import('../components/Editor'), { ssr: false });
|
|
101
|
+
|
|
102
|
+
export default function EditorPage() {
|
|
103
|
+
return <Editor />;
|
|
104
|
+
}
|
|
105
|
+
`,
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
notes: [
|
|
109
|
+
"Visit /editor, or import the dynamic Editor into any page.",
|
|
110
|
+
"ssr:false is required for the Pages Router too.",
|
|
111
|
+
],
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
case "remix": {
|
|
115
|
+
const varName = "EDDYTER_API_KEY";
|
|
116
|
+
return {
|
|
117
|
+
framework,
|
|
118
|
+
installCommand: INSTALL,
|
|
119
|
+
env: { file: ".env", varName, line: `${varName}=your_key_here` },
|
|
120
|
+
files: [
|
|
121
|
+
{
|
|
122
|
+
path: "app/components/Editor.client.tsx",
|
|
123
|
+
action: "create",
|
|
124
|
+
// `.client.tsx` keeps this out of the server bundle in Remix.
|
|
125
|
+
content: baseEditor("(window as any).ENV?.EDDYTER_API_KEY"),
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
path: "app/routes/editor.tsx",
|
|
129
|
+
action: "create",
|
|
130
|
+
content: `import { ClientOnly } from 'remix-utils/client-only';
|
|
131
|
+
import Editor from '~/components/Editor.client';
|
|
132
|
+
|
|
133
|
+
export default function EditorRoute() {
|
|
134
|
+
// ClientOnly ensures the DOM-only editor never renders on the server.
|
|
135
|
+
return <ClientOnly fallback={null}>{() => <Editor />}</ClientOnly>;
|
|
136
|
+
}
|
|
137
|
+
`,
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
notes: [
|
|
141
|
+
"The `.client.tsx` suffix + ClientOnly keep the editor off the server.",
|
|
142
|
+
"Expose EDDYTER_API_KEY to the client via your root loader's window.ENV, or use a public env strategy.",
|
|
143
|
+
"`remix-utils` provides ClientOnly: npm install remix-utils",
|
|
144
|
+
],
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/** Compact, agent-readable rendering of a snippet. */
|
|
150
|
+
export function renderSnippet(s) {
|
|
151
|
+
const files = s.files
|
|
152
|
+
.map((f) => `--- ${f.action.toUpperCase()} ${f.path} ---\n${f.content}`)
|
|
153
|
+
.join("\n");
|
|
154
|
+
return [
|
|
155
|
+
`Framework: ${s.framework}`,
|
|
156
|
+
``,
|
|
157
|
+
`1) Install:\n ${s.installCommand}`,
|
|
158
|
+
``,
|
|
159
|
+
`2) Env (${s.env.file}):\n ${s.env.line}`,
|
|
160
|
+
``,
|
|
161
|
+
`3) Files:`,
|
|
162
|
+
files,
|
|
163
|
+
``,
|
|
164
|
+
`Notes:`,
|
|
165
|
+
s.notes.map((n) => ` • ${n}`).join("\n"),
|
|
166
|
+
].join("\n");
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=snippets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snippets.js","sourceRoot":"","sources":["../src/snippets.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "eddyter-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "Model Context Protocol server for Eddyter — let your AI agent set up and configure the Eddyter editor.",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"eddyter-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"main": "dist/index.js",
|
|
11
|
+
"files": [
|
|
12
|
+
"dist"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"dev": "tsc --watch",
|
|
17
|
+
"start": "node dist/index.js",
|
|
18
|
+
"inspect": "npx @modelcontextprotocol/inspector node dist/index.js",
|
|
19
|
+
"typecheck": "tsc --noEmit"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
23
|
+
"zod": "^3.23.8"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^20.14.0",
|
|
27
|
+
"typescript": "5.8.2"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18"
|
|
31
|
+
}
|
|
32
|
+
}
|