run_algorithm
Execute media protection algorithms to safeguard content from AI training and detect AI-generated material using the Sidearm MCP Server.
Instructions
Run one or more named algorithms on media. Provide algorithm IDs (from list_algorithms) and either a public media_url or base64-encoded media content. For text, use the text param. Returns a job_id for async processing — use check_job to poll for results. Requires credits.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| algorithms | Yes | Algorithm IDs to run (e.g. ['nightshade', 'glaze']). Use list_algorithms to discover IDs. | |
| media_url | No | Public URL of the media file to process | |
| media | No | Base64-encoded media content (alternative to media_url) | |
| text | No | Plain text content (for text algorithms like spectra, textmark) | |
| mime | No | MIME type of the media (e.g. image/png, audio/wav) | |
| tags | No | Tags for organizing and filtering | |
| webhook_url | No | URL to receive a POST when the job completes | |
| c2pa_wrap | No | Wrap output in C2PA provenance signing (default: true) | |
| filename | No | Original filename for human-readable output naming |
Implementation Reference
- src/tools/run-algorithm.ts:5-95 (handler)Main tool implementation: registers the 'run_algorithm' MCP tool with its Zod schema (lines 12-52) and async handler function (lines 53-93) that processes input parameters, builds the request body, calls the API client's post method, and returns the job_id and status_url or error response.
export function register(server: McpServer, api: ApiClient): void { server.tool( "run_algorithm", "Run one or more named algorithms on media. Provide algorithm IDs (from list_algorithms) " + "and either a public media_url or base64-encoded media content. For text, use the text param. " + "Returns a job_id for async processing — use check_job to poll for results. Requires credits.", { algorithms: z .array(z.string()) .min(1) .describe( "Algorithm IDs to run (e.g. ['nightshade', 'glaze']). Use list_algorithms to discover IDs.", ), media_url: z .string() .url() .optional() .describe("Public URL of the media file to process"), media: z .string() .optional() .describe("Base64-encoded media content (alternative to media_url)"), text: z .string() .optional() .describe("Plain text content (for text algorithms like spectra, textmark)"), mime: z .string() .optional() .describe("MIME type of the media (e.g. image/png, audio/wav)"), tags: z .array(z.string()) .optional() .describe("Tags for organizing and filtering"), webhook_url: z .string() .url() .optional() .describe("URL to receive a POST when the job completes"), c2pa_wrap: z .boolean() .optional() .describe("Wrap output in C2PA provenance signing (default: true)"), filename: z .string() .optional() .describe("Original filename for human-readable output naming"), }, async (params) => { try { const body: Record<string, unknown> = { algorithms: params.algorithms, }; if (params.media_url) body.media_url = params.media_url; if (params.media) body.media = params.media; if (params.text) body.text = params.text; if (params.mime) body.mime = params.mime; if (params.tags) body.tags = params.tags; if (params.webhook_url) body.webhook_url = params.webhook_url; if (params.c2pa_wrap !== undefined) body.c2pa_wrap = params.c2pa_wrap; if (params.filename) body.filename = params.filename; const result = await api.post("/api/v1/run", body); const res = result as { job_id: string; status_url: string }; return { content: [ { type: "text" as const, text: `Job created successfully.\n\n` + `Job ID: ${res.job_id}\n` + `Status URL: ${res.status_url}\n\n` + `Use check_job with this job_id to poll for results.`, }, ], }; } catch (err) { return { content: [ { type: "text" as const, text: `Error: ${err instanceof Error ? err.message : String(err)}`, }, ], isError: true as const, }; } }, ); } - src/index.ts:7-43 (registration)Tool registration: imports the register function from run-algorithm.ts as 'runAlgorithm' (line 7) and calls it with the server and api instances (line 43) to register the run_algorithm tool with the MCP server.
import { register as runAlgorithm } from "./tools/run-algorithm.js"; import { register as protectMedia } from "./tools/protect-media.js"; import { register as checkJob } from "./tools/check-job.js"; import { register as searchMedia } from "./tools/search-media.js"; import { register as listSearches } from "./tools/list-searches.js"; import { register as detectAi } from "./tools/detect-ai.js"; import { register as detectFingerprint } from "./tools/detect-fingerprint.js"; import { register as detectMembership } from "./tools/detect-membership.js"; import { register as registerMedia } from "./tools/register-media.js"; import { register as listMedia } from "./tools/list-media.js"; import { register as getMedia } from "./tools/get-media.js"; import { register as updateMedia } from "./tools/update-media.js"; import { register as deleteMedia } from "./tools/delete-media.js"; import { register as getRights } from "./tools/get-rights.js"; import { register as getBilling } from "./tools/get-billing.js"; const apiKey = process.env.SDRM_API_KEY; if (!apiKey) { process.stderr.write( "Error: SDRM_API_KEY environment variable is required.\n" + "Get your API key at https://sdrm.io/api-keys\n", ); process.exit(1); } const api = new ApiClient(apiKey, process.env.SDRM_BASE_URL); const server = new McpServer({ name: "sdrm", version: "0.1.0", }); // Discovery listAlgorithms(server, api); // Protection runAlgorithm(server, api); - src/api.ts:25-31 (helper)API client helper: the post method used by run_algorithm handler to send requests to the API endpoint (specifically /api/v1/run). Handles JSON serialization and authorization headers.
async post<T = unknown>(path: string, body: unknown): Promise<T> { return this.request<T>(new URL(`${this.baseUrl}${path}`), { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); } - src/tools/list-algorithms.ts:5-47 (helper)Related helper tool: list_algorithms is used to discover valid algorithm IDs before calling run_algorithm, as mentioned in the run_algorithm description. It queries the /api/v1/algorithms endpoint.
export function register(server: McpServer, api: ApiClient): void { server.tool( "list_algorithms", "List available algorithms for media protection, watermarking, and AI content disruption. " + "Returns algorithm IDs, names, supported media types, and descriptions. " + "Use this to discover valid algorithm IDs before calling run_algorithm. " + "Filter by category (open = research algorithms, proprietary = Sidearm bundles) " + "or media_type (image, video, audio, text, pdf, gif).", { category: z .enum(["open", "proprietary"]) .optional() .describe("Filter by algorithm category"), media_type: z .enum(["image", "video", "audio", "text", "pdf", "gif"]) .optional() .describe("Filter by supported media type"), }, async ({ category, media_type }) => { try { const result = await api.get("/api/v1/algorithms", { category, media_type, }); return { content: [ { type: "text" as const, text: JSON.stringify(result, null, 2) }, ], }; } catch (err) { return { content: [ { type: "text" as const, text: `Error: ${err instanceof Error ? err.message : String(err)}`, }, ], isError: true as const, }; } }, ); } - src/tools/check-job.ts:15-70 (helper)Related helper tool: check_job is used to poll for results of jobs created by run_algorithm, returning status, progress, and result data including download URLs when complete.
export function register(server: McpServer, api: ApiClient): void { server.tool( "check_job", "Check the status of an asynchronous job (from run_algorithm, protect_media, or detect_ai). " + "Returns status (queued, processing, completed, failed), progress percentage, " + "and result data including download URLs when complete.", { job_id: z.string().describe("The job ID returned by a previous tool call"), }, async ({ job_id }) => { try { const result = (await api.get( `/api/v1/jobs/${encodeURIComponent(job_id)}`, )) as JobResponse; const lines: string[] = [ `Status: ${result.status}`, ]; if (result.progress !== undefined) { lines.push(`Progress: ${result.progress}%`); } if (result.created_at) { lines.push(`Created: ${result.created_at}`); } if (result.completed_at) { lines.push(`Completed: ${result.completed_at}`); } if (result.error) { lines.push(`\nError: ${result.error}`); } if (result.result) { lines.push(`\nResult:\n${JSON.stringify(result.result, null, 2)}`); } if (result.status === "queued" || result.status === "processing") { lines.push(`\nJob is still running. Call check_job again in a few seconds.`); } return { content: [{ type: "text" as const, text: lines.join("\n") }], }; } catch (err) { return { content: [ { type: "text" as const, text: `Error: ${err instanceof Error ? err.message : String(err)}`, }, ], isError: true as const, }; } }, ); }