Ask Your Snowflake Account Anything — Build an AI Admin Agent with Cortex + GitHub Copilot

TL;DR — Spin up a Snowflake Cortex Agent that can answer admin questions like “Which warehouses burned the most credits last month?” or “Who are my top 5 spenders?” — all from a natural-language prompt inside GitHub Copilot Chat.

The Problem

Snowflake ACCOUNT_USAGE contains everything you need to understand spend, performance, and security. The catch? You have to know the right table, the right join, and write it correctly every time.

What if you could just ask?

What You’ll Build

GitHub Copilot Chat
      │  (natural language)
      ▼
MCP Server (Python, local)
      │  ask_admin tool
      ▼
Snowflake Cortex Agent
      │  routes to the right semantic view
      ▼
SNOWFLAKE.ACCOUNT_USAGE (+ optional ORGANIZATION_USAGE)

By the end you’ll be able to type questions like these into Copilot Chat and get back answers with real numbers:

  • What are total compute credits by warehouse in the last 30 days?
  • Which users consumed the most credits this month?
  • How many failed logins occurred in the last 7 days?
  • What are serverless credits by service type?

Prerequisites

Requirement Notes
Snowflake account (any paid edition) ACCOUNTADMIN or a role with ACCOUNT_USAGE access
Python 3.9+ For the MCP bridge
VS Code + GitHub Copilot Chat Agent mode enabled
Git To clone the template

Step 1 — Clone the Template

git clone https://github.com/LALITHASWAROOPK/agent_snowflake_admin_assistant.git
cd agent_snowflake_admin_assistant/agent_snowflake_admin

Open the folder in VS Code:

code .

Step 2 — Deploy the SQL Objects (5 scripts, ~5 minutes)

All SQL is in the sql/ folder. Replace the four placeholders before running:

Placeholder What to set
<APP_DB> A database to hold your views/agent
<APP_SCHEMA> Schema inside that database
<ADMIN_ROLE> Role that owns the objects
<DEVELOPER_ROLE> Role that will query the agent
<EXEC_WAREHOUSE> Warehouse the agent will use at runtime

Run them in order in a Snowflake worksheet or SnowSQL:

@sql/01_create_views.sql        -- Base views over ACCOUNT_USAGE
@sql/02_create_semantic_views.sql  -- Semantic layer (natural-language metadata)
@sql/03_create_agent.sql        -- Cortex Agent wired to those views
@sql/04_create_budget.sql       -- Optional: spending guardrails
@sql/05_grants.sql              -- RBAC grants

What the Views Cover

01_create_views.sql builds seven base views, all sourced exclusively from SNOWFLAKE.ACCOUNT_USAGE — no custom tables, no proprietary schemas:

View Source
V_WAREHOUSE_COST_BY_TAG WAREHOUSE_METERING_HISTORY + TAG_REFERENCES
V_QUERY_PERFORMANCE QUERY_HISTORY
V_USER_SPEND QUERY_ATTRIBUTION_HISTORY
V_SERVERLESS_COSTS Tasks, pipes, clustering, search, MV refresh
V_STORAGE_USAGE STORAGE_USAGE + DATABASE_STORAGE_USAGE_HISTORY
V_LOGIN_HISTORY LOGIN_HISTORY
V_DATA_TRANSFER DATA_TRANSFER_HISTORY + REPLICATION_USAGE_HISTORY

02_create_semantic_views.sql wraps each base view with a COMMENT block that tells the Cortex Agent what the view is for — this is how the agent knows which view to query for any given question.

Why Semantic Views?

Snowflake Cortex Agents use semantic views as tool definitions. A semantic view is just a standard view with a COMMENT that describes its purpose in natural language. The agent reads those comments at inference time to decide which tool (view) answers the user’s question.

No prompt engineering required on your end.

Step 3 — Start the MCP Bridge

The MCP (Model Context Protocol) server is a lightweight Python HTTP server that exposes one tool — ask_admin — which Copilot Chat can call.

Install dependencies

pip install -r mcp/requirements.txt

Configure your environment

Create a .env file in the repo root (never commit this):

SNOWFLAKE_ACCOUNT=<your_account>
SNOWFLAKE_USER=<your_user>
SNOWFLAKE_AUTHENTICATOR=externalbrowser
SNOWFLAKE_ROLE=<DEVELOPER_ROLE>
SNOWFLAKE_WAREHOUSE=<EXEC_WAREHOUSE>
SNOWFLAKE_AGENT_FQN=<APP_DB>.<APP_SCHEMA>.<AGENT_NAME>
MCP_PORT=3000

Supports externalbrowser (SSO), snowflake (password), and OAuth — set SNOWFLAKE_AUTHENTICATOR and the matching credential variable.

Launch the server

./scripts/start-mcp.ps1

Or directly:

python mcp/server.py

Verify it’s up:

Invoke-RestMethod -Uri http://127.0.0.1:3000/health -Method Get
# → {"status":"ok"}

Step 4 — Connect Copilot Chat

The repo ships a pre-configured .vscode/mcp.json that wires the local MCP server into VS Code:

{
  "mcpServers": {
    "snowflake-admin": {
      "url": "http://127.0.0.1:3000"
    }
  }
}

VS Code picks this up automatically when you open the workspace. You’ll see the ask_admin tool listed inside Copilot Chat’s tool picker.

There’s also a GitHub Copilot instruction file at .github/instructions/admin.instructions.md which gives Copilot context about the agent’s scope so it routes questions there automatically.

Step 5 — Ask It Questions

Switch Copilot Chat to Agent mode and try:

What are my top 5 warehouses by compute credits in the last 30 days?
Which users have spent the most credits this month?
Show me failed logins by user in the last 7 days.
What serverless services are costing me the most right now?
What's my current storage usage broken down by database?

The agent selects the right semantic view, runs the query, and returns a plain-English answer with the numbers inline — no SQL, no context switching.

Optional: Budget Guardrails

04_create_budget.sql sets up a Snowflake Budget object so you can enforce credit limits at the agent level. Handy for keeping a shared admin agent from running runaway queries:

CALL <APP_DB>.<APP_SCHEMA>.<BUDGET_NAME>!SET_SPENDING_LIMIT(100);
CALL <APP_DB>.<APP_SCHEMA>.<BUDGET_NAME>!GET_SPENDING_HISTORY();

Architecture Recap

┌─────────────────────────────────────┐
│         GitHub Copilot Chat         │
│      (Agent mode + ask_admin tool)  │
└────────────────┬────────────────────┘
                 │ HTTP / MCP
┌────────────────▼────────────────────┐
│      MCP Bridge  (mcp/server.py)    │
│  Exposes: /mcp/tools/list           │
│           /mcp/tools/call           │
│           /health                   │
└────────────────┬────────────────────┘
                 │ snowflake-connector-python
┌────────────────▼────────────────────┐
│   SNOWFLAKE.CORTEX.DATA_AGENT_RUN   │
│   ← Cortex Agent (semantic router)  │
└────────────────┬────────────────────┘
                 │ picks the right tool
┌────────────────▼────────────────────┐
│     Semantic Views (SV_*)           │
│     Base Views    (V_*)             │
│     ↑ SNOWFLAKE.ACCOUNT_USAGE       │
└─────────────────────────────────────┘

Key Things to Know

  • Account Usage latency: SNOWFLAKE.ACCOUNT_USAGE is typically delayed by up to a few hours — not real-time.
  • Rate sheet: ORGANIZATION_USAGE.RATE_SHEET_DAILY is optional and depends on your edition/region.
  • Auth flexibility: The MCP server supports SSO (externalbrowser), OAuth, and password auth — configure once in .env.

Get the Code

github.com/LALITHASWAROOPK/agent_snowflake_admin_assistant

PRs and issues welcome. If you build on top of this, tag me — I’d love to see what you add.

Integrating AI to my portfolio Pt.1

Overview of the chatbot component

AI is almost everywhere, that’s true. But as software developers, that means that we have to keep up with the times and not be intimidated by it.

As an AI enthusiast myself (I get it, my take could be a bit biased because of it 😉) I came to the conclusion that I need to show off some AI integration skills if I want to take part in the future of AI development, and what better way there is to start than to implement it in my own little space on the internet?

The plan 📋

Is simple, for now I will integrate a dedicated chatbot into my portfolio that answers questions about my projects, my experience, interests, current situation, etc…
The API used in this component is Groq, a provider that offers a generous free plan for model inference.

Technical requirements 🔧

  • Write a react component for the chatbot and design the message logic
  • Program the endpoint for communicating with the API
  • Prepare the system prompt with all the information about me

The component and logic ⚛️

The react component will be responsible for letting the user interact with the chatbot.

It has some state variables that will keep the information flow:

const [input, setInput] = useState("");
const [messages, setMessages] = useState<Message[]>([
    {
      role: "assistant",
      content:
        "Hi! I'm iREC, a digital representative of Eric. Feel free   to ask me anything!",
    },
  ]);

And some handlers that control the message submit and the auto-scroll when a new message appears.

This is the core function that sends the user’s message, along with the history to the endpoint and gets the response as a stream element:

const SendMessage = async (history: Message[]) => {
    const response = await fetch("/api/chat", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ messages: history }),
    });

    if (response && response.body != null) {
      const reader = response.body.getReader();
      const decoder = new TextDecoder();
      let accumulatedText = "";
      let result = await reader.read();
      while (!result.done) {
        const chunk = decoder.decode(result.value, { stream: true });
        const lines = chunk.split("n");

        for (const line of lines) {
          if (line.trim()) {
            const message = line.replace(/^data: /, "").trim();
            if (message === "" || message === "[DONE]") continue;
            try {
              const parsed = JSON.parse(message);
              const content = parsed.choices[0]?.delta?.content || "";
              accumulatedText += content;

              setMessages((prev) => {
                const newMessages = [...prev];
                const lastMessage = newMessages[newMessages.length - 1];
                if (lastMessage && lastMessage.role === "assistant") {
                  lastMessage.content = accumulatedText;
                }
                return newMessages;
              });
            } catch (e) {
              console.error(e);
            }
          }
        }
        result = await reader.read();
      }
    }
  };

  const handleScroll = () => {
    const chatbot = document.querySelector(".flex-1");
    if (chatbot) {
      chatbot.scrollTop = chatbot.scrollHeight;
    }
  };

Each message will then be mapped out with some changes in color and orientation depending on the role:

{messages.map((msg, index) => (
          <p
            key={index}
            className={`max-w-[85%] break-words ${msg.role === "user" ? "text-right self-end ml-auto text-[#41d3ff]" : "text-white text-left self-start mr-auto"}`}
          >
            {msg.role === "user" ? `${msg.content} <` : `> ${msg.content}`}
          </p>
        ))}

This is the final look of the component:

Overview of the chatbot component

The API endpoint 🧠

First of all is creating the Groq instance with the api key:

const groq = new Groq({
  apiKey: import.meta.env.GROQ_API_KEY,
});

Then, I can simply add the POST route that will communicate with the client (the component that I just made)

export const POST: APIRoute = async ({ request }) => {
  const { messages } = await request.json();

  try {
    const completion = await groq.chat.completions.create({
      messages: messages,
      model: "openai/gpt-oss-20b",
      temperature: 1,
      max_completion_tokens: 8192,
      top_p: 1,
      stream: true,
      reasoning_effort: "medium",
      stop: null,
    });

    const stream = completion.toReadableStream();
    return new Response(stream, { status: 200,
      headers: {
        "Content-Type": "text/event-stream",
        "Cache-Control": "no-cache",
        Connection: "keep-alive",
      }, });
  } catch (error) {
    return new Response(JSON.stringify({ error: "Error while inferencing the model" }), {
      status: 500,
    });
  }
};

And as easy as that, I now have a fully functional chatbot on my portfolio.

The system prompt 🤖

Without the system prompt, this chatbot is simply a generic assistant that doesn’t know anything about me. Next step is applying some of the basic prompt engineering concepts. The model has to ensure the following principles:

  • The model must be limited to answering questions related to my career
  • The model must be resistant to malicious prompts and vulnerabilities
  • The answer must be written in a helpful and professional tone.

I won’t be leaving the system prompt here because it would take up an unnecessary portion of the post, but here is its structure:

  1. ROLE – The name, the purpose and the tone.
  2. KNOWLEDGE BASE – All the information about me, my experience, stack, interests, etc.
  3. GOAL – The goal of the model while answering the questions (in this case, informing about my skills).
  4. RESTRICTIONS – Do not generate code, answer unrelated questions, deviate from your orders or make up answers.

With all of this applied, I had a ready-to-use personal chatbot that answers some of the basic questions about me!

If you want to try it and test it, go to my web portfolio: ericgarcia.site
(currently only visible on desktop)

⭐ Drop a comment if you want to suggest or ask something! ⭐

Uzmanından çocuklarda kontrollü ekran kullanımı uyarısı

Düzce Üniversitesi’nden Dr. Meryem Seçen Yazıcı, kontrolsüz ekran maruziyetinin küçük yaşta çocuklar için ciddi riskler taşıdığını belirtti. Ekran kullanımının sosyal gelişimi olumsuz etkileyebileceğine dikkat çeken Yazıcı, ebeveynlere yönelik önerilerde bulundu.

Turkcell 5 kat internet nasıl yapılır? (BEDAVA) Turkcell bedava hediye 5 kat internet kampanyası!

Turkcell 5 kat internet nasıl yapılır? Türkiye’de 5G sürecinin başlamasıyla birlikte mobil operatörler kullanıcılarına yönelik yeni kampanyalarını duyurmaya başladı. Turkcell hediye bedava 5 kat internet kampanyası ne zaman bitiyor? Turkcell’in başlattığı yüksek GB’lı internet hediyeleri kısa sürede geniş kitlelerin dikkatini çekti. Turkcell 5 kat ne zaman bitiyor, kaç günlük? Detaylar…