Sign inSign up

patentdev/patent-connector

By patentdev

•Updated about 6 hours ago

Self-host patent & trademark server for AI agents - CLI + MCP + REST, official IP offices

Image
Machine learning & AI
0

4.2K

patentdev/patent-connector repository overview

⁠Patent Connector - CLI & MCP Server

Agentic-AI-first toolkit for patent and trademark research: a patent CLI and a server that give AI agents and applications one uniform interface to the official IP offices, using your own API keys (BYOK, stored encrypted).

One image, two roles:

  • CLI tool - built agentic-first: an agent (Claude Code, etc.) or you can drive it from the terminal, running patent and trademark lookups in-process or against a server.
  • Server - the long-lived service this image runs (patent server): an MCP endpoint for AI agents, a REST API for apps and the CLI, and a web dashboard to manage your account and provider credentials.

Data sources - official patent and trademark offices:

  • EPO OPS - European Patent Office / Europäisches Patentamt (Open Patent Services)
  • USPTO ODP - United States Patent and Trademark Office (Open Data Portal)
  • USPTO TSDR - US trademark status and document retrieval
  • EUIPO - EU Intellectual Property Office (trademarks and designs)
  • DPMA - Deutsches Patent- und Markenamt (German Patent and Trade Mark Office)
  • IP Australia - Australian patents, trade marks and designs
  • JPO - Japan Patent Office / 特許庁
  • TIPO - Taiwan Intellectual Property Office / 經濟部智慧財產局
  • INPI France - Institut national de la propriété industrielle (French patents, trademarks and designs)
  • Global Dossier - USPTO Global Dossier: public EP/JP/KR/CN file wrappers (office actions, decisions, EP opposition and appeal filings) as PDFs and page images; no API key required, self-hosted deployments only
  • Lens - Lens.org: patent documents from 100+ jurisdictions with forward citations, DOCDB families and calculated legal status (bring your own Lens API token)
  • Patent Reference server - built-in corpus of CPC/IPC classification and examination manuals (MPEP, EPO Guidelines); no API key required
  • and more

Homepage: https://patent.dev⁠


⁠Setup

Use the hosted service - sign in at https://patent.dev/patent-connector/⁠, then add the MCP endpoint https://patent.dev/mcp to your AI client, or run patent login for the CLI.

Run it fully on-prem (the server and in-process tools) - get in touch at [email protected]⁠. Write the free license into a persistent volume so the server picks it up automatically:

# 1) register (an activation code is sent to your email):
docker run --rm patentdev/patent-connector patent register --email [email protected]

# 2) activate, storing the license in a persistent volume:
docker run --rm -v patent-data:/app/data \
  patentdev/patent-connector patent activate <code>

The license lands at /app/data/patent.license (the image's default PATENT_LICENSE_FILE); mount the same patent-data volume into the server below.

Or license from the browser. Start the server without a license and it does not crash - it serves a setup page at your BASE_URL with live deployment diagnostics (reachability, certificate, DNS, SMTP). Paste a lic_... key there, confirming with the one-time setup code printed in docker logs, or set PATENT_LICENSE_KEY in the environment. The server restarts into normal mode as soon as a valid license is present - including when it is minted or renewed on our side, with nothing to do on the box. An unlicensed instance only contacts license.patent.dev to check its own license; nothing else is sent. Because it serves this page rather than exiting, keep a restart policy on the container (restart: unless-stopped, already in the examples below).


⁠Self-host quick start

The dataset is tiny (accounts and settings), so the bundled SQLite backend is plenty - persist one volume and you are done.

services:
  patent-connector:
    image: patentdev/patent-connector:latest
    restart: unless-stopped
    ports:
      - "8080:8080"
    environment:
      BASE_URL: http://localhost:8080      # externally reachable URL
      ENCRYPTION_KEY: ${ENCRYPTION_KEY}     # 32 hex chars: openssl rand -hex 16
      # Sign-in is passwordless: a one-time code is emailed. Configure SMTP so
      # users can self-serve login (no relay? mint a code with `issue-code`, below):
      SMTP_HOST: smtp.example.com          # your mail server / relay
      SMTP_FROM: [email protected]        # from address for sign-in codes
      # SMTP_USER/SMTP_PASS: set both if the relay needs a login; omit both for
      # an internal relay that authorizes by IP.
      # DB (SQLite) and license default into /app/data, the mounted volume below.
      # For MySQL, override DATABASE_URL with a DSN:
      # DATABASE_URL: user:pass@tcp(host:3306)/patent_mcp?parseTime=true
    volumes:
      - patent-data:/app/data

volumes:
  patent-data:
export ENCRYPTION_KEY=$(openssl rand -hex 16)
docker compose up -d

Open http://localhost:8080⁠ and sign in with your email - login is passwordless: you enter your email, receive a one-time 6-digit code by email (delivered via the SMTP relay above), and enter it. Then add your provider API keys in the dashboard. Set BASE_URL to the exact URL you open in the browser (on a LAN that is something like http://192.168.1.55:8080; behind a proxy, your https:// URL) - the login cookie follows BASE_URL, so over plain HTTP this keeps the dashboard login working.

No mail relay? Sign-in codes are normally emailed (configure SMTP above). Without SMTP, mint a code on the host and type it on the sign-in page - handy for the first admin login or whenever email is unavailable:

docker exec <container> patent server issue-code --email [email protected]

⁠HTTPS without a reverse proxy

AI assistants (Claude, ChatGPT) only connect to HTTPS endpoints on the standard port, and the server can terminate TLS itself - no Caddy/nginx/Traefik needed:

services:
  patent-connector:
    image: patentdev/patent-connector:latest
    restart: unless-stopped
    ports:
      - "443:443"
      - "80:80"    # Let's Encrypt HTTP-01 challenge + redirect to HTTPS
    environment:
      BASE_URL: https://patents.acme.com   # DNS must point at this host
      ENCRYPTION_KEY: ${ENCRYPTION_KEY}
      TLS_ACME: "true"                     # automatic Let's Encrypt certificate
      TLS_ACME_EMAIL: [email protected]          # optional: CA expiry notices
      SMTP_HOST: smtp.acme.com
      SMTP_FROM: [email protected]
      # Optional: dashboard (and login) only from your offices/VPN; connected
      # assistants keep working from anywhere (they authenticate with tokens):
      # WEBUI_ALLOWED_CIDRS: 203.0.113.0/24,2001:db8:acme::/48
    volumes:
      - patent-data:/app/data

volumes:
  patent-data:

With TLS_ACME=true the listen port defaults to 443 and certificates are obtained and renewed automatically (cached in /app/data, so keep the volume). Issuance retries in the background with backoff - but do not delete the volume and restart repeatedly against the production CA, Let's Encrypt rate-limits failed attempts per hour. Prefer your own certificates? Set TLS_CERT_FILE/TLS_KEY_FILE instead; renewed files are picked up without a restart. A server without public ingress (LAN/VPN) can still get Let's Encrypt certificates via the DNS-01 challenge (TLS_ACME_DNS_PROVIDER + TLS_ACME_DNS_API_TOKEN).

A classic reverse proxy in front remains fully supported: terminate TLS there, set BASE_URL to the public HTTPS URL, and bind the port locally (ports: ["127.0.0.1:8080:8080"]) so only the proxy reaches it. If you combine a proxy with WEBUI_ALLOWED_CIDRS, also set TRUSTED_PROXIES to the proxy's address - otherwise every request appears to come from the proxy and the allowlist cannot see real client addresses.


⁠Environment variables

VariableRequiredDescription
BASE_URLyesExternally reachable URL (e.g. https://patents.acme.com)
ENCRYPTION_KEYyes32 hex chars; encrypts stored provider credentials (BYOK)
DATABASE_URLnoDefaults to sqlite:/app/data/patent.db; set a MySQL DSN to override
PATENT_LICENSE_FILEnoDefaults to /app/data/patent.license (put your activated license there)
PORTnoListen port (default 8080; 443 when TLS is enabled)
TLS_CERT_FILE, TLS_KEY_FILEnoServe HTTPS directly with your own PEM pair (mount both into the container). Renewed files are picked up without a restart. Mutually exclusive with TLS_ACME
TLS_ACMEnotrue = obtain and renew a Let's Encrypt certificate for the BASE_URL host automatically. The domain must resolve to this server with ports 80+443 published, or use the DNS-01 variables below
TLS_ACME_EMAILnoOptional ACME account email (certificate expiry notices from the CA)
TLS_ACME_DNS_PROVIDER, TLS_ACME_DNS_API_TOKENnoDNS-01 challenge for servers without public ingress (LAN/VPN): cloudflare or hetzner plus an API token. Scope the token as narrowly as possible: Cloudflare tokens can be restricted to one zone; Hetzner DNS tokens are account-wide - treat that token like a root credential for all your zones
HTTP_PORTnoPlain-HTTP listener while TLS is on: answers the Let's Encrypt HTTP-01 challenge and 301-redirects everything else to HTTPS (default 80; 0 disables it, e.g. with DNS-01 on a LAN)
WEBUI_ALLOWED_CIDRSnoRestrict the browser dashboard, login and admin console to these IP ranges (comma-separated CIDRs or single addresses, IPv4+IPv6). MCP/OAuth/REST endpoints for connected assistants stay reachable from anywhere (they carry their own token auth), as do /health, /api/version and temporary media links. Connecting a NEW assistant includes a browser sign-in step, so that must happen from an allowed range; already-connected assistants are unaffected. If the host has an AAAA record, include your IPv6 prefixes too
TRUSTED_PROXIESnoCIDRs of reverse proxies in front of this server. Required for WEBUI_ALLOWED_CIDRS behind a proxy: only when the direct peer is a trusted proxy is X-Forwarded-For used to determine the real client address
WEBUI_SUBTITLEnoSelf-host only: text shown under the "Patent Connector" header in the dashboard, e.g. your firm's name, so users see at a glance that this is the internal instance. Served via the open /api/version endpoint, so do not put anything confidential here
PROVIDER_ENV_SELECTORnotrue reveals a per-provider Production/Sandbox dropdown in the dashboard (for offices with a test system, e.g. EUIPO). Hidden by default
SMTP_HOST, SMTP_FROMfor loginDeliver passwordless sign-in codes (and feedback email). Required together to enable self-service email login (otherwise mint codes with patent server issue-code)
SMTP_USER, SMTP_PASSnoSMTP auth. Optional pair (set both or neither); omit both for an internal relay that authorizes by IP
SMTP_PORTnoSMTP port (default 587; 465 = implicit TLS, otherwise STARTTLS per SMTP_STARTTLS)
SMTP_STARTTLSnoSTARTTLS policy on non-465 ports: auto (default - mandatory on submission ports, opportunistic on port 25), off (never; a deliberately plaintext relay), opportunistic (upgrade if offered, otherwise plaintext), required (must upgrade or fail). Use off/opportunistic for an internal relay that advertises a broken STARTTLS (454 TLS not available)
SMTP_TLS_CA_FILEnoPEM file with an internal CA root, trusted for the SMTP connection only (mount it into the container). For relays whose certificate the system store cannot verify
SMTP_TLS_INSECURE_SKIP_VERIFYnotrue disables SMTP TLS certificate verification. Last resort - prefer SMTP_TLS_CA_FILE or fixing the relay certificate
ALLOWED_EMAIL_DOMAINSnoRestrict sign-in to these email domains (comma-separated). A user must control a mailbox at an approved domain to receive the code - handy access control alongside a reverse-proxy/IP restriction. Exact domain match; unset = any domain
SSO_TENANT_IDnoEntra ID single sign-on (licensed feature): your tenant GUID. Employees sign in with their Microsoft work account; accounts are created on first login. Mutually exclusive with SSO_ISSUER. Register the redirect URI <BASE_URL>/auth/sso/callback (verbatim, including any path prefix) as a single-tenant web app
SSO_ISSUERnoGeneric OIDC issuer URL instead of the Entra preset (Keycloak, ADFS, Authentik, sovereign Entra clouds). Must be https
SSO_CLIENT_IDwith SSOApp registration / OIDC client id
SSO_CLIENT_SECRETwith SSOClient secret (SSO_CLIENT_SECRET_FILE reads it from a mounted file, e.g. a docker secret). Entra caps secrets at 24 months - calendar the rotation
SSO_SCOPESnoExtra scopes requested at login (space/comma separated), for connectors that need delegated tokens
SSO_ADMIN_GROUPnoApp role values or group object ids (comma-separated, any match) synced to the admin flag on each SSO login - grant AND revoke. Manual make-admin/console promotions stay sticky; console demotion of a still-in-group user is re-granted at next login (remove them from the group instead)
SSO_ALLOWED_GROUPnoOnly users carrying one of these app roles / group ids may sign in via SSO. NOTE: gates only the SSO path - combine with SSO_DISABLE_OTP_LOGIN=true to make it an instance gate, otherwise email-code login remains a way in
SSO_ACCESS_REQUEST_EMAILnoContact shown to a user the SSO_ALLOWED_GROUP gate turns away, so they know whom to ask for access ("To request access, contact ..."). Defaults to FEEDBACK_EMAIL, then SMTP_FROM; unset everywhere = no contact line
SSO_LINK_EXISTINGnoHow an SSO login whose email matches an existing email-code account links up: otp (default; confirmation code to that mailbox, needs SMTP), auto (link directly - never for admin accounts; only sensible if you trust every tenant guest), off (reject)
SSO_DISABLE_OTP_LOGINnotrue disables self-service email-code login server-side while SSO is armed (the request-code endpoint answers 404). patent server issue-code + code entry keeps working as the operator break-glass
PATENT_UPDATE_NOTIFY_TOnoEmail this address once per new release (needs SMTP), so you do not have to watch the dashboard's update chip or the server log. Unset = off
PATENT_UPDATE_NOTIFY_INTERVALnoCheck cadence for the update email (Go duration, e.g. 12h; default 24h)

Provider API keys (EPO OPS, USPTO, EUIPO, ...) are not set via environment variables - add them in the dashboard after first login; they are stored encrypted with ENCRYPTION_KEY.


⁠Admin console & shared credentials

The first account to sign in becomes the instance admin automatically. To promote another existing account from the host:

docker exec <container> patent server make-admin --email [email protected]

Offboarding from the host (also works when the user cannot log in themselves):

docker exec <container> patent server delete-user --email [email protected]

Admins open the console at <BASE_URL>/#/admin: instance diagnostics, user management (grant/revoke admin and delete accounts; the last admin and the shared-credentials source are protected), and shared credentials for firm-wide use - designate one account (a dedicated service account like [email protected] works best) whose provider credentials every user transacts under. Providers configured on that account are locked for other users; rotate a key by updating it on the source account and it propagates instantly. Providers the source lacks stay configurable per-user.


⁠Persistence

Persist /app/data - it holds the SQLite database, the license file and the Let's Encrypt certificate cache. Keep your ENCRYPTION_KEY stable and backed up; it decrypts stored provider credentials, so losing it means re-entering all provider keys.


⁠Health and debugging

The container exposes a health endpoint and ships with curl and jq:

curl -s http://localhost:8080/health | jq
docker exec <container> curl -s http://localhost:8080/health | jq

⁠Support


Envisioned and crafted by Wolfgang Stark - patent.dev - Funktionslust GmbH

Tag summary

Content type

Image

Digest

sha256:584812782…

Size

44.8 MB

Last updated

about 6 hours ago

docker pull patentdev/patent-connector