|
1 | 1 | """Define custom MCP tools for the Materials Project API.""" |
2 | 2 | from __future__ import annotations |
3 | 3 |
|
4 | | -from typing import Any |
5 | 4 | from urllib.parse import urljoin |
6 | 5 |
|
7 | 6 | import httpx |
8 | 7 | from fastmcp import FastMCP |
9 | | -from pydantic import BaseModel, Field, PrivateAttr |
10 | | - |
11 | | -from mp_api.client import MPRester |
12 | | -from mp_api.mcp import tools as mcp_tools |
13 | | - |
14 | | - |
15 | | -class MPMcp(BaseModel): |
16 | | - name: str = Field("Materials_Project_MCP") |
17 | | - client_kwargs: dict[str, Any] | None = Field(None) |
18 | | - _client: MPRester | None = PrivateAttr(None) |
19 | | - |
20 | | - @property |
21 | | - def client(self) -> MPRester: |
22 | | - # Always return JSON compliant output for MCP |
23 | | - if not self._client: |
24 | | - kwargs = { |
25 | | - **(self.client_kwargs or {}), |
26 | | - "use_document_model": False, |
27 | | - "monty_decode": False, |
28 | | - } |
29 | | - self._client = MPRester(**kwargs) |
30 | | - return self._client |
31 | | - |
32 | | - def mcp(self, **kwargs) -> FastMCP: |
33 | | - mcp = FastMCP(self.name, **kwargs) |
34 | | - |
35 | | - for attr in {x for x in dir(mcp_tools) if x.startswith("get")}: |
36 | | - mcp.tool(getattr(mcp_tools, attr)) |
37 | | - |
38 | | - return mcp |
39 | | - |
40 | | - def bootstrap_mcp(self, **kwargs) -> FastMCP: |
41 | | - """Bootstrap an MP API MCP only from the OpenAPI spec.""" |
42 | | - return FastMCP.from_openapi( |
43 | | - openapi_spec=httpx.get( |
44 | | - urljoin(self.client.endpoint, "openapi.json") |
45 | | - ).json(), |
46 | | - client=httpx.AsyncClient( |
47 | | - base_url=self.client.endpoint, |
48 | | - headers={"x-api-key": self.client.api_key}, |
49 | | - ), |
50 | | - name=self.name, |
51 | | - **kwargs, |
52 | | - ) |
| 8 | + |
| 9 | +from mp_api.mcp.tools import MPMcpTools |
| 10 | +from mp_api.mcp.utils import _NeedsMPClient |
| 11 | + |
| 12 | + |
| 13 | +def get_mcp() -> FastMCP: |
| 14 | + """MCP with finer depth of control over tool names.""" |
| 15 | + mp_mcp = FastMCP("Materials_Project_MCP") |
| 16 | + mcp_tools = MPMcpTools() |
| 17 | + for attr in {x for x in dir(mcp_tools) if x.startswith("get_")}: |
| 18 | + mp_mcp.tool(getattr(mcp_tools, attr)) |
| 19 | + return mp_mcp |
| 20 | + |
| 21 | + |
| 22 | +def get_bootstrap_mcp() -> FastMCP: |
| 23 | + """Bootstrap an MP API MCP only from the OpenAPI spec.""" |
| 24 | + client = _NeedsMPClient().client |
| 25 | + |
| 26 | + return FastMCP.from_openapi( |
| 27 | + openapi_spec=httpx.get(urljoin(client.endpoint, "openapi.json")).json(), |
| 28 | + client=httpx.AsyncClient( |
| 29 | + base_url=client.endpoint, |
| 30 | + headers={"x-api-key": client.api_key}, |
| 31 | + ), |
| 32 | + name="MP_OpenAPI_MCP", |
| 33 | + ) |
0 commit comments