# Building an MCP Server for Duvo

> **Note:** Building a Custom MCP server requires software development expertise. This page assumes familiarity with API development and deployment. If you're new to MCP, start with the [official MCP documentation](https://modelcontextprotocol.io/docs/getting-started/intro), which has tutorials and SDKs in multiple languages.

This page covers what your MCP server needs to look like so Duvo can connect to it as a [Custom MCP Connection](/mcp/custom-mcp-servers.md). For the user-facing setup steps in Duvo, see the [Custom MCP connection page](https://github.com/duvoai/monorepo/blob/gitbook-prod/knowledge-base/connections/available-connections/custom-mcp.md).

## Requirements

For Duvo to call your MCP server, it must meet these technical requirements:

* **Cloud accessibility** — The server must be reachable over the public internet via HTTPS. Localhost and private-network-only endpoints are not supported.
* **Streamable HTTP transport** — Duvo uses MCP's Streamable HTTP transport (`POST /mcp`). Servers that only expose STDIO or the older HTTP/SSE transport cannot connect.
* **Valid HTTPS certificate** — Self-signed certificates may fail to connect; use a certificate from a trusted CA.

## Authentication options

Pick whichever method fits your deployment:

* **None** — No auth. Use this only when the server handles auth at the network layer or genuinely has no auth surface.
* **Static API key or token** — Each teammate enters their own key when they sign into the Connection. Duvo passes the key as a bearer token (or however your server expects it).
* **Custom HTTP headers** — Each teammate provides their own header values when they sign in. Use this for any auth that doesn't fit "API key as bearer token".
* **OAuth** — Recommended for multi-user deployments. Duvo detects [Dynamic Client Registration (RFC 7591)](https://datatracker.ietf.org/doc/html/rfc7591) automatically; if your server supports DCR, teammates can connect with no Client ID/Secret setup at all.

### Implementing OAuth with DCR

Supporting DCR is the most polished experience for Duvo users. The MCP server side typically needs:

* A `GET /.well-known/oauth-protected-resource` endpoint declaring the authorization server (RFC 9728)
* A `WWW-Authenticate: Bearer resource_metadata=...` challenge on 401 responses
* A backing OAuth authorization server that implements [RFC 7591 Dynamic Client Registration](https://datatracker.ietf.org/doc/html/rfc7591)

If your authorization server doesn't support DCR, you can still use OAuth — teammates will enter a pre-registered Client ID and Client Secret when setting up the Connection, and you'll need to register Duvo's redirect URI (`https://platform.duvo.ai/v1/oauth/mcp/callback`) on your provider.

## Designing your tools

A few practical guidelines that pay off when Duvo Assignments call your tools:

* **Describe every tool clearly.** The description is what an Assignment sees when deciding whether to call it.
* **Use flat input schemas.** A single top-level object reads better than wrapping params in `query`, `body`, etc.
* **Return structured data when possible** (JSON, not just human-readable strings). The Assignment can pick out the field it needs without re-parsing.
* **Surface errors as proper MCP errors** rather than 200 responses with an error string in the body. Duvo's Assignments can retry or escalate more reliably this way.
* **Idempotency keys** on destructive operations are a good defense against accidental double-calls.

## Hosting

You're free to host MCP servers wherever you like — Cloud Run, Lambda + API Gateway, Fly, a VM, a managed PaaS, your own Kubernetes cluster. Anything that can expose a public HTTPS endpoint works.

For small-scale deployments, a single container behind a TLS-terminating load balancer is usually enough. Add caching where it helps, and rate-limit defensively if the underlying system is sensitive to bursts.

## After deploying

Once your server is running publicly:

1. Open the [Connections page](https://app.duvo.ai/integrations) in Duvo.
2. Click **Add custom connection** at the bottom of the catalog.
3. Pick your auth method, enter the server URL, and click **Create**.
4. Each teammate signs into the new Connection with their own credentials.

For the full UI walkthrough, see [Custom MCP](https://github.com/duvoai/monorepo/blob/gitbook-prod/knowledge-base/connections/available-connections/custom-mcp.md).

## Related

* [Custom MCP Connections](/mcp/custom-mcp-servers.md) — overview of the user-facing Connection
* [Custom MCP connection page](https://github.com/duvoai/monorepo/blob/gitbook-prod/knowledge-base/connections/available-connections/custom-mcp.md) — full setup walkthrough
* [Official MCP documentation](https://modelcontextprotocol.io) — protocol spec, SDKs, and reference servers


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.duvo.ai/mcp/building-mcp-servers.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
