TaricHive MCP server — setup & operations
The MCP server (src/TaricHive.Mcp, image ghcr.io/rousseauxy/tarichive-mcp) exposes
TaricHive's tariff intelligence as Model Context Protocol tools over Streamable HTTP at
https://mcp.opentaric.eu/mcp. It is an OAuth 2.1 resource server (RFC 9728):
every request needs an Authentik-issued access token. It shares the TaricHive SQL
database read-only (the Web app owns migrations and imports).
Tools
| Tool | Scope | Purpose |
|---|---|---|
search_goods |
taric:read | Ranked code/text nomenclature search |
get_goods_hierarchy |
taric:read | Chapter → heading → code breadcrumb |
get_measures |
taric:read | All measures per code (duties, preferences, quotas, remedies, restrictions, national taxes) |
compare_sources |
taric:read | MFN duty comparison across EU/GB/CH/NO/SE/PL/TR/… |
get_quota |
taric:read | Quota volumes / balance / critical state |
get_rulings |
taric:read | EU BTIs + UK ATaRs (classification precedents) |
get_notes |
taric:read | Section/chapter legal notes, footnotes, CN explanatory notes |
calculate_duty |
taric:calculate | Duty + national-tax calculation for a declaration line |
validate_declaration |
taric:calculate | Condition/certificate validation for a declaration line |
1. Authentik (auth.xact.cloud)
Create a second provider/application next to the existing tarichive web login:
- Provider → OAuth2/OpenID Provider, name
tarichive-mcp: - Client type: Public (no client secret; PKCE is enforced for public clients).
- Redirect URIs (strict where possible):
https://claude.ai/api/mcp/auth_callbackhttps://claude.com/api/mcp/auth_callback- Regex:
http://localhost:\d+/callback(Claude Code + MCP Inspector loopback)
- Signing key: same certificate as the
tarichiveprovider. - Custom scopes (Customization → Property Mappings → Scope Mapping, expression can
simply be
return {}): taric:read— "Read tariff data"taric:calculate— "Calculate duties" Then edit the provider → Advanced protocol settings → add both to Scopes.- Application "TaricHive MCP", slug
tarichive-mcp, bound to the provider. Bind the same groups as the web app (Admins/Users) so only known users can authorize. - Note from the provider page:
- Issuer:
https://auth.xact.cloud/application/o/tarichive-mcp/ - Client ID: →
Auth__Mcp__StaticClientId
2. Container / compose
tarichive-mcp:
image: ghcr.io/rousseauxy/tarichive-mcp:1
restart: unless-stopped
mem_limit: 1g
environment:
ConnectionStrings__TaricHive: "<same as tarichive web>"
Auth__Mcp__Authority: "https://auth.xact.cloud/application/o/tarichive-mcp/"
Auth__Mcp__StaticClientId: "<client id from Authentik>"
Auth__Mcp__PublicBaseUrl: "https://mcp.opentaric.eu"
# healthcheck: GET /healthz
DNS: mcp.opentaric.eu → same host as the app; Traefik router → this container :8080
(TLS at Traefik, plain HTTP inside — identical to the web app).
3. Connecting clients
- claude.ai → Settings → Connectors → Add custom connector → URL
https://mcp.opentaric.eu/mcp. No client ID/secret needed — the server implements a DCR shim (see below). Authorize via the Authentik login. - Claude Code:
claude mcp add --transport http taric https://mcp.opentaric.eu/mcpthen/mcpto authenticate. - MCP Inspector (debugging):
npx @modelcontextprotocol/inspector, connect to the same URL, transport "Streamable HTTP".
4. The DCR shim (temporary)
Authentik does not implement RFC 7591 Dynamic Client Registration (upstream issue
goauthentik/authentik#8751, milestoned 2026.8), while MCP clients self-register before
the OAuth flow. Auth/McpOAuthFacade.cs bridges this:
/.well-known/oauth-protected-resource(SDK) advertises mcp.opentaric.eu itself as the authorization server;/.well-known/oauth-authorization-serverre-serves Authentik's discovery document with aregistration_endpointadded (issuer rewritten to the facade per RFC 8414 §3.3 — token validation still pins the real Authentik issuer);POST /oauth/registerreturns the one static public client to every caller.
Authorize/token/JWKS traffic goes directly to Authentik; the facade never sees tokens.
When Authentik ships DCR: delete McpOAuthFacade.cs, point
ResourceMetadata.AuthorizationServers at the Authentik issuer, and drop
Auth__Mcp__StaticClientId.
5. Headless / machine-to-machine clients (Hermes, scripts, services)
Server-side agents can't run the interactive OAuth flow. Instead of a no-auth bypass, they use client_credentials tokens from a second, confidential Authentik provider:
- Authentik → new OAuth2/OpenID Provider
tarichive-mcp-m2m: - Client type: Confidential (client_credentials needs a secret).
- Signing key: the SAME certificate as
tarichive-mcp(the MCP server discovers JWKS from the primary provider only — a different key will fail validation). - Add the
taric:read/taric:calculatescope mappings to its allowed scopes. - Access-token validity: pick per rotation appetite (e.g.
days=90) — headless clients hold the token as a static header, so it lives until expiry. - Application "TaricHive MCP M2M" bound to the provider.
- MCP server env — allow the extra issuer/audience:
- Mint a token:
- Configure the client with a static header, e.g. Hermes
config.yaml:withmcp_servers: taric: url: "https://mcp.dv.opentaric.eu/mcp" headers: Authorization: "Bearer ${TARIC_MCP_TOKEN}"TARIC_MCP_TOKENin the container environment. Rotation = re-run the curl, update the env, restart the client.
6. Security notes
- Access is controlled by the Authentik application binding (groups), not by client registration — the static client is public by design (PKCE, no secret to leak).
- The endpoint policy requires
taric:read;calculate_duty/validate_declarationadditionally requiretaric:calculatein the token'sscope. - Audience validation: Authentik sets
audto the client ID; override withAuth__Mcp__Audienceif a dedicated audience is configured later.