KINTO Tech Blog
SRE

MCPを使ってNew RelicのデータにSlackからアクセスしよう!

Cover Image for MCPを使ってNew RelicのデータにSlackからアクセスしよう!

はじめに

この記事は New Relic Advent Calendar 2025 の24日目の記事です。

こんにちは、SREチームのおさないです。本記事では先日発表されたNew Relic MCPサーバーをSlackから利用できるようにする方法について紹介します。

MCPサーバー自体はClaudeなどの対話型AIやCodex, Kiroなどのコーディングエージェントなど、さまざまなインターフェースで利用することができますが、Slackというインターフェースで使えるようにすることで、New Relicのデータ利活用の幅をさらに広げられると感じています。

構成図

今回は以下のような構成でSlackからNew Relic MCPを利用します。


今回作成する構成図

SlackでBotにメンションして質問することで、回答に必要な情報をNew Relic MCPを使って取得してスレッドに返信してくれます。

構築手順

New Relic APIキーの発行

New RelicのAPI Keysのページに遷移して、Create a keyボタンを押下します。
Key TypeはUserを選択して作成し、発行されたAPIキーを控えておきます。

Slack Appの作成と設定 - その1

まずはSlack Appを作成します。
Slack Appの作成方法についてはさまざまな記事があるため詳細な説明は省きますが、こちらからFrom scratchでSlack Appを作成してください。
作成したらOAuth & Permissionsのページに遷移し、Bot Token Scopesにchat:writeの権限を追加します。
そしてInstall Appから対象のSlackワークスペースにインストールし、発行されたBot User OAuth Tokenを控えておきます。

アプリケーションの構築

今回はAWS SAMStrands Agentsを使って構築します。私が構築した際のSAM CLIのバージョンは1.149.0でした。
以下にtemplate.yamlとそれぞれのLambda関数のコード、requirements.txtを記載します。

template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Globals:
  Function:
    Timeout: 120

Resources:
  SlackEventHandlerFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: slack-event-handler-function
      CodeUri: src/
      Handler: slack_event_handler.lambda_handler
      Runtime: python3.14
      Role: !GetAtt SlackEventHandlerFunctionRole.Arn
      SnapStart:
        ApplyOn: PublishedVersions
      AutoPublishAlias: SnapStart
      Architectures:
      - x86_64
      Events:
        NewRelicBot:
          Type: Api
          Properties:
            Path: /event
            Method: post
      Environment:
        Variables:
          SQS_QUEUE_URL: !GetAtt SlackMessageSQS.QueueUrl

  SlackEventHandlerFunctionPermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !Ref SlackEventHandlerFunction
      Principal: apigateway.amazonaws.com
  SlackEventHandlerFunctionRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: slack-event-handler-function-role
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
        - arn:aws:iam::aws:policy/AmazonSQSFullAccess

  SlackAIExecutorFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: slack-ai-executor-function
      CodeUri: src/
      Handler: slack_ai_executor.lambda_handler
      Runtime: python3.14
      MemorySize: 256
      Role: !GetAtt SlackAIExecutorFunctionRole.Arn
      Architectures:
      - x86_64
      Events:
        SlackMessageSQS:
          Type: SQS
          Properties:
            Queue: !GetAtt SlackMessageSQS.Arn
            BatchSize: 1
            Enabled: true
      Environment:
        Variables:
          NEW_RELIC_API_KEY: '' # 発行したNew Relic APIキーを設定
          SLACK_BOT_TOKEN: '' # 発行したSlack Bot User OAuth Tokenを設定

  SlackAIExecutorFunctionRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: slack-ai-executor-function-role
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
        - arn:aws:iam::aws:policy/service-role/AWSLambdaSQSQueueExecutionRole
        - arn:aws:iam::aws:policy/AmazonBedrockFullAccess

  SlackMessageSQS:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: slack-message-sqs.fifo
      FifoQueue: true
      VisibilityTimeout: 180
      MessageRetentionPeriod: 600
      ContentBasedDeduplication: true
      DeduplicationScope: messageGroup
      RedrivePolicy:
        deadLetterTargetArn: !GetAtt SlackMessageDLQ.Arn
        maxReceiveCount: 2

  SlackMessageDLQ:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: slack-message-dlq.fifo
      FifoQueue: true
      VisibilityTimeout: 180
      MessageRetentionPeriod: 604800

※ NEW_RELIC_API_KEY, SLACK_BOT_TOKENは必要に応じてパラメータストアから取得するなどの方法で設定してください。

slack_event_handler.py
import json
import boto3
import hashlib
import os

sqs = boto3.client('sqs')
queue_url = os.environ.get('SQS_QUEUE_URL')

def lambda_handler(event, context):
    body = json.loads(event['body'])

    if body.get('challenge') is not None:
        challenge = body['challenge']
        return response('200', challenge)

    if body['event'].get('edited') is not None:
        return response('200', 'edited event ignored')

    if body['event']['type'] == 'app_mention':
        ts = body['event']['ts']
        channel = body['event']['channel']
        thread_ts = body['event'].get('thread_ts')
        text = body['event'].get('text')
        if text is None:
            return response('200', 'no text field')

        payload = {
            'text': text,
            'channel': channel,
            'ts': thread_ts if thread_ts is not None else ts,
        }

        sqs.send_message(
            QueueUrl=queue_url,
            MessageBody=json.dumps(payload),
            MessageGroupId=channel,
            MessageDeduplicationId=hashlib.md5(text.encode()).hexdigest()
        )
    return response('200', 'ok')

def response(status_code, body):
    return {
        'statusCode': status_code,
        'body': body,
        'headers': {
            'Content-Type': 'text/plain',
        },
    }
  • 多重起動を防ぐために、同じ文言を送信した場合はSQSの重複排除機能によりメッセージが破棄されるようにしています
  • textには<@U12345678>のようにSlack AppのメンバーIDが含まれるため、SQSにメッセージを送信する前に必要に応じてトリミング処理を追加してください。
slack_ai_executor.py
import json
import os
from contextlib import asynccontextmanager
from mcp.client.streamable_http import streamable_http_client, create_mcp_http_client
from strands.tools.mcp.mcp_client import MCPClient
from strands import Agent
from strands.models import BedrockModel
from slack_sdk import WebClient


SYSTEM_PROMPT = """
あなたはNew Relicのデータを駆使して開発者をサポートする優秀なエンジニアです。
"""

@asynccontextmanager
async def _nr_transport():
    async with create_mcp_http_client(
        headers={
            "api-key": os.environ.get('NEW_RELIC_API_KEY'),
            "include-tags": "discovery,alerting,data-access,incident-response,performance-analytics,advanced-analysis",
        }
    ) as http_client:
        async with streamable_http_client(
            url="https://mcp.newrelic.com/mcp/",
            http_client=http_client,
            terminate_on_close=True,
        ) as streams:
            yield streams


nr_mcp_client = MCPClient(_nr_transport)
slack_client = WebClient(token=os.environ.get('SLACK_BOT_TOKEN'))
model_id = "jp.anthropic.claude-sonnet-4-5-20250929-v1:0"

def lambda_handler(event, context):
    body = json.loads(event['Records'][0]['body'])

    text = body['text']
    channel = body['channel']
    ts = body['ts']

    with nr_mcp_client:
        tools = nr_mcp_client.list_tools_sync()
        
        model = BedrockModel(model_id=model_id, region_name='ap-northeast-1', temperature=0)
        agent = Agent(system_prompt=SYSTEM_PROMPT, tools=tools, model=model)

        response = agent(text)
        post_response = slack_client.chat_postMessage(channel=channel, thread_ts=ts, text=str(response))
        if post_response['ok']:
            return True
        else:
            return False
requirements.txt
annotated-types==0.7.0
anyio==4.12.0
attrs==25.4.0
boto3==1.42.13
botocore==1.42.13
certifi==2025.11.12
cffi==2.0.0
click==8.3.1
cryptography==46.0.3
docstring_parser==0.17.0
h11==0.16.0
httpcore==1.0.9
httpx==0.28.1
httpx-sse==0.4.3
idna==3.11
importlib_metadata==8.7.0
jmespath==1.0.1
jsonschema==4.25.1
jsonschema-specifications==2025.9.1
mcp==1.24.0
opentelemetry-api==1.39.1
opentelemetry-instrumentation==0.60b1
opentelemetry-instrumentation-threading==0.60b1
opentelemetry-sdk==1.39.1
opentelemetry-semantic-conventions==0.60b1
packaging==25.0
pycparser==2.23
pydantic==2.12.5
pydantic-settings==2.12.0
pydantic_core==2.41.5
PyJWT==2.10.1
python-dateutil==2.9.0.post0
python-dotenv==1.2.1
python-multipart==0.0.21
referencing==0.37.0
rpds-py==0.30.0
s3transfer==0.16.0
six==1.17.0
slack_sdk==3.39.0
sse-starlette==3.0.4
starlette==0.50.0
strands-agents==1.20.0
typing-inspection==0.4.2
typing_extensions==4.15.0
urllib3==2.6.2
uvicorn==0.38.0
watchdog==6.0.0
wrapt==1.17.3
zipp==3.23.0

※仮想環境でslack_sdkとstrands-agentsをインストールしてpip freezeしたもの

この内容でsam buildおよびsam deployを実行してデプロイします。
デプロイしたらAWSコンソールから作成されたAPI Gatewayのステージを確認し、作成したエンドポイントのURLを控えておきます。
例: https://xxxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/event

Slack Appの設定 - その2

先ほど作成したSlack Appの設定画面に戻り、Event Subscriptionsのページに遷移します。

  • Enable EventsをONにして、Request URLに先ほど控えたAPI GatewayのエンドポイントURLを設定
    • Verifiedと表示されればOKです
  • Subscribe to bot eventsのAdd Bot User Eventからapp_mentionを追加して保存
    • OAuth & PermissionsのBot Token Scopesにapp_mentions:readの権限が自動で追加されます

※変更を保存したら、再度Slackワークスペースにインストールしてください

動作確認

以上で構築は完了です!
Slackの任意のチャンネルに作成したSlack Appを追加してメンションを飛ばしてみましょう。質問内容にもよりますが、1分程度で返信がくるはずです。


Slackでの動作イメージ

MCPの機能はNew Relic MCP ツールリファレンスで確認できます。ツールを組み合わせることでさまざまな活用ができそうです。

  • 日頃ダッシュボードで確認している項目をSlack Reminderで定期的にBotにメンションして、要約してもらう
  • アラート発生時のメッセージでBotをメンションに含めることで自動で一次調査させる
  • 特定のセッショントレースIDでのユーザーの行動をまとめてレポートを作成させる

おわりに

New Relic MCPサーバーは本記事執筆時点ではPublic Previewの段階ですが、十分に実用的なレベルで利用でき、今後のアップデートが非常に楽しみです!

また、この仕組みはNew RelicのMCPサーバーに限らず、他のMCPサーバーに対しても応用可能なものになっているので、普段活用しているMCPサーバーを使いやすいインターフェースで使えるようにしてみてはいかがでしょうか?

Facebook

関連記事 | Related Posts

We are hiring!

【クラウドプラットフォームエンジニア】プラットフォームG/東京・大阪・福岡

プラットフォームグループについてAWS を中心とするインフラ上で稼働するアプリケーション運用改善のサポートを担当しています。

【カスタマーサクセスエンジニア】プラットフォームG/東京・大阪・福岡

プラットフォームグループについてAWS を中心とするインフラ上で稼働するアプリケーション運用改善のサポートを担当しています。

イベント情報