From 9ce0e3ae79337eab0a7ac60a8b38547a3af1e6f4 Mon Sep 17 00:00:00 2001 From: Anshul Khandelwal Date: Mon, 19 Oct 2026 11:27:33 +0530 Subject: [PATCH 1/2] Add OpenRTB example with external MCP servers Co-Authored-By: Claude Opus 5.5 --- rill-openrtb-prog-ads-mcp/.gitignore | 6 + rill-openrtb-prog-ads-mcp/README.md | 23 ++ rill-openrtb-prog-ads-mcp/alerts/alert.yaml | 22 ++ rill-openrtb-prog-ads-mcp/apis/api.yaml | 8 + .../connectors/atlassian.yaml | 8 + .../connectors/duckdb.yaml | 4 + .../connectors/github.yaml | 10 + .../connectors/huggingface.yaml | 6 + .../connectors/linear.yaml | 35 +++ .../connectors/notion.yaml | 32 +++ .../dashboards/auction_explore.yaml | 51 +++++ .../dashboards/bids_explore.yaml | 70 ++++++ .../dashboards/executive_overview.yaml | 173 ++++++++++++++ .../metrics/auction_metrics.yaml | 136 +++++++++++ .../metrics/bids_metrics.yaml | 212 ++++++++++++++++++ .../models/annotations_auction.sql | 86 +++++++ .../models/auction_data_model.sql | 18 ++ .../models/bids_data_model.sql | 16 ++ rill-openrtb-prog-ads-mcp/rill.yaml | 22 ++ .../sources/auction_data_raw.yaml | 11 + .../sources/bids_data_raw.yaml | 11 + 21 files changed, 960 insertions(+) create mode 100644 rill-openrtb-prog-ads-mcp/.gitignore create mode 100644 rill-openrtb-prog-ads-mcp/README.md create mode 100644 rill-openrtb-prog-ads-mcp/alerts/alert.yaml create mode 100644 rill-openrtb-prog-ads-mcp/apis/api.yaml create mode 100644 rill-openrtb-prog-ads-mcp/connectors/atlassian.yaml create mode 100644 rill-openrtb-prog-ads-mcp/connectors/duckdb.yaml create mode 100644 rill-openrtb-prog-ads-mcp/connectors/github.yaml create mode 100644 rill-openrtb-prog-ads-mcp/connectors/huggingface.yaml create mode 100644 rill-openrtb-prog-ads-mcp/connectors/linear.yaml create mode 100644 rill-openrtb-prog-ads-mcp/connectors/notion.yaml create mode 100644 rill-openrtb-prog-ads-mcp/dashboards/auction_explore.yaml create mode 100644 rill-openrtb-prog-ads-mcp/dashboards/bids_explore.yaml create mode 100644 rill-openrtb-prog-ads-mcp/dashboards/executive_overview.yaml create mode 100644 rill-openrtb-prog-ads-mcp/metrics/auction_metrics.yaml create mode 100644 rill-openrtb-prog-ads-mcp/metrics/bids_metrics.yaml create mode 100644 rill-openrtb-prog-ads-mcp/models/annotations_auction.sql create mode 100644 rill-openrtb-prog-ads-mcp/models/auction_data_model.sql create mode 100644 rill-openrtb-prog-ads-mcp/models/bids_data_model.sql create mode 100644 rill-openrtb-prog-ads-mcp/rill.yaml create mode 100644 rill-openrtb-prog-ads-mcp/sources/auction_data_raw.yaml create mode 100644 rill-openrtb-prog-ads-mcp/sources/bids_data_raw.yaml diff --git a/rill-openrtb-prog-ads-mcp/.gitignore b/rill-openrtb-prog-ads-mcp/.gitignore new file mode 100644 index 0000000..047a622 --- /dev/null +++ b/rill-openrtb-prog-ads-mcp/.gitignore @@ -0,0 +1,6 @@ +.DS_Store + +# Rill +.env +tmp +.rillcloud \ No newline at end of file diff --git a/rill-openrtb-prog-ads-mcp/README.md b/rill-openrtb-prog-ads-mcp/README.md new file mode 100644 index 0000000..6ca7aff --- /dev/null +++ b/rill-openrtb-prog-ads-mcp/README.md @@ -0,0 +1,23 @@ +# OpenRTB Programmatic Advertising with External MCP Servers + +This is the [OpenRTB example](../rill-openrtb-prog-ads) with external MCP servers connected to Rill's AI assistant, so it can combine the bid stream data with Linear, Notion, Atlassian, GitHub and Hugging Face. + +The servers are declared in `connectors/`. See [External MCP Servers](https://docs.rilldata.com/guide/ai/external-mcp) for the connector reference. + +## Set up + +Deploy the project to Rill Cloud: + +``` +git clone https://github.com/rilldata/rill-examples.git +cd rill-examples/rill-openrtb-prog-ads-mcp +rill deploy +``` + +- **Linear, Notion, Atlassian, Hugging Face** use per-user OAuth, which is available in Rill Cloud only. Each user clicks **Connect** in the chat's **External tools** menu. +- **GitHub** uses a personal access token. Set it before chatting: + ``` + rill env set GITHUB_PAT + ``` + +The Linear and Notion connectors expose only a subset of their tools through `security.include`, which keeps the assistant under the limit of 128 tools per request. diff --git a/rill-openrtb-prog-ads-mcp/alerts/alert.yaml b/rill-openrtb-prog-ads-mcp/alerts/alert.yaml new file mode 100644 index 0000000..3658884 --- /dev/null +++ b/rill-openrtb-prog-ads-mcp/alerts/alert.yaml @@ -0,0 +1,22 @@ +# Alert YAML +# Reference documentation: https://docs.rilldata.com/reference/project-files/alerts + +type: alert +display_name: Project Resource Status Check + +# Check the alert every hour. +refresh: + cron: 0 * * * * + +data: + resource_status: + where_error: true + +# Send notifications in Slack. +notify: + slack: + channels: + - '#rill-alerts' + email: + recipients: + - admin_team@domain.com \ No newline at end of file diff --git a/rill-openrtb-prog-ads-mcp/apis/api.yaml b/rill-openrtb-prog-ads-mcp/apis/api.yaml new file mode 100644 index 0000000..2762ad9 --- /dev/null +++ b/rill-openrtb-prog-ads-mcp/apis/api.yaml @@ -0,0 +1,8 @@ +# API YAML +# Reference documentation: https://docs.rilldata.com/reference/project-files/apis +# Test your API endpoint at http://localhost:9009/v1/instances/default/api/api + +type: api + +metrics_sql: | + select requests, app_site_name from auction_metrics limit 10 diff --git a/rill-openrtb-prog-ads-mcp/connectors/atlassian.yaml b/rill-openrtb-prog-ads-mcp/connectors/atlassian.yaml new file mode 100644 index 0000000..10d408f --- /dev/null +++ b/rill-openrtb-prog-ads-mcp/connectors/atlassian.yaml @@ -0,0 +1,8 @@ +type: connector +driver: mcp +url: https://mcp.atlassian.com/v1/mcp +display_name: Atlassian +ai_instructions: > + Use Atlassian to look up Jira issues and Confluence pages. + Never create or update anything unless the user explicitly asks. +auth: oauth diff --git a/rill-openrtb-prog-ads-mcp/connectors/duckdb.yaml b/rill-openrtb-prog-ads-mcp/connectors/duckdb.yaml new file mode 100644 index 0000000..3e2d5a9 --- /dev/null +++ b/rill-openrtb-prog-ads-mcp/connectors/duckdb.yaml @@ -0,0 +1,4 @@ +type: connector +driver: duckdb + +managed: true \ No newline at end of file diff --git a/rill-openrtb-prog-ads-mcp/connectors/github.yaml b/rill-openrtb-prog-ads-mcp/connectors/github.yaml new file mode 100644 index 0000000..a7127c3 --- /dev/null +++ b/rill-openrtb-prog-ads-mcp/connectors/github.yaml @@ -0,0 +1,10 @@ +type: connector +driver: mcp +url: https://api.githubcopilot.com/mcp/ +display_name: GitHub +ai_instructions: > + Use GitHub to look up repositories, issues and pull requests. + Never create or modify anything unless the user explicitly asks. +headers: + Authorization: "Bearer {{ .env.GITHUB_PAT }}" + X-MCP-Toolsets: "repos,issues,pull_requests" diff --git a/rill-openrtb-prog-ads-mcp/connectors/huggingface.yaml b/rill-openrtb-prog-ads-mcp/connectors/huggingface.yaml new file mode 100644 index 0000000..03256c4 --- /dev/null +++ b/rill-openrtb-prog-ads-mcp/connectors/huggingface.yaml @@ -0,0 +1,6 @@ +type: connector +driver: mcp +url: https://huggingface.co/mcp +display_name: Hugging Face +ai_instructions: Use Hugging Face to search models, datasets and papers. +auth: oauth diff --git a/rill-openrtb-prog-ads-mcp/connectors/linear.yaml b/rill-openrtb-prog-ads-mcp/connectors/linear.yaml new file mode 100644 index 0000000..b0e7856 --- /dev/null +++ b/rill-openrtb-prog-ads-mcp/connectors/linear.yaml @@ -0,0 +1,35 @@ +type: connector +driver: mcp +url: https://mcp.linear.app/mcp +display_name: Linear +ai_instructions: > + Use Linear to look up issues and projects. + Never create or update issues unless the user explicitly asks. +auth: oauth +security: + access: true + include: + - if: true + names: + - list_issues + - get_issue + - list_comments + - list_issue_statuses + - list_issue_labels + - list_projects + - get_project + - list_milestones + - get_milestone + - list_cycles + - list_initiatives + - get_initiative + - get_status_updates + - list_teams + - get_team + - list_users + - get_user + - list_documents + - get_document + - search_documentation + - save_issue + - save_comment diff --git a/rill-openrtb-prog-ads-mcp/connectors/notion.yaml b/rill-openrtb-prog-ads-mcp/connectors/notion.yaml new file mode 100644 index 0000000..ff55809 --- /dev/null +++ b/rill-openrtb-prog-ads-mcp/connectors/notion.yaml @@ -0,0 +1,32 @@ +type: connector +driver: mcp +url: https://mcp.notion.com/mcp +display_name: Notion +ai_instructions: > + Use Notion to search and read pages and databases. + Never create or edit pages unless the user explicitly asks. +auth: oauth +security: + access: true + include: + - if: true + names: + - notion-search + - notion-fetch + - notion-query-data-sources + - notion-query-multiple-data-sources + - notion-query-meeting-notes + - notion-list-recent-pages + - notion-list-shared-pages + - notion-list-private-pages + - notion-list-favorite-pages + - notion-list-page-backlinks + - notion-get-comments + - notion-get-users + - notion-get-teams + - notion-get-self + - notion-check-mcp-next-steps + - notion-show-advanced-analysis-next-steps + - notion-create-pages + - notion-update-page + - notion-create-comment diff --git a/rill-openrtb-prog-ads-mcp/dashboards/auction_explore.yaml b/rill-openrtb-prog-ads-mcp/dashboards/auction_explore.yaml new file mode 100644 index 0000000..f3f7fd3 --- /dev/null +++ b/rill-openrtb-prog-ads-mcp/dashboards/auction_explore.yaml @@ -0,0 +1,51 @@ +# Explore YAML +# Reference documentation: https://docs.rilldata.com/reference/project-files/explores + +type: explore + +tags: + - dashboard + - explore + - auction + - programmatic_ads + +display_name: "Programmatic Ads Auction" +metrics_view: auction_metrics + +dimensions: '*' +measures: '*' + +time_zones: + - America/Los_Angeles + - America/Chicago + - America/New_York + - Europe/London + - Europe/Paris + +embeds: + hide_pivot: true +defaults: + measures: + - requests + - avg_bid_floor + - 1d_qps + dimensions: + - app_site_name + - app_site_domain + - pub_name + - app_site_cat + - ad_size + - device_state + - device_osv + - bid_floor_bucket + - platform_browser + - device_os + - device_type + - ad_position + - video_max_duration_bucket + - video_min_duration_bucket + - placement_type + - auction_type + - app_or_site + - device_country + time_range: P7D diff --git a/rill-openrtb-prog-ads-mcp/dashboards/bids_explore.yaml b/rill-openrtb-prog-ads-mcp/dashboards/bids_explore.yaml new file mode 100644 index 0000000..e76a31c --- /dev/null +++ b/rill-openrtb-prog-ads-mcp/dashboards/bids_explore.yaml @@ -0,0 +1,70 @@ +# Explore YAML +# Reference documentation: https://docs.rilldata.com/reference/project-files/explores + +type: explore + +tags: + - dashboard + - explore + - bids + - programmatic_ads + +display_name: "Programmatic Ads Bids" +metrics_view: bids_metrics + +dimensions: '*' +measures: '*' + +#default: +# time_range: "PT24H" + +time_zones: + - America/Los_Angeles + - America/Chicago + - America/New_York + - Europe/London + - Europe/Paris + - Asia/Jerusalem + - Europe/Moscow + - Asia/Kolkata + - Asia/Shanghai + - Asia/Tokyo + - Australia/Sydney +defaults: + measures: + - overall_spend + - total_bids + - impressions + - win_rate + - clicks + - ctr + - video_starts + - video_completes + - video_completion_rate + - avg_bid_price + - ecpm + - avg_bid_floor + dimensions: + - adomain + - advertiser_name + - app_or_site + - sites_domain + - app_site_name + - auction_type + - bid_floor_bucket + - campaign_name + - creative_type + - device_os + - device_osv + - device_region + - device_type + - interstitial + - line_item_name + - placement_type + - platform_browser + - player_size + - privacy + - pub_name + - sdk + - video_activity + time_range: P7D diff --git a/rill-openrtb-prog-ads-mcp/dashboards/executive_overview.yaml b/rill-openrtb-prog-ads-mcp/dashboards/executive_overview.yaml new file mode 100644 index 0000000..ae215db --- /dev/null +++ b/rill-openrtb-prog-ads-mcp/dashboards/executive_overview.yaml @@ -0,0 +1,173 @@ +# Explore YAML +# Reference documentation: https://docs.rilldata.com/reference/project-files/canvas-dashboards + +type: canvas +tags: + - dashboard + - canvas + - executive + - overview + - programmatic_ads +display_name: Executive summary report +defaults: + time_range: 14D as of latest/D+1D + comparison_mode: time + filters: {} +filters: + pinned: + - app_site_name + - pub_name +rows: + - items: + - markdown: + alignment: + horizontal: center + vertical: middle + content: "# Executive summary report" + description: "" + title: "" + width: 12 + height: 80px + - items: + - kpi_grid: + comparison: + - delta + - percent_change + - previous + measures: + - overall_spend + - total_bids + - impressions + metrics_view: bids_metrics + sparkline: bottom + title: Bids + width: 6 + - kpi_grid: + comparison: + - delta + - percent_change + measures: + - requests + - avg_bid_floor + - 1d_qps + metrics_view: auction_metrics + sparkline: bottom + title: Auctions + width: 6 + height: 300px + - items: + - stacked_bar: + metrics_view: bids_metrics + description: "" + color: + field: device_type + type: nominal + vl_config: |- + { + "range": { + "category": [ + "#1f77b4", + "#ff7f0e", + "#2ca02c", + "#d62728", + "#9467bd", + "#8c564b", + "#e377c2", + "#7f7f7f", + "#bcbd22", + "#17becf" + ] + } + } + x: + field: __time + limit: 20 + sort: -y + type: temporal + y: + field: overall_spend + type: quantitative + zeroBasedOrigin: true + width: 8 + - donut_chart: + color: + field: device_os + limit: 20 + type: nominal + innerRadius: 50 + measure: + field: overall_spend + type: quantitative + metrics_view: bids_metrics + width: 4 + height: 320px + - items: + - combo_chart: + color: + field: measures + legendOrientation: top + type: value + description: "" + metrics_view: bids_metrics + title: "" + x: + field: __time + limit: 30 + showAxisTitle: true + showNull: false + type: temporal + y1: + field: total_bids + mark: bar + type: quantitative + zeroBasedOrigin: true + y2: + field: clicks + mark: line + type: quantitative + zeroBasedOrigin: true + width: 6 + - heatmap: + color: + field: total_bids + type: quantitative + legendOrientation: none + dimension_filters: device_type NIN ('Not Available','Connected Device') + metrics_view: bids_metrics + time_filters: tr=rill-PDC&tz=UTC&grain=hour + title: Bidding Pattern by Device + x: + field: __time + limit: 40 + type: temporal + y: + field: device_type + limit: 40 + type: nominal + width: 6 + height: 386px + - items: + - table: + col_dimensions: [] + columns: + - advertiser_name + - overall_spend + - total_bids + - impressions + - clicks + - ctr + - ecpm + measures: + - overall_spend + - total_bids + - impressions + - win_rate + - clicks + metrics_view: bids_metrics + row_dimensions: + - advertiser_name + time_filters: tr=P7D&compare_tr=rill-PW&grain=day + title: Per advertiser metrics – last 7 days + width: 12 + height: 300px +max_width: 1440 diff --git a/rill-openrtb-prog-ads-mcp/metrics/auction_metrics.yaml b/rill-openrtb-prog-ads-mcp/metrics/auction_metrics.yaml new file mode 100644 index 0000000..6ec8d0b --- /dev/null +++ b/rill-openrtb-prog-ads-mcp/metrics/auction_metrics.yaml @@ -0,0 +1,136 @@ +# Metrics view YAML +# Reference documentation: https://docs.rilldata.com/reference/project-files/dashboards + +version: 1 +model: "auction_data_model" +type: metrics_view +tags: + - metrics_view + - auction + - programmatic_ads + +timeseries: "__time" +smallest_time_grain: "hour" + +security: + access: true + #row_filter: pub_name in (select pub_name from pub_user_mapping where user_email = '{{.user.email}}' ) + +annotations: + - model: annotations_auction + name: auction_annotations + measures: ['requests'] + +measures: + - display_name: Total Requests + name: requests + expression: sum(bid_request_cnt) + description: Total Requests + format_preset: humanize + tags: [measure, auction, programmatic_ads] + - display_name: "Avg Bid Floor" + name: avg_bid_floor + expression: "sum(bid_floor) / sum(has_bid_floor_cnt) " + description: "Average Bid Floor" + format_preset: currency_usd + tags: [measure, auction, programmatic_ads] + - display_name: "1D QPS" + name: 1d_qps + expression: "sum(bid_request_cnt) / 86400 " + description: "1D QPS" + format_preset: humanize + tags: [measure, auction, programmatic_ads] + +dimensions: + - name: app_site_name + display_name: App Site Name + column: app_site_name + description: "" + tags: [dimension, auction, programmatic_ads] + - display_name: App Site Domain + column: app_site_domain + description: "" + name: app_site_domain + tags: [dimension, auction, programmatic_ads] + - name: pub_name + display_name: Pub Name + column: pub_name + description: "" + tags: [dimension, auction, programmatic_ads] + - name: app_site_cat + display_name: App Site Cat + column: app_site_cat + description: "" + tags: [dimension, auction, programmatic_ads] + - name: ad_size + display_name: Ad Size + column: ad_size + description: "" + tags: [dimension, auction, programmatic_ads] + - name: device_state + display_name: Device State + column: device_state + description: "" + tags: [dimension, auction, programmatic_ads] + - name: device_osv + display_name: Device OS Version + column: device_osv + description: "" + tags: [dimension, auction, programmatic_ads] + - name: bid_floor_bucket + display_name: Bid Floor Bucket + column: bid_floor_bucket + description: "" + tags: [dimension, auction, programmatic_ads] + - name: platform_browser + display_name: Platform Browser + column: platform_browser + description: "" + tags: [dimension, auction, programmatic_ads] + - name: device_os + display_name: Device OS + column: device_os + description: "" + tags: [dimension, auction, programmatic_ads] + - name: device_type + display_name: Device Type + column: device_type + description: "" + tags: [dimension, auction, programmatic_ads] + - name: ad_position + display_name: Ad Position + column: ad_position + description: "" + tags: [dimension, auction, programmatic_ads] + - name: video_max_duration_bucket + display_name: Video Max Duration Bucket + column: video_max_duration_bucket + description: "" + tags: [dimension, auction, programmatic_ads] + - name: video_min_duration_bucket + display_name: Video Min Duration Bucket + column: video_min_duration_bucket + description: "" + tags: [dimension, auction, programmatic_ads] + - display_name: Placement Type + column: placement_type + description: "" + name: placement_type + tags: [dimension, auction, programmatic_ads] + - name: auction_type + display_name: Auction Type + column: auction_type + description: "" + tags: [dimension, auction, programmatic_ads] + - name: app_or_site + display_name: App or Site + column: app_or_site + description: "" + tags: [dimension, auction, programmatic_ads] + - name: device_country + display_name: "Device Country" + column: "device_country" + description: "" + tags: [dimension, auction, programmatic_ads] + +#ai_instructions: Add intructions here for AI agent to understand the context of this metrics view. \ No newline at end of file diff --git a/rill-openrtb-prog-ads-mcp/metrics/bids_metrics.yaml b/rill-openrtb-prog-ads-mcp/metrics/bids_metrics.yaml new file mode 100644 index 0000000..0834a3d --- /dev/null +++ b/rill-openrtb-prog-ads-mcp/metrics/bids_metrics.yaml @@ -0,0 +1,212 @@ +# Metrics view YAML +# Reference documentation: https://docs.rilldata.com/reference/project-files/dashboards + +version: 1 +model: "bids_data_model" +type: metrics_view +tags: + - metrics_view + - bids + - programmatic_ads + +timeseries: "__time" +smallest_time_grain: "hour" + +security: + access: true + #row_filter: advertiser_name in (select advertiser_name from advertiser_user_mapping where user_email = '{{.user.email}}' ) + +measures: + - display_name: Advertising Spend Overall + name: overall_spend + expression: sum(media_spend_usd)/1000 + description: Total Spend + format_preset: currency_usd + tags: [measure, bids, programmatic_ads] + - display_name: "Bids" + name: total_bids + expression: "sum(bid_cnt)" + description: "Total Bids" + format_preset: humanize + tags: [measure, bids, programmatic_ads] + - display_name: "Impressions" + name: impressions + expression: "sum(imp_cnt)" + description: "Total Impressions" + format_preset: humanize + tags: [measure, bids, programmatic_ads] + - display_name: "Win Rate" + name: win_rate + expression: "sum(imp_cnt)*1.0/sum(bid_cnt)" + description: "Impressions / Bids" + format_preset: percentage + tags: [measure, bids, programmatic_ads] + - display_name: "Clicks" + name: clicks + expression: "sum(click_reg_cnt)" + description: "Total Clicks" + format_preset: humanize + tags: [measure, bids, programmatic_ads] + - display_name: "CTR" + name: ctr + expression: "sum(click_reg_cnt)*1.0/nullif(sum(imp_cnt),0)" + description: "Click Through Rate" + format_preset: percentage + tags: [measure, bids, programmatic_ads] + - display_name: "Video Starts" + name: video_starts + expression: "sum(video_start_cnt)" + description: "Total Video Starts" + format_preset: humanize + tags: [measure, bids, programmatic_ads] + - display_name: "Video Completes" + name: video_completes + expression: "sum(video_complete_cnt)" + description: "Total Video Completes" + format_preset: humanize + tags: [measure, bids, programmatic_ads] + - display_name: "Video Completion Rate" + name: video_completion_rate + expression: "sum(video_complete_cnt)*1.0/sum(video_start_cnt)" + description: "Video Completion Rate" + format_preset: percentage + tags: [measure, bids, programmatic_ads] + - display_name: "Avg Bid Price" + name: avg_bid_price + expression: "sum(bid_price_usd)*1.0/sum(bid_cnt)/1000" + description: "Average Bid Price" + format_preset: currency_usd + tags: [measure, bids, programmatic_ads] + - display_name: "eCPM" + name: ecpm + expression: "sum(media_spend_usd)*1.0/1000/nullif(sum(imp_cnt),0)" + description: "eCPM" + format_preset: currency_usd + tags: [measure, bids, programmatic_ads] + - display_name: "Avg Bid Floor" + name: avg_bid_floor + expression: "sum(bid_floor)*1.0/sum(has_bid_floor_cnt)" + description: "Average Bid Floor" + format_preset: currency_usd + tags: [measure, bids, programmatic_ads] + # Advanced Measure: 7 Day Rolling Average Bids, requires total_bids measure. + # See https://docs.rilldata.com/build/metrics-view/measures/windows for more info. + - name: bids_7day_rolling_avg + display_name: 7 Day Bid rolling avg + expression: AVG(total_bids) + requires: [total_bids] + window: + order: "__time" + frame: RANGE BETWEEN INTERVAL 6 DAY PRECEDING AND CURRENT ROW + tags: [measure, bids, programmatic_ads] + +dimensions: + - column: adomain + name: adomain + display_name: Adomain + tags: [dimension, bids, programmatic_ads] + - column: advertiser_name + name: advertiser_name + display_name: Advertiser Name + tags: [dimension, bids, programmatic_ads] + - display_name: App or Site + name: app_or_site + column: app_or_site + description: "" + tags: [dimension, bids, programmatic_ads] + - display_name: Site Domain + name: sites_domain + column: app_site_domain + description: "" + tags: [dimension, bids, programmatic_ads] + - column: app_site_name + name: app_site_name + display_name: App Name + tags: [dimension, bids, programmatic_ads] + - name: auction_type + display_name: Auction Type + column: auction_type + description: "" + tags: [dimension, bids, programmatic_ads] + - display_name: Bid Floor Bucket + column: bid_floor_bucket + description: "" + name: bid_floor_bucket + tags: [dimension, bids, programmatic_ads] + - name: campaign_name + display_name: Campaign Name + column: campaign_name + description: "" + tags: [dimension, bids, programmatic_ads] + - name: creative_type + display_name: Creative type + column: creative_type + description: "" + tags: [dimension, bids, programmatic_ads] + - name: device_os + display_name: Device OS + column: device_os + description: + tags: [dimension, bids, programmatic_ads] + - name: device_osv + display_name: Device OSV + column: device_osv + description: "" + tags: [dimension, bids, programmatic_ads] + - name: device_region + display_name: Device Region + column: device_region + description: "" + tags: [dimension, bids, programmatic_ads] + - display_name: Device Type + column: device_type + description: "" + name: device_type + tags: [dimension, bids, programmatic_ads] + - name: interstitial + display_name: Interstitial + column: interstitial + description: "" + tags: [dimension, bids, programmatic_ads] + - name: line_item_name + display_name: Line Item Name + column: line_item_name + description: "" + tags: [dimension, bids, programmatic_ads] + - display_name: Placement Type + column: placement_type + description: "" + name: placement_type + tags: [dimension, bids, programmatic_ads] + - name: platform_browser + display_name: Platform Browser + column: platform_browser + description: "" + tags: [dimension, bids, programmatic_ads] + - name: player_size + display_name: Player Size + column: player_size + description: "" + tags: [dimension, bids, programmatic_ads] + - name: privacy + display_name: Privacy + column: privacy + description: "" + tags: [dimension, bids, programmatic_ads] + - name: pub_name + display_name: Pub Name + column: pub_name + description: "" + tags: [dimension, bids, programmatic_ads] + - name: sdk + display_name: SDK + column: sdk + description: "" + tags: [dimension, bids, programmatic_ads] + - name: video_activity + display_name: Video Activity + column: video_activity + description: "" + tags: [dimension, bids, programmatic_ads] + +#ai_instructions: Add intructions here for AI agent to understand the context of this metrics view. \ No newline at end of file diff --git a/rill-openrtb-prog-ads-mcp/models/annotations_auction.sql b/rill-openrtb-prog-ads-mcp/models/annotations_auction.sql new file mode 100644 index 0000000..f3b9dc0 --- /dev/null +++ b/rill-openrtb-prog-ads-mcp/models/annotations_auction.sql @@ -0,0 +1,86 @@ +-- Model SQL +-- Reference documentation: https://docs.rilldata.com/reference/project-files/models +-- @materialize: true + +WITH min_max AS ( + SELECT + MIN(__time) AS min_time, + MAX(__time) AS max_time, + (MAX(__time) - MIN(__time)) AS total_span + FROM auction_data_model +), +sequence AS ( + SELECT + min_time + ((total_span * i::DOUBLE) / 49) + ((random() - 0.5) * total_span * 0.02) AS time, + i + FROM min_max, range(50) t(i) +), +descriptions AS ( + SELECT description + FROM UNNEST([ + 'Programmatic auction started', + 'Bid request sent to DSP', + 'Ad impression logged', + 'Auction timeout occurred', + 'Floor price applied', + 'Bid won by DSP A', + 'Creative served successfully', + 'Ad rendered on device', + 'Invalid traffic detected', + 'Ad click tracked', + 'Viewability threshold met', + 'Brand safety check passed', + 'Geo-targeting matched', + 'Device type classified', + 'Ad not rendered due to timeout', + 'Second price auction applied', + 'Ad slot refreshed', + 'Campaign budget depleted', + 'Frequency cap reached', + 'DSP throttled bid rate', + 'Publisher added new inventory', + 'SSP filtering low-value bids', + 'Demand spike observed', + 'Creative rejected by scanner', + 'Bid caching mechanism used', + 'No eligible bids', + 'Programmatic deal triggered', + 'PMP deal preference applied', + 'Dynamic price floor used', + 'Contextual signal evaluated', + 'Latency spike during bidding', + 'Outstream video ad served', + 'Ad slot collapsed', + 'Native ad rendered', + 'OpenRTB 2.5 used', + 'Header bidding enabled', + 'Ad verification passed', + 'Campaign pacing adjusted', + 'Click-through rate spike', + 'Device ID match successful', + 'Cross-device attribution fired', + 'Lookalike segment matched', + 'Audience extension applied', + 'Server-side auction triggered', + 'Bid shading applied', + 'DSP bidding aggressively', + 'Ad failed brand check', + 'Ad muted by user', + 'Impression fraud prevented', + 'Auction closed' + ]) AS t(description) +), +annotations AS ( + SELECT + s.time, + d.description + FROM sequence s + JOIN ( + SELECT description, ROW_NUMBER() OVER () - 1 AS rn FROM descriptions + ) d ON d.rn = s.i +) +SELECT + *, + --'day' as duration -- Allows you to floor a annotation to a specified duration +FROM annotations +ORDER BY time \ No newline at end of file diff --git a/rill-openrtb-prog-ads-mcp/models/auction_data_model.sql b/rill-openrtb-prog-ads-mcp/models/auction_data_model.sql new file mode 100644 index 0000000..b204140 --- /dev/null +++ b/rill-openrtb-prog-ads-mcp/models/auction_data_model.sql @@ -0,0 +1,18 @@ +-- Model SQL +-- Reference documentation: https://docs.rilldata.com/reference/project-files/models +-- @materialize: true + +WITH time_shift AS ( + SELECT + MAX(__time) AS max_time + FROM auction_data_raw +), +shifted_data AS ( + SELECT + __time + (CURRENT_TIMESTAMP - time_shift.max_time) AS __time, + * EXCLUDE (__time, device_region), + CASE WHEN device_region ILIKE '%/%' THEN SPLIT(device_region, '/')[2] ELSE 'Unknown' END AS device_state, + CASE WHEN device_region ILIKE '%/%' THEN SPLIT(device_region, '/')[1] ELSE 'Unknown' END AS device_country + FROM auction_data_raw, time_shift +) +SELECT * FROM shifted_data \ No newline at end of file diff --git a/rill-openrtb-prog-ads-mcp/models/bids_data_model.sql b/rill-openrtb-prog-ads-mcp/models/bids_data_model.sql new file mode 100644 index 0000000..336f3a6 --- /dev/null +++ b/rill-openrtb-prog-ads-mcp/models/bids_data_model.sql @@ -0,0 +1,16 @@ +-- Model SQL +-- Reference documentation: https://docs.rilldata.com/reference/project-files/models +-- @materialize: true + +WITH time_shift AS ( + SELECT + MAX(__time) AS max_time + FROM bids_data_raw +), +shifted_data AS ( + SELECT + __time + (CURRENT_TIMESTAMP - time_shift.max_time) AS __time, + * EXCLUDE (__time) + FROM bids_data_raw, time_shift +) +SELECT * FROM shifted_data \ No newline at end of file diff --git a/rill-openrtb-prog-ads-mcp/rill.yaml b/rill-openrtb-prog-ads-mcp/rill.yaml new file mode 100644 index 0000000..1c3df49 --- /dev/null +++ b/rill-openrtb-prog-ads-mcp/rill.yaml @@ -0,0 +1,22 @@ +compiler: rillv1 + +display_name: Open RTB Ads with MCP +description: Open RTB Ads with external MCP servers + +olap_connector: duckdb + +mock_users: +- email: someone@rilldata.com + name: John Doe + admin: true +- email: jane@partnercompany.com + groups: + - partners +- email: test@domain.com + +models: + refresh: + every: 24h + +features: + cloud_editing: true diff --git a/rill-openrtb-prog-ads-mcp/sources/auction_data_raw.yaml b/rill-openrtb-prog-ads-mcp/sources/auction_data_raw.yaml new file mode 100644 index 0000000..95ae483 --- /dev/null +++ b/rill-openrtb-prog-ads-mcp/sources/auction_data_raw.yaml @@ -0,0 +1,11 @@ +# Model YAML +# Reference documentation: https://docs.rilldata.com/reference/project-files/models + +type: model +materialize: true + +connector: duckdb +# create_secrets_from_connectors: gcs + +sql: | + select * from read_parquet('https://storage.googleapis.com/rilldata-public/auction_data.parquet') diff --git a/rill-openrtb-prog-ads-mcp/sources/bids_data_raw.yaml b/rill-openrtb-prog-ads-mcp/sources/bids_data_raw.yaml new file mode 100644 index 0000000..4b1922b --- /dev/null +++ b/rill-openrtb-prog-ads-mcp/sources/bids_data_raw.yaml @@ -0,0 +1,11 @@ +# Model YAML +# Reference documentation: https://docs.rilldata.com/reference/project-files/models + +type: model +materialize: true + +connector: duckdb +# create_secrets_from_connectors: gcs + +sql: | + select * from read_parquet('https://storage.googleapis.com/rilldata-public/bids_data.parquet') From 48bc7f063f9a0e14fe7d99bfd3c3927d436dc3f4 Mon Sep 17 00:00:00 2001 From: Anshul Khandelwal Date: Mon, 19 Oct 2026 11:50:34 +0530 Subject: [PATCH 2/2] Add Slack connector to the MCP example Co-Authored-By: Claude Opus 5.5 --- rill-openrtb-prog-ads-mcp/README.md | 11 ++++++++++- rill-openrtb-prog-ads-mcp/connectors/slack.yaml | 11 +++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 rill-openrtb-prog-ads-mcp/connectors/slack.yaml diff --git a/rill-openrtb-prog-ads-mcp/README.md b/rill-openrtb-prog-ads-mcp/README.md index 6ca7aff..cddbe64 100644 --- a/rill-openrtb-prog-ads-mcp/README.md +++ b/rill-openrtb-prog-ads-mcp/README.md @@ -1,6 +1,6 @@ # OpenRTB Programmatic Advertising with External MCP Servers -This is the [OpenRTB example](../rill-openrtb-prog-ads) with external MCP servers connected to Rill's AI assistant, so it can combine the bid stream data with Linear, Notion, Atlassian, GitHub and Hugging Face. +This is the [OpenRTB example](../rill-openrtb-prog-ads) with external MCP servers connected to Rill's AI assistant, so it can combine the bid stream data with Linear, Notion, Atlassian, GitHub, Hugging Face and Slack. The servers are declared in `connectors/`. See [External MCP Servers](https://docs.rilldata.com/guide/ai/external-mcp) for the connector reference. @@ -19,5 +19,14 @@ rill deploy ``` rill env set GITHUB_PAT ``` +- **Slack** uses per-user OAuth with a Slack app you create, because Slack doesn't support dynamic client registration: + 1. Create an app at [api.slack.com/apps](https://api.slack.com/apps) and keep it internal to your workspace. Slack only allows internal and Marketplace apps to use its MCP server. + 2. Under **OAuth & Permissions**, add the redirect URL `https://admin.rilldata.com/connectors/oauth/callback` and the user token scopes listed in `oauth_scopes` in `connectors/slack.yaml`. + 3. Under **Agents & AI Apps**, enable MCP access. + 4. Set the app's credentials from **Basic Information**: + ``` + rill env set SLACK_MCP_CLIENT_ID + rill env set SLACK_MCP_CLIENT_SECRET + ``` The Linear and Notion connectors expose only a subset of their tools through `security.include`, which keeps the assistant under the limit of 128 tools per request. diff --git a/rill-openrtb-prog-ads-mcp/connectors/slack.yaml b/rill-openrtb-prog-ads-mcp/connectors/slack.yaml new file mode 100644 index 0000000..0a5b3a0 --- /dev/null +++ b/rill-openrtb-prog-ads-mcp/connectors/slack.yaml @@ -0,0 +1,11 @@ +type: connector +driver: mcp +url: https://mcp.slack.com/mcp +display_name: Slack +ai_instructions: > + Use Slack to search messages and read channels. + Never post messages unless the user explicitly asks. +auth: oauth +oauth_client_id: "{{ .env.SLACK_MCP_CLIENT_ID }}" +oauth_client_secret: "{{ .env.SLACK_MCP_CLIENT_SECRET }}" +oauth_scopes: [search:read.public, search:read.users, channels:read, channels:history, users:read, users:read.email]