-
Notifications
You must be signed in to change notification settings - Fork 5
fix: intermediate Palette MCP server tutorial #86
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
85175b4
fix: add content for intermediate mcp tutorial
karl-cardenas-coding 1330315
chore: fix husky warning
karl-cardenas-coding 13d5556
chore: comment out agents
karl-cardenas-coding a41e5ca
chore: name update
karl-cardenas-coding a9f5de2
chore: cleanup
karl-cardenas-coding cdc9f75
chore: updates to flags
karl-cardenas-coding 242072f
chore: commented out pieces ready
karl-cardenas-coding 6e9b557
chore: updated README
karl-cardenas-coding d4ba6c0
ci: updated taskfile
karl-cardenas-coding 704a727
chore: update to not tag manually (#90)
karl-cardenas-coding 21ff2a8
chore: update function name
karl-cardenas-coding c0d51e1
chore: feedback
karl-cardenas-coding 27d291a
chore: minor updates
karl-cardenas-coding f4a9623
Apply suggestion from @addetz
addetz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,5 +11,7 @@ project { | |
| header_ignore = [ | ||
| # "vendors/**", | ||
| # "**autogen**", | ||
| ".venv", | ||
| "__pycache__" | ||
| ] | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1 @@ | ||
| #!/usr/bin/env sh | ||
| . "$(dirname -- "$0")/_/husky.sh" | ||
| npx --no -- commitlint --edit '' | ||
addetz marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| # Copyright (c) Spectro Cloud | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| version: "3" | ||
|
|
||
| dotenv: [".env"] | ||
|
|
||
| tasks: | ||
| init: | ||
| desc: Install dependencies and setup the project | ||
| cmds: | ||
| - echo "initializing npm dependencies" | ||
| - npm ci | ||
| - npx husky install | ||
|
|
||
| help: | ||
| desc: Display this help | ||
| cmds: | ||
| - task --list | ||
|
|
||
| build-docker: | ||
| desc: Build docker image | ||
| cmds: | ||
| - echo "Building docker image" | ||
| - | | ||
| docker build --build-arg PALETTE_VERSION=$PALETTE_VERSION \ | ||
| --build-arg PALETTE_CLI_VERSION=$PALETTE_CLI_VERION \ | ||
| --build-arg PALETTE_EDGE_VERSION=$PALETTE_EDGE_VERSION \ | ||
| --build-arg PACKER_VERSION=$PACKER_VERSION \ | ||
| --build-arg ORAS_VERSION=$ORAS_VERSION \ | ||
| --build-arg TERRAFORM_VERSION=$TERRAFORM_VERSION \ | ||
| --build-arg K9S_VERSION=$K9S_VERSION \ | ||
| -t tutorials . | ||
|
|
||
| license: | ||
| desc: Adds a license header to all files. Reference https://github.com/hashicorp/copywrite to learn more. | ||
| cmds: | ||
| - echo "Applying license headers..." | ||
| - copywrite headers |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| 3.12 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| # Integrate Palette MCP | ||
|
|
||
| This folder contains the demo code for the Integrate Palette MCP in an Agentic Workflow tutorial. The user will learn how to integrate Palette MCP into a LangChain agent workflow. | ||
|
|
||
| The workflow is as follows: | ||
| - Identify if a specific pack is present in your environment's cluster profiles and deployed clusters. | ||
| - If the pack is present, the workflow asks you what tags you want to apply to the cluster profiles containing the pack and any active clusters using cluster | ||
| profiles containing the pack. This allows you to more readily identify the cluster profiles and active clusters that are | ||
| using the pack. | ||
|
|
||
| ## Get Started | ||
|
|
||
| Follow the instructions in the tutorial to get started. | ||
|
|
||
| ## Environment Variables | ||
|
|
||
| | Variable | Description | Example | | ||
| | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | | ||
| | `OPENAI_API_KEY` | OpenAI API key. Required. | `sk-...` | | ||
| | `OPENAI_MODEL` | Default model used for all agents. Overridden per-agent by the model-specific variables below. | `gpt-4o` | | ||
| | `OPENAI_ACTIVE_CLUSTER_MODEL` | Model for the active cluster finder agent. Defaults to `gpt-5.4-nano`. Falls back to `OPENAI_MODEL`. | `gpt-4o-mini` | | ||
| | `OPENAI_REPORTER_MODEL` | Model for the reporter agent. Defaults to `gpt-5.4-mini`. Falls back to `OPENAI_MODEL`. | `gpt-4o-mini` | | ||
| | `OPENAI_TAGGING_MODEL` | Model for the tagging agent. Falls back to `OPENAI_MODEL`. | `gpt-4o-mini` | | ||
| | `PACK_NAME` | Target pack name to search for in cluster profiles. Can also be set via `--pack`. | `nginx` | | ||
| | `DEBUG` | Log level. Accepted values: `warn`, `info`, `debug`, `verbose`. Defaults to `info`. Can also be set via `--log-level`. | `debug` | | ||
| | `PALETTE_MCP_ENV_FILE` | Path to the env file passed to the Palette MCP container. Defaults to `~/.palette/.env-mcp`. | `/home/user/.palette/.env-mcp` | | ||
| | `PALETTE_MCP_KUBECONFIG_DIR` | Path to a local directory to bind-mount into the container as `/tmp/kubeconfig`. Omitted if not set. | `/home/user/.kube` | | ||
| | `PALETTE_MCP_IMAGE` | Palette MCP container image to use. Defaults to `public.ecr.aws/palette-ai/palette-mcp-server:latest`. | `public.ecr.aws/palette-ai/palette-mcp-server:v1.2.0` | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| version: "3" | ||
|
|
||
| dotenv: [".env", "../../../.env"] | ||
|
|
||
| tasks: | ||
| start-agent: | ||
| desc: "Start the Palette MCP LangChain agent. Usage: task start-agent -- --pack <name> [--model <model>]" | ||
| env: | ||
| DEBUG: "info" | ||
| PACK_NAME: "{{.PACK_NAME}}" | ||
| OPENAI_MODEL: "{{.OPENAI_MODEL}}" | ||
| cmds: | ||
| - uv run python main.py {{.CLI_ARGS}} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| # Copyright (c) Spectro Cloud | ||
| # SPDX-License-Identifier: Apache-2.0 |
98 changes: 98 additions & 0 deletions
98
ai/palette-mcp/integrate-palette-mcp/agents/active_cluster_agent.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| # Copyright (c) Spectro Cloud | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| """Palette-focused agent for active cluster mapping.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import importlib | ||
| import json | ||
| from typing import Any | ||
|
|
||
| from pydantic import BaseModel | ||
|
|
||
| from helpers import suppress_console_output | ||
|
|
||
| ACTIVE_CLUSTER_SYSTEM_PROMPT = ( | ||
| "You are a Palette active-cluster mapping specialist. " | ||
| "Use only Palette MCP tools to identify active clusters that use a provided set of cluster profile UIDs. " | ||
| "Return factual results only." | ||
| ) | ||
|
|
||
|
|
||
| class ActiveCluster(BaseModel): | ||
| uid: str | ||
| name: str | ||
| cluster_profile_uid: str | ||
| cluster_profile_name: str | ||
| evidence_field_path: str | ||
| evidence: str | ||
|
|
||
|
|
||
| class ActiveClusterOutput(BaseModel): | ||
| pack_name: str | ||
| target_profile_uids: list[str] | ||
| total_active_clusters_scanned: int | ||
| active_clusters_using_matched_profiles: list[ActiveCluster] | ||
| checked_active_cluster_uids: list[str] | ||
| notes: str | ||
|
|
||
|
|
||
| async def initialize_active_cluster_agent( | ||
| model: str, | ||
| mcp_tools: list, | ||
| ) -> Any: | ||
| from langchain.agents import create_agent | ||
| from langchain_openai import ChatOpenAI | ||
|
|
||
| checkpoint_module = importlib.import_module("langgraph.checkpoint.memory") | ||
| InMemorySaver = checkpoint_module.InMemorySaver | ||
|
|
||
| llm = ChatOpenAI(model=model) | ||
| return create_agent( | ||
| model=llm, | ||
| tools=mcp_tools, | ||
| system_prompt=ACTIVE_CLUSTER_SYSTEM_PROMPT, | ||
| response_format=ActiveClusterOutput, | ||
| checkpointer=InMemorySaver(), | ||
| ) | ||
|
|
||
|
|
||
| # async def invoke_active_cluster_agent( | ||
| # agent: Any, | ||
| # pack_name: str, | ||
| # matched_profiles_output: str, | ||
| # debug_level: str, | ||
| # run_id: str, | ||
| # ) -> str: | ||
| # hide_mcp_output = debug_level != "verbose" | ||
| # schema = json.dumps(ActiveClusterOutput.model_json_schema(), indent=2) | ||
| # active_cluster_prompt = ( | ||
| # f"Given this profile discovery result for pack '{pack_name}':\n" | ||
| # f"{matched_profiles_output}\n\n" | ||
| # "Required process:\n" | ||
| # "1) Extract matched profile UIDs from the input JSON.\n" | ||
| # "2) Call gather_or_delete_clusters with action='list' and active_only=true.\n" | ||
| # "3) For each active cluster uid from step 2, call gather_or_delete_clusters with action='get'.\n" | ||
| # "4) Match clusters using explicit profile UID fields only.\n" | ||
| # "5) If no clusters match, return an empty list and include every checked active cluster uid.\n\n" | ||
| # "Return a response that conforms to this JSON schema:\n" | ||
| # f"{schema}\n" | ||
| # ) | ||
| # run_config = { | ||
| # "configurable": {"thread_id": f"active-cluster:{pack_name.lower()}:{run_id}"} | ||
| # } | ||
| # with suppress_console_output(hide_mcp_output): | ||
| # result = await agent.ainvoke( | ||
| # {"messages": [{"role": "user", "content": active_cluster_prompt}]}, | ||
| # config=run_config, | ||
| # ) | ||
| # structured = result.get("structured_response") | ||
| # if isinstance(structured, ActiveClusterOutput): | ||
| # return structured.model_dump_json() | ||
| # messages = result.get("messages", []) | ||
| # for message in reversed(messages): | ||
| # content = getattr(message, "content", None) | ||
| # if isinstance(content, str) and content.strip(): | ||
| # return content | ||
| # return str(result) |
99 changes: 99 additions & 0 deletions
99
ai/palette-mcp/integrate-palette-mcp/agents/palette_profile_agent.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| # Copyright (c) Spectro Cloud | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| """Palette-focused agent for cluster profile discovery.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import importlib | ||
| import json | ||
| from typing import Any, Literal | ||
|
|
||
| from pydantic import BaseModel | ||
|
|
||
| from helpers import suppress_console_output | ||
|
|
||
| PROFILE_FINDER_SYSTEM_PROMPT = ( | ||
| "You are a Palette profile discovery specialist. " | ||
| "Use only Palette MCP tools to find cluster profiles that include a target pack. " | ||
| "First list cluster profiles, then inspect details when needed. " | ||
| "Always capture and return cluster profile scope. " | ||
| "Return factual results only." | ||
| ) | ||
|
|
||
|
|
||
| class MatchedProfile(BaseModel): | ||
| uid: str | ||
| name: str | ||
| scope: Literal["tenant", "project", "system", "unknown"] | ||
| pack_references: list[str] | ||
| evidence: str | ||
|
|
||
|
|
||
| class ProfileDiscoveryOutput(BaseModel): | ||
| pack_name: str | ||
| total_profiles_scanned: int | ||
| matched_profiles: list[MatchedProfile] | ||
| notes: str | ||
|
|
||
|
|
||
| # async def initialize_profile_finder_agent( | ||
| # model: str, | ||
| # mcp_tools: list, | ||
| # ) -> Any: | ||
| # from langchain.agents import create_agent | ||
| # from langchain_openai import ChatOpenAI | ||
|
|
||
| # checkpoint_module = importlib.import_module("langgraph.checkpoint.memory") | ||
| # InMemorySaver = checkpoint_module.InMemorySaver | ||
|
|
||
| # llm = ChatOpenAI(model=model) | ||
| # return create_agent( | ||
| # model=llm, | ||
| # tools=mcp_tools, | ||
| # system_prompt=PROFILE_FINDER_SYSTEM_PROMPT, | ||
| # response_format=ProfileDiscoveryOutput, | ||
| # checkpointer=InMemorySaver(), | ||
| # ) | ||
|
|
||
|
|
||
| # async def invoke_profile_finder_agent( | ||
| # agent: Any, | ||
| # pack_name: str, | ||
| # debug_level: str, | ||
| # run_id: str, | ||
| # ) -> str: | ||
| # hide_mcp_output = debug_level != "verbose" | ||
| # schema = json.dumps(ProfileDiscoveryOutput.model_json_schema(), indent=2) | ||
| # profile_finder_prompt = ( | ||
| # "Find all cluster profiles in Palette that use the pack named " | ||
| # f"'{pack_name}'. Use Palette MCP tools only.\n\n" | ||
| # "Required process:\n" | ||
| # "1) Call gather_or_delete_clusterprofiles with action='list', limit=50, and compact=false.\n" | ||
| # "2) Review the output and check if the pack name is in the list. Match pack name case-insensitively. If the pack present, add it to the list of cluster profiles to review. If it is not, do not add it to the list.\n" | ||
| # "3) For each matched profile, include scope from metadata.annotations.scope when available.\n" | ||
| # "4) If there is a continue token, use it to retrieve the next page of results. Do not stop until you have gathered all cluster profiles and there is no continue token. Repeat the process until you have gathered all cluster profiles.\n" | ||
| # "5) If scope is missing, set scope to 'unknown' and mention in notes.\n\n" | ||
| # "6) If the pack name is not in the list, return an empty list and mention in notes.\n" | ||
| # "Important:\n" | ||
| # "Make sure you reviewed all cluster profiles. There should be no continue token at the end of the process." | ||
| # "Return a response that conforms to this JSON schema:\n" | ||
| # f"{schema}\n" | ||
| # ) | ||
| # run_config = { | ||
| # "configurable": {"thread_id": f"profile-finder:{pack_name.lower()}:{run_id}"} | ||
| # } | ||
| # with suppress_console_output(hide_mcp_output): | ||
| # result = await agent.ainvoke( | ||
| # {"messages": [{"role": "user", "content": profile_finder_prompt}]}, | ||
| # config=run_config, | ||
| # ) | ||
| # structured = result.get("structured_response") | ||
| # if isinstance(structured, ProfileDiscoveryOutput): | ||
| # return structured.model_dump_json() | ||
| # messages = result.get("messages", []) | ||
| # for message in reversed(messages): | ||
| # content = getattr(message, "content", None) | ||
| # if isinstance(content, str) and content.strip(): | ||
| # return content | ||
| # return str(result) |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.