Skip to content
Merged
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
19 changes: 19 additions & 0 deletions apps/api/src/handlers/slack/helpers/event-normalization.test.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions apps/docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@
"integrations/asana",
"integrations/better-stack",
"integrations/braintrust",
"integrations/cloudflare",
"integrations/elevenlabs",
"integrations/exa",
"integrations/grafana",
Expand Down
28 changes: 28 additions & 0 deletions apps/docs/integrations/cloudflare.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
title: Cloudflare
description: Inspect and operate Cloudflare resources from Roomote tasks.
icon: 'https://api.iconify.design/simple-icons:cloudflare.svg?color=currentColor'
---

Connect Cloudflare when tasks need account, zone, DNS, Workers, security, or
other Cloudflare API context.

## How setup works

A deployment operator connects Cloudflare once from **Settings > Integrations**
using OAuth. Cloudflare uses dynamic client registration and asks the operator
to approve the permissions available to the shared deployment connection.

## Permissions and changes

Cloudflare exposes one tool for finding API operations and one tool for running
them. The same execution tool can call read and write APIs, so Roomote treats
mutations as explicit actions: it only changes Cloudflare resources when the
request clearly asks for that specific change. Cloudflare still enforces the
permissions granted during OAuth consent.

## Verify the connection

Start with a read-only request, such as listing zones or inspecting a Worker.
If the request is denied, reconnect Cloudflare and review the permissions made
available during consent.
1 change: 1 addition & 0 deletions apps/docs/integrations/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ from [Personal Settings](/personal-settings).
| <IntegrationName href="/integrations/asana" icon="asana" name="Asana" /> | Project and task context from Asana | Admin connection once |
| <IntegrationName href="/integrations/better-stack" icon="betterstack" name="Better Stack" /> | Monitoring and incident context | Admin connection once |
| <IntegrationName href="/integrations/braintrust" icon="braintrust" name="Braintrust" /> | Prompts, runs, and evaluation context | Enable first, then teammates link accounts |
| <IntegrationName href="/integrations/cloudflare" icon="cloudflare" name="Cloudflare" /> | Cloud infrastructure and API operations | Admin connection once |
| <IntegrationName href="/integrations/elevenlabs" icon="elevenlabs" name="ElevenLabs" /> | Voice narration for feature-demo videos | Admin connection once |
| <IntegrationName href="/integrations/exa" icon="exa" name="Exa" /> | Keyless web search; optional Agent research | Admin enables; API key optional |
| <IntegrationName href="/integrations/grafana" icon="grafana" name="Grafana" /> | Dashboards, alerting, and monitoring context | Admin connection once |
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/components/settings/Integrations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ const DEEP_LINK_ENABLE_DESCRIPTIONS: Record<string, string> = {
'Roomote will be able to inspect monitoring, incidents, and telemetry.',
braintrust:
'Roomote will be able to inspect prompts, evaluations, and AI run history.',
cloudflare:
'Roomote will be able to inspect Cloudflare resources and run explicitly requested API operations with the permissions granted during OAuth.',
grafana:
'Roomote will be able to inspect dashboards, alert rules, live alert state, annotations, and data sources.',
granola:
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/components/system/custom/logos/brand-icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
siAsana,
siBetterstack,
siBraintrust,
siCloudflare,
siElevenlabs,
siDependabot,
siDatadog,
Expand Down Expand Up @@ -42,6 +43,7 @@ const SIMPLE_ICONS: Record<string, SimpleIcon> = {
asana: siAsana,
betterstack: siBetterstack,
braintrust: siBraintrust,
cloudflare: siCloudflare,
elevenlabs: siElevenlabs,
dependabot: siDependabot,
datadog: siDatadog,
Expand Down
8 changes: 8 additions & 0 deletions apps/worker/src/mcp/roomote-mcp-server/integration-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ Sentry uses the workspace MCP integration:

Once connected, tasks can inspect Sentry issue and project context, and scheduled Sentry triage automation uses the same read-only MCP connection.

# Cloudflare

Cloudflare uses OAuth:
1. A deployment operator enables Cloudflare from Settings > Integrations.
2. That operator connects Cloudflare once for the deployment via OAuth.

Once connected, I can search and inspect Cloudflare resources. The execute tool can also call write APIs, so I only make changes when the user explicitly requests the specific mutation and the OAuth connection permits it.

# Pylon

Pylon uses OAuth:
Expand Down
7 changes: 7 additions & 0 deletions packages/cloud-agents/src/server/mcp-self-setup/catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ export const MCP_SETUP_INTEGRATION_METADATA: Record<
string,
SetupMcpIntegrationMetadata
> = {
cloudflare: {
capabilities: [
'Search the Cloudflare API for available operations',
'Inspect zones, Workers, DNS, security, and account resources',
'Run explicitly requested Cloudflare API operations with the connected permissions',
],
},
asana: {
capabilities: [
'Inspect Asana workspaces, projects, and tasks',
Expand Down
28 changes: 28 additions & 0 deletions packages/slack/src/__tests__/forwarded-message-context.test.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

51 changes: 50 additions & 1 deletion packages/slack/src/forwarded-message-context.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { dataVisualizationBlockSchema } from '@roomote/types';
import {
dataVisualizationBlockSchema,
findSlackMcpSetupServicesInText,
matchSlackMcpSetupServiceUrl,
type SlackMcpSetupServiceDefinition,
} from '@roomote/types';

import type { SlackFile } from './types';

Expand Down Expand Up @@ -1165,6 +1170,44 @@ export function formatSlackBlockLinkContext(
].join('\n');
}

export function formatSlackMcpSetupRecommendationContext(
text: string,
blocks?: unknown[],
attachments?: unknown[],
): string | undefined {
const services = new Map<string, SlackMcpSetupServiceDefinition>();
for (const service of findSlackMcpSetupServicesInText(text)) {
services.set(service.id, service);
}

const links: SlackBlockLink[] = [];
const seenKeys = new Set<string>();
extractBlockLinks(blocks, links, seenKeys);
for (const attachment of attachments ?? []) {
if (isRecord(attachment)) {
extractBlockLinks(attachment.blocks, links, seenKeys);
}
}
for (const link of links) {
const service = matchSlackMcpSetupServiceUrl(link.url);
if (service) {
services.set(service.id, service);
}
}

if (services.size === 0) {
return undefined;
}

return [
'Slack integration setup recommendations:',
...[...services.values()].map(
(service) =>
`- ${service.name}: if it is unavailable, offer to connect the built-in integration from ${service.deploymentSettingsPath}.`,
),
].join('\n');
}

export function formatSlackBlockTextContext(
blocks?: unknown[],
existingText = '',
Expand Down Expand Up @@ -1303,11 +1346,17 @@ export function formatSlackAttachmentContext(
textWithForwardedContext,
);
const blockLinkContext = formatSlackBlockLinkContext(blocks, attachments);
const integrationSetupContext = formatSlackMcpSetupRecommendationContext(
text,
blocks,
attachments,
);
const additionalContexts = [
formatSlackForwardedMessageContext(attachments),
attachmentTitleContext,
blockTextContext,
blockLinkContext,
integrationSetupContext,
].filter((context): context is string => Boolean(context));

if (additionalContexts.length === 0) {
Expand Down
2 changes: 2 additions & 0 deletions packages/slack/src/mcp-recommendations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ const SLACK_ENABLE_DESCRIPTIONS: Record<string, string> = {
'Roomote will be able to inspect and manage shared email infrastructure.',
braintrust:
'Roomote will be able to inspect prompts, evaluations, and AI run history.',
cloudflare:
'Roomote will be able to inspect Cloudflare resources and run explicitly requested API operations with the connected permissions.',
linear:
'Roomote will be able to pull issue, project, and roadmap context into tasks.',
monday:
Expand Down
15 changes: 15 additions & 0 deletions packages/types/src/__tests__/mcp-oauth.test.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 32 additions & 0 deletions packages/types/src/__tests__/mcp-service-detection.test.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions packages/types/src/mcp-oauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,17 @@ export const MCP_INTEGRATIONS: McpIntegration[] = [
instructions:
'Sentry advertises only a few tools directly (find_organizations, find_projects, search_issues, search_events, get_sentry_resource). Reach everything else (issue details, event stack traces, breadcrumbs, tag values, issue events, releases, traces, replays, attachments, monitors, alert rules, docs) by calling search_sentry_tools with a short query, then execute_sentry_tool with the returned tool name and arguments. Which tools exist depends on the access the admin granted when connecting. Treat Sentry as read-only unless the request explicitly asks to change Sentry state: do not resolve, assign, ignore, or otherwise update issues, and do not create or modify projects, teams, DSNs, or monitors on your own initiative.',
},
{
id: 'cloudflare',
name: 'Cloudflare',
url: 'https://mcp.cloudflare.com/mcp',
description: `Inspect and operate Cloudflare resources from ${PRODUCT_NAME} tasks`,
icon: 'cloudflare',
connectionScope: 'deployment',
oauthResource: 'https://mcp.cloudflare.com/mcp',
instructions:
"Use Cloudflare to search and inspect the deployment's Cloudflare resources. The execute tool can call both read and write Cloudflare APIs; make changes only when the user explicitly requests the specific mutation, and preserve the permissions granted during OAuth consent.",
},
{
id: 'pylon',
name: 'Pylon',
Expand Down
Loading
Loading