> built 2026-09-22 14:39 UTC from 8c29353 (main) · py2mcp 0.1.15. Details: build_info.json

# index.html.md

<!-- generated by epythet -->

# py2mcp

Quick MCP (Model Context Protocol) server creation from Python functions.

<!-- epythet:agentic-readme:start -->

## For AI agents

`py2mcp` publishes its documentation in forms made for coding agents. If you are one, start here.

**The documentation, machine-readable**: [`llms.txt`](https://i2mint.github.io/py2mcp/llms.txt) indexes every page; [`py2mcp.md`](https://i2mint.github.io/py2mcp/py2mcp.md) is the whole documentation in one file; every page has a `.md` twin; [`objects.inv`](https://i2mint.github.io/py2mcp/objects.inv) maps symbols to URLs.

If you identify as a dinosaur, the rest of this README is written for you, starting at [Installation]().

<!-- epythet:agentic-readme:end -->

## Installation

```bash
pip install py2mcp
```

## Quick Start

```python
from py2mcp import mk_mcp_server


def add(a: int, b: int) -> int:
    """Add two numbers"""
    return a + b


def greet(name: str = "world") -> str:
    """Greet someone"""
    return f"Hello, {name}!"


# Create and run MCP server
mcp = mk_mcp_server([add, greet])

if __name__ == "__main__":
    mcp.run()
```

That’s it! Your functions are now available as MCP tools.

## Features

- **Simple**: Just pass functions to `mk_mcp_server()`
- **Flexible**: Supports input/output transformations
- **Pythonic**: Clean, decorator-free function definitions
- **Powerful**: Built on FastMCP for production-ready servers

## Input Transformations

Transform inputs before they reach your functions:

```python
from py2mcp import mk_mcp_server, mk_input_trans
import numpy as np


def add_arrays(a, b):
    """Add two numpy arrays"""
    return (a + b).tolist()


# Convert list inputs to numpy arrays
input_trans = mk_input_trans({"a": np.array, "b": np.array})
mcp = mk_mcp_server([add_arrays], input_trans=input_trans)
```

## From Stores (MutableMapping)

Automatically expose CRUD operations from any mapping:

```python
from py2mcp import mk_mcp_from_store

projects = {"proj1": {"name": "Project 1"}, "proj2": {"name": "Project 2"}}
mcp = mk_mcp_from_store(projects, name="project")

# Automatically creates: list_projects, get_project, set_project, delete_project
```

## Serving: local (stdio) and remote (HTTP + OAuth)

`mk_mcp_*` build a server *object*; py2mcp also gives you two ways to **run** one.

**Local (stdio)** — for a one-click bundle (e.g. a Claude Desktop `.mcpb`):

```python
from py2mcp import serve_stdio

serve_stdio(["mypkg.tools:summarize", "mypkg.tools:translate"], name="My Tools")
# or:  python -m py2mcp --config py2mcp_config.json
```

**Remote (Streamable HTTP + OAuth 2.1)** — for a hosted MCP server reached from a
vendor’s cloud (e.g. a claude.ai custom connector). The server is an OAuth 2.1
**resource server**: it *validates* a managed IdP’s JWTs (audience-bound per
RFC 8707) and never issues tokens itself.

```python
from py2mcp.http import mk_http_app

AUTH = {
    "type": "jwt",  # resource-server: validate the IdP's JWTs
    "jwks_uri": "https://idp.example.com/.well-known/jwks.json",
    "issuer": "https://idp.example.com",
    "audience": "https://my-connector.example.com/mcp",  # THIS server (RFC 8707)
    "authorization_servers": ["https://idp.example.com"],
    "base_url": "https://my-connector.example.com",
    "required_scopes": ["mcp:read"],
}

# An ASGI app you run under any ASGI server (uvicorn, gunicorn, serverless):
app = mk_http_app(["mypkg.tools:summarize"], name="My Connector", auth=AUTH)
#   uvicorn server.app:app --host 0.0.0.0 --port 8000   (behind TLS)
```

`serve_http(...)` builds and runs it in-process (FastMCP/uvicorn). Both wrap
FastMCP’s native transports/OAuth — py2mcp does not reinvent them.

## Middleware (metering, logging, rate-limiting)

Every builder accepts `middleware=` — a single [FastMCP middleware](https://gofastmcp.com/servers/middleware) or an iterable of them — attached at construction, exactly as `auth=` is. It’s the one clean seam for cross-cutting concerns that must wrap *every* tool call (usage metering, cost logging, audit trails, rate limiting), so you don’t decorate each function individually — and can’t forget one (a missed decorator on a paid tool means untracked cost):

```python
from fastmcp.server.middleware import Middleware


class UsageMeter(Middleware):
    async def on_call_tool(self, context, call_next):
        result = await call_next(context)  # the tool runs here
        record(context.message.name)  # ... then meter it
        return result


mcp = mk_mcp_server([render, estimate], middleware=[UsageMeter()])
# same on mk_mcp_from_refs(...), mk_mcp_from_store(...), mk_http_app(...),
#         serve_http(...), serve_stdio(...)
```

On the remote path `auth=` (transport-level) runs first, so a middleware can read
the authenticated caller via `fastmcp.server.dependencies.get_access_token()`.
Middleware is a *programmatic* hook — it takes Python objects, so it isn’t wired
through the `python -m py2mcp` CLI / JSON-config path (unlike `refs`/`name`/`auth`).

## Instructions (the server’s model-facing description)

Every builder also accepts `instructions=` — a natural-language string surfaced to
the connecting client/model as the server’s [`instructions`](https://gofastmcp.com/servers/server),
attached at construction exactly like `auth=`/`middleware=`. It’s the place to say
what the tools are for and the intended workflow, so a model can orient itself
without calling a tool:

```python
mcp = mk_mcp_server(
    [render, estimate],
    instructions="Turn source docs into narrated audio. Always estimate_cost before a render.",
)
# same keyword on mk_mcp_from_refs(...), mk_mcp_from_store(...), mk_http_app(...),
#                 serve_http(...), serve_stdio(...)
```

Like `middleware=`, it’s a programmatic argument (not yet wired through the
`python -m py2mcp` CLI / JSON-config path).

## Prompts and resources

Every builder also accepts `prompts=` and `resources=`, so a server that ships MCP
[prompts](https://gofastmcp.com/servers/prompts) and
[resources](https://gofastmcp.com/servers/resources) alongside its tools can be
built declaratively in one call, instead of reaching past the builder to register
them by hand on the returned `FastMCP` object:

```python
def summarize_request(topic: str) -> str:
    return f"Summarize the latest on {topic}."


def schema() -> dict:
    return {"type": "object"}


mcp = mk_mcp_server(
    [render, estimate],
    prompts=summarize_request,  # a callable, or an iterable of them
    resources={"schema://analysis": schema},  # {uri: callable}
)
# same keywords on mk_mcp_from_refs(...), mk_mcp_from_store(...), mk_http_app(...),
#                  serve_http(...), serve_stdio(...)
```

`prompts` accepts a single callable or an iterable, normalized the same way `funcs`
is for tools. `resources` is a `{uri: callable}` mapping — each callable is invoked
to produce that resource’s content when a client reads its URI.

## “Add to Claude” install links

Once a server is hosted, the last mile is getting a human to add it. There’s no
true one-click install for an unlisted connector (listing requires Anthropic
review), but a prefilled link opens the add-connector modal with the name and
URL already filled in, so the user only has to confirm:

```python
from py2mcp import claude_install_link, markdown_install_badge

claude_install_link("snout", "https://example.com/api/snout_mcp/mcp")
# 'https://claude.ai/customize/connectors?modal=add-custom-connector&connectorName=snout&...'

markdown_install_badge("snout", "https://example.com/api/snout_mcp/mcp")
# '[Add snout to Claude](https://claude.ai/customize/connectors?...)'  <- paste into a README

claude_install_link("snout", "...", admin=True)  # org-wide page, not per-user
```

Both are pure string functions (stdlib only, no server needed). Three caveats
the link itself can’t express:

- Custom connectors are a **paid-plan** feature, so the link goes nowhere for a
  Free-plan user.
- `admin=True` targets the org-wide install page — the right one when an admin
  is rolling a connector out to a workspace, the wrong one for a personal install.
- **A link is not an access grant.** If the server is an OAuth resource server
  with an allowlist (see above), someone not on it can follow the link, complete
  the flow, and still be refused. Hand out the link together with whatever adds
  them to the allowlist.

## License

MIT

<p class="epythet-aggregates">This documentation as a single file: <a href="py2mcp.md">py2mcp.md</a> (Markdown, for agents).</p>


# _autosummary/py2mcp.base.html.md

# py2mcp.base

Private helpers shared by the builders in [`py2mcp.main`](_autosummary/py2mcp.main.html.md#module-py2mcp.main).

Argument normalization (`funcs` and `middleware` accept one object or an
iterable) and the wrapper that applies an `input_trans` to a tool’s keyword
arguments before the function runs. Nothing here is part of the public API.


# _autosummary/py2mcp.html.md

# py2mcp

py2mcp: Quick MCP server creation from Python functions.

Pass ordinary Python functions and get back a Model Context Protocol (MCP)
server, built on FastMCP, with each function registered as a tool. The
`mk_mcp_*` builders return a server *object* and leave running it to you:
`mcp.run()` for stdio, or [`py2mcp.serve`](_autosummary/py2mcp.serve.html.md#module-py2mcp.serve) and [`py2mcp.http`](_autosummary/py2mcp.http.html.md#module-py2mcp.http) for a
packaged stdio launcher and a Streamable-HTTP (optionally OAuth 2.1) server.

Main entry points:

- `mk_mcp_server`: functions in, `FastMCP` server out
- `mk_mcp_from_refs`: the same from `'module:function'` strings
- `mk_mcp_from_store`: list/get/set/delete tools over any `MutableMapping`
- `mk_input_trans`: per-argument conversion of tool inputs
- `serve_stdio` and `serve_http`: build from refs and run

```pycon
>>> from py2mcp import mk_mcp_server
>>> def add(a: int, b: int) -> int:
...     '''Add two numbers'''
...     return a + b
>>> mcp = mk_mcp_server([add])
>>> mcp.name
'py2mcp Server'
>>> # mcp.run()  # Start the server over stdio
```

### Functions

| [`mk_mcp_server`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_server)(funcs, \*[, name, input_trans, ...])   | Create an MCP server from Python functions.                                    |
|-------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------|
| [`mk_mcp_from_store`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_from_store)(store, \*[, name, plural, ...])    | Create an MCP server from a MutableMapping with CRUD operations.               |
| [`mk_mcp_from_refs`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_from_refs)(refs, \*[, name, ...])              | Create an MCP server from `'module:function'` reference strings.               |
| [`mk_input_trans`](_autosummary/py2mcp.html.md#py2mcp.mk_input_trans)([name_func_relationships])            | Create an input transformation function from name->func mappings.              |
| [`import_object`](_autosummary/py2mcp.html.md#py2mcp.import_object)(ref)                                   | Resolve a `'module.path:attr'` (preferred) or `'module.path.attr'` reference.  |
| [`claude_install_link`](_autosummary/py2mcp.html.md#py2mcp.claude_install_link)(name, mcp_url, \*[, admin])      | Prefilled "Add custom connector" link for claude.ai.                           |
| [`markdown_install_badge`](_autosummary/py2mcp.html.md#py2mcp.markdown_install_badge)(name, mcp_url, \*[, admin])   | Markdown link that installs an MCP server as a claude.ai connector.            |
| [`serve_stdio`](_autosummary/py2mcp.html.md#py2mcp.serve_stdio)(refs, \*[, name, input_trans, ...])      | Build an MCP server from `'module:function'` refs and run it over stdio.       |
| [`resolve_server_config`](_autosummary/py2mcp.html.md#py2mcp.resolve_server_config)(\*[, config, refs, name])      | Merge a config file and explicit `refs`/`name` into `(refs, name)`.            |
| [`load_server_config`](_autosummary/py2mcp.html.md#py2mcp.load_server_config)(path)                             | Load a server config JSON of the form `{"name": str, "refs": [str, ...]}`.     |
| [`mk_http_app`](_autosummary/py2mcp.html.md#py2mcp.mk_http_app)(refs, \*[, name, auth, ...])             | Build a Streamable-HTTP **ASGI app** from `refs` (+ optional OAuth).           |
| [`serve_http`](_autosummary/py2mcp.html.md#py2mcp.serve_http)(refs, \*[, name, host, port, ...])        | Build and **run** a Streamable-HTTP MCP server (blocking) via FastMCP/uvicorn. |
| [`mk_auth_provider`](_autosummary/py2mcp.html.md#py2mcp.mk_auth_provider)(auth)                               | Build a FastMCP **resource-server** auth provider from an auth-config dict.    |

### py2mcp.claude_install_link(name, mcp_url, , admin=False)

Prefilled “Add custom connector” link for claude.ai.

There is no true one-click install for an unlisted MCP server (listing
requires Anthropic review), but this link opens the add-connector modal with
the name and URL already filled in, so the user only has to confirm — which
beats “go to Settings, find Connectors, paste this long URL”.

* **Parameters:**
  * **name** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Connector name to prefill (what the user will see in their list).
  * **mcp_url** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Full URL of the MCP endpoint, e.g. `https://host/api/x/mcp`.
  * **admin** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Target the org-wide install page instead of the per-user one.
    Use it when an admin is rolling the connector out to a whole
    workspace; the default (`False`) is the personal install.
* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)
* **Returns:**
  The claude.ai URL, safe to paste into a README or a chat message.

```pycon
>>> claude_install_link('snout', 'https://example.com/api/snout_mcp/mcp')
'https://claude.ai/customize/connectors?modal=add-custom-connector&connectorName=snout&connectorUrl=https%3A%2F%2Fexample.com%2Fapi%2Fsnout_mcp%2Fmcp'
```

Names and URLs are percent-encoded, so spaces (and `&`) can’t break the
query string:

```pycon
>>> claude_install_link('my server', 'https://x.io/mcp', admin=True)
'https://claude.ai/admin-settings/connectors?modal=add-custom-connector&connectorName=my%20server&connectorUrl=https%3A%2F%2Fx.io%2Fmcp'
```

Note that the link is a convenience, not an access grant: if the server is an
OAuth resource server with an allowlist, a user who isn’t on it can follow
the link, complete the flow, and still be refused. Custom connectors are also
a paid-plan feature, so the link goes nowhere for a Free-plan user.

### py2mcp.import_object(ref)

Resolve a `'module.path:attr'` (preferred) or `'module.path.attr'` reference.

Useful for building MCP servers from configuration strings (e.g. tool
references declared in a file), so callers don’t reimplement the
`importlib` dance. With a colon, everything before it is the module and
everything after it an attribute path; without one, the last dot splits
module from attribute, so `'pkg.mod.Class.method'` cannot be reached in
the dotted form (use `'pkg.mod:Class.method'`).

* **Parameters:**
  **ref** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The reference string; the `module:attr` form is preferred.
* **Return type:**
  [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)
* **Returns:**
  The object the reference names, after importing its module.
* **Raises:**
  * [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – The reference has no module part or no attribute part.
  * [**ImportError**](https://docs.python.org/3/builtins/exceptions.html#ImportError) – The module part does not import (`ModuleNotFoundError`
        when the module does not exist; a plain `ImportError` if it
        exists but fails while importing).
  * [**AttributeError**](https://docs.python.org/3/builtins/exceptions.html#AttributeError) – The attribute path does not exist on the module.

### Examples

```pycon
>>> import_object('json:dumps')
<function dumps at ...>
>>> import_object('os.path.join')
<function join at ...>
>>> import_object('no-separator')
Traceback (most recent call last):
    ...
ValueError: Invalid object reference 'no-separator'; expected 'module:attr' or 'module.path.attr'.
```

#### SEE ALSO
[`py2mcp.mk_mcp_from_refs()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_from_refs): build a server from such references.

### py2mcp.load_server_config(path)

Load a server config JSON of the form `{"name": str, "refs": [str, ...]}`.

Only the shape is checked here; the refs are resolved later, when the
server is built. An actionable error beats a server that starts with no
tools.

* **Parameters:**
  **path** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)) – The JSON file to read.
* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)
* **Returns:**
  The parsed JSON object, untouched, with at least a `refs` list.
* **Raises:**
  * [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – The file is not valid JSON, or is not a JSON object carrying
        a `refs` list.
  * [**OSError**](https://docs.python.org/3/builtins/exceptions.html#OSError) – The file cannot be read.

### Examples

```pycon
>>> import json, tempfile
>>> from pathlib import Path
>>> path = Path(tempfile.mkdtemp()) / 'py2mcp_config.json'
>>> _ = path.write_text(json.dumps({'name': 'My Tools', 'refs': ['os.path:basename']}))
>>> load_server_config(path)
{'name': 'My Tools', 'refs': ['os.path:basename']}
>>> _ = path.write_text(json.dumps({'name': 'no refs here'}))
>>> load_server_config(path)
Traceback (most recent call last):
    ...
ValueError: py2mcp server config '...py2mcp_config.json' must be a JSON object with a "refs" list, ...
```

#### SEE ALSO
[`resolve_server_config()`](_autosummary/py2mcp.html.md#py2mcp.resolve_server_config): merge the config with command-line refs.

### py2mcp.markdown_install_badge(name, mcp_url, , admin=False)

Markdown link that installs an MCP server as a claude.ai connector.

The dominant use of [`claude_install_link()`](_autosummary/py2mcp.html.md#py2mcp.claude_install_link) is pasting one into a README,
so this saves writing the same link syntax around it. Arguments are those of
[`claude_install_link()`](_autosummary/py2mcp.html.md#py2mcp.claude_install_link).

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

```pycon
>>> markdown_install_badge('snout', 'https://x.io/mcp')
'[Add snout to Claude](https://claude.ai/customize/connectors?modal=add-custom-connector&connectorName=snout&connectorUrl=https%3A%2F%2Fx.io%2Fmcp)'
```

Brackets and backslashes in the name are escaped in the link text, so a name
like `tools [beta]` can’t end the link text early and break the link (the
URL itself is percent-encoded already):

```pycon
>>> print(markdown_install_badge('tools [beta]', 'https://x.io/mcp').split('](')[0])
[Add tools \[beta\] to Claude
```

### py2mcp.mk_auth_provider(auth)

Build a FastMCP **resource-server** auth provider from an auth-config dict.

`auth` is `None`/falsy (no auth) or a dict with a `type` key:

`type='jwt'` (default) — validate JWTs issued by a managed IdP. Keys:

- `jwks_uri` *or* `public_key` — where to get the IdP’s signing key(s).
- `issuer` — the IdP issuer URL (the token’s `iss`).
- `audience` (**required**) — **this** server’s resource id (the token’s
  `aud`). RFC 8707 audience binding is mandatory: it stops a token minted for
  another service being replayed here (the confused-deputy defense), so this
  helper refuses to build a verifier that would skip it.
- `authorization_servers` (or a single `issuer`) — IdP issuer URL(s)
  advertised in the RFC 9728 protected-resource metadata.
- `base_url` — this server’s public base URL.
- `required_scopes` (optional) — scopes every request must carry.

Building the provider performs **no network I/O** (key fetching is lazy, on the
first request), so this is safe to call at scaffold/import time.

* **Parameters:**
  **auth** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – The auth-config dict described above, or `None`/`{}` for no
  authentication.
* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]
* **Returns:**
  A `RemoteAuthProvider` (a resource server), or `None` when `auth`
  is falsy.
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – `auth` is not a dict, its `type` is not supported, or a
      required key (`jwks_uri`/`public_key`, `base_url`,
      `audience`, `authorization_servers`/`issuer`) is missing.

### Examples

```pycon
>>> mk_auth_provider(None) is None
True
>>> provider = mk_auth_provider({
...     'type': 'jwt',
...     'jwks_uri': 'https://idp.example.com/.well-known/jwks.json',
...     'issuer': 'https://idp.example.com',
...     'audience': 'https://conn.example.com/mcp',
...     'base_url': 'https://conn.example.com',
... })
>>> type(provider).__name__
'RemoteAuthProvider'
>>> provider.authorization_servers
['https://idp.example.com']
```

Leaving out the audience is refused rather than silently unchecked:

```pycon
>>> mk_auth_provider({'type': 'jwt', 'jwks_uri': 'https://idp.example.com/jwks',
...                   'base_url': 'https://conn.example.com'})
Traceback (most recent call last):
    ...
ValueError: jwt auth needs 'audience' (this server's resource id). ...
```

#### SEE ALSO
[`mk_http_app()`](_autosummary/py2mcp.html.md#py2mcp.mk_http_app): where the provider is attached to a server.

### py2mcp.mk_http_app(refs, , name='py2mcp Server', auth=None, input_trans=None, transport='streamable-http', path=None, stateless_http=None, middleware=None, instructions=None, prompts=None, resources=None)

Build a Streamable-HTTP **ASGI app** from `refs` (+ optional OAuth).

Returns the ASGI application (a Starlette app), so any ASGI server can run it:

```default
# server/app.py
from py2mcp.http import mk_http_app
app = mk_http_app(['mypkg.tools:summarize'], name='My Connector', auth=AUTH)
# then:  uvicorn server.app:app --host 0.0.0.0 --port 8000
```

Builds the app with **no network I/O**.

* **Parameters:**
  * **refs** ([`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – `'module:function'` references, one per tool.
  * **name** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Server name.
  * **auth** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Resolved by [`mk_auth_provider()`](_autosummary/py2mcp.html.md#py2mcp.mk_auth_provider) (`None` → no auth; a remote
    connector should always set it).
  * **input_trans** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)[[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)], [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]]) – Forwarded to [`py2mcp.mk_mcp_from_refs()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_from_refs).
  * **transport** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The FastMCP HTTP transport; `DFLT_TRANSPORT` is
    Streamable HTTP.
  * **path** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – URL path the MCP endpoint is mounted at; `None` keeps
    FastMCP’s default (`/mcp`).
  * **stateless_http** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`bool`](https://docs.python.org/3/builtins/functions.html#bool)]) – `True` is recommended behind a load balancer (MCP
    sessions are stateful, so default in-memory sessions break across
    replicas — go stateless or externalize session state). `None`
    keeps FastMCP’s default.
  * **middleware** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]) – A single FastMCP middleware or a list, attached for
    cross-cutting concerns — metering, logging, rate limiting. Because
    `auth` runs first, it can read the authenticated caller via
    `fastmcp.server.dependencies.get_access_token()`.
  * **instructions** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – The server’s model-facing description (surfaced to the
    connecting client/model).
  * **prompts** (`Union`[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable), [`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)], [`None`](https://docs.python.org/3/builtins/constants.html#None)]) – Forwarded to [`py2mcp.mk_mcp_from_refs()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_from_refs).
  * **resources** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)]]) – Forwarded to [`py2mcp.mk_mcp_from_refs()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_from_refs).
* **Return type:**
  [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)
* **Returns:**
  The Starlette ASGI application.
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – `auth` is malformed (see [`mk_auth_provider()`](_autosummary/py2mcp.html.md#py2mcp.mk_auth_provider)) or a
      reference cannot be parsed (see [`py2mcp.mk_mcp_from_refs()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_from_refs)).

### Examples

```pycon
>>> app = mk_http_app(['os.path:basename'], name='Paths')
>>> callable(app)
True
>>> [route.path for route in app.routes]
['/mcp']
```

With OAuth and a custom mount path, the RFC 9728 protected-resource
metadata route is added next to the endpoint:

```pycon
>>> AUTH = {
...     'type': 'jwt',
...     'jwks_uri': 'https://idp.example.com/.well-known/jwks.json',
...     'issuer': 'https://idp.example.com',
...     'audience': 'https://conn.example.com/mcp',
...     'base_url': 'https://conn.example.com',
... }
>>> app = mk_http_app(['os.path:basename'], name='Paths', auth=AUTH, path='/api/mcp')
>>> [route.path for route in app.routes]
['/.well-known/oauth-protected-resource/api/mcp', '/api/mcp']
```

#### SEE ALSO
[`serve_http()`](_autosummary/py2mcp.html.md#py2mcp.serve_http): build and run in-process instead of returning the app.
[`py2mcp.serve.serve_stdio()`](_autosummary/py2mcp.serve.html.md#py2mcp.serve.serve_stdio): the local stdio counterpart.

### py2mcp.mk_input_trans(name_func_relationships=None)

Create an input transformation function from name->func mappings.

The returned callable takes a tool call’s keyword arguments as a dict and
returns a new dict in which each named argument has been passed through its
converter; arguments with no converter are copied through unchanged. Pass it
as `input_trans` to [`py2mcp.mk_mcp_server()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_server) and friends.

* **Parameters:**
  **name_func_relationships** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping)]) – Which converter applies to which argument, as
  `{name: func}`, or reversed as `{func: name}` or
  `{func: [name, ...]}` when one converter serves several arguments.
  `None` gives a transformation that only copies the dict.
* **Return type:**
  [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)[[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)], [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]
* **Returns:**
  A function from a kwargs dict to a new kwargs dict.
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – The same argument name is mapped more than once.

### Examples

```pycon
>>> def to_int(x): return int(x)
>>> trans = mk_input_trans({'x': to_int})
>>> trans({'x': '42', 'y': 'hello'})
{'x': 42, 'y': 'hello'}
```

One converter for several arguments, written the reversed way:

```pycon
>>> trans = mk_input_trans({to_int: ['x', 'y']})
>>> trans({'x': '1', 'y': '2', 'z': '3'})
{'x': 1, 'y': 2, 'z': '3'}
```

No mapping means no conversion:

```pycon
>>> mk_input_trans()({'x': '1'})
{'x': '1'}
```

#### SEE ALSO
[`py2mcp.mk_mcp_server()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_server): where the returned callable is applied.

### py2mcp.mk_mcp_from_refs(refs, , name='py2mcp Server', input_trans=None, auth=None, middleware=None, instructions=None, prompts=None, resources=None)

Create an MCP server from `'module:function'` reference strings.

Resolves each reference to a callable via [`py2mcp.util.import_object()`](_autosummary/py2mcp.util.html.md#py2mcp.util.import_object)
and delegates to [`mk_mcp_server()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_server). One call from config strings to a
runnable server — what tools that read tool references from a file (e.g.
`coact`’s `mcp` backend) need.

* **Parameters:**
  * **refs** ([`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – `'module.path:attr'` (or `'module.path.attr'`) strings, one per
    tool. Every module is imported when this is called.
  * **name** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Name of the MCP server.
  * **input_trans** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)[[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)], [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]]) – Forwarded to [`mk_mcp_server()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_server).
  * **auth** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]) – Forwarded to [`mk_mcp_server()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_server); the remote/HTTP path attaches
    its OAuth provider here.
  * **middleware** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]) – Forwarded to [`mk_mcp_server()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_server).
  * **instructions** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Forwarded to [`mk_mcp_server()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_server) as the server’s
    model-facing description.
  * **prompts** (`Union`[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable), [`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)], [`None`](https://docs.python.org/3/builtins/constants.html#None)]) – Forwarded to [`mk_mcp_server()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_server).
  * **resources** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)]]) – Forwarded to [`mk_mcp_server()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_server).
* **Return type:**
  `FastMCP`
* **Returns:**
  A FastMCP server with one tool per reference, each named after the
  resolved function.
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – A reference has no `module` or `attr` part (see
      [`py2mcp.util.import_object()`](_autosummary/py2mcp.util.html.md#py2mcp.util.import_object), whose import errors propagate too).

### Examples

```pycon
>>> mcp = mk_mcp_from_refs(['os.path:basename', 'os.path:dirname'], name='Paths')
>>> mcp.name
'Paths'
```

The tools are the resolved functions:

```pycon
>>> import asyncio
>>> sorted(tool.name for tool in asyncio.run(mcp.list_tools()))
['basename', 'dirname']
>>> asyncio.run(mcp.call_tool('basename', {'p': '/a/b/c.txt'})).content[0].text
'c.txt'
```

#### SEE ALSO
[`py2mcp.serve.serve_stdio()`](_autosummary/py2mcp.serve.html.md#py2mcp.serve.serve_stdio): build from refs and run over stdio.
[`py2mcp.http.mk_http_app()`](_autosummary/py2mcp.http.html.md#py2mcp.http.mk_http_app): build from refs as an ASGI app.

### py2mcp.mk_mcp_from_store(store, , name='item', plural='', server_name=None, middleware=None, instructions=None, prompts=None, resources=None)

Create an MCP server from a MutableMapping with CRUD operations.

Generates four tools over the store, `list_<plural>`, `get_<name>`,
`set_<name>` and `delete_<name>`, so any key-value store (a dict, a
`dol` store, a database wrapper) is one call away from being MCP tools.
The store is used live: a tool call reads or writes the mapping you passed.

* **Parameters:**
  * **store** ([`MutableMapping`](https://docs.python.org/3/library/typing.html#typing.MutableMapping)[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any), [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]) – A MutableMapping to expose via MCP.
  * **name** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Singular name for items (e.g., ‘project’, ‘user’); used in the
    tool names.
  * **plural** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Plural form used by the list tool (defaults to name + ‘s’).
  * **server_name** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Name of the MCP server (defaults to “{name} Store”).
  * **middleware** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]) – Optional FastMCP middleware (a single middleware or an
    iterable), forwarded to [`mk_mcp_server()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_server) — wraps every generated
    CRUD tool call, e.g. to meter or audit store reads and mutations.
  * **instructions** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional natural-language server description, forwarded to
    [`mk_mcp_server()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_server) as the server’s model-facing `instructions`.
  * **prompts** (`Union`[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable), [`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)], [`None`](https://docs.python.org/3/builtins/constants.html#None)]) – Forwarded to [`mk_mcp_server()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_server).
  * **resources** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)]]) – Forwarded to [`mk_mcp_server()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_server).
* **Return type:**
  `FastMCP`
* **Returns:**
  A FastMCP server with the four CRUD tools.

### Examples

```pycon
>>> import asyncio
>>> projects = {'p1': {'name': 'Project 1'}, 'p2': {'name': 'Project 2'}}
>>> mcp = mk_mcp_from_store(projects, name='project')
>>> mcp.name
'project Store'
>>> sorted(tool.name for tool in asyncio.run(mcp.list_tools()))
['delete_project', 'get_project', 'list_projects', 'set_project']
>>> asyncio.run(mcp.call_tool('get_project', {'key': 'p1'})).structured_content
{'result': {'name': 'Project 1'}}
```

An irregular plural and an explicit server name:

```pycon
>>> mcp = mk_mcp_from_store({}, name='entry', plural='entries', server_name='Ledger')
>>> mcp.name
'Ledger'
>>> sorted(tool.name for tool in asyncio.run(mcp.list_tools()))
['delete_entry', 'get_entry', 'list_entries', 'set_entry']
```

#### SEE ALSO
[`py2mcp.util.store_to_funcs()`](_autosummary/py2mcp.util.html.md#py2mcp.util.store_to_funcs): the CRUD functions without a server.
[`mk_mcp_server()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_server): expose your own functions instead.

### py2mcp.mk_mcp_server(funcs, , name='py2mcp Server', input_trans=None, auth=None, middleware=None, instructions=None, prompts=None, resources=None)

Create an MCP server from Python functions.

This is the main entry point for py2mcp. Pass one or more functions,
and get back a FastMCP server ready to run. Each function becomes one
tool, named after the function, with its signature and docstring as the
tool’s schema and description.

* **Parameters:**
  * **funcs** (`Union`[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable), [`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)]]) – A function or iterable of functions to expose as MCP tools.
  * **name** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Name of the MCP server.
  * **input_trans** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)[[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)], [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]]) – Called with the dict of keyword arguments of every tool
    call; the dict it returns is what the function receives. Build one
    with [`py2mcp.mk_input_trans()`](_autosummary/py2mcp.html.md#py2mcp.mk_input_trans). `None` passes arguments
    through untouched.
  * **auth** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]) – Optional `fastmcp.server.auth` provider attached at construction —
    used by the remote (HTTP) path for OAuth 2.1 (see [`py2mcp.http`](_autosummary/py2mcp.http.html.md#module-py2mcp.http)).
    `None` (the default) leaves the server unauthenticated, which is
    correct for the local stdio path.
  * **middleware** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]) – Optional FastMCP middleware (a single middleware or a list),
    attached at construction, for cross-cutting concerns that must wrap
    *every* tool call — usage metering, cost logging, audit, rate limiting.
    Preferred over decorating each tool: you can’t forget to wrap one (a
    missed paid tool means untracked cost). On the remote path `auth`
    runs first, so a middleware can read the authenticated caller via
    `fastmcp.server.dependencies.get_access_token()`.
  * **instructions** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional natural-language description of the server, surfaced
    to the client/model as the server’s `instructions` — a good place to
    explain what the tools do and the intended workflow. `None` (default)
    leaves it unset.
  * **prompts** (`Union`[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable), [`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)], [`None`](https://docs.python.org/3/builtins/constants.html#None)]) – Optional callable or iterable of callables to register as MCP
    prompts (via FastMCP’s `@mcp.prompt`), so a prompts-and-tools server
    can be built declaratively in one call instead of reaching past the
    builder to register prompts by hand on the returned server.
  * **resources** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)]]) – Optional `{uri: callable}` mapping to register as MCP
    resources (via FastMCP’s `@mcp.resource(uri)`) — the callable is
    invoked to produce the resource’s content when a client reads `uri`.
* **Return type:**
  `FastMCP`
* **Returns:**
  A FastMCP server instance ready to run, with one tool per function.

### Examples

```pycon
>>> def add(a: int, b: int) -> int:
...     '''Add two numbers'''
...     return a + b
>>> mcp = mk_mcp_server(add)
>>> mcp.name
'py2mcp Server'
```

Several functions, a server name, and a look at the registered tools:

```pycon
>>> import asyncio
>>> def greet(name: str) -> str:
...     return f"Hello, {name}!"
>>> mcp = mk_mcp_server([add, greet], name="Math & Greetings")
>>> mcp.name
'Math & Greetings'
>>> sorted(tool.name for tool in asyncio.run(mcp.list_tools()))
['add', 'greet']
```

Calling a tool the way an MCP client would:

```pycon
>>> result = asyncio.run(mcp.call_tool('add', {'a': 2, 'b': 3}))
>>> result.structured_content
{'result': 5}
```

Prompts and resources, declared alongside the tools:

```pycon
>>> def summarize_request(topic: str) -> str:
...     return f"Summarize the latest on {topic}."
>>> def schema() -> dict:
...     return {"type": "object"}
>>> mcp = mk_mcp_server(
...     add,
...     prompts=summarize_request,
...     resources={"schema://analysis": schema},
... )
>>> sorted(p.name for p in asyncio.run(mcp.list_prompts()))
['summarize_request']
>>> [str(r.uri) for r in asyncio.run(mcp.list_resources())]
['schema://analysis']
```

#### SEE ALSO
[`mk_mcp_from_refs()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_from_refs): the same from `'module:function'` strings.
[`mk_mcp_from_store()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_from_store): CRUD tools generated from a mapping.
[`py2mcp.mk_input_trans()`](_autosummary/py2mcp.html.md#py2mcp.mk_input_trans): build an `input_trans` from per-argument converters.

### py2mcp.resolve_server_config(, config=None, refs=(), name=None)

Merge a config file and explicit `refs`/`name` into `(refs, name)`.

Refs from `--ref` are appended after any from the config file; an explicit
`name` wins over the config’s. Pure (no I/O beyond reading `config`), so
it is unit-testable without standing up a server.

* **Parameters:**
  * **config** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path), [`None`](https://docs.python.org/3/builtins/constants.html#None)]) – Path of a JSON config as read by [`load_server_config()`](_autosummary/py2mcp.html.md#py2mcp.load_server_config),
    or `None` for no config file.
  * **refs** ([`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Extra `'module:function'` references, appended after the
    config’s.
  * **name** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Server name; overrides the config’s `name`. When neither is
    given, `DFLT_SERVER_NAME`.
* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)], [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]
* **Returns:**
  The merged list of references and the server name.
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – Neither the config nor `refs` supplies any reference, or
      the config file is malformed (see [`load_server_config()`](_autosummary/py2mcp.html.md#py2mcp.load_server_config)).

### Examples

```pycon
>>> resolve_server_config(refs=['os.path:basename'], name='Paths')
(['os.path:basename'], 'Paths')
```

With a config file, its refs come first and its name is the fallback:

```pycon
>>> import json, tempfile
>>> from pathlib import Path
>>> path = Path(tempfile.mkdtemp()) / 'py2mcp_config.json'
>>> _ = path.write_text(json.dumps({'name': 'My Tools', 'refs': ['os.path:basename']}))
>>> resolve_server_config(config=path, refs=['os.path:dirname'])
(['os.path:basename', 'os.path:dirname'], 'My Tools')
>>> resolve_server_config(config=path, name='Override')
(['os.path:basename'], 'Override')
```

### py2mcp.serve_http(refs, , name='py2mcp Server', host='127.0.0.1', port=8000, auth=None, input_trans=None, transport='streamable-http', stateless_http=None, middleware=None, instructions=None, prompts=None, resources=None)

Build and **run** a Streamable-HTTP MCP server (blocking) via FastMCP/uvicorn.

For a self-hosted process. Binds `127.0.0.1` by default — expose a public
interface only behind a TLS-terminating reverse proxy (a remote connector must
be reachable over public **HTTPS**, and binding locally is the spec’s
DNS-rebinding-safe default). `auth` is resolved by [`mk_auth_provider()`](_autosummary/py2mcp.html.md#py2mcp.mk_auth_provider);
`middleware` (a single FastMCP middleware or a list) is attached as in
[`mk_http_app()`](_autosummary/py2mcp.html.md#py2mcp.mk_http_app); `instructions` sets the server’s model-facing description.
`prompts`/`resources` are forwarded to [`py2mcp.mk_mcp_from_refs()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_from_refs).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### py2mcp.serve_stdio(refs, , name='py2mcp Server', input_trans=None, middleware=None, instructions=None, prompts=None, resources=None)

Build an MCP server from `'module:function'` refs and run it over stdio.

Blocks, serving the MCP protocol on stdin/stdout until the host disconnects.
Thin wrapper over [`py2mcp.mk_mcp_from_refs()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_from_refs) + `FastMCP.run` so that
packaged integrations have one command to launch. No example here: the call
does not return while the server runs.

* **Parameters:**
  * **refs** ([`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – `'module:function'` references, one per tool.
  * **name** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Server name.
  * **input_trans** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)[[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)], [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]]) – Forwarded to [`py2mcp.mk_mcp_from_refs()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_from_refs).
  * **middleware** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]) – A single FastMCP middleware or a list, forwarded for
    cross-cutting concerns; logging/metering is as useful on the local
    stdio path as on the remote one.
  * **instructions** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – The server’s model-facing description.
  * **prompts** (`Union`[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable), [`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)], [`None`](https://docs.python.org/3/builtins/constants.html#None)]) – Forwarded to [`py2mcp.mk_mcp_from_refs()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_from_refs).
  * **resources** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)]]) – Forwarded to [`py2mcp.mk_mcp_from_refs()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_from_refs).

#### SEE ALSO
[`py2mcp.http.serve_http()`](_autosummary/py2mcp.http.html.md#py2mcp.http.serve_http): the same over Streamable HTTP.
[`main()`](_autosummary/py2mcp.main.html.md#module-py2mcp.main): the command line that calls this.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### Modules

| [`base`](_autosummary/py2mcp.base.html.md#module-py2mcp.base)   | Private helpers shared by the builders in [`py2mcp.main`](_autosummary/py2mcp.main.html.md#module-py2mcp.main).   |
|----------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------|
| [`http`](_autosummary/py2mcp.http.html.md#module-py2mcp.http)   | Serve a py2mcp `FastMCP` server over Streamable HTTP with optional OAuth 2.1.                                                |
| [`main`](_autosummary/py2mcp.main.html.md#module-py2mcp.main)   | Build a `FastMCP` server from Python functions, reference strings, or a store.                                               |
| [`serve`](_autosummary/py2mcp.serve.html.md#module-py2mcp.serve) | Serve a py2mcp `FastMCP` server over stdio, as a packaged integration launches it.                                           |
| [`trans`](_autosummary/py2mcp.trans.html.md#module-py2mcp.trans) | Input transformation for py2mcp tools: convert arguments before a function runs.                                             |
| [`util`](_autosummary/py2mcp.util.html.md#module-py2mcp.util)   | Resolve object references and turn a mapping into CRUD functions.                                                            |


# _autosummary/py2mcp.http.html.md

# py2mcp.http

Serve a py2mcp `FastMCP` server over Streamable HTTP with optional OAuth 2.1.

The *remote* counterpart to [`py2mcp.serve`](_autosummary/py2mcp.serve.html.md#module-py2mcp.serve) (stdio). A *remote* MCP server (e.g. a claude.ai custom connector) is reached over public
HTTPS from the vendor’s cloud and authenticates with **OAuth 2.1**. Per the MCP
authorization spec the MCP server is an OAuth 2.1 **resource server** — it
*validates* bearer tokens minted by a **managed identity provider** (the
authorization server) and **never issues tokens itself**. Two hard rules fall out
of that and are enforced here by construction:

- **Audience binding (RFC 8707).** The verifier checks the token’s `aud` equals
  *this* server’s resource id, so a token minted for another service cannot be
  replayed here (the confused-deputy defense).
- **No token passthrough.** This layer only *verifies* the inbound token; it never
  forwards it upstream. Any upstream call your tools make must use their own
  credentials.

It wraps FastMCP’s native machinery (no transport/OAuth code is reinvented):

- [`mk_auth_provider()`](_autosummary/py2mcp.http.html.md#py2mcp.http.mk_auth_provider) — an auth-config dict → a
  `fastmcp.server.auth.RemoteAuthProvider` (a `JWTVerifier` resource server
  that validates the IdP’s JWTs and publishes the RFC 9728
  `/.well-known/oauth-protected-resource` document pointing at the IdP).
- [`mk_http_app()`](_autosummary/py2mcp.http.html.md#py2mcp.http.mk_http_app) — build the server (via [`py2mcp.mk_mcp_from_refs()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_from_refs)),
  attach the auth provider, and return a Streamable-HTTP **ASGI app** to run under
  any ASGI server (uvicorn, gunicorn, a serverless adapter).
- [`serve_http()`](_autosummary/py2mcp.http.html.md#py2mcp.http.serve_http) — build and *run* it (blocking), for a self-hosted process.

`coact`’s `claude-remote-connector` publish target scaffolds a deployable
service around these — coact writes packaging, py2mcp builds and serves the MCP
server (the same division of labour as the stdio `.mcpb` path).

Building the app performs no network I/O, so it is safe to do at import time:

```pycon
>>> from py2mcp.http import mk_http_app
>>> app = mk_http_app(['os.path:basename'], name='Paths')
>>> [route.path for route in app.routes]
['/mcp']
```

### Module Attributes

| [`SUPPORTED_AUTH_TYPES`](_autosummary/py2mcp.http.html.md#py2mcp.http.SUPPORTED_AUTH_TYPES)   | Auth `type` values [`mk_auth_provider()`](_autosummary/py2mcp.http.html.md#py2mcp.http.mk_auth_provider) understands.   |
|-------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------|
| [`DFLT_TRANSPORT`](_autosummary/py2mcp.http.html.md#py2mcp.http.DFLT_TRANSPORT)         | Default Streamable-HTTP transport (the current remote MCP transport).                                 |

### Functions

| [`mk_auth_provider`](_autosummary/py2mcp.http.html.md#py2mcp.http.mk_auth_provider)(auth)                        | Build a FastMCP **resource-server** auth provider from an auth-config dict.    |
|------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------|
| [`mk_http_app`](_autosummary/py2mcp.http.html.md#py2mcp.http.mk_http_app)(refs, \*[, name, auth, ...])      | Build a Streamable-HTTP **ASGI app** from `refs` (+ optional OAuth).           |
| [`serve_http`](_autosummary/py2mcp.http.html.md#py2mcp.http.serve_http)(refs, \*[, name, host, port, ...]) | Build and **run** a Streamable-HTTP MCP server (blocking) via FastMCP/uvicorn. |

### py2mcp.http.DFLT_TRANSPORT *= 'streamable-http'*

Default Streamable-HTTP transport (the current remote MCP transport).

### py2mcp.http.SUPPORTED_AUTH_TYPES *= ('jwt',)*

Auth `type` values [`mk_auth_provider()`](_autosummary/py2mcp.http.html.md#py2mcp.http.mk_auth_provider) understands. `'jwt'` is the
vendor-neutral resource-server pattern (validate a managed IdP’s JWTs);
managed-provider shortcuts (auth0/workos/…) can be added as new types.

### py2mcp.http.mk_auth_provider(auth)

Build a FastMCP **resource-server** auth provider from an auth-config dict.

`auth` is `None`/falsy (no auth) or a dict with a `type` key:

`type='jwt'` (default) — validate JWTs issued by a managed IdP. Keys:

- `jwks_uri` *or* `public_key` — where to get the IdP’s signing key(s).
- `issuer` — the IdP issuer URL (the token’s `iss`).
- `audience` (**required**) — **this** server’s resource id (the token’s
  `aud`). RFC 8707 audience binding is mandatory: it stops a token minted for
  another service being replayed here (the confused-deputy defense), so this
  helper refuses to build a verifier that would skip it.
- `authorization_servers` (or a single `issuer`) — IdP issuer URL(s)
  advertised in the RFC 9728 protected-resource metadata.
- `base_url` — this server’s public base URL.
- `required_scopes` (optional) — scopes every request must carry.

Building the provider performs **no network I/O** (key fetching is lazy, on the
first request), so this is safe to call at scaffold/import time.

* **Parameters:**
  **auth** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – The auth-config dict described above, or `None`/`{}` for no
  authentication.
* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]
* **Returns:**
  A `RemoteAuthProvider` (a resource server), or `None` when `auth`
  is falsy.
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – `auth` is not a dict, its `type` is not supported, or a
      required key (`jwks_uri`/`public_key`, `base_url`,
      `audience`, `authorization_servers`/`issuer`) is missing.

### Examples

```pycon
>>> mk_auth_provider(None) is None
True
>>> provider = mk_auth_provider({
...     'type': 'jwt',
...     'jwks_uri': 'https://idp.example.com/.well-known/jwks.json',
...     'issuer': 'https://idp.example.com',
...     'audience': 'https://conn.example.com/mcp',
...     'base_url': 'https://conn.example.com',
... })
>>> type(provider).__name__
'RemoteAuthProvider'
>>> provider.authorization_servers
['https://idp.example.com']
```

Leaving out the audience is refused rather than silently unchecked:

```pycon
>>> mk_auth_provider({'type': 'jwt', 'jwks_uri': 'https://idp.example.com/jwks',
...                   'base_url': 'https://conn.example.com'})
Traceback (most recent call last):
    ...
ValueError: jwt auth needs 'audience' (this server's resource id). ...
```

#### SEE ALSO
[`mk_http_app()`](_autosummary/py2mcp.http.html.md#py2mcp.http.mk_http_app): where the provider is attached to a server.

### py2mcp.http.mk_http_app(refs, , name='py2mcp Server', auth=None, input_trans=None, transport='streamable-http', path=None, stateless_http=None, middleware=None, instructions=None, prompts=None, resources=None)

Build a Streamable-HTTP **ASGI app** from `refs` (+ optional OAuth).

Returns the ASGI application (a Starlette app), so any ASGI server can run it:

```default
# server/app.py
from py2mcp.http import mk_http_app
app = mk_http_app(['mypkg.tools:summarize'], name='My Connector', auth=AUTH)
# then:  uvicorn server.app:app --host 0.0.0.0 --port 8000
```

Builds the app with **no network I/O**.

* **Parameters:**
  * **refs** ([`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – `'module:function'` references, one per tool.
  * **name** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Server name.
  * **auth** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Resolved by [`mk_auth_provider()`](_autosummary/py2mcp.http.html.md#py2mcp.http.mk_auth_provider) (`None` → no auth; a remote
    connector should always set it).
  * **input_trans** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)[[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)], [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]]) – Forwarded to [`py2mcp.mk_mcp_from_refs()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_from_refs).
  * **transport** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The FastMCP HTTP transport; `DFLT_TRANSPORT` is
    Streamable HTTP.
  * **path** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – URL path the MCP endpoint is mounted at; `None` keeps
    FastMCP’s default (`/mcp`).
  * **stateless_http** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`bool`](https://docs.python.org/3/builtins/functions.html#bool)]) – `True` is recommended behind a load balancer (MCP
    sessions are stateful, so default in-memory sessions break across
    replicas — go stateless or externalize session state). `None`
    keeps FastMCP’s default.
  * **middleware** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]) – A single FastMCP middleware or a list, attached for
    cross-cutting concerns — metering, logging, rate limiting. Because
    `auth` runs first, it can read the authenticated caller via
    `fastmcp.server.dependencies.get_access_token()`.
  * **instructions** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – The server’s model-facing description (surfaced to the
    connecting client/model).
  * **prompts** (`Union`[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable), [`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)], [`None`](https://docs.python.org/3/builtins/constants.html#None)]) – Forwarded to [`py2mcp.mk_mcp_from_refs()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_from_refs).
  * **resources** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)]]) – Forwarded to [`py2mcp.mk_mcp_from_refs()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_from_refs).
* **Return type:**
  [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)
* **Returns:**
  The Starlette ASGI application.
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – `auth` is malformed (see [`mk_auth_provider()`](_autosummary/py2mcp.http.html.md#py2mcp.http.mk_auth_provider)) or a
      reference cannot be parsed (see [`py2mcp.mk_mcp_from_refs()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_from_refs)).

### Examples

```pycon
>>> app = mk_http_app(['os.path:basename'], name='Paths')
>>> callable(app)
True
>>> [route.path for route in app.routes]
['/mcp']
```

With OAuth and a custom mount path, the RFC 9728 protected-resource
metadata route is added next to the endpoint:

```pycon
>>> AUTH = {
...     'type': 'jwt',
...     'jwks_uri': 'https://idp.example.com/.well-known/jwks.json',
...     'issuer': 'https://idp.example.com',
...     'audience': 'https://conn.example.com/mcp',
...     'base_url': 'https://conn.example.com',
... }
>>> app = mk_http_app(['os.path:basename'], name='Paths', auth=AUTH, path='/api/mcp')
>>> [route.path for route in app.routes]
['/.well-known/oauth-protected-resource/api/mcp', '/api/mcp']
```

#### SEE ALSO
[`serve_http()`](_autosummary/py2mcp.http.html.md#py2mcp.http.serve_http): build and run in-process instead of returning the app.
[`py2mcp.serve.serve_stdio()`](_autosummary/py2mcp.serve.html.md#py2mcp.serve.serve_stdio): the local stdio counterpart.

### py2mcp.http.serve_http(refs, , name='py2mcp Server', host='127.0.0.1', port=8000, auth=None, input_trans=None, transport='streamable-http', stateless_http=None, middleware=None, instructions=None, prompts=None, resources=None)

Build and **run** a Streamable-HTTP MCP server (blocking) via FastMCP/uvicorn.

For a self-hosted process. Binds `127.0.0.1` by default — expose a public
interface only behind a TLS-terminating reverse proxy (a remote connector must
be reachable over public **HTTPS**, and binding locally is the spec’s
DNS-rebinding-safe default). `auth` is resolved by [`mk_auth_provider()`](_autosummary/py2mcp.http.html.md#py2mcp.http.mk_auth_provider);
`middleware` (a single FastMCP middleware or a list) is attached as in
[`mk_http_app()`](_autosummary/py2mcp.http.html.md#py2mcp.http.mk_http_app); `instructions` sets the server’s model-facing description.
`prompts`/`resources` are forwarded to [`py2mcp.mk_mcp_from_refs()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_from_refs).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)


# _autosummary/py2mcp.main.html.md

# py2mcp.main

Build a `FastMCP` server from Python functions, reference strings, or a store.

Each builder returns a `FastMCP` server *object* with one tool per function
and does not run it; [`py2mcp.serve`](_autosummary/py2mcp.serve.html.md#module-py2mcp.serve) (stdio) and [`py2mcp.http`](_autosummary/py2mcp.http.html.md#module-py2mcp.http)
(Streamable HTTP) do the running. All three accept `middleware`,
`instructions`, `prompts` and `resources` and attach them at
construction; `mk_mcp_server` and `mk_mcp_from_refs` take the server
`name` directly, while `mk_mcp_from_store` takes the singular item noun
instead and derives the server name from it (`server_name` overrides).

Main entry points:

- `mk_mcp_server`: register callables as tools
- `mk_mcp_from_refs`: resolve `'module:function'` strings, then the same
- `mk_mcp_from_store`: generate list/get/set/delete tools over a `MutableMapping`

```pycon
>>> from py2mcp.main import mk_mcp_from_refs
>>> mk_mcp_from_refs(['os.path:basename'], name='Paths').name
'Paths'
```

### Functions

| [`mk_mcp_from_refs`](_autosummary/py2mcp.main.html.md#py2mcp.main.mk_mcp_from_refs)(refs, \*[, name, ...])            | Create an MCP server from `'module:function'` reference strings.   |
|-----------------------------------------------------------------------------------------------------|--------------------------------------------------------------------|
| [`mk_mcp_from_store`](_autosummary/py2mcp.main.html.md#py2mcp.main.mk_mcp_from_store)(store, \*[, name, plural, ...])  | Create an MCP server from a MutableMapping with CRUD operations.   |
| [`mk_mcp_server`](_autosummary/py2mcp.main.html.md#py2mcp.main.mk_mcp_server)(funcs, \*[, name, input_trans, ...]) | Create an MCP server from Python functions.                        |

### py2mcp.main.mk_mcp_from_refs(refs, , name='py2mcp Server', input_trans=None, auth=None, middleware=None, instructions=None, prompts=None, resources=None)

Create an MCP server from `'module:function'` reference strings.

Resolves each reference to a callable via [`py2mcp.util.import_object()`](_autosummary/py2mcp.util.html.md#py2mcp.util.import_object)
and delegates to [`mk_mcp_server()`](_autosummary/py2mcp.main.html.md#py2mcp.main.mk_mcp_server). One call from config strings to a
runnable server — what tools that read tool references from a file (e.g.
`coact`’s `mcp` backend) need.

* **Parameters:**
  * **refs** ([`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – `'module.path:attr'` (or `'module.path.attr'`) strings, one per
    tool. Every module is imported when this is called.
  * **name** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Name of the MCP server.
  * **input_trans** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)[[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)], [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]]) – Forwarded to [`mk_mcp_server()`](_autosummary/py2mcp.main.html.md#py2mcp.main.mk_mcp_server).
  * **auth** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]) – Forwarded to [`mk_mcp_server()`](_autosummary/py2mcp.main.html.md#py2mcp.main.mk_mcp_server); the remote/HTTP path attaches
    its OAuth provider here.
  * **middleware** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]) – Forwarded to [`mk_mcp_server()`](_autosummary/py2mcp.main.html.md#py2mcp.main.mk_mcp_server).
  * **instructions** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Forwarded to [`mk_mcp_server()`](_autosummary/py2mcp.main.html.md#py2mcp.main.mk_mcp_server) as the server’s
    model-facing description.
  * **prompts** (`Union`[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable), [`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)], [`None`](https://docs.python.org/3/builtins/constants.html#None)]) – Forwarded to [`mk_mcp_server()`](_autosummary/py2mcp.main.html.md#py2mcp.main.mk_mcp_server).
  * **resources** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)]]) – Forwarded to [`mk_mcp_server()`](_autosummary/py2mcp.main.html.md#py2mcp.main.mk_mcp_server).
* **Return type:**
  `FastMCP`
* **Returns:**
  A FastMCP server with one tool per reference, each named after the
  resolved function.
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – A reference has no `module` or `attr` part (see
      [`py2mcp.util.import_object()`](_autosummary/py2mcp.util.html.md#py2mcp.util.import_object), whose import errors propagate too).

### Examples

```pycon
>>> mcp = mk_mcp_from_refs(['os.path:basename', 'os.path:dirname'], name='Paths')
>>> mcp.name
'Paths'
```

The tools are the resolved functions:

```pycon
>>> import asyncio
>>> sorted(tool.name for tool in asyncio.run(mcp.list_tools()))
['basename', 'dirname']
>>> asyncio.run(mcp.call_tool('basename', {'p': '/a/b/c.txt'})).content[0].text
'c.txt'
```

#### SEE ALSO
[`py2mcp.serve.serve_stdio()`](_autosummary/py2mcp.serve.html.md#py2mcp.serve.serve_stdio): build from refs and run over stdio.
[`py2mcp.http.mk_http_app()`](_autosummary/py2mcp.http.html.md#py2mcp.http.mk_http_app): build from refs as an ASGI app.

### py2mcp.main.mk_mcp_from_store(store, , name='item', plural='', server_name=None, middleware=None, instructions=None, prompts=None, resources=None)

Create an MCP server from a MutableMapping with CRUD operations.

Generates four tools over the store, `list_<plural>`, `get_<name>`,
`set_<name>` and `delete_<name>`, so any key-value store (a dict, a
`dol` store, a database wrapper) is one call away from being MCP tools.
The store is used live: a tool call reads or writes the mapping you passed.

* **Parameters:**
  * **store** ([`MutableMapping`](https://docs.python.org/3/library/typing.html#typing.MutableMapping)[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any), [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]) – A MutableMapping to expose via MCP.
  * **name** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Singular name for items (e.g., ‘project’, ‘user’); used in the
    tool names.
  * **plural** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Plural form used by the list tool (defaults to name + ‘s’).
  * **server_name** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Name of the MCP server (defaults to “{name} Store”).
  * **middleware** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]) – Optional FastMCP middleware (a single middleware or an
    iterable), forwarded to [`mk_mcp_server()`](_autosummary/py2mcp.main.html.md#py2mcp.main.mk_mcp_server) — wraps every generated
    CRUD tool call, e.g. to meter or audit store reads and mutations.
  * **instructions** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional natural-language server description, forwarded to
    [`mk_mcp_server()`](_autosummary/py2mcp.main.html.md#py2mcp.main.mk_mcp_server) as the server’s model-facing `instructions`.
  * **prompts** (`Union`[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable), [`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)], [`None`](https://docs.python.org/3/builtins/constants.html#None)]) – Forwarded to [`mk_mcp_server()`](_autosummary/py2mcp.main.html.md#py2mcp.main.mk_mcp_server).
  * **resources** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)]]) – Forwarded to [`mk_mcp_server()`](_autosummary/py2mcp.main.html.md#py2mcp.main.mk_mcp_server).
* **Return type:**
  `FastMCP`
* **Returns:**
  A FastMCP server with the four CRUD tools.

### Examples

```pycon
>>> import asyncio
>>> projects = {'p1': {'name': 'Project 1'}, 'p2': {'name': 'Project 2'}}
>>> mcp = mk_mcp_from_store(projects, name='project')
>>> mcp.name
'project Store'
>>> sorted(tool.name for tool in asyncio.run(mcp.list_tools()))
['delete_project', 'get_project', 'list_projects', 'set_project']
>>> asyncio.run(mcp.call_tool('get_project', {'key': 'p1'})).structured_content
{'result': {'name': 'Project 1'}}
```

An irregular plural and an explicit server name:

```pycon
>>> mcp = mk_mcp_from_store({}, name='entry', plural='entries', server_name='Ledger')
>>> mcp.name
'Ledger'
>>> sorted(tool.name for tool in asyncio.run(mcp.list_tools()))
['delete_entry', 'get_entry', 'list_entries', 'set_entry']
```

#### SEE ALSO
[`py2mcp.util.store_to_funcs()`](_autosummary/py2mcp.util.html.md#py2mcp.util.store_to_funcs): the CRUD functions without a server.
[`mk_mcp_server()`](_autosummary/py2mcp.main.html.md#py2mcp.main.mk_mcp_server): expose your own functions instead.

### py2mcp.main.mk_mcp_server(funcs, , name='py2mcp Server', input_trans=None, auth=None, middleware=None, instructions=None, prompts=None, resources=None)

Create an MCP server from Python functions.

This is the main entry point for py2mcp. Pass one or more functions,
and get back a FastMCP server ready to run. Each function becomes one
tool, named after the function, with its signature and docstring as the
tool’s schema and description.

* **Parameters:**
  * **funcs** (`Union`[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable), [`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)]]) – A function or iterable of functions to expose as MCP tools.
  * **name** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Name of the MCP server.
  * **input_trans** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)[[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)], [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]]) – Called with the dict of keyword arguments of every tool
    call; the dict it returns is what the function receives. Build one
    with [`py2mcp.mk_input_trans()`](_autosummary/py2mcp.html.md#py2mcp.mk_input_trans). `None` passes arguments
    through untouched.
  * **auth** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]) – Optional `fastmcp.server.auth` provider attached at construction —
    used by the remote (HTTP) path for OAuth 2.1 (see [`py2mcp.http`](_autosummary/py2mcp.http.html.md#module-py2mcp.http)).
    `None` (the default) leaves the server unauthenticated, which is
    correct for the local stdio path.
  * **middleware** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]) – Optional FastMCP middleware (a single middleware or a list),
    attached at construction, for cross-cutting concerns that must wrap
    *every* tool call — usage metering, cost logging, audit, rate limiting.
    Preferred over decorating each tool: you can’t forget to wrap one (a
    missed paid tool means untracked cost). On the remote path `auth`
    runs first, so a middleware can read the authenticated caller via
    `fastmcp.server.dependencies.get_access_token()`.
  * **instructions** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional natural-language description of the server, surfaced
    to the client/model as the server’s `instructions` — a good place to
    explain what the tools do and the intended workflow. `None` (default)
    leaves it unset.
  * **prompts** (`Union`[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable), [`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)], [`None`](https://docs.python.org/3/builtins/constants.html#None)]) – Optional callable or iterable of callables to register as MCP
    prompts (via FastMCP’s `@mcp.prompt`), so a prompts-and-tools server
    can be built declaratively in one call instead of reaching past the
    builder to register prompts by hand on the returned server.
  * **resources** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)]]) – Optional `{uri: callable}` mapping to register as MCP
    resources (via FastMCP’s `@mcp.resource(uri)`) — the callable is
    invoked to produce the resource’s content when a client reads `uri`.
* **Return type:**
  `FastMCP`
* **Returns:**
  A FastMCP server instance ready to run, with one tool per function.

### Examples

```pycon
>>> def add(a: int, b: int) -> int:
...     '''Add two numbers'''
...     return a + b
>>> mcp = mk_mcp_server(add)
>>> mcp.name
'py2mcp Server'
```

Several functions, a server name, and a look at the registered tools:

```pycon
>>> import asyncio
>>> def greet(name: str) -> str:
...     return f"Hello, {name}!"
>>> mcp = mk_mcp_server([add, greet], name="Math & Greetings")
>>> mcp.name
'Math & Greetings'
>>> sorted(tool.name for tool in asyncio.run(mcp.list_tools()))
['add', 'greet']
```

Calling a tool the way an MCP client would:

```pycon
>>> result = asyncio.run(mcp.call_tool('add', {'a': 2, 'b': 3}))
>>> result.structured_content
{'result': 5}
```

Prompts and resources, declared alongside the tools:

```pycon
>>> def summarize_request(topic: str) -> str:
...     return f"Summarize the latest on {topic}."
>>> def schema() -> dict:
...     return {"type": "object"}
>>> mcp = mk_mcp_server(
...     add,
...     prompts=summarize_request,
...     resources={"schema://analysis": schema},
... )
>>> sorted(p.name for p in asyncio.run(mcp.list_prompts()))
['summarize_request']
>>> [str(r.uri) for r in asyncio.run(mcp.list_resources())]
['schema://analysis']
```

#### SEE ALSO
[`mk_mcp_from_refs()`](_autosummary/py2mcp.main.html.md#py2mcp.main.mk_mcp_from_refs): the same from `'module:function'` strings.
[`mk_mcp_from_store()`](_autosummary/py2mcp.main.html.md#py2mcp.main.mk_mcp_from_store): CRUD tools generated from a mapping.
[`py2mcp.mk_input_trans()`](_autosummary/py2mcp.html.md#py2mcp.mk_input_trans): build an `input_trans` from per-argument converters.


# _autosummary/py2mcp.serve.html.md

# py2mcp.serve

Serve a py2mcp `FastMCP` server over stdio, as a packaged integration launches it.

[`py2mcp.mk_mcp_server()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_server) / [`py2mcp.mk_mcp_from_refs()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_from_refs) build a server
*object* but deliberately leave *running* it to the caller (the only run hint in
`main` is a comment). This module adds the thin “actually serve it over stdio”
layer plus a small JSON-config loader, so a one-click bundle (e.g. a Claude
Desktop `.mcpb` Desktop Extension) can point its `manifest.json` at:

```default
python -m py2mcp --config ${__dirname}/server/py2mcp_config.json
```

and get a live MCP server. The config is just `{"name": ..., "refs": [...]}`
where each ref is a `'module:function'` string resolved by
[`py2mcp.util.import_object()`](_autosummary/py2mcp.util.html.md#py2mcp.util.import_object).

stdio note: an MCP stdio server speaks newline-delimited JSON-RPC on
stdout, so nothing else may be written there. `FastMCP`’s `run` handles this;
keep application logging on stderr.

Main entry points:

- `serve_stdio`: build from refs and run over stdio (blocking)
- `resolve_server_config`: merge a config file with explicit refs and name
- `load_server_config`: read and check the JSON config
- `main`: the `python -m py2mcp` command line

```pycon
>>> from py2mcp.serve import resolve_server_config
>>> resolve_server_config(refs=['os.path:basename'])
(['os.path:basename'], 'py2mcp Server')
```

### Module Attributes

| [`DFLT_SERVER_NAME`](_autosummary/py2mcp.serve.html.md#py2mcp.serve.DFLT_SERVER_NAME)   | Default server name when neither a config nor `--name` supplies one.   |
|---------------------------------------------------------------------|------------------------------------------------------------------------|

### Functions

| [`load_server_config`](_autosummary/py2mcp.serve.html.md#py2mcp.serve.load_server_config)(path)                        | Load a server config JSON of the form `{"name": str, "refs": [str, ...]}`.   |
|--------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------|
| [`main`](_autosummary/py2mcp.serve.html.md#py2mcp.serve.main)([argv])                                    | CLI: `python -m py2mcp --config cfg.json` (or `--ref mod:func ...`).         |
| [`resolve_server_config`](_autosummary/py2mcp.serve.html.md#py2mcp.serve.resolve_server_config)(\*[, config, refs, name]) | Merge a config file and explicit `refs`/`name` into `(refs, name)`.          |
| [`serve_stdio`](_autosummary/py2mcp.serve.html.md#py2mcp.serve.serve_stdio)(refs, \*[, name, input_trans, ...]) | Build an MCP server from `'module:function'` refs and run it over stdio.     |

### py2mcp.serve.DFLT_SERVER_NAME *= 'py2mcp Server'*

Default server name when neither a config nor `--name` supplies one.

### py2mcp.serve.load_server_config(path)

Load a server config JSON of the form `{"name": str, "refs": [str, ...]}`.

Only the shape is checked here; the refs are resolved later, when the
server is built. An actionable error beats a server that starts with no
tools.

* **Parameters:**
  **path** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)) – The JSON file to read.
* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)
* **Returns:**
  The parsed JSON object, untouched, with at least a `refs` list.
* **Raises:**
  * [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – The file is not valid JSON, or is not a JSON object carrying
        a `refs` list.
  * [**OSError**](https://docs.python.org/3/builtins/exceptions.html#OSError) – The file cannot be read.

### Examples

```pycon
>>> import json, tempfile
>>> from pathlib import Path
>>> path = Path(tempfile.mkdtemp()) / 'py2mcp_config.json'
>>> _ = path.write_text(json.dumps({'name': 'My Tools', 'refs': ['os.path:basename']}))
>>> load_server_config(path)
{'name': 'My Tools', 'refs': ['os.path:basename']}
>>> _ = path.write_text(json.dumps({'name': 'no refs here'}))
>>> load_server_config(path)
Traceback (most recent call last):
    ...
ValueError: py2mcp server config '...py2mcp_config.json' must be a JSON object with a "refs" list, ...
```

#### SEE ALSO
[`resolve_server_config()`](_autosummary/py2mcp.serve.html.md#py2mcp.serve.resolve_server_config): merge the config with command-line refs.

### py2mcp.serve.main(argv=None)

CLI: `python -m py2mcp --config cfg.json` (or `--ref mod:func ...`).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### py2mcp.serve.resolve_server_config(, config=None, refs=(), name=None)

Merge a config file and explicit `refs`/`name` into `(refs, name)`.

Refs from `--ref` are appended after any from the config file; an explicit
`name` wins over the config’s. Pure (no I/O beyond reading `config`), so
it is unit-testable without standing up a server.

* **Parameters:**
  * **config** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path), [`None`](https://docs.python.org/3/builtins/constants.html#None)]) – Path of a JSON config as read by [`load_server_config()`](_autosummary/py2mcp.serve.html.md#py2mcp.serve.load_server_config),
    or `None` for no config file.
  * **refs** ([`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Extra `'module:function'` references, appended after the
    config’s.
  * **name** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Server name; overrides the config’s `name`. When neither is
    given, `DFLT_SERVER_NAME`.
* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)], [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]
* **Returns:**
  The merged list of references and the server name.
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – Neither the config nor `refs` supplies any reference, or
      the config file is malformed (see [`load_server_config()`](_autosummary/py2mcp.serve.html.md#py2mcp.serve.load_server_config)).

### Examples

```pycon
>>> resolve_server_config(refs=['os.path:basename'], name='Paths')
(['os.path:basename'], 'Paths')
```

With a config file, its refs come first and its name is the fallback:

```pycon
>>> import json, tempfile
>>> from pathlib import Path
>>> path = Path(tempfile.mkdtemp()) / 'py2mcp_config.json'
>>> _ = path.write_text(json.dumps({'name': 'My Tools', 'refs': ['os.path:basename']}))
>>> resolve_server_config(config=path, refs=['os.path:dirname'])
(['os.path:basename', 'os.path:dirname'], 'My Tools')
>>> resolve_server_config(config=path, name='Override')
(['os.path:basename'], 'Override')
```

### py2mcp.serve.serve_stdio(refs, , name='py2mcp Server', input_trans=None, middleware=None, instructions=None, prompts=None, resources=None)

Build an MCP server from `'module:function'` refs and run it over stdio.

Blocks, serving the MCP protocol on stdin/stdout until the host disconnects.
Thin wrapper over [`py2mcp.mk_mcp_from_refs()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_from_refs) + `FastMCP.run` so that
packaged integrations have one command to launch. No example here: the call
does not return while the server runs.

* **Parameters:**
  * **refs** ([`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – `'module:function'` references, one per tool.
  * **name** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Server name.
  * **input_trans** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)[[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)], [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]]) – Forwarded to [`py2mcp.mk_mcp_from_refs()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_from_refs).
  * **middleware** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]) – A single FastMCP middleware or a list, forwarded for
    cross-cutting concerns; logging/metering is as useful on the local
    stdio path as on the remote one.
  * **instructions** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – The server’s model-facing description.
  * **prompts** (`Union`[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable), [`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)], [`None`](https://docs.python.org/3/builtins/constants.html#None)]) – Forwarded to [`py2mcp.mk_mcp_from_refs()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_from_refs).
  * **resources** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)]]) – Forwarded to [`py2mcp.mk_mcp_from_refs()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_from_refs).

#### SEE ALSO
[`py2mcp.http.serve_http()`](_autosummary/py2mcp.http.html.md#py2mcp.http.serve_http): the same over Streamable HTTP.
[`main()`](_autosummary/py2mcp.serve.html.md#py2mcp.serve.main): the command line that calls this.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)


# _autosummary/py2mcp.trans.html.md

# py2mcp.trans

Input transformation for py2mcp tools: convert arguments before a function runs.

MCP clients send JSON, so a tool that wants a `numpy` array, a `Path` or a
parsed date needs its inputs converted first. `mk_input_trans` builds the
`input_trans` callable that [`py2mcp.mk_mcp_server()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_server),
[`py2mcp.mk_mcp_from_refs()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_from_refs), and the HTTP/stdio builders that wrap them
apply to every tool call, from a mapping of argument names to converter
functions. (`mk_mcp_from_store`’s generated CRUD tools take no
`input_trans`.)

Main entry points:

- `mk_input_trans`: name-to-converter mapping in, `input_trans` callable out

```pycon
>>> from py2mcp.trans import mk_input_trans
>>> trans = mk_input_trans({'n': int})
>>> trans({'n': '3', 'label': 'x'})
{'n': 3, 'label': 'x'}
```

### Functions

| [`mk_input_trans`](_autosummary/py2mcp.trans.html.md#py2mcp.trans.mk_input_trans)([name_func_relationships])   | Create an input transformation function from name->func mappings.   |
|----------------------------------------------------------------------------------------------|---------------------------------------------------------------------|

### py2mcp.trans.mk_input_trans(name_func_relationships=None)

Create an input transformation function from name->func mappings.

The returned callable takes a tool call’s keyword arguments as a dict and
returns a new dict in which each named argument has been passed through its
converter; arguments with no converter are copied through unchanged. Pass it
as `input_trans` to [`py2mcp.mk_mcp_server()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_server) and friends.

* **Parameters:**
  **name_func_relationships** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping)]) – Which converter applies to which argument, as
  `{name: func}`, or reversed as `{func: name}` or
  `{func: [name, ...]}` when one converter serves several arguments.
  `None` gives a transformation that only copies the dict.
* **Return type:**
  [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)[[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)], [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]
* **Returns:**
  A function from a kwargs dict to a new kwargs dict.
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – The same argument name is mapped more than once.

### Examples

```pycon
>>> def to_int(x): return int(x)
>>> trans = mk_input_trans({'x': to_int})
>>> trans({'x': '42', 'y': 'hello'})
{'x': 42, 'y': 'hello'}
```

One converter for several arguments, written the reversed way:

```pycon
>>> trans = mk_input_trans({to_int: ['x', 'y']})
>>> trans({'x': '1', 'y': '2', 'z': '3'})
{'x': 1, 'y': 2, 'z': '3'}
```

No mapping means no conversion:

```pycon
>>> mk_input_trans()({'x': '1'})
{'x': '1'}
```

#### SEE ALSO
[`py2mcp.mk_mcp_server()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_server): where the returned callable is applied.


# _autosummary/py2mcp.util.html.md

# py2mcp.util

Resolve object references and turn a mapping into CRUD functions.

Two small tools the builders in [`py2mcp.main`](_autosummary/py2mcp.main.html.md#module-py2mcp.main) rest on, usable on their
own: `import_object` is how `'module:function'` strings from a config file
become callables, and `store_to_funcs` is the function-level half of
[`py2mcp.mk_mcp_from_store()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_from_store).

Main entry points:

- `import_object`: `'module.path:attr'` string to the object it names
- `store_to_funcs`: list/get/set/delete functions over a `MutableMapping`

```pycon
>>> from py2mcp.util import import_object
>>> import_object('os.path:basename')('/a/b/c.txt')
'c.txt'
```

### Functions

| [`claude_install_link`](_autosummary/py2mcp.util.html.md#py2mcp.util.claude_install_link)(name, mcp_url, \*[, admin])    | Prefilled "Add custom connector" link for claude.ai.                          |
|-----------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------|
| [`import_object`](_autosummary/py2mcp.util.html.md#py2mcp.util.import_object)(ref)                                 | Resolve a `'module.path:attr'` (preferred) or `'module.path.attr'` reference. |
| [`markdown_install_badge`](_autosummary/py2mcp.util.html.md#py2mcp.util.markdown_install_badge)(name, mcp_url, \*[, admin]) | Markdown link that installs an MCP server as a claude.ai connector.           |
| [`store_to_funcs`](_autosummary/py2mcp.util.html.md#py2mcp.util.store_to_funcs)(store, \*[, name, plural])          | Convert a MutableMapping into CRUD functions.                                 |

### py2mcp.util.claude_install_link(name, mcp_url, , admin=False)

Prefilled “Add custom connector” link for claude.ai.

There is no true one-click install for an unlisted MCP server (listing
requires Anthropic review), but this link opens the add-connector modal with
the name and URL already filled in, so the user only has to confirm — which
beats “go to Settings, find Connectors, paste this long URL”.

* **Parameters:**
  * **name** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Connector name to prefill (what the user will see in their list).
  * **mcp_url** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Full URL of the MCP endpoint, e.g. `https://host/api/x/mcp`.
  * **admin** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Target the org-wide install page instead of the per-user one.
    Use it when an admin is rolling the connector out to a whole
    workspace; the default (`False`) is the personal install.
* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)
* **Returns:**
  The claude.ai URL, safe to paste into a README or a chat message.

```pycon
>>> claude_install_link('snout', 'https://example.com/api/snout_mcp/mcp')
'https://claude.ai/customize/connectors?modal=add-custom-connector&connectorName=snout&connectorUrl=https%3A%2F%2Fexample.com%2Fapi%2Fsnout_mcp%2Fmcp'
```

Names and URLs are percent-encoded, so spaces (and `&`) can’t break the
query string:

```pycon
>>> claude_install_link('my server', 'https://x.io/mcp', admin=True)
'https://claude.ai/admin-settings/connectors?modal=add-custom-connector&connectorName=my%20server&connectorUrl=https%3A%2F%2Fx.io%2Fmcp'
```

Note that the link is a convenience, not an access grant: if the server is an
OAuth resource server with an allowlist, a user who isn’t on it can follow
the link, complete the flow, and still be refused. Custom connectors are also
a paid-plan feature, so the link goes nowhere for a Free-plan user.

### py2mcp.util.import_object(ref)

Resolve a `'module.path:attr'` (preferred) or `'module.path.attr'` reference.

Useful for building MCP servers from configuration strings (e.g. tool
references declared in a file), so callers don’t reimplement the
`importlib` dance. With a colon, everything before it is the module and
everything after it an attribute path; without one, the last dot splits
module from attribute, so `'pkg.mod.Class.method'` cannot be reached in
the dotted form (use `'pkg.mod:Class.method'`).

* **Parameters:**
  **ref** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The reference string; the `module:attr` form is preferred.
* **Return type:**
  [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)
* **Returns:**
  The object the reference names, after importing its module.
* **Raises:**
  * [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – The reference has no module part or no attribute part.
  * [**ImportError**](https://docs.python.org/3/builtins/exceptions.html#ImportError) – The module part does not import (`ModuleNotFoundError`
        when the module does not exist; a plain `ImportError` if it
        exists but fails while importing).
  * [**AttributeError**](https://docs.python.org/3/builtins/exceptions.html#AttributeError) – The attribute path does not exist on the module.

### Examples

```pycon
>>> import_object('json:dumps')
<function dumps at ...>
>>> import_object('os.path.join')
<function join at ...>
>>> import_object('no-separator')
Traceback (most recent call last):
    ...
ValueError: Invalid object reference 'no-separator'; expected 'module:attr' or 'module.path.attr'.
```

#### SEE ALSO
[`py2mcp.mk_mcp_from_refs()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_from_refs): build a server from such references.

### py2mcp.util.markdown_install_badge(name, mcp_url, , admin=False)

Markdown link that installs an MCP server as a claude.ai connector.

The dominant use of [`claude_install_link()`](_autosummary/py2mcp.util.html.md#py2mcp.util.claude_install_link) is pasting one into a README,
so this saves writing the same link syntax around it. Arguments are those of
[`claude_install_link()`](_autosummary/py2mcp.util.html.md#py2mcp.util.claude_install_link).

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

```pycon
>>> markdown_install_badge('snout', 'https://x.io/mcp')
'[Add snout to Claude](https://claude.ai/customize/connectors?modal=add-custom-connector&connectorName=snout&connectorUrl=https%3A%2F%2Fx.io%2Fmcp)'
```

Brackets and backslashes in the name are escaped in the link text, so a name
like `tools [beta]` can’t end the link text early and break the link (the
URL itself is percent-encoded already):

```pycon
>>> print(markdown_install_badge('tools [beta]', 'https://x.io/mcp').split('](')[0])
[Add tools \[beta\] to Claude
```

### py2mcp.util.store_to_funcs(store, , name='item', plural='')

Convert a MutableMapping into CRUD functions.

The functions close over `store` and operate on it live. The list
function takes no arguments; the others take `key` (and `value` for
set). Set and delete return a short confirmation string.

* **Parameters:**
  * **store** ([`MutableMapping`](https://docs.python.org/3/library/typing.html#typing.MutableMapping)[[`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`KT`), [`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`VT`)]) – The mapping the functions read and write.
  * **name** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Singular noun used in the function names.
  * **plural** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Plural noun for the list function (defaults to name + ‘s’).
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)]
* **Returns:**
  The four functions, in the order list, get, set, delete, named
  `list_<plural>`, `get_<name>`, `set_<name>`, `delete_<name>`.

### Examples

```pycon
>>> projects = {'p1': {'name': 'Project 1'}}
>>> funcs = store_to_funcs(projects, name='project')
>>> len(funcs)
4
>>> [f.__name__ for f in funcs]
['list_projects', 'get_project', 'set_project', 'delete_project']
>>> list_projects, get_project, set_project, delete_project = funcs
>>> set_project('p2', {'name': 'Project 2'})
"Set project 'p2'"
>>> list_projects()
['p1', 'p2']
>>> delete_project('p1')
"Deleted project 'p1'"
>>> projects
{'p2': {'name': 'Project 2'}}
```

#### SEE ALSO
[`py2mcp.mk_mcp_from_store()`](_autosummary/py2mcp.html.md#py2mcp.mk_mcp_from_store): the same functions, served as MCP tools.


# about-this-build.html.md

<!-- generated by epythet -->

# About this build

This documentation was built on **2026-09-22 14:39 UTC** from commit <a href="https://github.com/i2mint/py2mcp/commit/8c29353b7a5af119cdbac906a58d4b8b8aebe0ce"><code>8c29353</code></a> on branch <code>main</code>, for **py2mcp 0.1.15** (from <code>pyproject.toml</code>).

#### NOTE
Nothing suggests a mismatch: the tree was clean at the commit above, and the documented version is the one on PyPI.

## Source

|                     |                                                                                                                                                      |
|---------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|
| Commit              | <a href="https://github.com/i2mint/py2mcp/commit/8c29353b7a5af119cdbac906a58d4b8b8aebe0ce"><code>8c29353b7a5af119cdbac906a58d4b8b8aebe0ce</code></a> |
| Branch              | <code>main</code>                                                                                                                                    |
| Tags at this commit | <code>0.1.15</code>                                                                                                                                  |
| Working tree        | clean                                                                                                                                                |
| Remote              | <code>https://github.com/i2mint/py2mcp</code>                                                                                                        |

## Continuous integration

|              |                                                                                            |
|--------------|--------------------------------------------------------------------------------------------|
| Repository   | <code>i2mint/py2mcp</code>                                                                 |
| Run          | <a href="https://github.com/i2mint/py2mcp/actions/runs/35741542434">35741542434</a>        |
| Ref          | <code>refs/heads/main</code>                                                               |
| Event commit | <code>f0944aa43037ff573b2d04ce77825339faa5212a</code> (in the history of the built commit) |

## Tools

|          |         |
|----------|---------|
| epythet  | 0.2.12  |
| Sphinx   | 9.1.0   |
| docutils | 0.22.4  |
| Python   | 3.12.14 |

## Configuration as resolved

|               |                                                                   |
|---------------|-------------------------------------------------------------------|
| theme         | <code>auto</code> (Sphinx theme <code>pydata_sphinx_theme</code>) |
| accent        | <code>#6e3f8d</code>                                              |
| api_generator | <code>autosummary</code>                                          |
| ignore        | <code>tests/</code>, <code>scrap/</code>, <code>examples/</code>  |
| agent_outputs | <code>true</code>                                                 |
| aggregates    | <code>md</code>                                                   |
| ai_artifacts  | <code>true</code>                                                 |

## Package on PyPI

Latest release: <a href="https://pypi.org/project/py2mcp/0.1.15/">0.1.15</a>, the same as the documented version.

## Reproduce

```bash
git clone https://github.com/i2mint/py2mcp && cd py2mcp
git checkout 8c29353b7a5af119cdbac906a58d4b8b8aebe0ce
pip install "epythet==0.2.12"
epythet quickstart . --ignore tests/ scrap/ examples/
```

The same data, for machines: <a href="build_info.json"><code>build_info.json</code></a> (schema version 1).


# ai-agents.html.md

<!-- generated by epythet -->

# For AI agents

`py2mcp` ships artifacts for coding agents alongside its code. This page lists
them, says where each lives in the repository, and points at the
machine-readable copies of this documentation.

## Instruction files

Files agents read before working in this repository.

- [`.claude/CLAUDE.md`](https://github.com/i2mint/py2mcp/tree/HEAD/.claude/CLAUDE.md): read by Claude Code

## Machine-readable documentation

This site publishes the same documentation in forms that fit an agent’s context window:

- [`llms.txt`](https://i2mint.github.io/py2mcp/llms.txt): an index of every page with a one-line description ([llms.txt](https://llmstxt.org) format)
- [`py2mcp.md`](https://i2mint.github.io/py2mcp/py2mcp.md): the whole documentation as one Markdown file
- `<page>.html.md`: a rendered Markdown twin of every page, advertised from each page’s `<head>` with `<link rel="alternate" type="text/markdown">`
- [`objects.inv`](https://i2mint.github.io/py2mcp/objects.inv): the Sphinx inventory: a symbol-to-URL index (`sphobjinv convert plain objects.inv -`)


# api.html.md

# API reference

| [`py2mcp`](_autosummary/py2mcp.html.md#module-py2mcp)   | py2mcp: Quick MCP server creation from Python functions.   |
|-------------------------------------------------------------------------|------------------------------------------------------------|


