Skip to content
Draft
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
44 changes: 44 additions & 0 deletions ai_agents/anthropic_mcp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
title: Anthropic MCP Sample
description: Simple usage of the Anthropic API via AutoKitteh
integrations: ["anthropic", "slack"]
categories: ["AI", "Productivity"]
---

# Anthropic MCP Sample

[![Start with AutoKitteh](https://autokitteh.com/assets/autokitteh-badge.svg)](https://app.autokitteh.cloud/template?name=ai_agents/anthropic_mcp)

This sample demonstrates how to interact with Anthropic's API using AutoKitteh, enabling you to leverage advanced language models for your automation workflows.

It includes functionality to send prompts to Anthropic's Claude models and process the responses.

API details:

- [Anthropic API documentation](https://docs.anthropic.com/claude/docs)
- [AutoKitteh Anthropic integration](https://docs.autokitteh.com/integrations/anthropic)

## How It Works

1. Sends a prompt to the Anthropic Claude API.
2. Receives and logs the model's response in the AutoKitteh session.

## Cloud Usage (Recommended)

1. Initialize your Anthropic connection through the AutoKitteh UI.
2. Copy the webhook trigger's URL (for the [Trigger Workflows](#trigger-workflows) section below):

- Hover over the trigger's (i) icon for the webhook you want to use.
- Click the copy icon next to the webhook URL for your selected trigger.
- (Detailed instructions
[here](https://docs.autokitteh.com/get_started/deployment#webhook-urls))

3. Set any required environment variables or project variables (such as your Anthropic API key).

## Trigger Workflows

`send_prompt`:

```shell
curl -i "${WEBHOOK_URL}" --url-query prompt="Your question for Claude"
```
24 changes: 24 additions & 0 deletions ai_agents/anthropic_mcp/autokitteh.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# This file is part of the MCP project, which integrates with Anthropic and Slack.

version: v1

project:
name: anthropic_mcp

connections:
- name: slack_conn
integration: slack

triggers:
- name: send_msg
event_type: message
connection: slack_conn
call: program.py:start_workflow

vars:
- name: ANTHROPIC_API_KEY
value: ""
- name: SERVER_URL
value: ""
- name: SLACK_CHANNEL
value: ""
96 changes: 96 additions & 0 deletions ai_agents/anthropic_mcp/program.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
"""MCPClient integration with Anthropic and Slack.

This module provides an MCPClient class that connects to a server.
Processes queries using Anthropic's API, and posts results to a Slack channel.
"""

import asyncio
from contextlib import AsyncExitStack
import os

from anthropic import Anthropic
import autokitteh
from autokitteh.slack import slack_client
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client


slack = slack_client("slack_conn")

ANTHROPIC_API_KEY = os.getenv("ANTHROPIC_API_KEY")
SERVER_URL = os.getenv("SERVER_URL")
SLACK_CHANNEL = os.getenv("SLACK_CHANNEL") # ID or name.


class MCPClient:
"""Client for MCP server queries using Anthropic, with results posted to Slack."""

def __init__(self):
self.session: ClientSession | None = None
self.exit_stack = AsyncExitStack()
self.anthropic = Anthropic(api_key=ANTHROPIC_API_KEY)

@autokitteh.activity
async def connect_to_server(self, server_url: str):
transport = await self.exit_stack.enter_async_context(
streamablehttp_client(server_url)
)
self.read, self.write, _ = transport
self.session = await self.exit_stack.enter_async_context(
ClientSession(self.read, self.write)
)
await self.session.initialize()
print(f"\nConnected to server at {server_url}")

@autokitteh.activity
async def process_query(self, query: str) -> str:
messages = [{"role": "user", "content": query}]
tools = (await self.session.list_tools()).tools
available_tools = [
{
"name": t.name,
"description": t.description,
"input_schema": t.inputSchema,
}
for t in tools
]

response = self.anthropic.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1000,
messages=messages,
tools=available_tools,
)

for content in response.content:
if content.type == "text":
slack.chat_postMessage(channel=SLACK_CHANNEL, text=content.text)
elif content.type == "tool_use":
result = await self.session.call_tool(content.name, content.input)
if result.content:
text = f"tool result: {result.content[0].text}\n"
slack.chat_postMessage(channel=SLACK_CHANNEL, text=text)
else:
text = "Tool returned no content."
slack.chat_postMessage(channel=SLACK_CHANNEL, text=text)

async def cleanup(self):
await self.exit_stack.aclose()


async def handle_mcp_request(query):
"""Handle a request to the MCP server with the given query."""
client = MCPClient()
try:
await client.connect_to_server(SERVER_URL)
await client.process_query(query)
except (ConnectionError, RuntimeError, ValueError) as e:
print(f"\nError: {str(e)}")
finally:
await client.cleanup()


def start_workflow(event):
"""Entrypoint for the MCPClient workflow."""
query = event.data.text
asyncio.run(handle_mcp_request(query))
1 change: 1 addition & 0 deletions tests/metadata_definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
METADATA = ("title", "description", "integrations", "categories")

ALLOWED_INTEGRATIONS = (
"anthropic",
"asana",
"auth0",
"autokitteh",
Expand Down
Loading