Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ jobs:
type=ref,event=tag
type=raw,value=latest,enable={{is_default_branch}}
type=raw,value=${{ steps.tag.outputs.VERSION }}
labels: |
org.opencontainers.image.title=mcp-optimizer
org.opencontainers.image.description=MCP Optimizer - Intelligent MCP server orchestration
org.opencontainers.image.vendor=Stacklok
org.opencontainers.image.source=https://github.com/${{ github.repository }}
org.opencontainers.image.version=${{ steps.tag.outputs.VERSION }}
org.opencontainers.image.revision=${{ github.sha }}
org.opencontainers.image.created=${{ github.event.head_commit.timestamp }}

- name: Build and push container
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
Expand All @@ -59,6 +67,8 @@ jobs:
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
provenance: true
sbom: true

- name: Install Cosign
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
Expand Down
20 changes: 16 additions & 4 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,24 @@ tasks:
deps:
- install

run-container:
desc: Run the application in a container
build:
desc: Build multi-architecture container image
cmds:
- docker build -t mcp-optimizer .
- docker buildx create --name mcp-optimizer-builder --use --bootstrap || docker buildx use mcp-optimizer-builder
- docker buildx build --platform linux/amd64,linux/arm64 -t mcp-optimizer:latest --load .
- docker rm -f mcp-optimizer-container || true

build-local:
desc: Build container image for local architecture only (faster for development)
cmds:
- docker build -t mcp-optimizer:latest .
- docker rm -f mcp-optimizer-container || true
- docker run --network host --name mcp-optimizer-container mcp-optimizer

run-container:
desc: Run the application in a container (builds for local arch only)
cmds:
- task: build-local
- docker run --network host --name mcp-optimizer-container mcp-optimizer:latest

install-hooks:
desc: Install git hooks for the project
Expand Down
52 changes: 49 additions & 3 deletions src/mcp_optimizer/mcp_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ async def _execute_with_session(self, operation: Callable[[ClientSession], Await
proxy_mode = self._determine_proxy_mode()

logger.info(
f"Using {proxy_mode} client for workload",
f"Using {proxy_mode} client for workload '{self.workload.name}'",
workload=self.workload.name,
proxy_mode_field=self.workload.proxy_mode,
url=self.workload.url,
Expand Down Expand Up @@ -173,16 +173,42 @@ async def _execute_streamable_session(
self, operation: Callable[[ClientSession], Awaitable]
) -> Any:
"""Execute operation with streamable HTTP session."""
logger.debug(
f"Establishing streamable HTTP session for workload '{self.workload.name}'",
workload=self.workload.name,
url=self.workload.url,
)
async with streamablehttp_client(self.workload.url) as (read_stream, write_stream, _):
async with ClientSession(read_stream, write_stream) as session:
logger.info(
f"Initializing MCP session for workload '{self.workload.name}'",
workload=self.workload.name,
)
await session.initialize()
logger.debug(
f"MCP session initialized successfully for workload '{self.workload.name}'",
workload=self.workload.name,
)
return await operation(session)

async def _execute_sse_session(self, operation: Callable[[ClientSession], Awaitable]) -> Any:
"""Execute operation with SSE session."""
logger.debug(
f"Establishing SSE session for workload '{self.workload.name}'",
workload=self.workload.name,
url=self.workload.url,
)
async with sse_client(self.workload.url) as (read_stream, write_stream):
async with ClientSession(read_stream, write_stream) as session:
logger.info(
f"Initializing MCP session for workload '{self.workload.name}'",
workload=self.workload.name,
)
await session.initialize()
logger.debug(
f"MCP session initialized successfully for workload '{self.workload.name}'",
workload=self.workload.name,
)
return await operation(session)

async def list_tools(self) -> ListToolsResult:
Expand All @@ -192,7 +218,16 @@ async def list_tools(self) -> ListToolsResult:
Returns:
ListToolsResult: Available tools from the MCP server
"""
return await self._execute_with_session(lambda session: session.list_tools())
logger.debug(
f"Listing tools for workload '{self.workload.name}'", workload=self.workload.name
)
result = await self._execute_with_session(lambda session: session.list_tools())
logger.debug(
f"Retrieved {len(result.tools)} tools from workload '{self.workload.name}'",
workload=self.workload.name,
tool_count=len(result.tools),
)
return result

async def call_tool(self, tool_name: str, arguments: dict[str, Any]) -> CallToolResult:
"""
Expand All @@ -205,6 +240,17 @@ async def call_tool(self, tool_name: str, arguments: dict[str, Any]) -> CallTool
Returns:
CallToolResult: Result of the tool execution
"""
return await self._execute_with_session(
logger.debug(
f"Calling tool '{tool_name}' on workload '{self.workload.name}'",
workload=self.workload.name,
tool_name=tool_name,
)
result = await self._execute_with_session(
lambda session: session.call_tool(tool_name, arguments)
)
logger.debug(
f"Tool '{tool_name}' call completed on workload '{self.workload.name}'",
workload=self.workload.name,
tool_name=tool_name,
)
return result
Loading