Skip to content

Hosted MCP server

llms.txtAsk agent

The API serves a Model Context Protocol (MCP) server, so an agent can sign media, read jobs and assets, and check what a file’s Content Credentials declare without writing REST calls. Each tool maps onto a REST operation: the same service runs, with the same scopes, refusals, billing and per-key rate limit.

What Value
Endpoint POST https://api.originalpictures.com/mcp
Transport Streamable HTTP, stateless: no session, JSON responses, GET and DELETE answer 405
Protocol revisions MCP 2025-11-25, and the older 2025-06-18, 2025-03-26 and 2024-11-05 a client asks for
Credential An API key as a bearer token (OAuth sign-in is not available yet)

Send an API key exactly as for the REST API (Authentication):

Authorization: Bearer opk_<lookup>.<secret>

Create a dedicated key for each MCP client, holding only the scopes the client needs. A key lists only the tools its scopes reach; sign:write and verify:write enable the billable tools, so leave them out of a key meant for reading. The key’s own restrictions apply to every request, including its rate limit and IP allowlist. When a hosted agent platform (OpenAI, xAI) connects on your behalf, the requests come from that platform’s network, not yours.

  • A missing or invalid key, or a console session, is refused with 401 and a WWW-Authenticate challenge naming the endpoint’s protected-resource metadata. That document names no authorization server yet; an API key is the only accepted credential.
  • A key that holds no scope any tool uses (a publishable disclosure key, for example) is refused with 403.
  • Every HTTP request to /mcp counts as one request against the key’s rate limit, whatever it carries (initialize, tools/list, one tools/call). The REST operation a tool calls does not count a second time.
Tool Scope REST operation
get_current_user identity:read GET /v1/me
list_assets, get_asset, list_receipts, get_signing_receipt assets:read GET /v1/assets, /v1/receipts and their single reads
create_download assets:read POST /v1/assets/{asset_id}/download
list_jobs, get_job, list_recipes jobs:read GET /v1/jobs, /v1/jobs/{job_id}, /v1/recipes
get_recipe recipes:read GET /v1/recipes/{recipe_id}
get_usage, get_pricing billing:read GET /v1/usage, GET /v1/billing/pricing
list_verifications, get_verification_history verifications:read GET /v1/verifications, /v1/verification-history/…
get_verification recovery:read GET /v1/verifications/{verification_id}
upload_file, create_upload, complete_upload uploads:write POST /v1/uploads, POST /v1/uploads/{id}/complete
sign sign:write POST /v1/sign (billable)
create_verification verify:write POST /v1/verifications (billable)
get_disclosure_policy any admitted key GET /v1/disclosures/policies

Every tool has a title and explicit readOnlyHint, destructiveHint, idempotentHint and openWorldHint annotations. No tool deletes anything. A refusal comes back as a tool error naming the HTTP status and reason code, never the key or a storage location.

An agent connected to the hosted server has no local file the API can read, so it uploads first:

  1. upload_file with the file’s bytes as base64 (at most 2 MiB) and its media_type. It returns an asset_id and an object_id. For a larger file, create_upload returns a presigned PUT request: send the bytes there yourself, then call complete_upload.
  2. sign with the asset_id, watermark and fingerprint flags, an idempotency_key, and the confirmation string sign:<asset_id>. It returns the job (job_…), which runs asynchronously.
  3. get_job until the job is completed; its result names the signed output’s asset_id.
  4. create_download with that asset_id returns a time-limited link to the signed file. It is a bearer link: anyone holding it can download the file until it expires.

create_verification takes an object_id (and verify:<object_id> as its confirmation). A verification reports what the file’s Content Credentials declare about its origin. It is not a judgement of whether the content is true.

  • sign and create_verification need an exact confirmation string and an idempotency key, so a retry returns the first result instead of billing twice.
  • Their descriptions carry a cost preview, also in _meta["com.originalpictures/billing"]: create_verification starts one billable operation; sign starts one for the signature plus one each for the watermark and fingerprints when chosen (audio and video watermarks and fingerprints are priced per second). get_pricing returns the rates.
  • Both accept an optional max_billable_operations argument. A client can also send the header X-OP-Max-Billable-Operations: <0-1000> with every request. A call that would start more billable operations than the lower of the two is refused before anything is sent; 0 makes the connection read-only for billable work.
  • Set a rate limit on the key (Restrict a key) to bound how fast an agent can spend.

In every example, keep the key in an environment variable or the client’s secret store, never in a file you commit.

Terminal window
claude mcp add --transport http originalpictures https://api.originalpictures.com/mcp \
--header "Authorization: Bearer $ORIGINALPICTURES_API_KEY"

~/.cursor/mcp.json (or .cursor/mcp.json in a project):

{
"mcpServers": {
"originalpictures": {
"url": "https://api.originalpictures.com/mcp",
"headers": { "Authorization": "Bearer ${env:ORIGINALPICTURES_API_KEY}" }
}
}
}

.vscode/mcp.json; VS Code asks for the key once and stores it as a secret:

{
"inputs": [
{
"type": "promptString",
"id": "originalpictures-api-key",
"description": "Original Pictures API key (opk_...)",
"password": true
}
],
"servers": {
"originalpictures": {
"type": "http",
"url": "https://api.originalpictures.com/mcp",
"headers": { "Authorization": "Bearer ${input:originalpictures-api-key}" }
}
}
}

In the agent, open Tools, Add a tool, New tool, Model Context Protocol. Enter the endpoint URL, choose API key authentication sent as a header named Authorization, and when you create the connection enter the value Bearer opk_….

On a plan that offers custom connectors, add a custom remote MCP connector with the endpoint URL and API-key authentication, sent as the Authorization header with the value Bearer opk_….

A remote MCP tool in a Responses request:

{
"model": "<a Grok model>",
"input": "List my five most recent sign jobs.",
"tools": [
{
"type": "mcp",
"server_label": "originalpictures",
"server_url": "https://api.originalpictures.com/mcp",
"headers": { "Authorization": "Bearer opk_..." },
"allowed_tools": ["list_jobs", "get_job"]
}
]
}

A hosted MCP tool: OpenAI’s platform calls the endpoint for the agent.

import asyncio
import os
from agents import Agent, HostedMCPTool, Runner
agent = Agent(
name="Provenance assistant",
tools=[
HostedMCPTool(
tool_config={
"type": "mcp",
"server_label": "originalpictures",
"server_url": "https://api.originalpictures.com/mcp",
"headers": {"Authorization": f"Bearer {os.environ['ORIGINALPICTURES_API_KEY']}"},
"allowed_tools": ["list_jobs", "get_job", "get_asset"],
"require_approval": "never",
}
)
],
)
result = asyncio.run(Runner.run(agent, "List my five most recent sign jobs."))
print(result.final_output)

Use a read-only key for an agent that should not sign, or keep require_approval on for the billable tools.

Smithery can list a remote server by its URL and pass the API key as the client’s configuration. The Original Pictures listing is not published yet; until it is, connect directly with one of the configurations above.

Limit Value
Request body 4 MiB (413 above it)
upload_file file size 2 MiB; use create_upload for larger files
Tool deadline 20 s by default, deadline_seconds up to 55 s
Tool result 256 KiB of JSON; list tools page at most 100 items
Server-sent events Not offered: every answer is a single JSON response