Repository: jlowin/fastmcp

Issue:
include contrib in pyproject?
### Enhancement Description

otherwise, how do people use the stuff in contrib directory?

### Use Case

_No response_

### Proposed Implementation

```Python

```

Discussion:
@strawgate: @jlowin was the intention to have contrib be its own package or `import fastmcp.contrib...` and it should move to the FastMCP folder?@jlowin: Oh! This is my fault for overlooking in the PR @strawgate -- I intended for it to be a module e.g. `import fastmcp.contrib`. I will move it in a PR and release.@jlowin: 2.2.2 is out with this change, thank you @strawgate and @yihuang! 

Solution patch:
diff --git a/docs/docs.json b/docs/docs.json
index 237dee3..c70f804 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -61,7 +61,8 @@
                     "patterns/composition",
                     "patterns/decorating-methods",
                     "patterns/openapi",
-                    "patterns/fastapi"
+                    "patterns/fastapi",
+                    "patterns/contrib"
                 ]
             },
             {
diff --git a/docs/patterns/contrib.mdx b/docs/patterns/contrib.mdx
new file mode 100644
index 0000000..920b248
--- /dev/null
+++ b/docs/patterns/contrib.mdx
@@ -0,0 +1,42 @@
+---
+title: "Contrib Modules"
+description: "Community-contributed modules extending FastMCP"
+icon: "cubes"
+---
+
+
+FastMCP includes a `contrib` package that holds community-contributed modules. These modules extend FastMCP's functionality but aren't officially maintained by the core team.
+
+Contrib modules provide additional features, integrations, or patterns that complement the core FastMCP library. They offer a way for the community to share useful extensions while keeping the core library focused and maintainable.
+
+The available modules can be viewed in the [contrib directory](https://github.com/jlowin/fastmcp/tree/main/src/contrib).
+
+## Usage
+
+To use a contrib module, import it from the `fastmcp.contrib` package:
+
+```python
+from fastmcp.contrib import my_module
+```
+
+## Important Considerations
+
+- **Stability**: Modules in `contrib` may have different testing requirements or stability guarantees compared to the core library.
+- **Compatibility**: Changes to core FastMCP might break modules in `contrib` without explicit warnings in the main changelog.
+- **Dependencies**: Contrib modules may have additional dependencies not required by the core library. These dependencies are typically documented in the module's README or separate requirements files.
+
+## Contributing
+
+We welcome contributions to the `contrib` package! If you have a module that extends FastMCP in a useful way, consider contributing it:
+
+1. Create a new directory in `src/fastmcp/contrib/` for your module
+3. Add proper tests for your module in `tests/contrib/`
+2. Include comprehensive documentation in a README.md file, including usage and examples, as well as any additional dependencies or installation instructions
+5. Submit a pull request
+
+The ideal contrib module:
+- Solves a specific use case or integration need
+- Follows FastMCP coding standards
+- Includes thorough documentation and examples
+- Has comprehensive tests
+- Specifies any additional dependencies
diff --git a/src/fastmcp/client/transports.py b/src/fastmcp/client/transports.py
index 6854451..210286b 100644
--- a/src/fastmcp/client/transports.py
+++ b/src/fastmcp/client/transports.py
@@ -3,6 +3,7 @@ import contextlib
 import datetime
 import os
 import shutil
+import sys
 from collections.abc import AsyncIterator
 from pathlib import Path
 from typing import (
@@ -185,7 +186,7 @@ class PythonStdioTransport(StdioTransport):
         args: list[str] | None = None,
         env: dict[str, str] | None = None,
         cwd: str | None = None,
-        python_cmd: str = "python",
+        python_cmd: str = sys.executable,
     ):
         """
         Initialize a Python transport.
diff --git a/src/contrib/README.md b/src/fastmcp/contrib/README.md
similarity index 58%
rename from src/contrib/README.md
rename to src/fastmcp/contrib/README.md
index 3df31bf..7b1dbb5 100644
--- a/src/contrib/README.md
+++ b/src/fastmcp/contrib/README.md
@@ -6,4 +6,14 @@ This directory holds community-contributed modules for FastMCP. These modules ex
 *   Modules in `contrib` may have different testing requirements or stability guarantees compared to the core library.
 *   Changes to the core FastMCP library might break modules in `contrib` without explicit warnings in the main changelog.
 
-Use these modules at your own discretion. Contributions are welcome, but please include tests and documentation.
\ No newline at end of file
+Use these modules at your own discretion. Contributions are welcome, but please include tests and documentation.
+
+## Usage
+
+To use a contrib module, import it from the `fastmcp.contrib` package.
+
+```python
+from fastmcp.contrib import my_module
+```
+
+Note that the contrib modules may have different dependencies than the core library, which can be noted in their respective README's or even separate requirements / dependency files.
\ No newline at end of file
diff --git a/src/contrib/bulk_tool_caller/README.md b/src/fastmcp/contrib/bulk_tool_caller/README.md
similarity index 100%
rename from src/contrib/bulk_tool_caller/README.md
rename to src/fastmcp/contrib/bulk_tool_caller/README.md
diff --git a/src/contrib/bulk_tool_caller/__init__.py b/src/fastmcp/contrib/bulk_tool_caller/__init__.py
similarity index 100%
rename from src/contrib/bulk_tool_caller/__init__.py
rename to src/fastmcp/contrib/bulk_tool_caller/__init__.py
diff --git a/src/contrib/bulk_tool_caller/bulk_tool_caller.py b/src/fastmcp/contrib/bulk_tool_caller/bulk_tool_caller.py
similarity index 97%
rename from src/contrib/bulk_tool_caller/bulk_tool_caller.py
rename to src/fastmcp/contrib/bulk_tool_caller/bulk_tool_caller.py
index aa02c5e..365dd95 100644
--- a/src/contrib/bulk_tool_caller/bulk_tool_caller.py
+++ b/src/fastmcp/contrib/bulk_tool_caller/bulk_tool_caller.py
@@ -3,10 +3,14 @@ from typing import Any
 from mcp.types import CallToolResult
 from pydantic import BaseModel, Field
 
-from contrib.mcp_mixin.mcp_mixin import _DEFAULT_SEPARATOR_TOOL, MCPMixin, mcp_tool
 from fastmcp import FastMCP
 from fastmcp.client import Client
 from fastmcp.client.transports import FastMCPTransport
+from fastmcp.contrib.mcp_mixin.mcp_mixin import (
+    _DEFAULT_SEPARATOR_TOOL,
+    MCPMixin,
+    mcp_tool,
+)
 
 
 class CallToolRequest(BaseModel):
diff --git a/src/contrib/bulk_tool_caller/example.py b/src/fastmcp/contrib/bulk_tool_caller/example.py
similarity index 81%
rename from src/contrib/bulk_tool_caller/example.py
rename to src/fastmcp/contrib/bulk_tool_caller/example.py
index 76170c1..85139fe 100644
--- a/src/contrib/bulk_tool_caller/example.py
+++ b/src/fastmcp/contrib/bulk_tool_caller/example.py
@@ -1,7 +1,7 @@
 """Sample code for FastMCP using MCPMixin."""
 
-from contrib.bulk_tool_caller import BulkToolCaller
 from fastmcp import FastMCP
+from fastmcp.contrib.bulk_tool_caller import BulkToolCaller
 
 mcp = FastMCP()
 
diff --git a/src/contrib/mcp_mixin/README.md b/src/fastmcp/contrib/mcp_mixin/README.md
similarity index 92%
rename from src/contrib/mcp_mixin/README.md
rename to src/fastmcp/contrib/mcp_mixin/README.md
index 3acb661..6dce9c9 100644
--- a/src/contrib/mcp_mixin/README.md
+++ b/src/fastmcp/contrib/mcp_mixin/README.md
@@ -10,7 +10,7 @@ Inherit from `MCPMixin` and use the decorators on the methods you want to regist
 
 ```python
 from fastmcp import FastMCP
-from contrib.mcp_mixin import MCPMixin, mcp_tool, mcp_resource
+from fastmcp.contrib.mcp_mixin import MCPMixin, mcp_tool, mcp_resource
 
 class MyComponent(MCPMixin):
     @mcp_tool(name="my_tool", description="Does something cool.")
diff --git a/src/contrib/mcp_mixin/__init__.py b/src/fastmcp/contrib/mcp_mixin/__init__.py
similarity index 100%
rename from src/contrib/mcp_mixin/__init__.py
rename to src/fastmcp/contrib/mcp_mixin/__init__.py
diff --git a/src/contrib/mcp_mixin/example.py b/src/fastmcp/contrib/mcp_mixin/example.py
similarity index 96%
rename from src/contrib/mcp_mixin/example.py
rename to src/fastmcp/contrib/mcp_mixin/example.py
index 5e99663..dcdc4c2 100644
--- a/src/contrib/mcp_mixin/example.py
+++ b/src/fastmcp/contrib/mcp_mixin/example.py
@@ -2,13 +2,13 @@
 
 import asyncio
 
-from contrib.mcp_mixin import (
+from fastmcp import FastMCP
+from fastmcp.contrib.mcp_mixin import (
     MCPMixin,
     mcp_prompt,
     mcp_resource,
     mcp_tool,
 )
-from fastmcp import FastMCP
 
 mcp = FastMCP()
 
diff --git a/src/contrib/mcp_mixin/mcp_mixin.py b/src/fastmcp/contrib/mcp_mixin/mcp_mixin.py
similarity index 100%
rename from src/contrib/mcp_mixin/mcp_mixin.py
rename to src/fastmcp/contrib/mcp_mixin/mcp_mixin.py


Test patch:
diff --git a/tests/contrib/test_bulk_tool_caller.py b/tests/contrib/test_bulk_tool_caller.py
index 5e55198..348eb0e 100644
--- a/tests/contrib/test_bulk_tool_caller.py
+++ b/tests/contrib/test_bulk_tool_caller.py
@@ -3,12 +3,12 @@ from typing import Any
 import pytest
 from mcp.types import EmbeddedResource, ImageContent, TextContent
 
-from contrib.bulk_tool_caller.bulk_tool_caller import (
+from fastmcp import FastMCP
+from fastmcp.contrib.bulk_tool_caller.bulk_tool_caller import (
     BulkToolCaller,
     CallToolRequest,
     CallToolRequestResult,
 )
-from fastmcp import FastMCP
 
 ContentType = TextContent | ImageContent | EmbeddedResource
 
diff --git a/tests/contrib/test_mcp_mixin.py b/tests/contrib/test_mcp_mixin.py
index b31919c..a39b293 100644
--- a/tests/contrib/test_mcp_mixin.py
+++ b/tests/contrib/test_mcp_mixin.py
@@ -2,18 +2,18 @@
 
 import pytest
 
-from contrib.mcp_mixin import (
+from fastmcp import FastMCP
+from fastmcp.contrib.mcp_mixin import (
     MCPMixin,
     mcp_prompt,
     mcp_resource,
     mcp_tool,
 )
-from contrib.mcp_mixin.mcp_mixin import (
+from fastmcp.contrib.mcp_mixin.mcp_mixin import (
     _DEFAULT_SEPARATOR_PROMPT,
     _DEFAULT_SEPARATOR_RESOURCE,
     _DEFAULT_SEPARATOR_TOOL,
 )
-from fastmcp import FastMCP
 
 
 class TestMCPMixin: