Sign In

@clipy/mcp

Package Overview
Dependencies
Maintainers
1
Versions
22
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@clipy/mcp - npm Package Compare versions

Comparing version
0.8.6
to
0.9.0
+98
dist/arecRange.js
/**
* Range slicing for compiled AREC markdown (`recording.md`).
*
* The compiler emits a header block (title, metadata, visual gaps, frames)
* followed by `## Transcript` and then one `### MM:SS` heading every ~150s,
* with `[MM:SS] spoken text` lines under it. Slicing at heading granularity
* keeps a section's interleaved frame images attached to the words they
* illustrate, which per-line filtering would strip.
*
* Kept dependency-free and side-effect-free so it can be unit tested offline.
*/
const HEADING = /^###\s+(\d{1,2}:\d{2}(?::\d{2})?)\s*$/;
/** `MM:SS` or `H:MM:SS` → milliseconds. Returns null if it isn't a timestamp. */
export function parseTimestamp(value) {
const parts = value.trim().split(":");
if (parts.length < 2 || parts.length > 3)
return null;
const nums = parts.map((p) => Number(p));
if (nums.some((n) => !Number.isFinite(n) || n < 0))
return null;
const [h, m, s] = parts.length === 3 ? nums : [0, nums[0], nums[1]];
return (h * 3600 + m * 60 + s) * 1000;
}
/** Splits compiled AREC markdown into its header block and timestamped sections. */
export function parseArecMarkdown(markdown) {
const lines = markdown.split("\n");
const headerLines = [];
const sections = [];
let current = null;
for (const line of lines) {
const m = HEADING.exec(line);
const ms = m ? parseTimestamp(m[1]) : null;
if (ms !== null) {
if (current) {
sections.push({ startMs: current.startMs, endMs: ms, text: current.lines.join("\n").trim() });
}
current = { startMs: ms, lines: [line] };
continue;
}
if (current)
current.lines.push(line);
else
headerLines.push(line);
}
if (current) {
sections.push({
startMs: current.startMs,
endMs: Number.POSITIVE_INFINITY,
text: current.lines.join("\n").trim(),
});
}
return { header: headerLines.join("\n").trim(), sections };
}
/** Default ceiling on a full (unranged) read, in characters. */
export const MAX_MARKDOWN_CHARS = 60_000;
/**
* Returns the header block plus every section overlapping [startMs, endMs).
* An open-ended range (either bound omitted) extends to the document edge.
*/
export function sliceArecMarkdown(markdown, range = {}, maxChars = MAX_MARKDOWN_CHARS) {
const { header, sections } = parseArecMarkdown(markdown);
const hasRange = range.startMs !== undefined || range.endMs !== undefined;
if (!hasRange) {
if (markdown.length <= maxChars) {
return {
markdown,
sectionsReturned: sections.length,
sectionsTotal: sections.length,
truncated: false,
};
}
return {
markdown: `${markdown.slice(0, maxChars)}\n\n[... truncated at ${maxChars} characters. This recording is long — re-read a portion with startMs/endMs (the document is sectioned every ~150s, headings are the [MM:SS] markers).]\n`,
sectionsReturned: sections.length,
sectionsTotal: sections.length,
truncated: true,
note: "Truncated. Use startMs/endMs to read a specific span.",
};
}
const from = range.startMs ?? 0;
const to = range.endMs ?? Number.POSITIVE_INFINITY;
const kept = sections.filter((s) => s.startMs < to && s.endMs > from);
const body = kept.length
? kept.map((s) => s.text).join("\n\n")
: "_No transcript sections fall in that range._";
let out = `${header}\n\n${body}\n`;
let truncated = false;
if (out.length > maxChars) {
out = `${out.slice(0, maxChars)}\n\n[... truncated at ${maxChars} characters. Narrow the range.]\n`;
truncated = true;
}
return {
markdown: out,
sectionsReturned: kept.length,
sectionsTotal: sections.length,
truncated,
};
}
+3
-2
{
"name": "@clipy/mcp",
"version": "0.8.6",
"version": "0.9.0",
"mcpName": "online.clipy/mcp",

@@ -35,3 +35,4 @@ "description": "Model Context Protocol (MCP) server for Clipy — give Claude, Cursor, and other AI agents read access to your screen recordings' transcripts, AI summaries, and key moments with frames.",

"prepublishOnly": "npm run build",
"start": "node dist/index.js"
"start": "node dist/index.js",
"test": "npm run build && node scripts/arec-range.test.mjs"
},

@@ -38,0 +39,0 @@ "dependencies": {

+3
-0

@@ -114,2 +114,5 @@ # @clipy/mcp

| `abort_recording` | Discard the active session; nothing is uploaded. |
| `list_context_documents` | List the user's **context documents** — YouTube videos and local video files they imported with `clipy context import`, so agents can read them. A separate library from their own screen recordings. |
| `get_context_document` | One context document's metadata: source, duration, tags, the server's classification (video type, whether visual evidence is needed, planned moments), and which transcript/frames exist. Not the transcript itself. |
| `read_context_document` | Read a context document as compiled markdown — header, metadata, then the timestamped transcript with frame captions interleaved. Takes `startMs`/`endMs` so you can walk a two-hour video section by section instead of flooding your context. |
| `replace_transcript` | **Replace a recording's transcript** with text you author (needs the `ingest` scope). Fix a bad speech-to-text pass, translate, or enrich a silent agent capture; the summary regenerates automatically. Marked as agent-edited, never passed off as speech-to-text. |

@@ -116,0 +119,0 @@

Sorry, the diff of this file is too big to display