Skip to content

Adding an MCP tentacle

Most connectors need no code. A bare, oauth or oauth_discovery block in tentacles.yaml declares a remote MCP server, and build_mcp turns it into an McpTentacle users can install. See MCP proxy.

A bespoke tentacle exists for one reason: the connector must behave differently from a generic declaration. Slack's does, because its tools must act as the same person the channel is talking to, and because it ships instructions of its own.

The class

McpTentacle is a description: an auth_kind, an optional operator token, a label, the upstream URL, instructions shown to the model with the tool catalog, and serving, which says whether the connector is on at all. Two variants ship: BareMcpTentacle for an operator credential and OAuthMcpTentacle for per-user grants.

To write one:

class MyServiceTentacle(OAuthMcpTentacle):
    """My Service's MCP server, acting as the person who connected it."""

    label = "My Service"
    upstream = "https://mcp.myservice.example/mcp"
    instructions = "The tools act as the connected person. Ids are ..."

    def __init__(self, id: str, octomate: Octomate, *, config: MyServiceConfig) -> None:
        super().__init__(id=id, octomate=octomate)
        octomate.oauth.register(
            OAuthConnector(
                id=id,
                mcp_url=self.upstream,
                flows=[AuthorizationCodeFlow(...)],
                callback_transport=DirectHttpOAuthCallbackTransport(
                    config.callback_base_uri
                ),
            )
        )

    @property
    def serving(self) -> bool:
        return self.config.mcp

The OAuth connector is what the OAuth manager uses to start and complete a user's authorisation and to refresh their tokens; resolve_profile on it is optional, and is how Slack and Discord link a channel profile from a successful grant.

Register the config model in McpConfigVariant and add a case to build_mcp in octomate/tentacles/mcp.py. Octomate.connect files every McpTentacle with the MCP manager, which is what makes it an offering.

A channel can be a connector too, as Slack is, by inheriting both bases and letting a channel flag decide serving.