McpServer

Hosted MCP servers: your tools at one URL for any MCP client.

server = noclick.McpServer.from_name("crm-tools", tools=["hubspot", "search"], create_if_missing=True)
print(server.url)   # paste into Claude, ChatGPT, Cursor or any MCP client

The tools are the ones a Toolkit or an agent takes (connections, single operations, search and fetch), served over Streamable HTTP. Calls act in the server's user's connections, under your policy, and bill like toolkit calls. A name that is no connection there serves an integration's operations that need no account, on NoClick's key: tools=["exa.search"] needs no connection.

class McpServer

Your tools at one URL, for Claude, ChatGPT, Cursor or any MCP client. Anyone with url can call the tools, so keep it like a key and rotate() it if it leaks. tool_count is how many tools the server serves right now (a connection's operations count one each; a connection that isn't connected serves none); last_client_at is when any client last talked to it.

Attributes
idstr
namestr
urlstr

The URL MCP clients connect to; the path is its secret.

toolslist[Any]

What it serves, as you listed it: refs, and {tool, bind} for an operation with arguments bound.

tool_countint
user_idstr | None
created_atAny
last_client_atAny

When a client last called it (None until one does).

createclassmethodasync

noclick.McpServer.create(name, *, tools, user=..., client=None) -> McpServer

Create a server called name (unique per account and user; a duplicate raises noclick.Conflict with existing_id). tools are what an agent or a Toolkit takes: connections (a Connection or its name), single operations ("hubspot.create_contact"), "search" and "fetch", or a Toolkit, at most 64 tools in all. A name is a connection in the server's user (the account for an account server), else an integration's operations that need no account (on NoClick's key), so "exa.search" needs no connection. Operations with arguments bound (hubspot.only(...).bind(...), ConnectionOperations.bind) keep them out of every client's reach; there {{user...}} is the server's user.

Parameters
namestrrequired
toolsSequence[Any]required
userAny

Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).

(defaults to the current user)
clientClient | None

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

(default None)
Returns
McpServer

The new server, with the url to give your MCP client.

Usage
with noclick.as_user("acme"):
    hubspot = noclick.Connection.from_name("hubspot", integration="hubspot", create_if_missing=True)
    server = noclick.McpServer.create("crm-tools", tools=[hubspot, "search"])
print(server.url)

from_nameclassmethod

noclick.McpServer.from_name(
    name, *, tools=None, create_if_missing=False, user=..., client=None
) -> McpServer

A lazy reference to the server called name in the current (or given) user. With create_if_missing=True your code is the definition: it's created on first use, and an existing server whose tools differ is updated to match. Its URL stays the same either way.

Parameters
namestrrequired
toolsSequence[Any] | Nonedefault None
create_if_missingbooldefault False
userAny

Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).

(defaults to the current user)
clientClient | None

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

(default None)
Returns
McpServer

A server that loads (or is created, or brought in step with your definition) the first time you read a field. Nothing is sent yet.

Usage

Serve a user's CRM and web search to their Claude:

server = noclick.McpServer.from_name(
    "crm-tools", tools=["hubspot", "search"], create_if_missing=True, user="acme",
)
print(server.url)

Refer to one by name:

server = noclick.McpServer.from_name("crm-tools", user="acme")
print(server.tool_count, server.last_client_at)

from_idclassmethod

noclick.McpServer.from_id(server_id, *, client=None, user=...) -> McpServer

A lazy reference to a server by id.

Parameters
server_idstrrequired
clientClient | None

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

(default None)
userAny

Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).

(defaults to the current user)
Returns
McpServer

A server that loads the first time you read a field. Nothing is sent yet.

Usage
server = noclick.McpServer.from_id("6f1d8b3a-2c7e-4a9f-b0d5-3e8c1a7f4b92")
for client in server.clients():
    print(client.name, client.last_seen_at)

getclassmethodasync

noclick.McpServer.get(server_id, *, user=..., client=None) -> McpServer

Load a server by id now; an unknown id raises noclick.NotFound.

Parameters
server_idstrrequired
userAny

Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).

(defaults to the current user)
clientClient | None

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

(default None)
Returns
McpServer

The server, loaded.

Usage
server = noclick.McpServer.get("6f1d8b3a-2c7e-4a9f-b0d5-3e8c1a7f4b92")
print(server.name, server.url, server.tools)

listclassmethodasync

noclick.McpServer.list(
    *, limit=None, user_filter=None, user=..., client=None
) -> list[McpServer]

The current user's servers (the account's own outside as_user, or those user_filter covers).

Parameters
limitint | None

Return at most this many.

(default None)
user_filterAny

Which users a read covers: noclick.ALL_USERS, noclick.NO_USER, a User, or your id for one. It filters without acting as that user.

(default None)
userAny

Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).

(defaults to the current user)
clientClient | None

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

(default None)
Returns
list[McpServer]

Every matching server, newest first, fetched page by page.

Usage
for server in noclick.McpServer.list(user_filter=noclick.ALL_USERS):
    print(server.name, server.user_id, server.tool_count, server.last_client_at)

list_pageclassmethodasync

noclick.McpServer.list_page(
    *, cursor=None, limit=None, user_filter=None, user=..., client=None
) -> Page

One page of servers; pass next_cursor back as cursor.

Parameters
cursorstr | None

The next_cursor of the page before; omit it for the first page.

(default None)
limitint | None

Return at most this many.

(default None)
user_filterAny

Which users a read covers: noclick.ALL_USERS, noclick.NO_USER, a User, or your id for one. It filters without acting as that user.

(default None)
userAny

Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).

(defaults to the current user)
clientClient | None

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

(default None)
Returns
Page

A page whose data holds the servers and whose next_cursor is None on the last page.

Usage
page = noclick.McpServer.list_page(limit=50)
while True:
    for server in page.data:
        print(server.name)
    if page.next_cursor is None:
        break
    page = noclick.McpServer.list_page(limit=50, cursor=page.next_cursor)

updateasync

server.update(*, tools=..., name=...) -> McpServer

Change what it serves, or its name; the URL stays. A connected client sees new tools when it next lists them (most do when a session starts).

Parameters
toolsAnyomit to leave it unchanged
nameAnyomit to leave it unchanged
Returns
McpServer

This server, updated.

Usage
server = noclick.McpServer.from_name("crm-tools", user="acme")
server.update(tools=["hubspot", "search", "fetch"])

rotateasync

server.rotate() -> McpServer

Give it a new URL; the old one stops answering at once, so reconnect your clients with the new one.

Returns
McpServer

This server, with its new url.

Usage
server = noclick.McpServer.from_name("crm-tools", user="acme")
server.rotate()
print(server.url)

clientsasync

server.clients() -> list[McpClient]

The MCP clients that have connected, most recently seen first: whether your agent has actually started using the server, apart from whether the server is ready.

Returns
list[McpClient]

Each client's name and version as it reported them, when it was first and last seen, and how many tools it listed. An empty list until a client connects.

Usage
server = noclick.McpServer.from_name("crm-tools", user="acme")
for client in server.clients():
    print(client.name, client.version, client.last_seen_at, client.tool_count)

deleteasync

server.delete() -> None

Delete the server; its URL stops answering. The connections it served stay.

Usage
noclick.McpServer.from_name("crm-tools", user="acme").delete()

McpClient

class McpClient

An MCP client that has connected to a server, as it named itself (Claude, cursor, openai-mcp). tool_count is how many tools it saw when it last listed them: fewer than the server serves means it listed them before you added some, and most clients list again only when a session starts.

Attributes
namestr
versionstr | None
first_seen_atAny
last_seen_atAny
tool_countint | None
rawdict[str, Any]

The response JSON exactly as the API sent it.