Skip to main content
Glama

any2mcp

Turn any Python module into an MCP server — without handing a language model your shell.

CI PyPI Python License: MIT

You have a file full of useful Python functions and you want an agent to call them. Today that means importing an MCP framework and decorating every function — so your business logic now depends on MCP.

any2mcp skips that. Point it at a module and every public function becomes a fully-schematized MCP tool, with argument types and descriptions derived from the type hints and docstrings you already wrote:

any2mcp ./tools.py

But "expose every function" is a dangerous default, and most MCP tooling stops there. If your module happens to contain a run_shell() or a delete_path(), that naive approach just gave a language model your machine.

So any2mcp reads your code before it exposes it:

$ any2mcp examples/devtools.py --risk-report

any2mcp risk report: examples/devtools.py
policy: guard (allows up to 'moderate')

  RISK      TOOL               CAPABILITIES  STATUS
  --------  -----------------  ------------  -------
  safe      word_count         -             exposed
  low       read_project_file  fs-read       exposed
      - .read_text (line 36) [heuristic]
  low       list_directory     fs-read       exposed
      - os.listdir (line 44)
  moderate  write_note         fs-write      exposed
      - .write_text (line 50) [heuristic]
  low       parse_json_file    fs-read       exposed
      - open (line 56)
  high      run_shell          subprocess    BLOCKED
      - subprocess.run (line 66)
      ! blocked: risk 'high' (subprocess) exceeds policy 'guard' (max 'moderate')
  high      delete_path        fs-delete     BLOCKED
      - shutil.rmtree (line 85)
      - .unlink (line 87) [heuristic]
      ! blocked: risk 'high' (fs-delete) exceeds policy 'guard' (max 'moderate')

5 exposed, 2 blocked.

The two dangerous functions are not exposed, and no flags were needed to get that.


The difference

Before — one decorator per function, in a file that now depends on MCP:

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("calculator")

@mcp.tool()
def add(a: float, b: float) -> float:
    """Add two numbers."""
    return a + b

@mcp.tool()
def divide(a: float, b: float) -> float:
    """Divide a by b."""
    return a / b

# ...repeat for every function, forever

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

After — your module stays plain Python and knows nothing about MCP:

# calculator.py — no imports, no decorators
def add(a: float, b: float) -> float:
    """Add two numbers."""
    return a + b

def divide(a: float, b: float) -> float:
    """Divide a by b."""
    return a / b
any2mcp calculator.py

Related MCP server: py2mcp

Install

pip install any2mcp
# or, with uv:
uv tool install any2mcp

Not yet on PyPI? Install straight from source: uv tool install "git+https://github.com/SanoberRehman/any2mcp"

Quickstart

any2mcp tools.py --risk-report       # audit what each function can reach
any2mcp tools.py --list              # show the exposed tools + JSON schemas
any2mcp tools.py                     # serve over stdio
any2mcp tools.py:add                 # expose only `add`
any2mcp tools.py --exclude '_*'      # filter with globs
any2mcp mypackage.tools              # a dotted, importable module works too

The safety model

This is the part worth reading carefully, because the guarantees are uneven and it matters which is which.

What is enforced

A blocked function is never registered as a tool. It is absent from tools/list, so the model cannot see it, cannot call it, and cannot talk its way past it. There is no confirmation token to forge and no runtime check to race — the tool simply does not exist on the server.

You can watch this happen against a real MCP client:

$ python examples/demo_safety.py

=== any2mcp devtools.py (default policy: guard) ===
tools visible to the model: ['word_count', 'read_project_file', 'list_directory', 'write_note', 'parse_json_file']
  run_shell: withheld; calling it anyway -> Unknown tool: run_shell
  delete_path: withheld; calling it anyway -> Unknown tool: delete_path
  word_count still works -> {"words": 3, "lines": 1, "characters": 18}

What is a heuristic

Which functions get classified as risky. any2mcp parses your module and looks for calls that reach the filesystem, the network, subprocesses, or the dynamic-execution builtins. It resolves import aliases, so both of these are caught:

import subprocess as sp
def a(cmd): sp.run(cmd)              # -> subprocess (high)

from os import system as sh
def b(cmd): sh(cmd)                  # -> subprocess (high)

and it follows calls within your module, so a wrapper inherits its helper's capabilities:

def _upload(data): requests.post("https://...", data=data)
def publish(data): _upload(data)     # -> network, "via _upload()"

But static analysis is not sound, and indirection defeats it:

def sneaky(cmd):
    getattr(os, "system")(cmd)       # -> reported SAFE. Not detected.

So a safe classification means "no known-risky call was found", never "this function is harmless". These blind spots are asserted in tests/test_risk.py under TestDocumentedBlindSpots, so this claim cannot quietly drift into implying more than it should.

What it is not

any2mcp is not a sandbox. Tools that are exposed run in-process with your full privileges. The policy decides what gets offered to a model; it does not contain what happens next.

Importing a module also executes its top-level code. That is why --risk-report never imports a file target — it works from source, so you can audit code before running any of it, and even audit a module whose dependencies you don't have installed.

Policies

Policy

Allows

Use it when

open

everything

You have read the code and want the 0.1 behaviour.

guard (default)

up to moderate — reads, writes, network

General use. Blocks subprocesses, eval, recursive deletes, and anything unanalyzable.

readonly

up to low — reads, env

The agent should look but not touch.

strict

safe only

Pure computation, no side effects of any kind.

Overrides, in order of precedence — naming a specific tool beats a category rule, and an explicit deny always wins:

any2mcp tools.py --deny-tool 'admin_*'        # never expose these
any2mcp tools.py --allow-tool run_shell       # expose this one on purpose
any2mcp tools.py --deny-capability network    # block a whole capability

Capabilities are fs-read, fs-write, fs-delete, network, subprocess, dynamic-exec, and env.

Use it as a CI gate

--risk-report exits 3 when anything is blocked and 0 when nothing is, so it can fail a build if someone adds a subprocess call to a module you expose to agents:

- run: any2mcp src/agent_tools.py --risk-report --format json

Audit log

any2mcp tools.py --audit-log calls.jsonl

Every call is appended as one JSON object. Argument names and types are recorded; values are not — tool arguments routinely carry API keys and file contents, and a log that silently copied them to disk would be its own vulnerability. Opt in with --audit-values when you've decided that's appropriate for your data.

{"ts": "2026-07-28T05:45:14.604+00:00", "event": "blocked", "tool": "run_shell",
 "risk": "high", "reason": "risk 'high' (subprocess) exceeds policy 'guard' (max 'moderate')"}
{"ts": "2026-07-28T05:45:14.612+00:00", "event": "call", "tool": "write_note",
 "risk": "moderate", "duration_ms": 0.431, "ok": true,
 "args": {"path": "str", "text": "str"}}
{"ts": "2026-07-28T05:45:14.614+00:00", "event": "call", "tool": "word_count",
 "risk": "safe", "duration_ms": 0.003, "ok": true, "args": {"text": "str"}}

Blocked tools are recorded too, so the log shows what was withheld, not just what ran.

Rich schemas, for free

any2mcp doesn't invent its own type system — it hands each function to the MCP SDK's pydantic machinery, the same code path the official @tool decorator uses. So the hard cases just work: Optional[...], unions, Enum, Literal, list/dict/tuple, pydantic models and dataclasses as parameters, defaults, and async def.

// any2mcp calculator.py --list  (excerpt for round_to)
{
  "name": "round_to",
  "input_schema": {
    "properties": {
      "value":   { "type": "number" },
      "ndigits": { "type": "integer", "default": 2 },
      "mode":    { "$ref": "#/$defs/Rounding", "default": "nearest" }
    },
    "$defs": { "Rounding": { "enum": ["up", "down", "nearest"], "type": "string" } },
    "required": ["value"]
  }
}

A function whose signature genuinely can't be schematized is skipped with a message on stderr — one unsupported function costs one tool, never the whole server.

Use it with Claude Desktop (or any MCP client)

{
  "mcpServers": {
    "my-tools": {
      "command": "any2mcp",
      "args": ["/absolute/path/to/tools.py"]
    }
  }
}

Add "--policy", "readonly" to the args array to tighten it further.

Which functions get exposed?

Resolved in this order:

  1. A :selector (tools.py:add) exposes exactly that one function.

  2. Otherwise, if the module defines __all__, that list is the allow-list.

  3. Otherwise, every public function (no leading underscore) defined in the module — functions merely imported into it are skipped, so from os.path import join won't leak in as a tool.

--include / --exclude globs apply on top, then the policy decides what survives.

Roadmap

  • Policy file (any2mcp.toml) so per-tool decisions can be reviewed in git

  • Expose class methods and @staticmethods

  • Resources & prompts, not just tools

  • Taint tracking, to tell read_file("/etc/passwd") from read_file(user_path)

OpenAPI and FastAPI conversion are intentionally out of scope — fastmcp already does those well. any2mcp focuses on the gap they leave: plain modules, zero decoration, and a defensible answer to "what did I just expose?"

Development

uv sync
uv run pytest        # 127 tests, incl. real MCP client sessions
uv run ruff check .

License

MIT — see LICENSE.

Available Tools

5 tools
list_directoryC

List the entries of a directory, sorted.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathNo.

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

C2.9/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are present, so the description carries the full behavioral burden. It discloses only that output is sorted, without saying the sort key, whether the operation is read-only, how a missing path is handled, or whether listing recurses. For a simple read tool the risk is low, but the disclosure is thin.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single clause with zero filler, stating the action first and the ordering trait second. Nothing is padded and nothing is buried.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return-format explanation is unnecessary, and the schema already supplies the path default. However, with a 1-parameter tool and no annotations, the description omits path semantics and basic read-only/error behavior, leaving it only minimally sufficient.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The single parameter 'path' has a schema default of '.' but 0% description coverage, and the description never mentions it. It does not say whether the path is relative or absolute, that it defaults to the current directory, or what happens for invalid paths, so it fails to compensate for the coverage gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (List) and resource (entries of a directory) with an added ordering trait ('sorted'), which is enough to distinguish it from siblings like read_project_file or word_count. It is clear but stops short of naming scope details (recursive vs. single level, hidden files) that would fully separate it from adjacent file tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no when-to-use guidance, no statement of prerequisites, and no mention of any alternative. The agent must infer that this is the discovery step that precedes read_project_file, which is reasonable but entirely undocumented.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

parse_json_fileC

Load and return the JSON value stored in a file.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYes

TDQS

C2.8/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full behavioral burden, yet it omits what happens on missing files, invalid JSON, encoding issues, or large files. It does not even confirm the operation is read-only or side-effect free, leaving key traits to inference.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single efficient sentence with the action front-loaded. It is appropriately sized, though it omits information an agent would find useful rather than containing waste.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter, no-output-schema read tool this is minimally adequate, but with no annotations and an undocumented 'path' parameter the description should say more about error behavior and path semantics to be fully callable.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The single parameter 'path' has 0% schema description coverage, and the description never explains it (relative vs absolute, resolution base, allowed forms). It adds no meaning beyond naming the concept of a file.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb (load and return) and resource (the JSON value stored in a file), so the agent knows exactly what operation is performed. It does not, however, distinguish itself from the sibling read_project_file, which could plausibly read the same file without JSON parsing.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no guidance on when to use this tool versus read_project_file or how it relates to the other siblings. No conditions, exclusions, or alternatives are mentioned.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

read_project_fileB

Read a UTF-8 text file from disk, truncating past max_bytes.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYes
max_bytesNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

B3.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden, and it does disclose one real behavior beyond the schema: output is truncated past max_bytes. However, it is silent on error behavior for missing files, non-UTF-8 content, and whether truncation is silent or flagged.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single tight sentence with the constraint front-loaded and no filler. It is arguably too terse for a tool with two undocumented parameters, but nothing in it is wasted.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need no explanation, and truncation is covered. Still, for a file-read tool with zero annotation and zero schema-description coverage, the absence of any error, encoding, or path-resolution context leaves real gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the schema documents neither parameter's meaning. The description partially compensates by explaining that max_bytes is a truncation threshold, but the required 'path' parameter's semantics (absolute vs relative, workspace root) are left entirely unexplained.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: read a UTF-8 text file from disk. This is clearly distinguishable from list_directory and word_count, but the description never names those siblings or states the boundary between this and other read-style tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No when-to-use guidance, no alternatives, no prerequisites. An agent must infer from the name alone that this is the tool for viewing file contents rather than scanning a directory or counting words.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

word_countA

Count words, lines, and characters in a string.

Pure computation - no side effects at all.

ParametersJSON Schema
NameRequiredDescriptionDefault
textYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden and does disclose the key behavioral trait: 'Pure computation - no side effects at all,' which tells the agent this is a safe, non-mutating operation with no auth or state implications. It stops short of noting edge cases (empty string, how newlines define a 'line'), but the central safety property is covered.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences, zero filler, with the core purpose front-loaded and the safety note following. Nothing could be cut without losing information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need no explanation, and for a single-parameter pure function the definition is nearly complete. The only residual gap is that edge-case counting semantics (newline handling, whitespace) are left undefined.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Only one parameter (text) and schema description coverage is 0%, so the schema adds nothing. The description's phrase 'in a string' at least confirms the input type and nature, but it gives no detail on encoding, multi-line handling, or how whitespace affects the word count.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (count) and the exact resources measured (words, lines, characters) against a clearly named input (a string). This is unambiguous and easily distinguished from the sibling tools, which all operate on files or directories rather than in-memory strings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is only implied: the tool's utility is self-evident for a pure string computation, but the description never states when to reach for it versus, say, reading a file and counting externally. No exclusions or alternatives are offered, though for a trivial utility the cost of that omission is low.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

write_noteA

Write text to path, replacing any existing file. Returns bytes written.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYes
textYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden. It discloses the critical destructive trait ('replacing any existing file') and the return value ('bytes written'), but omits permission requirements, encoding, or error behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single sentence that front-loads the action and includes only necessary details. Every clause earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple two-parameter write tool with an output schema, the description covers the core action and overwrite behavior. However, with zero schema description coverage and no annotations, it leaves parameter semantics and permission/encoding details unaddressed.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It only restates the parameter names ('text' and 'path') in context, adding little beyond the schema's own 'Path' and 'Text' titles. No format, constraints, or path semantics are provided.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: 'Write text to path.' It also clarifies the scope with 'replacing any existing file' and the return value, making it immediately distinguishable from the sibling read/list tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by the write action, but the description never states when to use this tool versus alternatives or any prerequisites. For a simple write tool with no competing siblings, the implied usage is adequate but not explicitly guided.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 5 tool updatesv0.2.0
    • First observedlist_directory
    • First observedparse_json_file
    • First observedread_project_file
    • First observedword_count
    • First observedwrite_note

TDQS

B3.4/5.0

Scored across 5 tools

Disambiguation4/5

word_count is pure computation, list_directory is distinct, and write_note is the sole writer, so most tools are easy to tell apart. read_project_file and parse_json_file overlap somewhat since both read files from disk, though the parsed-JSON vs raw-text distinction helps.

Naming Consistency4/5

Four tools follow a clear verb_noun pattern (read_project_file, list_directory, write_note, parse_json_file). word_count deviates as a noun_noun form, a minor inconsistency in an otherwise predictable scheme.

Tool Count5/5

Five tools is well-scoped for a lightweight file/text/JSON utility server, with each tool earning its place and no redundancy.

Completeness3/5

The surface covers reading, listing, writing, JSON parsing, and word counting, but has no delete/remove, no JSON writing or in-place edit, and no directory creation, leaving notable lifecycle gaps for a file-oriented toolkit.

Maintenance

ActivityMaintained
ResponsivenessUnresponsive

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    A minimal Python package for easily setting up and running MCP servers and clients, allowing functions to be automatically exposed as tools that LLMs can use with just 2 lines of code.
    22
    -
  • A
    license
    Not graded
    quality
    B
    maintenance
    Create MCP servers from Python functions instantly, with support for input transformations and store-based CRUD operations.
    319 PyPI
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Turn your typed TypeScript functions into an MCP server — tool, resource, and prompt schemas inferred from your types and JSDoc. No schema library, no decorators, no boilerplate.
    8 npm
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Enables building and running zero-dependency MCP servers with automatic JSON Schema generation, exposing Python tools to Claude Desktop, Cursor, and autonomous agent fleets.
    2
    Apache 2.0