--- title: "Connect: with MCP" description: Let a coding agent build your Operator Contract modules through the OneHazel MCP server, and what stays with a human. --- # Connect with MCP The OneHazel MCP server gives a coding agent (Claude Code, Codex, Cursor, or any MCP client) everything it needs to build your modules: the contract with your URLs filled in, generated samples, the conformance scenarios, and the tools to register endpoints and run conformance. The agent writes the code in **your** repository; OneHazel never sees your source. ## 1. Create a key with Connect enabled In the app, **Connect casino**, then **Integrate with AI**, mints an API key with the Connect tools enabled and shows it once. Keep it in your secret manager or shell environment, never in the repository. **Check:** the key starts with `oh_live_` and the tab lists each module with the chip **Not started**. ## 2. Add the server to your agent ### Claude Code Clients that speak HTTP MCP connect directly: ```bash claude mcp add --transport http onehazel https://api.onehazel.com/mcp-server --header "Authorization: Bearer $ONEHAZEL_API_KEY" ``` Clients that only speak stdio use the bridge, which forwards every message to the hosted server: ```bash claude mcp add onehazel -e ONEHAZEL_API_KEY="$ONEHAZEL_API_KEY" -- npx -y @onehazel/mcp ``` The bridge exits with code 2 and a message naming `ONEHAZEL_API_KEY` if the key is missing; it never prints the key. `ONEHAZEL_MCP_URL` overrides the server address it forwards to. After either command, the key must have Connect enabled (step 1), and the agent then reads the `integrate_with_onehazel` prompt and `onehazel://agents.md` (step 3). ### Codex Add the server to `~/.codex/config.toml` (or `.codex/config.toml` in a trusted project). Over HTTP, Codex sends the key from the named environment variable as `Authorization: Bearer`: ```toml [mcp_servers.onehazel] url = "https://api.onehazel.com/mcp-server" bearer_token_env_var = "ONEHAZEL_API_KEY" ``` Or run the stdio bridge, passing the key through from your shell so it never sits in the file: ```toml [mcp_servers.onehazel] command = "npx" args = ["-y", "@onehazel/mcp"] env_vars = ["ONEHAZEL_API_KEY"] ``` Use one of the two forms, not both. The key must have Connect enabled (step 1), and the agent then reads the `integrate_with_onehazel` prompt and `onehazel://agents.md` (step 3). ### Cursor Add the server to `~/.cursor/mcp.json` in your home folder, outside your repository. A project `.cursor/mcp.json` is safe only with the `${env:ONEHAZEL_API_KEY}` form shown here, which reads the key from your environment; never type the key into a project file. The step-by-step version is [Install in Cursor](/connect/mcp/install/cursor). Over HTTP: ```json { "mcpServers": { "onehazel": { "type": "http", "url": "https://api.onehazel.com/mcp-server", "headers": { "Authorization": "Bearer ${env:ONEHAZEL_API_KEY}" } } } } ``` Or through the stdio bridge: ```json { "mcpServers": { "onehazel": { "command": "npx", "args": ["-y", "@onehazel/mcp"], "env": { "ONEHAZEL_API_KEY": "${env:ONEHAZEL_API_KEY}" } } } } ``` The key must have Connect enabled (step 1), and the agent then reads the `integrate_with_onehazel` prompt and `onehazel://agents.md` (step 3). ### Check the connection **Check:** ask the agent to call `connect_status`. It answers with your operator id and one entry per module. **If it answers `-32001 MCP_CONNECT_NOT_ENABLED`:** This key has no Connect scope. Create one in app.onehazel.com under Connect casino, Integrate with AI (the API Keys page cannot add it). ## 3. Give the agent its instructions Have the agent read the resource `onehazel://skill/integrate` first; it says what the agent does alone and what it must hand back to you. For a persistent setup, paste `onehazel://agents.md` into your repository's `CLAUDE.md` or `AGENTS.md`. **Check:** the agent can summarise which modules your chosen vendors need and which steps are yours. ## 4. Build, register, conform The agent calls the tools in the order each answer's `journey.next_step` names; [the journey](/connect/mcp/journey) lists every step. These are all 16 `connect_*` tools: | Tool | Use | |---|---| | `connect_contract` | the contract for the modules you need, your URLs filled in | | `connect_samples` | the generated sample for your language, framework and module; with `framework: "adapter"` the adapter install snippet, `language` optional | | `connect_conformance_scenarios` | what will be tested, before writing code | | `connect_register_endpoint` | register a deployed base URL; returns the signing secret and callback key ONCE. For `wallet`, pass `testPlayerId` and `testGame: { provider_slug, game_code }`, one active game in your catalogue that test sessions start with. For `player`, pass `testPlayerId` and `restrictedPlayerId`. Optional `callbackKey` supplies your own callback key instead of a minted one. The call probes `GET /healthz` first (see below) | | `connect_rotate_secret` | rotate the signing secret, the callback key or both (`what`, default `both`); new values returned once with `save_as` variable names, previous ones accepted for one hour | | `connect_unregister_endpoint` | remove a module's registered URL, secret and callback key; conformance history is kept and the module reads `not_started`. Refused while an active connection's connector requires the module | | `connect_run_conformance` | run the scenarios against the registered URL | | `connect_conformance_evidence` | the per-scenario requests and responses of a run, secrets masked | | `connect_status` | where every module stands and what still blocks go-live; every `connect_*` answer also carries the `journey` block | | `connect_simulate` | play one simulated round (or a rollback or free round) against your registered wallet, with your test player | | `connect_deliveries` | the last 50 outbound webhook deliveries, secrets masked | | `connect_list_marketplace` | the public marketplace connectors and the modules each requires | | `connect_connector_requirements` | what activating one connector needs: missing modules, widget snippets and the vendor credential field names | | `connect_activate` | create the INACTIVE connection for a connector once every module it requires is conformant; never goes live | | `connect_set_credentials` | save the vendor's credential values on the inactive connection; the answer names fields only | | `connect_request_go_live` | notify your team that the connection is ready for review; changes no status | Deploy your implementation (or the adapter) first, check its `/healthz` yourself with `curl https:////healthz`, and only then register. `connect_register_endpoint` probes `GET /healthz` (both URLs when you pass `baseUrls`) with a 3 second timeout and does not follow redirects. Any 2xx, 3xx or 4xx answer means something is listening and the URL is stored. A connection error, a timeout or a 5xx answers `CONNECT_ENDPOINT_UNREACHABLE` and stores nothing. To register before deploying, pass `allowUnreachable: true`; `connect_status` then shows `reachability: unverified` for that module until you register again with the implementation running. Registering a module that is already registered keeps its signing secret and callback key (the result says secretUnchanged: true and carries no secret), so fixing an address, a test player or the test game this way changes only OneHazel's registration and needs no adapter restart; only connect_rotate_secret, or a different callbackKey, replaces them. Registering again keeps every input you omit (baseUrl or baseUrls, either half of baseUrls, testPlayerId, restrictedPlayerId, testGame); send null to clear testPlayerId, restrictedPlayerId, testGame or baseUrls.test, and a value to replace one. A call is refused only when the stored row would still lack an input connect_status lists in requiredInputs. Registered the wrong URL? Call connect_register_endpoint again with the right one: the secrets stay the same. Unregister only to remove a module, because registering it again after that mints new values your human must copy into onehazel.env before restarting the adapter. Its conformance runs from before the unregister no longer count: connect_status shows the module as registered, and the journey asks your human for that restart before a new run. In the app, the **Remove** button on the **Module status** card does what `connect_unregister_endpoint` does, with the same new secrets and the same runs that stop counting. **Check:** `connect_status` shows every module your vendors need as `conformant`. ## What stays with a human - Creating the account and the API key, and keeping the key out of the repository. - Entering vendor credentials. - A PAM service credential for the adapter, allowed to call the PAM routes each module forwards to (the skill `onehazel://skill/integrate` lists them, generated from the contract). - Deploying the adapter. Order: check config, deploy, probe healthz, register. Run the snippet's --check line on every config until each exits 0, then ask your human to run the snippet's Deploy section on the host (deploying the adapter is a human step), confirm both /healthz probes answer 200, then register. - Anything that moves real money. - **Go live.** No tool can activate a vendor; a person clicks Go live in the app. ## Safety notes - Tool output never contains keys or secrets; `oh_live_`, `whsec_` and similar values are masked. - The exception is a first `connect_register_endpoint` for a module and `connect_rotate_secret`, which return the signing secret and callback key once with a `handling` field (registering an existing module again returns neither, unless you pass a different `callbackKey`): your agent writes them to `/.onehazel/.env` (mode 0600, gitignored) or your secret store and tells you only the path and the variable names. - If a secret was ever printed, logged or committed, call connect_rotate_secret for that module with what "both" (or "callback_key" or "signing_secret" for one of them); the previous values stay accepted for 1 hour, so update the adapter within that hour. - Data the tools return (for example delivery logs) can reach your agent's model. Use a key scoped to Connect only. - Every URL you register is checked: `https`, public address, no credentials. ## See also The step-by-step MCP guide, written for a person with no engineering background: - [Install](/connect/mcp/install), then the page for your agent: [Codex](/connect/mcp/install/codex), [Cursor](/connect/mcp/install/cursor) or [Claude Code](/connect/mcp/install/claude-code). - [The journey](/connect/mcp/journey): every step to a live game, and every question your agent can ask. - [Your casino](/connect/mcp/your-casino): the adapter, the checklist, and how to start it. - [Go live](/connect/mcp/go-live) and [PatePlay](/connect/mcp/providers/pateplay). - [MCP troubleshooting](/connect/mcp/troubleshooting) and [MCP FAQ](/connect/mcp/faq). Also: - [How to integrate with AI](/connect/how-to-integrate-with-ai): the same setup written for the person, with what the module chips mean. - [Troubleshooting](/connect/troubleshooting) and [FAQ](/connect/faq) for the Operator Contract in general.