@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 +27 -0
- package/README.md +89 -0
- package/bin/cinefiller-mcp.js +177 -0
- package/package.json +29 -0
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
|
+
}
|