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 clientThe 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 McpServerYour 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.
- Like every live object, it also has hydrate(), refresh() and raw.
| id | str | |
| name | str | |
| url | str | The URL MCP clients connect to; the path is its secret. |
| tools | list[Any] | What it serves, as you listed it: refs, and |
| tool_count | int | |
| user_id | str | None | |
| created_at | Any | |
| last_client_at | Any | When a client last called it (None until one does). |
createclassmethodasync
noclick.McpServer.create(name, *, tools, user=..., client=None) -> McpServerCreate 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.
Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
None)The new server, with the url to give your MCP client.
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
) -> McpServerA 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.
NoneFalseWho the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
None)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.
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=...) -> McpServerA lazy reference to a server by id.
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
None)Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).
A server that loads the first time you read a field. Nothing is sent yet.
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) -> McpServerLoad a server by id now; an unknown id raises noclick.NotFound.
Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
None)The server, loaded.
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).
Return at most this many.
None)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.
None)Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
None)Every matching server, newest first, fetched page by page.
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
) -> PageOne page of servers; pass next_cursor back as cursor.
The next_cursor of the page before; omit it for the first page.
None)Return at most this many.
None)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.
None)Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
None)A page whose data holds the servers and whose next_cursor is None on the last page.
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=...) -> McpServerChange 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).
This server, updated.
server = noclick.McpServer.from_name("crm-tools", user="acme")
server.update(tools=["hubspot", "search", "fetch"])rotateasync
server.rotate() -> McpServerGive it a new URL; the old one stops answering at once, so reconnect your clients with the new one.
This server, with its new url.
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.
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.
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() -> NoneDelete the server; its URL stops answering. The connections it served stay.
noclick.McpServer.from_name("crm-tools", user="acme").delete()McpClient
class McpClientAn 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.
| name | str | |
| version | str | None | |
| first_seen_at | Any | |
| last_seen_at | Any | |
| tool_count | int | None | |
| raw | dict[str, Any] | The response JSON exactly as the API sent it. |