Skip to main content

MCP Server

Stirling PDF ships a built-in Model Context Protocol (MCP) server. MCP is the open standard MCP clients (Claude Desktop, the MCP Inspector, IDE agents, and custom tools) use to discover and call tools on a remote server. When enabled, Stirling PDF exposes its PDF operations as MCP tools so an MCP-capable assistant can run them on your behalf.

The MCP server is built into the Stirling PDF self-hosted server and the desktop app in Local / Self-hosted modes. It is off by default and must be enabled and configured per deployment.

Self-hosted capability

This page documents the MCP server you run on your own Stirling PDF instance. The per-user MCP tab in Stirling Cloud is a separate, cloud-only surface and is not covered here. For where each deployment mode applies, see Modes.


Enable the server

The MCP server runs only when mcp.enabled is true. While off, there is no /mcp endpoint and no MCP metadata.

Turn it on either way:

  • Settings file / environment variable: set mcp.enabled: true in settings.yml, or the environment variable MCP_ENABLED=true. A restart applies file edits.
  • Admin UI: open Admin Settings → MCP Server. This page is shown to admins, and on instances where login is disabled. Saving prompts for a restart.

Enabling alone is not enough - you also need to choose and configure an authentication mode before clients can call tools.


Transport and protocol

  • Endpoint: POST /mcp on the same host and port as the rest of Stirling PDF.
  • Protocol: JSON-RPC 2.0 over streamable-HTTP.
  • Supported MCP protocol versions: 2025-06-18 (preferred), 2025-03-26, and 2024-11-05. The server echoes the client's requested version when it is supported, otherwise it advertises the preferred version.

Tools exposed

The server presents a small set of category tools rather than one tool per operation. An MCP client lists them, then drills into a specific PDF operation using stirling_describe_operation.

ToolPurpose
stirling_describe_operationLook up the parameters and JSON schema for a specific operation id.
stirling_pagesPage-level operations (merge, split, rotate, reorder, add blank pages, and similar).
stirling_convertConversions to and from PDF (images, office formats, and similar).
stirling_miscMiscellaneous utilities (compress, flatten, repair, and similar).
stirling_securitySecurity operations (encrypt, decrypt, permissions, and similar).
stirling_uploadStore a file server-side and get back a fileId for large inputs.
stirling_downloadFetch a result that was returned by reference rather than inline.
stirling_aiAI-engine capabilities. Not usable in self-hosted - see the caveat below.
stirling_ai requires a Stirling Cloud AI engine

stirling_ai only has capabilities when a Stirling AI engine is configured. The AI engine is a Stirling Cloud feature and is not available in self-hosted today, so on a self-hosted server stirling_ai exposes nothing and only the PDF tools above are usable over MCP.


Authentication

Pick one of two modes with mcp.auth.mode.

OAuth2 resource server (oauth, default)

In OAuth mode the /mcp endpoint runs as an OAuth2 resource server: it validates incoming JWTs (signature, issuer, expiry, and audience) and binds each token to an existing Stirling account. It publishes RFC 9728 protected-resource metadata at /.well-known/oauth-protected-resource so MCP clients can discover the authorization server.

KeyEnvDefaultPurpose
mcp.auth.issuerUriMCP_AUTH_ISSUERURIemptyOAuth2 issuer URI (e.g. http://localhost:9000). Required in OAuth mode; every token is rejected until it is set.
mcp.auth.jwksUriMCP_AUTH_JWKSURIemptyJWKS URI. Blank means it is derived from the issuer's /.well-known/openid-configuration.
mcp.auth.resourceIdMCP_AUTH_RESOURCEIDemptyRFC 8707 resource identifier of this server. Must equal the public /mcp URL clients call and end in /mcp (e.g. http://localhost:8080/mcp). Tokens that do not list it in aud are rejected.
mcp.auth.acceptedAudiencesMCP_AUTH_ACCEPTEDAUDIENCES[]Extra aud values accepted on top of resourceId. Empty keeps strict RFC 8707 binding. Use this for IdPs that cannot mint a resource-specific audience (e.g. Supabase always issues aud=authenticated).
mcp.auth.usernameClaimMCP_AUTH_USERNAMECLAIMsubJWT claim matched against a Stirling username. Set to email or preferred_username if your IdP maps users differently.
mcp.auth.requireExistingAccountMCP_AUTH_REQUIREEXISTINGACCOUNTtrueReject tokens whose subject has no enabled Stirling account. Keep true unless you intend open access for any IdP-valid token.
mcp.scopesEnabledMCP_SCOPESENABLEDtrueEnforce the mcp.tools.read / mcp.tools.write scopes. Read-style tools require mcp.tools.read; mutating operations require mcp.tools.write. Set false only if your IdP can issue a single coarse token.

Each validated token is bound to the matching Stirling account so that audit and attribution are correct.

API key (apikey)

API-key mode is the low-friction option for self-hosters with no external identity provider. Set mcp.auth.mode: apikey (env MCP_AUTH_MODE=apikey) and clients authenticate with an existing per-user Stirling API key.

Send the key as either header:

X-API-KEY: <your-stirling-api-key>

or

Authorization: Bearer <your-stirling-api-key>

The key must belong to an existing, enabled account (generate one under Account → API Keys - see API documentation). No external IdP, OAuth, or JWKS configuration is needed.


Restrict which operations are exposed

Two MCP-level lists control which operations clients can see and call. They use the same kebab-case operation ids as the Endpoint or Feature Customisation page (e.g. compress-pdf).

KeyEnvDefaultBehaviour
mcp.allowedOperationsMCP_ALLOWEDOPERATIONS[]When non-empty, acts as a strict allow-list - only these operations are exposed over MCP; everything else is hidden, undescribable, and uninvocable. Empty means allow all.
mcp.blockedOperationsMCP_BLOCKEDOPERATIONS[]A deny-list. Anything listed is always removed, applied after the allow-list, so a blocked id wins even if it is also allowed.

These lists layer on top of the global endpoints.toRemove / endpoints.groupsToRemove configuration. An operation disabled globally is never exposed over MCP regardless of these lists.


Limits

KeyEnvDefaultPurpose
mcp.maxRequestBytesMCP_MAXREQUESTBYTES10 MBMaximum MCP request body size. Inline file uploads ride in the JSON-RPC body, so this caps how large an inline input can be.
mcp.maxInlineResponseBytesMCP_MAXINLINERESPONSEBYTES10 MBResults up to this size return inline as base64; larger results return a fileId instead, which the client fetches with stirling_download.
mcp.engineCapabilityRefreshMinutesMCP_ENGINECAPABILITYREFRESHMINUTES5How often the AI capabilities manifest is refreshed from the engine (only relevant when an AI engine is available).

Troubleshooting and operability

  • Startup config validation: when MCP is enabled, the server validates the resolved config at boot and logs findings. Misconfiguration (missing issuer, a resourceId that does not end in /mcp, a username-claim of sub with require-existing-account=true, and similar) is logged as a warning so it surfaces in the logs instead of as a later rejected-token 401.
  • Meaningful 401 responses: a rejected OAuth token returns a WWW-Authenticate header carrying a real error_description (audience, issuer, or expiry mismatch), plus a resource_metadata pointer for discovery. A tokenless 401 is the normal discovery handshake, not an error.
  • Audit logging: MCP calls are attributed to the bound Stirling account, and no secret is written to the audit log.

Connect a client

Point any MCP client at http://your-host:8080/mcp (use your real host, port, and scheme).

MCP Inspector (quick manual testing):

npx @modelcontextprotocol/inspector

Then set the transport to streamable-HTTP and the URL to your /mcp endpoint, adding the appropriate auth header (X-API-KEY in API-key mode, or a Bearer token in OAuth mode).

Claude Desktop via the mcp-remote bridge - add to your Claude Desktop config:

{
"mcpServers": {
"stirling-pdf": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"http://your-host:8080/mcp",
"--header",
"X-API-KEY:your-stirling-api-key"
]
}
}
}

In OAuth mode, drop the X-API-KEY header and let mcp-remote complete the OAuth flow against your configured issuer.


x