Skip to content

mcp refactor#153

Merged
ncfrey merged 8 commits intomainfrom
n/mcp-factory-exps
Jul 25, 2025
Merged

mcp refactor#153
ncfrey merged 8 commits intomainfrom
n/mcp-factory-exps

Conversation

@ncfrey
Copy link
Contributor

@ncfrey ncfrey commented Jul 22, 2025

Description

This pull request introduces significant updates to the documentation, architecture, and tooling for the Lobster MCP server, focusing on modularity, usability, and integration with external tools like Cursor and Claude Desktop. Key changes include the introduction of a modular architecture for the MCP server, enhanced documentation for development workflows, and improved setup instructions for integration with Cursor and Claude Desktop.

Documentation Enhancements:

  • Added detailed sections in docs/MCP_INTEGRATION.md on the modular architecture, including descriptions of the models/, schemas/, and tools/ directories, as well as the server.py file. These updates emphasize separation of concerns and scalability. [1] [2]
  • Expanded the "Development Workflow" section to include step-by-step instructions on adding new MCP tools.
  • Updated README.md and docs/MCP_INTEGRATION.md with clearer setup instructions for Cursor and Claude Desktop, including automated and manual installation options. [1] [2]

Code Organization:

  • Refactored the MCP server to follow a modular design pattern, moving tool implementations to tools/, request/response schemas to schemas/, and model management to models/. The server.py file now serves as the main entry point. [1] [2]
  • Deprecated the legacy inference_server.py file in favor of the new server.py implementation.

Integration Improvements:

  • Introduced a one-click install option for Cursor integration, allowing users to easily add the Lobster MCP server using a deep link.
  • Added a quick start guide link in docs/MCP_INTEGRATION.md for concise setup and usage instructions.

Tooling Updates:

  • Updated the pyproject.toml file to point the lobster_mcp_server entry to the new server.py implementation.
  • Revised command-line tool descriptions and parameters in the documentation to align with the new modular architecture. [1] [2]

Code Quality and Testing:

  • Emphasized type safety and validation using Pydantic schemas, with detailed error messages for input validation.
  • Updated the testing framework to include modular component tests for the new architecture.

Type of Change

  • Bug fix
  • New feature
  • Documentation update
  • Performance improvement
  • Code refactoring

Testing

  • Tests pass locally
  • Added new tests for new functionality
  • Updated existing tests if needed

Checklist

  • Code follows style guidelines
  • Self-review completed
  • Documentation updated if needed
  • No breaking changes (or clearly documented)

@ncfrey ncfrey temporarily deployed to test.pypi.org July 22, 2025 17:12 — with GitHub Actions Inactive
@ncfrey ncfrey temporarily deployed to test.pypi.org July 24, 2025 18:38 — with GitHub Actions Inactive
@ncfrey ncfrey temporarily deployed to test.pypi.org July 24, 2025 21:12 — with GitHub Actions Inactive
@ncfrey ncfrey temporarily deployed to test.pypi.org July 24, 2025 21:59 — with GitHub Actions Inactive
@ncfrey ncfrey requested a review from cgrambow July 24, 2025 22:01
@ncfrey ncfrey temporarily deployed to test.pypi.org July 25, 2025 19:12 — with GitHub Actions Inactive
@ncfrey ncfrey marked this pull request as ready for review July 25, 2025 20:15
@ncfrey ncfrey merged commit b3d4c4e into main Jul 25, 2025
5 checks passed
@ncfrey ncfrey deleted the n/mcp-factory-exps branch July 25, 2025 20:24
- **Build package**: `uv run python -m build .`

### Development Setup
- **Sync all dependencies**: `uv sync` (installs all optional groups automatically)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you have it configured somewhere to automatically install all optional groups? if not, then only if you add --all-extras would it install them

After setup, you can use Lobster models directly in Cursor or Claude Desktop with natural language commands like:
#### Option 1: One-Click Install (Recommended)

[![Add Lobster to Cursor](https://img.shields.io/badge/Add%20to%20Cursor-MCP%20Server-blue?style=for-the-badge&logo=cursor)](cursor://anysphere.cursor-deeplink/mcp/install?name=lobster-inference&config=eyJjb21tYW5kIjogInV2IiwgImFyZ3MiOiBbInJ1biIsICItLXByb2plY3QiLCAiLiIsICItLWV4dHJhIiwgIm1jcCIsICJsb2JzdGVyX21jcF9zZXJ2ZXIiXSwgImVudiI6IHt9LCAiY3dkIjogIiR7d29ya3NwYWNlRm9sZGVyfSJ9Cg==)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the command encoded in this link will only work if the cursor working dir is the lobster project dir, right? (because you have uv run --project . ...). I don't think there's a great way to get around this with a local MCP server, but you could use the --directory option and then tell a user to set that to the directory where they cloned the lobster repo.

self.app = app
self._registered_tools = {}

def register_all_tools(self) -> None:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your solution is fine and makes a lot of sense, but it does require that new tools are always imported and added here. Another way of doing it would be to move tool registration to where the tools are defined, e.g., you could have a @register_tool decorator or something. Or, if new tools in tools/ are always meant to be registered, you could have some sort of auto-registration that just registers all functions it's able to import from tools.

logger = logging.getLogger("lobster-fastmcp-server")


def get_sequence_concepts(model_name: str, sequences: list[str]) -> dict:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: more specific type hint dict[str, Any]? If you want to be more specific, use a Pydantic model (actually I'm not completely sure whether that's supported for function output, but you could try): https://gofastmcp.com/servers/tools#pydantic-models. It would also be nice if fastmcp just supported TypedDict

raise


def get_supported_concepts(model_name: str) -> dict:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: output type hint

concept: str,
edits: int = 5,
intervention_type: Literal["positive", "negative"] = "negative",
) -> dict:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: output type hint

sequences: list[str],
model_type: Literal["masked_lm", "concept_bottleneck"],
representation_type: Literal["cls", "pooled", "full"] = "pooled",
) -> SequenceRepresentationResult:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you tested whether the dataclass output works? The only thing I can find in the docs on more complex data is Pydantic models: https://gofastmcp.com/servers/tools#pydantic-models

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems to, i don't think that it's necessary though

return "cuda" if torch.cuda.is_available() else "cpu"


def _load_model(model_name: str, model_type: str):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

slap on a @cache decorator from functools?

return "cuda" if torch.cuda.is_available() else "cpu"


def _load_model(model_name: str, model_type: str):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return type?

return model


def list_available_models() -> dict[str, Any]:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like the MCP client workflow right now requires a list_available_models call at some point to discover which models are available and then passing in the correct names into the tool functions via model_name. To avoid requiring the list_available_models call, you could modify AVAILABLE_MODELS to be an Enum instead (or split into two enums for masked_lm and concept_bottleneck) and then set the model_name (and model_type) type hint to the enum class: https://gofastmcp.com/servers/tools#enums
That being said, I'm not sure whether the information from the enum type hint is provided as context to the LLM when it calls a tool, but if it is, then you'd always have the available model info automatically for each tool call.

taylormjs pushed a commit that referenced this pull request Jul 31, 2025
* hot fix badge

* mcp

* fix and expand tests

* mcp factory

* changes

* fixes

* test fixes

---------

Co-authored-by: freyn6 <freyn6@gene.com>
taylormjs pushed a commit that referenced this pull request Aug 1, 2025
* hot fix badge

* mcp

* fix and expand tests

* mcp factory

* changes

* fixes

* test fixes

---------

Co-authored-by: freyn6 <freyn6@gene.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants