Skip to content

Commit 0662b14

Browse files
feat(mcp-host-config): implement LMStudioAdapter
Task 2.2.4.1: LMStudio Adapter - Create LMStudioAdapter with same validation as Claude/Cursor - Uses LMSTUDIO_FIELDS for field support (includes 'type' field) - Register in AdapterRegistry for host name 'lmstudio'
1 parent d97b99e commit 0662b14

File tree

3 files changed

+90
-7
lines changed

3 files changed

+90
-7
lines changed

hatch/mcp_host_config/adapters/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from hatch.mcp_host_config.adapters.cursor import CursorAdapter
1111
from hatch.mcp_host_config.adapters.gemini import GeminiAdapter
1212
from hatch.mcp_host_config.adapters.kiro import KiroAdapter
13+
from hatch.mcp_host_config.adapters.lmstudio import LMStudioAdapter
1314
from hatch.mcp_host_config.adapters.registry import AdapterRegistry, get_adapter, get_default_registry
1415
from hatch.mcp_host_config.adapters.vscode import VSCodeAdapter
1516

@@ -27,6 +28,7 @@
2728
"CursorAdapter",
2829
"GeminiAdapter",
2930
"KiroAdapter",
31+
"LMStudioAdapter",
3032
"VSCodeAdapter",
3133
]
3234

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
"""LM Studio adapter for MCP host configuration.
2+
3+
LM Studio follows the Cursor/Claude format with the same field set.
4+
"""
5+
6+
from typing import Any, Dict, FrozenSet
7+
8+
from hatch.mcp_host_config.adapters.base import AdapterValidationError, BaseAdapter
9+
from hatch.mcp_host_config.fields import LMSTUDIO_FIELDS
10+
from hatch.mcp_host_config.models import MCPServerConfig
11+
12+
13+
class LMStudioAdapter(BaseAdapter):
14+
"""Adapter for LM Studio MCP host.
15+
16+
LM Studio uses the same configuration format as Claude/Cursor:
17+
- Supports 'type' field for transport discrimination
18+
- Requires exactly one transport (command XOR url)
19+
"""
20+
21+
@property
22+
def host_name(self) -> str:
23+
"""Return the host identifier."""
24+
return "lmstudio"
25+
26+
def get_supported_fields(self) -> FrozenSet[str]:
27+
"""Return fields supported by LM Studio."""
28+
return LMSTUDIO_FIELDS
29+
30+
def validate(self, config: MCPServerConfig) -> None:
31+
"""Validate configuration for LM Studio.
32+
33+
Same rules as Claude: exactly one transport required.
34+
"""
35+
has_command = config.command is not None
36+
has_url = config.url is not None
37+
has_http_url = config.httpUrl is not None
38+
39+
# LM Studio doesn't support httpUrl
40+
if has_http_url:
41+
raise AdapterValidationError(
42+
"httpUrl is not supported (use 'url' for remote servers)",
43+
field="httpUrl",
44+
host_name=self.host_name
45+
)
46+
47+
# Must have exactly one transport
48+
if not has_command and not has_url:
49+
raise AdapterValidationError(
50+
"Either 'command' (local) or 'url' (remote) must be specified",
51+
host_name=self.host_name
52+
)
53+
54+
if has_command and has_url:
55+
raise AdapterValidationError(
56+
"Cannot specify both 'command' and 'url' - choose one transport",
57+
host_name=self.host_name
58+
)
59+
60+
# Validate type consistency if specified
61+
if config.type is not None:
62+
if config.type == "stdio" and not has_command:
63+
raise AdapterValidationError(
64+
"type='stdio' requires 'command' field",
65+
field="type",
66+
host_name=self.host_name
67+
)
68+
if config.type in ("sse", "http") and not has_url:
69+
raise AdapterValidationError(
70+
f"type='{config.type}' requires 'url' field",
71+
field="type",
72+
host_name=self.host_name
73+
)
74+
75+
def serialize(self, config: MCPServerConfig) -> Dict[str, Any]:
76+
"""Serialize configuration for LM Studio format."""
77+
self.validate(config)
78+
return self.filter_fields(config)
79+

hatch/mcp_host_config/adapters/registry.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,42 +12,44 @@
1212
from hatch.mcp_host_config.adapters.cursor import CursorAdapter
1313
from hatch.mcp_host_config.adapters.gemini import GeminiAdapter
1414
from hatch.mcp_host_config.adapters.kiro import KiroAdapter
15+
from hatch.mcp_host_config.adapters.lmstudio import LMStudioAdapter
1516
from hatch.mcp_host_config.adapters.vscode import VSCodeAdapter
1617

1718

1819
class AdapterRegistry:
1920
"""Registry for MCP host configuration adapters.
20-
21+
2122
The registry provides:
2223
- Host name to adapter mapping
2324
- Factory method to get adapters by host name
2425
- Registration of custom adapters
2526
- List of all supported hosts
26-
27+
2728
Example:
2829
>>> registry = AdapterRegistry()
2930
>>> adapter = registry.get_adapter("claude-desktop")
3031
>>> adapter.host_name
3132
'claude-desktop'
32-
33+
3334
>>> registry.get_supported_hosts()
34-
['claude-code', 'claude-desktop', 'codex', 'cursor', 'gemini', 'kiro', 'vscode']
35+
['claude-code', 'claude-desktop', 'codex', 'cursor', 'gemini', 'kiro', 'lmstudio', 'vscode']
3536
"""
36-
37+
3738
def __init__(self):
3839
"""Initialize the registry with default adapters."""
3940
self._adapters: Dict[str, BaseAdapter] = {}
4041
self._register_defaults()
41-
42+
4243
def _register_defaults(self) -> None:
4344
"""Register all built-in adapters."""
4445
# Claude variants
4546
self.register(ClaudeAdapter(variant="desktop"))
4647
self.register(ClaudeAdapter(variant="code"))
47-
48+
4849
# Other hosts
4950
self.register(VSCodeAdapter())
5051
self.register(CursorAdapter())
52+
self.register(LMStudioAdapter())
5153
self.register(GeminiAdapter())
5254
self.register(KiroAdapter())
5355
self.register(CodexAdapter())

0 commit comments

Comments
 (0)