@cinefiller/mcp-server 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/LICENSE ADDED
@@ -0,0 +1,27 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CineFiller
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ ---
24
+
25
+ This licence covers the agent skills, the plugin manifest and the MCP stdio
26
+ bridge only. The CineFiller platform, its hosted API and its models are
27
+ proprietary and are not licensed by this file.
package/README.md ADDED
@@ -0,0 +1,89 @@
1
+ # @cinefiller/mcp-server
2
+
3
+ The Cinefiller MCP server for AI agents — the same Cinefiller API, speaking
4
+ your agent's protocol. A Claude Code session can take a one-paragraph brief
5
+ to a reviewed storyboard: director treatments to choose from, review gates a
6
+ human approves, every asset landing on your project's CDN and in your
7
+ project workspace automatically.
8
+
9
+ This package is a thin stdio bridge: it speaks MCP over stdio locally and
10
+ forwards to the hosted Cinefiller MCP endpoint
11
+ (`https://www.cinefiller.com/mcp`) with your program API key. Same API key,
12
+ same credits, same projects as the REST API.
13
+
14
+ > **Status: API v1 preview.** The full v1 tool surface — the marketing-video
15
+ > group (project, import, concepts, storyboard, gates, jobs, assets) and the M3
16
+ > production tools (`generate_shot`, `assemble_cut`, `verify_cut`, now wired to
17
+ > the render façade in the backend) — is exposed through this bridge. Schemas
18
+ > are frozen (additive-only); public access to the hosted endpoint ships as the
19
+ > MCP server comes online.
20
+
21
+ ## Install (Claude Code)
22
+
23
+ ```bash
24
+ claude mcp add cinefiller -- npx @cinefiller/mcp-server
25
+ ```
26
+
27
+ Set your key first (or pass it inline with `--env`):
28
+
29
+ ```bash
30
+ export CINEFILLER_API_KEY=cf_live_sk_...
31
+ # or:
32
+ claude mcp add cinefiller --env CINEFILLER_API_KEY=cf_live_sk_... -- npx @cinefiller/mcp-server
33
+ ```
34
+
35
+ Or with a plain `.mcp.json`:
36
+
37
+ ```json
38
+ {
39
+ "mcpServers": {
40
+ "cinefiller": {
41
+ "command": "npx",
42
+ "args": ["@cinefiller/mcp-server"],
43
+ "env": { "CINEFILLER_API_KEY": "cf_live_sk_..." }
44
+ }
45
+ }
46
+ }
47
+ ```
48
+
49
+ Advanced clients that speak streamable HTTP directly can skip the bridge:
50
+
51
+ ```bash
52
+ claude mcp add --transport http cinefiller https://www.cinefiller.com/mcp \
53
+ --header "Authorization: Bearer cf_live_sk_..."
54
+ ```
55
+
56
+ ## Configuration
57
+
58
+ | Env var | Required | Meaning |
59
+ |---|---|---|
60
+ | `CINEFILLER_API_KEY` | yes | Your program key (`cf_live_sk_…`), minted in the developer dashboard — the same key used for the REST API. |
61
+ | `CINEFILLER_MCP_URL` | no | Endpoint override (default `https://www.cinefiller.com/mcp`). Point at `http://localhost:4050/mcp` for a local backend. |
62
+ | `CINEFILLER_PROJECT_ID` | no | Default project for tools that omit `project_id`; an explicit tool argument always wins. |
63
+
64
+ ## What the tools are
65
+
66
+ The marketing-video pipeline with human review gates:
67
+ `create_project` / `import_source` → `generate_concepts` →
68
+ (human approves `concept_select`) → `generate_storyboard` →
69
+ (human approves `storyboard_approval`) → M3 production. Plus
70
+ `gate_status` / `gate_wait` / `gate_respond`, `get_job_status`,
71
+ `list_assets`, `export_asset`, `project_status`.
72
+
73
+ Every tool maps 1:1 to a REST endpoint under
74
+ `www.cinefiller.com/api/public/v1` — same auth, same rate limits, same
75
+ credit meter, same error codes. Full contract:
76
+ `docs/CINEFILLER_MCP_INTERFACE.html` in the main repo.
77
+
78
+ ## Publishing (maintainers)
79
+
80
+ This package is delivered publish-ready but **not published by CI or
81
+ agents** — publishing is a maintainer step:
82
+
83
+ ```bash
84
+ cd mcp-server-npm
85
+ npm pack # inspect the tarball
86
+ npm publish --access public # requires npm org access to @cinefiller
87
+ ```
88
+
89
+ No build step; the bin script is dependency-free (Node ≥ 18).
@@ -0,0 +1,177 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @cinefiller/mcp-server — stdio ↔ streamable-HTTP bridge.
4
+ *
5
+ * Speaks MCP over stdio locally (what `claude mcp add cinefiller -- npx
6
+ * @cinefiller/mcp-server` launches) and forwards every JSON-RPC message to
7
+ * the hosted Cinefiller MCP endpoint with the partner's program API key as
8
+ * the Bearer token — the same cf_live_sk_ key used for the REST API.
9
+ *
10
+ * Environment:
11
+ * CINEFILLER_API_KEY required — your program key (cf_live_sk_…)
12
+ * CINEFILLER_MCP_URL optional — defaults to
13
+ * https://www.cinefiller.com/mcp
14
+ * CINEFILLER_PROJECT_ID optional — default project for tools that omit
15
+ * project_id (sent as X-Cinefiller-Project)
16
+ *
17
+ * No dependencies: Node >= 18 (global fetch + web streams).
18
+ */
19
+
20
+ import { createInterface } from "node:readline";
21
+ import process from "node:process";
22
+
23
+ /**
24
+ * An `.mcp.json` `${VAR}` reference whose variable is unset does NOT arrive as
25
+ * undefined — Claude Code passes the *literal* text `${VAR}` through. A plain
26
+ * falsy check therefore sees a non-empty string and lets an unconfigured key
27
+ * reach the server, which answers 401 "Invalid API key" and sends the user
28
+ * hunting for a bad key rather than a missing one. Treat an unexpanded
29
+ * placeholder as unset so the guidance below still fires.
30
+ */
31
+ const isUnset = (v) => !v || /^\$\{[A-Za-z_][A-Za-z0-9_]*(:-.*)?\}$/.test(v);
32
+ const envOr = (name, fallback) =>
33
+ isUnset(process.env[name]) ? fallback : process.env[name];
34
+
35
+ const MCP_URL = envOr("CINEFILLER_MCP_URL", "https://www.cinefiller.com/mcp");
36
+ const API_KEY = envOr("CINEFILLER_API_KEY", undefined);
37
+ const PROJECT_ID = envOr("CINEFILLER_PROJECT_ID", undefined);
38
+
39
+ if (!API_KEY) {
40
+ process.stderr.write(
41
+ "[cinefiller-mcp] CINEFILLER_API_KEY is not set.\n" +
42
+ "Create a key in the Cinefiller developer dashboard, then either:\n" +
43
+ " export CINEFILLER_API_KEY=cf_live_sk_... # plugin / .mcp.json\n" +
44
+ ' claude mcp add cinefiller --env CINEFILLER_API_KEY=cf_live_sk_... -- npx @cinefiller/mcp-server\n'
45
+ );
46
+ process.exit(1);
47
+ }
48
+
49
+ let sessionId = null; // mcp-session-id echoed after initialize
50
+ let protocolVersion = null; // recorded from the initialize result
51
+
52
+ function writeMessage(msg) {
53
+ process.stdout.write(JSON.stringify(msg) + "\n");
54
+ }
55
+
56
+ function errorResponse(id, code, message) {
57
+ return { jsonrpc: "2.0", id, error: { code, message } };
58
+ }
59
+
60
+ /** Parse an SSE body, emitting each `data:` JSON payload to stdout. */
61
+ async function pumpSSE(response) {
62
+ const decoder = new TextDecoder();
63
+ let buffer = "";
64
+ for await (const chunk of response.body) {
65
+ buffer += decoder.decode(chunk, { stream: true });
66
+ // SSE allows \r\n line endings (sse-starlette uses them) — normalize.
67
+ buffer = buffer.split("\r\n").join("\n");
68
+ let sep;
69
+ while ((sep = buffer.indexOf("\n\n")) !== -1) {
70
+ const rawEvent = buffer.slice(0, sep);
71
+ buffer = buffer.slice(sep + 2);
72
+ const dataLines = rawEvent
73
+ .split("\n")
74
+ .filter((l) => l.startsWith("data:"))
75
+ .map((l) => l.slice(5).trimStart());
76
+ if (!dataLines.length) continue;
77
+ try {
78
+ handleServerMessage(JSON.parse(dataLines.join("\n")));
79
+ } catch (e) {
80
+ process.stderr.write(`[cinefiller-mcp] bad SSE payload: ${e}\n`);
81
+ }
82
+ }
83
+ }
84
+ }
85
+
86
+ function handleServerMessage(msg) {
87
+ // Record the negotiated protocol version from the initialize result so
88
+ // subsequent HTTP requests carry the MCP-Protocol-Version header.
89
+ if (msg && msg.result && msg.result.protocolVersion) {
90
+ protocolVersion = msg.result.protocolVersion;
91
+ }
92
+ writeMessage(msg);
93
+ }
94
+
95
+ async function forward(message) {
96
+ const headers = {
97
+ "content-type": "application/json",
98
+ accept: "application/json, text/event-stream",
99
+ authorization: `Bearer ${API_KEY}`,
100
+ };
101
+ if (sessionId) headers["mcp-session-id"] = sessionId;
102
+ if (protocolVersion) headers["mcp-protocol-version"] = protocolVersion;
103
+ if (PROJECT_ID) headers["x-cinefiller-project"] = PROJECT_ID;
104
+
105
+ let response;
106
+ try {
107
+ response = await fetch(MCP_URL, {
108
+ method: "POST",
109
+ headers,
110
+ body: JSON.stringify(message),
111
+ });
112
+ } catch (e) {
113
+ if (message.id !== undefined) {
114
+ writeMessage(errorResponse(
115
+ message.id, -32001, `cinefiller endpoint unreachable: ${e.message}`));
116
+ } else {
117
+ process.stderr.write(`[cinefiller-mcp] send failed: ${e.message}\n`);
118
+ }
119
+ return;
120
+ }
121
+
122
+ const sid = response.headers.get("mcp-session-id");
123
+ if (sid) sessionId = sid;
124
+
125
+ if (response.status === 202) return; // notification accepted, no body
126
+
127
+ const contentType = (response.headers.get("content-type") || "").split(";")[0];
128
+
129
+ if (!response.ok && contentType !== "application/json" && contentType !== "text/event-stream") {
130
+ const text = await response.text();
131
+ if (message.id !== undefined) {
132
+ writeMessage(errorResponse(
133
+ message.id, -32000, `HTTP ${response.status} from ${MCP_URL}: ${text.slice(0, 500)}`));
134
+ } else {
135
+ process.stderr.write(`[cinefiller-mcp] HTTP ${response.status}: ${text.slice(0, 500)}\n`);
136
+ }
137
+ return;
138
+ }
139
+
140
+ if (contentType === "text/event-stream") {
141
+ await pumpSSE(response);
142
+ return;
143
+ }
144
+
145
+ const text = await response.text();
146
+ if (!text.trim()) return;
147
+ try {
148
+ handleServerMessage(JSON.parse(text));
149
+ } catch (e) {
150
+ if (message.id !== undefined) {
151
+ writeMessage(errorResponse(
152
+ message.id, -32700, `unparseable response from ${MCP_URL}: ${e.message}`));
153
+ }
154
+ }
155
+ }
156
+
157
+ const rl = createInterface({ input: process.stdin, terminal: false });
158
+ const inflight = new Set();
159
+
160
+ rl.on("line", (line) => {
161
+ const trimmed = line.trim();
162
+ if (!trimmed) return;
163
+ let message;
164
+ try {
165
+ message = JSON.parse(trimmed);
166
+ } catch (e) {
167
+ process.stderr.write(`[cinefiller-mcp] ignoring unparseable input line\n`);
168
+ return;
169
+ }
170
+ const task = forward(message).finally(() => inflight.delete(task));
171
+ inflight.add(task);
172
+ });
173
+
174
+ rl.on("close", async () => {
175
+ await Promise.allSettled([...inflight]);
176
+ process.exit(0);
177
+ });
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@cinefiller/mcp-server",
3
+ "version": "0.1.0",
4
+ "description": "Cinefiller MCP server — a stdio bridge to the hosted Cinefiller API (www.cinefiller.com/mcp). Same API key, same credits, same projects as the REST API.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "cinefiller-mcp-server": "bin/cinefiller-mcp.js"
9
+ },
10
+ "files": [
11
+ "bin/",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "engines": {
16
+ "node": ">=18"
17
+ },
18
+ "keywords": [
19
+ "mcp",
20
+ "model-context-protocol",
21
+ "cinefiller",
22
+ "video-generation"
23
+ ],
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/cinefiller/cinefiller.git",
27
+ "directory": "mcp-server-npm"
28
+ }
29
+ }