# MCP & connectors (https://docs-ecmrhub-v2.lucaspiera.com/docs/mcp)

> Connect Claude, ChatGPT or another MCP client to a company account through the Hub remote MCP server, and how its tools behave.

> **In build (MCP server):** The remote MCP server is part of phase 1 and not released yet. Address, sign-in and tool
> behaviour below are the agreed contract.

Hub runs a remote MCP server. A chat assistant connected to it works in one company's account,
with the permissions a person granted during sign-in.

| | |
| --- | --- |
| Server URL | `https://api-ecmr-dev.lucaspiera.com/functions/v1/mcp` |
| Transport | Streamable HTTP, stateless, JSON responses |
| Sign-in | OAuth 2.1; clients register themselves (Dynamic Client Registration) |
| Protected resource metadata | `https://api-ecmr-dev.lucaspiera.com/functions/v1/mcp/.well-known/oauth-protected-resource` |

## Claude (web, desktop, mobile) [#claude-web-desktop-mobile]

On Free, Pro and Max plans you add the connector to your own account. The Free plan allows one
custom connector.

1. Open **Customize > Connectors** and click **Add custom connector**.
2. Enter a name (for example "eCMR Hub") and the server URL `https://api-ecmr-dev.lucaspiera.com/functions/v1/mcp`. Leave the OAuth client
   fields empty: Claude registers itself.
3. Click **Add**, then **Connect**.
4. Sign in to Hub in the window that opens, choose the permissions and keep "Require my approval"
   on. Claude can now use the Hub tools.

On Team and Enterprise plans an Owner adds the connector under **Organization settings >
Connectors** (**Add**, then **Custom**, type **Web**), and each member connects with their own
Hub account under **Customize > Connectors**.

Source: Anthropic, "Add a connector that isn't in the directory" (claude.com/docs/connectors,
read 19 October 2026).

## Claude Code [#claude-code]

```bash
claude mcp add --transport http ecmr-hub https://api-ecmr-dev.lucaspiera.com/functions/v1/mcp
```

Then run `/mcp` in Claude Code, choose the server and **Authenticate** in the browser. For a
terminal agent the [CLI](https://docs-ecmrhub-v2.lucaspiera.com/docs/cli.md) is an alternative with the same operations.

## ChatGPT [#chatgpt]

Custom MCP servers are added on ChatGPT on the web. Whether you can add one depends on your plan
and on your workspace's settings.

1. Go to **chatgpt.com/plugins**, select the plus button, then **Add custom MCP server**.
2. Enter a name and, under **Connection**, the server URL `https://api-ecmr-dev.lucaspiera.com/functions/v1/mcp`.
3. Choose **OAuth** authentication. ChatGPT registers itself.
4. Read the risk warning, select **I understand and want to continue**, then **Create as a plugin**.
5. Install the plugin, sign in to Hub, choose the permissions.
6. In a conversation type `@` and pick the plugin.

ChatGPT asks you to confirm each write tool call on its side. That confirmation is separate from
Hub's approval in the dashboard: issuing still needs the person's approval in Hub.

Source: OpenAI, "Add custom MCP server" (developers.openai.com, read 19 October 2026).

## Other MCP clients [#other-mcp-clients]

Any client with Streamable HTTP and OAuth 2.1 sign-in works the same way: point it at
`https://api-ecmr-dev.lucaspiera.com/functions/v1/mcp` and let it register. A typical configuration file:

```json
{
  "mcpServers": {
    "ecmr-hub": { "url": "https://api-ecmr-dev.lucaspiera.com/functions/v1/mcp" }
  }
}
```

Prefer OAuth. A client that only supports a fixed header can send a company API key instead:

```bash
claude mcp add --transport http ecmr-hub https://api-ecmr-dev.lucaspiera.com/functions/v1/mcp \
  --header "Authorization: Bearer $ECMR_HUB_API_KEY"
```

The server signs in with the key for you; the key's permissions and approval setting apply. Keep
the key out of shared configuration files.

## Tools [#tools]

Tools come from the same [operation catalog](https://docs-ecmrhub-v2.lucaspiera.com/docs/cli/operations.md) as the CLI:

- The tool name is the operation name with dots replaced by underscores: `draft.publish` becomes
  `draft_publish`.
- Only operations Hub executes on the server and a connection may call are tools: about 40 of the
  catalog. Operations reserved for a person or the owner (approving, managing people, keys and
  connections, deleting an account) and operations of the eCMR app screen are not.
- Each tool carries `readOnlyHint` and `destructiveHint`, so a client can ask you to confirm
  writes on its side.
- Every writing tool has an optional `approvalId` argument. Pass it only after a result with
  `status: "approval_required"`.
- A tool fails when the connection lacks its permission.
- `document_pdf` returns the PDF as an embedded resource (`application/pdf`) with its SHA-256.

The list your client sees comes from `tools/list`, and it is the final word.

## When a tool needs approval [#when-a-tool-needs-approval]

A tool that needs a person's approval does not fail. It returns a normal result:

```json
{
  "structuredContent": {
    "status": "approval_required",
    "approvalId": "6f1c2a9e-...",
    "url": "https://ecmrhub-v2.lucaspiera.com/dashboard/approvals/6f1c2a9e-...",
    "expiresAt": "2026-10-20T10:00:00.000Z",
    "operation": "draft.publish"
  }
}
```

The text content says the same in words. Show the link to the user. After they approve, call the
same tool with the same arguments plus `"approvalId": "6f1c2a9e-..."`. The rules are on
[Human approvals](https://docs-ecmrhub-v2.lucaspiera.com/docs/approvals.md).

Other results a tool may return, each with a text explaining the next step:

| `status` | Error | Meaning |
| --- | --- | --- |
| `approval_rejected` | yes | The person rejected it; ask the user what to change |
| `approval_expired` | yes | Older than 24 hours; call again without `approvalId` |
| `approval_mismatch` | yes | The arguments differ from the approved ones |
| `error` | yes | Any other error, with `code`, `httpStatus` and `retryable` |

## Sign-in errors [#sign-in-errors]

| Answer | Meaning |
| --- | --- |
| `401` with `WWW-Authenticate: Bearer resource_metadata="..."` | No token: the client starts the OAuth sign-in |
| `401 CONNECTION_REVOKED` | The company disconnected this client |
| `401 CONNECTION_REQUIRED` | The token is not from a connection or an API key (for example a dashboard session) |
| `401 API_KEY_INVALID` | The API key is wrong, revoked or expired |
| `503 AUTH_UNAVAILABLE` | Sign-in could not be checked; retry |

## Server instructions [#server-instructions]

On connect the server sends short instructions to the model: approvals come from a person and
the model must not try to approve; never invent data, ask the user; eFTI support is validation and
a list of gaps only. Agents should follow them in every conversation.

## Disconnecting [#disconnecting]

The company sees every connected client in the dashboard under Connections and can disconnect
it. After that the client's calls fail with `401 CONNECTION_REVOKED`; connecting again needs a new
sign-in.
