Snowflake Managed MCP Serverが気になるので調べてみた
はじめに
少し前、Tableau MCPを触ってみて面白い!Snowflakeでもやってみよう!
と思ったのに難しすぎて心が折れた私。
このたびSnowflake Managed MCPがパブリックプレビューになったということで、クイックスタートになぞってチャレンジしてみようと思います。
Tableau MCP楽しいぜのつぶやきがこれ。
ちなみに心折れたつぶやきはこれ↓
前提
参考にするクイックスタート
このたび、こちらのクイックスタートを参考にしてSnowflake Managed MCPを作ってみたいと思います。
MCP(Managed Containerized Process)ってなに?っていうのをこちらのクイックスタートの絵が分かりやすく示してくださっています。
「AIモデルと外部データソースやツール間のシームレスな統合」とのこと。

クイックスタートではCursorで問い合わせをするようなイメージですが、今回はClaude Desktopから問い合わせできるよう作ってみようと思います。
環境設定
Githubに載っているSetup.sqlをSQLワークシートに貼り付けて実行します。

個人アクセストークンの作成
こちらの個人ページの認証画面からPersonal Access Token (PAT)を作成します。



個人アクセストークンはコピーし、メモ帳などに貼り付けて画面を閉じます。
Cortex検索サービスを作成

右上の「作成」をクリックします






Snowflake Managed MCP サーバの作成
以下のSQLをワークスペースに貼り付けて実行します。
create or replace mcp server dash_mcp_db.data.dash_mcp_server from specification
$$
tools:
- name: "Support_Tickets_Search_Service"
identifier: "dash_mcp_db.data.support_tickets"
type: "CORTEX_SEARCH_SERVICE_QUERY"
description: "A tool that performs keyword and vector search over support tickets and call transcripts."
title: "Support Tickets"
$$;
Node.jsのインストール
こちらからNode.jsをインストールしてください。
Claude Desktopのインストール
Claude Desktopのインストーラーをダウンロードおよび実行し、Claude Desktopをインストールしてください。
Claude Desktopの接続設定ファイルを作成する
c:\mcpというフォルダを作り、snowflake-mcp-proxy.jsという名前でプロキシファイルを作ります。
Claude DesktopからのリクエストをSnowflake MCP Serverに転送する設定となります。
snowflake-mcp-proxy.js
#!/usr/bin/env node
const https = require('https');
const readline = require('readline');
const fs = require('fs');
const SNOWFLAKE_HOST = '<アカウントロケーター>.snowflakecomputing.com';
const MCP_PATH = '/api/v2/databases/DASH_MCP_DB/schemas/DATA/mcp-servers/dash_mcp_server';
const AUTH_TOKEN = 'Bearer <PATを登録したときにコピーしたトークン>';
const logFile = 'C:\\mcp\\debug.log';
function log(message) {
const timestamp = new Date().toISOString();
const logMessage = `${timestamp} - ${message}\n`;
fs.appendFileSync(logFile, logMessage);
console.error(logMessage);
}
// ツール名のマッピング(MCP名 -> Snowflake名)
const toolNameMap = {};
const reverseToolNameMap = {};
function sanitizeToolName(name) {
return name.replace(/\s+/g, '_').replace(/[^a-zA-Z0-9_-]/g, '');
}
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false
});
rl.on('line', (line) => {
log(`Received: ${line}`);
try {
const request = JSON.parse(line);
log(`Parsed request: ${JSON.stringify(request)}`);
const isNotification = request.method && request.method.startsWith('notifications/');
// tools/call の場合、ツール名を元に戻す
if (request.method === 'tools/call' && request.params && request.params.name) {
const sanitizedName = request.params.name;
const originalName = reverseToolNameMap[sanitizedName];
if (originalName) {
log(`Converting tool name: ${sanitizedName} -> ${originalName}`);
request.params.name = originalName;
}
}
const postData = JSON.stringify(request);
const options = {
hostname: SNOWFLAKE_HOST,
port: 443,
path: MCP_PATH,
method: 'POST',
headers: {
'Authorization': AUTH_TOKEN,
'Content-Type': 'application/json',
'Accept': 'application/json',
'User-Agent': 'Claude-MCP-Client/1.0',
'Content-Length': Buffer.byteLength(postData)
}
};
const req = https.request(options, (res) => {
log(`Response status: ${res.statusCode}`);
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
log(`Response body (raw): ${data}`);
if (res.statusCode === 202 || isNotification) {
log(`Notification acknowledged (status ${res.statusCode})`);
return;
}
if (res.statusCode !== 200) {
log(`Error: Non-200 status code: ${res.statusCode}`);
const errorResponse = {
jsonrpc: '2.0',
id: request.id,
error: {
code: -32603,
message: `Snowflake returned status ${res.statusCode}: ${data}`,
}
};
console.log(JSON.stringify(errorResponse));
return;
}
if (!data || data.trim() === '') {
const errorResponse = {
jsonrpc: '2.0',
id: request.id,
error: { code: -32603, message: 'Empty response from Snowflake' }
};
console.log(JSON.stringify(errorResponse));
return;
}
try {
const parsed = JSON.parse(data);
// tools/list レスポンスの場合、ツール名を修正してマッピングを保存
if (request.method === 'tools/list' && parsed.result && parsed.result.tools) {
parsed.result.tools = parsed.result.tools.map(tool => {
const originalName = tool.name;
const sanitizedName = sanitizeToolName(tool.name);
// マッピングを保存
toolNameMap[originalName] = sanitizedName;
reverseToolNameMap[sanitizedName] = originalName;
log(`Tool name mapping: "${originalName}" -> "${sanitizedName}"`);
return {
...tool,
name: sanitizedName
};
});
}
const compactJson = JSON.stringify(parsed);
log(`Sending to Claude: ${compactJson}`);
console.log(compactJson);
} catch (e) {
log(`JSON parse error: ${e.message}`);
const errorResponse = {
jsonrpc: '2.0',
id: request.id,
error: {
code: -32700,
message: `Invalid JSON response: ${e.message}`,
data: data.substring(0, 200)
}
};
console.log(JSON.stringify(errorResponse));
}
});
});
req.on('error', (error) => {
log(`Request error: ${error.message}`);
if (!isNotification) {
console.log(JSON.stringify({
jsonrpc: '2.0',
id: request.id,
error: { code: -32603, message: error.message }
}));
}
});
req.write(postData);
req.end();
} catch (error) {
log(`Parse error: ${error.message}`);
console.log(JSON.stringify({
jsonrpc: '2.0',
error: { code: -32700, message: `Parse error: ${error.message}` }
}));
}
});
log('Snowflake MCP Proxy started');
ファイル―設定-開発者

設定の編集を押すとエクスプローラ画面が開くので、
claude_desktop_config.json を開き下記のように設定します。
{
"mcpServers": {
"snowflake": {
"command": "node",
"args": ["C:\\mcp\\snowflake-mcp-proxy.js"]
}
}
}
ちなみに、Tableau MCPと併用する場合はこのような形で設定します。
これで完成です!
{
"mcpServers": {
"tableau": {
"command": "node",
"args": [jsファイルのパス],
"env": {
"TRANSPORT": "stdio",
"SERVER": "https://10ax.online.tableau.com/",
"SITE_NAME": "サイト名",
"PAT_NAME": "PAT名",
"PAT_VALUE": "PATシークレット"
}
},
"snowflake": {
"command": "node",
"args": ["C:\\mcp\\snowflake-mcp-proxy.js"]
}
}
}
Claude Desktopで問い合わせてみる
「サポートチケットを検索して、最近の問い合わせ内容を教えて」と問い合わせてみて、そのあと「グラフにして」とお願いしたらこのように表示されました!!

まとめ
Claude DesktopでSnowflake Managed MCPに接続して問い合わせができました!
ただ、接続部分でエラーが出て数時間ハマってしまいました。
こちらのブログの設定でうまくいくと思うのですが、エラーで動作しない等ありましたらご指摘ください。
ありがとうございました。